Bladeren bron

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 jaren geleden
bovenliggende
commit
6a02de1614
3 gewijzigde bestanden met toevoegingen van 184 en 86 verwijderingen
  1. +1
    -1
      examples/Makefile.am
  2. +174
    -85
      examples/img2twit.cpp
  3. +9
    -0
      genethumb/mygetopt.h

+ 1
- 1
examples/Makefile.am Bestand weergeven

@@ -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


+ 174
- 85
examples/img2twit.cpp Bestand weergeven

@@ -11,24 +11,17 @@

#include <pipi.h>

#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<std::pair<K::Point_2, K::FT> > 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 <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;
}

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);
}



+ 9
- 0
genethumb/mygetopt.h Bestand weergeven

@@ -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


Laden…
Annuleren
Opslaan