|
|
@@ -71,10 +71,6 @@ static const uint32_t *unichars; |
|
|
|
#define RANGE_H 2000 |
|
|
|
#define RANGE_V 10 |
|
|
|
|
|
|
|
/* How does the algorithm work: one point per cell, or two? XXX: changing |
|
|
|
* this value breaks compatibility with other images. */ |
|
|
|
#define POINTS_PER_CELL 2 |
|
|
|
|
|
|
|
/* Start with a random image (1), or with a good estimate (0)? */ |
|
|
|
#define RANDOM_START 0 |
|
|
|
|
|
|
@@ -91,6 +87,9 @@ static int MAX_MSG_LEN = 140; |
|
|
|
/* Iterations per point -- larger means slower but nicer */ |
|
|
|
static int ITERATIONS_PER_POINT = 50; |
|
|
|
|
|
|
|
/* Points per cell -- 1 allows to put more cells, but 2 gives better results */ |
|
|
|
static int POINTS_PER_CELL = 2; |
|
|
|
|
|
|
|
/* The range value for point parameters: X Y, red/green/blue, "strength" |
|
|
|
* Tested values (on Mona Lisa) are: |
|
|
|
* 16 16 5 5 5 2 -> 0.06511725914 |
|
|
@@ -153,12 +152,16 @@ void compute_ranges(int width, int height) |
|
|
|
TOTAL_BITS = MAX_MSG_LEN * logf(NUM_CHARACTERS) / logf(2); |
|
|
|
HEADER_BITS = logf(RANGE_W * RANGE_H * RANGE_V) / logf(2); |
|
|
|
DATA_BITS = TOTAL_BITS - HEADER_BITS; |
|
|
|
#if POINTS_PER_CELL == 2 |
|
|
|
CELL_BITS = (2 * logf(RANGE_SBGR) + logf(RANGE_XY2)) / logf(2); |
|
|
|
//CELL_BITS = 2 * logf(RANGE_SBGRXY) / logf(2); |
|
|
|
#else |
|
|
|
CELL_BITS = logf(RANGE_SBGRXY) / logf(2); |
|
|
|
#endif |
|
|
|
if(version == 0) |
|
|
|
{ |
|
|
|
POINTS_PER_CELL = 1; |
|
|
|
CELL_BITS = logf(RANGE_SBGRXY) / logf(2); |
|
|
|
} |
|
|
|
else if(version == 1) |
|
|
|
{ |
|
|
|
POINTS_PER_CELL = 2; |
|
|
|
CELL_BITS = (2 * logf(RANGE_SBGR) + logf(RANGE_XY2)) / logf(2); |
|
|
|
} |
|
|
|
TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS); |
|
|
|
MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS; |
|
|
|
|
|
|
@@ -839,35 +842,24 @@ static void analyse(pipi_image_t *src) |
|
|
|
wmin = 0.0, wmax = 1.0; |
|
|
|
|
|
|
|
#if RANDOM_START == 1 |
|
|
|
# if POINTS_PER_CELL == 2 |
|
|
|
add_random_point(); |
|
|
|
# endif |
|
|
|
add_random_point(); |
|
|
|
for(int i = 0; i < POINTS_PER_CELL; i++) |
|
|
|
add_random_point(); |
|
|
|
#else |
|
|
|
/* 0.80 and 0.20 were chosen empirically, it gives a 10% better |
|
|
|
* initial distance. Definitely worth it. */ |
|
|
|
# if POINTS_PER_CELL == 1 |
|
|
|
if(total < min + (max - min) / 2) |
|
|
|
{ |
|
|
|
# endif |
|
|
|
add_point(xmin, ymin, |
|
|
|
data[4 * (xmin + ymin * p->w) + 0] * 0.80 + mr * 0.20, |
|
|
|
data[4 * (xmin + ymin * p->w) + 1] * 0.80 + mg * 0.20, |
|
|
|
data[4 * (xmin + ymin * p->w) + 2] * 0.80 + mb * 0.20, |
|
|
|
wmin); |
|
|
|
# if POINTS_PER_CELL == 1 |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
# endif |
|
|
|
add_point(xmax, ymax, |
|
|
|
data[4 * (xmax + ymax * p->w) + 0] * 0.80 + mr * 0.20, |
|
|
|
data[4 * (xmax + ymax * p->w) + 1] * 0.80 + mg * 0.20, |
|
|
|
data[4 * (xmax + ymax * p->w) + 2] * 0.80 + mb * 0.20, |
|
|
|
wmax); |
|
|
|
# if POINTS_PER_CELL == 1 |
|
|
|
} |
|
|
|
# endif |
|
|
|
if(POINTS_PER_CELL == 2 || total < min + (max - min) / 2) |
|
|
|
add_point(xmin, ymin, |
|
|
|
data[4 * (xmin + ymin * p->w) + 0] * 0.80 + mr * 0.20, |
|
|
|
data[4 * (xmin + ymin * p->w) + 1] * 0.80 + mg * 0.20, |
|
|
|
data[4 * (xmin + ymin * p->w) + 2] * 0.80 + mb * 0.20, |
|
|
|
wmin); |
|
|
|
|
|
|
|
if(POINTS_PER_CELL == 2 || total >= min + (max - min) / 2) |
|
|
|
add_point(xmax, ymax, |
|
|
|
data[4 * (xmax + ymax * p->w) + 0] * 0.80 + mr * 0.20, |
|
|
|
data[4 * (xmax + ymax * p->w) + 1] * 0.80 + mg * 0.20, |
|
|
|
data[4 * (xmax + ymax * p->w) + 2] * 0.80 + mb * 0.20, |
|
|
|
wmax); |
|
|
|
#endif |
|
|
|
} |
|
|
|
} |
|
|
@@ -1052,6 +1044,13 @@ int main(int argc, char *argv[]) |
|
|
|
/* The first thing we pop from the stream is the version information */ |
|
|
|
version = b.pop(RANGE_V); |
|
|
|
|
|
|
|
if(version > 1) |
|
|
|
{ |
|
|
|
fprintf(stderr, "Error: unsupported algorithm version %i\n", |
|
|
|
version); |
|
|
|
return EXIT_FAILURE; |
|
|
|
} |
|
|
|
|
|
|
|
/* Read width and height from bitstream */ |
|
|
|
width = b.pop(RANGE_W) + 1; |
|
|
|
height = b.pop(RANGE_H) + 1; |
|
|
@@ -1080,12 +1079,6 @@ int main(int argc, char *argv[]) |
|
|
|
return EXIT_FAILURE; |
|
|
|
} |
|
|
|
|
|
|
|
if(version > 1) |
|
|
|
{ |
|
|
|
fprintf(stderr, "Error: unsupported algorithm version %i\n", version); |
|
|
|
return EXIT_FAILURE; |
|
|
|
} |
|
|
|
|
|
|
|
compute_ranges(width, height); |
|
|
|
|
|
|
|
/* Try to cram some more information into our points as long as it |
|
|
@@ -1147,7 +1140,7 @@ int main(int argc, char *argv[]) |
|
|
|
if(srcname) |
|
|
|
{ |
|
|
|
/* Resize and filter image to better state */ |
|
|
|
tmp = pipi_gaussian_blur(src, 0.5 * dw * RANGE_X / width); |
|
|
|
tmp = pipi_gaussian_blur(src, 0.25 * dw * RANGE_X / width); |
|
|
|
pipi_free(src); |
|
|
|
src = pipi_resize(tmp, dw * RANGE_X, dh * RANGE_Y); |
|
|
|
pipi_free(tmp); |
|
|
@@ -1202,16 +1195,17 @@ int main(int argc, char *argv[]) |
|
|
|
|
|
|
|
apply_op(op1, &points[pt]); |
|
|
|
|
|
|
|
#if POINTS_PER_CELL == 2 |
|
|
|
/* Check that two points don't fall at the same place */ |
|
|
|
while(points[pt].x == points[pt ^ 1].x |
|
|
|
&& points[pt].y == points[pt ^ 1].y) |
|
|
|
if(POINTS_PER_CELL == 2) |
|
|
|
{ |
|
|
|
points[pt] = oldpt; |
|
|
|
op1 = rand_op(); |
|
|
|
apply_op(op1, &points[pt]); |
|
|
|
while(points[pt].x == points[pt ^ 1].x |
|
|
|
&& points[pt].y == points[pt ^ 1].y) |
|
|
|
{ |
|
|
|
points[pt] = oldpt; |
|
|
|
op1 = rand_op(); |
|
|
|
apply_op(op1, &points[pt]); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
render(scrap, zonex * RANGE_X, zoney * RANGE_Y, |
|
|
|
zonew * RANGE_X, zoneh * RANGE_Y, false); |
|
|
@@ -1280,7 +1274,7 @@ int main(int argc, char *argv[]) |
|
|
|
else |
|
|
|
fprintf(stderr, "\r \r"); |
|
|
|
|
|
|
|
#if 1 |
|
|
|
#if 0 |
|
|
|
dst = pipi_resize(tmp, width, height); |
|
|
|
pipi_free(tmp); |
|
|
|
|
|
|
@@ -1292,33 +1286,36 @@ int main(int argc, char *argv[]) |
|
|
|
/* Push our points to the bitstream */ |
|
|
|
for(int i = 0; i < npoints; i += POINTS_PER_CELL) |
|
|
|
{ |
|
|
|
#if POINTS_PER_CELL == 2 |
|
|
|
int x1, y1, x2, y2; |
|
|
|
x1 = points[i].x; |
|
|
|
y1 = points[i].y; |
|
|
|
x2 = points[i + 1].x; |
|
|
|
y2 = points[i + 1].y; |
|
|
|
|
|
|
|
bool swap; |
|
|
|
uint32_t pack = pack_coords(x1, y1, x2, y2, &swap); |
|
|
|
|
|
|
|
b.push(points[i + (swap ? 1 : 0)].s, RANGE_S); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].b, RANGE_B); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].g, RANGE_G); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].r, RANGE_R); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].s, RANGE_S); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].b, RANGE_B); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].g, RANGE_G); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].r, RANGE_R); |
|
|
|
b.push(pack, RANGE_XY2); |
|
|
|
#else |
|
|
|
b.push(points[i].s, RANGE_S); |
|
|
|
b.push(points[i].b, RANGE_B); |
|
|
|
b.push(points[i].g, RANGE_G); |
|
|
|
b.push(points[i].r, RANGE_R); |
|
|
|
b.push(points[i].x, RANGE_X); |
|
|
|
b.push(points[i].y, RANGE_Y); |
|
|
|
#endif |
|
|
|
if(POINTS_PER_CELL == 2) |
|
|
|
{ |
|
|
|
int x1, y1, x2, y2; |
|
|
|
x1 = points[i].x; |
|
|
|
y1 = points[i].y; |
|
|
|
x2 = points[i + 1].x; |
|
|
|
y2 = points[i + 1].y; |
|
|
|
|
|
|
|
bool swap; |
|
|
|
uint32_t pack = pack_coords(x1, y1, x2, y2, &swap); |
|
|
|
|
|
|
|
b.push(points[i + (swap ? 1 : 0)].s, RANGE_S); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].b, RANGE_B); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].g, RANGE_G); |
|
|
|
b.push(points[i + (swap ? 1 : 0)].r, RANGE_R); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].s, RANGE_S); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].b, RANGE_B); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].g, RANGE_G); |
|
|
|
b.push(points[i + (swap ? 0 : 1)].r, RANGE_R); |
|
|
|
b.push(pack, RANGE_XY2); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
b.push(points[i].s, RANGE_S); |
|
|
|
b.push(points[i].b, RANGE_B); |
|
|
|
b.push(points[i].g, RANGE_G); |
|
|
|
b.push(points[i].r, RANGE_R); |
|
|
|
b.push(points[i].x, RANGE_X); |
|
|
|
b.push(points[i].y, RANGE_Y); |
|
|
|
} |
|
|
|
} |
|
|
|
b.push(height - 1, RANGE_H); |
|
|
|
b.push(width - 1, RANGE_W); |
|
|
@@ -1334,31 +1331,34 @@ int main(int argc, char *argv[]) |
|
|
|
/* Pop points from the bitstream */ |
|
|
|
for(int i = dw * dh; i--; ) |
|
|
|
{ |
|
|
|
#if POINTS_PER_CELL == 2 |
|
|
|
uint32_t pack = b.pop(RANGE_XY2); |
|
|
|
int x1, y1, x2, y2; |
|
|
|
unpack_coords(pack, &x1, &y1, &x2, &y2); |
|
|
|
|
|
|
|
points[i * 2 + 1].y = y2; |
|
|
|
points[i * 2 + 1].x = x2; |
|
|
|
points[i * 2 + 1].r = b.pop(RANGE_R); |
|
|
|
points[i * 2 + 1].g = b.pop(RANGE_G); |
|
|
|
points[i * 2 + 1].b = b.pop(RANGE_B); |
|
|
|
points[i * 2 + 1].s = b.pop(RANGE_S); |
|
|
|
points[i * 2].y = y1; |
|
|
|
points[i * 2].x = x1; |
|
|
|
points[i * 2].r = b.pop(RANGE_R); |
|
|
|
points[i * 2].g = b.pop(RANGE_G); |
|
|
|
points[i * 2].b = b.pop(RANGE_B); |
|
|
|
points[i * 2].s = b.pop(RANGE_S); |
|
|
|
#else |
|
|
|
points[i].y = b.pop(RANGE_Y); |
|
|
|
points[i].x = b.pop(RANGE_X); |
|
|
|
points[i].r = b.pop(RANGE_R); |
|
|
|
points[i].g = b.pop(RANGE_G); |
|
|
|
points[i].b = b.pop(RANGE_B); |
|
|
|
points[i].s = b.pop(RANGE_S); |
|
|
|
#endif |
|
|
|
if(POINTS_PER_CELL == 2) |
|
|
|
{ |
|
|
|
uint32_t pack = b.pop(RANGE_XY2); |
|
|
|
int x1, y1, x2, y2; |
|
|
|
unpack_coords(pack, &x1, &y1, &x2, &y2); |
|
|
|
|
|
|
|
points[i * 2 + 1].y = y2; |
|
|
|
points[i * 2 + 1].x = x2; |
|
|
|
points[i * 2 + 1].r = b.pop(RANGE_R); |
|
|
|
points[i * 2 + 1].g = b.pop(RANGE_G); |
|
|
|
points[i * 2 + 1].b = b.pop(RANGE_B); |
|
|
|
points[i * 2 + 1].s = b.pop(RANGE_S); |
|
|
|
points[i * 2].y = y1; |
|
|
|
points[i * 2].x = x1; |
|
|
|
points[i * 2].r = b.pop(RANGE_R); |
|
|
|
points[i * 2].g = b.pop(RANGE_G); |
|
|
|
points[i * 2].b = b.pop(RANGE_B); |
|
|
|
points[i * 2].s = b.pop(RANGE_S); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
points[i].y = b.pop(RANGE_Y); |
|
|
|
points[i].x = b.pop(RANGE_X); |
|
|
|
points[i].r = b.pop(RANGE_R); |
|
|
|
points[i].g = b.pop(RANGE_G); |
|
|
|
points[i].b = b.pop(RANGE_B); |
|
|
|
points[i].s = b.pop(RANGE_S); |
|
|
|
} |
|
|
|
} |
|
|
|
npoints = dw * dh * POINTS_PER_CELL; |
|
|
|
|
|
|
|