title: Recognizing an image from a list with OpenCV SIFT using the FLANN matching
body: <p>The point of the application is to recognize an image from an already set list of images. The list of images have had their SIFT descriptors extracted and saved in files. Nothing interesting here: </p>
  
  <pre><code>std::vector&lt;cv::KeyPoint&gt; detectedKeypoints;
  cv::Mat objectDescriptors;
  
  // Extract data
  cv::SIFT sift;
  sift.detect(image, detectedKeypoints);
  sift.compute(image, detectedKeypoints, objectDescriptors);
  
  // Save the file
  cv::FileStorage fs(file, cv::FileStorage::WRITE);
  fs &lt;&lt; "descriptors" &lt;&lt; objectDescriptors;
  fs &lt;&lt; "keypoints" &lt;&lt; detectedKeypoints;
  fs.release();
  </code></pre>
  
  <p>Then the device takes a picture. SIFT descriptors are extracted in the same way. The idea now was to compare the descriptors to the ones from the files. I am doing that using the FLANN matcher from OpenCV. I am trying to quantify the similarity, image by image. After going through the whole list I should have the best match. </p>
  
  <pre><code>const cv::Ptr&lt;cv::flann::IndexParams&gt;&amp; indexParams = new cv::flann::KDTreeIndexParams(1);
  const cv::Ptr&lt;cv::flann::SearchParams&gt;&amp; searchParams = new cv::flann::SearchParams(64);
  
  // Match using Flann
  cv::Mat indexMat;
  cv::FlannBasedMatcher matcher(indexParams, searchParams);
  std::vector&lt; cv::DMatch &gt; matches;
  matcher.match(objectDescriptors, readDescriptors, matches);
  </code></pre>
  
  <p>After matching I understand that I get a list of the closest found distances between the feature vectors. I find the minimum distance and, using it I can count "good matches" and even get a list of the respective points: </p>
  
  <pre><code>// Count the number of mathes where the distance is less than 2 * min_dist
  int goodCount = 0;
  for (int i = 0; i &lt; objectDescriptors.rows; i++)
  {
      if (matches[i].distance &lt;  2 * min_dist)
      {
          ++goodCount;
          // Save the points for the homography calculation
          obj.push_back(detectedKeypoints[matches[i].queryIdx].pt);
          scene.push_back(readKeypoints[matches[i].trainIdx].pt);
      }
  }
  </code></pre>
  
  <p>I'm showing easy parts of the code just to make this more easy to follow, I know some of it doesn't need to be here. </p>
  
  <p>Continuing, I was hoping that simply counting the number of good matches like this would be enough, but it turned out to mostly just point me to the image with the most descriptors. What I tried to after this was computing the homography. The aim was to compute it and see whether it's a valid homoraphy or not. The hope was that a good match, and only a good match, would have a homography that is a good transformation. Creating the homography was done simply using <em>cv::findHomography</em> on the <em>obj</em> and <em>scene</em> which are <em>std::vector&lt; cv::Point2f></em>. I checked the validity of the homography using some code I found online: </p>
  
  <pre><code>bool niceHomography(cv::Mat H)
  {
      std::cout &lt;&lt; H &lt;&lt; std::endl;
  
      const double det = H.at&lt;double&gt;(0, 0) * H.at&lt;double&gt;(1, 1) - H.at&lt;double&gt;(1, 0) * H.at&lt;double&gt;(0, 1);
      if (det &lt; 0)
      {
          std::cout &lt;&lt; "Homography: bad determinant" &lt;&lt; std::endl;
          return false;
      }
  
      const double N1 = sqrt(H.at&lt;double&gt;(0, 0) * H.at&lt;double&gt;(0, 0) + H.at&lt;double&gt;(1, 0) * H.at&lt;double&gt;(1, 0));
      if (N1 &gt; 4 || N1 &lt; 0.1)
      {
          std::cout &lt;&lt; "Homography: bad first column" &lt;&lt; std::endl;
          return false;
      }
  
      const double N2 = sqrt(H.at&lt;double&gt;(0, 1) * H.at&lt;double&gt;(0, 1) + H.at&lt;double&gt;(1, 1) * H.at&lt;double&gt;(1, 1));
      if (N2 &gt; 4 || N2 &lt; 0.1)
      {
          std::cout &lt;&lt; "Homography: bad second column" &lt;&lt; std::endl;
          return false;
      }
  
      const double N3 = sqrt(H.at&lt;double&gt;(2, 0) * H.at&lt;double&gt;(2, 0) + H.at&lt;double&gt;(2, 1) * H.at&lt;double&gt;(2, 1));
      if (N3 &gt; 0.002)
      {
          std::cout &lt;&lt; "Homography: bad third row" &lt;&lt; std::endl;
          return false;
      }
  
      return true;
  }
  </code></pre>
  
  <p>I don't understand the math behind this so, while testing, I sometimes replaced this function with a simple check whether the determinant of the homography was positive. The problem is that I kept having issues here. The homographies were either all bad, or good when they shouldn't have been (when I was checking only the determinant). </p>
  
  <p>I figured I should actually use the homography and for a number of points just compute their position in the destination image using their position in the source image. Then I would compare these average distances, and I would ideally get a very obvious smaller average distance in the case of the correct image. This did not work <strong>at all</strong>. All the distances were colossal. I thought I might have used the homography the other way around to calculate the right position, but switching <em>obj</em> and <em>scene</em> with each other gave similar results. </p>
  
  <p>Other things I tried were SURF descriptors instead of SIFT, BFMatcher (brute force) instead of FLANN, getting the <em>n</em> smallest distances for every image instead of a number depending on the minimum distance, or getting distances depending on a global maximum distance. None of these approaches gave me definite good results, and I feel stuck now. </p>
  
  <p>My only next strategy would be to sharpen the images or even turn them to binary images using some local threshold or some algorithms used for segmentation. I am looking for any suggestions or mistake anyone can see in my work. </p>
  
  <p>I don't know whether this is relevant, but I added some of the images I am testing this on. Many times in the test images most of the SIFT vectors come from the frame (higher contrast) than the painting. This is why I'm thinking sharpening the images might work, but I don't want to go deeper in case something I did previously is wrong. </p>
  
  <p>The gallery of images is <a href="http://imgur.com/a/YBYFx#0" rel="noreferrer">here</a> with the descriptions in the titles. The images are of quite high resolution, please view in case it might give some hints. </p>