Java 8, 47,867 changes total.
Uses the average of the image as the center point. It then draws all possible rays to the center and gives it the best radius to color. It then colors all invalid points black.
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);
}
}
}
Results
Image 1 - 0 changes, Image 2 - 13,698 changes
![1](../../I/static/images/62b32359d66bd0ec261a31b6a45050f4d080c08129b80a54ffd31aa4a61fa9a2.png)
![2](../../I/static/images/ffc27ef6fce1c3908c2daf57cddc3fffe1c5687df0a5a1113414ce4d5fd3d6e1.png)
Image 3 - 24,269 changes, Image 4 - 103 changes
![3](../../I/static/images/7959c09294e9cb92eb01873b9152a9b6096342c1cedc181ea0ffc61c02db1d55.png)
![4](../../I/static/images/8182669ff5f614f90216a9ab7f0569d77b66797302bccd6c3cf7cec4c8596db6.png)
Image 5 - 5,344 changes, Image 6 - 4,456 changes
![5](../../I/static/images/790e9c7390eaf6bd81b8d174b0317e06b6fd0f1cbe7d137d7a4beedc31a1584f.png)
![6](../../I/static/images/ae55d598ea7dfafa98aedfdf266cecf2733bd8e4e38b43a8f39d45057255ff3e.png)
Without invalid pixels removed, 42,782 changes total
Green pixels are the first layer of invalid pixels.
Image 1 - 0 changes, Image 2- 9,889 changes
![1](../../I/static/images/8e98cda50faab7f905ed3d066c8721c93a88a05647bf7a35088f3c0bc2d4a9e6.png)
![2](../../I/static/images/c5ee3ae2925ef460a4d7b6effd59ea34debf4e219a44a27f491cd63d69589a90.png)
Image 3 - 24,268 changes, Image 4 - 103 changes
![3](../../I/static/images/2af707d7338627526bc1a3c00eee1d5fd7b04cbc7f7f84cdfa400e5480361e0f.png)
![4](../../I/static/images/0168f615097b26ba1c003cf8a36d6a45cc89c9ae2ea80d38933eba7c7be52534.png)
Image 5 - 4,471 changes, Image 6- 4,050 changes
![5](../../I/static/images/28b116fdf76aaedc768527767498fc8cfcdcec95ac8cfc26e8146bfa03a0460d.png)
![6](../../I/static/images/8c20ceab1358676c0a17adcb409823a06cc1cd5b495189d86c7d11a31cd65125.png)
All white pixels in all the pictures can have a line drawn to them from the center pixel if the line does not have to originate/end at the centers but rather anywhere on the pixel.
args[0]
contains input file name.
args[1]
contains output file name.
Prints to stdout
number of changes.
2It would help if you were to explain Fig. 1. Why are you connecting red pixels, for instance? – DavidC – 2015-01-09T15:45:55.110
4I'm not really sure what you mean. Can you give a before and after of one of your test cases? – None – 2015-01-09T16:54:14.153
How close does a line have to be to a pixel corner for it to be considered to pass through? – TheNumberOne – 2015-01-09T17:49:13.267
I added some examples and tried to clarify the text, I hope it is clear now! – flawr – 2015-01-09T23:59:35.690
Is there anyone else intending to make an attempt this challenge? I am somewhat confused, since quite a few people did upvote this challenge but we've only got one (not very serious) answer so far. Any criticism? – flawr – 2015-01-11T22:00:17.960
I do intend to make a submission once I am back at my desktop. Ones like this just take a little while to make, and are difficult to test on a phone. – AJMansfield – 2015-01-11T22:35:55.707