diff --git a/examples/img2twit.cpp b/examples/img2twit.cpp index 38d7e82..87894a6 100644 --- a/examples/img2twit.cpp +++ b/examples/img2twit.cpp @@ -1,3 +1,15 @@ +/* + * img2twit Image to short text message encoder/decoder + * Copyright (c) 2009 Sam Hocevar + * All Rights Reserved + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + */ + #include "config.h" #include @@ -56,6 +68,19 @@ static const uint32_t unichars[] = /* How does the algorithm work: one point per cell, or two */ #define POINTS_PER_CELL 2 +/* + * These values can be overwritten at runtime + */ + +/* Debug mode */ +static bool DEBUG_MODE = false; + +/* The maximum message length */ +static int MAX_MSG_LEN = 140; + +/* Iterations per point -- larger means slower but nicer */ +static int ITERATIONS_PER_POINT = 50; + /* 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 @@ -70,17 +95,6 @@ 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 */ @@ -273,6 +287,13 @@ public: private: bitstack(uint32_t i) { alloc(); init(i); } + bitstack(bitstack &b) + { + alloc(); + msb = b.msb; + memcpy(digits, b.digits, (MAX_MSG_LEN + 1) * sizeof(uint32_t)); + } + bitstack(bitstack const &b) { alloc(); @@ -686,7 +707,7 @@ static void analyse(pipi_image_t *src) for(unsigned int dy = 0; dy < dh; dy++) for(unsigned int dx = 0; dx < dw; dx++) { - float min = 1.1f, max = -0.1f; + float min = 1.1f, max = -0.1f, mr = 0.0f, mg = 0.0f, mb = 0.0f; float total = 0.0; int xmin = 0, xmax = 0, ymin = 0, ymax = 0; int npixels = 0; @@ -699,6 +720,11 @@ static void analyse(pipi_image_t *src) lum += data[4 * (ix + iy * p->w) + 0]; lum += data[4 * (ix + iy * p->w) + 1]; lum += data[4 * (ix + iy * p->w) + 2]; + lum /= 3; + + mr += data[4 * (ix + iy * p->w) + 0]; + mg += data[4 * (ix + iy * p->w) + 1]; + mb += data[4 * (ix + iy * p->w) + 2]; if(lum < min) { @@ -719,6 +745,9 @@ static void analyse(pipi_image_t *src) } total /= npixels; + mr /= npixels; + mg /= npixels; + mb /= npixels; float wmin, wmax; @@ -730,17 +759,19 @@ static void analyse(pipi_image_t *src) wmin = 0.0, wmax = 1.0; #if 0 -add_random_point(); -add_random_point(); + add_random_point(); + 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], - data[4 * (xmin + ymin * p->w) + 1], - data[4 * (xmin + ymin * p->w) + 2], + 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 } @@ -748,9 +779,9 @@ add_random_point(); { #endif 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], + 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 } @@ -764,7 +795,6 @@ add_random_point(); 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; @@ -777,12 +807,13 @@ int main(int argc, char *argv[]) static struct myoption long_options[] = { { "output", 1, NULL, 'o' }, + { "length", 1, NULL, 'l' }, { "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); + int c = mygetopt(argc, argv, "o:l:q:dh", long_options, &option_index); if(c == -1) break; @@ -792,15 +823,23 @@ int main(int argc, char *argv[]) case 'o': dstname = myoptarg; break; + case 'l': + MAX_MSG_LEN = atoi(myoptarg); + if(MAX_MSG_LEN < 16) + { + fprintf(stderr, "Warning: rounding minimum message length to 16\n"); + MAX_MSG_LEN = 16; + } + 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; + else if(ITERATIONS_PER_POINT > 100) + ITERATIONS_PER_POINT = 100; break; case 'd': - DEBUG = true; + DEBUG_MODE = true; break; case 'h': printf("Usage: img2twit [OPTIONS] SOURCE\n"); @@ -809,6 +848,7 @@ int main(int argc, char *argv[]) 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(" -l, --length message length in characters (default 140)\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"); @@ -858,6 +898,9 @@ int main(int argc, char *argv[]) TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS); MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS; + bitstack b; /* We cannot declare this before, because MAX_MSG_LEN + * wouldn't be defined. */ + if(dstname) { /* Decoding mode: read UTF-8 text from stdin, find each @@ -909,7 +952,7 @@ int main(int argc, char *argv[]) while(dw * (dh + 1) <= TOTAL_CELLS) dw++; /* Print debug information */ - if(DEBUG) + if(DEBUG_MODE) { fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN); fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS); @@ -935,7 +978,6 @@ int main(int argc, char *argv[]) pipi_free(src); src = pipi_median_ext(tmp, 1, 1); pipi_free(tmp); -pipi_save(src, "lol.bmp"); /* Analyse image */ analyse(src); @@ -943,10 +985,9 @@ pipi_save(src, "lol.bmp"); /* 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) + if(DEBUG_MODE) fprintf(stderr, "Initial distance: %2.10g\n", error); memset(opstats, 0, sizeof(opstats)); @@ -960,7 +1001,7 @@ pipi_save(tmp, "lol2.bmp"); failures = 0; } - if(!DEBUG && !(iter % 16)) + if(!DEBUG_MODE && !(iter % 16)) fprintf(stderr, "\rEncoding... %i%%", iter * 100 / MAX_ITERATIONS); @@ -1026,7 +1067,7 @@ pipi_save(tmp, "lol2.bmp"); pipi_free(tmp); tmp = scrap; - if(DEBUG) + if(DEBUG_MODE) fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n", iter, (int)((error - besterr) * 100000000), error, op1, pt); @@ -1051,7 +1092,7 @@ pipi_save(tmp, "lol2.bmp"); } } - if(DEBUG) + if(DEBUG_MODE) { for(int j = 0; j < 2; j++) {