ScaleSpace.cpp
上传用户:yli86818
上传日期:2014-07-15
资源大小:273k
文件大小:30k
源码类别:

图形图像处理

开发平台:

Visual C++

  1. // ScaleSpace.cpp: implementation of the CScaleSpace class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "TestSIFT.h"
  6. #include "ScaleSpace.h"
  7. #ifdef _DEBUG
  8. #undef THIS_FILE
  9. static char THIS_FILE[]=__FILE__;
  10. #define new DEBUG_NEW
  11. #endif
  12. //////////////////////////////////////////////////////////////////////
  13. // Construction/Destruction
  14. //////////////////////////////////////////////////////////////////////
  15. // Variable s is from explanation in Lowe paper
  16. CScaleSpace::CScaleSpace(int s,double basePixScale)
  17. {
  18. m_scaleLevel = s;
  19. m_dogCount = m_scaleLevel+2;
  20. m_gaussCount = m_scaleLevel+3;
  21. m_gaussImgs = NULL;
  22. m_dogImgs = NULL;
  23. m_magnitudes = NULL;
  24. m_directions = NULL;
  25. m_basePixScale = basePixScale;
  26. }
  27. CScaleSpace::~CScaleSpace()
  28. {
  29. if (m_dogImgs != NULL)
  30. {
  31. for (int i = 0; i < m_dogCount; i++)
  32. cvReleaseImage (&m_dogImgs[i]);
  33. delete m_dogImgs;
  34. }
  35. if (m_gaussImgs != NULL)
  36. {
  37. for (int i = 0; i < m_gaussCount; i++)
  38. cvReleaseImage (&m_gaussImgs[i]);
  39. delete m_gaussImgs;
  40. }
  41. }
  42. void CScaleSpace::buildDoG(IplImage *img,double startsigma,double firstScale)
  43. {
  44. m_gaussImgs = new IplImage*[m_gaussCount];
  45. for (int i = 0; i < m_gaussCount; i++)
  46. m_gaussImgs[i] = cvCreateImage (cvGetSize(img),IPL_DEPTH_32F,1);
  47. if (img->depth == IPL_DEPTH_32F)
  48. cvCopy(img,m_gaussImgs[0]);
  49. else
  50. {
  51. for (int y = 0; y < cvGetSize(img).height; y++)
  52. for (int x = 0; x < cvGetSize(img).width; x++)
  53. cvSet2D (m_gaussImgs[0],y,x,cvGet2D(img,y,x));
  54. }
  55. m_dogImgs = new IplImage*[m_dogCount];
  56. for (i = 0; i < m_dogCount; i++)
  57. m_dogImgs[i] = cvCreateImage (cvGetSize(img),IPL_DEPTH_32F,1);
  58. /* This explaination is from Sebastian Nowozin's C# code
  59.  *
  60.  * Gaussian G(sigma), with relation
  61.  * G(sigma_1) * G(sigma_2) = G(sqrt(sigma_1^2 + * sigma_2^2))
  62.  *
  63.  * Then, we have:
  64.  *
  65.  * G(k^{p+1}) = G(k^p) * G(sigma),
  66.  * and our goal is to compute every iterations sigma value so this
  67.  * equation iteratively produces the next level.  Hence:
  68.  *
  69.  * sigma = sqrt{left(k^{p+1}right)^2 - left(k^pright)^2}
  70.  *   = sqrt{k^{2p+2} - k^{2p}}
  71.  *   = sqrt{k^2p (k^2 - 1)}
  72.  *   = k^p sqrt{k^2 - 1}
  73.  *
  74.  * In the code below, 'w' is the running k^p, where p increases by one
  75.  * each iteration.  kTerm is the constant sqrt{k^2 - 1} term.
  76.  */
  77.  // SToK (scales) = 2^(1/scales) == k
  78. double k = exp (1.0/m_scaleLevel*log(2));
  79. double kTerm = sqrt (k*k - 1.0);
  80. double w = startsigma;
  81. for (i = 1 ; i < m_gaussCount ; i++) {
  82. cvSmooth( m_gaussImgs[i-1], m_gaussImgs[i],CV_GAUSSIAN,0,0,w);
  83. cvAbsDiff (m_gaussImgs[i-1],m_gaussImgs[i],m_dogImgs[i-1]);
  84. w *= k;
  85. }
  86. }
  87. IplImage* CScaleSpace::getDoG(int i)
  88. {
  89. return m_dogImgs[i];
  90. }
  91. int CScaleSpace::getDoGCount()
  92. {
  93. return m_dogCount;
  94. }
  95. void CScaleSpace::findPeak(double r,double dogThresh,std::vector<CScalePoint>* vec)
  96. {
  97. // dogThresh is in range [0,1]
  98. dogThresh *= 255; 
  99. // {x,y,scale offset}
  100. int dir[27][3] = 
  101. {{-1,-1,0},{0,-1,0},{1,-1,0},{-1,0,0},{1,0,0},{-1,1,0},{0,1,0},{1,1,0},
  102. {-1,-1,-1},{0,-1,-1},{1,-1,-1},{-1,0,-1},{0,0,-1},{1,0,-1},{-1,1,-1},{0,1,-1},{1,1,-1},
  103. {-1,-1,1},{0,-1,1},{1,-1,1},{-1,0,1},{0,0,1},{1,0,1},{-1,1,1},{0,1,1},{1,1,1}};
  104. for (int i = 1; i < m_dogCount-1; i++)
  105. {
  106. for (int y = 1; y < cvGetSize (m_dogImgs[i]).height-1; y++)
  107. for (int x = 1; x < cvGetSize (m_dogImgs[i]).width-1; x++)
  108. {
  109. double val = cvGet2D (m_dogImgs[i],y,x).val[0];
  110. bool maxima = true;
  111. for (int k = 0; k < 27; k++)
  112. {
  113. if (val < dogThresh || val < cvGet2D (m_dogImgs[i+dir[k][2]],y+dir[k][1],x+dir[k][0]).val[0])
  114. {
  115. maxima = false;
  116. break;
  117. }
  118. }
  119. if (maxima)
  120. {
  121. // Eliminate edge response
  122. double dxx = cvGet2D(m_dogImgs[i],y,x+1).val[0] +
  123.  cvGet2D(m_dogImgs[i],y,x-1).val[0] -
  124.  2 * cvGet2D(m_dogImgs[i],y,x).val[0];
  125. double dyy = cvGet2D(m_dogImgs[i],y+1,x).val[0] +
  126.  cvGet2D(m_dogImgs[i],y-1,x).val[0] -
  127.  2 * cvGet2D(m_dogImgs[i],y,x).val[0];
  128. double dxy = 0.25 * (cvGet2D(m_dogImgs[i],y+1,x+1).val[0] -
  129.  cvGet2D(m_dogImgs[i],y-1,x+1).val[0] -
  130.  cvGet2D(m_dogImgs[i],y+1,x-1).val[0] +
  131.  cvGet2D(m_dogImgs[i],y-1,x-1).val[0]);
  132. double tr = dxx+dyy; // trace 
  133. double det = dxx*dyy - dxy*dxy; //det
  134. double rsq = r+1;
  135. rsq *= rsq;
  136. if (tr*tr/(det+1e-20) > (rsq)/r)
  137. maxima = false;
  138. }
  139. if (maxima)
  140. {
  141. CScalePoint p (x,y,i);
  142. vec->push_back(p);
  143. }
  144. //else
  145. // cvSet2D (dogImg[i],y,x,cvScalar(0));
  146. }
  147. }
  148. }
  149. void CScaleSpace::filterAndLocalizePeaks(std::vector<CScalePoint>& peaks,std::vector<CScalePoint>* filtered, 
  150.  double dValueLoThresh, double scaleAdjustThresh, int relocationMaximum)
  151. {
  152. dValueLoThresh *= 255;
  153. CvMat *processedMap = cvCreateMat (cvGetSize(m_dogImgs[0]).height,cvGetSize(m_dogImgs[0]).width,CV_8UC1);
  154. cvSetZero (processedMap);
  155. for (int i = 0; i < peaks.size(); i++) 
  156. {
  157. // Kuas : already reject edge like in findPeak
  158. //if (IsTooEdgelike (spaces[peak.Level], peak.X, peak.Y, edgeRatio))
  159. // continue;
  160. // When the localization hits some problem, i.e. while moving the
  161. // point a border is reached, then skip this point.
  162. if (LocalizeIsWeak (peaks[i], relocationMaximum, processedMap))
  163. continue;
  164. if (abs (peaks[i].m_fineS) > scaleAdjustThresh)
  165. continue;
  166. // Additional local pixel information is now available, threshhold
  167. // the D(^x)
  168. // Console.WriteLine ("{0} {1} {2} # == DVALUE", peak.Y, peak.X, peak.Local.DValue);
  169. if (abs (peaks[i].m_DValue) <= dValueLoThresh)
  170. continue;
  171. // its edgy enough, add it
  172. filtered->push_back (peaks[i]);
  173. }
  174. cvReleaseMat (&processedMap);
  175. //return (filtered);
  176. }
  177. bool CScaleSpace::LocalizeIsWeak(CScalePoint& peak, int steps, CvMat* processed)
  178. {
  179. bool needToAdjust = true;
  180. int adjusted = steps;
  181. while (needToAdjust) 
  182. {
  183. int x = peak.m_x;
  184. int y = peak.m_y;
  185. // Points we cannot say anything about, as they lie on the border
  186. // of the scale space
  187. // Kuas : seem to be not happen because in FindPeaks not get these peaks
  188. //if (point.Level <= 0 || point.Level >= (spaces.Length - 1))
  189. // return (true);
  190. IplImage* dog = m_dogImgs[peak.m_level];
  191. if (x <= 0 || x >= (cvGetSize(dog).width-1))
  192. return (true);
  193. if (y <= 0 || y >= (cvGetSize(dog).height-1))
  194. return (true);
  195. double dp;
  196. CvMat *adj = cvCreateMat (3,1,CV_32F);
  197. GetAdjustment (peak, peak.m_level, x, y, &dp, adj);
  198. // Get adjustments and check if we require further adjustments due
  199. // to pixel level moves. If so, turn the adjustments into real
  200. // changes and continue the loop. Do not adjust the plane, as we
  201. // are usually quite low on planes in thie space and could not do
  202. // further adjustments from the top/bottom planes.
  203. double adjS = cvGet2D (adj,0,0).val[0];//adj[0, 0];
  204. double adjY = cvGet2D (adj,1,0).val[0];//adj[1, 0];
  205. double adjX = cvGet2D (adj,2,0).val[0];//adj[2, 0];
  206. cvReleaseMat (&adj);
  207. if (abs (adjX) > 0.5 || abs (adjY) > 0.5) 
  208. {
  209. // Already adjusted the last time, give up
  210. if (adjusted == 0) {
  211. //Console.WriteLine ("too many adjustments, returning");
  212. return (true);
  213. }
  214. adjusted -= 1;
  215. // Check that just one pixel step is needed, otherwise discard
  216. // the point
  217. double distSq = adjX * adjX + adjY * adjY;
  218. if (distSq > 2.0)
  219. return (true);
  220. peak.m_x = (int) (peak.m_x + adjX + 0.5);
  221. peak.m_y = (int) (peak.m_y + adjY + 0.5);
  222. //point.Level = (int) (point.Level + adjS + 0.5);
  223. //TRACE ("moved point by (%f,%f: %f) to (%d,%d: %d)",
  224. // adjX, adjY, adjS, peak.m_x, peak.m_y, peak.m_level);
  225. continue;
  226. }
  227. // Check if we already have a keypoint within this octave for this
  228. // pixel position in order to avoid dupes. (Maybe we can move this
  229. // check earlier after any adjustment, so we catch dupes earlier).
  230. // If its not in there, mark it for later searches.
  231. //
  232. // FIXME: check why there does not seem to be a dupe at all
  233. if (cvGet2D (processed,peak.m_y,peak.m_x).val[0] != 0)
  234. return (true);
  235. cvSet2D (processed,peak.m_y,peak.m_x,cvScalar(1));
  236. // Save final sub-pixel adjustments.
  237. peak.m_fineS = adjS;
  238. peak.m_fineX = adjX;
  239. peak.m_fineY = adjY;
  240. peak.m_DValue = cvGet2D (dog,peak.m_y,peak.m_x).val[0] + 0.5 * dp;
  241. needToAdjust = false;
  242. }
  243. return false;
  244. }
  245. void CScaleSpace::GetAdjustment(CScalePoint& point, int level, int x, int y, double* dp,CvMat* adj)
  246. {
  247. *dp = 0.0;
  248. //if (point.Level <= 0 || point.Level >= (spaces.Length - 1))
  249. // throw (new ArgumentException ("point.Level is not within [bottom-1;top-1] range"));
  250. IplImage* below = m_dogImgs[level - 1];
  251. IplImage* current = m_dogImgs[level];
  252. IplImage* above = m_dogImgs[level + 1];
  253. CvMat* H = cvCreateMat( 3, 3, CV_32F);
  254. //H[0, 0] = below[x, y] - 2 * current[x, y] + above[x, y];
  255. cvSet2D (H,0,0,cvScalar (cvGet2D (below,y,x).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(above,y,x).val[0]) );
  256. cvSet2D (H,0,1,cvScalar(0.25 * (cvGet2D(above,y+1,x).val[0] - cvGet2D(above,y-1,x).val[0] - (cvGet2D(below,y+1,x).val[0] - cvGet2D(below,y-1,x).val[0]))));
  257. cvSet2D (H,1,0,cvGet2D (H,0,1));
  258. cvSet2D (H,0,2,cvScalar(0.25 * (cvGet2D(above,y,x+1).val[0] - cvGet2D(above,y,x-1).val[0] - (cvGet2D(below,y,x+1).val[0] - cvGet2D(below,y,x-1).val[0]))));
  259. cvSet2D (H,2,0,cvGet2D(H,0,2)); 
  260. cvSet2D (H,1,1,cvScalar(cvGet2D(current,y-1,x).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(current,y+1,x).val[0]));
  261. cvSet2D (H,1,2,cvScalar(0.25 * (cvGet2D(current,y+1,x+1).val[0] - cvGet2D(current,y+1,x-1).val[0] -(cvGet2D(current,y-1,x+1).val[0] - cvGet2D(current,y-1,x-1).val[0]))));
  262. cvSet2D (H,2,1,cvGet2D(H,1,2));
  263. cvSet2D (H,2,2,cvScalar(cvGet2D(current,y,x-1).val[0] - 2 * cvGet2D(current,y,x).val[0] + cvGet2D(current,y,x+1).val[0]));
  264. CvMat* d = cvCreateMat(3, 1, CV_32F);
  265. cvSet2D (d,0,0,cvScalar(0.5 * (cvGet2D(above,y,x).val[0] - cvGet2D(below,y,x).val[0])));
  266. cvSet2D (d,1,0,cvScalar(0.5 * (cvGet2D(current,y+1,x).val[0] - cvGet2D(current,y-1,x).val[0])));
  267. cvSet2D (d,2,0,cvScalar(0.5 * (cvGet2D(current,y,x+1).val[0] - cvGet2D(current,y,x-1).val[0])));
  268. CvMat* b = cvCloneMat(d);
  269. //b.Negate ();
  270. cvSet2D (b,0,0,cvScalar(-cvGet2D(b,0,0).val[0]));
  271. cvSet2D (b,1,0,cvScalar(-cvGet2D(b,1,0).val[0]));
  272. cvSet2D (b,2,0,cvScalar(-cvGet2D(b,2,0).val[0]));
  273. // Solve: A x = b
  274. //H.SolveLinear (b);
  275. // adj ::= x
  276. cvSolve (H,b,adj);
  277. *dp = cvDotProduct(adj,d);
  278. cvReleaseMat (&H);
  279. cvReleaseMat (&d);
  280. cvReleaseMat (&b);
  281. }
  282. #define sqr(x) (x*x)
  283. void CScaleSpace::GenMagnitudeAndDirectionMaps()
  284. {
  285. // We leave the first entry to null, and ommit the last. This way, the
  286. // magnitudes and directions maps have the same index as the
  287. // imgScaled maps they belong to.
  288. m_magnitudes = new IplImage*[m_dogCount - 1];
  289. m_directions = new IplImage*[m_dogCount - 1];
  290. // Build the maps, omitting the border pixels, as we cannot build
  291. // gradient information there.
  292. for (int s = 1 ; s < (m_dogCount - 1); s++) 
  293. {
  294. m_magnitudes[s] = cvCreateImage (cvGetSize(m_dogImgs[0]),IPL_DEPTH_32F,1);
  295. m_directions[s] = cvCreateImage (cvGetSize(m_dogImgs[0]),IPL_DEPTH_32F,1);
  296. for (int y = 1 ; y < cvGetSize(m_gaussImgs[s]).height - 1 ; y++) 
  297. {
  298. for (int x = 1 ; x < cvGetSize(m_gaussImgs[s]).width - 1 ; x++) 
  299. {
  300. // gradient magnitude m
  301. double dx = cvGet2D(m_gaussImgs[s],y,x+1).val[0] - 
  302. cvGet2D(m_gaussImgs[s],y,x-1).val[0];
  303. double dy = cvGet2D(m_gaussImgs[s],y+1,x).val[0] - 
  304. cvGet2D(m_gaussImgs[s],y-1,x).val[0];
  305. cvSet2D (m_magnitudes[s],y,x,cvScalar( sqrt (dx*dx + dy*dy) ) );
  306. // gradient direction theta
  307. cvSet2D (m_directions[s],y,x,cvScalar(atan2(dy,dx) ));
  308. }
  309. }
  310. }
  311. }
  312. void CScaleSpace::ClearMagnitudeAndDirectionMaps()
  313. {
  314. for (int i = 1; i < m_dogCount-1; i++)
  315. {
  316. cvReleaseImage (&m_magnitudes[i]);
  317. cvReleaseImage (&m_directions[i]);
  318. }
  319. delete m_magnitudes;
  320. delete m_directions;
  321. }
  322. void CScaleSpace::GenerateKeypoints (std::vector<CScalePoint>& localizedPeaks, std::vector<CKeyPoint*>* keypoints,int scaleCount, double octaveSigma)
  323. {
  324. for (int i = 0; i < localizedPeaks.size(); i++) 
  325. {
  326. std::vector<CKeyPoint*> thisKeyPoints;
  327. // Generate zero or more keypoints from the scale point locations.
  328. // TODO: make the values configurable
  329. // Songkran : make 36,0.8 be constant
  330. GenerateKeypointSingle (m_basePixScale, localizedPeaks[i], 36, 0.8, scaleCount, octaveSigma, &thisKeyPoints);
  331. // Generate the feature descriptor.
  332. CreateDescriptors (&thisKeyPoints,m_magnitudes[localizedPeaks[i].m_level], m_directions[localizedPeaks[i].m_level], 2.0, 4, 8, 0.2);
  333. // Only copy over those keypoints that have been successfully
  334. // assigned a descriptor (feature vector).
  335. // Songkran : all key must be already assign feature vector i think
  336. for (int j = 0; j < thisKeyPoints.size(); j++) 
  337. {
  338. //if (kp.HasFV == false)
  339. // throw (new Exception ("should not happen"));
  340. // Transform the this level image relative coordinate system
  341. // to the original image coordinates by multiplying with the
  342. // current img scale (which starts with either 0.5 or 1.0 and
  343. // then always doubles: 2.0, 4.0, ..)
  344. // Note that the kp coordinates are not used for processing by
  345. // the detection methods and this has to be the last step.
  346. // Also transform the relative-to-image scale to an
  347. // absolute-to-original-image scale.
  348. // Songkran : 
  349. // m_kpScale = octaveSigma * exp (((point.m_level + point.m_fineS) / scaleCount) * log(2));
  350. CKeyPoint* kp = thisKeyPoints[j];
  351. if (kp->m_featureVec == NULL)
  352. TRACE ("Songkran : Something wrong with feature point descriptorn");
  353. kp->m_x *= kp->m_imgScale;
  354. kp->m_y *= kp->m_imgScale;
  355. kp->m_kpScale *= kp->m_imgScale;
  356. keypoints->push_back (kp);
  357. }
  358. }
  359. }
  360. // Average the content of the direction bins.
  361. void CScaleSpace::AverageWeakBins (double* bins, int binCount)
  362. {
  363. // TODO: make some tests what number of passes is the best. (its clear
  364. // one is not enough, as we may have something like
  365. // ( 0.4, 0.4, 0.3, 0.4, 0.4 ))
  366. for (int sn = 0 ; sn < 4 ; ++sn) 
  367. {
  368. double firstE = bins[0];
  369. double last = bins[binCount - 1];
  370. for (int sw = 0 ; sw < binCount ; ++sw) 
  371. {
  372. double cur = bins[sw];
  373. double next = (sw == (binCount - 1)) ? firstE : bins[(sw + 1) % binCount];
  374. bins[sw] = (last + cur + next) / 3.0;
  375. last = cur;
  376. }
  377. }
  378. }
  379. // Fit a parabol to the three points (-1.0 ; left), (0.0 ; middle) and
  380. // (1.0 ; right).
  381. //
  382. // Formulas:
  383. // f(x) = a (x - c)^2 + b
  384. //
  385. // c is the peak offset (where f'(x) is zero), b is the peak value.
  386. //
  387. // In case there is an error false is returned, otherwise a correction
  388. // value between [-1 ; 1] is returned in 'degreeCorrection', where -1
  389. // means the peak is located completely at the left vector, and -0.5 just
  390. // in the middle between left and middle and > 0 to the right side. In
  391. // 'peakValue' the maximum estimated peak value is stored.
  392. bool CScaleSpace::InterpolateOrientation (double left, double middle,
  393.  double right, double* degreeCorrection, double* peakValue)
  394. {
  395. // Kuas:
  396. // if we set f(x) = 0 then
  397. // a ( x^2 -2cx + c^2) + b= 0
  398. // x1+x2 = 2c/a ,so we get
  399. // a = 2c/(x1+x2)
  400. // and so .. i don't under stand this code
  401. double a = ((left + right) - 2.0 * middle) / 2.0;
  402. // Not a parabola
  403. if (a == 0.0)
  404. return (false);
  405. *peakValue = 0; //Double.NaN;
  406. *degreeCorrection = 0; //Double.NaN;
  407. double c = (((left - middle) / a) - 1.0) / 2.0;
  408. double b = middle - c * c * a;
  409. if (c < -0.5 || c > 0.5)
  410. {
  411. TRACE ("KUAS : ERROR IN InterpolateOrientationn");
  412. }
  413. *degreeCorrection = c;
  414. *peakValue = b;
  415. return (true);
  416. }
  417. // Create the descriptor vector for a list of keypoints.
  418. //
  419. // keypoints: The list of keypoints to be processed. Everything but the
  420. //     descriptor must be filled in already.
  421. // magnitude/direction: The precomputed gradient magnitude and direction
  422. //     maps.
  423. // considerScaleFactor: The downscale factor, which describes the amount
  424. //     of pixels in the circular region relative to the keypoint scale.
  425. //     Low values means few pixels considered, large values extend the
  426. //     range. (Use values between 1.0 and 6.0)
  427. // descDim: The dimension size of the output descriptor. There will be
  428. //     descDim * descDim * directionCount elements in the feature vector.
  429. // directionCount: The dimensionality of the low level gradient vectors.
  430. // fvGradHicap: The feature vector gradient length hi-cap threshhold.
  431. //     (Should be: 0.2)
  432. //
  433. // Some parts modelled after Alexandre Jenny's Matlab implementation.
  434. //
  435. // Songkran : I think this is not implements according to Lowe's paper
  436. //   but the idia behind is quite the same
  437. //   in this implementation he use variable size of window (change by scale of DoG img)
  438. void CScaleSpace::CreateDescriptors (std::vector<CKeyPoint*>* keypoints,
  439. IplImage* magnitude, IplImage* direction,
  440. double considerScaleFactor, int descDim, int directionCount,
  441. double fvGradHicap)
  442. // 2.0, 4, 8,
  443. // 0.2);
  444. {
  445. if (keypoints->size() <= 0)
  446. return;
  447. considerScaleFactor *= (*keypoints)[0]->m_kpScale;
  448. double dDim05 = ((double) descDim) / 2.0;
  449. // Now calculate the radius: We consider pixels in a square with
  450. // dimension 'descDim' plus 0.5 in each direction. As the feature
  451. // vector elements at the diagonal borders are most distant from the
  452. // center pixel we have scale up with sqrt(2).
  453. // Songkran : I think in Lowe paper he suggest to use radius = (descDim*4)/2 * sqrt(2)
  454. int radius = (int) (((descDim + 1.0) / 2) *
  455. sqrt (2.0) * considerScaleFactor + 0.5);
  456. // Precompute the sigma for the "center-most, border-less" gaussian
  457. // weighting.
  458. // (We are operating to dDim05, CV book tells us G(x), x > 3 sigma
  459. //  negligible, but this range seems much shorter!?)
  460. //
  461. // In Lowe03, page 15 it says "A Gaussian weighting function with
  462. // sigma equal to one half the width of the descriptor window is
  463. // used", so we just use his advice.
  464. // Songkran : should the line below recorrect to 2.0 * (r/2) * (r/2)
  465. double sigma2Sq = 2.0 * dDim05 * dDim05;
  466. for (int i = 0; i < keypoints->size(); i++)
  467. {
  468. CKeyPoint* kp = (*keypoints)[i];
  469. // The angle to rotate with: negate the orientation.
  470. double angle = -kp->m_orientation;
  471. kp->CreateVector (descDim, descDim, directionCount);
  472. //Console.WriteLine ("  FV allocated");
  473. for (int y = -radius ; y < radius ; ++y) 
  474. {
  475. for (int x = -radius ; x < radius ; ++x) 
  476. {
  477. // Rotate and scale
  478. double yR = sin (angle) * x + cos (angle) * y;
  479. double xR = cos (angle) * x - sin (angle) * y;
  480. yR /= considerScaleFactor;
  481. xR /= considerScaleFactor;
  482. // Now consider all (xR, yR) that are anchored within
  483. // (- descDim/2 - 0.5 ; -descDim/2 - 0.5) to
  484. //    (descDim/2 + 0.5 ; descDim/2 + 0.5),
  485. // as only those can influence the FV.
  486. if (yR >= (dDim05 + 0.5) || xR >= (dDim05 + 0.5) ||
  487. xR <= -(dDim05 + 0.5) || yR <= -(dDim05 + 0.5))
  488. continue;
  489. int currentX = (int) (x + kp->m_x + 0.5);
  490. int currentY = (int) (y + kp->m_y + 0.5);
  491. if (currentX < 1 || currentX >= (cvGetSize(magnitude).width - 1) ||
  492. currentY < 1 || currentY >= (cvGetSize(magnitude).height - 1))
  493. continue;
  494. // Weight the magnitude relative to the center of the
  495. // whole FV. We do not need a normalizing factor now, as
  496. // we normalize the whole FV later anyway (see below).
  497. // xR, yR are each in -(dDim05 + 0.5) to (dDim05 + 0.5)
  498. // range
  499. double magW = exp (-(xR * xR + yR * yR) / sigma2Sq) * 
  500. cvGet2D (magnitude,currentY,currentX).val[0];
  501. // Anchor to (-1.0, -1.0)-(dDim + 1.0, dDim + 1.0), where
  502. // the FV points are located at (x, y)
  503. // Songkran : i think this should be yR += dDim05;
  504. // Songkran : and range is from (-0.5,-0.5) to (dDim+0.5,dDim+0.5)
  505. //yR += dDim05 - 0.5;
  506. //xR += dDim05 - 0.5;
  507. yR += dDim05;
  508. xR += dDim05;
  509. // Build linear interpolation weights:
  510. // A B
  511. // C D
  512. //
  513. // The keypoint is located between A, B, C and D.
  514. // Songkran : this is trilinear interpolation (3 variable x,y,dir)
  515. int xIdx[2];
  516. int yIdx[2];
  517. int dirIdx[2];
  518. double xWeight[2];
  519. double yWeight[2];
  520. double dirWeight[2];
  521. bool flagx[2],flagy[2],flagdir[2];
  522. memset (flagx,false,2*sizeof(bool));
  523. memset (flagy,false,2*sizeof(bool));
  524. memset (flagdir,false,2*sizeof(bool));
  525. // Songkran : have to check xR < descDim also ??
  526. if (xR >= 0 && xR < descDim) 
  527. {
  528. xIdx[0] = (int) xR;
  529. xWeight[0] = (1.0 - (xR - xIdx[0]));
  530. flagx[0] = true;
  531. }
  532. if (yR >= 0 && yR < descDim) 
  533. {
  534. yIdx[0] = (int) yR;
  535. yWeight[0] = (1.0 - (yR - yIdx[0]));
  536. flagy[0] = true;
  537. }
  538. if (xR+1 < descDim) 
  539. {
  540. xIdx[1] = (int) (xR + 1.0);
  541. // xWeight[0] + xWeight[1] = 1
  542. //  1 - xR + xIdx[0] + xR - xIdx[0] - 1 + 1
  543. xWeight[1] = xR - xIdx[1] + 1.0;
  544. flagx[1] = true;
  545. }
  546. if (yR+1 < descDim) 
  547. {
  548. // yWeight[0] + yWeight[1] = 1
  549. //  1 - yR + yIdy[0] + yR - yIdy[0] - 1 + 1
  550. yIdx[1] = (int) (yR + 1.0);
  551. yWeight[1] = yR - yIdx[1] + 1.0;
  552. flagy[1] = true;
  553. }
  554. // Rotate the gradient direction by the keypoint
  555. // orientation, then normalize to [-pi ; pi] range.
  556. // Songkran : should correct to dir += 2*Math.PI ?
  557. double dir = cvGet2D(direction,currentY,currentX).val[0] - kp->m_orientation;
  558. if (dir <= -PI)
  559. //dir += Math.PI;
  560. dir += 2*PI;
  561. if (dir > PI)
  562. //dir -= Math.PI;
  563. dir -= 2*PI;
  564. double idxDir = (dir * directionCount) / (2.0 * PI);
  565. if (idxDir < 0.0)
  566. idxDir += directionCount;
  567. dirIdx[0] = (int) idxDir;
  568. dirIdx[1] = (dirIdx[0] + 1) % directionCount;
  569. dirWeight[0] = 1.0 - (idxDir - dirIdx[0]);
  570. dirWeight[1] = idxDir - dirIdx[0];
  571. for (int iy = 0 ; iy < 2 ; ++iy) 
  572. for (int ix = 0 ; ix < 2 ; ++ix) 
  573. for (int id = 0 ; id < 2 ; ++id) 
  574. if (flagx[ix] && flagy[iy])
  575. kp->FVSet (xIdx[ix], yIdx[iy], dirIdx[id],
  576. kp->FVGet (xIdx[ix], yIdx[iy], dirIdx[id]) +
  577. xWeight[ix] * yWeight[iy] * dirWeight[id] * magW);
  578. }
  579. }
  580. // Normalize and hicap the feature vector, as recommended on page
  581. // 16 in Lowe03.
  582. kp->CapAndNormalizeFV (fvGradHicap);
  583. }
  584. }
  585. // Assign each feature point one or more standardized orientations.
  586. // (section 5 in Lowe's paper)
  587. //
  588. // We use an orientation histogram with 36 bins, 10 degrees each. For
  589. // this, every pixel (x,y) lieing in a circle of 'squareDim' diameter
  590. // within in a 'squareDim' sized field within the image L ('gaussImg') is
  591. // examined and two measures calculated:
  592. //
  593. //    m = sqrt{ (L_{x+1,y} - L_{x-1,y})^2 + (L_{x,y+1} - L_{x,y-1})^2 }
  594. //    theta = tan^{-1} ( frac{ L_{x,y+1} - L_{x,y-1} }
  595. //                { L_{x+1,y} - L_{x-1,y} } )
  596. //
  597. // Where m is the gradient magnitude around the pixel and theta is the
  598. // gradient orientation. The 'imgScale' value is the octave scale,
  599. // starting with 1.0 at the finest-detail octave, and doubling every
  600. // octave. The gradient orientations are discreetized to 'binCount'
  601. // directions (should be: 36). For every peak orientation that lies within
  602. // 'peakRelThresh' of the maximum peak value, a keypoint location is
  603. // added (should be: 0.8).
  604. //
  605. // Note that 'space' is the gaussian smoothed original image, not the
  606. // difference-of-gaussian one used for peak-search.
  607. // Kuas : imgScale is basePixScale (start scale (relative dimension with input img) of that octave)
  608. void CScaleSpace::GenerateKeypointSingle (double imgScale, CScalePoint point,
  609. int binCount, double peakRelThresh, int scaleCount,
  610. double octaveSigma, std::vector<CKeyPoint*>*vec)
  611. {
  612. // The relative estimated keypoint scale. The actual absolute keypoint
  613. // scale to the original image is yielded by the product of imgScale.
  614. // But as we operate in the current octave, the size relative to the
  615. // anchoring images is missing the imgScale factor.
  616. //double kpScale = octaveSigma * exp (((point.m_level + point.m_fineS) / scaleCount) * log(2));
  617. double kpScale = octaveSigma * (exp (((point.m_level) / scaleCount) * log(2))+ point.m_fineS);
  618. // Lowe03, "A gaussian-weighted circular window with a sigma three
  619. // times that of the scale of the keypoint".
  620. //
  621. // With sigma = 3.0 * kpScale, the square dimension we have to
  622. // consider is (3 * sigma) (until the weight becomes very small).
  623. // Kuas : Lowe04 say sigma = 1.5 * kpScale
  624. double sigma = 1.5 * kpScale;
  625. int radius = (int) (3.0 * sigma / 2.0 + 0.5);
  626. int radiusSq = radius * radius;
  627. IplImage* magnitude = m_magnitudes[point.m_level];
  628. IplImage* direction = m_directions[point.m_level];
  629. // As the point may lie near the border, build the rectangle
  630. // coordinates we can still reach, minus the border pixels, for which
  631. // we do not have gradient information available.
  632. int xMin = point.m_x - radius > 1 ? point.m_x - radius : 1;
  633. int xMax = point.m_x + radius < cvGetSize(magnitude).width-1 ? point.m_x + radius : cvGetSize(magnitude).width-1;
  634. int yMin = point.m_y - radius > 1 ? point.m_y - radius : 1;
  635. int yMax = point.m_y + radius < cvGetSize(magnitude).height-1 ? point.m_y + radius : cvGetSize(magnitude).height-1;
  636. // Precompute 1D gaussian divisor (2 sigma^2) in:
  637. // G(r) = e^{-frac{r^2}{2 sigma^2}}
  638. double gaussianSigmaFactor = 2.0 * sigma * sigma;
  639. double *bins = new double[binCount];
  640. memset (bins,0,sizeof(double)*binCount);
  641. // Build the direction histogram
  642. for (int y = yMin ; y < yMax ; ++y) {
  643. for (int x = xMin ; x < xMax ; ++x) {
  644. // Only consider pixels in the circle, else we might skew the
  645. // orientation histogram by considering more pixels into the
  646. // corner directions
  647. int relX = x - point.m_x;
  648. int relY = y - point.m_y;
  649. if (sqr(relX) + sqr(relY) > radiusSq)
  650. continue;
  651. // The gaussian weight factor.
  652. double gaussianWeight = exp (- ((relX * relX + relY * relY) / gaussianSigmaFactor));
  653. // find the closest bin and add the direction
  654. //int binIdx = FindClosestRotationBin (binCount, direction[x, y]);
  655. double angle = cvGet2D (direction,y,x).val[0];
  656. angle += PI;
  657. angle /= 2.0 * PI;
  658. // calculate the aligned bin
  659. angle *= binCount;
  660. int binIdx = (int) angle;
  661. if (binIdx == binCount)
  662. binIdx = 0;
  663. bins[binIdx] += cvGet2D(magnitude,y,x).val[0] * gaussianWeight;
  664. }
  665. }
  666. // As there may be succeeding histogram entries like this:
  667. // ( ..., 0.4, 0.3, 0.4, ... ) where the real peak is located at the
  668. // middle of this three entries, we can improve the distinctiveness of
  669. // the bins by applying an averaging pass.
  670. //
  671. // TODO: is this really the best method? (we also loose a bit of
  672. // information. Maybe there is a one-step method that conserves more)
  673. AverageWeakBins (bins, binCount);
  674. // find the maximum peak in gradient orientation
  675. double maxGrad = 0.0;
  676. int maxBin = 0;
  677. for (int b = 0 ; b < binCount ; ++b) {
  678. if (bins[b] > maxGrad) {
  679. maxGrad = bins[b];
  680. maxBin = b;
  681. }
  682. }
  683. // First determine the real interpolated peak high at the maximum bin
  684. // position, which is guaranteed to be an absolute peak.
  685. //
  686. // XXX: should we use the estimated peak value as reference for the
  687. //   0.8 check or the original bin-value?
  688. double maxPeakValue, maxDegreeCorrection;
  689. InterpolateOrientation (bins[maxBin == 0 ? (binCount - 1) : (maxBin - 1)],
  690. bins[maxBin], bins[(maxBin + 1) % binCount],
  691. &maxDegreeCorrection, &maxPeakValue);
  692. // Now that we know the maximum peak value, we can find other keypoint
  693. // orientations, which have to fulfill two criterias:
  694. //
  695. //  1. They must be a local peak themselves. Else we might add a very
  696. //     similar keypoint orientation twice (imagine for example the
  697. //     values: 0.4 1.0 0.8, if 1.0 is maximum peak, 0.8 is still added
  698. //     with the default threshhold, but the maximum peak orientation
  699. //     was already added).
  700. //  2. They must have at least peakRelThresh times the maximum peak
  701. //     value.
  702. bool* binIsKeypoint = new bool[binCount];
  703. for (b = 0 ; b < binCount ; ++b) 
  704. {
  705. binIsKeypoint[b] = false;
  706. // The maximum peak of course is
  707. if (b == maxBin) 
  708. {
  709. binIsKeypoint[b] = true;
  710. continue;
  711. }
  712. // Local peaks are, too, in case they fulfill the threshhold
  713. if (bins[b] < (peakRelThresh * maxPeakValue))
  714. continue;
  715. int leftI = (b == 0) ? (binCount - 1) : (b - 1);
  716. int rightI = (b + 1) % binCount;
  717. if (bins[b] <= bins[leftI] || bins[b] <= bins[rightI])
  718. continue; // not local peak
  719. binIsKeypoint[b] = true;
  720. }
  721. // All the valid keypoint bins are now marked in binIsKeypoint, now
  722. // build them.
  723. // find other possible locations
  724. double oneBinRad = (2.0 * PI) / binCount;
  725. for (b = 0 ; b < binCount ; ++b) 
  726. {
  727. if (binIsKeypoint[b] == false)
  728. continue;
  729. int bLeft = (b == 0) ? (binCount - 1) : (b - 1);
  730. int bRight = (b + 1) % binCount;
  731. // Get an interpolated peak direction and value guess.
  732. double peakValue;
  733. double degreeCorrection;
  734. if (InterpolateOrientation (bins[bLeft], bins[b], bins[bRight],
  735. &degreeCorrection, &peakValue) == false)
  736. {
  737. //throw (new InvalidOperationException ("BUG: Parabola fitting broken"));
  738. TRACE ("BUG: Parabola fitting brokenn");
  739. }
  740. // [-1.0 ; 1.0] -> [0 ; binrange], and add the fixed absolute bin
  741. // position.
  742. // We subtract PI because bin 0 refers to 0, binCount-1 bin refers
  743. // to a bin just below 2PI, so -> [-PI ; PI]. Note that at this
  744. // point we determine the canonical descriptor anchor angle. It
  745. // does not matter where we set it relative to the peak degree,
  746. // but it has to be constant. Also, if the output of this
  747. // implementation is to be matched with other implementations it
  748. // must be the same constant angle (here: -PI).
  749. double degree = (b + degreeCorrection) * oneBinRad - PI;
  750. if (degree < -PI)
  751. degree += 2.0 * PI;
  752. else if (degree > PI)
  753. degree -= 2.0 * PI;
  754. CKeyPoint* kp = new CKeyPoint (m_gaussImgs[point.m_level],
  755. point.m_x + point.m_fineX,
  756. point.m_y + point.m_fineY,
  757. imgScale, kpScale, degree);
  758. vec->push_back (kp);
  759. }
  760. delete binIsKeypoint;
  761. delete bins;
  762. }