Hungry Image Snake - Hole # 3

25

Otwór nr 1

Joe wąż jest głodny.

On je zdjęcia, jeden piksel na raz.

On naprawdę lubi jasne piksele.

Wyzwanie

Zaprogramuj Joe, aby zjadał najjaśniejsze piksele, jakie może znaleźć, biorąc pod uwagę, że może poruszać się tylko w górę, w dół, w lewo lub w prawo.

Dane techniczne

  • Joe musi zacząć od lewego górnego piksela obrazu.
  • Joe może poruszać się tylko w poziomie lub w pionie o 1 każdy ruch
  • Joe ma tylko wystarczająco dużo czasu, aby przenieść 1/3 liczby pikseli na zdjęciu (1/3 tyle ruchów co pikseli). Jeśli liczba pikseli nie jest wielokrotnością 3, zaokrąglij w dół do najbliższej liczby całkowitej.
  • Joe może przekroczyć swoją ścieżkę, ale liczy się to jako 0 jasność
  • Jasność opiera się na sumie r, gib, więc rgb (0,0,0) ma jasność 0, a rgb (255,255,255) ma maksymalną jasność.

Wkład

Możesz wprowadzić obraz, jak chcesz.

Wydajność

  • obraz przedstawiający wynik końcowy twojego zdjęcia (z czarnymi pikselami zjadanymi).
  • Ilość zjedzonej jasności (proszę podać zakres w odpowiedzi)

Punktacja

Twój program będzie oceniany na:

  • Średnia jasność pikseli Joe je / Średnia jasność pikseli na obrazie *

* możesz to zakodować na stałe w swoim programie

Twój całkowity wynik będzie średnią wyników dla następujących zdjęć:

Testuj obrazy:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

http://upload.wikimedia.org/wikipedia/en/thumb/f/f4/The_Scream.jpg/800px-The_Scream.jpg

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Stretch Maniac
źródło
3
Zabawa Markdown fakt - można obracać obrazy w linki do ich oryginałów: [![image description](SE URL for downsized image)](URL for original image).
Calvin's Hobbies
1
Dobrym pomysłem może być poproszenie ludzi o podanie w swoich odpowiedziach przykładowego „zjedzonego” obrazu.
Nathaniel

Odpowiedzi:

16

C ++, wynik: 1.42042 1,46766

Jest to zasadniczo nieco bardziej wyrafinowana wersja dwóch istniejących rozwiązań: spośród czterech możliwych ruchów wybiera ten, który maksymalizuje jasność. Jednak zamiast patrzeć tylko na jasność piksela docelowego, patrzy na ważoną sumę jasności pikseli w sąsiedztwie piksela docelowego, gdzie piksele bliżej celu mają większą wagę.

EDYCJA: Zastosowanie nieliniowej jasności w obliczeniach sąsiedztwa poprawia nieznacznie wynik.

Kompiluj z g++ joe.cpp -ojoe -std=c++11 -O3 -lcairo. Wymaga Cairo.

Uruchom z joe <image-file> [<radius>]. <image-file>to wejściowy obraz PNG. <radius>(opcjonalny argument) to promień zsumowanego sąsiedztwa, w pikselach (mniejszy jest szybszy, większy jest (z grubsza) lepszy.) Wyprowadza wynik i nazwany obraz out.<image-file>.

#include <cairo/cairo.h>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>

using namespace std;

