가서 별이되게


14

이 콘테스트에서는 흑백 픽셀 이미지를 허용하고 가능한 한 적은 변화로 흰색 모양이 스타 도메인을 형성하도록 프로그램을 작성해야 합니다.

허용되는 변경 사항은 흰색 픽셀을 검은 색 픽셀로 바꾸고 검은 색 픽셀을 흰색 픽셀로 바꿉니다.

출력은 다시 동일한 이미지로 구성되어야하지만 이번에는 모든 변경 사항과 a /가 표시된 상태로 표시됩니다. 흰색에서 검은 색으로 변경된 픽셀은 파란색으로 표시되어야하고, 검은 색에서 흰색으로 변경된 픽셀은 노란색으로 표시되어야하며 하나 이상의 중앙 픽셀은 빨간색으로 표시되어야합니다. (정확한 색상은 사용자에게 달려 있습니다.) 프로그램은 지정된 총 이미지 수와 변경 한 총 수를 출력해야합니다.

정의

스타 도메인

이미지의 흰색 픽셀 세트 는 (적어도) 하나의 중심 픽셀 이있는 경우에만 스타 도메인을 나타냅니다 . 중심 화소는 a로 conneced 수 백 화소 중 하나 의 직선 라인에만 흰 화소를 통과하도록 다른 화이트 모든 화소. (따라서 중앙 픽셀 은 고유하지 않아도됩니다.)

두 픽셀 사이의 직선

두 개의 픽셀 (아래 그림에서 시작과 끝, 둘 다 빨간색)이 주어지면 두 픽셀 사이 의 스트 래그 스 라인 은 모든 픽셀로 구성되며 첫 번째의 중심에서 이어지는 (아래 그림에서 수학, 노란색) 라인에 닿습니다. 마지막 픽셀의 중심에 픽셀. 픽셀이 모퉁이 만 만지면 선에 닿지 않으므로 픽셀이 픽셀 선 에 속 하려면 (수학적, 노란색) 선이 해당 픽셀을 가로 질러 길이가 0이 아니어야합니다. 모퉁이 점에만 닿으면 길이가 0으로 간주됩니다. 다음 예를 고려하십시오.

픽셀

첫 번째 이미지는 예제 테스트 케이스 'input'을 나타내며 다른 두 이미지는 주어진 예제에 대해 두 가지 유효한 출력을 나타냅니다.

테스트 사례 첫 번째 예제 솔루션 두 번째 예제 솔루션

노란색 영역 (이전에는 검은 색)도 '백색'도메인에 포함되며, 파란색 영역 (이전에는 흰색)은 도메인 외부의 '검은 색'에 포함되며 매번 빨간색 점은 하나의 가능한 중심 픽셀을 나타냅니다.

테스트 사례

다음 테스트 사례는 각각 크기가 256 x 256 픽셀 인 png입니다.

테스트 사례 1 테스트 사례 2 테스트 사례 3 테스트 사례 4 테스트 사례 5 테스트 사례 6

채점

다음 테스트 사례로 프로그램을 실행하고 답변에 출력 (이미지 / 변경 횟수)을 포함 시키십시오. 각 테스트 케이스마다 리더 보드를 만들 것입니다. 점수는 순위표에서 각 순위의 합계가됩니다. 점수가 낮을수록 좋습니다. 표준 허점이 적용됩니다. 프로그램이 해당 테스트 케이스를 인식하고 특수한 경우를 실행할 수 없습니다. (각 해당 테스트 사례에 대해 최적의 중앙 픽셀을 사전 계산하고 저장할 수 없습니다.) 프로그램은 모든 이미지에 대해 작동해야합니다.

리더 보드

Name        | Score | 1     - rk | 2     - rk | 3     - rk | 4     - rk | 5     - rk | 5     - rk | Total Changes
------------+-------+------------+------------+------------+------------+------------+------------+--------------
Maltysen    |    11 | 28688 -  2 | 24208 -  2 | 24248 -  1 |  7103 -  2 | 11097 -  2 | 13019 -  2 | 108363
TheBestOne  |     7 | 0     -  1 | 13698 -  1 | 24269 -  2 |   103 -  1 |  5344 -  1 |  4456 -  1 |  47867  

2
그림 1을 설명하면 도움이 될 것입니다. 예를 들어 왜 적색 픽셀을 연결합니까?
DavidC

4
무슨 말인지 잘 모르겠습니다. 테스트 사례 중 하나 전후에 줄 수 있습니까?

통과하는 것으로 간주 되려면 선이 픽셀 모서리에 얼마나 가까워 야합니까?
TheNumberOne

나는 몇 가지 예를 추가하고 텍스트를 명확히하려고 노력했다.
flawr

