如何在opencv中找到单个图像关键点之间的欧氏距离

时间:2014-10-24 08:10:09

标签: c++ opencv

我想为图像中的每个关键点获取距离矢量d。距离矢量应包括从该关键点到该图像中所有其他关键点的距离。 注意:使用SIFT找到关键点。

我对opencv很新。 C ++中是否有一个库函数可以让我的任务变得简单?

3 个答案:

答案 0 :(得分:9)

如果您对位置距离感兴趣,但描述符距离则可以使用:

cv::Mat SelfDescriptorDistances(cv::Mat descr)
{
    cv::Mat selfDistances = cv::Mat::zeros(descr.rows,descr.rows, CV_64FC1);
    for(int keyptNr = 0; keyptNr < descr.rows; ++keyptNr)
    {
        for(int keyptNr2 = 0; keyptNr2 < descr.rows; ++keyptNr2)
        {
            double euclideanDistance = 0;
            for(int descrDim = 0; descrDim < descr.cols; ++descrDim)
            {
                double tmp = descr.at<float>(keyptNr,descrDim) - descr.at<float>(keyptNr2, descrDim);
                euclideanDistance += tmp*tmp;
            }

            euclideanDistance = sqrt(euclideanDistance);
            selfDistances.at<double>(keyptNr, keyptNr2) = euclideanDistance;
        }

    }
    return selfDistances;
}

将为您提供N×N矩阵(N =关键点数),其中Mat_i,j =关键点i和j之间的欧几里德距离。

使用此输入:

enter image description here

我得到了这些输出:

  1. 标记关键点的图像,其距离小于0.05
  2. enter image description here

    1. 与矩阵对应的图像。白色像素是dist&lt; 0.05。
    2. enter image description here

      备注:你可以在矩阵的计算中优化很多东西,因为距离是对称的!

      更新:

      这是另一种方法:

      从您的聊天中我知道您需要13GB内存才能保存41381个关键点(您尝试过)的距离信息。如果您只想要N个最佳匹配,请尝试以下代码:

      // choose double here if you are worried about precision!
      #define intermediatePrecision float
      //#define intermediatePrecision double
      // 
      void NBestMatches(cv::Mat descriptors1, cv::Mat descriptors2, unsigned int n, std::vector<std::vector<float> > & distances, std::vector<std::vector<int> > & indices)
      {
          // TODO: check whether descriptor dimensions and types are the same for both!
      
          // clear vector
          // get enough space to create n best matches
          distances.clear();
          distances.resize(descriptors1.rows);
          indices.clear();
          indices.resize(descriptors1.rows);
      
          for(int i=0; i<descriptors1.rows; ++i)
          {
              // references to current elements:
              std::vector<float> & cDistances = distances.at(i);
              std::vector<int>  & cIndices = indices.at(i);
              // initialize:
              cDistances.resize(n,FLT_MAX);
              cIndices.resize(n,-1);  // for -1 = "no match found"
      
              // now find the 3 best matches for descriptor i:
              for(int j=0; j<descriptors2.rows; ++j)
              {
                  intermediatePrecision euclideanDistance = 0;
                  for( int dim = 0; dim < descriptors1.cols; ++dim)
                  {
                      intermediatePrecision tmp = descriptors1.at<float>(i,dim) - descriptors2.at<float>(j, dim);
                      euclideanDistance += tmp*tmp;
                  }
                  euclideanDistance = sqrt(euclideanDistance);
      
                  float tmpCurrentDist = euclideanDistance;
                  int tmpCurrentIndex = j;
      
                  // update current best n matches:
                  for(unsigned int k=0; k<n; ++k)
                  {
                      if(tmpCurrentDist < cDistances.at(k))
                      {
                          int tmpI2 = cIndices.at(k);
                          float tmpD2 = cDistances.at(k);
      
                          // update current k-th best match
                          cDistances.at(k) = tmpCurrentDist;
                          cIndices.at(k) = tmpCurrentIndex;
      
                          // previous k-th best should be better than k+1-th best //TODO: a simple memcpy would be faster I guess.
                          tmpCurrentDist = tmpD2;
                          tmpCurrentIndex =tmpI2;
                      }
                  }
      
      
              }
          }
      
      }
      

      它计算第一个描述符与第二个描述符的每个关键点的N个最佳匹配。因此,如果您希望为相同的关键点执行此操作,则您将设置为descriptors1 = descriptors2您的呼叫,如下所示。 记住: 该函数不知道两个描述符集是相同的,所以第一个最佳匹配(或至少一个)将是关键点本身,距离0总是!如果使用结果,请记住这一点!

      此处的示例代码用于生成与上述图像类似的图像:

      int main()
      {
          cv::Mat input = cv::imread("../inputData/MultiLena.png");
      
          cv::Mat gray;
          cv::cvtColor(input, gray, CV_BGR2GRAY);
      
          cv::SiftFeatureDetector detector( 7500 );
          cv::SiftDescriptorExtractor describer;
      
          std::vector<cv::KeyPoint> keypoints;
      
          detector.detect( gray, keypoints );
      
          // draw keypoints
          cv::drawKeypoints(input,keypoints,input);
      
      
      
          cv::Mat descriptors;
          describer.compute(gray, keypoints, descriptors);
      
          int n = 4;
          std::vector<std::vector<float> > dists;
          std::vector<std::vector<int> > indices;
      
          // compute the N best matches between the descriptors and themselves.
          // REMIND: ONE best match will always be the keypoint itself in this setting!
          NBestMatches(descriptors, descriptors, n, dists, indices);
      
          for(unsigned int i=0; i<dists.size(); ++i)
          {
              for(unsigned int j=0; j<dists.at(i).size(); ++j)
              {
                  if(dists.at(i).at(j) < 0.05)
                      cv::line(input, keypoints[i].pt, keypoints[indices.at(i).at(j)].pt, cv::Scalar(255,255,255) );
              }
          }
      
          cv::imshow("input", input);
          cv::waitKey(0);
      
          return 0;
      }
      

答案 1 :(得分:2)

keypoint类有一个名为pt的成员,后者又有xy [点的(x,y)位置]作为自己的成员

鉴于两个关键点kp1kp2,它很容易计算出欧几里德距离:

Point diff = kp1.pt - kp2.pt;
float dist = std::sqrt( diff.x * diff.x + diff.y * diff.y )

在你的情况下,它将是一个迭代所有关键点的双循环。

答案 2 :(得分:2)

  1. 创建2D矢量(其大小为NXN) - &gt; std::vector< std::vector< float > > item;
  2. 创建2 for循环直到你拥有的关键点数(N)
  3. 按照a-Jays

    的建议计算距离

    Point diff = kp1.pt - kp2.pt; float dist = std::sqrt( diff.x * diff.x + diff.y * diff.y );

  4. 使用push_back为每个关键点添加到向量 - &gt; N次。