summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp507
1 files changed, 0 insertions, 507 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp
deleted file mode 100644
index 4f02446d8e..0000000000
--- a/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp
+++ /dev/null
@@ -1,507 +0,0 @@
-// 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;
-}