이 도전을 시도하려는 다른 사람이 있습니까? 꽤 많은 사람들이이 도전에 찬성했지만 지금까지 한 가지만 (심각하지 않은) 답변을 얻었 기 때문에 다소 혼란 스러웠습니다. 비판이 있습니까?
flawr

답변:


4

Java 8, 47,867은 총 변경됩니다.

이미지의 평균을 중심점으로 사용합니다. 그런 다음 가능한 모든 광선을 중앙으로 끌어 내고 색상에 가장 적합한 반경을 제공합니다. 그런 다음 모든 유효하지 않은 점을 검은 색으로 채색합니다.

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class MakeItStarry {

    private static final int RGB_RED = Color.RED.getRGB();
    static int[][] originalImage;

    static final int WHITE = 0;
    static final int BLACK = 1;
    static final int RGB_WHITE = Color.WHITE.getRGB();
    static final int RGB_BLACK = Color.BLACK.getRGB();
    static final int RGB_BLUE = Color.BLUE.getRGB();
    static final int RGB_YELLOW = Color.YELLOW.getRGB();

    public static void main(String[] args) throws Exception{
        originalImage = convert(ImageIO.read(new File(args[0])));
        Point center = findCenter(originalImage);
        int[][] nextImage = starry(originalImage, center);
        BufferedImage result = difference(originalImage, nextImage);
        result.setRGB(center.x, center.y, RGB_RED);
        String fileType;
        String fileName;
        if (args[1].split("\\.").length > 1){
            fileType = args[1].split("\\.")[1];
            fileName = args[1];
        } else {
            fileType = "PNG";
            fileName = args[1] + ".PNG";
        }
        ImageIO.write(result, fileType, new File(fileName));
        System.out.println(cost);
    }

    static int cost;

    private static BufferedImage difference(int[][] image1, int[][] image2) {
        cost = 0;
        int height = image1[0].length;
        int width = image1.length;
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++){
            for (int y = 0; y < width; y++){
                if (image1[x][y] == image2[x][y]){
                    if (image1[x][y] == WHITE){
                        result.setRGB(x, y, RGB_WHITE);
                    } else {
                        result.setRGB(x, y, RGB_BLACK);
                    }
                } else {
                    cost++;
                    if (image1[x][y] == WHITE){
                        result.setRGB(x, y, RGB_BLUE);
                    } else {
                        result.setRGB(x, y, RGB_YELLOW);
                    }
                }
            }
        }
        return result;
    }

    private static int[][] starry(int[][] image, Point center) {
        int width = image.length;
        int height = image[0].length;
        int[][] result = new int[width][height];
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                result[x][y] = BLACK;
            }
        }
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++) {
                Point endPoint = new Point(x, y, image);
                List<Point> line = Point.lineTo(center, endPoint, image);
                List<Point> newLine = starRay(line);
                newLine.stream().filter(point -> result[point.x][point.y] == BLACK).forEach(point -> {
                    result[point.x][point.y] = point.color;
                });
            }
        }
        int distance = 0;
        while (distance < height || distance < width){//This removes pixels that can't see the center.
            for (int x = Math.max(center.x - distance,0); x < center.x + distance && x < width; x++){
                for (int y = Math.max(center.y - distance, 0); y < center.y + distance && y < height; y++){
                    Point point = new Point(x, y, result);
                    if (Point.distance(center, point) != distance){
                        continue;
                    }
                    if (point.color == WHITE){
                        List<Point> line = Point.lineTo(center, point, result);
                        for (Point p : line){
                            if (p.color == BLACK){
                                point.color = BLACK;
                                break;
                            }
                        }
                        result[point.x][point.y] = point.color;
                    }
                }
            }//All white pixels can technically see the center but only if looking from the edge.
            distance++;
        }
        return result;
    }

    private static List<Point> starRay(List<Point> line) {
        int numOfWhites = 0;
        int farthestGoodPoint = 0;
        int blackCost = 0;
        int whiteCost = 0;
        for (int i = 0; i < line.size(); i++){
            if (line.get(i).color == WHITE){
                numOfWhites++;
                whiteCost++;
                if (numOfWhites + whiteCost > blackCost){
                    blackCost = 0;
                    whiteCost = 0;
                    farthestGoodPoint = i;
                }
            } else {
                blackCost++;
                numOfWhites = 0;
            }
        }
        List<Point> result = new ArrayList<>();
        for (int i = 0; i < line.size(); i++){
            Point p = line.get(i);
            if (i <= farthestGoodPoint){
                result.add(new Point(p.x, p.y, WHITE));
            } else {
                result.add(new Point(p.x, p.y, BLACK));
            }
        }
        return result;
    }

    private static Point findCenter(int[][] image) {
        double totalx = 0;
        double totaly = 0;
        int counter = 0;
        int width = image.length;
        int height = image[0].length;
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                if (image[x][y] == WHITE){
                    totalx += x;
                    totaly += y;
                    counter++;
                }
            }
        }
        return new Point((int)(totalx/counter), (int)(totaly/counter), image);
    }

    private static int[][] convert(BufferedImage image) {
        int width = image.getWidth();
        int height  = image.getHeight();
        int[][] result = new int[width][height];
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                if (image.getRGB(x, y) == RGB_WHITE){
                    result[x][y] = WHITE;
                } else {
                    result[x][y] = BLACK;
                }
            }
        }
        return result;
    }


    private static class Point {

        public int color;
        public int y;
        public int x;

        public Point(int x, int y, int[][] image) {
            this.x = x;
            this.y = y;
            this.color = image[x][y];
        }

        public Point(int x, int y, int color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public static List<Point> lineTo(Point point1, Point point2, int[][] image) {
            List<Point> result = new ArrayList<>();
            boolean reversed = false;
            if (point1.x > point2.x){
                Point buffer = point1;
                point1 = point2;
                point2 = buffer;
                reversed = !reversed;
            }
            int rise = point1.y - point2.y;
            int run = point1.x - point2.x;
            if (run == 0){
                if (point1.y > point2.y){
                    Point buffer = point1;
                    point1 = point2;
                    point2 = buffer;
                    reversed = !reversed;
                }
                int x = point1.x;
                for (int y = point1.y; y <= point2.y; y++){
                    result.add(new Point(x, y, image));
                }
                if (reversed){
                    return reversed(result);
                }
                return result;
            }
            if (rise == 0){
                if (point1.x > point2.x){
                    Point buffer = point1;
                    point1 = point2;
                    point2 = buffer;
                    reversed = !reversed;
                }
                int y = point1.y;
                for (int x = point1.x; x <= point2.x; x++){
                    result.add(new Point(x, y, image));
                }
                if (reversed){
                    return reversed(result);
                }
                return result;
            }
            int gcd = gcd(rise, run);
            rise /= gcd;
            run /= gcd;
            double slope = (rise + 0.0) / run;
            if (Math.abs(rise) >= Math.abs(run)){
                if (point1.y > point2.y){
                    Point buffer = point1;
                    point1 = point2;
                    point2 = buffer;
                    reversed = !reversed;
                }
                double x = point1.x;
                for (double y = point1.y + .5; y <= point2.y; y++){
                    int px = (int) Math.round(x);
                    if (Math.abs(Math.abs(px - x) - .5) < Math.abs(1.0 / (rise * 4))){
                        x += 1/slope;
                        continue;
                    }
                    result.add(new Point(px, (int) Math.round(y - .5), image));
                    result.add(new Point(px, (int) Math.round(y + .5), image));
                    x += 1/slope;
                }
                if (reversed){
                    return reversed(result);
                }
                return result;
            } else {
                if (point1.x > point2.x){
                    Point buffer = point1;
                    point1 = point2;
                    point2 = buffer;
                    reversed = !reversed;
                }
                double y = point1.y;
                for (double x = point1.x + .5; x <= point2.x; x++){
                    int py = (int) Math.round(y);
                    if (Math.abs(Math.abs(py - y) - .5) < Math.abs(1.0 / (run * 4))) {
                        y += slope;
                        continue;
                    }
                    result.add(new Point((int) Math.round(x - .5), py, image));
                    result.add(new Point((int) Math.round(x + .5), py, image));
                    y += slope;
                }
                if (reversed){
                    return reversed(result);
                }
                return result;
            }
        }

        private static List<Point> reversed(List<Point> points) {
            List<Point> result = new ArrayList<>();
            for (int i = points.size() - 1; i >= 0; i--){
                result.add(points.get(i));
            }
            return result;
        }

        private static int gcd(int num1, int num2) {
            if (num1 < 0 && num2 < 0){
                return -gcd(-num1, -num2);
            }
            if (num1 < 0){
                return gcd(-num1, num2);
            }
            if (num2 < 0){
                return gcd(num1, -num2);
            }
            if (num2 > num1){
                return gcd(num2, num1);
            }
            if (num2 == 0){
                return num1;
            }
            return gcd(num2, num1 % num2);
        }

        @Override
        public String toString(){
            return x + " " + y;
        }

        public static int distance(Point point1, Point point2) {
            return Math.abs(point1.x - point2.x) + Math.abs(point1.y - point2.y);
        }
    }
}

