Browse Source

Make img2twit more configurable: iterations per second and debug mode can

now be chosen at runtime. Message length will soon be.

git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@3522 92316355-f0b4-4df1-b90c-862c8a59935f
master
sam 15 years ago
parent
commit
6a02de1614
3 changed files with 184 additions and 86 deletions
  1. +1
    -1
      examples/Makefile.am
  2. +174
    -85
      examples/img2twit.cpp
  3. +9
    -0
      genethumb/mygetopt.h

+ 1
- 1
examples/Makefile.am View File

@@ -48,7 +48,7 @@ if USE_CGAL
img2twit = img2twit img2twit = img2twit
endif endif


img2twit_SOURCES = img2twit.cpp
img2twit_SOURCES = img2twit.cpp ../genethumb/mygetopt.c
img2twit_LDADD = ../pipi/libpipi.la img2twit_LDADD = ../pipi/libpipi.la
img2twit_CXXFLAGS = -frounding-math img2twit_CXXFLAGS = -frounding-math
img2twit_LDFLAGS = -lCGAL img2twit_LDFLAGS = -lCGAL


+ 174
- 85
examples/img2twit.cpp View File

@@ -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[1024];
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.000000000000001f;


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(decode)
if(dstname)
{ {
/* 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);
} }




+ 9
- 0
genethumb/mygetopt.h View File

@@ -16,6 +16,11 @@
* mygetopt.h: getopt_long reimplementation * mygetopt.h: getopt_long reimplementation
*/ */


#ifdef __cplusplus
extern "C"
{
#endif

struct myoption struct myoption
{ {
const char *name; const char *name;
@@ -29,3 +34,7 @@ extern char *myoptarg;


int mygetopt(int, char * const[], const char *, const struct myoption *, int *); int mygetopt(int, char * const[], const char *, const struct myoption *, int *);


#ifdef __cplusplus
}
#endif


Loading…
Cancel
Save