Vai a renderlo stellato


14

In questo contesto devi scrivere un programma, che accetta un'immagine di pixel in bianco e nero, e cerca di alterarlo, in modo che la forma bianca formi un dominio a stella , con il minor numero di modifiche possibile.

Le modifiche consentite stanno trasformando i pixel bianchi in neri e trasformando i pixel neri in bianchi.

L'output deve essere nuovamente costituito dalla stessa immagine ma questa volta con tutte le modifiche e con un / il centro contrassegnato. I pixel che sono stati modificati da bianco a nero devono essere visualizzati in blu, quelli che sono stati modificati da nero a bianco devono essere visualizzati in giallo e almeno un pixel centrale deve essere visualizzato in rosso. (I colori esatti dipendono da te.) Il programma deve produrre l'immagine specificata, nonché il numero totale di modifiche apportate.

definizioni

Star Domain

L'insieme di pixel bianchi dell'immagine rappresenta un dominio a stella se (e solo se) esiste (almeno) un pixel centrale . Il pixel centrale è uno dei pixel bianchi che possono essere collegati da una linea retta a tutti gli altri pixel bianchi in modo tale che la linea attraversi solo i pixel bianchi. (Il pixel centrale non è quindi necessariamente unico.)

Linea retta tra due pixel

Dati due pixel (inizio e fine, entrambi rossi nella figura sotto), la linea di rigidezza tra i due pixel è composta da tutti i pixel, che toccano la linea (matematica, gialla nella figura sotto) che porta dal centro del primo pixel al centro dell'ultimo pixel. Un pixel non tocca la linea se la tocca solo con un angolo, quindi affinché un pixel appartenga alla linea di pixel, la linea (matematica, gialla) deve attraversare il pixel in questione con una lunghezza diversa da zero. (Se tocca solo il punto d'angolo, questo viene considerato come lunghezza zero). Considera i seguenti esempi:

pixel

Esempio

La prima immagine dovrebbe rappresentare un esempio di "input" di testcase e le altre due immagini rappresentano due possibili output validi per l'esempio dato:

esempio testcase soluzione del primo esempio soluzione del secondo esempio

Le aree gialle (precedentemente nere) contano anche per il dominio "bianco", mentre le aree blu (precedentemente bianche) contano per la parte "nera" al di fuori del dominio e il punto rosso rappresenta ogni volta un possibile pixel centrale.

Casi test

I seguenti casi di test sono PNG con ciascuno una dimensione di 256 x 256 pixel.

test case 1 test case 2 test case 3 test case 4 test case 5 test case 6

punteggio

Esegui il programma con i seguenti casi di test e includi l'output (immagine / numero di modifiche) nella tua risposta. Farò una classifica per ogni caso di test. Il tuo punteggio sarà la somma di ogni classifica nella classifica: più basso è il punteggio, meglio è. Si applicano scappatoie standard. Non è consentito far riconoscere al programma quei casi di test ed eseguire un caso speciale per essi. (Non è consentito precalutare e salvare i pixel centrali ottimali per ciascuno di questi casi di test.) Il programma dovrebbe funzionare per qualsiasi immagine.

Classifica

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
Sarebbe utile se dovessi spiegare la Fig. 1. Perché stai collegando i pixel rossi, per esempio?
DavidC

4
Non sono davvero sicuro di cosa intendi. Puoi dare un prima e un dopo uno dei tuoi casi di test?

Quanto deve essere vicina una linea a un angolo di pixel per essere considerata passante?
TheNumberOne

Ho aggiunto alcuni esempi e ho cercato di chiarire il testo, spero sia chiaro ora!
flawr

C'è qualcun altro che intende tentare questa sfida? Sono un po 'confuso, dal momento che un bel po' di persone ha votato questa sfida, ma finora abbiamo avuto solo una (non molto seria) risposta. Qualche critica?
flawr

Risposte:


4

Java 8, 47.867 modifiche totali.

Utilizza la media dell'immagine come punto centrale. Quindi disegna tutti i raggi possibili al centro e gli conferisce il miglior raggio di colore. Quindi colora tutti i punti non validi in nero.

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

risultati

L'immagine 1 - 0 modifiche, l'immagine 2 - 13.698 modifiche

12

Immagine 3 - 24.269 modifiche, Immagine 4 - 103 modifiche

34

Immagine 5 - 5.344 modifiche, Immagine 6 - 4.456 modifiche

56

Senza pixel non validi rimossi, 42.782 modifiche totali

I pixel verdi sono il primo livello di pixel non validi.

L'immagine 1 - 0 modifiche, l'immagine 2- 9.889 modifiche

12

Immagine 3 - 24.268 modifiche, Immagine 4 - 103 modifiche

34

Immagine 5 - 4.471 modifiche, Immagine 6- 4.050 modifiche

56

Tutti i pixel bianchi in tutte le immagini possono avere una linea disegnata dal pixel centrale se la linea non deve originare / terminare al centro ma piuttosto ovunque sul pixel.

args[0] contiene il nome del file di input.

args[1] contiene il nome del file di output.

Stampa il stdoutnumero di modifiche.


Sembra fantastico! Puoi spiegare cosa intendi con "pixel non validi"? Non l'ho capito del tutto. Anche nell'immagine 2 in basso a destra non ho potuto seguire il motivo per cui il tuo programma 'scava' nel muro nero ma poi colora ancora di nuovo i punti bianchi in nero, ma penso che questo abbia a che fare con i 'pixel non validi'?
flawr

I pochi pixel non validi causano un effetto a cascata che ne rende molti altri non validi. Modificherò le ultime immagini per mostrare il primo livello di pixel non validi come verde.
TheNumberOne

3

Python - PIL - 216.228 108.363 variazioni totali

Whoo! Dividilo a metà grazie a @AJMansfield! Questo algoritmo salta tutte le preoccupazioni con il calcolo delle linee e l'ottimizzazione e cosa no. Cambia semplicemente tutti i bianchi in neri tranne uno. Se non ci sono bianchi, rende uno nero un bianco. Controlla se ci sono più bianchi o neri e cambia ogni singolo dell'altro tipo ad eccezione di uno. Se non ci sono neri rende (0, 0) il centro.

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

risultati

Immagine 1 - 28688 modifiche, Immagine 2 - 24208 modifiche

Immagine 3 - 24248 modifiche, Immagine 4 - 7103 modifiche

Immagine 5 - 11097 modifiche, Immagine 6 - 13019 modifiche

Prende il nome del file da raw_input e scrive su out.png e stampa il numero di modifiche.


Si noti che i pixel che sono stati modificati da nero a bianco dovrebbero essere gialli nell'output. Quelli che sono stati cambiati da bianco a nero dovrebbero essere blu e al centro (nel tuo caso il tuo unico pixel "bianco" dovrebbe essere rosso nella tua uscita. A parte questo, grazie per aver partecipato =) PS: Dovrebbe essere sempre possibile crea un dominio a stella, anche quando hai un'immagine completamente nera come input, puoi cambiare un pixel in bianco (rosso).
flawr

Può essere impossibile se non ci sono pixel bianchi o neri (cioè a colori). In ogni caso, sto apportando le altre modifiche.
Maltysen,

Oh. Immagine in bianco e nero. Colpa mia.
Maltysen,

Penso che potrebbe essere più efficiente fare la strategia opposta e cambiare tutti i pixel neri in bianchi. Ci hai provato?
AJMansfield,

@AJMansfield Penso che questo sarebbe solo più efficiente per il caso di test dato, quindi forse questo potrebbe già essere considerato come un condizionamento dell'algoritmo per i testcase dati.
flawr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.