결과

이미지 1-0 변경, 이미지 2-13,698 변경

12

이미지 3-24,269 변경, 이미지 4-103 변경

삼4

이미지 5-5,344 변경, 이미지 6-4,456 변경

56

유효하지 않은 픽셀을 제거하지 않으면 총 42,782 개의 변경 사항

녹색 픽셀은 유효하지 않은 픽셀의 첫 번째 레이어입니다.

이미지 1-0 변경, 이미지 2- 9,889 변경

12

이미지 3-24,268 변경, 이미지 4-103 변경

삼4

이미지 5-4,471 변경, 이미지 6-4,050 변경

56

선이 중심에서 시작 / 끝이 아니라 픽셀의 어느 곳에서나 시작해야하는 경우 모든 그림의 모든 흰색 픽셀에 중심 픽셀에서 선이 그려 질 수 있습니다.

args[0] 입력 파일 이름을 포함합니다.

args[1] 출력 파일 이름을 포함합니다.

stdout여러 변경 사항으로 인쇄합니다 .


좋아 보인다! '유효하지 않은 픽셀'의 의미를 설명 할 수 있습니까? 나는 그것을 이해하지 못했습니다. 또한 오른쪽 하단의 이미지 2에서 나는 왜 당신의 프로그램이 검은 벽에 'diggs'를 따르고 여전히 흰색 점을 검은 색으로 칠하는지 알 수 없었지만 이것이 '잘못된 픽셀'과 관련이 있다고 생각합니까?
flawr

