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
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
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
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
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
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
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
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
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
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
00147 int oindex = oi;
00148 v = mag * rfrac * cfrac * (1.0 - ofrac);
00149 descriptor[(rindex * INDEX_SIZE + cindex) * ORI_SIZE + oindex] += v;
00150
00151
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
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
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
00186 Real64 ori = direction->Val(c, r) - pointOrientation;
00187 ori = CanonicalizeAngle(ori);
00188 PutInVector(descriptor, mag, ori, rx, cx);
00189 }
00190
00191
00192
00193
00194
00195
00196
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
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
00212 const int magnificationFactor = 3;
00213 Real64 spacing = scale * magnificationFactor;
00214
00215
00216
00217
00218 Real64 radius = 1.414 * spacing * INDEX_SIZE / 2.0;
00219 int iradius = (int) (radius + 0.5);
00220
00221
00222
00223
00224 for (int i = -iradius; i <= iradius; i++)
00225 for (int j = -iradius; j <= iradius; j++) {
00226
00227
00228
00229
00230
00231
00232 rpos = ((cosinus * i + sinus * j) - (row - irow)) / spacing;
00233 cpos = ((- sinus * i + cosinus * j) - (col - icol)) / spacing;
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246 rx = rpos + INDEX_SIZE / 2.0 - 0.5;
00247 cx = cpos + INDEX_SIZE / 2.0 - 0.5;
00248
00249
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
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
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
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
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
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
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
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
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
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
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
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
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
00571 pointData->ReplaceAllDescriptors(channelDescriptors[0]);
00572 }
00573 else
00574 {
00575
00576 Matrix::Mat* descriptors =
00577 Matrix::MatConcatenateHorizontal(channelDescriptors);
00578
00579
00580 pointData->ReplaceAllDescriptors(descriptors);
00581
00582 for(int i=0 ; i<channelDescriptors.size() ; i++)
00583 delete channelDescriptors[i];
00584 }
00585
00586 }
00587
00588 }
00589 }
00590 }
00591
00592 #endif