|
|
@@ -0,0 +1,461 @@ |
|
|
|
#include "config.h" |
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <math.h> |
|
|
|
|
|
|
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> |
|
|
|
#include <CGAL/Delaunay_triangulation_2.h> |
|
|
|
#include <CGAL/natural_neighbor_coordinates_2.h> |
|
|
|
|
|
|
|
#include <pipi.h> |
|
|
|
|
|
|
|
#define TOTAL_POINTS 138 |
|
|
|
#define POINTS_PER_CELL 2 |
|
|
|
|
|
|
|
#define RANGE_X 16 |
|
|
|
#define RANGE_Y 16 |
|
|
|
#define RANGE_R 5 |
|
|
|
#define RANGE_G 5 |
|
|
|
#define RANGE_B 5 |
|
|
|
#define RANGE_S 1 |
|
|
|
|
|
|
|
#define RANGE_SY (RANGE_S*RANGE_Y) |
|
|
|
#define RANGE_SYX (RANGE_S*RANGE_Y*RANGE_X) |
|
|
|
#define RANGE_SYXR (RANGE_S*RANGE_Y*RANGE_X*RANGE_R) |
|
|
|
#define RANGE_SYXRG (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G) |
|
|
|
#define RANGE_SYXRGB (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G*RANGE_B) |
|
|
|
|
|
|
|
struct K : CGAL::Exact_predicates_inexact_constructions_kernel {}; |
|
|
|
typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation; |
|
|
|
typedef std::vector<std::pair<K::Point_2, K::FT> > Point_coordinate_vector; |
|
|
|
|
|
|
|
/* Global aspect ratio */ |
|
|
|
static int dw, dh; |
|
|
|
|
|
|
|
/* Global point encoding */ |
|
|
|
static uint32_t points[1024]; |
|
|
|
static int npoints = 0; |
|
|
|
|
|
|
|
/* Global triangulation */ |
|
|
|
static Delaunay_triangulation dt; |
|
|
|
|
|
|
|
static unsigned int det_rand(unsigned int mod) |
|
|
|
{ |
|
|
|
static unsigned long next = 1; |
|
|
|
next = next * 1103515245 + 12345; |
|
|
|
return ((unsigned)(next / 65536) % 32768) % mod; |
|
|
|
} |
|
|
|
|
|
|
|
static inline int float2int(float val, int range) |
|
|
|
{ |
|
|
|
int ret = (int)(val * ((float)range - 0.0001)); |
|
|
|
return ret < 0 ? 0 : ret > range - 1? range - 1 : ret; |
|
|
|
} |
|
|
|
|
|
|
|
static inline float int2float(int val, int range) |
|
|
|
{ |
|
|
|
return (float)(1 + 2 * val) / (float)(2 * range); |
|
|
|
} |
|
|
|
|
|
|
|
static inline uint32_t set_point(int index, float x, float y, float r, |
|
|
|
float g, float b, float s) |
|
|
|
{ |
|
|
|
int dx = (index / POINTS_PER_CELL) % dw; |
|
|
|
int dy = (index / POINTS_PER_CELL) / dw; |
|
|
|
|
|
|
|
float fx = (x - dx * RANGE_X) / RANGE_X; |
|
|
|
float fy = (y - dy * RANGE_Y) / RANGE_Y; |
|
|
|
|
|
|
|
int is = float2int(s, RANGE_S); |
|
|
|
|
|
|
|
int ix = float2int(fx, RANGE_X); |
|
|
|
int iy = float2int(fy, RANGE_Y); |
|
|
|
|
|
|
|
int ir = float2int(r, RANGE_R); |
|
|
|
int ig = float2int(g, RANGE_G); |
|
|
|
int ib = float2int(b, RANGE_B); |
|
|
|
|
|
|
|
points[index] = is + RANGE_S * (iy + RANGE_Y * (ix + RANGE_X * |
|
|
|
(ib + RANGE_B * (ig + (RANGE_R * ir))))); |
|
|
|
} |
|
|
|
|
|
|
|
static inline void get_point(int index, float *x, float *y, float *r, |
|
|
|
float *g, float *b, float *s) |
|
|
|
{ |
|
|
|
uint32_t pt = points[index]; |
|
|
|
|
|
|
|
unsigned int dx = (index / POINTS_PER_CELL) % dw; |
|
|
|
unsigned int dy = (index / POINTS_PER_CELL) / dw; |
|
|
|
|
|
|
|
float fs = int2float(pt % RANGE_S, RANGE_S); pt /= RANGE_S; |
|
|
|
|
|
|
|
*s = fs < 0.5 ? 0.0 : 1.0; |
|
|
|
|
|
|
|
float fy = int2float(pt % RANGE_Y, RANGE_Y); pt /= RANGE_Y; |
|
|
|
float fx = int2float(pt % RANGE_X, RANGE_X); pt /= RANGE_X; |
|
|
|
|
|
|
|
*x = (fx + dx) * RANGE_X; |
|
|
|
*y = (fy + dy) * RANGE_Y; |
|
|
|
|
|
|
|
*b = int2float(pt % RANGE_R, RANGE_R); pt /= RANGE_R; |
|
|
|
*g = int2float(pt % RANGE_G, RANGE_G); pt /= RANGE_G; |
|
|
|
*r = int2float(pt % RANGE_B, RANGE_B); pt /= RANGE_B; |
|
|
|
} |
|
|
|
|
|
|
|
static inline float clip(float x, int modulo) |
|
|
|
{ |
|
|
|
float mul = (float)modulo + 0.9999; |
|
|
|
int round = (int)(x * mul); |
|
|
|
return (float)round / (float)modulo; |
|
|
|
} |
|
|
|
|
|
|
|
static void add_point(float x, float y, float r, float g, float b, float s) |
|
|
|
{ |
|
|
|
set_point(npoints, x, y, r, g, b, s); |
|
|
|
get_point(npoints, &x, &y, &r, &g, &b, &s); |
|
|
|
npoints++; |
|
|
|
} |
|
|
|
|
|
|
|
#define MAX_OPS 15 |
|
|
|
|
|
|
|
static uint32_t apply_op(uint8_t op, uint32_t val) |
|
|
|
{ |
|
|
|
uint32_t rem, ext; |
|
|
|
|
|
|
|
switch(op) |
|
|
|
{ |
|
|
|
case 0: /* Flip strength value */ |
|
|
|
return val ^ 1; |
|
|
|
case 1: /* Move up; if impossible, down */ |
|
|
|
rem = val % RANGE_S; |
|
|
|
ext = (val / RANGE_S) % RANGE_Y; |
|
|
|
ext = ext > 0 ? ext - 1 : ext + 1; |
|
|
|
return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem; |
|
|
|
case 2: /* Move down; if impossible, up */ |
|
|
|
rem = val % RANGE_S; |
|
|
|
ext = (val / RANGE_S) % RANGE_Y; |
|
|
|
ext = ext < RANGE_Y - 1 ? ext + 1 : ext - 1; |
|
|
|
return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem; |
|
|
|
case 3: /* Move left; if impossible, right */ |
|
|
|
rem = val % RANGE_SY; |
|
|
|
ext = (val / RANGE_SY) % RANGE_X; |
|
|
|
ext = ext > 0 ? ext - 1 : ext + 1; |
|
|
|
return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem; |
|
|
|
case 4: /* Move left; if impossible, right */ |
|
|
|
rem = val % RANGE_SY; |
|
|
|
ext = (val / RANGE_SY) % RANGE_X; |
|
|
|
ext = ext < RANGE_X - 1 ? ext + 1 : ext - 1; |
|
|
|
return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem; |
|
|
|
case 5: /* Corner 1 */ |
|
|
|
return apply_op(1, apply_op(3, val)); |
|
|
|
case 6: /* Corner 2 */ |
|
|
|
return apply_op(1, apply_op(4, val)); |
|
|
|
case 7: /* Corner 3 */ |
|
|
|
return apply_op(2, apply_op(4, val)); |
|
|
|
case 8: /* Corner 4 */ |
|
|
|
return apply_op(2, apply_op(3, val)); |
|
|
|
case 9: /* R-- (or R++) */ |
|
|
|
rem = val % RANGE_SYX; |
|
|
|
ext = (val / RANGE_SYX) % RANGE_R; |
|
|
|
ext = ext > 0 ? ext - 1 : ext + 1; |
|
|
|
return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem; |
|
|
|
case 10: /* R++ (or R--) */ |
|
|
|
rem = val % RANGE_SYX; |
|
|
|
ext = (val / RANGE_SYX) % RANGE_R; |
|
|
|
ext = ext < RANGE_R - 1 ? ext + 1 : ext - 1; |
|
|
|
return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem; |
|
|
|
case 11: /* G-- (or G++) */ |
|
|
|
rem = val % RANGE_SYXR; |
|
|
|
ext = (val / RANGE_SYXR) % RANGE_G; |
|
|
|
ext = ext > 0 ? ext - 1 : ext + 1; |
|
|
|
return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem; |
|
|
|
case 12: /* G++ (or G--) */ |
|
|
|
rem = val % RANGE_SYXR; |
|
|
|
ext = (val / RANGE_SYXR) % RANGE_G; |
|
|
|
ext = ext < RANGE_G - 1 ? ext + 1 : ext - 1; |
|
|
|
return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem; |
|
|
|
case 13: /* B-- (or B++) */ |
|
|
|
rem = val % RANGE_SYXRG; |
|
|
|
ext = (val / RANGE_SYXRG) % RANGE_B; |
|
|
|
ext = ext > 0 ? ext - 1 : ext + 1; |
|
|
|
return ext * RANGE_SYXRG + rem; |
|
|
|
case 14: /* B++ (or B--) */ |
|
|
|
rem = val % RANGE_SYXRG; |
|
|
|
ext = (val / RANGE_SYXRG) % RANGE_B; |
|
|
|
ext = ext < RANGE_B - 1 ? ext + 1 : ext - 1; |
|
|
|
return ext * RANGE_SYXRG + rem; |
|
|
|
#if 0 |
|
|
|
case 15: /* Brightness-- */ |
|
|
|
return apply_op(9, apply_op(11, apply_op(13, val))); |
|
|
|
case 16: /* Brightness++ */ |
|
|
|
return apply_op(10, apply_op(12, apply_op(14, val))); |
|
|
|
case 17: /* RG-- */ |
|
|
|
return apply_op(9, apply_op(11, val)); |
|
|
|
case 18: /* RG++ */ |
|
|
|
return apply_op(10, apply_op(12, val)); |
|
|
|
case 19: /* GB-- */ |
|
|
|
return apply_op(11, apply_op(13, val)); |
|
|
|
case 20: /* GB++ */ |
|
|
|
return apply_op(12, apply_op(14, val)); |
|
|
|
case 21: /* RB-- */ |
|
|
|
return apply_op(9, apply_op(13, val)); |
|
|
|
case 22: /* RB++ */ |
|
|
|
return apply_op(10, apply_op(14, val)); |
|
|
|
#endif |
|
|
|
default: |
|
|
|
return val; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh) |
|
|
|
{ |
|
|
|
uint8_t lookup[TOTAL_POINTS / POINTS_PER_CELL * RANGE_X * RANGE_Y]; |
|
|
|
pipi_pixels_t *p = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32); |
|
|
|
float *data = (float *)p->pixels; |
|
|
|
float fx, fy, fr, fg, fb, fs; |
|
|
|
int i, x, y; |
|
|
|
|
|
|
|
memset(lookup, 0, sizeof(lookup)); |
|
|
|
dt.clear(); |
|
|
|
for(i = 0; i < npoints; i++) |
|
|
|
{ |
|
|
|
get_point(i, &fx, &fy, &fr, &fg, &fb, &fs); |
|
|
|
lookup[(int)fx + dw * RANGE_X * (int)fy] = i; /* Keep link to point */ |
|
|
|
dt.insert(K::Point_2(fx, fy)); |
|
|
|
} |
|
|
|
|
|
|
|
/* Add fake points to close the triangulation */ |
|
|
|
dt.insert(K::Point_2(-p->w, -p->h)); |
|
|
|
dt.insert(K::Point_2(2 * p->w, -p->h)); |
|
|
|
dt.insert(K::Point_2(-p->w, 2 * p->h)); |
|
|
|
dt.insert(K::Point_2(2 * p->w, 2 * p->h)); |
|
|
|
|
|
|
|
for(y = ry; y < ry + rh; y++) |
|
|
|
{ |
|
|
|
for(x = rx; x < rx + rw; x++) |
|
|
|
{ |
|
|
|
int i1 = 0, i2 = 0, i3 = 0; |
|
|
|
float d1 = 1000000, d2 = 0, d3 = 0; |
|
|
|
|
|
|
|
K::Point_2 m(x, y); |
|
|
|
Point_coordinate_vector coords; |
|
|
|
CGAL::Triple< |
|
|
|
std::back_insert_iterator<Point_coordinate_vector>, |
|
|
|
K::FT, bool> result = |
|
|
|
CGAL::natural_neighbor_coordinates_2(dt, m, |
|
|
|
std::back_inserter(coords)); |
|
|
|
|
|
|
|
float r = 0.0f, g = 0.0f, b = 0.0f, norm = 0.0f; |
|
|
|
|
|
|
|
Point_coordinate_vector::iterator it; |
|
|
|
for(it = coords.begin(); it != coords.end(); ++it) |
|
|
|
{ |
|
|
|
float fx = (*it).first.x(); |
|
|
|
float fy = (*it).first.y(); |
|
|
|
|
|
|
|
if(fx < 0 || fy < 0 || fx > p->w - 1 || fy > p->h - 1) |
|
|
|
continue; |
|
|
|
|
|
|
|
int index = lookup[(int)fx + dw * RANGE_X * (int)fy]; |
|
|
|
|
|
|
|
get_point(index, &fx, &fy, &fr, &fg, &fb, &fs); |
|
|
|
|
|
|
|
float k = (*it).second; |
|
|
|
if(fs > 0.5) k = pow(k, 1.2); |
|
|
|
else k = pow(k, 0.8); |
|
|
|
//float k = (*it).second * (1.0f + fs); |
|
|
|
|
|
|
|
r += k * fr; |
|
|
|
g += k * fg; |
|
|
|
b += k * fb; |
|
|
|
norm += k; |
|
|
|
} |
|
|
|
|
|
|
|
data[4 * (x + y * p->w) + 0] = r / norm; |
|
|
|
data[4 * (x + y * p->w) + 1] = g / norm; |
|
|
|
data[4 * (x + y * p->w) + 2] = b / norm; |
|
|
|
data[4 * (x + y * p->w) + 3] = 0.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pipi_release_pixels(dst, p); |
|
|
|
} |
|
|
|
|
|
|
|
static void analyse(pipi_image_t *src) |
|
|
|
{ |
|
|
|
pipi_pixels_t *p = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32); |
|
|
|
float *data = (float *)p->pixels; |
|
|
|
int i; |
|
|
|
|
|
|
|
for(int dy = 0; dy < dh; dy++) |
|
|
|
for(int dx = 0; dx < dw; dx++) |
|
|
|
{ |
|
|
|
float min = 1.1f, max = -0.1f; |
|
|
|
float total = 0.0; |
|
|
|
int xmin = 0, xmax = 0, ymin = 0, ymax = 0; |
|
|
|
int npixels = 0; |
|
|
|
|
|
|
|
for(int iy = RANGE_Y * dy; iy < RANGE_Y * (dy + 1); iy++) |
|
|
|
for(int ix = RANGE_X * dx; ix < RANGE_X * (dx + 1); ix++) |
|
|
|
{ |
|
|
|
float lum = 0.0f; |
|
|
|
|
|
|
|
lum += data[4 * (ix + iy * p->w) + 0]; |
|
|
|
lum += data[4 * (ix + iy * p->w) + 1]; |
|
|
|
lum += data[4 * (ix + iy * p->w) + 2]; |
|
|
|
|
|
|
|
if(lum < min) |
|
|
|
{ |
|
|
|
min = lum; |
|
|
|
xmin = ix; |
|
|
|
ymin = iy; |
|
|
|
} |
|
|
|
|
|
|
|
if(lum > max) |
|
|
|
{ |
|
|
|
max = lum; |
|
|
|
xmax = ix; |
|
|
|
ymax = iy; |
|
|
|
} |
|
|
|
|
|
|
|
total += lum; |
|
|
|
npixels++; |
|
|
|
} |
|
|
|
|
|
|
|
total /= npixels; |
|
|
|
|
|
|
|
float wmin, wmax; |
|
|
|
|
|
|
|
if(total < min + (max - min) / 4) |
|
|
|
wmin = 1.0, wmax = 0.0; |
|
|
|
else if(total < min + (max - min) / 4 * 2) |
|
|
|
wmin = 0.0, wmax = 0.0; |
|
|
|
else if(total < min + (max - min) / 4 * 3) |
|
|
|
wmin = 0.0, wmax = 0.0; |
|
|
|
else |
|
|
|
wmin = 0.0, wmax = 1.0; |
|
|
|
|
|
|
|
//wmin = wmax = 1.0; |
|
|
|
//if(total < min + (max - min) /3 ) |
|
|
|
//if((dx + dy) & 1) |
|
|
|
{ |
|
|
|
add_point(xmin, ymin, |
|
|
|
data[4 * (xmin + ymin * p->w) + 0], |
|
|
|
data[4 * (xmin + ymin * p->w) + 1], |
|
|
|
data[4 * (xmin + ymin * p->w) + 2], |
|
|
|
wmin); |
|
|
|
} |
|
|
|
//else |
|
|
|
{ |
|
|
|
add_point(xmax, ymax, |
|
|
|
data[4 * (xmax + ymax * p->w) + 0], |
|
|
|
data[4 * (xmax + ymax * p->w) + 1], |
|
|
|
data[4 * (xmax + ymax * p->w) + 2], |
|
|
|
wmax); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int main(int argc, char *argv[]) |
|
|
|
{ |
|
|
|
int opstats[2 * MAX_OPS]; |
|
|
|
pipi_image_t *src, *tmp, *dst; |
|
|
|
double error = 1.0; |
|
|
|
int width, height, ret = 0; |
|
|
|
|
|
|
|
/* Load image */ |
|
|
|
pipi_set_gamma(1.0); |
|
|
|
src = pipi_load(argv[1]); |
|
|
|
width = pipi_get_image_width(src); |
|
|
|
height = pipi_get_image_height(src); |
|
|
|
|
|
|
|
/* Compute best w/h ratio */ |
|
|
|
dw = 1; |
|
|
|
for(int i = 1; i <= TOTAL_POINTS / POINTS_PER_CELL; i++) |
|
|
|
{ |
|
|
|
float r = (float)width / (float)height; |
|
|
|
float ir = (float)i / (float)(TOTAL_POINTS / POINTS_PER_CELL / i); |
|
|
|
float dwr = (float)dw / (float)(TOTAL_POINTS / POINTS_PER_CELL / dw); |
|
|
|
|
|
|
|
if(fabs(logf(r / ir)) < fabs(logf(r / dwr))) |
|
|
|
dw = i; |
|
|
|
} |
|
|
|
dh = TOTAL_POINTS / POINTS_PER_CELL / dw; |
|
|
|
fprintf(stderr, "Chosen image ratio: %i:%i\n", dw, dh); |
|
|
|
|
|
|
|
/* Resize and filter image to better state */ |
|
|
|
tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y); |
|
|
|
pipi_free(src); |
|
|
|
src = pipi_median_ext(tmp, 2, 2); |
|
|
|
|
|
|
|
/* Analyse image */ |
|
|
|
analyse(src); |
|
|
|
|
|
|
|
/* Render what we just computed */ |
|
|
|
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y); |
|
|
|
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y); |
|
|
|
error = pipi_measure_rmsd(src, tmp); |
|
|
|
|
|
|
|
fprintf(stderr, "Distance: %2.10g\n", error); |
|
|
|
|
|
|
|
memset(opstats, 0, sizeof(opstats)); |
|
|
|
for(int iter = 0, failures = 0; /*failures < 200 &&*/ iter < 3000; iter++) |
|
|
|
{ |
|
|
|
uint32_t oldval; |
|
|
|
pipi_image_t *scrap = pipi_copy(tmp); |
|
|
|
|
|
|
|
/* Choose a point at random */ |
|
|
|
int pt = det_rand(npoints); |
|
|
|
oldval = points[pt]; |
|
|
|
|
|
|
|
/* Apply a random operation and measure its effect */ |
|
|
|
uint8_t op = det_rand(MAX_OPS); |
|
|
|
points[pt] = apply_op(op, oldval); |
|
|
|
render(scrap, 0, 0, dw * RANGE_X, dh * RANGE_Y); |
|
|
|
|
|
|
|
opstats[op * 2]++; |
|
|
|
|
|
|
|
double newerr = pipi_measure_rmsd(src, scrap); |
|
|
|
if(newerr < error) |
|
|
|
{ |
|
|
|
pipi_free(tmp); |
|
|
|
tmp = scrap; |
|
|
|
error = newerr; |
|
|
|
fprintf(stderr, "%06i %2.010g after op%i(%i)\n", |
|
|
|
iter, error, op, pt); |
|
|
|
opstats[op * 2 + 1]++; |
|
|
|
failures = 0; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
pipi_free(scrap); |
|
|
|
points[pt] = oldval; |
|
|
|
failures++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fprintf(stderr, "operation: "); |
|
|
|
for(int i = 0; i < MAX_OPS; i++) |
|
|
|
fprintf(stderr, "%3i ", i); |
|
|
|
fprintf(stderr, "\nattempts: "); |
|
|
|
for(int i = 0; i < MAX_OPS; i++) |
|
|
|
fprintf(stderr, "%3i ", opstats[i * 2]); |
|
|
|
fprintf(stderr, "\nsuccesses: "); |
|
|
|
for(int i = 0; i < MAX_OPS; i++) |
|
|
|
fprintf(stderr, "%3i ", opstats[i * 2 + 1]); |
|
|
|
fprintf(stderr, "\n"); |
|
|
|
|
|
|
|
fprintf(stderr, "Distance: %2.10g\n", error); |
|
|
|
|
|
|
|
dst = pipi_resize(tmp, width, height); |
|
|
|
pipi_free(tmp); |
|
|
|
|
|
|
|
/* Save image and bail out */ |
|
|
|
pipi_save(dst, "lol.bmp"); |
|
|
|
pipi_free(dst); |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|