home *** CD-ROM | disk | FTP | other *** search
- Path: usenet.ee.pdx.edu!cs.uoregon.edu!sgiblab!swrinde!cs.utexas.edu!howland.reston.ans.net!xlink.net!newshost.uni-koblenz.de!usenet
- From: lidl@informatik.uni-koblenz.de
- Newsgroups: comp.graphics.algorithms
- Subject: Re: Octree quantization method - how?
- Date: 13 Apr 1994 08:25:56 GMT
- Organization: University Koblenz / Germany
- Lines: 535
- Message-ID: <2ogaak$1ck@newshost.uni-koblenz.de>
- Reply-To: lidl@infko.uni-koblenz.de
- NNTP-Posting-Host: nepal.uni-koblenz.de
-
- Hi,
-
- i've implemented a version of the octree algorith, shown in the
- Graphics Gems (the article isn't very correct, is it? :-))))
-
- Well, let's start with the header:
-
- /*----------------------------------------------------------*/
-
- #define maxTreeDepth 7
- #define maxDepthElems (maxTreeDepth+1)
- #ifndef sqr
- #define sqr(x) ((x)*(x))
- #endif
-
- typedef char *octreeLeaves[8];
-
- typedef int colorSum[3];
-
- typedef struct {
- int colorCount,
- children,
- isLeaf;
- colorSum RGB;
- unsigned char midPoint[3],
- colorIndex;
- octreeLeaves nextBranch;
- } octreeNode;
-
-
- extern void initOctree(int,int);
- extern void destroyNodes(octreeNode **);
- extern void destroyOctree();
- extern void newOctreeNode(octreeNode **);
- extern void reduceTree();
- extern void insertNewOctree(octreeNode **,unsigned char
- *,unsigned
- char *,int,int);
- extern void generateEntry(octreeNode *);
- extern int generatePalette(octreeNode *,unsigned char *);
- extern unsigned char quant(octreeNode *,unsigned char *);
- extern unsigned char *quantRgb(octreeNode *,unsigned char *);
- extern int octreeBranch(unsigned char *,unsigned char *);
-
- /*----------------------------------------------------------*/
-
- Now, the main file:
-
- /*----------------------------------------------------------*/
- #import "octree.h"
-
- #import <stdlib.h>
- #import <ansi/math.h>
- #import <objc/List.h>
-
-
- int depthDist[maxDepthElems] = { 128,64,32,16,8,4,2,1 };
-
- int representedNodes,
- wantedColors,
- reduceLevel,
- paletteIndex,
- reduceDir;
- List *reducible[maxTreeDepth];
- unsigned char *genPal,
- *genPalBase;
- octreeNode *lastLeaf;
-
-
- void initOctree(int colorAnz,int reduceMode)
- {
- int initInd;
-
-
- representedNodes = 0;
- wantedColors = colorAnz;
- reduceLevel = (maxTreeDepth-1);
- reduceDir = reduceMode;
- for ( initInd=0 ; initInd<maxTreeDepth ; initInd++ )
- reducible[initInd] = [[List alloc] init];
- }
-
-
- void destroyNodes(octreeNode **root)
- {
- int delInd;
-
-
- if (*root)
- {
- for ( delInd=0 ; delInd<8 ; delInd++ )
- destroyNodes((octreeNode **)&((*root)->nextBranch[delInd]));
- free(*root);
- *root = NULL;
- }
- }
-
-
- void destroyOctree()
- {
- int destroyInd;
-
-
- for ( destroyInd=0 ; destroyInd<maxTreeDepth ; destroyInd++ )
- {
- [reducible[destroyInd] empty];
- [reducible[destroyInd] free];
- reducible[destroyInd] = NULL;
- }
- }
-
-
- void newOctreeNode(octreeNode **newNode)
- {
- octreeNode *initNode;
- int initInd;
-
-
- initNode = (octreeNode *)malloc(sizeof(octreeNode));
- for ( initInd=0 ; initInd<8 ; initInd++ )
- initNode->nextBranch[initInd] = NULL;
- initNode->colorCount = 0;
- initNode->children = 0;
- initNode->isLeaf = 0;
- for ( initInd=0 ; initInd<3 ; initInd++ )
- initNode->RGB[initInd] = 0;
- initNode->midPoint[0] = 0;
- initNode->midPoint[1] = 0;
- initNode->midPoint[2] = 0;
- *newNode = initNode;
- }
-
-
- void reduceTree()
- {
- int redCount,
- redInd,
- redPixels,
- redMaxAdr,
- redChild;
- octreeNode *reduceTo,
- *redTemp;
- colorSum reduceSum;
-
- while (!(redCount = [reducible[reduceLevel] count]))
- reduceLevel--;
- if (reduceDir) { redPixels = MAXINT; }
- else { redPixels = 0; }
- redMaxAdr = 0;
- for ( redInd=0 ; redInd<redCount ; redInd++ )
- {
- reduceTo = (octreeNode *)[reducible[reduceLevel] objectAt:redInd];
- if (reduceDir)
- {
- if (reduceTo->colorCount < redPixels)
- {
- redPixels = reduceTo->colorCount;
- redMaxAdr = redInd;
- }
- }
- else
- {
- if (reduceTo->colorCount > redPixels)
- {
- redPixels = reduceTo->colorCount;
- redMaxAdr = redInd;
- }
- }
- }
- reduceTo = (octreeNode *)[reducible[reduceLevel]
- removeObjectAt:redMaxAdr];
- if (reduceTo)
- {
- for ( redInd=0 ; redInd<3 ; redInd++ )
- reduceSum[redInd] = 0;
- redChild = 0;
- for ( redInd=0 ; redInd<8 ; redInd++ )
- if (reduceTo->nextBranch[redInd])
- {
- redChild++;
- redTemp = (octreeNode *)reduceTo->nextBranch[redInd];
- reduceSum[0] += redTemp->RGB[0];
- reduceSum[1] += redTemp->RGB[1];
- reduceSum[2] += redTemp->RGB[2];
- destroyNodes((octreeNode
- **)&(reduceTo->nextBranch[redInd]));
- reduceTo->nextBranch[redInd] = NULL;
- }
- reduceTo->isLeaf = 1;
- reduceTo->RGB[0] = reduceSum[0];
- reduceTo->RGB[1] = reduceSum[1];
- reduceTo->RGB[2] = reduceSum[2];
- representedNodes = (representedNodes - redChild + 1);
- }
- }
-
-
- void insertNewOctree(octreeNode **actBranch,unsigned char *center,
- unsigned char *insertRGB,int depth,int doReduce)
- {
- int newBranch,
- distSub;
- unsigned char newMid[3];
- octreeNode *newNode;
-
-
- if (!(*actBranch))
- {
- newOctreeNode(&newNode);
- *actBranch = newNode;
- if ((depth<maxTreeDepth) && (doReduce))
- {
- [reducible[depth] addObject:(id)newNode];
- reduceLevel = depth;
- }
- memcpy(newNode->midPoint,center,3);
- if (depth == maxTreeDepth)
-
- {
- representedNodes++;
- newNode->isLeaf = 1;
- lastLeaf = newNode;
- }
- }
- else newNode = *actBranch;
- newNode->colorCount++;
- if ((*actBranch)->isLeaf)
- {
- newNode->isLeaf = 1;
- newNode->children++;
- newNode->RGB[0] += *(insertRGB);
- newNode->RGB[1] += *(insertRGB+1);
- newNode->RGB[2] += *(insertRGB+2);
- while ((representedNodes>wantedColors) && (doReduce))
- reduceTree();
- }
- else
-
- {
- newBranch = octreeBranch(newNode->midPoint,insertRGB);
- memcpy(newMid,newNode->midPoint,3);
- distSub = depthDist[depth];
- if (newBranch & 0x0004) { newMid[0] += distSub; }
- else { newMid[0] -= distSub; }
- if (newBranch & 0x0002) { newMid[1] += distSub; }
- else { newMid[1] -= distSub; }
- if (newBranch & 0x0001) { newMid[2] += distSub; }
- else { newMid[2] -= distSub; }
- insertNewOctree((octreeNode **)&(newNode->nextBranch[newBranch]),
- newMid,insertRGB,depth+1,doReduce);
- }
- }
-
-
- void generateEntry(octreeNode *node)
- {
- int branchInd;
- octreeNode *genTemp;
-
-
- if (node->isLeaf)
- {
- node->colorIndex = paletteIndex++;
- *genPal++ = (unsigned char)(node->RGB[0] / node->colorCount);
- *genPal++ = (unsigned char)(node->RGB[1] / node->colorCount);
- *genPal++ = (unsigned char)(node->RGB[2] / node->colorCount);
- }
- else
- {
- for ( branchInd=0 ; branchInd<8 ; branchInd++ )
- {
- genTemp = (octreeNode *)node->nextBranch[branchInd];
- if (genTemp) generateEntry(genTemp);
- }
- }
- }
-
-
- int generatePalette(octreeNode *tree,unsigned char *palette)
- {
- paletteIndex = 0;
- genPal = palette;
- genPalBase = genPal;
- generateEntry(tree);
- return paletteIndex;
- }
-
-
- unsigned char quant(octreeNode *tree,unsigned char *color)
- {
- octreeNode *traverse,
- *newNode;
- int newDir,
- actLevel,
- minDist,
- deltaR,
- deltaG,
- deltaB,
- quantDelta,
- findInd,
- minPalEntry,
- distSub;
- unsigned char *actPalEntry,
- newMid[3];
-
-
- traverse = tree;
- actLevel = 0;
- while (!(traverse->isLeaf))
- {
- newDir = octreeBranch(traverse->midPoint,color);
- newNode = (octreeNode *)traverse->nextBranch[newDir];
- if (newNode)
- {
- traverse = newNode;
- }
- else
- {
- memcpy(newMid,traverse->midPoint,3);
- distSub = depthDist[actLevel];
- if (newDir & 0x0004) { newMid[0] += distSub; }
- else { newMid[0] -= distSub; }
- if (newDir & 0x0002) { newMid[1] += distSub; }
- else { newMid[1] -= distSub; }
- if (newDir & 0x0001) { newMid[2] += distSub; }
- else { newMid[2] -= distSub; }
- insertNewOctree((octreeNode
- **)&(traverse->nextBranch[newDir]),
- newMid,
- color,
- actLevel+1,
- 0);
- traverse = lastLeaf;
- traverse->isLeaf = 1;
- minPalEntry = 0;
- actPalEntry = genPalBase;
- minDist = MAXINT;
- for ( findInd=0 ; findInd<paletteIndex ; findInd++ )
- {
- deltaR = *(color);
- deltaR -= *actPalEntry++;
- deltaG = *(color+1);
- deltaG -= *actPalEntry++;
- deltaB = *(color+2);
- deltaB -= *actPalEntry++;
- quantDelta = (sqr(deltaR) + sqr(deltaG) +
- sqr(deltaB));
- if (quantDelta<minDist)
- {
- minDist = quantDelta;
- minPalEntry = findInd;
- }
- }
- traverse->colorIndex = minPalEntry;
- }
- actLevel++;
- }
- return traverse->colorIndex;
- }
-
-
- unsigned char *quantRgb(octreeNode *tree,unsigned char *color)
- {
- octreeNode *traverse,
- *newNode;
- int newDir,
- actLevel,
- minDist,
- deltaR,
- deltaG,
- deltaB,
- quantDelta,
- findInd,
- minPalEntry,
- distSub;
- unsigned char *actPalEntry,
- newMid[3];
-
-
- traverse = tree;
- actLevel = 0;
- while (!(traverse->isLeaf))
- {
- newDir = octreeBranch(traverse->midPoint,color);
- newNode = (octreeNode *)traverse->nextBranch[newDir];
- if (newNode)
- {
- traverse = newNode;
- }
- else
- {
- memcpy(newMid,traverse->midPoint,3);
- distSub = depthDist[actLevel];
- if (newDir & 0x0004) { newMid[0] += distSub; }
- else { newMid[0] -= distSub; }
- if (newDir & 0x0002) { newMid[1] += distSub; }
- else { newMid[1] -= distSub; }
- if (newDir & 0x0001) { newMid[2] += distSub; }
- else { newMid[2] -= distSub; }
- insertNewOctree((octreeNode
- **)&(traverse->nextBranch[newDir]),
- newMid,
- color,
- actLevel+1,
- 0);
- traverse = lastLeaf;
- traverse->isLeaf = 1;
- minPalEntry = 0;
- actPalEntry = genPalBase;
- minDist = MAXINT;
- for ( findInd=0 ; findInd<paletteIndex ; findInd++ )
- {
- deltaR = *(color);
- deltaR -= *actPalEntry++;
- deltaG = *(color+1);
- deltaG -= *actPalEntry++;
- deltaB = *(color+2);
- deltaB -= *actPalEntry++;
- quantDelta = (sqr(deltaR) + sqr(deltaG) +
- sqr(deltaB));
- if (quantDelta<minDist)
- {
- minDist = quantDelta;
- minPalEntry = findInd;
- }
- }
- traverse->colorIndex = minPalEntry;
- }
- actLevel++;
- }
- return traverse->midPoint;
- }
-
-
- int octreeBranch(unsigned char *mid,unsigned char *direction)
- {
- int actBranch;
-
- actBranch = 0;
- if (*(mid) <= *(direction) ) actBranch |= 0x0004;
- if (*(mid+1) <= *(direction+1)) actBranch |= 0x0002;
- if (*(mid+2) <= *(direction+2)) actBranch |= 0x0001;
- return actBranch;
-
- }
-
- /*----------------------------------------------------------*/
-
- Ok, let's look a litle bit closer into the code. There's only one
- Next-dependent part in the code: #import "List.h". But the name
- corresponds to the meaning I think. There are seven functions for
- using the List ("methods" in Objective-C):
-
- alloc = allocate a new List object
- init = initialize it
- empty = empty the list without freeing the elements
- free = discard the list
- addObject= append a new element to the list (always at the
- end of the list; always of the ID, a special kind
- of a pointer)
- removeObjectAt= remove the element at position i
- objectAt = get the i-th element of the list
-
- Using the code is fairly simple:
-
-
-
- unsigned char centerPoint[3];
- unsigned char *source,
- *pixel,
- colorIndex,
- *palette;
- octreeNode *tree;
- int quantWidth,
- quantHeight,
- xLoop,
- yLoop,
-
- initOctree(colorCount,reduceMode);
- /* colorCount = maximal number of colors in the */
- /* resulting image */
- /* reduceMode = algorithm, used when reducing */
- /* the tree: 0 = reduce nodes with */
- /* the highest color */
- /* count */
- /* 1 = reduce nodes with */
- /* the lowest color */
- /* count */
-
- tree = NULL;
-
- centerPoint[0] = 0;
- centerPoint[1] = 0;
- centerPoint[2] = 0;
- /* Coordinates of the octree-root */
-
- pixel = source;
-
- for ( yLoop=0 ; yLoop<quantHeight ; yLoop++ )
- for ( xLoop=0 ; xLoop<quantWidth ; xLoop++ )
- {
- insertNewOctree(&tree,centerPoint,pixel,0,1);
- pixel += 3;
- }
-
- palette = (byte *)malloc(3*colorAnz);
- memset(palette,0,3*colorAnz);
- *entrysUsed = (generatePalette(tree,palette)+1);
-
- pixel = target;
- for ( yLoop=0 ; yLoop<quantHeight ; yLoop++ )
- {
- for ( xLoop=0 ; xLoop<quantWidth ; xLoop++ )
- {
- colorIndex = quant(tree,source);
- *pixel++ = colorIndex;
- source += 3;
- }
- }
-
- destroyNodes(&tree);
- destroyOctree();
-
-
- Pffffffft, that's all. Any question? No? Ok.
-
- Hope, it helps.
-
-
- Bye
-
-
- LIDL
-
-
-
-