diff --git a/examples/Makefile.am b/examples/Makefile.am index fc94a24..6d486fb 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -48,7 +48,7 @@ if USE_CGAL img2twit = img2twit endif -img2twit_SOURCES = img2twit.cpp +img2twit_SOURCES = img2twit.cpp ../genethumb/mygetopt.c img2twit_LDADD = ../pipi/libpipi.la img2twit_CXXFLAGS = -frounding-math img2twit_LDFLAGS = -lCGAL diff --git a/examples/img2twit.cpp b/examples/img2twit.cpp index 522ff08..38d7e82 100644 --- a/examples/img2twit.cpp +++ b/examples/img2twit.cpp @@ -11,24 +11,17 @@ #include +#include "../genethumb/mygetopt.h" + /* * 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_ */ static const uint32_t unichars[] = { /* Printable ASCII (except space) */ - 0x0021, 0x007f, + //0x0021, 0x007f, /* Stupid symbols and Dingbats shit */ //0x25a0, 0x2600, /* Geometric Shapes */ @@ -41,10 +34,10 @@ static const uint32_t unichars[] = //0x2e80, 0x2e9a, 0x2e9b, 0x2ef4, /* CJK Radicals Supplement */ //0x2f00, 0x2fd6, /* Kangxi Radicals */ //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 */ - 0xac00, 0xd7a4, /* Hangul Syllables */ + //0xac00, 0xd7a4, /* Hangul Syllables */ /* More Chinese */ //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_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 */ @@ -87,6 +91,7 @@ static float DATA_BITS; static float CELL_BITS; static int NUM_CHARACTERS; +static int MAX_ITERATIONS; static unsigned int TOTAL_CELLS; #define RANGE_SY (RANGE_S*RANGE_Y) @@ -103,7 +108,7 @@ typedef std::vector > Point_coordinate_vector; static unsigned int dw, dh; /* Global point encoding */ -static uint32_t points[1024]; +static uint32_t points[4096]; /* FIXME: allocate this dynamically */ static int npoints = 0; /* Global triangulation */ @@ -225,7 +230,9 @@ static void fwrite_utf8(FILE *f, uint32_t x) class bitstack { public: - bitstack() { init(0); } + bitstack() { alloc(); init(0); } + + ~bitstack() { delete[] digits; delete[] str; } char const *tostring() { @@ -264,13 +271,26 @@ public: } 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) { - memset(digits, 0, sizeof(digits)); - digits[0] = i; 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! */ @@ -280,7 +300,7 @@ private: void add(bitstack const &_b) { /* Copy the operand in case we get added to ourselves */ - bitstack b = _b; + bitstack b(_b); uint64_t x = 0; if(msb < b.msb) @@ -304,7 +324,7 @@ private: void sub(bitstack const &_b) { /* Copy the operand in case we get substracted from ourselves */ - bitstack b = _b; + bitstack b(_b); uint64_t x = 0; /* We cannot substract a larger number! */ @@ -337,7 +357,7 @@ private: void mul(uint32_t x) { - bitstack b = *this; + bitstack b(*this); init(0); while(x) @@ -351,7 +371,7 @@ private: uint32_t div(uint32_t x) { - bitstack b = *this; + bitstack b(*this); for(int i = msb; i >= 0; i--) { @@ -369,8 +389,8 @@ private: } 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 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; *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) { - 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); float *data = (float *)p->pixels; - int i, x, y; + int x, y; memset(lookup, 0, sizeof(lookup)); dt.clear(); - for(i = 0; i < npoints; i++) + for(int i = 0; i < npoints; i++) { float 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)); + /* Keep link to point */ + lookup[(int)(fx * 2) + dw * RANGE_X * 2 * (int)(fy * 2)] = i; } /* 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, 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.000000000000001f; Point_coordinate_vector::iterator 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) 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); @@ -737,23 +759,86 @@ add_random_point(); } } +#define MOREINFO "Try `%s --help' for more information.\n" + int main(int argc, char *argv[]) { int opstats[2 * NB_OPS]; bitstack b; + char const *srcname = NULL, *dstname = NULL; pipi_image_t *src, *tmp, *dst; double error = 1.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 output resulting image to filename\n"); + printf(" -q, --quality 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 .\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 Encode image and print result to stdout\n"); - fprintf(stderr, " img2twit -o 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; } + 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); /* Precompute bit allocation */ @@ -771,8 +856,9 @@ int main(int argc, char *argv[]) CELL_BITS = 2 * logf(RANGE_SYXRGB) / logf(2); #endif TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS); + MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS; - if(decode) + if(dstname) { /* Decoding mode: read UTF-8 text from stdin, find each * character's index in our character list, and push it to our @@ -791,11 +877,11 @@ int main(int argc, char *argv[]) else { /* Argument given: open image for encoding */ - src = pipi_load(argv[1]); + src = pipi_load(srcname); if(!src) { - fprintf(stderr, "Error loading %s\n", argv[1]); + fprintf(stderr, "Error loading %s\n", srcname); return EXIT_FAILURE; } @@ -823,31 +909,33 @@ int main(int argc, char *argv[]) while(dw * (dh + 1) <= TOTAL_CELLS) dw++; /* 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 */ tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y); pipi_free(src); src = pipi_median_ext(tmp, 1, 1); pipi_free(tmp); +pipi_save(src, "lol.bmp"); /* Analyse image */ analyse(src); @@ -855,11 +943,11 @@ int main(int argc, char *argv[]) /* Render what we just computed */ tmp = pipi_new(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); -#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)); for(int iter = 0, stuck = 0, failures = 0, success = 0; @@ -872,11 +960,9 @@ int main(int argc, char *argv[]) failures = 0; } -#if !DEBUG - if(!(iter % 16)) + if(!DEBUG && !(iter % 16)) fprintf(stderr, "\rEncoding... %i%%", iter * 100 / MAX_ITERATIONS); -#endif pipi_image_t *scrap = pipi_copy(tmp); @@ -939,10 +1025,12 @@ int main(int argc, char *argv[]) pipi_free(tmp); 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; opstats[op1 * 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 dst = pipi_resize(tmp, width, height); @@ -1026,7 +1115,7 @@ int main(int argc, char *argv[]) pipi_free(tmp); /* Save image and bail out */ - pipi_save(dst, argv[2]); + pipi_save(dst, dstname); pipi_free(dst); } diff --git a/genethumb/mygetopt.h b/genethumb/mygetopt.h index 2cf62e5..4b395ed 100644 --- a/genethumb/mygetopt.h +++ b/genethumb/mygetopt.h @@ -16,6 +16,11 @@ * mygetopt.h: getopt_long reimplementation */ +#ifdef __cplusplus +extern "C" +{ +#endif + struct myoption { const char *name; @@ -29,3 +34,7 @@ extern char *myoptarg; int mygetopt(int, char * const[], const char *, const struct myoption *, int *); +#ifdef __cplusplus +} +#endif +