【特征检测器】检测器

在这里插入图片描述

1. Shi-Tomasi 检测器

1.1 算法特点:

  • Shi-Tomasi 检测器使用了 Harris 算法的改良版,在处理图像边缘时具有更好的性能表现,能够检测出更多的稳定角点。
  • Shi-Tomasi 检测器通过计算特征点处的协方差矩阵并对其进行特征值分解,从而得到每个特征点的角点响应函数。
  • Shi-Tomasi 检测器可以通过设置阈值来控制检测的角点数量,从而适应不同的应用场景。
  • Shi-Tomasi 检测器的计算速度比其他角点检测算法如 SIFT 和 SURF 要快得多,因此适合于实时计算机视觉应用。

总之, Shi-Tomasi 检测器作为一种快速、准确且可控制角点数量的角点检测算法,在计算机视觉领域中有着广泛的应用。

1.2算法步骤

  1. 计算图像中每个像素点 x 和 y 方向的梯度,使用 Sobel 导数滤波器或其他梯度估计算法。

  2. 对于每个像素点,计算一个 2x2 的局部自相关矩阵 M:

M = ( ∑ x , y ∈ N ( I x ) 2 ∑ x , y ∈ N I x I y ∑ x , y ∈ N I x I y ∑ x , y ∈ N ( I y ) 2 ) , M=\begin{pmatrix} \sum_{x,y\in N}(I_x)^2 & \sum_{x,y\in N}I_xI_y \\ \sum_{x,y\in N}I_xI_y & \sum_{x,y\in N}(I_y)^2 \end{pmatrix}, M=(x,yN(Ix)2x,yNIxIyx,yNIxIyx,yN(Iy)2),

其中,N 表示以当前像素点为中心的一个邻域窗口。

  1. 计算 M 的特征值 λ1 和 λ2。

  2. 判断这个像素点是否为角点。采用的方法是结合 λ1 和 λ2 来计算一个响应函数 R。

    常用响应函数包括:

R = m i n ( λ 1 , λ 2 ) R = λ 1 + λ 2 R = λ 1 λ 2 R = {\rm min}(\lambda_1, \lambda_2)\\ R = \lambda_1 + \lambda_2 \\ R = \frac{\lambda_1}{\lambda_2} R=min(λ1,λ2)R=λ1+λ2R=λ2λ1

通常使用第一种函数来计算角点得分,在比较所有像素点的得分以确定是否为角点。

  1. 进行非极大值抑制,去除重复的角点。
  2. 首先进行Shi-Tomasi角点检测,得到所有检测到的角点和它们的响应值(关键点分数)。
    对于每个关键点,计算其周围一定尺寸(例如3x3或5x5)的邻域内所有关键点的响应值,找到其中最大的响应值,并将该值与当前关键点的响应值比较。
    如果当前关键点的响应值不是最大的,则将其从候选列表中删除。否则,保留该点并继续向下处理下一个点。
bool compShiTomasiScore(const cv::Mat& img, const Eigen::Vector2i& px, double* score) {
    
    
  CHECK_NOTNULL(score);
  CHECK(img.type() == CV_8UC1);
  constexpr int kHalfPatchSize = 4;
  constexpr int kPatchSize = 2 * kHalfPatchSize;
  constexpr int kPatchArea = kPatchSize * kPatchSize;

  const int x_min = px(0) - kHalfPatchSize;
  const int x_max = px(0) + kHalfPatchSize;
  const int y_min = px(1) - kHalfPatchSize;
  const int y_max = px(1) + kHalfPatchSize;

  if (x_min < 1 || x_max >= img.cols - 1 || y_min < 1 || y_max >= img.rows - 1)
    return false;

  float dXX = 0.0;
  float dYY = 0.0;
  float dXY = 0.0;
  const int stride = img.step.p[0];
  for (int y = y_min; y < y_max; ++y) {
    
    
    const uint8_t* ptr_left = img.data + stride * y + x_min - 1;
    const uint8_t* ptr_right = img.data + stride * y + x_min + 1;
    const uint8_t* ptr_top = img.data + stride * (y - 1) + x_min;
    const uint8_t* ptr_bottom = img.data + stride * (y + 1) + x_min;
    for (int x = 0; x < kPatchSize; ++x, ++ptr_left, ++ptr_right, ++ptr_top, ++ptr_bottom) {
    
    
      float dx = *ptr_right - *ptr_left;
      float dy = *ptr_bottom - *ptr_top;
      dXX += dx * dx;
      dYY += dy * dy;
      dXY += dx * dy;
    }
  }

  // Find and return smaller eigenvalue:
  dXX = dXX / (2.0f * kPatchArea);
  dYY = dYY / (2.0f * kPatchArea);
  dXY = dXY / (2.0f * kPatchArea);
  *score = 0.5f * (dXX + dYY - std::sqrt((dXX - dYY) * (dXX - dYY) + 4 * dXY * dXY));
  return true;
}

这就是 Shi-Tomasi 检测器的算法步骤,它可以通过检测到图像中显著的角点来帮助计算机视觉任务,例如特征匹配和目标跟踪等应用。

1.3 8-邻域非最大抑制算法

8-邻域非最大抑制算法。非最大抑制是计算机视觉中常用的技术,它可以帮助去掉图像中冗余的特征点,只保留最具代表性的特征点。这个函数针对一个输入分数图score和一个角点网格grid进行操作,并输出筛选后的角点corners。

在具体实现时,该函数通过遍历score矩阵中每个点,确定了中心点(center)及它周围8个点(p1~p8)的坐标,并定位它们在角点网格中的位置(k)。如果中心点得分小于设定的阈值,就直接跳过。否则,检查该点与周围点之间的得分关系,如果不满足non-maximum suppression 的条件,则跳过。如果得分高于其它点且大于当前角点的得分,则更新角点信息并加入到corners列表中,具体包括点的坐标、尺度(即金字塔层数,level)、得分、角度等信息,其中角度需要调用 getAngleAtPixelUsingHistogram() 函数计算。

整个非最大抑制过程将会在每个金字塔层都执行一次,并针对不同的尺度(scale)进行处理,来获取更全局的角点特征。

  // 8-neighbor nonmax suppression
  const int stride = score.step;
  for (int y = border; y < score.rows - border; ++y) {
    
    
    const float* p = &score.at<float>(y, border);
    for (int x = border; x < score.cols - border; ++x, ++p) {
    
    
      const int k = grid.getCellIndex(x, y, scale);
      if (grid.occupancy_.at(k))
        continue;

      const float* const center = p;
      if (*center < threshold)
        continue;
      if (*(center + 1) >= *center)
        continue;
      if (*(center - 1) > *center)
        continue;
      const float* const p1 = (center + stride);
      const
      float* const p2 = (center - stride);
      if (*p1 >= *center)
        continue;
      if (*p2 > *center)
        continue;
      if (*(p1 + 1) >= *center)
        continue;
      if (*(p1 - 1) > *center)
        continue;
      if (*(p2 + 1) >= *center)
        continue;
      if (*(p2 - 1) > *center)
        continue;

      Corner& c = corners.at(k);
      if (*p > c.score) {
    
    
        c.x = x * scale;
        c.y = y * scale;
        c.level = level - 1;
        c.score = *p;
        c.angle = getAngleAtPixelUsingHistogram(img_pyr[level], Eigen::Vector2i(x, y), 4);
      }
    }
  }

猜你喜欢

转载自blog.csdn.net/Darlingqiang/article/details/131324869