int main(int argc, const char* argv[]) {
    auto* img = cairo_image_surface_create_from_png(argv[1]);
    int width = cairo_image_surface_get_width(img),
        height = cairo_image_surface_get_height(img),
        stride = cairo_image_surface_get_stride(img);
    unsigned char* data = cairo_image_surface_get_data(img);

    double* brightness = new double[width * height];
    double total_brightness = 0;
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            const unsigned char* p = data + stride * y + 4 * x;
            total_brightness += brightness[y * width + x] = p[0] + p[1] + p[2];
        }
    }

    const int r = argc > 2 ? stoi(argv[2]) : 64, R = 2 * r + 1;
    double* weight = new double[R * R];
    for (int y = -r; y <= r; ++y) {
        for (int x = -r; x <= r; ++x)
            weight[R * (y + r) + (x + r)] = 1.0 / (x*x + y*y + 1);
    }

    auto neighborhood = [&] (int x, int y) {
        double b = 0;
        int x1 = max(x - r, 0), x2 = min(x + r, width - 1);
        int y1 = max(y - r, 0), y2 = min(y + r, height - 1);
        for (int v = y1; v <= y2; ++v) {
            const double *B = brightness + width * v + x1;
            const double *W = weight + R * (v - (y - r)) + (x1 - (x - r));
            for (int u = x1; u <= x2; ++u, ++B, ++W)
                b += (*W) * (*B) * (*B);
        }
        return b;
    };

    int n = (2 * width * height + 3) / 6;
    int x = 0, y = 0;
    double path_brightness = 0;
    int O[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    for (int i = 0; i < n; ++i) {
        if (i % 1000 == 0) cerr << (200 * i + n) / (2 * n) << "%\r";

        path_brightness += brightness[width * y + x]; brightness[width * y + x] = 0;
        unsigned char* p = data + stride * y + 4 * x;
        p[0] = p[1] = 16 * i % 255; p[2] = 0;

        auto O_end = partition(begin(O), end(O), [&] (const int* o) {
            return x + o[0] >= 0 && x + o[0] < width &&
                   y + o[1] >= 0 && y + o[1] < height;
        });
        const int* o_max; double o_max_neighborhood = -1;
        for (auto o = O; o != O_end; ++o) {
            double o_neighborhood = neighborhood(x + (*o)[0], y + (*o)[1]);
            if (o_neighborhood > o_max_neighborhood)
                o_max = *o, o_max_neighborhood = o_neighborhood;
        }
        x += o_max[0]; y += o_max[1];
    }

    cout << (path_brightness * width * height) / (n * total_brightness) << endl;

    cairo_surface_write_to_png(img, (string("out.") + argv[1]).c_str());

    delete []brightness;
    delete []weight;
    cairo_surface_destroy(img);
}

Wyniki

Bridge    1.39945
Balls     1.77714
Scream    1.38349
Fractal   1.31727
Vortex    1.66493
Tornado   1.26366
-----------------
Average   1.46766

Most kulki Krzyk Fraktal Wir Tornado

Więcej słodyczy

Animacja wirowa Animacja Tornado

Łokieć
źródło
Właśnie wypróbowałem twój program na niektórych moich przykładowych obrazach, a jeden ma bardzo czarny kolor tylko wokół punktu początkowego, i tam twój program ulega awarii z powodu zwracania NaN przez lambda z sąsiedztwa.
PlasmaHH
@PlasmaHH Umysł udostępniasz obrazę?
Ell
Karmiłem to zdjęciami z wakacji ... Czysta czerń 400x400 też „podstępem”.
PlasmaHH
@PlasmaHH Cóż, czysto czarny obraz ma niezdefiniowany wynik, zero od zera, co byłoby NaN. Nie powinno to jednak wpływać na obliczanie sąsiedztwa ani zawieszać programu (chociaż może to zależeć od środowiska zmiennoprzecinkowego.)
Ell
Spójrz na wskaźnik o_max, if (o_neighborhood > o_max_neighborhood) o_max = *o, o_max_neighborhood = o_neighborhood;tylko ten kod go ustawia. jednak ze względu na zaangażowane nan porównanie jest zawsze fałszywe, dlatego o_max nigdy nie jest ustawiane i używane w sposób niezainicjowany.
PlasmaHH
7

Python 3, wynik = 1,57

Najpierw nasz wąż podróżuje po obrazie, tworząc pionowe linie w równej odległości od siebie.

za

Możemy rozszerzyć tego węża, biorąc dwa punkty obok siebie w linii pionowej i tworząc pętlę, której punktami końcowymi są one.

|      |
|  =>  +----+
|      +----+
|      |

