Home || Visual Search || Applications || Architecture || Important Messages || OGL || Src

Color64.h

Go to the documentation of this file.
00001 #ifndef Impala_Core_Feature_Color64_h
00002 #define Impala_Core_Feature_Color64_h
00003 
00004 // extract color 64 bin features, provided by xirong.
00005 // color64: 44-d color correlgoram, 14-d color texture moment, and 6-d RGB color moment
00006 
00007 // Color correlgoram:
00008 // J. Huang, S. Kumar, M. Mitra, W. Zhu, and R. Zabih, “Image indexing using color correlograms,” in Proc. CVPR, 1997, pp. 762–768.
00009 // Color texture:
00010 // H. Yu, M. Li, H. Zhang, and J. Feng, “Color texture moment for contentbased image retrieval,” in Proc. ICIP, 2002, pp. 929–932.
00011 //
00012 // We have used this simple color64 feature a few times in the past, with a moderate success for large-scale web image retrieval, e.g.,
00013 // http://staff.science.uva.nl/~xirong/pub/li-tag-double.pdf.
00014 // For each image, we extract 44-d color correlgoram [44], 14-d color texture moment [45], and 6-d RGB color moment. 
00015 // We separately normalize the three features into unit length and concatenate them into a single vector. 
00016 // We use the Euclidean distance as a dissimilarity measurement.
00017 
00018 #include "Basis/String.h"
00019 #include "Core/Database/RawDataSet.h"
00020 #include "Core/Feature/FeatureDefinition.h"
00021 #include "Core/Vector/VectorTem.h"
00022 #include "Core/Feature/FeatureTable.h"
00023 #include "Core/Feature/Grid.h"
00024 
00025 #ifndef max
00026 #define max(a,b)            (((a) > (b)) ? (a) : (b))
00027 #endif
00028 
00029 #ifndef min
00030 #define min(a,b)            (((a) < (b)) ? (a) : (b))
00031 #endif
00032 
00033 namespace Impala
00034 {
00035 namespace Core
00036 {
00037 namespace Feature
00038 {
00039 
00040 
00041 class Color64
00042 {
00043 public:
00044 
00045     Color64(String featureName="color64")
00046     {
00047         mFeatureName = featureName;
00048 
00049         // set the feature mode based on mFeatureName
00050         // as: 0, 1, 2, 3, 4, 5, 6
00051         //
00052         // mode=0: color64
00053         // mode=1: 44-d color correlogram
00054         // mode=2: 14-d color texture moment
00055         // mode=3: 6-d RGB color moment
00056         // mode=4: color64 grid 2x2
00057         // mode=5: color64 grid 3x3
00058         // mode=6: color64 grid 4x4
00059         // mode=7: color64 grid 5x5
00060         // mode=8: color64 grid 6x6
00061         //
00062         SetFeatureMode();
00063 
00064         mFeature = new float [64];
00065 
00066         //mGrid = new Core::Feature::Grid(2,2);
00067 
00068     }
00069 
00070     ~Color64()
00071     {
00072         delete mFeature;
00073 
00074         //delete mGrid;
00075     }
00076 
00077     String
00078     GetName() const
00079     {
00080         return mFeatureName;
00081     }
00082 
00083     void
00084     SetGridDimension(int nRow, int nCol)
00085     {
00086         mRow = nRow;
00087         mCol = nCol;
00088     }
00089 
00090     int
00091     Extract(Core::Array::Array2dVec3UInt8* im, Core::Vector::VectorReal64& histogram, Core::VideoSet::VideoSet* vs = NULL)
00092     {
00093         switch (mode)
00094         {
00095          case 0: // "color64", 64dim = 44+14+6
00096 
00097              ExtractCorrelogramTextureMoment(im->CPB(), im->CW(), im->CH(), mFeature);
00098 
00099              for (int d=0; d<64; d++)
00100              {
00101                  histogram[d] = mFeature[d];
00102              }
00103              //VerifyFeatureSum(mFeature, 64, 3);
00104              VerifyFeatureRange(mFeature, 64);
00105 
00106          case 1: // 44-d color correlogram
00107 
00108              ExtractCorrelogramTextureMoment(im->CPB(), im->CW(), im->CH(), mFeature, 1);
00109 
00110              for (int d=0; d<44; d++)
00111              {
00112                  histogram[d] = mFeature[d];
00113              }
00114              //VerifyFeatureSum(mFeature, 44, 1);
00115              VerifyFeatureRange(mFeature, 44);
00116 
00117              break;
00118 
00119          case 2: // 14-d color texture moment
00120 
00121              ExtractCorrelogramTextureMoment(im->CPB(), im->CW(), im->CH(), mFeature, 2);
00122              for (int d=0; d<14; d++)
00123              {
00124                  histogram[d] = mFeature[d+44];
00125              }
00126              //VerifyFeatureSum(mFeature+44, 14, 1);
00127              VerifyFeatureRange(mFeature+44,14);
00128              break;
00129 
00130          case 3: // 6-d RGB color moment
00131             
00132              ExtractCorrelogramTextureMoment(im->CPB(), im->CW(), im->CH(), mFeature, 3);
00133              for (int d=0; d<6; d++)
00134              {
00135                  histogram[d] = mFeature[d+44+14];
00136              }
00137              //VerifyFeatureSum(mFeature+44+14, 6, 1);
00138              VerifyFeatureRange(mFeature+44+14, 6);
00139              break;
00140 
00141          case 4: // 256-d color64 grid 2x2
00142 
00143              ComputeGrid(im, histogram, 2, 2, vs);             
00144 
00145              break;
00146 
00147          case 5: // 576-d color64 grid 3x3
00148              
00149              ComputeGrid(im, histogram, 3, 3, vs);
00150 
00151              break;
00152 
00153          case 6: // 1024-d color64 grid 4x4
00154              
00155              ComputeGrid(im, histogram, 4, 4, vs);
00156 
00157              break;
00158 
00159          case 7: // 1600-d color64 grid 5x5
00160              
00161              ComputeGrid(im, histogram, 5, 5, vs);
00162 
00163              break;
00164 
00165          case 8: // 2304-d color64 grid 6x6
00166              
00167              ComputeGrid(im, histogram, 6, 6, vs);
00168 
00169              break;
00170 
00171          default: 
00172 
00173              std::cout << "Feature Extraction is skipped." << std::endl;
00174              break;
00175         }
00176 
00177         return 0;
00178     }
00179 
00180     void
00181     ComputeGrid(Core::Array::Array2dVec3UInt8* im, Core::Vector::VectorReal64& histogram, int nRow, int nCol, Core::VideoSet::VideoSet* vs = NULL)
00182     {
00183         SetGridDimension(nRow, nCol);
00184 
00185         Core::Feature::Grid* pGrid = new Core::Feature::Grid(mRow, mCol);
00186 
00187         BYTE** grid_buffers= new BYTE* [mRow*mCol];
00188         int nGridWidth = (im->CW()/mCol);
00189         int nGridHeight = (im->CH()/mRow);
00190         for (int k=0; k< mRow*mCol; k++)
00191         {
00192          grid_buffers[k] = new BYTE[nGridWidth*nGridHeight*3];
00193         }
00194 
00195         pGrid->RetrievalGridBuffers( (BYTE*)im->CPB(), grid_buffers, im->CW(), im->CH(), vs);
00196 
00197 
00198         for (int k=0; k< mRow*mCol; k++)
00199         {
00200          ExtractCorrelogramTextureMoment(grid_buffers[k], nGridWidth, nGridHeight, mFeature);
00201          for (int d=0; d<64; d++)
00202          {
00203              histogram[k*64+d] = mFeature[d];
00204          }
00205          //VerifyFeatureSum(mFeature, 64, 3);
00206          VerifyFeatureRange(mFeature, 64);
00207          
00208         }
00209 
00210         for (int k=0; k< mRow*mCol; k++)
00211         {
00212          delete [](grid_buffers[k]);
00213         }
00214         delete []grid_buffers;
00215         delete pGrid;
00216 
00217     }
00218 
00219     void
00220     SetFeatureMode()
00221     {
00222         if (mFeatureName == "color64")
00223         {
00224             mode=0;
00225             mBinCount = 64;
00226         }
00227         else if (mFeatureName == "correlogram44")
00228         {
00229             mode=1;
00230             mBinCount = 44;
00231         }
00232         else if (mFeatureName == "texturemoment14")
00233         {
00234             mode=2;
00235             mBinCount = 14;
00236         }
00237         else if (mFeatureName == "rgbmoment6")
00238         {
00239             mode=3;
00240             mBinCount = 6;
00241         }
00242         else if (mFeatureName == "color64grid2x2")
00243         {
00244             mode=4;
00245             mBinCount = 64*2*2;
00246         }
00247         else if (mFeatureName == "color64grid3x3")
00248         {
00249             mode=5;
00250             mBinCount = 64*3*3;
00251         }
00252         else if (mFeatureName == "color64grid4x4")
00253         {
00254             mode=6;
00255             mBinCount = 64*4*4;
00256         }
00257         else if (mFeatureName == "color64grid5x5")
00258         {
00259             mode=7;
00260             mBinCount = 64*5*5;
00261         }
00262         else if (mFeatureName == "color64grid6x6")
00263         {
00264             mode=8;
00265             mBinCount = 64*6*6;
00266         }
00267         else if (mFeatureName == "")
00268         {
00269             mode=-1;
00270             mBinCount = 0;
00271             std::cout << "FeatureName is empty." << std::endl;
00272         }
00273         else // "color64"
00274         {
00275             mode=-1;
00276             mBinCount = 0;
00277             std::cout << "FeatureName is un-defined: " << mFeatureName << std::endl;
00278         }
00279     }
00280 
00281     // Main Function for the "color64" feature extraction
00282     //
00283     // mode: see SetFeatureMode()
00284     int ExtractCorrelogramTextureMoment(BYTE *pImage, int nWidth, int nHeight,  void *pFeature, int mode=0)
00285     {
00286             if (pFeature == 0)
00287             {
00288                     return -1;
00289             }
00290         memset(pFeature, 0, sizeof(float) * 64);
00291 
00292         if (nWidth < 3 || nHeight < 3)
00293         {
00294             return -1;
00295         }
00296 
00297         float* pfCorrelogram44 = (float*)pFeature;
00298         float* pfTextureMoment = pfCorrelogram44 + 44;
00299         float* pfColorMoment = pfTextureMoment + 14;
00300 
00301         float* pfImage = new float[nWidth * nHeight];
00302         BYTE* pbImage = new BYTE[nWidth * nHeight];
00303 
00304         if (pbImage != NULL && pfImage != NULL)
00305         {
00306             for (int y = 0; y < nHeight; y ++)
00307             {
00308                 float* pf = pfImage + y * nWidth;
00309                 BYTE* pb = pbImage + y * nWidth;
00310 
00311                 for (int x = 0; x < nWidth; x ++)
00312                 {
00313                     // Note: the order of the buffer is RGBRGB...
00314                     BYTE r = pImage[(y*nWidth+x)*3+0];
00315                     BYTE g = pImage[(y*nWidth+x)*3+1];
00316                     BYTE b = pImage[(y*nWidth+x)*3+2];
00317 
00318                     pf[x] =(float) r + g + b;
00319                     pb[x] = QuantizeColorHSV44(r, g, b);
00320 
00321                     pfColorMoment[0] += r;
00322                     pfColorMoment[1] += g;
00323                     pfColorMoment[2] += b;
00324 
00325                     pfColorMoment[3] += r * r;
00326                     pfColorMoment[4] += g * g;
00327                     pfColorMoment[5] += b * b;
00328                 }
00329             }
00330 
00331             float fArea =(float) nWidth * nHeight;
00332 
00333             switch (mode)
00334             {
00335              case 1: // 44-d color correlogram
00336 
00337                 ExtractCorrelogram50(pbImage, nWidth, nHeight, pfCorrelogram44);
00338                 break;
00339 
00340              case 2: // 14-d color texture moment
00341 
00342                 ExtractTextureMoment(pfImage, nWidth, nHeight, pfTextureMoment);
00343                 break;
00344 
00345              case 3: // 6-d RGB color moment
00346                 
00347                 for (int i = 0; i < 3; i ++)
00348                 {
00349                     pfColorMoment[i] /= fArea;
00350                     pfColorMoment[i + 3] = pfColorMoment[i + 3] / fArea - pfColorMoment[i] * pfColorMoment[i];
00351                     pfColorMoment[i + 3] = (float)sqrt(fabs(pfColorMoment[i + 3]));
00352                 }
00353                 NormalizeVector(pfColorMoment, 6);
00354                 break;
00355 
00356              default: // color64 = 44+14+6
00357 
00358                 ExtractCorrelogram50(pbImage, nWidth, nHeight, pfCorrelogram44);
00359                 ExtractTextureMoment(pfImage, nWidth, nHeight, pfTextureMoment);
00360 
00361                 for (int i = 0; i < 3; i ++)
00362                 {
00363                     pfColorMoment[i] /= fArea;
00364                     pfColorMoment[i + 3] = pfColorMoment[i + 3] / fArea - pfColorMoment[i] * pfColorMoment[i];
00365                     pfColorMoment[i + 3] = (float)sqrt(fabs(pfColorMoment[i + 3]));
00366                 }
00367                 NormalizeVector(pfColorMoment, 6);
00368 
00369                 break;
00370             }
00371 
00372         }
00373         if (pbImage != NULL)
00374         {
00375             delete []pbImage;
00376         }
00377         if (pfImage != NULL)
00378         {
00379             delete []pfImage;
00380         }
00381 
00382             return 0;
00383     }
00384 
00385     void ExtractCorrelogram50(BYTE* pbImage, int nWidth, int nHeight, float* pfFeature)
00386     {
00387         float afHistogram[50], afCorrelogram[50];
00388         memset(afHistogram, 0, sizeof(afHistogram));
00389         memset(afCorrelogram, 0, sizeof(afCorrelogram));
00390 
00391         for (int y = 1; y < nHeight - 1; y ++)
00392         {
00393             BYTE* pbCurr = pbImage + y * nWidth;
00394             BYTE* pbPrev = pbCurr - nWidth;
00395             BYTE* pbNext = pbCurr + nWidth;
00396 
00397             for (int x = 1; x < nWidth - 1; x ++)
00398             {
00399                 // Note: nIndex = [0,43]
00400                 int nIndex = pbCurr[x];
00401                 int nCount = (pbPrev[x - 1] == nIndex) + (pbPrev[x] == nIndex) + (pbPrev[x + 1] == nIndex)
00402                            + (pbCurr[x - 1] == nIndex) + (pbCurr[x + 1] == nIndex)
00403                            + (pbNext[x - 1] == nIndex) + (pbNext[x] == nIndex) + (pbNext[x + 1] == nIndex);
00404                 afHistogram[nIndex] ++;
00405                 afCorrelogram[nIndex] += nCount;
00406             }
00407         }
00408 
00409         for (int i = 0; i < 50; i ++)
00410         {
00411             if (afHistogram[i] > 0)
00412             {
00413                 pfFeature[i] = afCorrelogram[i] / afHistogram[i];
00414             }
00415         }
00416         NormalizeVector(pfFeature, 50);
00417     }
00418 
00419     void ExtractTextureMoment(float* pfImage, int nWidth, int nHeight, float* pfFeature)
00420     {
00421         const float fW2 = (float)sqrt(2.0);
00422         for (int y = 1; y < nHeight - 1; y ++)
00423         {
00424             float* pfCurr = pfImage + y * nWidth;
00425             float* pfPrev = pfCurr - nWidth;
00426             float* pfNext = pfCurr + nWidth;
00427 
00428             for (int x = 1; x < nWidth - 1; x ++)
00429             {
00430                 float af[8];
00431 
00432                 af[0] = pfPrev[x - 1] + pfPrev[x] + pfPrev[x + 1]
00433                         + pfCurr[x - 1] + pfCurr[x + 1]
00434                         + pfNext[x - 1] + pfNext[x] + pfNext[x + 1];
00435 
00436                 af[1] = - pfPrev[x - 1] + pfPrev[x] - pfPrev[x + 1]
00437                         + pfCurr[x - 1] + pfCurr[x + 1]
00438                         - pfNext[x - 1] + pfNext[x] - pfNext[x + 1];
00439 
00440                 af[2] = - pfPrev[x - 1] + pfPrev[x + 1] - pfNext[x - 1] + pfNext[x + 1]
00441                         + fW2 * (- pfCurr[x - 1] + pfCurr[x + 1]);
00442 
00443                 af[3] = - pfPrev[x - 1] - pfPrev[x + 1] + pfNext[x - 1] + pfNext[x + 1]
00444                         + fW2 * (- pfPrev[x] + pfNext[x]);
00445 
00446                 af[4] = fW2 * (- pfPrev[x] + pfCurr[x - 1] + pfCurr[x + 1] - pfNext[x]);
00447 
00448                 af[5] = fW2 * (pfPrev[x - 1] - pfPrev[x + 1] - pfNext[x - 1] + pfNext[x + 1]);
00449 
00450                 af[6] = pfPrev[x - 1] - pfPrev[x + 1] + pfNext[x - 1] - pfNext[x + 1]
00451                         + fW2 * (- pfCurr[x - 1] + pfCurr[x + 1]);
00452 
00453                 af[7] = - pfPrev[x - 1] - pfPrev[x + 1] + pfNext[x - 1] + pfNext[x + 1]
00454                         + fW2 * (pfPrev[x] - pfNext[x]);
00455 
00456                 /*float fValueNorm = af[0];
00457                 if (fValueNorm < 1e-4f)
00458                 {
00459                     fValueNorm = 1e-4f;
00460                 }
00461                 fValueNorm = 1.0f / fValueNorm;
00462                 */
00463                 for (int i = 1; i < 8; i ++)
00464                 {
00465                     //af[i] *= fValueNorm;
00466                     pfFeature[i - 1] += fabs(af[i]);
00467                     pfFeature[i + 6] += af[i] * af[i];
00468                 }
00469             }
00470         }
00471 
00472         float fSizeNorm = 1.0f / ((nWidth - 2) * (nHeight - 2));
00473         for (int i = 0; i < 7; i ++)
00474         {
00475             pfFeature[i] *= fSizeNorm;
00476             pfFeature[i + 7] = (float)sqrt(fabs(pfFeature[i + 7] * fSizeNorm - pfFeature[i] * pfFeature[i]));
00477         }
00478         NormalizeVector(pfFeature, 14);
00479     }
00480 
00481     void NormalizeVector(float* pf, int nLen)
00482     {
00483         if (pf != NULL && nLen > 0)
00484         {
00485             float f = 0;
00486                     for (int i = 0; i < nLen; i ++)
00487             {
00488                 f += pf[i] * pf[i];
00489             }
00490             f = sqrt(f);
00491             if (f < 1e-4f)
00492             {
00493                 f = 1e-4f;
00494             }
00495             for (int i = 0; i < nLen; i ++)
00496             {
00497                 pf[i] /= (float)f;
00498             }
00499         }
00500     }
00501 
00502     int QuantizeColorHSV44(BYTE bRed, BYTE bGreen, BYTE bBlue)
00503     {
00504         static BYTE RGB2HSV[16][16][16];
00505         static bool bInit = false;
00506         if (!bInit)
00507         {
00508             for (int r = 0; r < 16; r ++)
00509             {
00510                 for (int g = 0; g < 16; g ++)
00511                 {
00512                     for (int b = 0; b < 16; b ++)
00513                     {
00514                         int nMax = max(r, max(g, b));
00515                         int nMin = min(r, min(g, b));
00516 
00517                         float h, s, v;
00518                         if (nMax == nMin)
00519                         {
00520                             h = 0;
00521                         }
00522                         else if (r == nMin)
00523                         {
00524                             h = 3.0f + (float)(b - g) / (nMax - nMin);
00525                         }
00526                         else if (g == nMin)
00527                         {
00528                             h = 5.0f + (float)(r - b) / (nMax - nMin);
00529                         }
00530                         else //if (b == nMin)
00531                         {
00532                             h = 1.0f + (float)(g - r) / (nMax - nMin);
00533                         }
00534                         if (nMax > 0)
00535                         {
00536                             s = (float)(nMax - nMin) / nMax;
00537                         }
00538                         else
00539                         {
00540                             s = 0;
00541                         }
00542                         v = (float)nMax / 15.0f;
00543 
00544                         int H = (int)(h * 60);
00545                         int S = (int)(s * 10);
00546                         int V = (int)(v * 10);
00547                         int nIndex;
00548 
00549                         if (V < 2)     // black area
00550                         {
00551                             nIndex = 0;
00552                         }
00553                         else if (S < 2) // Gray area
00554                         {
00555                             if (V < 8)
00556                             {
00557                                 nIndex = V - 1; // 1~6, gray area
00558                             }
00559                             else
00560                             {
00561                                 nIndex = 7 + (V >= 9);   // 7~8, white area
00562                             }
00563                         }
00564                         else // Color area
00565                         {
00566                             int nColor;
00567                             if (H < 22)
00568                             {
00569                                 nColor = 0; // red
00570                             }
00571                             else if (H < 45)
00572                             {
00573                                 nColor = 1; // orance
00574                             }
00575                             else if (H < 70)
00576                             {
00577                                 nColor = 2; // yellow
00578                             }
00579                             else if (H < 155)
00580                             {
00581                                 nColor = 3; // green
00582                             }
00583                             else if (H < 186)
00584                             {
00585                                 nColor = 4; // cyan
00586                             }
00587                             else if (H < 278)
00588                             {
00589                                 nColor = 5; // blue
00590                             }
00591                             else if (H < 330)
00592                             {
00593                                 nColor = 6; // purple
00594                             }
00595                             else
00596                             {
00597                                 nColor = 0; // red
00598                             }
00599                             
00600                             if (V >= 7) // SV plane is segmented into 5 regions
00601                             {
00602                                 nIndex = 2 + (S >= 5) + (S >= 8);
00603                             }
00604                             else
00605                             {
00606                                 nIndex = (S >= 7);
00607                             }
00608                             nIndex += 9 + 5 * nColor; // 9~43
00609                         }
00610                         RGB2HSV[r][g][b] = nIndex;
00611                     }
00612                 }
00613             }
00614             bInit = true;
00615         }
00616         return RGB2HSV[bRed / 16][bGreen / 16][bBlue / 16];
00617     }
00618 
00619     bool VerifyFeatureSum(float* pFeature, int nDim, float ref=1.0)
00620     {
00621         float sum = 0;
00622         for (int i=0; i<nDim; i++)
00623         {
00624             sum += pFeature[i];
00625         }
00626 
00627         float diff = sum-ref;
00628         if ( fabs(diff) < 1e-10 )
00629             return true;
00630         else
00631         {
00632             std::cout << "ERROR: Feature Sum Verifiction failed." << std::endl;
00633             return false;
00634         }
00635         
00636     }
00637 
00638     bool VerifyFeatureRange(float* pFeature, int nDim, float min=0, float max=1.0)
00639     {
00640         float sum = 0;
00641         for (int i=0; i<nDim; i++)
00642         {
00643             float value = pFeature[i];
00644             if (value< min || value > max )
00645             {
00646                 std::cout << "ERROR: Feature Range Verifiction failed: " << value << std::endl;
00647                 return false;
00648             }
00649         }
00650 
00651         return true;
00652     }
00653 
00654 private:
00655 
00656     Database::RawDataSet* mDataSet;
00657     String                mFeatureName;
00658     int                   mode;
00659     float*                mFeature;
00660 
00661     int                   mBinCount;
00662     //Core::Feature::Grid*  mGrid;
00663     int                   mRow;
00664     int                   mCol;
00665 
00666 };//class
00667 
00668 } // namespace Feature
00669 } // namespace Core
00670 } // namespace Impala
00671 
00672 #endif

Generated on Thu Jan 13 09:04:24 2011 for ImpalaSrc by  doxygen 1.5.1