배고픈 이미지 뱀-구멍 # 3 아닌 경우 가장

홀 # 1

뱀이 배고프다.

그는 한 번에 한 픽셀 씩 그림을 먹는다.

그는 정말 밝은 픽셀을 좋아합니다.

도전

Joe가 위, 아래, 왼쪽 또는 오른쪽으로 만 움직일 수 있다는 점에서 가장 밝은 픽셀을 먹도록 프로그램하십시오.

명세서

  • Joe는 이미지의 왼쪽 상단 픽셀에서 시작해야합니다.
  • Joe는 한 번만 가로 또는 세로로만 움직일 수 있습니다
  • Joe는 그림에서 픽셀 양의 1/3을 이동할 수있는 시간이 충분합니다 (1/3는 픽셀 수만큼 이동). 픽셀 수가 3의 배수가 아닌 경우 가장 가까운 정수로 내림합니다.
  • Joe는 경로를 건너 갈 수 있지만 밝기는 0입니다.
  • 밝기는 r, g 및 b의 합을 기준으로하므로 rgb (0,0,0)의 밝기는 0이고 rgb (255,255,255)의 밝기는 최대입니다.

입력

원하는대로 이미지를 입력 할 수 있습니다.

산출

  • 사진의 최종 결과를 보여주는 이미지 (검은 색 픽셀).
  • 섭취 한 밝기의 양 (답의 범위를 지정하십시오)

채점

귀하의 프로그램은 다음에 평가됩니다 :

  • Joe가 먹는 픽셀의 평균 밝기 / 사진에서 픽셀의 평균 밝기 *

* 프로그램에서 이것을 하드 코딩 할 수 있습니다

총점은 다음 이미지의 평균 점수가됩니다.

테스트 이미지 :

여기에 이미지 설명을 입력하십시오

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



답변

C ++, 점수 : 1.42042 1.46766

이것은 본질적으로 기존의 두 가지 솔루션 중 약간 더 정교한 버전입니다. 네 가지 가능한 동작 중 밝기를 최대화하는 것을 선택합니다. 그러나 대상 픽셀의 밝기 만 보는 대신 대상 픽셀 근처에서 가중 된 픽셀 밝기의 합을보고, 대상에 더 가까운 픽셀의 가중치가 더 큽니다.

편집 : 이웃 계산에 비선형 밝기를 사용하면 점수가 약간 향상됩니다.

로 컴파일하십시오 g++ joe.cpp -ojoe -std=c++11 -O3 -lcairo. 카이로가 필요합니다.

로 실행하십시오 joe <image-file> [<radius>].
<image-file>입력 PNG 이미지입니다.
<radius>(선택적 인수)는 합산 된 이웃의 반경을 픽셀 단위로 나타냅니다 (작을수록 빠를수록 클수록 (거의) 좋습니다). 점수와 이미지를 출력합니다 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);
}

결과

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






더 눈 사탕



답변

파이썬 3, 점수 = 1.57

먼저 뱀이 이미지를 이동하여 서로 같은 거리에있는 세로선을 만듭니다.

세로줄에서 서로 옆에 두 개의 점을 찍고 끝 점이 그들 인 루프를 만들어이 뱀을 확장 할 수 있습니다.

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

점을 쌍으로 구성하고 모든 쌍에 대해 루프의 크기와 평균 밝기 값을 저장하여 평균 밝기가 가장 높습니다.

모든 단계에서 우리는 가장 높은 값을 가진 쌍을 선택하여 루프에서 확장하여 최대 평균 밝기를 얻도록하고 새로운 최적의 루프 크기와 밝기 값을 계산합니다.

(value, size, point_pair) 삼중 항을 값으로 정렬 된 힙 구조에 저장하므로 가장 큰 요소 (O (1))를 제거하고 새로운 수정 된 요소 (O (log n))를 효율적으로 추가 할 수 있습니다.

픽셀 수 제한에 도달하면 그 뱀이 마지막 뱀이됩니다.

수직선 사이의 거리는 결과에 거의 영향을 미치지 않으므로 일정한 40 픽셀이 선택되었습니다.

결과

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






참고 : 원본 “Scream”그림을 사용할 수 없었으므로 비슷한 해상도의 다른 “Scream”그림을 사용했습니다.

“소용돌이”이미지에서 뱀 확장 과정을 보여주는 GIF :

이 코드는 stdin에서 하나 이상의 공백으로 구분 된 파일 이름을 가져 와서 결과 뱀 이미지를 png 파일에 기록하고 점수를 stdout에 인쇄합니다.

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))

답변

파이썬 2 (점수 : 0.0797116)

볼 롤링을 얻기 위해 매우 간단하고 순진한 욕심 많은 알고리즘.

#!/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')

산출:

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)

답변

자바 (점수 : 0.6949)

현재 픽셀을 둘러싸고있는 4 개의 픽셀 중 가장 밝은 픽셀을 먹는 간단한 알고리즘입니다. 동점의 경우, 먹은 픽셀은 임의적이며, 점수가 다르고 매 실행마다 이미지가 달라집니다. 따라서 아래 점수는 각 이미지에서 평균 50 회 이상 실행 된 것입니다.

그것을 세 가지 인수는 소스 (클래스 상수로 기존) 중 편집을 실행하거나 형태로 명령 줄을 통해이를 전달하려면 먹을 이미지의 소스 파일, 이미지를 먹는 횟수 (복용입니다 평균 점수 및 모든 반복에서 최고 점수 저장) 및 각 반복의 점수를 인쇄하려면 true 이고, 그렇지 않으면 false입니다.java HungryImageSnake <source> <iterations> <printScores><source><iterations><printScores>

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;
    }
}

이미지를 기준으로 50 회 이상의 평균 점수 :

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

동일한 50 회 반복에서 이미지 별 최고 점수 :

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

가장 높은 점수를받은 런의 이미지 :






이미지에서 알 수 있듯이, 뱀은 이미 먹은 픽셀 사이에 때때로 붙어 있기 때문에 픽셀의 3 분의 1보다 훨씬 적게 실제로 먹습니다.이 지점에서 움직임의 임의성이 나타날 때까지 죽은 영역 내에 붙어 있습니다. 이미지의 식용 부분.

뱀이 다시 먹는 픽셀의 결과로, 데드 픽셀의 제로 밝기가 다시 평균으로 반영되기 때문에 점수가 아래로 바이어스됩니다. 스코어링 알고리즘을 수정하여 모든 움직임이 아닌 새로운 픽셀을 먹는 움직임의 수로 만 나누도록 수정하면 뱀이 이전에 먹은 죽은 픽셀을 먹는 동작을 포함하여 훨씬 더 높은 점수를 기대합니다. ).

물론 더 나은 방법은 각 픽셀에 대해 일종의 밝기 휴리스틱을 생성하고 width * height / 3평균 평균 밝기가 가장 높은 픽셀 의 경로를 찾는 것입니다 . 그러나이 방법은 런타임에서 특히 숫자가 큰 이미지에서 최적 일 것입니다. 가능한 순열의 수가 매우 클 것입니다. 나중에이 접근법의 어떤 형태로 촬영하여 별도의 답변에 게시 할 수 있습니다.


답변

파이썬 2, 점수 : 1.205

얼마 전에 빠른 Python 솔루션을 만들었지 만 게시하지 않았습니다. 여기있어. 이미지에서 가장 풍부한 블록을 찾은 다음 각 블록으로 이동하여 가장 좋은 블록을 모두 섭취합니다.

결과

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

예시 사진

파이썬 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)