Java 8, 47 867 changements au total.
Utilise la moyenne de l'image comme point central. Il attire ensuite tous les rayons possibles vers le centre et lui donne le meilleur rayon de couleur. Il colore ensuite tous les points invalides en noir.
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);
}
}
}
Résultats
Image 1 - 0 changements, Image 2 - 13 698 changements
Image 3 - 24 269 changements, Image 4 - 103 changements
Image 5 - 5 344 changements, Image 6 - 4 456 changements
Sans pixels non valides supprimés, 42 782 changements au total
Les pixels verts sont la première couche de pixels non valides.
Image 1 - 0 changements, Image 2- 9 889 changements
Image 3 - 24 268 changements, Image 4 - 103 changements
Image 5 - 4 471 changements, Image 6 - 4 050 changements
Tous les pixels blancs dans toutes les images peuvent avoir une ligne tirée vers eux à partir du pixel central si la ligne ne doit pas commencer / se terminer au centre mais plutôt n'importe où sur le pixel.
args[0]
contient le nom du fichier d'entrée.
args[1]
contient le nom du fichier de sortie.
Imprime en stdout
nombre de modifications.