OpenCV4入门教程107:moravec角点检测

索引地址:系列索引

角点和角点检测:角点检测

moravec算法

基本原理

这是一种基于灰度方差的角点检测算子,该算子计算图像中每个像素点沿着水平、垂直、对角线及反对角线的四个方向的灰度方差,其中的最小值选作该像素点的兴趣值IV(interest value),再通过局部非极大值抑制来检测其是否为特征点(角点)。

算法步骤

(1)计算各像元的兴趣值IV。以像素(c,r)为中心的 w x w 的影像窗口(如 5x5 的窗口),计算四个方向相邻像素灰度差的平方和:

1
2
3
4
5
a-a-a
-aaa-
--a--
-aaa-
a-a-a

其计算公式为:

V1=i=kk1(gc+i,rgc+i+1,r)2V2=i=kk1(gc+i,r+igc+i+1,r+i+1)2V3=i=kk1(gc,r+igc,r+i+1)2V4=i=kk1(gc+i,r1gc+i+1,ri1)2V_1 = \sum\limits_{i=-k}^{k-1}(g_{c+i,r}-g_{c+i+1,r})^2 \\ V_2 = \sum\limits_{i=-k}^{k-1}(g_{c+i,r+i}-g_{c+i+1,r+i+1})^2 \\ V_3 = \sum\limits_{i=-k}^{k-1}(g_{c,r+i}-g_{c,r+i+1})^2 \\ V_4 = \sum\limits_{i=-k}^{k-1}(g_{c+i,r-1}-g_{c+i+1,r-i-1})^2

其中k =INT (w/ 2),即窗口的一半取整。取其中最小者作为该像素(c,r)的兴趣值。

(2)给定一经验阈值,将兴趣值大于该阈值的点作为候选点。阈值的原则应以候选点中包括的所需要的特征点而又不含过多的非特征点为原则。

(3)选取候选点中的极值点作为特征点。在一定大小的窗口内,将候选点中兴趣值不是最大者均去掉,仅留下一个兴趣值最大者,该像素即为一个特征点(抑制局部非最大)。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 功能:代码 5-25 moravec 角点检测
// 作者:朱伟 zhu1988wei@163.com
// 来源:《OpenCV图像处理编程实例》
// 博客:http://blog.csdn.net/zhuwei1988
// 更新:2016-8-1
// 说明:版权所有,引用或摘录请联系作者,并按照上面格式注明出处,谢谢。//
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
// Moravec Corners角点检测
cv::Mat MoravecCorners(cv::Mat srcImage, int kSize, int threshold) {
cv::Mat resMorMat = srcImage.clone();
// 获取初始化参数信息
int r = kSize / 2;
const int nRows = srcImage.rows;
const int nCols = srcImage.cols;
int nConut = 0;
Point * pPoint = new Point[ nRows * nCols ];
// 图像遍历
for (int i = r; i < srcImage.rows - r; i++) {
for (int j = r; j < srcImage.cols - r; j++) {
int wV1, wV2, wV3, wV4;
wV1 = wV2 = wV3 = wV4 = 0;
// 计算水平方向窗内兴趣值
for (int k = -r; k < r; k++)
wV1 += (srcImage.at<uchar>(i, j + k) - srcImage.at<uchar>(i, j + k + 1)) *
(srcImage.at<uchar>(i, j + k) - srcImage.at<uchar>(i, j + k + 1));
// 计算垂直方向窗内兴趣值
for (int k = -r; k < r; k++)
wV2 += (srcImage.at<uchar>(i + k, j) - srcImage.at<uchar>(i + k + 1, j)) *
(srcImage.at<uchar>(i + k, j) - srcImage.at<uchar>(i + k + 1, j));
// 计算45度方向窗内兴趣值
for (int k = -r; k < r; k++)
wV3 +=
(srcImage.at<uchar>(i + k, j + k) - srcImage.at<uchar>(i + k + 1, j + k + 1)) *
(srcImage.at<uchar>(i + k, j + k) - srcImage.at<uchar>(i + k + 1, j + k + 1));
// 计算135度方向窗内兴趣值
for (int k = -r; k < r; k++)
wV4 +=
(srcImage.at<uchar>(i + k, j - k) - srcImage.at<uchar>(i + k + 1, j - k - 1)) *
(srcImage.at<uchar>(i + k, j - k) - srcImage.at<uchar>(i + k + 1, j - k - 1));
// std::cout << wV1 << wV2 << wV3 << wV4 << std::endl;
// 取其中的最小值作为该像素点的最终兴趣值
int value = min(min(wV1, wV2), min(wV3, wV4));
// 若兴趣值大于阈值,则将点的坐标存入数组中
if (value > threshold) {
pPoint[ nConut ] = Point(j, i);
nConut++;
}
}
}
//绘制兴趣点
for (int i = 0; i < nConut; i++)
cv::circle(resMorMat, pPoint[ i ], 5, cv::Scalar(255, 0, 0));
return resMorMat;
}

int main() {
cv::Mat srcImage = imread("circles.jpg", 0);
if (!srcImage.data)
return -1;
cv::Mat resMorMat = MoravecCorners(srcImage, 5, 10000);
cv::imshow("srcImage", srcImage);
cv::imshow("resMorMat", resMorMat);
cv::waitKey(0);
return 0;
}

效果为

moravec


OpenCV4入门教程107:moravec角点检测
https://blog.jackeylea.com/opencv/opencv-moravec-corner-detection/
作者
JackeyLea
发布于
2020年10月5日
许可协议