Organizujemy punkty w pary i dla każdej pary przechowujemy rozmiar i średnią wartość jasności pętli, która daje największą średnią jasność.

Na każdym kroku wybieramy parę z najwyższą wartością przedłużenia pętli, aby osiągnąć maksymalną średnią jasność na przedłużeniu i obliczamy nowy optymalny rozmiar pętli i wartość jasności dla pary.

Przechowujemy trojaczki (wartość, rozmiar, para_punkt) w strukturze sterty posortowanej według wartości, dzięki czemu możemy usunąć największy element (w O (1)) i wydajnie dodać nowy zmodyfikowany (w O (log n)).

Zatrzymujemy się, gdy osiągniemy limit liczby pikseli, a ten wąż będzie ostatnim wężem.

Odległość między pionowymi liniami ma bardzo niewielki wpływ na wyniki, dlatego wybrano stałą 40 pikseli.

Wyniki

swirl    1.33084397946
chaos    1.76585674741
fractal  1.49085737611
bridge   1.42603926741
balls    1.92235115238
scream   1.48603818637
----------------------
average  1.57033111819

za za za za za za

Uwaga: oryginalne zdjęcie „Krzyk” nie było dostępne, więc użyłem innego obrazu „Krzyk” o podobnej rozdzielczości.

Gif przedstawiający proces rozciągania węża na obrazie „wirowania”:

za

Kod pobiera jedną (lub więcej oddzielonych spacjami) nazwę pliku ze standardowego wejścia i zapisuje powstałe obrazy węża do plików png i drukuje wyniki na standardowe wyjście.

from PIL import Image
import numpy as np
import heapq as hq

def upd_sp(p,st):
    vs,c=0,0
    mv,mp=-1,0
    for i in range(st,gap):
        if p[1]+i<h:
            vs+=v[p[0],p[1]+i]+v[p[0]+1,p[1]+i]
            c+=2
            if vs/c>mv:
                mv=vs/c
                mp=i
    return (-mv,mp)

mrl=[]
bf=input().split()

for bfe in bf:
    mr,mg=0,0    
    for gap in range(40,90,1500):

        im=Image.open(bfe)
        im_d=np.asarray(im).astype(int)

        v=im_d[:,:,0]+im_d[:,:,1]+im_d[:,:,2]

        w,h=v.shape

        fp=[]
        sp=[]
        x,y=0,0
        d=1

        go=True
        while go:
            if 0<=x+2*d<w:
                fp+=[(x,y)]
                fp+=[(x+d,y)]
                sp+=[(x-(d<0),y)]
                x+=2*d
                continue
            if y+gap<h:
                for k in range(gap):
                    fp+=[(x,y+k)]
                y+=gap
                d=-d
                continue
            go=False

        sh=[]
        px=im.load()

        pl=[]

        for p in fp:
            pl+=[v[p[0],p[1]]]
            px[p[1],p[0]]=(0,127,0)   

        for p in sp:
            mv,mp=upd_sp(p,1)
            if mv<=0:
                hq.heappush(sh,(mv,1,mp+1,p))

        empty=False
        pleft=h*w//3
        pleft-=len(fp)
        while pleft>gap*2 and not empty:

            if len(sh)>0:
                es,eb,ee,p=hq.heappop(sh)
            else:
                empty=True
            pleft-=(ee-eb)*2

            mv,mp=upd_sp(p,ee)
            if mv<=0:
                hq.heappush(sh,(mv,ee,mp+1,p))    

            for o in range(eb,ee):
                pl+=[v[p[0],p[1]+o]]
                pl+=[v[p[0]+1,p[1]+o]]
                px[p[1]+o,p[0]]=(0,127,0)   
                px[p[1]+o,p[0]+1]=(0,127,0)

        pl+=[0]*pleft

        sb=sum(pl)/len(pl)
        ob=np.sum(v)/(h*w)

        im.save(bfe[:-4]+'snaked.png')

        if sb/ob>mr:
            mr=sb/ob
            mg=gap

    print(bfe,mr)
    mrl+=[mr]

