No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

634 líneas
19 KiB

  1. #include "config.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <math.h>
  6. #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
  7. #include <CGAL/Delaunay_triangulation_2.h>
  8. #include <CGAL/natural_neighbor_coordinates_2.h>
  9. #include <pipi.h>
  10. /*
  11. * User-definable settings.
  12. */
  13. /* The maximum message length */
  14. #define MAX_MSG_LEN 140
  15. /* The number of characters at disposal */
  16. //#define NUM_CHARACTERS 0x7fffffff // The sky's the limit
  17. //#define NUM_CHARACTERS 1111998 // Full valid Unicode set
  18. //#define NUM_CHARACTERS 100507 // Full graphic Unicode
  19. #define NUM_CHARACTERS 32768 // Chinese characters
  20. //#define NUM_CHARACTERS 127 // ASCII
  21. /* The maximum image size we want to support */
  22. #define MAX_W 4000
  23. #define MAX_H 4000
  24. /* How does the algorithm work: one point per cell, or two */
  25. #define POINTS_PER_CELL 2
  26. /* The range value for point parameters: X Y, red/green/blue, "strength"
  27. * Tested values (on Mona Lisa) are:
  28. * 16 16 5 5 5 2 -> 0.06511725914
  29. * 16 16 6 7 6 1 -> 0.05731491348 *
  30. * 16 16 7 6 6 1 -> 0.06450513783
  31. * 14 14 7 7 6 1 -> 0.0637207893
  32. * 19 19 6 6 5 1 -> 0.06801999094 */
  33. static unsigned int RANGE_X = 16;
  34. static unsigned int RANGE_Y = 16;
  35. static unsigned int RANGE_R = 6;
  36. static unsigned int RANGE_G = 6;
  37. static unsigned int RANGE_B = 6;
  38. static unsigned int RANGE_S = 1;
  39. /*
  40. * These values are computed at runtime
  41. */
  42. static float TOTAL_BITS;
  43. static float HEADER_BITS;
  44. static float DATA_BITS;
  45. static float POINT_BITS;
  46. static unsigned int TOTAL_CELLS;
  47. #define RANGE_SY (RANGE_S*RANGE_Y)
  48. #define RANGE_SYX (RANGE_S*RANGE_Y*RANGE_X)
  49. #define RANGE_SYXR (RANGE_S*RANGE_Y*RANGE_X*RANGE_R)
  50. #define RANGE_SYXRG (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G)
  51. #define RANGE_SYXRGB (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G*RANGE_B)
  52. struct K : CGAL::Exact_predicates_inexact_constructions_kernel {};
  53. typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation;
  54. typedef std::vector<std::pair<K::Point_2, K::FT> > Point_coordinate_vector;
  55. /* Global aspect ratio */
  56. static unsigned int dw, dh;
  57. /* Global point encoding */
  58. static uint32_t points[1024];
  59. static int npoints = 0;
  60. /* Global triangulation */
  61. static Delaunay_triangulation dt;
  62. static unsigned int det_rand(unsigned int mod)
  63. {
  64. static unsigned long next = 1;
  65. next = next * 1103515245 + 12345;
  66. return ((unsigned)(next / 65536) % 32768) % mod;
  67. }
  68. static inline int range2int(float val, int range)
  69. {
  70. int ret = (int)(val * ((float)range - 0.0001));
  71. return ret < 0 ? 0 : ret > range - 1 ? range - 1 : ret;
  72. }
  73. static inline float int2midrange(int val, int range)
  74. {
  75. return (float)(1 + 2 * val) / (float)(2 * range);
  76. }
  77. static inline float int2fullrange(int val, int range)
  78. {
  79. return range > 1 ? (float)val / (float)(range - 1) : 0.0;
  80. }
  81. static inline void set_point(int index, float x, float y, float r,
  82. float g, float b, float s)
  83. {
  84. int dx = (index / POINTS_PER_CELL) % dw;
  85. int dy = (index / POINTS_PER_CELL) / dw;
  86. float fx = (x - dx * RANGE_X) / RANGE_X;
  87. float fy = (y - dy * RANGE_Y) / RANGE_Y;
  88. int is = range2int(s, RANGE_S);
  89. int ix = range2int(fx, RANGE_X);
  90. int iy = range2int(fy, RANGE_Y);
  91. int ir = range2int(r, RANGE_R);
  92. int ig = range2int(g, RANGE_G);
  93. int ib = range2int(b, RANGE_B);
  94. points[index] = is + RANGE_S * (iy + RANGE_Y * (ix + RANGE_X *
  95. (ib + RANGE_B * (ig + (RANGE_R * ir)))));
  96. }
  97. static inline void get_point(int index, float *x, float *y, float *r,
  98. float *g, float *b, float *s)
  99. {
  100. uint32_t pt = points[index];
  101. unsigned int dx = (index / POINTS_PER_CELL) % dw;
  102. unsigned int dy = (index / POINTS_PER_CELL) / dw;
  103. *s = int2fullrange(pt % RANGE_S, RANGE_S); pt /= RANGE_S;
  104. float fy = int2midrange(pt % RANGE_Y, RANGE_Y); pt /= RANGE_Y;
  105. float fx = int2midrange(pt % RANGE_X, RANGE_X); pt /= RANGE_X;
  106. *x = (fx + dx) * RANGE_X;
  107. *y = (fy + dy) * RANGE_Y;
  108. *b = int2midrange(pt % RANGE_R, RANGE_R); pt /= RANGE_R;
  109. *g = int2midrange(pt % RANGE_G, RANGE_G); pt /= RANGE_G;
  110. *r = int2midrange(pt % RANGE_B, RANGE_B); pt /= RANGE_B;
  111. }
  112. static inline float clip(float x, int modulo)
  113. {
  114. float mul = (float)modulo + 0.9999;
  115. int round = (int)(x * mul);
  116. return (float)round / (float)modulo;
  117. }
  118. static void add_point(float x, float y, float r, float g, float b, float s)
  119. {
  120. set_point(npoints, x, y, r, g, b, s);
  121. npoints++;
  122. }
  123. static void add_random_point()
  124. {
  125. points[npoints] = det_rand(RANGE_SYXRGB);
  126. npoints++;
  127. }
  128. #define NB_OPS 20
  129. static uint8_t rand_op(void)
  130. {
  131. uint8_t x = det_rand(NB_OPS);
  132. /* Randomly ignore statistically less efficient ops */
  133. if(x == 0)
  134. return rand_op();
  135. if(x == 1 && (RANGE_S == 1 || det_rand(2)))
  136. return rand_op();
  137. if(x <= 5 && det_rand(2))
  138. return rand_op();
  139. //if((x < 10 || x > 15) && !det_rand(4)) /* Favour colour changes */
  140. // return rand_op();
  141. return x;
  142. }
  143. static uint32_t apply_op(uint8_t op, uint32_t val)
  144. {
  145. uint32_t rem, ext;
  146. switch(op)
  147. {
  148. case 0: /* Flip strength value */
  149. case 1:
  150. /* Statistics show that this helps often, but does not reduce
  151. * the error significantly. */
  152. return val ^ 1;
  153. case 2: /* Move up; if impossible, down */
  154. rem = val % RANGE_S;
  155. ext = (val / RANGE_S) % RANGE_Y;
  156. ext = ext > 0 ? ext - 1 : ext + 1;
  157. return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem;
  158. case 3: /* Move down; if impossible, up */
  159. rem = val % RANGE_S;
  160. ext = (val / RANGE_S) % RANGE_Y;
  161. ext = ext < RANGE_Y - 1 ? ext + 1 : ext - 1;
  162. return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem;
  163. case 4: /* Move left; if impossible, right */
  164. rem = val % RANGE_SY;
  165. ext = (val / RANGE_SY) % RANGE_X;
  166. ext = ext > 0 ? ext - 1 : ext + 1;
  167. return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem;
  168. case 5: /* Move left; if impossible, right */
  169. rem = val % RANGE_SY;
  170. ext = (val / RANGE_SY) % RANGE_X;
  171. ext = ext < RANGE_X - 1 ? ext + 1 : ext - 1;
  172. return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem;
  173. case 6: /* Corner 1 */
  174. return apply_op(2, apply_op(4, val));
  175. case 7: /* Corner 2 */
  176. return apply_op(2, apply_op(5, val));
  177. case 8: /* Corner 3 */
  178. return apply_op(3, apply_op(5, val));
  179. case 9: /* Corner 4 */
  180. return apply_op(3, apply_op(4, val));
  181. case 16: /* Double up */
  182. return apply_op(2, apply_op(2, val));
  183. case 17: /* Double down */
  184. return apply_op(3, apply_op(3, val));
  185. case 18: /* Double left */
  186. return apply_op(4, apply_op(4, val));
  187. case 19: /* Double right */
  188. return apply_op(5, apply_op(5, val));
  189. case 10: /* R-- (or R++) */
  190. rem = val % RANGE_SYX;
  191. ext = (val / RANGE_SYX) % RANGE_R;
  192. ext = ext > 0 ? ext - 1 : ext + 1;
  193. return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem;
  194. case 11: /* R++ (or R--) */
  195. rem = val % RANGE_SYX;
  196. ext = (val / RANGE_SYX) % RANGE_R;
  197. ext = ext < RANGE_R - 1 ? ext + 1 : ext - 1;
  198. return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem;
  199. case 12: /* G-- (or G++) */
  200. rem = val % RANGE_SYXR;
  201. ext = (val / RANGE_SYXR) % RANGE_G;
  202. ext = ext > 0 ? ext - 1 : ext + 1;
  203. return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem;
  204. case 13: /* G++ (or G--) */
  205. rem = val % RANGE_SYXR;
  206. ext = (val / RANGE_SYXR) % RANGE_G;
  207. ext = ext < RANGE_G - 1 ? ext + 1 : ext - 1;
  208. return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem;
  209. case 14: /* B-- (or B++) */
  210. rem = val % RANGE_SYXRG;
  211. ext = (val / RANGE_SYXRG) % RANGE_B;
  212. ext = ext > 0 ? ext - 1 : ext + 1;
  213. return ext * RANGE_SYXRG + rem;
  214. case 15: /* B++ (or B--) */
  215. rem = val % RANGE_SYXRG;
  216. ext = (val / RANGE_SYXRG) % RANGE_B;
  217. ext = ext < RANGE_B - 1 ? ext + 1 : ext - 1;
  218. return ext * RANGE_SYXRG + rem;
  219. #if 0
  220. case 15: /* Brightness-- */
  221. return apply_op(9, apply_op(11, apply_op(13, val)));
  222. case 16: /* Brightness++ */
  223. return apply_op(10, apply_op(12, apply_op(14, val)));
  224. case 17: /* RG-- */
  225. return apply_op(9, apply_op(11, val));
  226. case 18: /* RG++ */
  227. return apply_op(10, apply_op(12, val));
  228. case 19: /* GB-- */
  229. return apply_op(11, apply_op(13, val));
  230. case 20: /* GB++ */
  231. return apply_op(12, apply_op(14, val));
  232. case 21: /* RB-- */
  233. return apply_op(9, apply_op(13, val));
  234. case 22: /* RB++ */
  235. return apply_op(10, apply_op(14, val));
  236. #endif
  237. default:
  238. return val;
  239. }
  240. }
  241. static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
  242. {
  243. uint8_t lookup[TOTAL_CELLS * RANGE_X * RANGE_Y];
  244. pipi_pixels_t *p = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
  245. float *data = (float *)p->pixels;
  246. int i, x, y;
  247. memset(lookup, 0, sizeof(lookup));
  248. dt.clear();
  249. for(i = 0; i < npoints; i++)
  250. {
  251. float fx, fy, fr, fg, fb, fs;
  252. get_point(i, &fx, &fy, &fr, &fg, &fb, &fs);
  253. lookup[(int)fx + dw * RANGE_X * (int)fy] = i; /* Keep link to point */
  254. dt.insert(K::Point_2(fx, fy));
  255. }
  256. /* Add fake points to close the triangulation */
  257. dt.insert(K::Point_2(-p->w, -p->h));
  258. dt.insert(K::Point_2(2 * p->w, -p->h));
  259. dt.insert(K::Point_2(-p->w, 2 * p->h));
  260. dt.insert(K::Point_2(2 * p->w, 2 * p->h));
  261. for(y = ry; y < ry + rh; y++)
  262. {
  263. for(x = rx; x < rx + rw; x++)
  264. {
  265. K::Point_2 m(x, y);
  266. Point_coordinate_vector coords;
  267. CGAL::Triple<
  268. std::back_insert_iterator<Point_coordinate_vector>,
  269. K::FT, bool> result =
  270. CGAL::natural_neighbor_coordinates_2(dt, m,
  271. std::back_inserter(coords));
  272. float r = 0.0f, g = 0.0f, b = 0.0f, norm = 0.0f;
  273. Point_coordinate_vector::iterator it;
  274. for(it = coords.begin(); it != coords.end(); ++it)
  275. {
  276. float fx, fy, fr, fg, fb, fs;
  277. fx = (*it).first.x();
  278. fy = (*it).first.y();
  279. if(fx < 0 || fy < 0 || fx > p->w - 1 || fy > p->h - 1)
  280. continue;
  281. int index = lookup[(int)fx + dw * RANGE_X * (int)fy];
  282. get_point(index, &fx, &fy, &fr, &fg, &fb, &fs);
  283. //float k = pow((*it).second * (1.0 + fs), 1.2);
  284. float k = (*it).second * (1.00f + fs);
  285. //float k = (*it).second * (0.60f + fs);
  286. //float k = pow((*it).second, (1.0f + fs));
  287. r += k * fr;
  288. g += k * fg;
  289. b += k * fb;
  290. norm += k;
  291. }
  292. data[4 * (x + y * p->w) + 0] = r / norm;
  293. data[4 * (x + y * p->w) + 1] = g / norm;
  294. data[4 * (x + y * p->w) + 2] = b / norm;
  295. data[4 * (x + y * p->w) + 3] = 0.0;
  296. }
  297. }
  298. pipi_release_pixels(dst, p);
  299. }
  300. static void analyse(pipi_image_t *src)
  301. {
  302. pipi_pixels_t *p = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
  303. float *data = (float *)p->pixels;
  304. for(unsigned int dy = 0; dy < dh; dy++)
  305. for(unsigned int dx = 0; dx < dw; dx++)
  306. {
  307. float min = 1.1f, max = -0.1f;
  308. float total = 0.0;
  309. int xmin = 0, xmax = 0, ymin = 0, ymax = 0;
  310. int npixels = 0;
  311. for(unsigned int iy = RANGE_Y * dy; iy < RANGE_Y * (dy + 1); iy++)
  312. for(unsigned int ix = RANGE_X * dx; ix < RANGE_X * (dx + 1); ix++)
  313. {
  314. float lum = 0.0f;
  315. lum += data[4 * (ix + iy * p->w) + 0];
  316. lum += data[4 * (ix + iy * p->w) + 1];
  317. lum += data[4 * (ix + iy * p->w) + 2];
  318. if(lum < min)
  319. {
  320. min = lum;
  321. xmin = ix;
  322. ymin = iy;
  323. }
  324. if(lum > max)
  325. {
  326. max = lum;
  327. xmax = ix;
  328. ymax = iy;
  329. }
  330. total += lum;
  331. npixels++;
  332. }
  333. total /= npixels;
  334. float wmin, wmax;
  335. if(total < min + (max - min) / 4)
  336. wmin = 1.0, wmax = 0.0;
  337. else if(total < min + (max - min) / 4 * 3)
  338. wmin = 0.0, wmax = 0.0;
  339. else
  340. wmin = 0.0, wmax = 1.0;
  341. #if 0
  342. add_random_point();
  343. add_random_point();
  344. #else
  345. #if POINTS_PER_CELL == 1
  346. if(total < min + (max - min) / 2)
  347. {
  348. #endif
  349. add_point(xmin, ymin,
  350. data[4 * (xmin + ymin * p->w) + 0],
  351. data[4 * (xmin + ymin * p->w) + 1],
  352. data[4 * (xmin + ymin * p->w) + 2],
  353. wmin);
  354. #if POINTS_PER_CELL == 1
  355. }
  356. else
  357. {
  358. #endif
  359. add_point(xmax, ymax,
  360. data[4 * (xmax + ymax * p->w) + 0],
  361. data[4 * (xmax + ymax * p->w) + 1],
  362. data[4 * (xmax + ymax * p->w) + 2],
  363. wmax);
  364. #if POINTS_PER_CELL == 1
  365. }
  366. #endif
  367. #endif
  368. }
  369. }
  370. int main(int argc, char *argv[])
  371. {
  372. int opstats[2 * NB_OPS];
  373. pipi_image_t *src, *tmp, *dst;
  374. double error = 1.0;
  375. int width, height, ret = 0;
  376. /* Compute bit allocation */
  377. fprintf(stderr, "Available characters: %i\n", NUM_CHARACTERS);
  378. fprintf(stderr, "Maximum message size: %i\n", MAX_MSG_LEN);
  379. TOTAL_BITS = MAX_MSG_LEN * logf(NUM_CHARACTERS) / logf(2);
  380. fprintf(stderr, "Available bits: %f\n", TOTAL_BITS);
  381. fprintf(stderr, "Maximum image resolution: %ix%i\n", MAX_W, MAX_H);
  382. HEADER_BITS = logf(MAX_W * MAX_H) / logf(2);
  383. fprintf(stderr, "Header bits: %f\n", HEADER_BITS);
  384. DATA_BITS = TOTAL_BITS - HEADER_BITS;
  385. fprintf(stderr, "Bits available for data: %f\n", DATA_BITS);
  386. #if POINTS_PER_CELL == 1
  387. POINT_BITS = logf(RANGE_SYXRGB) / logf(2);
  388. #else
  389. float coord_bits = logf((RANGE_Y * RANGE_X) * (RANGE_Y * RANGE_X + 1) / 2);
  390. float other_bits = logf(RANGE_R * RANGE_G * RANGE_B * RANGE_S);
  391. POINT_BITS = (coord_bits + 2 * other_bits) / logf(2);
  392. #endif
  393. fprintf(stderr, "Cell bits: %f\n", POINT_BITS);
  394. TOTAL_CELLS = (int)(DATA_BITS / POINT_BITS);
  395. fprintf(stderr, "Available cells: %i\n", TOTAL_CELLS);
  396. fprintf(stderr, "Wasted bits: %f\n", DATA_BITS - POINT_BITS * TOTAL_CELLS);
  397. /* Load image */
  398. pipi_set_gamma(1.0);
  399. src = pipi_load(argv[1]);
  400. width = pipi_get_image_width(src);
  401. height = pipi_get_image_height(src);
  402. /* Compute best w/h ratio */
  403. dw = 1; dh = TOTAL_CELLS;
  404. for(unsigned int i = 1; i <= TOTAL_CELLS; i++)
  405. {
  406. int j = TOTAL_CELLS / i;
  407. float r = (float)width / (float)height;
  408. float ir = (float)i / (float)j;
  409. float dwr = (float)dw / (float)dh;
  410. if(fabs(logf(r / ir)) < fabs(logf(r / dwr)))
  411. {
  412. dw = i;
  413. dh = TOTAL_CELLS / dw;
  414. }
  415. }
  416. while((dh + 1) * dw <= TOTAL_CELLS) dh++;
  417. while(dw * (dh + 1) <= TOTAL_CELLS) dw++;
  418. fprintf(stderr, "Chosen image ratio: %i:%i (wasting %i point cells)\n",
  419. dw, dh, TOTAL_CELLS - dw * dh);
  420. fprintf(stderr, "Total wasted bits: %f\n",
  421. DATA_BITS - POINT_BITS * dw * dh);
  422. /* Resize and filter image to better state */
  423. tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
  424. pipi_free(src);
  425. src = pipi_median_ext(tmp, 1, 1);
  426. pipi_free(tmp);
  427. /* Analyse image */
  428. analyse(src);
  429. /* Render what we just computed */
  430. tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
  431. render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
  432. error = pipi_measure_rmsd(src, tmp);
  433. fprintf(stderr, "Distance: %2.10g\n", error);
  434. memset(opstats, 0, sizeof(opstats));
  435. for(int iter = 0, stuck = 0, failures = 0, success = 0;
  436. /*stuck < 5 && */iter < 10000;
  437. iter++)
  438. {
  439. if(failures > 500)
  440. {
  441. stuck++;
  442. failures = 0;
  443. }
  444. pipi_image_t *scrap = pipi_copy(tmp);
  445. /* Choose a point at random */
  446. int pt = det_rand(npoints);
  447. uint32_t oldval = points[pt];
  448. /* Compute the affected image zone */
  449. float fx, fy, fr, fg, fb, fs;
  450. get_point(pt, &fx, &fy, &fr, &fg, &fb, &fs);
  451. int zonex = (int)fx / RANGE_X - 1;
  452. int zoney = (int)fy / RANGE_Y - 1;
  453. int zonew = 3;
  454. int zoneh = 3;
  455. if(zonex < 0) { zonex = 0; zonew--; }
  456. if(zoney < 0) { zoney = 0; zoneh--; }
  457. if(zonex + zonew >= (int)dw) { zonew--; }
  458. if(zoney + zoneh >= (int)dh) { zoneh--; }
  459. /* Choose random operations and measure their effect */
  460. uint8_t op1 = rand_op();
  461. //uint8_t op2 = rand_op();
  462. uint32_t candidates[3];
  463. double besterr = error + 1.0;
  464. int bestop = -1;
  465. candidates[0] = apply_op(op1, oldval);
  466. //candidates[1] = apply_op(op2, oldval);
  467. //candidates[2] = apply_op(op1, apply_op(op2, oldval));
  468. for(int i = 0; i < 1; i++)
  469. //for(int i = 0; i < 3; i++)
  470. {
  471. if(oldval == candidates[i])
  472. continue;
  473. points[pt] = candidates[i];
  474. render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
  475. zonew * RANGE_X, zoneh * RANGE_Y);
  476. double newerr = pipi_measure_rmsd(src, scrap);
  477. if(newerr < besterr)
  478. {
  479. besterr = newerr;
  480. bestop = i;
  481. }
  482. }
  483. opstats[op1 * 2]++;
  484. //opstats[op2 * 2]++;
  485. if(besterr < error)
  486. {
  487. points[pt] = candidates[bestop];
  488. /* Redraw image if the last check wasn't the best one */
  489. if(bestop != 2)
  490. render(scrap, zonex * RANGE_X, zoney * RANGE_Y,
  491. zonew * RANGE_X, zoneh * RANGE_Y);
  492. pipi_free(tmp);
  493. tmp = scrap;
  494. //fprintf(stderr, "%08i %2.010g %2.010g after op%i(%i)\n",
  495. // iter, besterr - error, error, op1, pt);
  496. fprintf(stderr, "%08i -.%08i %2.010g after op%i(%i)\n", iter,
  497. (int)((error - besterr) * 100000000), error, op1, pt);
  498. error = besterr;
  499. opstats[op1 * 2 + 1]++;
  500. //opstats[op2 * 2 + 1]++;
  501. failures = 0;
  502. success++;
  503. /* Save image! */
  504. //char buf[128];
  505. //sprintf(buf, "twit%08i.bmp", success);
  506. //if((success % 10) == 0)
  507. // pipi_save(tmp, buf);
  508. }
  509. else
  510. {
  511. pipi_free(scrap);
  512. points[pt] = oldval;
  513. failures++;
  514. }
  515. }
  516. for(int j = 0; j < 2; j++)
  517. {
  518. fprintf(stderr, "operation: ");
  519. for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
  520. fprintf(stderr, "%4i ", i);
  521. fprintf(stderr, "\nattempts: ");
  522. for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
  523. fprintf(stderr, "%4i ", opstats[i * 2]);
  524. fprintf(stderr, "\nsuccesses: ");
  525. for(int i = NB_OPS / 2 * j; i < NB_OPS / 2 * (j + 1); i++)
  526. fprintf(stderr, "%4i ", opstats[i * 2 + 1]);
  527. fprintf(stderr, "\n");
  528. }
  529. fprintf(stderr, "Distance: %2.10g\n", error);
  530. dst = pipi_resize(tmp, width, height);
  531. pipi_free(tmp);
  532. /* Save image and bail out */
  533. pipi_save(dst, "lol.bmp");
  534. pipi_free(dst);
  535. return ret;
  536. }