/Users/lyon/j4p/src/ip/color/Octree.java
|
1 package ip.color;
2
3 public class Octree {
4 private static final int MAXDEPTH = 7;
5 private int numNodes = 0;
6 private int maxNodes = 0;
7 private int size, level, leafLevel;
8 private RGB colorLut[];
9 private Node tree = null;
10 private Node reduceList[] = new Node[MAXDEPTH + 1];
11 private int k;
12 private short r[][], g[][], b[][];
13
14 public Octree() {
15
16 }
17
18 public void octreeQuantization(short ra[][],
19 short ga[][],
20 short ba[][],
21 int ki) {
22 r = ra;
23 g = ga;
24 b = ba;
25 k = ki;
26
27 setColor();
28 reMap(r, g, b);
29 }
30
31 public void reMap(short r[][], short g[][], short b[][]) {
32 for (int x = 0; x < r.length; x++)
33 for (int y = 0; y < r[0].length; y++) {
34 RGB c = new RGB();
35 c.r = r[x][y];
36 c.g = g[x][y];
37 c.b = b[x][y];
38 int id = findColor(tree, c);
39 c = colorLut[id];
40 r[x][y] = c.r;
41 g[x][y] = c.b;
42 b[x][y] = c.g;
43 }
44 }
45
46 /**
47 * Use the octree color reduction algorithm on a image sequence, so
48 * that you can have a consistent color map for each image in the
49 * sequence. After you have added all the images, go back an remap each
50 * image. - DL
51 *
52 * @param r
53 * @param g
54 * @param b
55 */
56 public void addImagesSeen(short r[][], short g[][], short b[][]) {
57 RGB color = new RGB();
58 leafLevel = level + 1;
59 for (int y = 0; y < r[0].length; y++) {
60 for (int x = 0; x < r.length; x++) {
61 color.r = r[x][y];
62 color.g = g[x][y];
63 color.b = b[x][y];
64 tree = insertNode(tree, color, 0);
65 if (size > k)
66 reduceTree();
67 }
68 }
69 int index[] = new int[1];
70 index[0] = 0;
71 initVGAPalette(tree, index);
72 }
73
74 public void setColor() {
75 RGB color = new RGB();
76
77 colorLut = new RGB[k];
78 tree = null;
79 size = 0;
80 level = MAXDEPTH;
81 leafLevel = level + 1;
82 for (int y = 0; y < r[0].length; y++) {
83 for (int x = 0; x < r.length; x++) {
84 color.r = r[x][y];
85 color.g = g[x][y];
86 color.b = b[x][y];
87 tree = insertNode(tree, color, 0);
88 if (size > k)
89 reduceTree();
90 }
91 }
92 int index[] = new int[1];
93 index[0] = 0;
94 initVGAPalette(tree, index);
95
96 }
97
98 public int findColor(Node tree, RGB color) {
99 if (tree.leaf)
100 return tree.colorIndex;
101 else {
102 final int i = ((color.r >> (MAXDEPTH - tree.level)) & 1) <<
103 2 |
104 ((color.g >> (MAXDEPTH - tree.level)) & 1) << 1 |
105 (color.b >> (MAXDEPTH - tree.level)) & 1;
106 final Node treeNode = tree.link[i];
107 if (treeNode != null)
108 return findColor(treeNode, color);
109 return findNearestEntry(color);
110 }
111 }
112 /**
113 * search the color lookup table for
114 * @param color
115 * @return an index of the closest entry.
116 */
117 public int findNearestEntry(RGB color) {
118 int n = colorLut.length;
119 int error = Integer.MAX_VALUE;
120 int bestIndex = 0;
121 for (int i=0; i < n ; i++) {
122 RGB c = colorLut[i];
123 int e = c.getError(color);
124 if (e < error) {
125 error = e;
126 bestIndex = i;
127 }
128 }
129 return bestIndex;
130 }
131
132 public Node insertNode(Node node, RGB color, int depth) {
133 int branch;
134
135 if (node == null) // create new node
136 {
137 node = new Node();
138 numNodes++;
139 if (numNodes > maxNodes)
140 maxNodes = numNodes;
141 node.level = depth;
142 node.leaf = (depth >= leafLevel) ? true : false;
143 if (node.leaf)
144 size++;
145 }
146 node.colorCount++;
147 node.RGBSum.r += color.r;
148 node.RGBSum.g += color.g;
149 node.RGBSum.b += color.b;
150 if (!(node.leaf) && (depth < leafLevel)) {
151 branch = ((color.r >> (MAXDEPTH - depth)) & 1) << 2 |
152 ((color.g >> (MAXDEPTH - depth)) & 1) << 1 |
153 (color.b >> (MAXDEPTH - depth)) & 1;
154 if (node.link[branch] == null) {
155 node.child++;
156 if (node.child == 2) {
157 node.nextReduceable = reduceList[depth];
158 reduceList[depth] = node;
159 }
160 }
161 node.link[branch] =
162 insertNode(node.link[branch], color, depth + 1);
163 }
164
165 return node;
166 }
167
168 public Node killTree(Node tree) {
169 if (tree == null)
170 return null;
171 for (int i = 0; i < 8; i++)
172 tree.link[i] = killTree(tree.link[i]);
173
174 numNodes--;
175
176 return null;
177 }
178
179 public void reduceTree() {
180 Node node;
181 int new_Level;
182 int depth;
183
184 new_Level = level;
185 while (reduceList[new_Level] == null)
186 new_Level--;
187 node = reduceList[new_Level];
188 reduceList[new_Level] = reduceList[new_Level].nextReduceable;
189 node.leaf = true;
190 size = size - node.child + 1;
191 depth = node.level;
192 for (int i = 0; i < 8; i++)
193 node.link[i] = killTree(node.link[i]);
194 if (depth < level) {
195 level = depth;
196 leafLevel = level + 1;
197 }
198 }
199
200 public void initVGAPalette(Node tree, int index[]) {
201 if (tree != null) {
202 if (tree.leaf || tree.level == leafLevel) {
203 if (colorLut[index[0]] == null)
204 colorLut[index[0]] = new RGB();
205 colorLut[index[0]].r =
206 (short) (tree.RGBSum.r / tree.colorCount);
207 colorLut[index[0]].g =
208 (short) (tree.RGBSum.g / tree.colorCount);
209 colorLut[index[0]].b =
210 (short) (tree.RGBSum.b / tree.colorCount);
211 tree.colorIndex = index[0]++;
212 tree.leaf = true;
213 } else
214 for (int octant = 0; octant < 8; octant++)
215 initVGAPalette(tree.link[octant], index);
216 }
217 }
218
219 public static int getMAXDEPTH() {
220 return MAXDEPTH;
221 }
222
223 public int getNumNodes() {
224 return numNodes;
225 }
226
227 public void setNumNodes(int numNodes) {
228 this.numNodes = numNodes;
229 }
230
231 public int getMaxNodes() {
232 return maxNodes;
233 }
234
235 public void setMaxNodes(int maxNodes) {
236 this.maxNodes = maxNodes;
237 }
238
239 public int getSize() {
240 return size;
241 }
242
243 public void setSize(int size) {
244 this.size = size;
245 }
246
247 public int getLevel() {
248 return level;
249 }
250
251 public void setLevel(int level) {
252 this.level = level;
253 }
254
255 public int getLeafLevel() {
256 return leafLevel;
257 }
258
259 public void setLeafLevel(int leafLevel) {
260 this.leafLevel = leafLevel;
261 }
262
263 public Node getTree() {
264 return tree;
265 }
266
267 public int getK() {
268 return k;
269 }
270
271 public short[][] getR() {
272 return r;
273 }
274
275 public short[][] getG() {
276 return g;
277 }
278
279 public short[][] getB() {
280 return b;
281 }
282
283 public RGB[] getColorLut() {
284 return colorLut;
285 }
286
287 public Node[] getReduceList() {
288 return reduceList;
289 }
290 }
291
292 class ColorSum {
293 public long r, g, b;
294
295 public ColorSum() {
296 r = g = b = 0;
297 }
298 }
299
300 class RGB {
301 public short r, g, b;
302 /**
303 *
304 * @param rgb
305 * @return the square of the error in RGB space.
306 */
307 public int getError(RGB rgb) {
308 if (rgb == null) return 0;
309 int dr = r - rgb.r;
310 int dg = g - rgb.g;
311 int db = b - rgb.b;
312 return dr*dr+dg*dg+db*db;
313 }
314 }
315
316 class Node {
317 boolean leaf = false;
318 int level = 0;
319 int colorIndex = 0;
320 int child = 0;
321 long colorCount = 0;
322 ColorSum RGBSum = new ColorSum();
323 Node nextReduceable = null;
324 Node link[] = new Node[8];
325
326 public Node() {
327 for (int i = 0; i < 8; i++)
328 link[i] = null;
329 }
330 }