MedianCutPalettizer.cpp
Go to the documentation of this file.00001 //*** MedianCutPalettizer.h *** 00002 00003 #include "MedianCutPalettizer.h" 00004 #include "ColorHelper.h" 00005 00006 00007 #pragma warning (disable:4244) 00008 #pragma warning (disable:4530) 00009 #include <list> 00010 00011 #include <limits> 00012 00013 #include <queue> 00014 #include <algorithm> 00015 00016 00017 const int NUM_DIMENSIONS = 3; 00018 00019 struct Point 00020 { 00021 unsigned char x[NUM_DIMENSIONS]; 00022 }; 00023 00024 class Block 00025 { 00026 Point minCorner, maxCorner; 00027 Point* points; 00028 int pointsLength; 00029 public: 00030 Block(Point* points, int pointsLength); 00031 Point * getPoints(); 00032 int numPoints() const; 00033 int longestSideIndex() const; 00034 int longestSideLength() const; 00035 bool operator<(const Block& rhs) const; 00036 void shrink(); 00037 private: 00038 template <typename T> 00039 static T min(const T a, const T b) 00040 { 00041 if (a < b) 00042 return a; 00043 else 00044 return b; 00045 } 00046 00047 template <typename T> 00048 static T max(const T a, const T b) 00049 { 00050 if (a > b) 00051 return a; 00052 else 00053 return b; 00054 } 00055 00056 }; 00057 00058 template <int index> 00059 class CoordinatePointComparator 00060 { 00061 public: 00062 bool operator()(Point left, Point right) 00063 { 00064 return left.x[index] < right.x[index]; 00065 } 00066 }; 00067 00068 00069 Block::Block(Point* points, int pointsLength) 00070 { 00071 this->points = points; 00072 this->pointsLength = pointsLength; 00073 for(int i=0; i < NUM_DIMENSIONS; i++) 00074 { 00075 minCorner.x[i] = std::numeric_limits<unsigned char>::min(); 00076 maxCorner.x[i] = std::numeric_limits<unsigned char>::max(); 00077 } 00078 } 00079 00080 Point * Block::getPoints() 00081 { 00082 return points; 00083 } 00084 00085 int Block::numPoints() const 00086 { 00087 return pointsLength; 00088 } 00089 00090 int Block::longestSideIndex() const 00091 { 00092 int m = maxCorner.x[0] - minCorner.x[0]; 00093 int maxIndex = 0; 00094 for(int i=1; i < NUM_DIMENSIONS; i++) 00095 { 00096 int diff = maxCorner.x[i] - minCorner.x[i]; 00097 if (diff > m) 00098 { 00099 m = diff; 00100 maxIndex = i; 00101 } 00102 } 00103 return maxIndex; 00104 } 00105 00106 int Block::longestSideLength() const 00107 { 00108 int i = longestSideIndex(); 00109 return maxCorner.x[i] - minCorner.x[i]; 00110 } 00111 00112 bool Block::operator<(const Block& rhs) const 00113 { 00114 return this->longestSideLength() < rhs.longestSideLength(); 00115 } 00116 00117 void Block::shrink() 00118 { 00119 int i,j; 00120 for(j=0; j<NUM_DIMENSIONS; j++) 00121 { 00122 minCorner.x[j] = maxCorner.x[j] = points[0].x[j]; 00123 } 00124 for(i=1; i < pointsLength; i++) 00125 { 00126 for(j=0; j<NUM_DIMENSIONS; j++) 00127 { 00128 minCorner.x[j] = min(minCorner.x[j], points[i].x[j]); 00129 maxCorner.x[j] = max(maxCorner.x[j], points[i].x[j]); 00130 } 00131 } 00132 } 00133 00134 std::list<Point> medianCut(Point* image, int numPoints, unsigned int desiredSize) 00135 { 00136 std::priority_queue<Block> blockQueue; 00137 00138 Block initialBlock(image, numPoints); 00139 initialBlock.shrink(); 00140 00141 blockQueue.push(initialBlock); 00142 while (blockQueue.size() < desiredSize && blockQueue.top().numPoints() > 1) 00143 { 00144 Block longestBlock = blockQueue.top(); 00145 00146 blockQueue.pop(); 00147 Point * begin = longestBlock.getPoints(); 00148 Point * median = longestBlock.getPoints() + (longestBlock.numPoints()+1)/2; 00149 Point * end = longestBlock.getPoints() + longestBlock.numPoints(); 00150 switch(longestBlock.longestSideIndex()) 00151 { 00152 case 0: std::nth_element(begin, median, end, CoordinatePointComparator<0>()); break; 00153 case 1: std::nth_element(begin, median, end, CoordinatePointComparator<1>()); break; 00154 case 2: std::nth_element(begin, median, end, CoordinatePointComparator<2>()); break; 00155 } 00156 00157 Block block1(begin, median-begin), block2(median, end-median); 00158 block1.shrink(); 00159 block2.shrink(); 00160 00161 blockQueue.push(block1); 00162 blockQueue.push(block2); 00163 } 00164 00165 std::list<Point> result; 00166 while(!blockQueue.empty()) 00167 { 00168 Block block = blockQueue.top(); 00169 blockQueue.pop(); 00170 Point * points = block.getPoints(); 00171 00172 int sum[NUM_DIMENSIONS] = {0}; 00173 for(int i=0; i < block.numPoints(); i++) 00174 { 00175 for(int j=0; j < NUM_DIMENSIONS; j++) 00176 { 00177 sum[j] += points[i].x[j]; 00178 } 00179 } 00180 00181 Point averagePoint; 00182 for(int j=0; j < NUM_DIMENSIONS; j++) 00183 { 00184 averagePoint.x[j] = sum[j] / block.numPoints(); 00185 } 00186 00187 result.push_back(averagePoint); 00188 } 00189 00190 return result; 00191 } 00192 00193 00194 00195 unsigned char FindNearestColor(unsigned int color, unsigned int* palette, int paletteCount) 00196 { 00197 int i, distanceSquared, minDistanceSquared, bestIndex = 0; 00198 minDistanceSquared = 255*255 + 255*255 + 255*255 + 1; 00199 for (i=0; i<paletteCount; i++) 00200 { 00201 unsigned char cR=((unsigned char)((color&0x00ff0000)>>16)); 00202 unsigned char cG=((unsigned char)((color&0x0000ff00)>>8 )); 00203 unsigned char cB=((unsigned char)((color&0x000000ff) )); 00204 unsigned char pR=((unsigned char)((palette[i]&0x00ff0000)>>16)); 00205 unsigned char pG=((unsigned char)((palette[i]&0x0000ff00)>>8 )); 00206 unsigned char pB=((unsigned char)((palette[i]&0x000000ff) )); 00207 int Rdiff = ((int)cR) - pR; 00208 int Gdiff = ((int)cG) - pG; 00209 int Bdiff = ((int)cB) - pB; 00210 distanceSquared = Rdiff*Rdiff + Gdiff*Gdiff + Bdiff*Bdiff; 00211 if (distanceSquared < minDistanceSquared) 00212 { 00213 minDistanceSquared = distanceSquared; 00214 bestIndex = i; 00215 } 00216 } 00217 return (unsigned char)bestIndex; 00218 } 00219 00220 00221 //*** GeneratePalette *** 00222 00223 int MedianCutPalettizer::GeneratePalette(unsigned int* imageData, int imageWidth, int imageHeight, unsigned int* palette, int paletteMaxCount) 00224 { 00225 int colorCount=imageWidth*imageHeight; 00226 Point* data=new Point[colorCount]; 00227 int dataSize=0; 00228 for (int p=0; p<colorCount; p++) 00229 { 00230 unsigned int color=*imageData; 00231 imageData++; 00232 unsigned char a=((unsigned char)((color&0xff000000)>>24)); 00233 if (a>0) 00234 { 00235 unsigned char r=((unsigned char)((color&0x00ff0000)>>16)); 00236 unsigned char g=((unsigned char)((color&0x0000ff00)>>8 )); 00237 unsigned char b=((unsigned char)((color&0x000000ff) )); 00238 data[dataSize].x[0]=r; 00239 data[dataSize].x[1]=g; 00240 data[dataSize].x[2]=b; 00241 dataSize++; 00242 } 00243 } 00244 std::list<Point> result=medianCut(data,dataSize,paletteMaxCount); 00245 delete[] data; 00246 00247 std::list<Point>::iterator it=result.begin(); 00248 int i=0; 00249 while (it!=result.end() && i<paletteMaxCount) 00250 { 00251 Point p=*it; 00252 unsigned int c=0xff000000; 00253 c|=p.x[0]<<16; 00254 c|=p.x[1]<<8; 00255 c|=p.x[2]; 00256 int found=false; 00257 for (int j=0; j<i; j++) 00258 { 00259 if (palette[j]==c) 00260 { 00261 found=true; 00262 break; 00263 } 00264 } 00265 if (!found) 00266 { 00267 palette[i]=c; 00268 i++; 00269 } 00270 it++; 00271 } 00272 00273 return i; 00274 } 00275 00276 00277 //*** GeneratePalette *** 00278 00279 int MedianCutPalettizer::GeneratePalette(unsigned int* imageData, int imageWidth, int imageHeight, unsigned short* palette, int paletteMaxCount) 00280 { 00281 int colorCount=imageWidth*imageHeight; 00282 Point* data=new Point[colorCount]; 00283 int dataSize=0; 00284 for (int p=0; p<colorCount; p++) 00285 { 00286 unsigned int color=*imageData; 00287 imageData++; 00288 unsigned char a=((unsigned char)((color&0xff000000)>>24)); 00289 if (a>0) 00290 { 00291 color=RGB16TO32(RGB32TO16(color)); 00292 unsigned char r=((unsigned char)((color&0x00ff0000)>>16)); 00293 unsigned char g=((unsigned char)((color&0x0000ff00)>>8 )); 00294 unsigned char b=((unsigned char)((color&0x000000ff) )); 00295 data[dataSize].x[0]=r; 00296 data[dataSize].x[1]=g; 00297 data[dataSize].x[2]=b; 00298 dataSize++; 00299 } 00300 } 00301 std::list<Point> result=medianCut(data,dataSize,paletteMaxCount); 00302 delete[] data; 00303 00304 std::list<Point>::iterator it=result.begin(); 00305 int i=0; 00306 while (it!=result.end() && i<paletteMaxCount) 00307 { 00308 Point p=*it; 00309 unsigned int c=0xff000000; 00310 c|=p.x[0]<<16; 00311 c|=p.x[1]<<8; 00312 c|=p.x[2]; 00313 unsigned short c16=RGB32TO16(c); 00314 int found=false; 00315 for (int j=0; j<i; j++) 00316 { 00317 if (palette[j]==c16) 00318 { 00319 found=true; 00320 break; 00321 } 00322 } 00323 if (!found) 00324 { 00325 palette[i]=c16; 00326 i++; 00327 } 00328 it++; 00329 } 00330 00331 return i; 00332 } 00333 00334 00335 //*** PalettizeImage *** 00336 00337 void MedianCutPalettizer::PalettizeImage(unsigned int* imageData, int imageWidth, int imageHeight, unsigned int* palette, int paletteCount, unsigned char* outputData) 00338 { 00339 for (int y=0; y<imageHeight; y++) 00340 { 00341 for (int x=0; x<imageWidth; x++) 00342 { 00343 unsigned char paletteIndex=FindNearestColor(*imageData,palette,paletteCount); 00344 *outputData=paletteIndex; 00345 imageData++; 00346 outputData++; 00347 } 00348 } 00349 }
Reproduction/republishing of any material on this site without permission is strictly prohibited.
