行って星空にする


14

このコンテストでは、白​​黒のピクセル画像を受け入れ、変更を試みて、できるだけ少ない変更で白色の形状が星の領域を形成するようなプログラムを作成する必要があります。

許可される変更は、白いピクセルを黒いピクセルに変え、黒いピクセルを白いピクセルに変えることです。

出力も同じ画像で構成されている必要がありますが、今回はすべての変更があり、中央にマークが付いています。白から黒に変更されたピクセルは青で表示され、黒から白に変更されたピクセルは黄色で表示され、少なくとも1つの中心ピクセルは赤で表示されなければなりません。(正確な色はあなた次第です。)プログラムは、指定された画像と加えられた変更の総数を出力する必要があります。

定義

スタードメイン

画像の白いピクセルのセットは、(少なくとも)中央に 1つのピクセルがある場合(そしてその場合のみ)、スタードメインを表します中心画素は、によってconnecedすることができる白画素の1つであり、直線ラインが白画素のみを通過するように、他の白画素の全てに。(したがって、中心ピクセルは必ずしも一意ではありません。)

2つのピクセル間の直線

(開始および終了は、以下の図の両方赤)2つの画素所与、straigth線二つの画素間の全ての画素からなり、そのタッチ(数学下図で黄色)線最初の中心から導くこと最後のピクセルの中心へのピクセル。ピクセルが角に接している場合、ピクセルはラインに接触していないため、ピクセルがピクセルラインに属するためには、(数学的な、黄色の)ラインが問題のピクセルをゼロ以外の長さで横切らなければなりません。(コーナーポイントのみに触れる場合、これは長さゼロと見なされます)。以下の例を検討してください。

ピクセル数

最初の画像は、テストケースの「入力」の例を表し、他の2つの画像は、与えられた例の2つの有効な出力を表します。

テストケースの例 最初のソリューション例 2番目のソリューション例

黄色の領域(以前は黒)も「白」の領域にカウントされ、青色の領域(以前は白)は領域外の「黒」の部分にカウントされ、赤い点は毎回1つの可能な中心ピクセルを表します。

テストケース

次のテストケースは、それぞれ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

いくつかの例を追加し、テキストを明確にしようとしましたが、今は明確になっていることを願っています!
フレイ

この挑戦を試みるつもりの他の誰かがいますか?多くの人がこの挑戦に賛成したので、私はやや混乱していますが、これまでのところ1つ(あまり深刻ではない)の答えしかありません。批判はありますか?
フレイ

回答:


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件の変更

34

画像5-5,344件の変更、画像6-4,456件の変更

56

無効なピクセルを削除しないと、合計42,782の変更があります

緑のピクセルは、無効なピクセルの最初のレイヤーです。

画像1-0の変更、画像2-9,889の変更

12

画像3-24,268件の変更、画像4-103件の変更

34

画像5-4,471件の変更、画像6- 4,050件の変更

56

すべての写真内のすべての白いピクセルは、中心からではなくピクセルのどこかで線を開始/終了する必要がない場合、中心ピクセルから線を引くことができます。

args[0] 入力ファイル名が含まれます。

args[1] 出力ファイル名が含まれます。

stdout変更の数を印刷します。


素晴らしく見える!「無効なピクセル」の意味を説明できますか?私はそれをよく理解していませんでした。また、右下の画像2では、プログラムが黒い壁に「掘る」理由を追うことができませんでしたが、白いドットを再び黒い色にしていますが、これは「無効なピクセル」と関係があると思いますか?
フレイ

無効なピクセルが少ないと、カスケード効果が発生し、さらに多くの無効なピクセルが作成されます。最後のいくつかの画像を変更して、無効なピクセルの最初のレイヤーを緑色で表示します。
TheNumberOne

3

Python-PIL- 216,228 108,363合計変更

おー!@AJMansfieldのおかげで半分にカットできます!このアルゴリズムは、線の計算と最適化に関する心配をすべてスキップします。1つを除くすべての白を黒に変更します。白がなければ、黒を白にします。それはより多くの白または黒があるかどうかをチェックし、1つを除く他の種類のすべてを1つずつ変更します。黒がなければ、(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:常に可能であるべきです入力として完全な黒のイメージがある場合でも、スタードメインを作成して、1ピクセルを白(赤)に変更できます。
フレイ

または黒のピクセルがない場合(つまり、フルカラー)は不可能です。いずれにせよ、私は他の変更を行っています。
マルティセン

ああ。白黒画像。私の悪い。
マルティセン

反対の戦略を実行し、すべての黒いピクセルを白いピクセルに変更する方が効率的であると思います。試しましたか?
AJMansfield

@AJMansfieldこれは与えられたテストケースに対してのみより効率的だと思うので、おそらくこれは与えられたテストケースのアルゴリズムの条件付けとしてすでに考えられているかもしれません。
-flawr
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.