Đi và làm cho nó đầy sao


14

Trong cuộc thi này, bạn phải viết một chương trình, chấp nhận hình ảnh pixel đen trắng và cố gắng thay đổi nó, sao cho hình dạng màu trắng tạo thành miền sao , với càng ít thay đổi càng tốt.

Những thay đổi được phép là biến pixel trắng thành đen và biến pixel đen thành trắng.

Đầu ra phải một lần nữa bao gồm cùng một hình ảnh nhưng lần này với tất cả các thay đổi và với một / trung tâm được đánh dấu. Các pixel được thay đổi từ trắng sang đen phải được hiển thị màu xanh lam, các pixel được thay đổi từ đen sang trắng phải được hiển thị màu vàng và ít nhất một pixel ở giữa phải được hiển thị màu đỏ. (Màu sắc chính xác tùy thuộc vào bạn.) Chương trình phải hiển thị hình ảnh được chỉ định cũng như tổng số thay đổi đã được thực hiện.

Các định nghĩa

Tên miền sao

Tập hợp các pixel trắng của hình ảnh đại diện cho một miền sao nếu (và chỉ khi) có (ít nhất) một pixel trung tâm . Các trung tâm điểm ảnh là một trong những điểm ảnh màu trắng có thể được conneced bởi một đường thẳng cho tất cả các điểm ảnh màu trắng khác như vậy mà dòng chỉ đi qua các điểm ảnh màu trắng. ( Do đó, pixel trung tâm không nhất thiết phải là duy nhất.)

Đường thẳng giữa hai pixel

Cho hai pixel (bắt đầu và kết thúc, cả hai màu đỏ trong hình minh họa bên dưới), đường thẳng giữa hai pixel bao gồm tất cả các pixel, chạm vào đường (toán học, màu vàng trong hình minh họa bên dưới) dẫn từ trung tâm của điểm đầu tiên pixel đến trung tâm của pixel cuối cùng. Một pixel không chạm vào dòng nếu nó chỉ chạm vào một góc, do đó, để một pixel thuộc về dòng pixel, dòng (toán học, màu vàng) phải vượt qua pixel có chiều dài khác nhau. (Nếu nó chỉ chạm vào điểm góc thì đây được coi là độ dài bằng không). Hãy xem xét các ví dụ sau:

điểm ảnh

Thí dụ

Hình ảnh đầu tiên phải đại diện cho một ví dụ thử nghiệm 'đầu vào' và hai hình ảnh khác đại diện cho hai đầu ra hợp lệ có thể cho ví dụ đã cho:

ví dụ kiểm tra giải pháp ví dụ đầu tiên giải pháp ví dụ thứ hai

Các khu vực màu vàng (trước đây là màu đen) cũng được tính vào miền 'trắng', trong khi các khu vực màu xanh lam (trước đây là màu trắng) được tính vào phần 'đen' bên ngoài miền và mỗi chấm đỏ đại diện cho một pixel trung tâm có thể.

Các trường hợp thử nghiệm

Các trường hợp kiểm tra theo dõi là png với mỗi kích thước 256 x 256 pixel.

trường hợp kiểm tra 1 trường hợp thử nghiệm 2 trường hợp thử nghiệm 3 trường hợp thử nghiệm 4 trường hợp thử nghiệm 5 trường hợp kiểm tra 6

Chấm điểm

Vui lòng chạy chương trình của bạn với các trường hợp kiểm tra sau và bao gồm đầu ra (hình ảnh / số lượng thay đổi) trong câu trả lời của bạn. Tôi sẽ làm một bảng thành tích cho mỗi trường hợp thử nghiệm. Điểm của bạn sẽ là tổng của mỗi thứ hạng trong bảng xếp hạng - điểm càng thấp càng tốt. Tiêu chuẩn áp dụng. Không được phép làm cho chương trình nhận ra các trường hợp thử nghiệm đó và chạy trường hợp đặc biệt cho chúng. (Không được phép tính toán trước và lưu các pixel trung tâm tối ưu cho từng trường hợp thử nghiệm đó.) Chương trình sẽ hoạt động với mọi hình ảnh.

Bảng xếp hạng

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
Chẳng hạn như bạn sẽ giải thích được Hình 1. Tại sao bạn lại kết nối các pixel màu đỏ?
DavidC

4
Tôi thực sự không chắc ý của bạn là gì. Bạn có thể đưa ra trước và sau một trong những trường hợp thử nghiệm của bạn?

Làm thế nào gần một dòng phải đến một góc pixel để nó được coi là đi qua?
TheNumberOne

Tôi đã thêm một số ví dụ và cố gắng làm rõ văn bản, tôi hy vọng nó đã rõ ràng ngay bây giờ!
flawr

Có ai khác có ý định thực hiện thử thách này không? Tôi hơi bối rối, vì khá nhiều người đã thực hiện thử thách này nhưng cho đến nay chúng tôi chỉ có một câu trả lời (không nghiêm trọng lắm). Có chỉ trích nào không?
flawr