유효하지 않은 소수의 픽셀은 캐스케이드 효과를 일으켜 더 많은 유효하지 않습니다. 마지막 몇 개의 이미지를 수정하여 유효하지 않은 픽셀의 첫 번째 레이어를 녹색으로 표시합니다.
TheNumberOne

3

Python-PIL- 216,228 108,363 변경 총계

우와! @AJMansfield 덕분에 반으로 줄였습니다! 이 알고리즘은 라인 계산 및 최적화에 대한 모든 걱정을 건너 뜁니다. 하나를 제외하고 모든 흰색을 검은 색으로 바꿉니다. 흰색이 없으면 검은 색을 흰색으로 만듭니다. 흰색이나 검은 색이 더 있는지 확인하고 하나를 제외한 다른 종류의 모든 것을 바꿉니다. 검은 색이 없으면 중심을 (0, 0)으로 만듭니다.

import Image
from itertools import product

img = Image.open(raw_input())
img = img.convert("RGB")

pixdata = img.load()
changed=0

m=False
count=0
for x, y in product(xrange(img.size[1]), xrange(img.size[0])):
    if pixdata[x, y]==(0, 0, 0):
        count+=1

colors=[(0, 0, 0), (255, 255, 0)] if img.size[0]*img.size[1]-count>count else [(255, 255, 255), (0, 0, 255)]
m=False
for x, y in product(xrange(img.size[1]), xrange(img.size[0])):
    if pixdata[x, y] == colors[0]:
        if m:
            pixdata[x, y] = colors[1]
        else:
            pixdata[x, y] = (255, 0, 0)
            m=True
        changed+=1

if not m:
    pixdata[0, 0]==(255, 0, 0)
    changed+=1
if colors[0]==(255, 255, 255):
    changed-=1

print changed
img.save("out.png", "PNG")

결과

이미지 1-28688 변경, 이미지 2-24208 변경

이미지 3-24248 변경, 이미지 4-7103 변경

이미지 5-11097 변경, 이미지 6-13019 변경

raw_input에서 파일 이름을 가져와 out.png에 쓰고 많은 변경 사항을 인쇄합니다.


검정에서 흰색으로 변경된 픽셀은 출력에서 ​​노란색이어야합니다. 흰색에서 검은 색으로 바뀐 색조는 파란색이어야하며 가운데 (출력의 경우 '백색'픽셀 만 빨간색이어야합니다. 그 외에는 참여해 주셔서 감사합니다 =) PS : 항상 가능해야합니다. 입력으로 풀 블랙 이미지가있는 경우에도 스타 도메인을 만들면 한 픽셀을 흰색 (빨간색)으로 변경할 수 있습니다.
flawr

흰색 또는 검은 색 픽셀 이없는 경우 (예 : 풀 컬러) 불가능할 수 있습니다 . 어쨌든 나는 다른 변경을하고 있습니다.
Maltysen

오. 흑백 이미지. 내 잘못이야.
Maltysen

나는 반대 전략을 수행하고 모든 검은 색 픽셀을 흰색으로 변경하는 것이 더 효율적이라고 생각합니다. 당신은 그것을 시도 했습니까?
AJMansfield

@ AJMansfield 나는 이것이 주어진 테스트 케이스에 대해서만 더 효율적이라고 생각하므로 아마도 주어진 테스트 케이스에 대한 알고리즘을 컨디셔닝하는 것으로 간주 될 수 있습니다.
flawr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.