diff options
author | Kirill Volinsky <mataes2007@gmail.com> | 2012-12-22 16:38:58 +0000 |
---|---|---|
committer | Kirill Volinsky <mataes2007@gmail.com> | 2012-12-22 16:38:58 +0000 |
commit | 87d155ba9f4e699293adc65954c89f20fc65b36d (patch) | |
tree | d79d69145cc6ef4e97d2ad2912b4d645c7b27158 /plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp | |
parent | a8bd33017b1959588d60c9a8a5ae3a744cb75e4d (diff) |
AdvaImg plugin folder renaming
git-svn-id: http://svn.miranda-ng.org/main/trunk@2801 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp new file mode 100644 index 0000000000..6eb9aeaf48 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp @@ -0,0 +1,507 @@ +// NeuQuant Neural-Net Quantization Algorithm +// ------------------------------------------ +// +// Copyright (c) 1994 Anthony Dekker +// +// NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. +// See "Kohonen neural networks for optimal colour quantization" +// in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367. +// for a discussion of the algorithm. +// +// Any party obtaining a copy of these files from the author, directly or +// indirectly, is granted, free of charge, a full and unrestricted irrevocable, +// world-wide, paid up, royalty-free, nonexclusive right and license to deal +// in this software and documentation files (the "Software"), including without +// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons who receive +// copies from any such party to do so, with the only requirement being +// that this copyright notice remain intact. + +/////////////////////////////////////////////////////////////////////// +// History +// ------- +// January 2001: Adaptation of the Neural-Net Quantization Algorithm +// for the FreeImage 2 library +// Author: Hervé Drolon (drolon@infonie.fr) +// March 2004: Adaptation for the FreeImage 3 library (port to big endian processors) +// Author: Hervé Drolon (drolon@infonie.fr) +// April 2004: Algorithm rewritten as a C++ class. +// Fixed a bug in the algorithm with handling of 4-byte boundary alignment. +// Author: Hervé Drolon (drolon@infonie.fr) +/////////////////////////////////////////////////////////////////////// + +#include "Quantizers.h" +#include "FreeImage.h" +#include "Utilities.h" + + +// Four primes near 500 - assume no image has a length so large +// that it is divisible by all four primes +// ========================================================== + +#define prime1 499 +#define prime2 491 +#define prime3 487 +#define prime4 503 + +// ---------------------------------------------------------------- + +NNQuantizer::NNQuantizer(int PaletteSize) +{ + netsize = PaletteSize; + maxnetpos = netsize - 1; + initrad = netsize < 8 ? 1 : (netsize >> 3); + initradius = (initrad * radiusbias); + + network = NULL; + + network = (pixel *)malloc(netsize * sizeof(pixel)); + bias = (int *)malloc(netsize * sizeof(int)); + freq = (int *)malloc(netsize * sizeof(int)); + radpower = (int *)malloc(initrad * sizeof(int)); + + if ( !network || !bias || !freq || !radpower ) { + if(network) free(network); + if(bias) free(bias); + if(freq) free(freq); + if(radpower) free(radpower); + throw FI_MSG_ERROR_MEMORY; + } +} + +NNQuantizer::~NNQuantizer() +{ + if(network) free(network); + if(bias) free(bias); + if(freq) free(freq); + if(radpower) free(radpower); +} + +/////////////////////////////////////////////////////////////////////////// +// Initialise network in range (0,0,0) to (255,255,255) and set parameters +// ----------------------------------------------------------------------- + +void NNQuantizer::initnet() { + int i, *p; + + for (i = 0; i < netsize; i++) { + p = network[i]; + p[FI_RGBA_BLUE] = p[FI_RGBA_GREEN] = p[FI_RGBA_RED] = (i << (netbiasshift+8))/netsize; + freq[i] = intbias/netsize; /* 1/netsize */ + bias[i] = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// Unbias network to give byte values 0..255 and record position i to prepare for sort +// ------------------------------------------------------------------------------------ + +void NNQuantizer::unbiasnet() { + int i, j, temp; + + for (i = 0; i < netsize; i++) { + for (j = 0; j < 3; j++) { + // OLD CODE: network[i][j] >>= netbiasshift; + // Fix based on bug report by Juergen Weigert jw@suse.de + temp = (network[i][j] + (1 << (netbiasshift - 1))) >> netbiasshift; + if (temp > 255) temp = 255; + network[i][j] = temp; + } + network[i][3] = i; // record colour no + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Insertion sort of network and building of netindex[0..255] (to do after unbias) +// ------------------------------------------------------------------------------- + +void NNQuantizer::inxbuild() { + int i,j,smallpos,smallval; + int *p,*q; + int previouscol,startpos; + + previouscol = 0; + startpos = 0; + for (i = 0; i < netsize; i++) { + p = network[i]; + smallpos = i; + smallval = p[FI_RGBA_GREEN]; // index on g + // find smallest in i..netsize-1 + for (j = i+1; j < netsize; j++) { + q = network[j]; + if (q[FI_RGBA_GREEN] < smallval) { // index on g + smallpos = j; + smallval = q[FI_RGBA_GREEN]; // index on g + } + } + q = network[smallpos]; + // swap p (i) and q (smallpos) entries + if (i != smallpos) { + j = q[FI_RGBA_BLUE]; q[FI_RGBA_BLUE] = p[FI_RGBA_BLUE]; p[FI_RGBA_BLUE] = j; + j = q[FI_RGBA_GREEN]; q[FI_RGBA_GREEN] = p[FI_RGBA_GREEN]; p[FI_RGBA_GREEN] = j; + j = q[FI_RGBA_RED]; q[FI_RGBA_RED] = p[FI_RGBA_RED]; p[FI_RGBA_RED] = j; + j = q[3]; q[3] = p[3]; p[3] = j; + } + // smallval entry is now in position i + if (smallval != previouscol) { + netindex[previouscol] = (startpos+i)>>1; + for (j = previouscol+1; j < smallval; j++) + netindex[j] = i; + previouscol = smallval; + startpos = i; + } + } + netindex[previouscol] = (startpos+maxnetpos)>>1; + for (j = previouscol+1; j < 256; j++) + netindex[j] = maxnetpos; // really 256 +} + +/////////////////////////////////////////////////////////////////////////////// +// Search for BGR values 0..255 (after net is unbiased) and return colour index +// ---------------------------------------------------------------------------- + +int NNQuantizer::inxsearch(int b, int g, int r) { + int i, j, dist, a, bestd; + int *p; + int best; + + bestd = 1000; // biggest possible dist is 256*3 + best = -1; + i = netindex[g]; // index on g + j = i-1; // start at netindex[g] and work outwards + + while ((i < netsize) || (j >= 0)) { + if (i < netsize) { + p = network[i]; + dist = p[FI_RGBA_GREEN] - g; // inx key + if (dist >= bestd) + i = netsize; // stop iter + else { + i++; + if (dist < 0) + dist = -dist; + a = p[FI_RGBA_BLUE] - b; + if (a < 0) + a = -a; + dist += a; + if (dist < bestd) { + a = p[FI_RGBA_RED] - r; + if (a<0) + a = -a; + dist += a; + if (dist < bestd) { + bestd = dist; + best = p[3]; + } + } + } + } + if (j >= 0) { + p = network[j]; + dist = g - p[FI_RGBA_GREEN]; // inx key - reverse dif + if (dist >= bestd) + j = -1; // stop iter + else { + j--; + if (dist < 0) + dist = -dist; + a = p[FI_RGBA_BLUE] - b; + if (a<0) + a = -a; + dist += a; + if (dist < bestd) { + a = p[FI_RGBA_RED] - r; + if (a<0) + a = -a; + dist += a; + if (dist < bestd) { + bestd = dist; + best = p[3]; + } + } + } + } + } + return best; +} + +/////////////////////////////// +// Search for biased BGR values +// ---------------------------- + +int NNQuantizer::contest(int b, int g, int r) { + // finds closest neuron (min dist) and updates freq + // finds best neuron (min dist-bias) and returns position + // for frequently chosen neurons, freq[i] is high and bias[i] is negative + // bias[i] = gamma*((1/netsize)-freq[i]) + + int i,dist,a,biasdist,betafreq; + int bestpos,bestbiaspos,bestd,bestbiasd; + int *p,*f, *n; + + bestd = ~(((int) 1)<<31); + bestbiasd = bestd; + bestpos = -1; + bestbiaspos = bestpos; + p = bias; + f = freq; + + for (i = 0; i < netsize; i++) { + n = network[i]; + dist = n[FI_RGBA_BLUE] - b; + if (dist < 0) + dist = -dist; + a = n[FI_RGBA_GREEN] - g; + if (a < 0) + a = -a; + dist += a; + a = n[FI_RGBA_RED] - r; + if (a < 0) + a = -a; + dist += a; + if (dist < bestd) { + bestd = dist; + bestpos = i; + } + biasdist = dist - ((*p)>>(intbiasshift-netbiasshift)); + if (biasdist < bestbiasd) { + bestbiasd = biasdist; + bestbiaspos = i; + } + betafreq = (*f >> betashift); + *f++ -= betafreq; + *p++ += (betafreq << gammashift); + } + freq[bestpos] += beta; + bias[bestpos] -= betagamma; + return bestbiaspos; +} + +/////////////////////////////////////////////////////// +// Move neuron i towards biased (b,g,r) by factor alpha +// ---------------------------------------------------- + +void NNQuantizer::altersingle(int alpha, int i, int b, int g, int r) { + int *n; + + n = network[i]; // alter hit neuron + n[FI_RGBA_BLUE] -= (alpha * (n[FI_RGBA_BLUE] - b)) / initalpha; + n[FI_RGBA_GREEN] -= (alpha * (n[FI_RGBA_GREEN] - g)) / initalpha; + n[FI_RGBA_RED] -= (alpha * (n[FI_RGBA_RED] - r)) / initalpha; +} + +//////////////////////////////////////////////////////////////////////////////////// +// Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|] +// --------------------------------------------------------------------------------- + +void NNQuantizer::alterneigh(int rad, int i, int b, int g, int r) { + int j, k, lo, hi, a; + int *p, *q; + + lo = i - rad; if (lo < -1) lo = -1; + hi = i + rad; if (hi > netsize) hi = netsize; + + j = i+1; + k = i-1; + q = radpower; + while ((j < hi) || (k > lo)) { + a = (*(++q)); + if (j < hi) { + p = network[j]; + p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias; + p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias; + p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias; + j++; + } + if (k > lo) { + p = network[k]; + p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias; + p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias; + p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias; + k--; + } + } +} + +///////////////////// +// Main Learning Loop +// ------------------ + +/** + Get a pixel sample at position pos. Handle 4-byte boundary alignment. + @param pos pixel position in a WxHx3 pixel buffer + @param b blue pixel component + @param g green pixel component + @param r red pixel component +*/ +void NNQuantizer::getSample(long pos, int *b, int *g, int *r) { + // get equivalent pixel coordinates + // - assume it's a 24-bit image - + int x = pos % img_line; + int y = pos / img_line; + + BYTE *bits = FreeImage_GetScanLine(dib_ptr, y) + x; + + *b = bits[FI_RGBA_BLUE] << netbiasshift; + *g = bits[FI_RGBA_GREEN] << netbiasshift; + *r = bits[FI_RGBA_RED] << netbiasshift; +} + +void NNQuantizer::learn(int sampling_factor) { + int i, j, b, g, r; + int radius, rad, alpha, step, delta, samplepixels; + int alphadec; // biased by 10 bits + long pos, lengthcount; + + // image size as viewed by the scan algorithm + lengthcount = img_width * img_height * 3; + + // number of samples used for the learning phase + samplepixels = lengthcount / (3 * sampling_factor); + + // decrease learning rate after delta pixel presentations + delta = samplepixels / ncycles; + if(delta == 0) { + // avoid a 'divide by zero' error with very small images + delta = 1; + } + + // initialize learning parameters + alphadec = 30 + ((sampling_factor - 1) / 3); + alpha = initalpha; + radius = initradius; + + rad = radius >> radiusbiasshift; + if (rad <= 1) rad = 0; + for (i = 0; i < rad; i++) + radpower[i] = alpha*(((rad*rad - i*i)*radbias)/(rad*rad)); + + // initialize pseudo-random scan + if ((lengthcount % prime1) != 0) + step = 3*prime1; + else { + if ((lengthcount % prime2) != 0) + step = 3*prime2; + else { + if ((lengthcount % prime3) != 0) + step = 3*prime3; + else + step = 3*prime4; + } + } + + i = 0; // iteration + pos = 0; // pixel position + + while (i < samplepixels) { + // get next learning sample + getSample(pos, &b, &g, &r); + + // find winning neuron + j = contest(b, g, r); + + // alter winner + altersingle(alpha, j, b, g, r); + + // alter neighbours + if (rad) alterneigh(rad, j, b, g, r); + + // next sample + pos += step; + while (pos >= lengthcount) pos -= lengthcount; + + i++; + if (i % delta == 0) { + // decrease learning rate and also the neighborhood + alpha -= alpha / alphadec; + radius -= radius / radiusdec; + rad = radius >> radiusbiasshift; + if (rad <= 1) rad = 0; + for (j = 0; j < rad; j++) + radpower[j] = alpha * (((rad*rad - j*j) * radbias) / (rad*rad)); + } + } + +} + +////////////// +// Quantizer +// ----------- + +FIBITMAP* NNQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette, int sampling) { + + if ((!dib) || (FreeImage_GetBPP(dib) != 24)) { + return NULL; + } + + // 1) Select a sampling factor in range 1..30 (input parameter 'sampling') + // 1 => slower, 30 => faster. Default value is 1 + + + // 2) Get DIB parameters + + dib_ptr = dib; + + img_width = FreeImage_GetWidth(dib); // DIB width + img_height = FreeImage_GetHeight(dib); // DIB height + img_line = FreeImage_GetLine(dib); // DIB line length in bytes (should be equal to 3 x W) + + // For small images, adjust the sampling factor to avoid a 'divide by zero' error later + // (see delta in learn() routine) + int adjust = (img_width * img_height) / ncycles; + if(sampling >= adjust) + sampling = 1; + + + // 3) Initialize the network and apply the learning algorithm + + if ( netsize > ReserveSize ) { + netsize -= ReserveSize; + initnet(); + learn(sampling); + unbiasnet(); + netsize += ReserveSize; + } + + // 3.5) Overwrite the last few palette entries with the reserved ones + for (int i = 0; i < ReserveSize; i++) { + network[netsize - ReserveSize + i][FI_RGBA_BLUE] = ReservePalette[i].rgbBlue; + network[netsize - ReserveSize + i][FI_RGBA_GREEN] = ReservePalette[i].rgbGreen; + network[netsize - ReserveSize + i][FI_RGBA_RED] = ReservePalette[i].rgbRed; + network[netsize - ReserveSize + i][3] = netsize - ReserveSize + i; + } + + // 4) Allocate a new 8-bit DIB + + FIBITMAP *new_dib = FreeImage_Allocate(img_width, img_height, 8); + + if (new_dib == NULL) + return NULL; + + // 5) Write the quantized palette + + RGBQUAD *new_pal = FreeImage_GetPalette(new_dib); + + for (int j = 0; j < netsize; j++) { + new_pal[j].rgbBlue = (BYTE)network[j][FI_RGBA_BLUE]; + new_pal[j].rgbGreen = (BYTE)network[j][FI_RGBA_GREEN]; + new_pal[j].rgbRed = (BYTE)network[j][FI_RGBA_RED]; + } + + inxbuild(); + + // 6) Write output image using inxsearch(b,g,r) + + for (WORD rows = 0; rows < img_height; rows++) { + BYTE *new_bits = FreeImage_GetScanLine(new_dib, rows); + BYTE *bits = FreeImage_GetScanLine(dib_ptr, rows); + + for (WORD cols = 0; cols < img_width; cols++) { + new_bits[cols] = (BYTE)inxsearch(bits[FI_RGBA_BLUE], bits[FI_RGBA_GREEN], bits[FI_RGBA_RED]); + + bits += 3; + } + } + + return (FIBITMAP*) new_dib; +} |