C++, Score: 1.42042
This is essentially a slightly more sophisticated version of the two existing solutions: Of the four possible moves, it selects the one that maximizes brightness. However, instead of only looking at the brightness of the target pixel, it looks at the weighted sum of pixel-brightness in the neighborhood of the target pixel, where pixels closer to the target have a greater weight.
Compile with g++ joe.cpp -ojoe -std=c++11 -O3 -lcairo
. Requires cairo.
Run with joe <image-file> [<radius>]
.
<image-file>
is the input PNG image.
<radius>
(optional argument) is the radius of the summed-over neighborhood, in pixels (smaller is faster, larger is (roughly) better.)
Outputs the score and an image named out.<image-file>
.
#include <cairo/cairo.h>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, const char* argv[]) {
auto* img = cairo_image_surface_create_from_png(argv[1]);
int width = cairo_image_surface_get_width(img),
height = cairo_image_surface_get_height(img),
stride = cairo_image_surface_get_stride(img);
unsigned char* data = cairo_image_surface_get_data(img);
double* brightness = new double[width * height];
double total_brightness = 0;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
const unsigned char* p = data + stride * y + 4 * x;
total_brightness += brightness[y * width + x] = p[0] + p[1] + p[2];
}
}
const int r = argc > 2 ? stoi(argv[2]) : 64, R = 2 * r + 1;
double* weight = new double[R * R];
for (int y = -r; y <= r; ++y) {
for (int x = -r; x <= r; ++x)
weight[R * (y + r) + (x + r)] = 1.0 / (x*x + y*y + 1);
}
auto neighborhood = [&] (int x, int y) {
double b = 0;
int x1 = max(x - r, 0), x2 = min(x + r, width - 1);
int y1 = max(y - r, 0), y2 = min(y + r, height - 1);
for (int v = y1; v <= y2; ++v) {
const double *B = brightness + width * v + x1;
const double *W = weight + R * (v - (y - r)) + (x1 - (x - r));
for (int u = x1; u <= x2; ++u, ++B, ++W)
b += *W * *B;
}
return b;
};
int n = (2 * width * height + 3) / 6;
int x = 0, y = 0;
double path_brightness = 0;
int O[][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int i = 0; i < n; ++i) {
if (i % 1000 == 0) cerr << (200 * i + n) / (2 * n) << "%\r";
path_brightness += brightness[width * y + x]; brightness[width * y + x] = 0;
unsigned char* p = data + stride * y + 4 * x;
p[0] = p[1] = 16 * i % 255; p[2] = 0;
auto O_end = partition(begin(O), end(O), [&] (const int* o) {
return x + o[0] >= 0 && x + o[0] < width &&
y + o[1] >= 0 && y + o[1] < height;
});
const int* o_max; double o_max_neighborhood = -1;
for (auto o = O; o != O_end; ++o) {
double o_neighborhood = neighborhood(x + (*o)[0], y + (*o)[1]);
if (o_neighborhood > o_max_neighborhood)
o_max = *o, o_max_neighborhood = o_neighborhood;
}
x += o_max[0]; y += o_max[1];
}
cout << (path_brightness * width * height) / (n * total_brightness) << endl;
cairo_surface_write_to_png(img, (string("out.") + argv[1]).c_str());
delete []brightness;
delete []weight;
cairo_surface_destroy(img);
}
Results
Bridge 1.36869
Balls 1.66016
Scream 1.35321
Fractal 1.27017
Vortex 1.60528
Tornado 1.26503
-----------------
Average 1.42042
More eye-candy