@@ -11,24 +11,17 @@
#include <pipi.h>
#include <pipi.h>
#include "../genethumb/mygetopt.h"
/*
/*
* User-definable settings.
* User-definable settings.
*/
*/
/* Debug mode */
#define DEBUG 1
/* Encoding iterations: 1000 gives a fast answer, 10000 gives good quality */
#define MAX_ITERATIONS 10000
/* The maximum message length */
#define MAX_MSG_LEN 140
/* The Unicode characters at disposal - XXX: must be _ordered_ */
/* The Unicode characters at disposal - XXX: must be _ordered_ */
static const uint32_t unichars[] =
static const uint32_t unichars[] =
{
{
/* Printable ASCII (except space) */
/* Printable ASCII (except space) */
0x0021, 0x007f,
//0x0021, 0x007f,
/* Stupid symbols and Dingbats shit */
/* Stupid symbols and Dingbats shit */
//0x25a0, 0x2600, /* Geometric Shapes */
//0x25a0, 0x2600, /* Geometric Shapes */
@@ -41,10 +34,10 @@ static const uint32_t unichars[] =
//0x2e80, 0x2e9a, 0x2e9b, 0x2ef4, /* CJK Radicals Supplement */
//0x2e80, 0x2e9a, 0x2e9b, 0x2ef4, /* CJK Radicals Supplement */
//0x2f00, 0x2fd6, /* Kangxi Radicals */
//0x2f00, 0x2fd6, /* Kangxi Radicals */
//0x3400, 0x4db6, /* CJK Unified Ideographs Extension A */
//0x3400, 0x4db6, /* CJK Unified Ideographs Extension A */
// 0x4e00, 0x9fa6, /* CJK Unified Ideographs */
0x4e00, 0x9fa6, /* CJK Unified Ideographs */
/* Korean - most people don't know the difference anyway */
/* Korean - most people don't know the difference anyway */
0xac00, 0xd7a4, /* Hangul Syllables */
// 0xac00, 0xd7a4, /* Hangul Syllables */
/* More Chinese */
/* More Chinese */
//0xf900, 0xfa2e, 0xfa30, 0xfa6b, 0xfa70, 0xfada, /* CJK Compat. Idgphs. */
//0xf900, 0xfa2e, 0xfa30, 0xfa6b, 0xfa70, 0xfada, /* CJK Compat. Idgphs. */
@@ -77,6 +70,17 @@ static unsigned int RANGE_G = 6;
static unsigned int RANGE_B = 6;
static unsigned int RANGE_B = 6;
static unsigned int RANGE_S = 1;
static unsigned int RANGE_S = 1;
/*
* These values can be overwritten at runtime
*/
static bool DEBUG = false;
/* The maximum message length */
static int MAX_MSG_LEN = 1140;
static int ITERATIONS_PER_POINT = 50;
/*
/*
* These values are computed at runtime
* These values are computed at runtime
*/
*/
@@ -87,6 +91,7 @@ static float DATA_BITS;
static float CELL_BITS;
static float CELL_BITS;
static int NUM_CHARACTERS;
static int NUM_CHARACTERS;
static int MAX_ITERATIONS;
static unsigned int TOTAL_CELLS;
static unsigned int TOTAL_CELLS;
#define RANGE_SY (RANGE_S*RANGE_Y)
#define RANGE_SY (RANGE_S*RANGE_Y)
@@ -103,7 +108,7 @@ typedef std::vector<std::pair<K::Point_2, K::FT> > Point_coordinate_vector;
static unsigned int dw, dh;
static unsigned int dw, dh;
/* Global point encoding */
/* Global point encoding */
static uint32_t points[102 4];
static uint32_t points[4096 ]; /* FIXME: allocate this dynamically */
static int npoints = 0;
static int npoints = 0;
/* Global triangulation */
/* Global triangulation */
@@ -225,7 +230,9 @@ static void fwrite_utf8(FILE *f, uint32_t x)
class bitstack
class bitstack
{
{
public:
public:
bitstack() { init(0); }
bitstack() { alloc(); init(0); }
~bitstack() { delete[] digits; delete[] str; }
char const *tostring()
char const *tostring()
{
{
@@ -264,13 +271,26 @@ public:
}
}
private:
private:
bitstack(uint32_t i) { init(i); }
bitstack(uint32_t i) { alloc(); init(i); }
bitstack(bitstack const &b)
{
alloc();
msb = b.msb;
memcpy(digits, b.digits, (MAX_MSG_LEN + 1) * sizeof(uint32_t));
}
void alloc()
{
digits = new uint32_t[MAX_MSG_LEN + 1];
str = new char[(MAX_MSG_LEN + 1) * 8 + 1];
}
void init(uint32_t i)
void init(uint32_t i)
{
{
memset(digits, 0, sizeof(digits));
digits[0] = i;
msb = 0;
msb = 0;
memset(digits, 0, (MAX_MSG_LEN + 1) * sizeof(uint32_t));
digits[0] = i;
}
}
/* Could be done much faster, but we don't care! */
/* Could be done much faster, but we don't care! */
@@ -280,7 +300,7 @@ private:
void add(bitstack const &_b)
void add(bitstack const &_b)
{
{
/* Copy the operand in case we get added to ourselves */
/* Copy the operand in case we get added to ourselves */
bitstack b = _b ;
bitstack b(_b) ;
uint64_t x = 0;
uint64_t x = 0;
if(msb < b.msb)
if(msb < b.msb)
@@ -304,7 +324,7 @@ private:
void sub(bitstack const &_b)
void sub(bitstack const &_b)
{
{
/* Copy the operand in case we get substracted from ourselves */
/* Copy the operand in case we get substracted from ourselves */
bitstack b = _b ;
bitstack b(_b) ;
uint64_t x = 0;
uint64_t x = 0;
/* We cannot substract a larger number! */
/* We cannot substract a larger number! */
@@ -337,7 +357,7 @@ private:
void mul(uint32_t x)
void mul(uint32_t x)
{
{
bitstack b = *this ;
bitstack b(*this) ;
init(0);
init(0);
while(x)
while(x)
@@ -351,7 +371,7 @@ private:
uint32_t div(uint32_t x)
uint32_t div(uint32_t x)
{
{
bitstack b = *this ;
bitstack b(*this) ;
for(int i = msb; i >= 0; i--)
for(int i = msb; i >= 0; i--)
{
{
@@ -369,8 +389,8 @@ private:
}
}
int msb;
int msb;
uint32_t digits[MAX_MSG_LEN + 1] ; /* This is a safe max value */
char str[(MAX_MSG_LEN + 1) * 8 + 1] ;
uint32_t * digits;
char * str;
};
};
/*
/*
@@ -435,8 +455,8 @@ 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 fy = int2midrange(pt % RANGE_Y, RANGE_Y); pt /= RANGE_Y;
float fx = int2midrange(pt % RANGE_X, RANGE_X); pt /= RANGE_X;
float fx = int2midrange(pt % RANGE_X, RANGE_X); pt /= RANGE_X;
*x = (fx + dx) * RANGE_X;
*y = (fy + dy) * RANGE_Y;
*x = (fx + dx) * RANGE_X /*+ 0.5 * (index & 1)*/ ;
*y = (fy + dy) * RANGE_Y /*+ 0.5 * (index & 1)*/ ;
*b = int2midrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R;
*b = int2midrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R;
*g = int2midrange(pt % RANGE_G, RANGE_G); pt /= RANGE_G;
*g = int2midrange(pt % RANGE_G, RANGE_G); pt /= RANGE_G;
@@ -585,19 +605,20 @@ static uint32_t apply_op(uint8_t op, uint32_t val)
static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
{
{
uint8_t lookup[TOTAL_CELLS * RANGE_X * RANGE_Y ];
int lookup[dw * RANGE_X * 2 * dh * RANGE_Y * 2 ];
pipi_pixels_t *p = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
pipi_pixels_t *p = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
float *data = (float *)p->pixels;
float *data = (float *)p->pixels;
int i, x, y;
int x, y;
memset(lookup, 0, sizeof(lookup));
memset(lookup, 0, sizeof(lookup));
dt.clear();
dt.clear();
for(i = 0; i < npoints; i++)
for(int i = 0; i < npoints; i++)
{
{
float fx, fy, fr, fg, fb, fs;
float fx, fy, fr, fg, fb, fs;
get_point(i, &fx, &fy, &fr, &fg, &fb, &fs);
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));
dt.insert(K::Point_2(fx, fy));
/* Keep link to point */
lookup[(int)(fx * 2) + dw * RANGE_X * 2 * (int)(fy * 2)] = i;
}
}
/* Add fake points to close the triangulation */
/* Add fake points to close the triangulation */
@@ -618,7 +639,7 @@ static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
CGAL::natural_neighbor_coordinates_2(dt, m,
CGAL::natural_neighbor_coordinates_2(dt, m,
std::back_inserter(coords));
std::back_inserter(coords));
float r = 0.0f, g = 0.0f, b = 0.0f, norm = 0.0f;
float r = 0.0f, g = 0.0f, b = 0.0f, norm = 0.000000000000001 f;
Point_coordinate_vector::iterator it;
Point_coordinate_vector::iterator it;
for(it = coords.begin(); it != coords.end(); ++it)
for(it = coords.begin(); it != coords.end(); ++it)
@@ -631,7 +652,8 @@ static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
if(fx < 0 || fy < 0 || fx > p->w - 1 || fy > p->h - 1)
if(fx < 0 || fy < 0 || fx > p->w - 1 || fy > p->h - 1)
continue;
continue;
int index = lookup[(int)fx + dw * RANGE_X * (int)fy];
int index = lookup[(int)(fx * 2)
+ dw * RANGE_X * 2 * (int)(fy * 2)];
get_point(index, &fx, &fy, &fr, &fg, &fb, &fs);
get_point(index, &fx, &fy, &fr, &fg, &fb, &fs);
@@ -737,23 +759,86 @@ add_random_point();
}
}
}
}
#define MOREINFO "Try `%s --help' for more information.\n"
int main(int argc, char *argv[])
int main(int argc, char *argv[])
{
{
int opstats[2 * NB_OPS];
int opstats[2 * NB_OPS];
bitstack b;
bitstack b;
char const *srcname = NULL, *dstname = NULL;
pipi_image_t *src, *tmp, *dst;
pipi_image_t *src, *tmp, *dst;
double error = 1.0;
double error = 1.0;
int width, height, ret = 0;
int width, height, ret = 0;
bool decode = (argc >= 2 && !strcmp(argv[1], "-o"));
if(!((argc == 2 && !decode) || (argc == 3 && decode)))
/* Parse command-line options */
for(;;)
{
int option_index = 0;
static struct myoption long_options[] =
{
{ "output", 1, NULL, 'o' },
{ "quality", 1, NULL, 'q' },
{ "debug", 0, NULL, 'd' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 },
};
int c = mygetopt(argc, argv, "o:q:dh", long_options, &option_index);
if(c == -1)
break;
switch(c)
{
case 'o':
dstname = myoptarg;
break;
case 'q':
ITERATIONS_PER_POINT = 10 * atoi(myoptarg);
if(ITERATIONS_PER_POINT < 0)
ITERATIONS_PER_POINT = 0;
else if(ITERATIONS_PER_POINT > 10)
ITERATIONS_PER_POINT = 10;
break;
case 'd':
DEBUG = true;
break;
case 'h':
printf("Usage: img2twit [OPTIONS] SOURCE\n");
printf(" img2twit [OPTIONS] -o DESTINATION\n");
printf("Encode SOURCE image to stdout or decode stdin to DESTINATION.\n");
printf("\n");
printf("Mandatory arguments to long options are mandatory for short options too.\n");
printf(" -o, --output <filename> output resulting image to filename\n");
printf(" -q, --quality <rate> set image quality (0 - 10) (default 5)\n");
printf(" -d, --debug print debug information\n");
printf(" -h, --help display this help and exit\n");
printf("\n");
printf("Written by Sam Hocevar. Report bugs to <sam@hocevar.net>.\n");
return EXIT_SUCCESS;
default:
fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
printf(MOREINFO, argv[0]);
return EXIT_FAILURE;
}
}
if(myoptind == argc && !dstname)
{
{
fprintf(stderr, "img2twit: wrong argument count\n");
fprintf(stderr, "Usage: img2twit <image> Encode image and print result to stdout\n");
fprintf(stderr, " img2twit -o <image> Read data from stdin and decode image to file\n");
fprintf(stderr, "%s: too few arguments\n", argv[0]);
printf(MOREINFO, argv[0]);
return EXIT_FAILURE;
return EXIT_FAILURE;
}
}
if((myoptind == argc - 1 && dstname) || myoptind < argc - 1)
{
fprintf(stderr, "%s: too many arguments\n", argv[0]);
printf(MOREINFO, argv[0]);
return EXIT_FAILURE;
}
if(myoptind == argc - 1)
srcname = argv[myoptind];
pipi_set_gamma(1.0);
pipi_set_gamma(1.0);
/* Precompute bit allocation */
/* Precompute bit allocation */
@@ -771,8 +856,9 @@ int main(int argc, char *argv[])
CELL_BITS = 2 * logf(RANGE_SYXRGB) / logf(2);
CELL_BITS = 2 * logf(RANGE_SYXRGB) / logf(2);
#endif
#endif
TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS);
TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS);
MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS;
if(decod e)
if(dstnam e)
{
{
/* Decoding mode: read UTF-8 text from stdin, find each
/* Decoding mode: read UTF-8 text from stdin, find each
* character's index in our character list, and push it to our
* character's index in our character list, and push it to our
@@ -791,11 +877,11 @@ int main(int argc, char *argv[])
else
else
{
{
/* Argument given: open image for encoding */
/* Argument given: open image for encoding */
src = pipi_load(argv[1] );
src = pipi_load(srcname );
if(!src)
if(!src)
{
{
fprintf(stderr, "Error loading %s\n", argv[1] );
fprintf(stderr, "Error loading %s\n", srcname );
return EXIT_FAILURE;
return EXIT_FAILURE;
}
}
@@ -823,31 +909,33 @@ int main(int argc, char *argv[])
while(dw * (dh + 1) <= TOTAL_CELLS) dw++;
while(dw * (dh + 1) <= TOTAL_CELLS) dw++;
/* Print debug information */
/* Print debug information */
#if DEBUG
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
fprintf(stderr, "Available bits: %f\n", TOTAL_BITS);
fprintf(stderr, "Maximum image resolution: %ix%i\n", MAX_W, MAX_H);
fprintf(stderr, "Image resolution: %ix%i\n", width, height);
fprintf(stderr, "Header bits: %f\n", HEADER_BITS);
fprintf(stderr, "Bits available for data: %f\n", DATA_BITS);
fprintf(stderr, "Cell bits: %f\n", CELL_BITS);
fprintf(stderr, "Available cells: %i\n", TOTAL_CELLS);
fprintf(stderr, "Wasted bits: %f\n", DATA_BITS - CELL_BITS * TOTAL_CELLS);
fprintf(stderr, "Chosen image ratio: %i:%i (wasting %i point cells)\n",
dw, dh, TOTAL_CELLS - dw * dh);
fprintf(stderr, "Total wasted bits: %f\n",
DATA_BITS - CELL_BITS * dw * dh);
#endif
if(DEBUG)
{
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
fprintf(stderr, "Available bits: %f\n", TOTAL_BITS);
fprintf(stderr, "Maximum image resolution: %ix%i\n", MAX_W, MAX_H);
fprintf(stderr, "Image resolution: %ix%i\n", width, height);
fprintf(stderr, "Header bits: %f\n", HEADER_BITS);
fprintf(stderr, "Bits available for data: %f\n", DATA_BITS);
fprintf(stderr, "Cell bits: %f\n", CELL_BITS);
fprintf(stderr, "Available cells: %i\n", TOTAL_CELLS);
fprintf(stderr, "Wasted bits: %f\n",
DATA_BITS - CELL_BITS * TOTAL_CELLS);
fprintf(stderr, "Chosen image ratio: %i:%i (wasting %i point cells)\n",
dw, dh, TOTAL_CELLS - dw * dh);
fprintf(stderr, "Total wasted bits: %f\n",
DATA_BITS - CELL_BITS * dw * dh);
}
if(src)
if(srcname )
{
{
/* Resize and filter image to better state */
/* Resize and filter image to better state */
tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
pipi_free(src);
pipi_free(src);
src = pipi_median_ext(tmp, 1, 1);
src = pipi_median_ext(tmp, 1, 1);
pipi_free(tmp);
pipi_free(tmp);
pipi_save(src, "lol.bmp");
/* Analyse image */
/* Analyse image */
analyse(src);
analyse(src);
@@ -855,11 +943,11 @@ int main(int argc, char *argv[])
/* Render what we just computed */
/* Render what we just computed */
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
pipi_save(tmp, "lol2.bmp");
error = pipi_measure_rmsd(src, tmp);
error = pipi_measure_rmsd(src, tmp);
#if DEBUG
fprintf(stderr, "Distance: %2.10g\n", error);
#endif
if(DEBUG)
fprintf(stderr, "Initial distance: %2.10g\n", error);
memset(opstats, 0, sizeof(opstats));
memset(opstats, 0, sizeof(opstats));
for(int iter = 0, stuck = 0, failures = 0, success = 0;
for(int iter = 0, stuck = 0, failures = 0, success = 0;
@@ -872,11 +960,9 @@ int main(int argc, char *argv[])
failures = 0;
failures = 0;
}
}
#if !DEBUG
if(!(iter % 16))
if(!DEBUG && !(iter % 16))
fprintf(stderr, "\rEncoding... %i%%",
fprintf(stderr, "\rEncoding... %i%%",
iter * 100 / MAX_ITERATIONS);
iter * 100 / MAX_ITERATIONS);
#endif
pipi_image_t *scrap = pipi_copy(tmp);
pipi_image_t *scrap = pipi_copy(tmp);
@@ -939,10 +1025,12 @@ int main(int argc, char *argv[])
pipi_free(tmp);
pipi_free(tmp);
tmp = scrap;
tmp = scrap;
#if DEBUG
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n", iter,
(int)((error - besterr) * 100000000), error, op1, pt);
#endif
if(DEBUG)
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n",
iter, (int)((error - besterr) * 100000000), error,
op1, pt);
error = besterr;
error = besterr;
opstats[op1 * 2 + 1]++;
opstats[op1 * 2 + 1]++;
//opstats[op2 * 2 + 1]++;
//opstats[op2 * 2 + 1]++;
@@ -963,25 +1051,26 @@ int main(int argc, char *argv[])
}
}
}
}
fprintf(stderr, "\r \r");
#if DEBUG
for(int j = 0; j < 2; j++)
if(DEBUG)
{
{
fprintf(stderr, "operation: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", i);
fprintf(stderr, "\nattempts: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2]);
fprintf(stderr, "\nsuccesses: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2 + 1]);
fprintf(stderr, "\n");
}
for(int j = 0; j < 2; j++)
{
fprintf(stderr, "operation: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", i);
fprintf(stderr, "\nattempts: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2]);
fprintf(stderr, "\nsuccesses: ");
for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
fprintf(stderr, "%4i ", opstats[i * 2 + 1]);
fprintf(stderr, "\n");
}
fprintf(stderr, "Distance: %2.10g\n", error);
#endif
fprintf(stderr, "Distance: %2.10g\n", error);
}
else
fprintf(stderr, "\r \r");
#if 0
#if 0
dst = pipi_resize(tmp, width, height);
dst = pipi_resize(tmp, width, height);
@@ -1026,7 +1115,7 @@ int main(int argc, char *argv[])
pipi_free(tmp);
pipi_free(tmp);
/* Save image and bail out */
/* Save image and bail out */
pipi_save(dst, argv[2] );
pipi_save(dst, dstname );
pipi_free(dst);
pipi_free(dst);
}
}