检测器
- 1. Shi-Tomasi 检测器
- 1.1 算法特点:
- 1.2算法步骤
- 1.3 8-邻域非最大抑制算法
1. Shi-Tomasi 检测器
1.1 算法特点:
- Shi-Tomasi 检测器使用了 Harris 算法的改良版,在处理图像边缘时具有更好的性能表现,能够检测出更多的稳定角点。
- Shi-Tomasi 检测器通过计算特征点处的协方差矩阵并对其进行特征值分解,从而得到每个特征点的角点响应函数。
- Shi-Tomasi 检测器可以通过设置阈值来控制检测的角点数量,从而适应不同的应用场景。
- Shi-Tomasi 检测器的计算速度比其他角点检测算法如 SIFT 和 SURF 要快得多,因此适合于实时计算机视觉应用。
总之, Shi-Tomasi 检测器作为一种快速、准确且可控制角点数量的角点检测算法,在计算机视觉领域中有着广泛的应用。
1.2算法步骤
-
计算图像中每个像素点 x 和 y 方向的梯度,使用 Sobel 导数滤波器或其他梯度估计算法。
-
对于每个像素点,计算一个 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,y∈N(Ix)2∑x,y∈NIxIy∑x,y∈NIxIy∑x,y∈N(Iy)2),
其中,N 表示以当前像素点为中心的一个邻域窗口。
-
计算 M 的特征值 λ1 和 λ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
通常使用第一种函数来计算角点得分,在比较所有像素点的得分以确定是否为角点。
- 进行非极大值抑制,去除重复的角点。
- 首先进行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);
}
}
}