
com.idrsolutions.image.png.Quant32 Maven / Gradle / Ivy
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2015 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* Quant32.java
* ---------------
*/
package com.idrsolutions.image.png;
/**
* 32 bit to 8 bit Image Quantisation implementation
*/
public class Quant32 {
private static final int IndexBits = 6;
private static final int IndexBitsPlus = IndexBits + 1;
private static final int DoubleIndexBits = IndexBits * 2;
private static final int IndexAlphaBits = 3;
private static final int SumBits = IndexBits + IndexAlphaBits;
private static final int IndexCount = (1 << IndexBits) + 1;
private static final int IndexAlphaCount = (1 << IndexAlphaBits) + 1;
private static final int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount;
private final long[] vwt, vmr, vmg, vmb, vma;
private final double[] m2;
public Quant32() {
vwt = new long[TableLength];
vmr = new long[TableLength];
vmg = new long[TableLength];
vmb = new long[TableLength];
vma = new long[TableLength];
m2 = new double[TableLength];
}
private static int indexify(int r, int g, int b, int a) {
return (r << (DoubleIndexBits + IndexAlphaBits))
+ (r << (SumBits + 1))
+ (g << SumBits)
+ (r << DoubleIndexBits)
+ (r << IndexBitsPlus)
+ (g << IndexBits)
+ ((r + g + b) << IndexAlphaBits)
+ r + g + b + a;
}
private static double volume(Cube cube, long[] moment) {
return moment[indexify(cube.R1, cube.G1, cube.B1, cube.A1)]
- moment[indexify(cube.R1, cube.G1, cube.B1, cube.A0)]
- moment[indexify(cube.R1, cube.G1, cube.B0, cube.A1)]
+ moment[indexify(cube.R1, cube.G1, cube.B0, cube.A0)]
- moment[indexify(cube.R1, cube.G0, cube.B1, cube.A1)]
+ moment[indexify(cube.R1, cube.G0, cube.B1, cube.A0)]
+ moment[indexify(cube.R1, cube.G0, cube.B0, cube.A1)]
- moment[indexify(cube.R1, cube.G0, cube.B0, cube.A0)]
- moment[indexify(cube.R0, cube.G1, cube.B1, cube.A1)]
+ moment[indexify(cube.R0, cube.G1, cube.B1, cube.A0)]
+ moment[indexify(cube.R0, cube.G1, cube.B0, cube.A1)]
- moment[indexify(cube.R0, cube.G1, cube.B0, cube.A0)]
+ moment[indexify(cube.R0, cube.G0, cube.B1, cube.A1)]
- moment[indexify(cube.R0, cube.G0, cube.B1, cube.A0)]
- moment[indexify(cube.R0, cube.G0, cube.B0, cube.A1)]
+ moment[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
}
private static long base(Cube cube, int direction, long[] moment) {
switch (direction) {
case 3:
return -moment[indexify(cube.R0, cube.G1, cube.B1, cube.A1)]
+ moment[indexify(cube.R0, cube.G1, cube.B1, cube.A0)]
+ moment[indexify(cube.R0, cube.G1, cube.B0, cube.A1)]
- moment[indexify(cube.R0, cube.G1, cube.B0, cube.A0)]
+ moment[indexify(cube.R0, cube.G0, cube.B1, cube.A1)]
- moment[indexify(cube.R0, cube.G0, cube.B1, cube.A0)]
- moment[indexify(cube.R0, cube.G0, cube.B0, cube.A1)]
+ moment[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
case 2:
return -moment[indexify(cube.R1, cube.G0, cube.B1, cube.A1)]
+ moment[indexify(cube.R1, cube.G0, cube.B1, cube.A0)]
+ moment[indexify(cube.R1, cube.G0, cube.B0, cube.A1)]
- moment[indexify(cube.R1, cube.G0, cube.B0, cube.A0)]
+ moment[indexify(cube.R0, cube.G0, cube.B1, cube.A1)]
- moment[indexify(cube.R0, cube.G0, cube.B1, cube.A0)]
- moment[indexify(cube.R0, cube.G0, cube.B0, cube.A1)]
+ moment[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
case 1:
return -moment[indexify(cube.R1, cube.G1, cube.B0, cube.A1)]
+ moment[indexify(cube.R1, cube.G1, cube.B0, cube.A0)]
+ moment[indexify(cube.R1, cube.G0, cube.B0, cube.A1)]
- moment[indexify(cube.R1, cube.G0, cube.B0, cube.A0)]
+ moment[indexify(cube.R0, cube.G1, cube.B0, cube.A1)]
- moment[indexify(cube.R0, cube.G1, cube.B0, cube.A0)]
- moment[indexify(cube.R0, cube.G0, cube.B0, cube.A1)]
+ moment[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
case 0:
return -moment[indexify(cube.R1, cube.G1, cube.B1, cube.A0)]
+ moment[indexify(cube.R1, cube.G1, cube.B0, cube.A0)]
+ moment[indexify(cube.R1, cube.G0, cube.B1, cube.A0)]
- moment[indexify(cube.R1, cube.G0, cube.B0, cube.A0)]
+ moment[indexify(cube.R0, cube.G1, cube.B1, cube.A0)]
- moment[indexify(cube.R0, cube.G1, cube.B0, cube.A0)]
- moment[indexify(cube.R0, cube.G0, cube.B1, cube.A0)]
+ moment[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
default:
return 0;
}
}
/// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction).
private static long findTop(Cube cube, int direction, int position, long[] moment) {
switch (direction) {
case 3:
return moment[indexify(position, cube.G1, cube.B1, cube.A1)]
- moment[indexify(position, cube.G1, cube.B1, cube.A0)]
- moment[indexify(position, cube.G1, cube.B0, cube.A1)]
+ moment[indexify(position, cube.G1, cube.B0, cube.A0)]
- moment[indexify(position, cube.G0, cube.B1, cube.A1)]
+ moment[indexify(position, cube.G0, cube.B1, cube.A0)]
+ moment[indexify(position, cube.G0, cube.B0, cube.A1)]
- moment[indexify(position, cube.G0, cube.B0, cube.A0)];
case 2:
return moment[indexify(cube.R1, position, cube.B1, cube.A1)]
- moment[indexify(cube.R1, position, cube.B1, cube.A0)]
- moment[indexify(cube.R1, position, cube.B0, cube.A1)]
+ moment[indexify(cube.R1, position, cube.B0, cube.A0)]
- moment[indexify(cube.R0, position, cube.B1, cube.A1)]
+ moment[indexify(cube.R0, position, cube.B1, cube.A0)]
+ moment[indexify(cube.R0, position, cube.B0, cube.A1)]
- moment[indexify(cube.R0, position, cube.B0, cube.A0)];
case 1:
return moment[indexify(cube.R1, cube.G1, position, cube.A1)]
- moment[indexify(cube.R1, cube.G1, position, cube.A0)]
- moment[indexify(cube.R1, cube.G0, position, cube.A1)]
+ moment[indexify(cube.R1, cube.G0, position, cube.A0)]
- moment[indexify(cube.R0, cube.G1, position, cube.A1)]
+ moment[indexify(cube.R0, cube.G1, position, cube.A0)]
+ moment[indexify(cube.R0, cube.G0, position, cube.A1)]
- moment[indexify(cube.R0, cube.G0, position, cube.A0)];
case 0:
return moment[indexify(cube.R1, cube.G1, cube.B1, position)]
- moment[indexify(cube.R1, cube.G1, cube.B0, position)]
- moment[indexify(cube.R1, cube.G0, cube.B1, position)]
+ moment[indexify(cube.R1, cube.G0, cube.B0, position)]
- moment[indexify(cube.R0, cube.G1, cube.B1, position)]
+ moment[indexify(cube.R0, cube.G1, cube.B0, position)]
+ moment[indexify(cube.R0, cube.G0, cube.B1, position)]
- moment[indexify(cube.R0, cube.G0, cube.B0, position)];
default:
return 0;
}
}
private void histogram(int[][] image) {
int mm = 8 - IndexBits;
int nn = 8 - IndexAlphaBits;
int h = image.length;
int w = image[0].length;
int[] temp;
int a, r, g, b;
for (int y = 0; y < h; y++) {
temp = image[y];
for (int x = 0; x < w; x++) {
int val = temp[x];
a = (val >> 24)&0xff;
r = (val >> 16) &0xff;
g = (val >> 8) & 0xff;
b = val & 0xff;
int inr = r >> mm;
int ing = g >> mm;
int inb = b >> mm;
int ina = a >> nn;
int ind = indexify(inr + 1, ing + 1, inb + 1, ina + 1);
vwt[ind]++;
vmr[ind] += r;
vmg[ind] += g;
vmb[ind] += b;
vma[ind] += a;
m2[ind] += (r * r) + (g * g) + (b * b) + (a * a);
}
}
}
private void M3d() {
long[] volume, volR, volG, volB, volA;
double[] volTemp;
long[] area, areaR, areaG, areaB, areaA;
double[] areaTemp;
int multiCount = IndexCount * IndexAlphaCount;
for (int r = 1; r < IndexCount; r++) {
volume = new long[multiCount];
volR = new long[multiCount];
volG = new long[multiCount];
volB = new long[multiCount];
volA = new long[multiCount];
volTemp = new double[multiCount];
for (int g = 1; g < IndexCount; g++) {
area = new long[IndexAlphaCount];
areaR = new long[IndexAlphaCount];
areaG = new long[IndexAlphaCount];
areaB = new long[IndexAlphaCount];
areaA = new long[IndexAlphaCount];
areaTemp = new double[IndexAlphaCount];
for (int b = 1; b < IndexCount; b++) {
long line = 0;
long line_r = 0;
long line_g = 0;
long line_b = 0;
long line_a = 0;
double line2 = 0;
for (int a = 1; a < IndexAlphaCount; a++) {
int ind1 = indexify(r, g, b, a);
line += vwt[ind1];
line_r += vmr[ind1];
line_g += vmg[ind1];
line_b += vmb[ind1];
line_a += vma[ind1];
line2 += m2[ind1];
area[a] += line;
areaR[a] += line_r;
areaG[a] += line_g;
areaB[a] += line_b;
areaA[a] += line_a;
areaTemp[a] += line2;
int inv = (b * IndexAlphaCount) + a;
volume[inv] += area[a];
volR[inv] += areaR[a];
volG[inv] += areaG[a];
volB[inv] += areaB[a];
volA[inv] += areaA[a];
volTemp[inv] += areaTemp[a];
int ind2 = ind1 - indexify(1, 0, 0, 0);
vwt[ind1] = vwt[ind2] + volume[inv];
vmr[ind1] = vmr[ind2] + volR[inv];
vmg[ind1] = vmg[ind2] + volG[inv];
vmb[ind1] = vmb[ind2] + volB[inv];
vma[ind1] = vma[ind2] + volA[inv];
m2[ind1] = m2[ind2] + volTemp[inv];
}
}
}
}
}
private double variance(Cube cube) {
double dr = volume(cube, vmr);
double dg = volume(cube, vmg);
double db = volume(cube, vmb);
double da = volume(cube, vma);
double cc
= m2[indexify(cube.R1, cube.G1, cube.B1, cube.A1)]
- m2[indexify(cube.R1, cube.G1, cube.B1, cube.A0)]
- m2[indexify(cube.R1, cube.G1, cube.B0, cube.A1)]
+ m2[indexify(cube.R1, cube.G1, cube.B0, cube.A0)]
- m2[indexify(cube.R1, cube.G0, cube.B1, cube.A1)]
+ m2[indexify(cube.R1, cube.G0, cube.B1, cube.A0)]
+ m2[indexify(cube.R1, cube.G0, cube.B0, cube.A1)]
- m2[indexify(cube.R1, cube.G0, cube.B0, cube.A0)]
- m2[indexify(cube.R0, cube.G1, cube.B1, cube.A1)]
+ m2[indexify(cube.R0, cube.G1, cube.B1, cube.A0)]
+ m2[indexify(cube.R0, cube.G1, cube.B0, cube.A1)]
- m2[indexify(cube.R0, cube.G1, cube.B0, cube.A0)]
+ m2[indexify(cube.R0, cube.G0, cube.B1, cube.A1)]
- m2[indexify(cube.R0, cube.G0, cube.B1, cube.A0)]
- m2[indexify(cube.R0, cube.G0, cube.B0, cube.A1)]
+ m2[indexify(cube.R0, cube.G0, cube.B0, cube.A0)];
return cc - ((dr * dr + dg * dg + db * db + da * da) / volume(cube, vwt));
}
private Object[] maximize(Cube cube, int direction, int first, int last, double whole_r, double whole_g, double whole_b, double whole_a, double whole_w) {
long base_r = base(cube, direction, vmr);
long base_g = base(cube, direction, vmg);
long base_b = base(cube, direction, vmb);
long base_a = base(cube, direction, vma);
long base_w = base(cube, direction, vwt);
double max = 0.0;
int cut = -1;
for (int i = first; i < last; i++) {
double half_r = base_r + findTop(cube, direction, i, vmr);
double half_g = base_g + findTop(cube, direction, i, vmg);
double half_b = base_b + findTop(cube, direction, i, vmb);
double half_a = base_a + findTop(cube, direction, i, vma);
double half_w = base_w + findTop(cube, direction, i, vwt);
double temp;
if (half_w == 0) {
continue;
} else {
temp = ((half_r * half_r) + (half_g * half_g) + (half_b * half_b) + (half_a * half_a)) / half_w;
}
half_r = whole_r - half_r;
half_g = whole_g - half_g;
half_b = whole_b - half_b;
half_a = whole_a - half_a;
half_w = whole_w - half_w;
if (half_w == 0) {
continue;
} else {
temp += ((half_r * half_r) + (half_g * half_g) + (half_b * half_b) + (half_a * half_a)) / half_w;
}
if (temp > max) {
max = temp;
cut = i;
}
}
return new Object[]{cut, max};
}
private boolean cut(Cube set1, Cube set2) {
double whole_r = volume(set1, vmr);
double whole_g = volume(set1, vmg);
double whole_b = volume(set1, vmb);
double whole_a = volume(set1, vma);
double whole_w = volume(set1, vwt);
Object[] temp;
temp = maximize(set1, 3, set1.R0 + 1, set1.R1, whole_r, whole_g, whole_b, whole_a, whole_w);
int cutr = (Integer) temp[0];
double maxr = (Double) temp[1];
temp = maximize(set1, 2, set1.G0 + 1, set1.G1, whole_r, whole_g, whole_b, whole_a, whole_w);
int cutg = (Integer) temp[0];
double maxg = (Double) temp[1];
temp = maximize(set1, 1, set1.B0 + 1, set1.B1, whole_r, whole_g, whole_b, whole_a, whole_w);
int cutb = (Integer) temp[0];
double maxb = (Double) temp[1];
temp = maximize(set1, 0, set1.A0 + 1, set1.A1, whole_r, whole_g, whole_b, whole_a, whole_w);
int cuta = (Integer) temp[0];
double maxa = (Double) temp[1];
int dir;
if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) {
dir = 3;
if (cutr < 0) {
return false;
}
} else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) {
dir = 2;
} else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) {
dir = 1;
} else {
dir = 0;
}
set2.R1 = set1.R1;
set2.G1 = set1.G1;
set2.B1 = set1.B1;
set2.A1 = set1.A1;
switch (dir) {
case 3:
set2.R0 = set1.R1 = cutr;
set2.G0 = set1.G0;
set2.B0 = set1.B0;
set2.A0 = set1.A0;
break;
case 2:
set2.G0 = set1.G1 = cutg;
set2.R0 = set1.R0;
set2.B0 = set1.B0;
set2.A0 = set1.A0;
break;
case 1:
set2.B0 = set1.B1 = cutb;
set2.R0 = set1.R0;
set2.G0 = set1.G0;
set2.A0 = set1.A0;
break;
case 0:
set2.A0 = set1.A1 = cuta;
set2.R0 = set1.R0;
set2.G0 = set1.G0;
set2.B0 = set1.B0;
break;
}
set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0);
set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0);
return true;
}
// private void mark(Cube cube, byte label, byte[] tag) {
// for (int r = cube.R0 + 1; r <= cube.R1; r++) {
// for (int g = cube.G0 + 1; g <= cube.G1; g++) {
// for (int b = cube.B0 + 1; b <= cube.B1; b++) {
// for (int a = cube.A0 + 1; a <= cube.A1; a++) {
// tag[indexify(r, g, b, a)] = label;
// }
// }
// }
// }
// }
private void buildCube(Cube[] cube, int colorCount) {
double[] vv = new double[colorCount];
for (int i = 0; i < colorCount; i++) {
cube[i] = new Cube();
}
cube[0].R0 = cube[0].G0 = cube[0].B0 = cube[0].A0 = 0;
cube[0].R1 = cube[0].G1 = cube[0].B1 = IndexCount - 1;
cube[0].A1 = IndexAlphaCount - 1;
int next = 0;
for (int i = 1; i < colorCount; i++) {
if (cut(cube[next], cube[i])) {
vv[next] = cube[next].Volume > 1 ? variance(cube[next]) : 0.0;
vv[i] = cube[i].Volume > 1 ? variance(cube[i]) : 0.0;
} else {
vv[next] = 0.0;
i--;
}
next = 0;
double temp = vv[0];
for (int k = 1; k <= i; k++) {
if (vv[k] > temp) {
temp = vv[k];
next = k;
}
}
if (temp <= 0.0) {
break;
}
}
}
public Object[] getPalette(int[][] image) {
int colorCount = 256;
histogram(image);
M3d();
Cube[] cube = new Cube[colorCount];
buildCube(cube, colorCount);
byte[] palette = new byte[256 * 3];
byte[] trns = new byte[256];
int z = 0;
for (int k = 0; k < colorCount; k++) {
double weight = volume(cube[k], vwt);
if (weight != 0) {
trns[k] = (byte) (volume(cube[k], vma) / weight);
palette[z++] = (byte) (volume(cube[k], vmr) / weight);
palette[z++] = (byte) (volume(cube[k], vmg) / weight);
palette[z++] = (byte) (volume(cube[k], vmb) / weight);
} else {
z += 4;
}
}
return new Object[]{palette, trns};
}
// public Object[] quantize(byte[] image) {
// int colorCount = 256;
// histogram(image);
// M3d();
//
// Cube[] cube = new Cube[colorCount];
// buildCube(cube, colorCount);
//
// return generateResult(image, colorCount, cube);
// }
// private Object[] generateResult(byte[] image, int colorCount, Cube[] cube) {
// byte[] palette = new byte[256 * 3];
//
// byte[] tag = new byte[TableLength];
// byte[] trns = new byte[256];
// int z = 0;
// for (int k = 0; k < colorCount; k++) {
// mark(cube[k], (byte) k, tag);
// double weight = volume(cube[k], vwt);
// if (weight != 0) {
// trns[k] = (byte) (volume(cube[k], vma) / weight);
// palette[z++] = (byte) (volume(cube[k], vmr) / weight);
// palette[z++] = (byte) (volume(cube[k], vmg) / weight);
// palette[z++] = (byte) (volume(cube[k], vmb) / weight);
//
// } else {
// trns[k] = (byte) 0xff;
// palette[z++] = 0;
// palette[z++] = 0;
// palette[z++] = 0;
// }
// }
//
// byte[] indexedBytes = new byte[image.length / 4];
// z = 0;
// int mm = 8 - IndexBits;
// int nn = 8 - IndexAlphaBits;
// int ii = image.length / 4;
// for (int i = 0; i < ii; i++) {
// int a = (image[z++] & 0xff) >> nn;
// int b = (image[z++] & 0xff) >> mm;
// int g = (image[z++] & 0xff) >> mm;
// int r = (image[z++] & 0xff) >> mm;
//
// int ind = indexify(r + 1, g + 1, b + 1, a + 1);
//
// indexedBytes[i] = tag[ind];
// }
// return new Object[]{palette, indexedBytes, trns};
// }
private class Cube {
int A0, A1, R0, R1, G0, G1, B0, B1, Volume;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy