PageRenderTime 83ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/miranda/plugins/freeimage/Source/FreeImage/NNQuantizer.cpp

http://miranda.googlecode.com/
C++ | 507 lines | 326 code | 80 blank | 101 comment | 85 complexity | b674abda954229b5460644c4ca36a8bd MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, LGPL-2.1
  1. // NeuQuant Neural-Net Quantization Algorithm
  2. // ------------------------------------------
  3. //
  4. // Copyright (c) 1994 Anthony Dekker
  5. //
  6. // NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
  7. // See "Kohonen neural networks for optimal colour quantization"
  8. // in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
  9. // for a discussion of the algorithm.
  10. //
  11. // Any party obtaining a copy of these files from the author, directly or
  12. // indirectly, is granted, free of charge, a full and unrestricted irrevocable,
  13. // world-wide, paid up, royalty-free, nonexclusive right and license to deal
  14. // in this software and documentation files (the "Software"), including without
  15. // limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
  16. // and/or sell copies of the Software, and to permit persons who receive
  17. // copies from any such party to do so, with the only requirement being
  18. // that this copyright notice remain intact.
  19. ///////////////////////////////////////////////////////////////////////
  20. // History
  21. // -------
  22. // January 2001: Adaptation of the Neural-Net Quantization Algorithm
  23. // for the FreeImage 2 library
  24. // Author: Hervé Drolon (drolon@infonie.fr)
  25. // March 2004: Adaptation for the FreeImage 3 library (port to big endian processors)
  26. // Author: Hervé Drolon (drolon@infonie.fr)
  27. // April 2004: Algorithm rewritten as a C++ class.
  28. // Fixed a bug in the algorithm with handling of 4-byte boundary alignment.
  29. // Author: Hervé Drolon (drolon@infonie.fr)
  30. ///////////////////////////////////////////////////////////////////////
  31. #include "Quantizers.h"
  32. #include "FreeImage.h"
  33. #include "Utilities.h"
  34. // Four primes near 500 - assume no image has a length so large
  35. // that it is divisible by all four primes
  36. // ==========================================================
  37. #define prime1 499
  38. #define prime2 491
  39. #define prime3 487
  40. #define prime4 503
  41. // ----------------------------------------------------------------
  42. NNQuantizer::NNQuantizer(int PaletteSize)
  43. {
  44. netsize = PaletteSize;
  45. maxnetpos = netsize - 1;
  46. initrad = netsize < 8 ? 1 : (netsize >> 3);
  47. initradius = (initrad * radiusbias);
  48. network = NULL;
  49. network = (pixel *)malloc(netsize * sizeof(pixel));
  50. bias = (int *)malloc(netsize * sizeof(int));
  51. freq = (int *)malloc(netsize * sizeof(int));
  52. radpower = (int *)malloc(initrad * sizeof(int));
  53. if( !network || !bias || !freq || !radpower ) {
  54. if(network) free(network);
  55. if(bias) free(bias);
  56. if(freq) free(freq);
  57. if(radpower) free(radpower);
  58. throw FI_MSG_ERROR_MEMORY;
  59. }
  60. }
  61. NNQuantizer::~NNQuantizer()
  62. {
  63. if(network) free(network);
  64. if(bias) free(bias);
  65. if(freq) free(freq);
  66. if(radpower) free(radpower);
  67. }
  68. ///////////////////////////////////////////////////////////////////////////
  69. // Initialise network in range (0,0,0) to (255,255,255) and set parameters
  70. // -----------------------------------------------------------------------
  71. void NNQuantizer::initnet() {
  72. int i, *p;
  73. for (i = 0; i < netsize; i++) {
  74. p = network[i];
  75. p[FI_RGBA_BLUE] = p[FI_RGBA_GREEN] = p[FI_RGBA_RED] = (i << (netbiasshift+8))/netsize;
  76. freq[i] = intbias/netsize; /* 1/netsize */
  77. bias[i] = 0;
  78. }
  79. }
  80. ///////////////////////////////////////////////////////////////////////////////////////
  81. // Unbias network to give byte values 0..255 and record position i to prepare for sort
  82. // ------------------------------------------------------------------------------------
  83. void NNQuantizer::unbiasnet() {
  84. int i, j, temp;
  85. for (i = 0; i < netsize; i++) {
  86. for (j = 0; j < 3; j++) {
  87. // OLD CODE: network[i][j] >>= netbiasshift;
  88. // Fix based on bug report by Juergen Weigert jw@suse.de
  89. temp = (network[i][j] + (1 << (netbiasshift - 1))) >> netbiasshift;
  90. if (temp > 255) temp = 255;
  91. network[i][j] = temp;
  92. }
  93. network[i][3] = i; // record colour no
  94. }
  95. }
  96. //////////////////////////////////////////////////////////////////////////////////
  97. // Insertion sort of network and building of netindex[0..255] (to do after unbias)
  98. // -------------------------------------------------------------------------------
  99. void NNQuantizer::inxbuild() {
  100. int i,j,smallpos,smallval;
  101. int *p,*q;
  102. int previouscol,startpos;
  103. previouscol = 0;
  104. startpos = 0;
  105. for (i = 0; i < netsize; i++) {
  106. p = network[i];
  107. smallpos = i;
  108. smallval = p[FI_RGBA_GREEN]; // index on g
  109. // find smallest in i..netsize-1
  110. for (j = i+1; j < netsize; j++) {
  111. q = network[j];
  112. if (q[FI_RGBA_GREEN] < smallval) { // index on g
  113. smallpos = j;
  114. smallval = q[FI_RGBA_GREEN]; // index on g
  115. }
  116. }
  117. q = network[smallpos];
  118. // swap p (i) and q (smallpos) entries
  119. if (i != smallpos) {
  120. j = q[FI_RGBA_BLUE]; q[FI_RGBA_BLUE] = p[FI_RGBA_BLUE]; p[FI_RGBA_BLUE] = j;
  121. j = q[FI_RGBA_GREEN]; q[FI_RGBA_GREEN] = p[FI_RGBA_GREEN]; p[FI_RGBA_GREEN] = j;
  122. j = q[FI_RGBA_RED]; q[FI_RGBA_RED] = p[FI_RGBA_RED]; p[FI_RGBA_RED] = j;
  123. j = q[3]; q[3] = p[3]; p[3] = j;
  124. }
  125. // smallval entry is now in position i
  126. if (smallval != previouscol) {
  127. netindex[previouscol] = (startpos+i)>>1;
  128. for (j = previouscol+1; j < smallval; j++)
  129. netindex[j] = i;
  130. previouscol = smallval;
  131. startpos = i;
  132. }
  133. }
  134. netindex[previouscol] = (startpos+maxnetpos)>>1;
  135. for (j = previouscol+1; j < 256; j++)
  136. netindex[j] = maxnetpos; // really 256
  137. }
  138. ///////////////////////////////////////////////////////////////////////////////
  139. // Search for BGR values 0..255 (after net is unbiased) and return colour index
  140. // ----------------------------------------------------------------------------
  141. int NNQuantizer::inxsearch(int b, int g, int r) {
  142. int i, j, dist, a, bestd;
  143. int *p;
  144. int best;
  145. bestd = 1000; // biggest possible dist is 256*3
  146. best = -1;
  147. i = netindex[g]; // index on g
  148. j = i-1; // start at netindex[g] and work outwards
  149. while ((i < netsize) || (j >= 0)) {
  150. if (i < netsize) {
  151. p = network[i];
  152. dist = p[FI_RGBA_GREEN] - g; // inx key
  153. if (dist >= bestd)
  154. i = netsize; // stop iter
  155. else {
  156. i++;
  157. if (dist < 0)
  158. dist = -dist;
  159. a = p[FI_RGBA_BLUE] - b;
  160. if (a < 0)
  161. a = -a;
  162. dist += a;
  163. if (dist < bestd) {
  164. a = p[FI_RGBA_RED] - r;
  165. if (a<0)
  166. a = -a;
  167. dist += a;
  168. if (dist < bestd) {
  169. bestd = dist;
  170. best = p[3];
  171. }
  172. }
  173. }
  174. }
  175. if (j >= 0) {
  176. p = network[j];
  177. dist = g - p[FI_RGBA_GREEN]; // inx key - reverse dif
  178. if (dist >= bestd)
  179. j = -1; // stop iter
  180. else {
  181. j--;
  182. if (dist < 0)
  183. dist = -dist;
  184. a = p[FI_RGBA_BLUE] - b;
  185. if (a<0)
  186. a = -a;
  187. dist += a;
  188. if (dist < bestd) {
  189. a = p[FI_RGBA_RED] - r;
  190. if (a<0)
  191. a = -a;
  192. dist += a;
  193. if (dist < bestd) {
  194. bestd = dist;
  195. best = p[3];
  196. }
  197. }
  198. }
  199. }
  200. }
  201. return best;
  202. }
  203. ///////////////////////////////
  204. // Search for biased BGR values
  205. // ----------------------------
  206. int NNQuantizer::contest(int b, int g, int r) {
  207. // finds closest neuron (min dist) and updates freq
  208. // finds best neuron (min dist-bias) and returns position
  209. // for frequently chosen neurons, freq[i] is high and bias[i] is negative
  210. // bias[i] = gamma*((1/netsize)-freq[i])
  211. int i,dist,a,biasdist,betafreq;
  212. int bestpos,bestbiaspos,bestd,bestbiasd;
  213. int *p,*f, *n;
  214. bestd = ~(((int) 1)<<31);
  215. bestbiasd = bestd;
  216. bestpos = -1;
  217. bestbiaspos = bestpos;
  218. p = bias;
  219. f = freq;
  220. for (i = 0; i < netsize; i++) {
  221. n = network[i];
  222. dist = n[FI_RGBA_BLUE] - b;
  223. if (dist < 0)
  224. dist = -dist;
  225. a = n[FI_RGBA_GREEN] - g;
  226. if (a < 0)
  227. a = -a;
  228. dist += a;
  229. a = n[FI_RGBA_RED] - r;
  230. if (a < 0)
  231. a = -a;
  232. dist += a;
  233. if (dist < bestd) {
  234. bestd = dist;
  235. bestpos = i;
  236. }
  237. biasdist = dist - ((*p)>>(intbiasshift-netbiasshift));
  238. if (biasdist < bestbiasd) {
  239. bestbiasd = biasdist;
  240. bestbiaspos = i;
  241. }
  242. betafreq = (*f >> betashift);
  243. *f++ -= betafreq;
  244. *p++ += (betafreq << gammashift);
  245. }
  246. freq[bestpos] += beta;
  247. bias[bestpos] -= betagamma;
  248. return bestbiaspos;
  249. }
  250. ///////////////////////////////////////////////////////
  251. // Move neuron i towards biased (b,g,r) by factor alpha
  252. // ----------------------------------------------------
  253. void NNQuantizer::altersingle(int alpha, int i, int b, int g, int r) {
  254. int *n;
  255. n = network[i]; // alter hit neuron
  256. n[FI_RGBA_BLUE] -= (alpha * (n[FI_RGBA_BLUE] - b)) / initalpha;
  257. n[FI_RGBA_GREEN] -= (alpha * (n[FI_RGBA_GREEN] - g)) / initalpha;
  258. n[FI_RGBA_RED] -= (alpha * (n[FI_RGBA_RED] - r)) / initalpha;
  259. }
  260. ////////////////////////////////////////////////////////////////////////////////////
  261. // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
  262. // ---------------------------------------------------------------------------------
  263. void NNQuantizer::alterneigh(int rad, int i, int b, int g, int r) {
  264. int j, k, lo, hi, a;
  265. int *p, *q;
  266. lo = i - rad; if (lo < -1) lo = -1;
  267. hi = i + rad; if (hi > netsize) hi = netsize;
  268. j = i+1;
  269. k = i-1;
  270. q = radpower;
  271. while ((j < hi) || (k > lo)) {
  272. a = (*(++q));
  273. if (j < hi) {
  274. p = network[j];
  275. p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
  276. p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
  277. p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
  278. j++;
  279. }
  280. if (k > lo) {
  281. p = network[k];
  282. p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
  283. p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
  284. p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
  285. k--;
  286. }
  287. }
  288. }
  289. /////////////////////
  290. // Main Learning Loop
  291. // ------------------
  292. /**
  293. Get a pixel sample at position pos. Handle 4-byte boundary alignment.
  294. @param pos pixel position in a WxHx3 pixel buffer
  295. @param b blue pixel component
  296. @param g green pixel component
  297. @param r red pixel component
  298. */
  299. void NNQuantizer::getSample(long pos, int *b, int *g, int *r) {
  300. // get equivalent pixel coordinates
  301. // - assume it's a 24-bit image -
  302. int x = pos % img_line;
  303. int y = pos / img_line;
  304. BYTE *bits = FreeImage_GetScanLine(dib_ptr, y) + x;
  305. *b = bits[FI_RGBA_BLUE] << netbiasshift;
  306. *g = bits[FI_RGBA_GREEN] << netbiasshift;
  307. *r = bits[FI_RGBA_RED] << netbiasshift;
  308. }
  309. void NNQuantizer::learn(int sampling_factor) {
  310. int i, j, b, g, r;
  311. int radius, rad, alpha, step, delta, samplepixels;
  312. int alphadec; // biased by 10 bits
  313. long pos, lengthcount;
  314. // image size as viewed by the scan algorithm
  315. lengthcount = img_width * img_height * 3;
  316. // number of samples used for the learning phase
  317. samplepixels = lengthcount / (3 * sampling_factor);
  318. // decrease learning rate after delta pixel presentations
  319. delta = samplepixels / ncycles;
  320. if(delta == 0) {
  321. // avoid a 'divide by zero' error with very small images
  322. delta = 1;
  323. }
  324. // initialize learning parameters
  325. alphadec = 30 + ((sampling_factor - 1) / 3);
  326. alpha = initalpha;
  327. radius = initradius;
  328. rad = radius >> radiusbiasshift;
  329. if (rad <= 1) rad = 0;
  330. for (i = 0; i < rad; i++)
  331. radpower[i] = alpha*(((rad*rad - i*i)*radbias)/(rad*rad));
  332. // initialize pseudo-random scan
  333. if ((lengthcount % prime1) != 0)
  334. step = 3*prime1;
  335. else {
  336. if ((lengthcount % prime2) != 0)
  337. step = 3*prime2;
  338. else {
  339. if ((lengthcount % prime3) != 0)
  340. step = 3*prime3;
  341. else
  342. step = 3*prime4;
  343. }
  344. }
  345. i = 0; // iteration
  346. pos = 0; // pixel position
  347. while (i < samplepixels) {
  348. // get next learning sample
  349. getSample(pos, &b, &g, &r);
  350. // find winning neuron
  351. j = contest(b, g, r);
  352. // alter winner
  353. altersingle(alpha, j, b, g, r);
  354. // alter neighbours
  355. if (rad) alterneigh(rad, j, b, g, r);
  356. // next sample
  357. pos += step;
  358. while (pos >= lengthcount) pos -= lengthcount;
  359. i++;
  360. if (i % delta == 0) {
  361. // decrease learning rate and also the neighborhood
  362. alpha -= alpha / alphadec;
  363. radius -= radius / radiusdec;
  364. rad = radius >> radiusbiasshift;
  365. if (rad <= 1) rad = 0;
  366. for (j = 0; j < rad; j++)
  367. radpower[j] = alpha * (((rad*rad - j*j) * radbias) / (rad*rad));
  368. }
  369. }
  370. }
  371. //////////////
  372. // Quantizer
  373. // -----------
  374. FIBITMAP* NNQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette, int sampling) {
  375. if ((!dib) || (FreeImage_GetBPP(dib) != 24)) {
  376. return NULL;
  377. }
  378. // 1) Select a sampling factor in range 1..30 (input parameter 'sampling')
  379. // 1 => slower, 30 => faster. Default value is 1
  380. // 2) Get DIB parameters
  381. dib_ptr = dib;
  382. img_width = FreeImage_GetWidth(dib); // DIB width
  383. img_height = FreeImage_GetHeight(dib); // DIB height
  384. img_line = FreeImage_GetLine(dib); // DIB line length in bytes (should be equal to 3 x W)
  385. // For small images, adjust the sampling factor to avoid a 'divide by zero' error later
  386. // (see delta in learn() routine)
  387. int adjust = (img_width * img_height) / ncycles;
  388. if(sampling >= adjust)
  389. sampling = 1;
  390. // 3) Initialize the network and apply the learning algorithm
  391. if( netsize > ReserveSize ) {
  392. netsize -= ReserveSize;
  393. initnet();
  394. learn(sampling);
  395. unbiasnet();
  396. netsize += ReserveSize;
  397. }
  398. // 3.5) Overwrite the last few palette entries with the reserved ones
  399. for (int i = 0; i < ReserveSize; i++) {
  400. network[netsize - ReserveSize + i][FI_RGBA_BLUE] = ReservePalette[i].rgbBlue;
  401. network[netsize - ReserveSize + i][FI_RGBA_GREEN] = ReservePalette[i].rgbGreen;
  402. network[netsize - ReserveSize + i][FI_RGBA_RED] = ReservePalette[i].rgbRed;
  403. network[netsize - ReserveSize + i][3] = netsize - ReserveSize + i;
  404. }
  405. // 4) Allocate a new 8-bit DIB
  406. FIBITMAP *new_dib = FreeImage_Allocate(img_width, img_height, 8);
  407. if (new_dib == NULL)
  408. return NULL;
  409. // 5) Write the quantized palette
  410. RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
  411. for (int j = 0; j < netsize; j++) {
  412. new_pal[j].rgbBlue = (BYTE)network[j][FI_RGBA_BLUE];
  413. new_pal[j].rgbGreen = (BYTE)network[j][FI_RGBA_GREEN];
  414. new_pal[j].rgbRed = (BYTE)network[j][FI_RGBA_RED];
  415. }
  416. inxbuild();
  417. // 6) Write output image using inxsearch(b,g,r)
  418. for (WORD rows = 0; rows < img_height; rows++) {
  419. BYTE *new_bits = FreeImage_GetScanLine(new_dib, rows);
  420. BYTE *bits = FreeImage_GetScanLine(dib_ptr, rows);
  421. for (WORD cols = 0; cols < img_width; cols++) {
  422. new_bits[cols] = (BYTE)inxsearch(bits[FI_RGBA_BLUE], bits[FI_RGBA_GREEN], bits[FI_RGBA_RED]);
  423. bits += 3;
  424. }
  425. }
  426. return (FIBITMAP*) new_dib;
  427. }