去让它满天星斗


14

在本竞赛中,您必须编写一个程序,该程序接受黑白像素图像,然后尝试对其进行更改,以使白色形状形成星形域,并尽可能减少更改。

允许的更改是将白色像素变成黑色,将黑色像素变成白色。

输出必须再次包含相同的图像,但是这次所有更改都带有,并且标有a /中心。从白色变为黑色的像素必须以蓝色显示,从黑色变为白色的像素必须以黄色显示,并且至少一个中心像素必须以红色显示。(完全的颜色由您决定。)程序必须输出指定的图像以及所做的更改总数。

定义

星域

当(且仅当)存在(至少)一个中心像素时,图像的白色像素集才代表星域。的中心像素是能够由被conneced白色像素中的一个直线到所有其他白色像素的,使得线仅横穿白色像素。(因此中心像素不一定是唯一的。)

两个像素之间的直线

给定两个像素(开始和结束,在下图中均为红色),两个像素之间的直线由所有像素组成,这些像素接触从第一个像素的中心开始的(下图中的数学黄色)线像素到最后一个像素的中心。如果一个像素仅碰到一个角就不会接触该线,所以要使一个像素属于该像素线,(数学上的黄色)线必须以不为零的长度穿过所讨论的像素。(如果它仅碰触拐角点,则视为长度为零)。请考虑以下示例:

像素

对于给定的示例,第一个图像应表示示例测试用例“输入”,而其他两个图像则表示两个有效的可能输出:

示例测试用例 第一个示例解决方案 第二个示例解决方案

黄色区域(以前为黑色)也计入“白色”域,而蓝色区域(以前为白色)计入域外的“黑色”部分,红色点每次都代表一个可能的中心像素。

测试用例

以下测试用例是png,每个大小为256 x 256像素。

测试案例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 2015年

4
我不太确定你的意思。您能否在其中一个测试用例之前和之后给出?

一条线必须距离像素角多近才能通过?
TheNumberOne

我添加了一些示例,并试图澄清文本,希望现在已经清楚了!
瑕疵的

还有其他人打算尝试这一挑战吗?我有些困惑,因为很多人都对这项挑战表示反对,但是到目前为止,我们只有一个答案(不是很严重)。有什么批评吗?
flawr

Answers:


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更改

1个2

图像3-24,269项更改,图像4-103项更改

34

图像5-5,344更改,图像6-4,456更改

56

在不删除无效像素的情况下,总共更改了42782

绿色像素是无效像素的第一层。

图像1-0更改,图像2-9,889更改

1个2

图像3-24,268更改,图像4-103更改

34

图像5-4,471更改,图像6-4,050更改

56

如果所有图片中的所有白色像素不必起源于/终止于中心而是位于像素的任何位置,则可以从中心像素向其绘制一条线。

args[0] 包含输入文件名。

args[1] 包含输出文件名。

打印到stdout更改数量。


看起来很棒!您能否解释“无效像素”的含义?我不太明白。同样在右下角的图像2中,我无法理解为什么您的程序“挖”入黑墙,然后仍然将白点重新涂成黑色,但是我认为这与“无效像素”有关吗?
flawr

少数无效像素会导致级联效应,从而使更多像素失效。我将修改最后几张图像,以将无效像素的第一层显示为绿色。
TheNumberOne 2015年

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并打印更改数量。


请注意,从黑色更改为白色的像素在您的输出中应为黄色。从白色更改为黑色的Thos应该是蓝色,并且中心(在您的情况下,您的输出中唯一的“白色”像素应该是红色。除此之外,感谢您的参与=)PS:应该始终可以制作星域,即使输入全黑图像,也可以将一个像素更改为白色(红色)。
瑕疵的

如果没有白色黑色像素(即全色),则不可能。无论如何,我都会进行其他更改。
Maltysen 2015年

哦。黑白图像。我的错。
Maltysen 2015年

我认为执行相反的策略并将所有黑色像素更改为白色像素可能会更有效率。你尝试过吗?
AJMansfield

@AJMansfield我认为这对于给定的测试用例只会更有效,因此也许已经可以考虑将其作为给定测试用例的算法条件。
瑕疵的
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.