Main Page | Alphabetical List | Compound List | File List | Compound Members | File Members

paloptimize.c

Go to the documentation of this file.
00001 
00002 /****************************************************************************************/
00003 /*  PalOptimize                                                                         */
00004 /*                                                                                      */
00005 /*  Author: Charles Bloom                                                               */
00006 /*  Description:  Palette Perfecting code                                               */
00007 /*                                                                                      */
00008 /*  The contents of this file are subject to the Genesis3D Public License               */
00009 /*  Version 1.01 (the "License"); you may not use this file except in                   */
00010 /*  compliance with the License. You may obtain a copy of the License at                */
00011 /*  http://www.genesis3d.com                                                            */
00012 /*                                                                                      */
00013 /*  Software distributed under the License is distributed on an "AS IS"                 */
00014 /*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See                */
00015 /*  the License for the specific language governing rights and limitations              */
00016 /*  under the License.                                                                  */
00017 /*                                                                                      */
00018 /*  The Original Code is Genesis3D, released March 25, 1999.                            */
00019 /*Genesis3D Version 1.1 released November 15, 1999                            */
00020 /*  Copyright (C) 1999 WildTangent, Inc. All Rights Reserved           */
00021 /*                                                                                      */
00022 /****************************************************************************************/
00023 
00024 /*********
00025 
00026 our colors are referred to as "uint8" triples, but are actually typically YUV
00027 
00028 --------
00029 
00030 The basics :
00031 
00032         find the average color to which each palette entry is mapped.
00033         set the palette entry to that color.
00034         repeat.
00035 
00036 The problems :
00037 
00038         1. this does NOT converge ; there's some wierd histerysis;
00039                 it also does NOT monotonically improve MSE !
00040                 we simply stop when MSE hits a local min.
00041 
00042                 <*> I think this is because our "ClosestPal" algorithm is imperfect
00043                         since it works within an oct-tree structure
00044 
00045         2. we can fall into "unstable local minimum" traps, like :
00046                 (this is a plot in color space)
00047 
00048                                 X
00049                            + +
00050                           X + X
00051 
00052                 here X indicates a blob of colors used, and + indicates a palette color;
00053                 this configuration is stable under our algorithm, though of
00054                 course the optimal is
00055 
00056                                 *
00057                 
00058                           *   *
00059 
00060                 (where * = X and +)
00061                 (note that these configurations cannot occur in 1d finite graphs; the
00062                 1d infinite graph for an unstable minimum is ...X+X+X+X+X+... )
00063 
00064                 this could be solved with like a Monte-Carlo walk,
00065                         adding some rare random component.
00066                 it doesn't work to just add a random wiggle whenever we stall out;
00067                         perhaps a random componenet in the direction of the deltas, like
00068 
00069                         diffR = Guassian_Rand( center = diffR , variance = abs(diffR) /2 );
00070 
00071                 tried it:
00072                         seems to help sometimes, but isn't a clear win
00073 
00074 **********/
00075 
00076 #include "yuv.h"
00077 #include "palettize.h"
00078 #include "utility.h"
00079 #include "tsc.h"
00080 #include "log.h"
00081 
00082 #include "bitmap.h"
00083 #include "pixelformat.h"
00084 
00085 /*******/
00086 
00087 typedef struct 
00088 {
00089         int totR,totG,totB,count;
00090 } palOptInfo;
00091 
00092 int stepTable[] = { 1009 , 757, 499, 401, 307, 239, 197, 157, 131, 103, 67, 41, 29, 17, 13, 7, 4, 1 };
00093 
00094 void paletteOptimize(const geBitmap_Info * BmInfo,const void * Bits,uint8 *palette,int palEntries,int maxSamples)
00095 {
00096 palInfo *palInfo;
00097 int pal,R,G,B,A;
00098 uint32 mse,last_mse;
00099 uint8 *palPtr;
00100 uint8 savePalette[768];
00101 int extraStepIndex,extraStepSize,extraStepSizeBytes=0,samples,totSamples;
00102 gePixelFormat_ColorGetter GetColor;
00103 const gePixelFormat_Operations * PixelOps;
00104 uint8 *ptr,*ptrEnd;
00105 int d;
00106 palOptInfo optInfo[256];
00107 
00108         assert(palEntries <= 256);
00109 
00110         pushTSC();
00111 
00112         // palette is 768 bytes
00113 
00114         R = palette[(palEntries-1)*3 + 0];
00115         G = palette[(palEntries-1)*3 + 1];
00116         B = palette[(palEntries-1)*3 + 2];
00117         for(pal=palEntries;pal<256;pal++)
00118         {
00119                 palette[pal*3 + 0] = R;
00120                 palette[pal*3 + 1] = G;
00121                 palette[pal*3 + 2] = B;
00122         }
00123 
00124         PixelOps = gePixelFormat_GetOperations(BmInfo->Format);
00125         GetColor = PixelOps->GetColor;
00126         ptrEnd = (uint8 *)Bits + BmInfo->Stride * BmInfo->Height * PixelOps->BytesPerPel;
00127 
00128         mse = ~(uint32)0;
00129         extraStepIndex = 0;
00130         extraStepSize = -1;
00131         totSamples = 0;
00132         if ( maxSamples <= 0 ) maxSamples = 0x0FFFFFFF;
00133         for(;;)
00134         {
00135                 if ( extraStepSize != 0 )
00136                 {
00137                         extraStepSize = ( stepTable[ extraStepIndex ] - 1 );
00138                         extraStepSizeBytes = extraStepSize * PixelOps->BytesPerPel;
00139                         extraStepIndex++;
00140                 }
00141 
00142                 last_mse = mse;
00143 
00144                 // <> this 'closestPal' is not great for this application
00145                 //              it's approximate & has a large initialization overhead
00146                 //       we should use the methods from the "Local K-Means" paper
00147 
00148                 palInfo = closestPalInit(palette);
00149                 if ( ! palInfo ) return;
00150 
00151                 memclear(optInfo,sizeof(palOptInfo)*palEntries);
00152 
00153                 mse = 0;
00154                 samples =0;
00155                 ptr = (uint8 *)Bits;
00156                 while( ptr < ptrEnd )
00157                 {
00158                         GetColor(&ptr,&R,&G,&B,&A);
00159 
00160                         pal = closestPal(R,G,B,palInfo);
00161 
00162                         if ( pal >= palEntries ) pal = palEntries-1;                    
00163                 
00164                         palPtr = palette + pal*3;
00165                         d = R - (*palPtr++);    mse += d*d;
00166                         d = G - (*palPtr++);    mse += d*d;
00167                         d = B - (*palPtr  );    mse += d*d;
00168 
00169                         optInfo[pal].totR += R;
00170                         optInfo[pal].totG += G;
00171                         optInfo[pal].totB += B;
00172                         optInfo[pal].count ++;
00173 
00174                         ptr += extraStepSizeBytes;
00175                         samples ++;
00176                 }
00177 
00178                 closestPalFree(palInfo);
00179 
00180                 if ( samples == 0 ) continue;
00181                 mse = (int)(((double)mse*256.0)/samples);
00182                 totSamples += samples;
00183 
00184                 if ( mse >= last_mse && extraStepSize == 0 )
00185                 {
00186                         memcpy(palette,savePalette,768);
00187                         mse = last_mse;
00188                         break;
00189                 }
00190                 else if ( mse > last_mse )
00191                 {
00192 #if 0
00193                         // seems to slow convergence (!?)
00194                         memcpy(palette,savePalette,768);
00195                         mse = last_mse;
00196 #endif
00197                 }
00198                 else
00199                 {
00200                         memcpy(savePalette,palette,768);
00201                 }
00202         
00203                 Log_Printf("mse*256 = %7d , extrastep = %4d, samples = %7d\n",mse,extraStepSize,samples);
00204 
00205                 if ( totSamples >= maxSamples )
00206                         break;
00207 
00208                 for(pal=0,palPtr = palette; pal<palEntries ; pal++,palPtr += 3)
00209                 {
00210                 double fd;
00211                 int diffR,diffG,diffB;
00212 
00213                         if ( optInfo[pal].count == 0 ) continue;
00214 
00215                         fd = 1.0 / optInfo[pal].count;
00216 
00217                         diffR = (int)(optInfo[pal].totR * fd) - palPtr[0];
00218                         diffG = (int)(optInfo[pal].totG * fd) - palPtr[1];
00219                         diffB = (int)(optInfo[pal].totB * fd) - palPtr[2];
00220 
00221 #if 1
00222                         #define DIV(diff)       if ( diff < 0 ) diff = - ((-diff + 1)>>1); else if ( diff > 0 ) diff = ((diff + 1)>>1)
00223                         DIV(diffR);
00224                         DIV(diffG);
00225                         DIV(diffB);
00226 #endif
00227 
00228 #if 0
00229                         // this helps sometimes, hurts sometimes
00230                         diffR = GaussianRand(diffR,abs(diffR));
00231                         diffG = GaussianRand(diffG,abs(diffG));
00232                         diffB = GaussianRand(diffB,abs(diffB));
00233 #endif
00234 
00235                         palPtr[0] = minmax( palPtr[0]+diffR , 0,255);
00236                         palPtr[1] = minmax( palPtr[1]+diffG , 0,255);
00237                         palPtr[2] = minmax( palPtr[2]+diffB , 0,255);
00238                 }
00239                 
00240                 if ( abs(mse - last_mse) < 50 && extraStepSize == 0 )
00241                 {
00242                         break;
00243                 }
00244         }
00245 
00246         showPopTSC("palOptimize");
00247 }

Generated on Tue Sep 30 12:36:04 2003 for GTestAndEngine by doxygen 1.3.2