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

KoenFIST2.h

Go to the documentation of this file.
00001 #ifndef Impala_Core_Feature_KoenFIST_h
00002 #define Impala_Core_Feature_KoenFIST_h
00003 
00004 #include "Core/Array/RecGauss.h"
00005 #include "Core/Array/Sqrt.h"
00006 #include "Core/Matrix/Mat.h"
00007 #include "Core/Matrix/MatFunc.h"
00008 #include "Core/Matrix/MatConcatenate.h"
00009 #include "Core/Feature/PointDescriptorTable.h"
00010 #include "Core/Feature/GetColorChannels.h"
00011 #include "Core/Array/Pow.h"
00012 #include "Core/Array/sRGB2RGB.h"
00013 #ifdef CUDA
00014 #include "Link/Cuda/KoenSIFT.h"
00015 #endif
00016 
00017 namespace Impala
00018 {
00019 namespace Core
00020 {
00021 namespace Feature
00022 {
00023 
00024 
00025 #ifdef PI
00026 #undef PI
00027 #endif
00028 #define PI 3.141592654
00029 
00030 
00031 /******************* original FIST *********************/
00032 
00033 template <int INDEX_SIZE, int ORI_SIZE>
00034 class FISTDescriptor
00035 {
00036 public:
00037     static const int VecLength = (INDEX_SIZE * INDEX_SIZE * ORI_SIZE);
00038     bool noGaussianWeighting;
00039     Real64 mOutputMultiplier;
00040     int mCustomNorm;
00041     bool mNoMaxBins;
00042     
00043     FISTDescriptor(bool noGaussianWeighting=false)
00044         : noGaussianWeighting(noGaussianWeighting), mOutputMultiplier(512), mCustomNorm(-1), mNoMaxBins(false)
00045     {
00046     }
00047     
00048     
00049     inline Real64
00050     CanonicalizeAngle(Real64 angle)
00051     {
00052         // make sure an angle lies between 0 and 2PI
00053         while (angle > 2*(Real64)PI)
00054         {
00055             angle -= 2*(Real64)PI;
00056         }
00057         while (angle < 0.0)
00058         {
00059             angle += 2*(Real64)PI;
00060         }
00061         return angle;
00062     }
00063     
00064     
00065     void
00066     PutInVector(Real64* descriptor,
00067                       Real64 mag, Real64 ori, Real64 rx, Real64 cx)
00068     {
00069         int ri, ci, oi;
00070         Real64 oval, rfrac, cfrac, ofrac;
00071        
00072         ori = CanonicalizeAngle(ori);
00073         oval = ORI_SIZE * ori / (2*(Real64)PI);
00074        
00075         ri = (int)floor(rx);
00076         ci = (int)floor(cx);
00077         oi = (int)floor(oval);
00078     
00079         // fractional parts of bin placement
00080         rfrac = rx - ri;
00081         cfrac = cx - ci;
00082         ofrac = oval - oi;
00083         if(!(rfrac >= 0.0 && rfrac <= 1.0 && cfrac >= 0.0 && cfrac <= 1.0 && ofrac >= 0.0 && ofrac <= 1.0))
00084         {
00085             std::cerr << "[Failure] Fractions outside valid range " << rfrac << " " << cfrac << " " << ofrac << std::endl;
00086             return;
00087         }
00088         if(oi >= ORI_SIZE) oi = 0;
00089     
00090         int rindex = ri;
00091         Real64 v;
00092         if((rindex >= 0) && (rindex < INDEX_SIZE))
00093         {
00094             int cindex = ci;
00095             if((cindex >= 0) && (cindex < INDEX_SIZE))
00096             {
00097                 // case 0
00098                 int oindex = oi;
00099                 v = mag * (1.0 - rfrac) * (1.0 - cfrac) * (1.0 - ofrac);
00100                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00101              
00102                 // case 1
00103                 oindex = oi + 1;
00104                 if(oindex >= ORI_SIZE) oindex = 0;
00105                 v = mag * (1.0 - rfrac) * (1.0 - cfrac) * ofrac;
00106                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00107             }
00108              
00109             cindex = ci + 1;
00110             if((cindex >= 0) && (cindex < INDEX_SIZE))
00111             {
00112                 // case 2
00113                 int oindex = oi;
00114                 v = mag * (1.0 - rfrac) * cfrac * (1.0 - ofrac);
00115                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00116              
00117                 // case 3
00118                 oindex = oi + 1;
00119                 if(oindex >= ORI_SIZE) oindex = 0;
00120                 v = mag * (1.0 - rfrac) * cfrac * ofrac;
00121                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00122             }
00123         }
00124         
00125         rindex = ri + 1;
00126         if((rindex >= 0) && (rindex < INDEX_SIZE))
00127         {
00128             int cindex = ci;
00129             if((cindex >= 0) && (cindex < INDEX_SIZE))
00130             {
00131                 // case 4
00132                 int oindex = oi;
00133                 v = mag * rfrac * (1.0 - cfrac) * (1.0 - ofrac);
00134                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00135              
00136                 // case 5
00137                 oindex = oi + 1;
00138                 if(oindex >= ORI_SIZE) oindex = 0;
00139                 v = mag * rfrac * (1.0 - cfrac) * ofrac;
00140                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00141             }
00142              
00143             cindex = ci + 1;
00144             if((cindex >= 0) && (cindex < INDEX_SIZE))
00145             {
00146                 // case 6
00147                 int oindex = oi;
00148                 v = mag * rfrac * cfrac * (1.0 - ofrac);
00149                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00150              
00151                 // case 7
00152                 oindex = oi + 1;
00153                 if(oindex >= ORI_SIZE) oindex = 0;
00154                 v = mag * rfrac * cfrac * ofrac;
00155                 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00156             }
00157         }
00158     }
00159     
00160     
00161     void
00162     InternalCount(Real64* descriptor, Array::Array2dScalarReal64* magnitude,
00163                   Array::Array2dScalarReal64* direction, 
00164                   int r, int c, Real64 rpos, Real64 cpos,
00165                   Real64 rx, Real64 cx, Real64 pointOrientation)
00166     {
00167         // bounds check
00168         if (r < 0  ||  r >= magnitude->CH()  ||  c < 0  ||  c >= magnitude->CW())
00169            return;
00170         
00171         Real64 mag = 0.0;
00172         if(noGaussianWeighting)
00173         {
00174             mag = magnitude->Val(c, r);
00175         }
00176         else
00177         {
00178             // find Gaussian weight, depending on distance from center
00179             const Real64 IndexSigma = 1.0;
00180             Real64 sigma = IndexSigma * 0.5 * INDEX_SIZE;
00181             Real64 weight = exp(- (rpos * rpos + cpos * cpos) / (2.0 * sigma * sigma));
00182             mag = weight * magnitude->Val(c, r);
00183         }
00184     
00185         /* Subtract keypoint orientation to give ori relative to keypoint. */
00186         Real64 ori = direction->Val(c, r) - pointOrientation;
00187         ori = CanonicalizeAngle(ori);
00188         PutInVector(descriptor, mag, ori, rx, cx);
00189     }
00190     
00191     
00192     /* Add features to vec obtained from sampling the grad and ori images
00193        for a particular scale.  Location of key is (scale,row,col) with respect
00194        to images at this scale.  We examine each pixel within a circular
00195        region containing the keypoint, and distribute the gradient for that
00196        pixel into the appropriate bins of the index array.
00197     */
00198     void
00199     InternalSample(Real64* descriptor, Array::Array2dScalarReal64* magnitude,
00200                    Array::Array2dScalarReal64* direction, 
00201                    Real64 scale, Real64 row, Real64 col, Real64 pointOrientation)
00202     {
00203        Real64 rpos, cpos, rx, cx;
00204     
00205        // integer version of the patch center position (in image coordinates)
00206        int irow = (int) (row + 0.5);
00207        int icol = (int) (col + 0.5);
00208        Real64 sinus = sin(pointOrientation);
00209        Real64 cosinus = cos(pointOrientation);
00210               
00211        /* The spacing of index samples in terms of pixels at this scale. */
00212        const int magnificationFactor = 3;
00213        Real64 spacing = scale * magnificationFactor;
00214     
00215        /* Radius of index sample region must extend to diagonal corner of
00216           index patch plus half sample for interpolation.
00217        */
00218        Real64 radius = 1.414 * spacing * INDEX_SIZE / 2.0;
00219        int iradius = (int) (radius + 0.5);
00220        
00221        /* Examine all points from the gradient image that could lie within the
00222           index square.
00223        */
00224        for (int i = -iradius; i <= iradius; i++)
00225          for (int j = -iradius; j <= iradius; j++) {
00226              
00227            /* Rotate sample offset to make it relative to key orientation.
00228               Uses (row,col) instead of (x,y) coords.  Also, make subpixel
00229               correction as later image offset must be an integer.  Divide
00230               by spacing to put in index units.
00231            */
00232            rpos = ((cosinus * i +   sinus * j) - (row - irow)) / spacing;
00233            cpos = ((- sinus * i + cosinus * j) - (col - icol)) / spacing;
00234 
00235            // these above are relative to the center of the patch, and also
00236            // normalized to a "unit distance" (e.g. we can just put in a 
00237            // gaussian now)
00238            // simplified (compensates for deviation from integer positions):
00239            // rpos = (i - (row - irow)) / spacing;
00240            // cpos = (j - (col - icol)) / spacing;
00241              
00242            /* Compute location of sample in terms of real-valued index array
00243               coordinates.  Subtract 0.5 so that rx of 1.0 means to put full
00244               weight on index[1] (e.g., when rpos is 0 and INDEX_SIZE is 3.
00245            */
00246            rx = rpos + INDEX_SIZE / 2.0 - 0.5;
00247            cx = cpos + INDEX_SIZE / 2.0 - 0.5;
00248     
00249            /* Test whether this sample falls within boundary of index patch. */
00250            if((rx > -1.0) && (rx < (Real64) INDEX_SIZE) &&
00251               (cx > -1.0) && (cx < (Real64) INDEX_SIZE))
00252              InternalCount(descriptor, magnitude, direction, irow + i, icol + j, rpos, cpos,
00253                            rx, cx, pointOrientation);
00254          }
00255     }
00256 
00257 
00258     void
00259     ComputeGradient(Array::Array2dScalarReal64*& magnitude, 
00260                     Array::Array2dScalarReal64*& direction, 
00261                     Array::Array2dScalarReal64* im,
00262                     Real64 sigma)
00263     {
00264         using namespace Impala::Core::Array;
00265 
00266         Real64 precision = 3.0;
00267     
00268         Array2dScalarReal64* Lx = 0;
00269         Array2dScalarReal64* Ly = 0;
00270         RecGauss(Lx, im, sigma, sigma, 1, 0, precision);
00271         RecGauss(Ly, im, sigma, sigma, 0, 1, precision);
00272     
00273         // compute gradient magnitude
00274         Array2dScalarReal64* Lx2 = 0;
00275         Array2dScalarReal64* Ly2 = 0;
00276         Mul(Lx2, Lx, Lx);
00277         Mul(Ly2, Ly, Ly);
00278         Add(magnitude, Lx2, Ly2);
00279         Sqrt(magnitude, magnitude);
00280         delete Lx2;
00281         delete Ly2;
00282         
00283         // compute gradient direction
00284         Atan2(direction, Ly, Lx);
00285         delete Lx;
00286         delete Ly;
00287     }
00288 
00289 
00290     void 
00291     SamplePoint(Real64* descriptor, Array::Array2dScalarReal64* magnitude,
00292                 Array::Array2dScalarReal64* direction, 
00293                 Real64 scale, Real64 y, Real64 x, Real64 pointOrientation)
00294     {
00295         for(int j = 0; j < VecLength; j++)
00296         {
00297             descriptor[j] = 0.0;
00298         }
00299 
00300         InternalSample(descriptor, magnitude, direction, scale, y, x, pointOrientation);
00301         
00302         if(mCustomNorm < 0)
00303         {
00304             // Normalize length of vec
00305             Real64 sqlen = 0.0;
00306             for (int j = 0; j < VecLength; j++)
00307                 sqlen += (descriptor[j] * descriptor[j]);
00308     
00309             Real64 factor = 1.0 / sqrt(sqlen);
00310             if(sqlen == 0.0) factor = 1.0;
00311             //std::cout << x << " " << sqlen << " " << factor << std::endl;
00312     
00313             const Real64 maxBinValue = 0.2f;
00314             for (int j = 0; j < VecLength; j++)
00315             {
00316                 descriptor[j] = descriptor[j] * factor;
00317                 if(descriptor[j] > maxBinValue)
00318                 {
00319                     descriptor[j] = maxBinValue;
00320                 }
00321             }
00322     
00323             // Normalize length of vec
00324             sqlen = 0.0;
00325             for (int j = 0; j < VecLength; j++)
00326                 sqlen += (descriptor[j] * descriptor[j]);
00327     
00328             factor = 1.0 / sqrt(sqlen);
00329             if(sqlen == 0.0) factor = 1.0;
00330             //std::cout << x << " " << factor << std::endl;
00331             
00332             for (int j = 0; j < VecLength; j++)
00333                 descriptor[j] = static_cast<int>(descriptor[j] * factor * mOutputMultiplier);
00334         }
00335         else if(mCustomNorm == 0)
00336         {
00337             for (int j = 0; j < VecLength; j++)
00338                 descriptor[j] = descriptor[j] * mOutputMultiplier;
00339         }
00340         else
00341         {
00342             // Normalize length of vec
00343             Real64 sqlen = 0.0;
00344             for (int j = 0; j < VecLength; j++)
00345                 sqlen += pow(descriptor[j], mCustomNorm);
00346     
00347             Real64 factor = 1.0 / pow(sqlen, 1.0 / mCustomNorm);
00348             if(sqlen == 0.0) factor = 1.0;
00349 
00350             if(!mNoMaxBins)
00351             {
00352                 const Real64 maxBinValue = 0.2f;
00353                 for (int j = 0; j < VecLength; j++)
00354                 {
00355                     descriptor[j] = descriptor[j] * factor;
00356                     if(descriptor[j] > maxBinValue)
00357                     {
00358                         descriptor[j] = maxBinValue;
00359                     }
00360                 }
00361         
00362                 // Normalize length of vec
00363                 sqlen = 0.0;
00364                 for (int j = 0; j < VecLength; j++)
00365                     sqlen += pow(descriptor[j], mCustomNorm);
00366         
00367                 factor = 1.0 / pow(sqlen, 1.0 / mCustomNorm);
00368                 if(sqlen == 0.0) factor = 1.0;
00369             }
00370     
00371             for (int j = 0; j < VecLength; j++)
00372                 descriptor[j] = descriptor[j] * factor * mOutputMultiplier;
00373         }
00374     }
00375 
00376 
00377     Matrix::Mat*
00378     DoCalculateFISTDescriptors(Array::Array2dScalarReal64* im, 
00379                                PointDescriptorTable* pointData)
00380     {
00381         using namespace Impala::Core::Array;
00382 
00383         /* Scales used by Lowe */
00384         const int clampScaleCount = 18;
00385         Real64 clampScales[clampScaleCount+1] = { 1.00794,
00386                                                   1.26992,
00387                                                   1.6,
00388                                                   2.01587,
00389                                                   2.53984,
00390                                                   3.2,
00391                                                   4.03175,
00392                                                   5.07968,
00393                                                   6.4,
00394                                                   8.06349,
00395                                                   10.1594,
00396                                                   12.8,
00397                                                   16.127,
00398                                                   20.3187,
00399                                                   25.6,
00400                                                   32.254,
00401                                                   40.6375,
00402                                                   51.2,
00403                                                   -1};
00404     
00405         bool warned = false;
00406         Matrix::Mat* allDescriptors = new Array2dScalarReal64(
00407                                            VecLength, pointData->Size(), 0, 0);
00408         for(int i = 0; i < clampScaleCount; i++) {
00409             Array2dScalarReal64* magnitude = 0; 
00410             Array2dScalarReal64* direction = 0;
00411             bool haveSome = false;
00412             for(int iter = 0; iter < pointData->Size(); iter++)
00413             {
00414                 Real64 scale = pointData->GetScale(iter);
00415                 if((clampScales[i] > scale) && (i != 0)) continue;
00416                 if(clampScales[i+1] <= scale) continue;
00417                 haveSome = true;
00418                 break;
00419             }
00420             if(haveSome)
00421             {
00422                 ComputeGradient(magnitude, direction, im, clampScales[i]);
00423                 MulVal(direction, direction, -1);
00424 
00425                 #pragma omp parallel for private(iter) shared(pointData, allDescriptors, magnitude, direction)
00426                 for(int iter = 0; iter < pointData->Size(); iter++)
00427                 {
00428                     Real64 scale = pointData->GetScale(iter);
00429                 
00430                     // check if this is the scale to compute the descriptor at
00431                     if((clampScales[i] > scale) && (i != 0)) continue;
00432                     if(clampScales[i+1] <= scale) continue;
00433 
00434                     Real64 x = pointData->GetX(iter);
00435                     Real64 y = pointData->GetY(iter);
00436                     Real64 pointOrientation = pointData->GetOrientation(iter);
00437     
00438                     SamplePoint(allDescriptors->CPB(0, iter), magnitude, direction, scale, y, x, pointOrientation);
00439                 }
00440             }
00441             /* clean up */
00442             if(magnitude) delete magnitude;
00443             if(direction) delete direction;
00444         }
00445         return allDescriptors;
00446     }
00447 
00448     
00449     Matrix::Mat*
00450     DoCalculateFISTDescriptorsAndEat(Array::Array2dScalarReal64* image, 
00451                                      PointDescriptorTable* pointData)
00452     {
00453         ILOG_VAR(Core.Feature.DoCalculateFISTDescriptorsAndEat);
00454         Matrix::Mat* result = 0;
00455         Timer timer;
00456 #ifdef CUDA
00457         if(Link::Cuda::CudaUsed())
00458         {
00459             result = Link::Cuda::DoCalculateSIFTDescriptors(image, pointData);
00460         }
00461         else
00462 #endif
00463         {
00464             result = DoCalculateFISTDescriptors(image, pointData);
00465         }
00466         ILOG_DEBUG("FIST channel in " << timer.SplitTimeStr());
00467         delete image;
00468         return result;
00469     }
00470 };
00471 
00472 
00473 bool
00474 StringIsFullyNumeric(String s)
00475 {
00476     for(int i = 0; i < s.size(); i++)
00477     {
00478         if((s[i] >= '0') && (s[i] <= '9'))
00479             continue;
00480         return false;
00481     }
00482     return true;
00483 }
00484 
00485 void
00486 CalculateFISTDescriptors(Array::Array2dVec3UInt8* inputNoBorder, 
00487                          PointDescriptorTable* pointData, 
00488                          String descriptor)
00489 {
00490     using namespace Impala::Core::Array;
00491 
00492     ILOG_VAR(Core.Feature.CalculateFISTDescriptors);
00493     FISTDescriptor<4, 8> defaultFIST(false);
00494     Array2dVec3Real64* input2 = 0;
00495     if (descriptor.find("fist") != String::npos)
00496     {
00497         ILOG_INFO("No max bins in norm");
00498         defaultFIST.mNoMaxBins = true;
00499         defaultFIST.mCustomNorm = 2;
00500     }
00501     if (descriptor.find("g0sift") != String::npos)
00502     {
00503         sRGB2RGB(input2, inputNoBorder);
00504     }
00505     else if (descriptor.find("g1sift") != String::npos)
00506     {
00507         sRGB2RGB(input2, inputNoBorder);
00508         Pow(input2, input2, 1.0 / 1.8);
00509     }
00510     else if (descriptor.find("g2sift") != String::npos)
00511     {
00512         sRGB2RGB(input2, inputNoBorder);
00513         Pow(input2, input2, 1.0 / 2.0);
00514     }
00515     else if (descriptor.find("g3sift") != String::npos)
00516     {
00517         sRGB2RGB(input2, inputNoBorder);
00518         MulVal(input2, input2, 1.0 / 255.0);
00519         Pow(input2, input2, 1.0 / 1.8);
00520         MulVal(input2, input2, 255.0);
00521     }
00522     else if (descriptor.find("g4sift") != String::npos)
00523     {
00524         sRGB2RGB(input2, inputNoBorder);
00525         MulVal(input2, input2, 1.0 / 255.0);
00526         Pow(input2, input2, 1.0 / 2.0);
00527         MulVal(input2, input2, 255.0);
00528     }
00529     else
00530     {
00531         MulVal(input2, inputNoBorder, 1.0);
00532     }
00533     descriptor = StringReplace(descriptor, "g0sift", "");
00534     descriptor = StringReplace(descriptor, "g1sift", "");
00535     descriptor = StringReplace(descriptor, "g2sift", "");
00536     descriptor = StringReplace(descriptor, "g3sift", "");
00537     descriptor = StringReplace(descriptor, "g4sift", "");
00538     descriptor = StringReplace(descriptor, "sift", "");
00539     descriptor = StringReplace(descriptor, "fist", "");
00540     if(descriptor.size() >= 2)
00541     {
00542         if((descriptor[descriptor.size() - 2] == 'n') && (StringIsFullyNumeric(descriptor.substr(descriptor.size() - 1, 1))))
00543         {
00544             defaultFIST.mCustomNorm = atoi(descriptor.substr(descriptor.size() - 1, 1));
00545             descriptor = descriptor.substr(0, descriptor.size() - 2);
00546         }
00547         if(descriptor.size() >= 3)
00548         {
00549             if((descriptor[descriptor.size() - 3] == 'n') && (StringIsFullyNumeric(descriptor.substr(descriptor.size() - 2, 2))))
00550             {
00551                 defaultFIST.mCustomNorm = atoi(descriptor.substr(descriptor.size() - 2, 2));
00552                 descriptor = descriptor.substr(0, descriptor.size() - 3);
00553             }
00554         }
00555         ILOG_DEBUG("Set custom norm: " << defaultFIST.mCustomNorm);
00556     }
00557 
00558     /* assumption: inputNoBorder has RGB input image */
00559     std::vector<Array2dScalarReal64*> channels =
00560         Core::Feature::GetColorChannels(input2, descriptor);
00561     delete input2;
00562     std::vector<Matrix::Mat*> channelDescriptors;
00563     for(int i=0 ; i<channels.size() ; i++)
00564     {
00565         channelDescriptors.push_back(defaultFIST.DoCalculateFISTDescriptorsAndEat(channels[i], pointData));
00566     }
00567     
00568     if(channelDescriptors.size() == 1)
00569     {
00570         // takes ownership of memory
00571         pointData->ReplaceAllDescriptors(channelDescriptors[0]);
00572     }
00573     else
00574     {
00575         // now turn them all into a big descriptor
00576         Matrix::Mat* descriptors =
00577             Matrix::MatConcatenateHorizontal(channelDescriptors);
00578         
00579         // takes ownership of memory
00580         pointData->ReplaceAllDescriptors(descriptors);
00581 
00582         for(int i=0 ; i<channelDescriptors.size() ; i++)
00583             delete channelDescriptors[i];
00584     }
00585 
00586 }
00587 
00588 } // namespace Feature
00589 } // namespace Core
00590 } // namespace Impala
00591 
00592 #endif

Generated on Fri Mar 19 09:31:07 2010 for ImpalaSrc by  doxygen 1.5.1