@@ -1,3 +1,15 @@
/*
* img2twit Image to short text message encoder/decoder
* Copyright (c) 2009 Sam Hocevar <sam@hocevar.net>
* 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 "config.h"
#include <stdio.h>
#include <stdio.h>
@@ -56,6 +68,19 @@ static const uint32_t unichars[] =
/* How does the algorithm work: one point per cell, or two */
/* How does the algorithm work: one point per cell, or two */
#define POINTS_PER_CELL 2
#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"
/* The range value for point parameters: X Y, red/green/blue, "strength"
* Tested values (on Mona Lisa) are:
* Tested values (on Mona Lisa) are:
* 16 16 5 5 5 2 -> 0.06511725914
* 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_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
*/
*/
@@ -273,6 +287,13 @@ public:
private:
private:
bitstack(uint32_t i) { alloc(); init(i); }
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)
bitstack(bitstack const &b)
{
{
alloc();
alloc();
@@ -686,7 +707,7 @@ static void analyse(pipi_image_t *src)
for(unsigned int dy = 0; dy < dh; dy++)
for(unsigned int dy = 0; dy < dh; dy++)
for(unsigned int dx = 0; dx < dw; dx++)
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;
float total = 0.0;
int xmin = 0, xmax = 0, ymin = 0, ymax = 0;
int xmin = 0, xmax = 0, ymin = 0, ymax = 0;
int npixels = 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) + 0];
lum += data[4 * (ix + iy * p->w) + 1];
lum += data[4 * (ix + iy * p->w) + 1];
lum += data[4 * (ix + iy * p->w) + 2];
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)
if(lum < min)
{
{
@@ -719,6 +745,9 @@ static void analyse(pipi_image_t *src)
}
}
total /= npixels;
total /= npixels;
mr /= npixels;
mg /= npixels;
mb /= npixels;
float wmin, wmax;
float wmin, wmax;
@@ -730,17 +759,19 @@ static void analyse(pipi_image_t *src)
wmin = 0.0, wmax = 1.0;
wmin = 0.0, wmax = 1.0;
#if 0
#if 0
add_random_point();
add_random_point();
add_random_point();
add_random_point();
#else
#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 POINTS_PER_CELL == 1
if(total < min + (max - min) / 2)
if(total < min + (max - min) / 2)
{
{
#endif
#endif
add_point(xmin, ymin,
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);
wmin);
#if POINTS_PER_CELL == 1
#if POINTS_PER_CELL == 1
}
}
@@ -748,9 +779,9 @@ add_random_point();
{
{
#endif
#endif
add_point(xmax, ymax,
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);
wmax);
#if POINTS_PER_CELL == 1
#if POINTS_PER_CELL == 1
}
}
@@ -764,7 +795,6 @@ add_random_point();
int main(int argc, char *argv[])
int main(int argc, char *argv[])
{
{
int opstats[2 * NB_OPS];
int opstats[2 * NB_OPS];
bitstack b;
char const *srcname = NULL, *dstname = NULL;
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;
@@ -777,12 +807,13 @@ int main(int argc, char *argv[])
static struct myoption long_options[] =
static struct myoption long_options[] =
{
{
{ "output", 1, NULL, 'o' },
{ "output", 1, NULL, 'o' },
{ "length", 1, NULL, 'l' },
{ "quality", 1, NULL, 'q' },
{ "quality", 1, NULL, 'q' },
{ "debug", 0, NULL, 'd' },
{ "debug", 0, NULL, 'd' },
{ "help", 0, NULL, 'h' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 },
{ 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)
if(c == -1)
break;
break;
@@ -792,15 +823,23 @@ int main(int argc, char *argv[])
case 'o':
case 'o':
dstname = myoptarg;
dstname = myoptarg;
break;
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':
case 'q':
ITERATIONS_PER_POINT = 10 * atoi(myoptarg);
ITERATIONS_PER_POINT = 10 * atoi(myoptarg);
if(ITERATIONS_PER_POINT < 0)
if(ITERATIONS_PER_POINT < 0)
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;
break;
case 'd':
case 'd':
DEBUG = true;
DEBUG_MODE = true;
break;
break;
case 'h':
case 'h':
printf("Usage: img2twit [OPTIONS] SOURCE\n");
printf("Usage: img2twit [OPTIONS] SOURCE\n");
@@ -809,6 +848,7 @@ int main(int argc, char *argv[])
printf("\n");
printf("\n");
printf("Mandatory arguments to long options are mandatory for short options too.\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(" -o, --output <filename> output resulting image to filename\n");
printf(" -l, --length <size> message length in characters (default 140)\n");
printf(" -q, --quality <rate> set image quality (0 - 10) (default 5)\n");
printf(" -q, --quality <rate> set image quality (0 - 10) (default 5)\n");
printf(" -d, --debug print debug information\n");
printf(" -d, --debug print debug information\n");
printf(" -h, --help display this help and exit\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);
TOTAL_CELLS = (int)(DATA_BITS / CELL_BITS);
MAX_ITERATIONS = ITERATIONS_PER_POINT * POINTS_PER_CELL * TOTAL_CELLS;
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)
if(dstname)
{
{
/* Decoding mode: read UTF-8 text from stdin, find each
/* 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++;
while(dw * (dh + 1) <= TOTAL_CELLS) dw++;
/* Print debug information */
/* Print debug information */
if(DEBUG)
if(DEBUG_MODE )
{
{
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
@@ -935,7 +978,6 @@ int main(int argc, char *argv[])
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);
@@ -943,10 +985,9 @@ pipi_save(src, "lol.bmp");
/* 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)
if(DEBUG_MODE )
fprintf(stderr, "Initial distance: %2.10g\n", error);
fprintf(stderr, "Initial distance: %2.10g\n", error);
memset(opstats, 0, sizeof(opstats));
memset(opstats, 0, sizeof(opstats));
@@ -960,7 +1001,7 @@ pipi_save(tmp, "lol2.bmp");
failures = 0;
failures = 0;
}
}
if(!DEBUG && !(iter % 16))
if(!DEBUG_MODE && !(iter % 16))
fprintf(stderr, "\rEncoding... %i%%",
fprintf(stderr, "\rEncoding... %i%%",
iter * 100 / MAX_ITERATIONS);
iter * 100 / MAX_ITERATIONS);
@@ -1026,7 +1067,7 @@ pipi_save(tmp, "lol2.bmp");
pipi_free(tmp);
pipi_free(tmp);
tmp = scrap;
tmp = scrap;
if(DEBUG)
if(DEBUG_MODE )
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n",
fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n",
iter, (int)((error - besterr) * 100000000), error,
iter, (int)((error - besterr) * 100000000), error,
op1, pt);
op1, pt);
@@ -1051,7 +1092,7 @@ pipi_save(tmp, "lol2.bmp");
}
}
}
}
if(DEBUG)
if(DEBUG_MODE )
{
{
for(int j = 0; j < 2; j++)
for(int j = 0; j < 2; j++)
{
{