Câu trả lời:


4

Java 8, 47.867 thay đổi tổng số.

Sử dụng trung bình của hình ảnh là điểm trung tâm. Sau đó, nó thu hút tất cả các tia có thể vào trung tâm và cho nó bán kính tốt nhất để tô màu. Sau đó, màu sắc tất cả các điểm không hợp lệ màu đen.

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

Các kết quả

Hình ảnh 1 - 0 thay đổi, Hình ảnh 2 - 13.698 thay đổi

12

Hình ảnh 3 - 24.269 thay đổi, Hình ảnh 4 - 103 thay đổi

34

Hình ảnh 5 - 5.344 thay đổi, Hình ảnh 6 - 4.456 thay đổi

56

Không có pixel không hợp lệ bị xóa, tổng số 42.782 thay đổi

Pixel màu xanh lá cây là lớp đầu tiên của pixel không hợp lệ.

Hình ảnh 1 - 0 thay đổi, Hình ảnh 2- 9,889 thay đổi

12

Hình ảnh 3 - 24.268 thay đổi, Hình ảnh 4 - 103 thay đổi

34

Hình ảnh 5 - 4.471 thay đổi, Hình ảnh 6- 4.050 thay đổi

56

Tất cả các pixel trắng trong tất cả các hình ảnh có thể có một đường được vẽ cho chúng từ pixel trung tâm nếu đường đó không phải xuất phát / kết thúc tại tâm mà thay vào đó là bất cứ nơi nào trên pixel.

args[0] chứa tên tập tin đầu vào.

args[1] chứa tên tập tin đầu ra.

In theo stdoutsố lượng thay đổi.


Trông rất tuyệt! Bạn có thể giải thích ý của bạn về 'pixel không hợp lệ' không? Tôi đã không hiểu điều đó. Ngoài ra, trong hình ảnh 2 ở dưới cùng bên phải, tôi không thể theo dõi lý do tại sao chương trình của bạn 'đào' vào bức tường màu đen nhưng sau đó vẫn tô màu các chấm trắng lại màu đen, nhưng tôi nghĩ điều này có liên quan đến các pixel'invalid không?
flawr

Một vài pixel không hợp lệ gây ra hiệu ứng xếp tầng làm cho nhiều điểm không hợp lệ hơn. Tôi sẽ sửa đổi một vài hình ảnh cuối cùng để hiển thị lớp pixel đầu tiên không hợp lệ thành màu xanh lá cây.
TheNumberOne

3

Python - PIL - 216.228 108.363 thay đổi tổng

Ôi! Giảm một nửa nhờ @AJMansfield! Thuật toán này bỏ qua tất cả những điều đáng lo ngại với việc tính toán các dòng và tối ưu hóa và những gì không. Nó chỉ thay đổi tất cả người da trắng thành màu đen ngoại trừ một. Nếu không có người da trắng, nó làm cho một màu đen thành màu trắng.Nó kiểm tra xem có nhiều người da trắng hay người da đen hay không và thay đổi từng người khác ngoại trừ một người khác. Nếu không có màu đen, nó làm cho (0, 0) là trung tâm.

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

Các kết quả

Hình ảnh 1 - 28688 thay đổi, Hình ảnh 2 - 24208 thay đổi

Hình ảnh 3 - 24248 thay đổi, Hình ảnh 4 - 7103 thay đổi

Hình ảnh 5 - 11097 thay đổi, Hình ảnh 6 - 13019 thay đổi

Lấy tên tệp từ raw_input và ghi vào out.png và in số lượng thay đổi.


Lưu ý rằng các pixel được thay đổi từ đen sang trắng sẽ có màu vàng trong đầu ra của bạn. Thos đã được thay đổi từ màu trắng sang màu đen nên có màu xanh lam và trung tâm (trong trường hợp của bạn, pixel 'trắng' duy nhất của bạn sẽ có màu đỏ trong đầu ra của bạn. Khác với điều đó, cảm ơn bạn đã tham gia =) PS: Luôn luôn có thể tạo một miền sao, ngay cả khi bạn có một hình ảnh đen đầy đủ làm đầu vào, bạn có thể thay đổi một pixel thành màu trắng (đỏ).
flawr

Có thể là không thể nếu không có pixel trắng hoặc đen (tức là màu đầy đủ). Trong mọi trường hợp tôi đang thực hiện các thay đổi khác.
Maltysen

Oh. Hình ảnh đen trắng. Lỗi của tôi.
Maltysen

Tôi nghĩ rằng có thể hiệu quả hơn khi thực hiện chiến lược ngược lại và thay đổi tất cả các pixel đen thành các pixel trắng. Bạn đã thử chưa?
AJMansfield

@AJMansfield Tôi nghĩ rằng điều này sẽ chỉ hiệu quả hơn đối với trường hợp thử nghiệm nhất định, vì vậy có lẽ điều này có thể được coi là điều chỉnh thuật toán cho các thử nghiệm đã cho.
flawr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.