print(sum(mrl)/len(mrl))
randomra
źródło
5

Python 2 (wynik: 0,0797116)

Po prostu bardzo prosty i naiwny chciwy algorytm, aby uruchomić piłkę.

#!/usr/bin/python

from PIL import Image

OFFSETS = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def test_img(filename):
    img = Image.open(filename)

    joe, eaten = (0, 0), []
    img_w, img_h = img.size
    all_pixels = [
        sum(img.getpixel((x, y)))
        for x in xrange(img_w)
        for y in xrange(img_h)
    ]
    total_brightness = float(sum(all_pixels)) / len(all_pixels)

    for _ in xrange(0, (img_w*img_h)/3):
        max_offset, max_brightness = (0, 0), 0
        for o in OFFSETS:
            try:
                brightness = sum(img.getpixel((joe[0] + o[0], joe[1] + o[1])))
            except IndexError:
                brightness = -1
            if brightness >= max_brightness:
                max_offset = o
                max_brightness = brightness

        joe = (joe[0] + max_offset[0], joe[1] + max_offset[1])
        eaten.append(max_brightness)
        img.putpixel(joe, (0, 0, 0))

    eaten_brightness = float(sum(eaten)) / len(eaten)
    print('%d of %d (score %f)' % (eaten_brightness, total_brightness, eaten_brightness / total_brightness))
    img.show()

test_img('img0.jpg')
test_img('img1.png')
test_img('img2.jpg')
test_img('img3.jpg')
test_img('img4.jpg')

Wydajność:

llama@llama:~/Code/python/ppcg40069hungrysnake$ ./hungrysnake.py 
15 of 260 (score 0.060699)
9 of 132 (score 0.074200)
16 of 300 (score 0.055557)
4 of 369 (score 0.010836)
79 of 400 (score 0.197266)
Klamka
źródło
1
Myślę, że twoja ocena jest wyłączona. Jest to średnia jasność Joe zjedzona ponad średnią jasność całego zdjęcia ... Losowy Joe powinien uzyskać wynik około 1
Stretch Maniac
@StretchManiac Hmm, nie widzę nic złego. sum of brightnesses of eaten pixels / amount of eaten pixelsczy właściwa formuła, prawda? Może to po prostu straszny algorytm;)
Klamka
@Doorknob it isThe average brightness of pixels Joe eats / The average brightness of the pixels in the picture*
hmatt1
Dla pomarańczowego i czarnego swirly (nie niebieskiego) mam średnią jasność 68,0846 ... Co wydaje się nie pasować do żadnej z twoich. Być może suma (getPixel (...)) nie zwraca tego, co chcesz? (Jestem trochę nowicjuszem w Pythonie). BTW, jest 6 zdjęć, ale masz tylko 5 wyjść. Czy mógłbyś też jakoś oznaczyć zdjęcia?
Stretch Maniac
@StretchManiac Jak obliczasz swoją jasność? Mój po prostu sumuje wartości R, G i B. Przepraszam, tęskniłem za swirly, który przychodzi od drugiego do ostatniego (chyba, że ​​wspomniałeś w swoim komentarzu). Dodam to rano (muszę teraz spać).
Klamka
5

Java (wynik: 0,6949)

Prosty algorytm, który zjada najjaśniejszy piksel z czterech pikseli otaczających aktualny piksel. W przypadku remisu zjedzony piksel jest losowy, co prowadzi do różnych wyników i obrazów wynikowych przy każdym wykonaniu. Jako takie, poniższe wyniki są średnimi z ponad 50 wykonań na każdym obrazie.

Aby go uruchomić, albo edytuj trzy argumenty (istniejące jako stałe klasy) w źródle lub przekaż je za pomocą wiersza poleceń w postaci, w java HungryImageSnake <source> <iterations> <printScores>której <source>znajduje się plik źródłowy obrazu do zjedzenia, <iterations>czyli ile razy zjesz obraz (biorąc średni wynik i zapisanie najlepszego wyniku ze wszystkich iteracji), i <printScores>prawdą jest wydrukowanie wyniku każdej iteracji lub fałsz, aby nie.

