From 470a28b96be813ed3c8ebb0b4d6d13d11074b5e9 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 28 May 2009 01:54:56 +0000 Subject: [PATCH] Optimise img2twit's cell packing even more, gaining around 60 bits. git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@3536 92316355-f0b4-4df1-b90c-862c8a59935f --- examples/img2twit.cpp | 133 ++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/examples/img2twit.cpp b/examples/img2twit.cpp index 399e388..add22e3 100644 --- a/examples/img2twit.cpp +++ b/examples/img2twit.cpp @@ -74,6 +74,9 @@ static const uint32_t *unichars; * 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 + /* * These values can be overwritten at runtime */ @@ -115,8 +118,7 @@ static int MAX_ITERATIONS; static unsigned int TOTAL_CELLS; #define RANGE_XY (RANGE_Y*RANGE_X) -#define RANGE_XYXY (RANGE_XY*RANGE_XY) -//#define RANGE_XYXY (RANGE_Y*RANGE_X*(RANGE_Y*RANGE_X+1)/2) +#define RANGE_XY2 (RANGE_Y*RANGE_X*(RANGE_Y*RANGE_X+1)/2) #define RANGE_RXY (RANGE_Y*RANGE_X*RANGE_R) #define RANGE_GRXY (RANGE_Y*RANGE_X*RANGE_R*RANGE_G) #define RANGE_BGRXY (RANGE_Y*RANGE_X*RANGE_R*RANGE_G*RANGE_B) @@ -146,14 +148,11 @@ void compute_ranges(int width, int height) TOTAL_BITS = MAX_MSG_LEN * logf(NUM_CHARACTERS) / logf(2); HEADER_BITS = logf(RANGE_W * RANGE_H) / logf(2); DATA_BITS = TOTAL_BITS - HEADER_BITS; -#if POINTS_PER_CELL == 1 - CELL_BITS = logf(RANGE_SBGRXY) / logf(2); +#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 - // TODO: implement the following shit - //float coord_bits = logf((RANGE_Y * RANGE_X) * (RANGE_Y * RANGE_X + 1) / 2); - //float other_bits = logf(RANGE_R * RANGE_G * RANGE_B * RANGE_S); - //CELL_BITS = (coord_bits + 2 * other_bits) / logf(2); - CELL_BITS = 2 * logf(RANGE_SBGRXY) / logf(2); + CELL_BITS = logf(RANGE_SBGRXY) / logf(2); #endif TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS); MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS; @@ -508,17 +507,17 @@ static inline void set_point(int index, float x, float y, float r, float fx = (x - dx * RANGE_X) / RANGE_X; float fy = (y - dy * RANGE_Y) / RANGE_Y; - int is = range2int(s, RANGE_S); - - int ix = range2int(fx, RANGE_X); int iy = range2int(fy, RANGE_Y); + int ix = range2int(fx, RANGE_X); int ir = range2int(r, RANGE_R); int ig = range2int(g, RANGE_G); int ib = range2int(b, RANGE_B); - points[index] = iy + RANGE_Y * (ix + RANGE_X * (ib + RANGE_B * - (ig + (RANGE_R * ir + (RANGE_S * is))))); + int is = range2int(s, RANGE_S); + + points[index] = iy + RANGE_Y * (ix + RANGE_X * (ir + RANGE_R * + (ig + (RANGE_G * ib + (RANGE_B * is))))); } static inline void get_point(int index, float *x, float *y, float *r, @@ -532,20 +531,20 @@ static inline void get_point(int index, float *x, float *y, float *r, float fy = int2midrange(pt % RANGE_Y, RANGE_Y); pt /= RANGE_Y; float fx = int2midrange(pt % RANGE_X, RANGE_X); pt /= RANGE_X; - *x = (fx + dx) * RANGE_X /*+ 0.5 * (index & 1)*/; *y = (fy + dy) * RANGE_Y /*+ 0.5 * (index & 1)*/; + *x = (fx + dx) * RANGE_X /*+ 0.5 * (index & 1)*/; if(final) { - *b = int2fullrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R; + *r = int2fullrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R; *g = int2fullrange(pt % RANGE_G, RANGE_G); pt /= RANGE_G; - *r = int2fullrange(pt % RANGE_B, RANGE_B); pt /= RANGE_B; + *b = int2fullrange(pt % RANGE_B, RANGE_B); pt /= RANGE_B; } else { - *b = int2midrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R; + *r = int2midrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R; *g = int2midrange(pt % RANGE_G, RANGE_G); pt /= RANGE_G; - *r = int2midrange(pt % RANGE_B, RANGE_B); pt /= RANGE_B; + *b = int2midrange(pt % RANGE_B, RANGE_B); pt /= RANGE_B; } *s = int2fullrange(pt % RANGE_S, RANGE_S); pt /= RANGE_S; @@ -557,20 +556,37 @@ static void add_point(float x, float y, float r, float g, float b, float s) npoints++; } -static uint32_t pack_coords(int x1, int y1, int x2, int y2) +static uint32_t pack_coords(int x1, int y1, int x2, int y2, bool *swap) { - return ((y2 * RANGE_X + x2) * RANGE_Y + y1) * RANGE_X + x1; + int k1 = y1 * RANGE_X + x1; + int k2 = y2 * RANGE_X + x2; + + /* XXX: this should not happen */ + if(k1 == k2) + k1 += (x1 > 0 ? -1 : 1); + + *swap = k1 > k2; + + if(*swap) + { + int tmp = k1; k1 = k2; k2 = tmp; + } + + return k2 * (k2 + 1) / 2 + k1; } static void unpack_coords(uint32_t pack, int *x1, int *y1, int *x2, int *y2) { - *x1 = pack % RANGE_X; pack /= RANGE_X; - *y1 = pack % RANGE_Y; pack /= RANGE_Y; - *x2 = pack % RANGE_X; pack /= RANGE_X; - *y2 = pack % RANGE_Y; + int k2 = ((int)sqrt(1.0 + 8 * pack) - 1) / 2; + int k1 = pack - k2 * (k2 + 1) / 2; + + *x1 = k1 % RANGE_X; + *y1 = k1 / RANGE_X; + *x2 = k2 % RANGE_X; + *y2 = k2 / RANGE_X; } -#if 0 +#if RANDOM_START == 1 static void add_random_point() { points[npoints] = det_rand(RANGE_SBGRXY); @@ -845,34 +861,36 @@ static void analyse(pipi_image_t *src) else wmin = 0.0, wmax = 1.0; -#if 0 +#if RANDOM_START == 1 +# if POINTS_PER_CELL == 2 add_random_point(); +# endif 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 POINTS_PER_CELL == 1 if(total < min + (max - min) / 2) { -#endif +# 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 +# if POINTS_PER_CELL == 1 } else { -#endif +# 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 +# if POINTS_PER_CELL == 1 } -#endif +# endif #endif } } @@ -938,8 +956,8 @@ int main(int argc, char *argv[]) ITERATIONS_PER_POINT = 10 * atof(myoptarg); if(ITERATIONS_PER_POINT < 0) ITERATIONS_PER_POINT = 0; - else if(ITERATIONS_PER_POINT > 100) - ITERATIONS_PER_POINT = 100; + else if(ITERATIONS_PER_POINT > 200) + ITERATIONS_PER_POINT = 200; break; case 'd': DEBUG_MODE = true; @@ -953,7 +971,7 @@ int main(int argc, char *argv[]) printf(" -o, --output output resulting image to filename\n"); printf(" -l, --length message length in characters (default 140)\n"); printf(" -c, --charset character set to use (ascii, [cjk], symbols)\n"); - printf(" -q, --quality set image quality (0 - 10) (default 5)\n"); + printf(" -q, --quality set image quality (0 - 20) (default 5)\n"); printf(" -d, --debug print debug information\n"); printf(" -h, --help display this help and exit\n"); printf("\n"); @@ -1095,7 +1113,7 @@ int main(int argc, char *argv[]) { revert; compute_ranges(width, height); } \ } while(0) - for(int i = 0; i < 5; i++) + for(int i = 0; i < 2; i++) { TRY(RANGE_G++, RANGE_G--); TRY(RANGE_R++, RANGE_R--); @@ -1141,9 +1159,9 @@ int main(int argc, char *argv[]) if(srcname) { /* Resize and filter image to better state */ - tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y); + tmp = pipi_gaussian_blur(src, 0.5 * dw * RANGE_X / width); pipi_free(src); - src = pipi_median_ext(tmp, 1, 1); + src = pipi_resize(tmp, dw * RANGE_X, dh * RANGE_Y); pipi_free(tmp); /* Analyse image */ @@ -1181,14 +1199,14 @@ int main(int argc, char *argv[]) /* Compute the affected image zone */ float fx, fy, fr, fg, fb, fs; get_point(pt, &fx, &fy, &fr, &fg, &fb, &fs); - int zonex = (int)fx / RANGE_X - 1; - int zoney = (int)fy / RANGE_Y - 1; - int zonew = 3; - int zoneh = 3; - if(zonex < 0) { zonex = 0; zonew--; } - if(zoney < 0) { zoney = 0; zoneh--; } - if(zonex + zonew >= (int)dw) { zonew--; } - if(zoney + zoneh >= (int)dh) { zoneh--; } + int zonex = (int)fx / RANGE_X - 2; + int zoney = (int)fy / RANGE_Y - 2; + int zonew = 4; + int zoneh = 4; + if(zonex < 0) { zonew += zonex; zonex = 0; } + if(zoney < 0) { zoneh += zoney; zoney = 0;; } + if(zonex + zonew > (int)dw) { zonew = dw - zonex; } + if(zoney + zoneh > (int)dh) { zoneh = dh - zoney; } /* Choose random operations and measure their effect */ uint8_t op1 = rand_op(); @@ -1246,10 +1264,10 @@ int main(int argc, char *argv[]) success++; /* Save image! */ - //char buf[128]; - //sprintf(buf, "twit%08i.bmp", success); - //if((success % 10) == 0) - // pipi_save(tmp, buf); + char buf[128]; + sprintf(buf, "twit%08i.bmp", success); + if((success % 10) == 0) + pipi_save(tmp, buf); } else { @@ -1298,11 +1316,13 @@ int main(int argc, char *argv[]) y1 = points[i] % RANGE_Y; x2 = (points[i + 1] / RANGE_Y) % RANGE_X; y2 = points[i + 1] % RANGE_Y; - uint32_t pack = pack_coords(x1, y1, x2, y2); - b.push(points[i] / RANGE_XY, RANGE_SBGR); - b.push(points[i + 1] / RANGE_XY, RANGE_SBGR); - b.push(pack, RANGE_XYXY); + bool swap; + uint32_t pack = pack_coords(x1, y1, x2, y2, &swap); + + b.push(points[i + (swap ? 1 : 0)] / RANGE_XY, RANGE_SBGR); + b.push(points[i + (swap ? 0 : 1)] / RANGE_XY, RANGE_SBGR); + b.push(pack, RANGE_XY2); #else b.push(points[i], RANGE_SBGRXY); #endif @@ -1321,10 +1341,11 @@ int main(int argc, char *argv[]) for(int i = dw * dh; i--; ) { #if POINTS_PER_CELL == 2 - uint32_t pack = b.pop(RANGE_XYXY); + uint32_t pack = b.pop(RANGE_XY2); uint32_t p2 = b.pop(RANGE_SBGR); uint32_t p1 = b.pop(RANGE_SBGR); int x1, y1, x2, y2; + unpack_coords(pack, &x1, &y1, &x2, &y2); points[i * 2] = p1 * RANGE_XY + x1 * RANGE_Y + y1; points[i * 2 + 1] = p2 * RANGE_XY + x2 * RANGE_Y + y2;