Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

img2twit.cpp 14 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. #define TOTAL_POINTS 138
  11. #define POINTS_PER_CELL 2
  12. #define RANGE_X 16
  13. #define RANGE_Y 16
  14. #define RANGE_R 5
  15. #define RANGE_G 5
  16. #define RANGE_B 5
  17. #define RANGE_S 1
  18. #define RANGE_SY (RANGE_S*RANGE_Y)
  19. #define RANGE_SYX (RANGE_S*RANGE_Y*RANGE_X)
  20. #define RANGE_SYXR (RANGE_S*RANGE_Y*RANGE_X*RANGE_R)
  21. #define RANGE_SYXRG (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G)
  22. #define RANGE_SYXRGB (RANGE_S*RANGE_Y*RANGE_X*RANGE_R*RANGE_G*RANGE_B)
  23. struct K : CGAL::Exact_predicates_inexact_constructions_kernel {};
  24. typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation;
  25. typedef std::vector<std::pair<K::Point_2, K::FT> > Point_coordinate_vector;
  26. /* Global aspect ratio */
  27. static int dw, dh;
  28. /* Global point encoding */
  29. static uint32_t points[1024];
  30. static int npoints = 0;
  31. /* Global triangulation */
  32. static Delaunay_triangulation dt;
  33. static unsigned int det_rand(unsigned int mod)
  34. {
  35. static unsigned long next = 1;
  36. next = next * 1103515245 + 12345;
  37. return ((unsigned)(next / 65536) % 32768) % mod;
  38. }
  39. static inline int float2int(float val, int range)
  40. {
  41. int ret = (int)(val * ((float)range - 0.0001));
  42. return ret < 0 ? 0 : ret > range - 1? range - 1 : ret;
  43. }
  44. static inline float int2float(int val, int range)
  45. {
  46. return (float)(1 + 2 * val) / (float)(2 * range);
  47. }
  48. static inline uint32_t set_point(int index, float x, float y, float r,
  49. float g, float b, float s)
  50. {
  51. int dx = (index / POINTS_PER_CELL) % dw;
  52. int dy = (index / POINTS_PER_CELL) / dw;
  53. float fx = (x - dx * RANGE_X) / RANGE_X;
  54. float fy = (y - dy * RANGE_Y) / RANGE_Y;
  55. int is = float2int(s, RANGE_S);
  56. int ix = float2int(fx, RANGE_X);
  57. int iy = float2int(fy, RANGE_Y);
  58. int ir = float2int(r, RANGE_R);
  59. int ig = float2int(g, RANGE_G);
  60. int ib = float2int(b, RANGE_B);
  61. points[index] = is + RANGE_S * (iy + RANGE_Y * (ix + RANGE_X *
  62. (ib + RANGE_B * (ig + (RANGE_R * ir)))));
  63. }
  64. static inline void get_point(int index, float *x, float *y, float *r,
  65. float *g, float *b, float *s)
  66. {
  67. uint32_t pt = points[index];
  68. unsigned int dx = (index / POINTS_PER_CELL) % dw;
  69. unsigned int dy = (index / POINTS_PER_CELL) / dw;
  70. float fs = int2float(pt % RANGE_S, RANGE_S); pt /= RANGE_S;
  71. *s = fs < 0.5 ? 0.0 : 1.0;
  72. float fy = int2float(pt % RANGE_Y, RANGE_Y); pt /= RANGE_Y;
  73. float fx = int2float(pt % RANGE_X, RANGE_X); pt /= RANGE_X;
  74. *x = (fx + dx) * RANGE_X;
  75. *y = (fy + dy) * RANGE_Y;
  76. *b = int2float(pt % RANGE_R, RANGE_R); pt /= RANGE_R;
  77. *g = int2float(pt % RANGE_G, RANGE_G); pt /= RANGE_G;
  78. *r = int2float(pt % RANGE_B, RANGE_B); pt /= RANGE_B;
  79. }
  80. static inline float clip(float x, int modulo)
  81. {
  82. float mul = (float)modulo + 0.9999;
  83. int round = (int)(x * mul);
  84. return (float)round / (float)modulo;
  85. }
  86. static void add_point(float x, float y, float r, float g, float b, float s)
  87. {
  88. set_point(npoints, x, y, r, g, b, s);
  89. get_point(npoints, &x, &y, &r, &g, &b, &s);
  90. npoints++;
  91. }
  92. #define MAX_OPS 15
  93. static uint32_t apply_op(uint8_t op, uint32_t val)
  94. {
  95. uint32_t rem, ext;
  96. switch(op)
  97. {
  98. case 0: /* Flip strength value */
  99. return val ^ 1;
  100. case 1: /* Move up; if impossible, down */
  101. rem = val % RANGE_S;
  102. ext = (val / RANGE_S) % RANGE_Y;
  103. ext = ext > 0 ? ext - 1 : ext + 1;
  104. return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem;
  105. case 2: /* Move down; if impossible, up */
  106. rem = val % RANGE_S;
  107. ext = (val / RANGE_S) % RANGE_Y;
  108. ext = ext < RANGE_Y - 1 ? ext + 1 : ext - 1;
  109. return (val / RANGE_SY * RANGE_Y + ext) * RANGE_S + rem;
  110. case 3: /* Move left; if impossible, right */
  111. rem = val % RANGE_SY;
  112. ext = (val / RANGE_SY) % RANGE_X;
  113. ext = ext > 0 ? ext - 1 : ext + 1;
  114. return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem;
  115. case 4: /* Move left; if impossible, right */
  116. rem = val % RANGE_SY;
  117. ext = (val / RANGE_SY) % RANGE_X;
  118. ext = ext < RANGE_X - 1 ? ext + 1 : ext - 1;
  119. return (val / RANGE_SYX * RANGE_X + ext) * RANGE_SY + rem;
  120. case 5: /* Corner 1 */
  121. return apply_op(1, apply_op(3, val));
  122. case 6: /* Corner 2 */
  123. return apply_op(1, apply_op(4, val));
  124. case 7: /* Corner 3 */
  125. return apply_op(2, apply_op(4, val));
  126. case 8: /* Corner 4 */
  127. return apply_op(2, apply_op(3, val));
  128. case 9: /* R-- (or R++) */
  129. rem = val % RANGE_SYX;
  130. ext = (val / RANGE_SYX) % RANGE_R;
  131. ext = ext > 0 ? ext - 1 : ext + 1;
  132. return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem;
  133. case 10: /* R++ (or R--) */
  134. rem = val % RANGE_SYX;
  135. ext = (val / RANGE_SYX) % RANGE_R;
  136. ext = ext < RANGE_R - 1 ? ext + 1 : ext - 1;
  137. return (val / RANGE_SYXR * RANGE_R + ext) * RANGE_SYX + rem;
  138. case 11: /* G-- (or G++) */
  139. rem = val % RANGE_SYXR;
  140. ext = (val / RANGE_SYXR) % RANGE_G;
  141. ext = ext > 0 ? ext - 1 : ext + 1;
  142. return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem;
  143. case 12: /* G++ (or G--) */
  144. rem = val % RANGE_SYXR;
  145. ext = (val / RANGE_SYXR) % RANGE_G;
  146. ext = ext < RANGE_G - 1 ? ext + 1 : ext - 1;
  147. return (val / RANGE_SYXRG * RANGE_G + ext) * RANGE_SYXR + rem;
  148. case 13: /* B-- (or B++) */
  149. rem = val % RANGE_SYXRG;
  150. ext = (val / RANGE_SYXRG) % RANGE_B;
  151. ext = ext > 0 ? ext - 1 : ext + 1;
  152. return ext * RANGE_SYXRG + rem;
  153. case 14: /* B++ (or B--) */
  154. rem = val % RANGE_SYXRG;
  155. ext = (val / RANGE_SYXRG) % RANGE_B;
  156. ext = ext < RANGE_B - 1 ? ext + 1 : ext - 1;
  157. return ext * RANGE_SYXRG + rem;
  158. #if 0
  159. case 15: /* Brightness-- */
  160. return apply_op(9, apply_op(11, apply_op(13, val)));
  161. case 16: /* Brightness++ */
  162. return apply_op(10, apply_op(12, apply_op(14, val)));
  163. case 17: /* RG-- */
  164. return apply_op(9, apply_op(11, val));
  165. case 18: /* RG++ */
  166. return apply_op(10, apply_op(12, val));
  167. case 19: /* GB-- */
  168. return apply_op(11, apply_op(13, val));
  169. case 20: /* GB++ */
  170. return apply_op(12, apply_op(14, val));
  171. case 21: /* RB-- */
  172. return apply_op(9, apply_op(13, val));
  173. case 22: /* RB++ */
  174. return apply_op(10, apply_op(14, val));
  175. #endif
  176. default:
  177. return val;
  178. }
  179. }
  180. static void render(pipi_image_t *dst, int rx, int ry, int rw, int rh)
  181. {
  182. uint8_t lookup[TOTAL_POINTS / POINTS_PER_CELL * RANGE_X * RANGE_Y];
  183. pipi_pixels_t *p = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
  184. float *data = (float *)p->pixels;
  185. float fx, fy, fr, fg, fb, fs;
  186. int i, x, y;
  187. memset(lookup, 0, sizeof(lookup));
  188. dt.clear();
  189. for(i = 0; i < npoints; i++)
  190. {
  191. get_point(i, &fx, &fy, &fr, &fg, &fb, &fs);
  192. lookup[(int)fx + dw * RANGE_X * (int)fy] = i; /* Keep link to point */
  193. dt.insert(K::Point_2(fx, fy));
  194. }
  195. /* Add fake points to close the triangulation */
  196. dt.insert(K::Point_2(-p->w, -p->h));
  197. dt.insert(K::Point_2(2 * p->w, -p->h));
  198. dt.insert(K::Point_2(-p->w, 2 * p->h));
  199. dt.insert(K::Point_2(2 * p->w, 2 * p->h));
  200. for(y = ry; y < ry + rh; y++)
  201. {
  202. for(x = rx; x < rx + rw; x++)
  203. {
  204. int i1 = 0, i2 = 0, i3 = 0;
  205. float d1 = 1000000, d2 = 0, d3 = 0;
  206. K::Point_2 m(x, y);
  207. Point_coordinate_vector coords;
  208. CGAL::Triple<
  209. std::back_insert_iterator<Point_coordinate_vector>,
  210. K::FT, bool> result =
  211. CGAL::natural_neighbor_coordinates_2(dt, m,
  212. std::back_inserter(coords));
  213. float r = 0.0f, g = 0.0f, b = 0.0f, norm = 0.0f;
  214. Point_coordinate_vector::iterator it;
  215. for(it = coords.begin(); it != coords.end(); ++it)
  216. {
  217. float fx = (*it).first.x();
  218. float fy = (*it).first.y();
  219. if(fx < 0 || fy < 0 || fx > p->w - 1 || fy > p->h - 1)
  220. continue;
  221. int index = lookup[(int)fx + dw * RANGE_X * (int)fy];
  222. get_point(index, &fx, &fy, &fr, &fg, &fb, &fs);
  223. float k = (*it).second;
  224. if(fs > 0.5) k = pow(k, 1.2);
  225. else k = pow(k, 0.8);
  226. //float k = (*it).second * (1.0f + fs);
  227. r += k * fr;
  228. g += k * fg;
  229. b += k * fb;
  230. norm += k;
  231. }
  232. data[4 * (x + y * p->w) + 0] = r / norm;
  233. data[4 * (x + y * p->w) + 1] = g / norm;
  234. data[4 * (x + y * p->w) + 2] = b / norm;
  235. data[4 * (x + y * p->w) + 3] = 0.0;
  236. }
  237. }
  238. pipi_release_pixels(dst, p);
  239. }
  240. static void analyse(pipi_image_t *src)
  241. {
  242. pipi_pixels_t *p = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
  243. float *data = (float *)p->pixels;
  244. int i;
  245. for(int dy = 0; dy < dh; dy++)
  246. for(int dx = 0; dx < dw; dx++)
  247. {
  248. float min = 1.1f, max = -0.1f;
  249. float total = 0.0;
  250. int xmin = 0, xmax = 0, ymin = 0, ymax = 0;
  251. int npixels = 0;
  252. for(int iy = RANGE_Y * dy; iy < RANGE_Y * (dy + 1); iy++)
  253. for(int ix = RANGE_X * dx; ix < RANGE_X * (dx + 1); ix++)
  254. {
  255. float lum = 0.0f;
  256. lum += data[4 * (ix + iy * p->w) + 0];
  257. lum += data[4 * (ix + iy * p->w) + 1];
  258. lum += data[4 * (ix + iy * p->w) + 2];
  259. if(lum < min)
  260. {
  261. min = lum;
  262. xmin = ix;
  263. ymin = iy;
  264. }
  265. if(lum > max)
  266. {
  267. max = lum;
  268. xmax = ix;
  269. ymax = iy;
  270. }
  271. total += lum;
  272. npixels++;
  273. }
  274. total /= npixels;
  275. float wmin, wmax;
  276. if(total < min + (max - min) / 4)
  277. wmin = 1.0, wmax = 0.0;
  278. else if(total < min + (max - min) / 4 * 2)
  279. wmin = 0.0, wmax = 0.0;
  280. else if(total < min + (max - min) / 4 * 3)
  281. wmin = 0.0, wmax = 0.0;
  282. else
  283. wmin = 0.0, wmax = 1.0;
  284. //wmin = wmax = 1.0;
  285. //if(total < min + (max - min) /3 )
  286. //if((dx + dy) & 1)
  287. {
  288. add_point(xmin, ymin,
  289. data[4 * (xmin + ymin * p->w) + 0],
  290. data[4 * (xmin + ymin * p->w) + 1],
  291. data[4 * (xmin + ymin * p->w) + 2],
  292. wmin);
  293. }
  294. //else
  295. {
  296. add_point(xmax, ymax,
  297. data[4 * (xmax + ymax * p->w) + 0],
  298. data[4 * (xmax + ymax * p->w) + 1],
  299. data[4 * (xmax + ymax * p->w) + 2],
  300. wmax);
  301. }
  302. }
  303. }
  304. int main(int argc, char *argv[])
  305. {
  306. int opstats[2 * MAX_OPS];
  307. pipi_image_t *src, *tmp, *dst;
  308. double error = 1.0;
  309. int width, height, ret = 0;
  310. /* Load image */
  311. pipi_set_gamma(1.0);
  312. src = pipi_load(argv[1]);
  313. width = pipi_get_image_width(src);
  314. height = pipi_get_image_height(src);
  315. /* Compute best w/h ratio */
  316. dw = 1;
  317. for(int i = 1; i <= TOTAL_POINTS / POINTS_PER_CELL; i++)
  318. {
  319. float r = (float)width / (float)height;
  320. float ir = (float)i / (float)(TOTAL_POINTS / POINTS_PER_CELL / i);
  321. float dwr = (float)dw / (float)(TOTAL_POINTS / POINTS_PER_CELL / dw);
  322. if(fabs(logf(r / ir)) < fabs(logf(r / dwr)))
  323. dw = i;
  324. }
  325. dh = TOTAL_POINTS / POINTS_PER_CELL / dw;
  326. fprintf(stderr, "Chosen image ratio: %i:%i\n", dw, dh);
  327. /* Resize and filter image to better state */
  328. tmp = pipi_resize(src, dw * RANGE_X, dh * RANGE_Y);
  329. pipi_free(src);
  330. src = pipi_median_ext(tmp, 2, 2);
  331. /* Analyse image */
  332. analyse(src);
  333. /* Render what we just computed */
  334. tmp = pipi_new(dw * RANGE_X, dh * RANGE_Y);
  335. render(tmp, 0, 0, dw * RANGE_X, dh * RANGE_Y);
  336. error = pipi_measure_rmsd(src, tmp);
  337. fprintf(stderr, "Distance: %2.10g\n", error);
  338. memset(opstats, 0, sizeof(opstats));
  339. for(int iter = 0, failures = 0; /*failures < 200 &&*/ iter < 3000; iter++)
  340. {
  341. uint32_t oldval;
  342. pipi_image_t *scrap = pipi_copy(tmp);
  343. /* Choose a point at random */
  344. int pt = det_rand(npoints);
  345. oldval = points[pt];
  346. /* Apply a random operation and measure its effect */
  347. uint8_t op = det_rand(MAX_OPS);
  348. points[pt] = apply_op(op, oldval);
  349. render(scrap, 0, 0, dw * RANGE_X, dh * RANGE_Y);
  350. opstats[op * 2]++;
  351. double newerr = pipi_measure_rmsd(src, scrap);
  352. if(newerr < error)
  353. {
  354. pipi_free(tmp);
  355. tmp = scrap;
  356. error = newerr;
  357. fprintf(stderr, "%06i %2.010g after op%i(%i)\n",
  358. iter, error, op, pt);
  359. opstats[op * 2 + 1]++;
  360. failures = 0;
  361. }
  362. else
  363. {
  364. pipi_free(scrap);
  365. points[pt] = oldval;
  366. failures++;
  367. }
  368. }
  369. fprintf(stderr, "operation: ");
  370. for(int i = 0; i < MAX_OPS; i++)
  371. fprintf(stderr, "%3i ", i);
  372. fprintf(stderr, "\nattempts: ");
  373. for(int i = 0; i < MAX_OPS; i++)
  374. fprintf(stderr, "%3i ", opstats[i * 2]);
  375. fprintf(stderr, "\nsuccesses: ");
  376. for(int i = 0; i < MAX_OPS; i++)
  377. fprintf(stderr, "%3i ", opstats[i * 2 + 1]);
  378. fprintf(stderr, "\n");
  379. fprintf(stderr, "Distance: %2.10g\n", error);
  380. dst = pipi_resize(tmp, width, height);
  381. pipi_free(tmp);
  382. /* Save image and bail out */
  383. pipi_save(dst, "lol.bmp");
  384. pipi_free(dst);
  385. return ret;
  386. }