import javax.imageio.ImageIO;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class HungryImageSnake {

    private static final String SOURCE = "tornado.jpg";
    private static final int ITERATIONS = 50;
    private static final boolean PRINT_SCORES = true;

    public static void main(String[] args) {
        try {
            String source = args.length > 0 ? args[0] : SOURCE;
            int iterations = args.length > 1 ? Integer.parseInt(args[1]) : ITERATIONS;
            boolean printScores = args.length > 2 ? Boolean.parseBoolean(args[2]) : PRINT_SCORES;

            System.out.printf("Reading '%s'...%n", source);
            System.out.printf("Performing %d meals...%n", iterations);
            BufferedImage image = ImageIO.read(new File(source));
            double totalScore = 0;
            double bestScore = 0;
            BufferedImage bestImage = null;
            for (int i = 0; i < iterations; i++) {
                HungryImageSnake snake = new HungryImageSnake(image);
                while (snake.isHungry()) {
                    snake.eat();
                }
                double score = snake.getScore();
                if (printScores) {
                    System.out.printf("    %d: score of %.4f%n", i + 1, score);
                }
                totalScore += score;
                if (bestImage == null || score > bestScore) {
                    bestScore = score;
                    bestImage = snake.getImage();
                }
            }
            System.out.printf("Average score: %.4f%n", totalScore / iterations);
            String formattedScore = String.format("%.4f", bestScore);
            String output = source.replaceFirst("^(.*?)(\\.[^.]+)?$", "$1-b" + formattedScore + "$2");
            ImageIO.write(bestImage, source.substring(source.lastIndexOf('.') + 1), new File(output));
            System.out.printf("Wrote best image (score: %s) to '%s'.%n", bestScore, output);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private int x;
    private int y;
    private int movesLeft;
    private int maximumMoves;
    private double eatenAverageBrightness;
    private double originalAverageBrightness;
    private BufferedImage image;

    public HungryImageSnake(BufferedImage image) {
        this.image = copyImage(image);
        int size = image.getWidth() * image.getHeight();
        this.maximumMoves = size / 3;
        this.movesLeft = this.maximumMoves;
        int totalBrightness = 0;
        for (int x = 0; x < image.getWidth(); x++) {
            for (int y = 0; y < image.getHeight(); y++) {
                totalBrightness += getBrightnessAt(x, y);
            }
        }
        this.originalAverageBrightness = totalBrightness / (double) size;
    }

    public BufferedImage getImage() {
        return image;
    }

    public double getEatenAverageBrightness() {
        return eatenAverageBrightness;
    }

    public double getOriginalAverageBrightness() {
        return originalAverageBrightness;
    }

    public double getScore() {
        return eatenAverageBrightness / originalAverageBrightness;
    }

    public boolean isHungry() {
        return movesLeft > 0;
    }

    public void eat() {
        if (!isHungry()) {
            return;
        }
        int[][] options = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        shuffleArray(options); // prevent snake from getting stuck in corners
        int[] bestOption = null;
        int bestBrightness = 0;
        for (int[] option : options) {
            int optionX = this.x + option[0];
            int optionY = this.y + option[1];
            if (exists(optionX, optionY)) {
                int brightness = getBrightnessAt(optionX, optionY);
                if (bestOption == null || brightness > bestBrightness) {
                    bestOption = new int[]{ optionX, optionY };
                    bestBrightness = brightness;
                }
            }
        }

        image.setRGB(bestOption[0], bestOption[1], 0);
        this.movesLeft--;
        this.x = bestOption[0];
        this.y = bestOption[1];
        this.eatenAverageBrightness += bestBrightness / (double) maximumMoves;
    }

    private boolean exists(int x, int y) {
        return x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight();
    }

    private int getBrightnessAt(int x, int y) {
        int rgb = image.getRGB(x, y);
        int r = (rgb >> 16) & 0xFF;
        int g = (rgb >> 8) & 0xFF;
        int b = rgb & 0xFF;
        return r + g + b;
    }

    private static <T> void shuffleArray(T[] array) {
        Random random = new Random();
        for (int i = array.length - 1; i > 0; i--) {
            int index = random.nextInt(i + 1);
            T temp = array[index];
            array[index] = array[i];
            array[i] = temp;
        }
    }

    private static BufferedImage copyImage(BufferedImage source){
        BufferedImage b = new BufferedImage(source.getWidth(), source.getHeight(), source.getType());
        Graphics g = b.getGraphics();
        g.drawImage(source, 0, 0, null);
        g.dispose();
        return b;
    }
}

Średnie wyniki, według obrazu, z ponad pięćdziesięciu iteracji:

Bridge - 0.7739
Spheres - 0.5580
Scream - 0.8197
Fractal - 0.3850
Vortex - 0.9158
Tornado - 0.7172

Najlepsze wyniki, według obrazu, z tych samych pięćdziesięciu iteracji:

Bridge - 0.8996
Spheres - 0.8741
Scream - 0.9183
Fractal - 0.5720
Vortex - 1.1520
Tornado - 0.9281

Obrazy z najwyższą liczbą punktów:

Most - 0,8996 Kulki - 0,8741 Krzyk - 0,9183 Fraktal - 0,5720 Vortex - 1.1520 Tornado - 0,9281

Jak widać na zdjęciach, w rzeczywistości zjadana jest znacznie mniej niż jedna trzecia pikseli, ponieważ wąż czasami utknął wśród pikseli, które już zjadł, w których utknął w martwym obszarze, dopóki losowość jego ruchów nie doprowadzi do jadalna część obrazu.

Również w wyniku ponownego zjadania pikseli przez węża wyniki są tendencyjne w dół, ponieważ zerowa jasność martwych pikseli jest ponownie uwzględniana w średniej. Oczekiwałbym, że zobaczę znacznie wyższe wyniki, gdyby zmodyfikowano algorytm oceniania, aby podzielić tylko liczbę ruchów, które doprowadziły do ​​zjedzenia nowych pikseli, a nie wszystkie ruchy (w tym te, w których wąż zjada martwy piksel, który zjadł wcześniej ).

Oczywiście lepszym rozwiązaniem byłoby stworzenie heurystycznego poziomu jasności dla każdego piksela i znalezienie ścieżki width * height / 3 pikseli o najwyższej średniej jasności, ale wątpię, aby to podejście było optymalne w czasie wykonywania, szczególnie na większych obrazach, ponieważ liczba możliwych permutacji byłby bardzo duży. Mogę później wypróbować jakąś formę tego podejścia i opublikować ją w osobnej odpowiedzi, jeśli tak.

FThompson
źródło
Jeśli komuś przeszkadza, jak duża jest moja odpowiedź (ze względu na zawarte w nim obrazy), daj mi znać, a usunę obrazy na korzyść linków lub innej mniej spacyjnej alternatywy. Lub, jeśli ktoś chciałby oryginałów w pełnej rozdzielczości jakichkolwiek „zjedzonych” obrazów, daj mi również znać w komentarzu.
FThompson
4

Python 2, wynik: 1,205

Jakiś czas temu przygotowałem szybkie rozwiązanie Python, ale zapomniałem je opublikować. Oto jest Znajduje najbogatsze bloki na obrazie, a następnie podróżuje do każdego bloku, zjadając wszystkie najlepsze bloki, do których dochodzi.

Wyniki

bridge.jpg: 591.97866/515.41501 =                               1.14855 
BallsRender.png: 493.24711/387.80635 =                          1.27189 
Mandel_zoom_04_seehorse_tail.jpg: 792.23990/624.60579 =         1.26838 
Red_Vortex_Apophysis_Fractal_Flame.jpg: 368.26121/323.08463 =   1.13983 
The_Scream.jpg: 687.18565/555.05221 =                           1.23806
swirl.jpg: 762.89469/655.73767 =                                1.16341

AVERAGE                                                         1.205

Przykładowe zdjęcie

Obraz przetworzonego mostu

Kod Python 2.7

from pygame.locals import *
import pygame, sys, random

fn = sys.argv[1]

screen = pygame.display.set_mode((1900,1000))
pic = pygame.image.load(fn)
pic.convert()
W,H = pic.get_size()
enough = W*H/3
screen.blit(pic, (0,0))

ORTH = [(-1,0), (1,0), (0,-1), (0,1)]
def orth(p):
    return [(p[0]+dx, p[1]+dy) for dx,dy in ORTH]

def look(p):
    x,y = p
    if 0 <= x < W and 0 <= y < H:
        return sum(pic.get_at(p))
    else:
        return -1

# index picture
locs = [(x,y) for x in range(W) for y in range(H)]
grid = dict( (p,sum(pic.get_at(p))) for p in locs )
rank = sorted( grid.values() )
median = rank[ len(rank)/2 ]
dark = dict( (k,v) for k,v in grid if v < median )
good = dict( (k,v) for k,v in grid if v > median )
pictotal = sum(rank)
picavg = 1.0 * pictotal/(W*H)
print('Indexed')

# compute zone values:
block = 16
xblocks, yblocks = (W-1)/block, (H-1)/block
zones = dict( ((zx,zy),0) for zx in range(xblocks) for zy in range(yblocks) )
for x,y in locs:
    div = (x/block, y/block)
    if div in zones:
        colsum = sum( pic.get_at((x,y)) )
        zones[div] += colsum

# choose best zones:
zonelist = sorted( (v,k) for k,v in zones.items() )
cut = int(xblocks * yblocks * 0.33)
bestzones = dict( (k,v) for v,k in zonelist[-cut:] )

# make segment paths:
segdrop = [(0,1)] * (block-1)
segpass = [(1,0)] * block
segloop = [(0,-1)] * (block-1) + [(1,0)] + [(0,1)] * (block-1)
segeat = ( segloop+[(1,0)] ) * (block/2)
segshort = [(1,0)] * 2 + ( segloop+[(1,0)] ) * (block/2 - 1)

def segtopath(path, seg, xdir):
    for dx,dy in seg:
        x,y = path[-1]
        newloc = (x+dx*xdir, y+dy)
        path.append(newloc)

# design good path:
xdir = 1
path = [(0,0)]
segtopath(path, segdrop, xdir)
shortzone = True
while True:
    x,y = path[-1]
    zone = (x/block, y/block)
    if zone in bestzones:
        if shortzone:
            seg = segshort
        else:
            seg = segeat
    else:
        seg = segpass
    segtopath(path, seg, xdir)
    shortzone = False
    # check end of x block run:
    x,y = path[-1]
    zone = (x/block, y/block)
    if not ( 0 <= x < xblocks*block ):
        del path[-1]
        segtopath(path, segdrop, xdir)
        shortzone = True
        xdir = xdir * -1
    if len(path) > enough:
        break
print('Path Found')

# show path on picture:
loc = path.pop(0)
eaten = 1
joetotal = grid[loc]
i = 0
while eaten <= enough:
    loc = path[i]
    i += 1
    pic.set_at(loc, (0,0,0))
    joetotal += grid[loc]
    eaten += 1
    if i % 1000 == 0:
        screen.blit(pic, (0,0))
        pygame.display.flip()
        for event in pygame.event.get():
            if event.type == QUIT: sys.exit(0)

# save picture and wait:
screen.blit(pic, (0,0))
pygame.display.flip()
pygame.image.save(pic, 'output/'+fn)
joeavg = 1.0 * joetotal/eaten
print '%s: %7.5f/%7.5f = %7.5f' % (fn, joeavg, picavg, joeavg/picavg)
while True:
    for event in pygame.event.get():
        if event.type == QUIT: sys.exit(0)
Logic Knight
źródło