亚像素提取 图像处理
2016-12-14 11:31:00 weixin_34239169 阅读数 6

图像处理之角点检测与亚像素角点定位

 

角点是图像中亮度变化最强地方反映了图像的本质特征,提取图像中的角点可以有效提高图像处理速度与精准度。所以对于整张图像来说特别重要,角点检测与提取的越准确图像处理与分析结果就越接近真实。同时角点检测对真实环境下的对象识别、对象匹配都起到决定性作用。Harris角点检测是图像处理中角点提取的经典算法之一,应用范围广发,在经典的SIFT特征提取算法中Harris角点检测起到关键作用。通常对角点检测算法都有如下要求:


1. 基于灰度图像、能够自动调整运行稳定,检测出角点的数目。

2. 对噪声不敏感、有一定的噪声抑制,有较强的角点角点检测能力。

3. 准确性够高,能够正确发现角点位置

4. 算法尽可能的快与运行时间短


Harris角点检测基本上满足了上述四点要求,所以被广发应用,除了Harris角点检测,另外一种常见的角点检测算法-Shi-Tomasi角点检测也得到了广发应用,OpenCV中对这两种算法均有实现API可以调用。关于Harris角点检测原理可以看我之前写的博文:

http://blog.csdn.net/jia20003/article/details/16908661

关于Shi-Tomasi角点检测,与Harris角点检测唯一不同就是在计算角点响应值R上面。


然后根据输入的阈值T大于该阈值的R对应像素点即为图像中角点位置坐标。此刻坐标往往都是整数出现,而在真实的世界中坐标多数时候都不是整数,假设我们计算出来的角点位置P(34, 189)而实际上准确角点位置是P(34.278, 189.706)这样带小数的位置,而这样的准确位置寻找过程就叫做子像素定位或者亚像素定位。这一步在SURF与SIFT算法中都有应用而且非常重要。常见的亚像素级别精准定位方法有三类:

1. 基于插值方法

2. 基于几何矩寻找方法

3. 拟合方法 - 比较常用

拟合方法中根据使用的公式不同可以分为高斯曲面拟合与多项式拟合等等。以高斯拟合为例


这样就求出了亚像素的位置。使用亚像素位置进行计算得到结果将更加准确,对图像特征提取、匹配结果效果显著。OpenCV中已经对角点检测实现了亚像素级别的API可以调用。

代码演示

OpenCV亚像素角点检测例子:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat src, gray_src;
int max_corners = 10;
int max_trackbar = 30;
const char* output_title = "subpxiel-result";
void GoodFeature2Track_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("D:/vcprojects/images/home.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow("input", src);

	createTrackbar("Corners:", output_title, &max_corners, max_trackbar, GoodFeature2Track_Demo);
	GoodFeature2Track_Demo(0, 0);

	waitKey(0);
	return 0;
}

void GoodFeature2Track_Demo(int, void*) {
	if (max_corners < 1) {
		max_corners = 1;
	}
	vector<Point2f> corners;
	double qualityLevel = 0.01;
	double minDistance = 10;
	int blockSize = 3;
	double k = 0.04;
	goodFeaturesToTrack(gray_src, corners, max_corners, qualityLevel, minDistance, Mat(), blockSize, false, k);
	cout << "number of corners : " << corners.size() << endl;
	Mat copy = src.clone();
	for (size_t t = 0; t < corners.size(); t++) {
		circle(copy, corners[t], 4, Scalar(255, 0, 0), 2, 8, 0);
	}
	imshow(output_title, copy);

	// locate corner point on sub pixel level
	Size winSize = Size(5, 5);
	Size zerozone = Size(-1, -1);
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
	cornerSubPix(gray_src, corners, winSize, zerozone, criteria);
	for (size_t t = 0; t < corners.size(); t++) {
		cout << (t+1) << ".point[x, y]=" << corners[t].x << "," << corners[t].y << endl;
	}

	return;
}

原图如下:


运行结果:


转载请注明来自【jia20003】的博客!


2016-12-14 11:31:09 jia20003 阅读数 10141

图像处理之角点检测与亚像素角点定位

 

角点是图像中亮度变化最强地方反映了图像的本质特征,提取图像中的角点可以有效提高图像处理速度与精准度。所以对于整张图像来说特别重要,角点检测与提取的越准确图像处理与分析结果就越接近真实。同时角点检测对真实环境下的对象识别、对象匹配都起到决定性作用。Harris角点检测是图像处理中角点提取的经典算法之一,应用范围广发,在经典的SIFT特征提取算法中Harris角点检测起到关键作用。通常对角点检测算法都有如下要求:


1. 基于灰度图像、能够自动调整运行稳定,检测出角点的数目。

2. 对噪声不敏感、有一定的噪声抑制,有较强的角点角点检测能力。

3. 准确性够高,能够正确发现角点位置

4. 算法尽可能的快与运行时间短


Harris角点检测基本上满足了上述四点要求,所以被广发应用,除了Harris角点检测,另外一种常见的角点检测算法-Shi-Tomasi角点检测也得到了广发应用,OpenCV中对这两种算法均有实现API可以调用。关于Harris角点检测原理可以看我之前写的博文:

http://blog.csdn.net/jia20003/article/details/16908661

关于Shi-Tomasi角点检测,与Harris角点检测唯一不同就是在计算角点响应值R上面。


然后根据输入的阈值T大于该阈值的R对应像素点即为图像中角点位置坐标。此刻坐标往往都是整数出现,而在真实的世界中坐标多数时候都不是整数,假设我们计算出来的角点位置P(34, 189)而实际上准确角点位置是P(34.278, 189.706)这样带小数的位置,而这样的准确位置寻找过程就叫做子像素定位或者亚像素定位。这一步在SURF与SIFT算法中都有应用而且非常重要。常见的亚像素级别精准定位方法有三类:

1. 基于插值方法

2. 基于几何矩寻找方法

3. 拟合方法 - 比较常用

拟合方法中根据使用的公式不同可以分为高斯曲面拟合与多项式拟合等等。以高斯拟合为例


这样就求出了亚像素的位置。使用亚像素位置进行计算得到结果将更加准确,对图像特征提取、匹配结果效果显著。OpenCV中已经对角点检测实现了亚像素级别的API可以调用。

代码演示

OpenCV亚像素角点检测例子:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat src, gray_src;
int max_corners = 10;
int max_trackbar = 30;
const char* output_title = "subpxiel-result";
void GoodFeature2Track_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("D:/vcprojects/images/home.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	imshow("input", src);

	createTrackbar("Corners:", output_title, &max_corners, max_trackbar, GoodFeature2Track_Demo);
	GoodFeature2Track_Demo(0, 0);

	waitKey(0);
	return 0;
}

void GoodFeature2Track_Demo(int, void*) {
	if (max_corners < 1) {
		max_corners = 1;
	}
	vector<Point2f> corners;
	double qualityLevel = 0.01;
	double minDistance = 10;
	int blockSize = 3;
	double k = 0.04;
	goodFeaturesToTrack(gray_src, corners, max_corners, qualityLevel, minDistance, Mat(), blockSize, false, k);
	cout << "number of corners : " << corners.size() << endl;
	Mat copy = src.clone();
	for (size_t t = 0; t < corners.size(); t++) {
		circle(copy, corners[t], 4, Scalar(255, 0, 0), 2, 8, 0);
	}
	imshow(output_title, copy);

	// locate corner point on sub pixel level
	Size winSize = Size(5, 5);
	Size zerozone = Size(-1, -1);
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
	cornerSubPix(gray_src, corners, winSize, zerozone, criteria);
	for (size_t t = 0; t < corners.size(); t++) {
		cout << (t+1) << ".point[x, y]=" << corners[t].x << "," << corners[t].y << endl;
	}

	return;
}

原图如下:


运行结果:


转载请注明来自【jia20003】的博客!


2017-06-09 17:15:12 SoaringLee_fighting 阅读数 1181

###Date: 2017/6/9

###Author: Soaringlee

参考:http://blog.csdn.net/sunshine_in_moon/article/details/45440111

http://blog.csdn.net/jia20003/article/details/53636284

角点是图像中亮度变化最强地方反映了图像的本质特征,提取图像中的角点可以有效提高图像处理速度与精准度。所以对于整张图像来说特别重要,角点检测与提取的越准确图像处理与分析结果就越接近真实。同时角点检测对真实环境下的对象识别、对象匹配都起到决定性作用。Harris角点检测是图像处理中角点提取的经典算法之一,应用范围广发,在经典的SIFT特征提取算法中Harris角点检测起到关键作用。通常对角点检测算法都有如下要求:


1. 基于灰度图像、能够自动调整运行稳定,检测出角点的数目。

2. 对噪声不敏感、有一定的噪声抑制,有较强的角点角点检测能力。

3. 准确性够高,能够正确发现角点位置

4. 算法尽可能的快与运行时间短


Harris角点检测基本上满足了上述四点要求,所以被广发应用,除了Harris角点检测,另外一种常见的角点检测算法-Shi-Tomasi角点检测也得到了广发应用,OpenCV中对这两种算法均有实现API可以调用。关于Harris角点检测原理可以看我之前写的博文:

http://blog.csdn.net/jia20003/article/details/16908661

关于Shi-Tomasi角点检测,与Harris角点检测唯一不同就是在计算角点响应值R上面。


然后根据输入的阈值T大于该阈值的R对应像素点即为图像中角点位置坐标。此刻坐标往往都是整数出现,而在真实的世界中坐标多数时候都不是整数,假设我们计算出来的角点位置P(34, 189)而实际上准确角点位置是P(34.278, 189.706)这样带小数的位置,而这样的准确位置寻找过程就叫做子像素定位或者亚像素定位。这一步在SURF与SIFT算法中都有应用而且非常重要。常见的亚像素级别精准定位方法有三类:

1. 基于插值方法

2. 基于几何矩寻找方法

3. 拟合方法 - 比较常用

拟合方法中根据使用的公式不同可以分为高斯曲面拟合与多项式拟合等等。以高斯拟合为例


这样就求出了亚像素的位置。使用亚像素位置进行计算得到结果将更加准确,对图像特征提取、匹配结果效果显著。


除了利用Harris进行角点检测利用Shi-Tomasi方法进行角点检测外,还可以使用cornerEigenValsAndVecs()函数和cornerMinEigenVal()函数自定义角点检测函数。如果对角点的精度有更高的要求,可以用cornerSubPix()函数将角点定位到子像素,从而取得亚像素级别的角点检测效果。

cornerSubPix()函数

(1)函数原型

cornerSubPix()函数在角点检测中精确化角点位置,其函数原型如下:

  1. C++: void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);  
  2. C: void cvFindCornerSubPix(const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria);  
[cpp] view plain copy
  1. C++: void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);  
  2. C: void cvFindCornerSubPix(const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria);  

(2)函数参数

函数参数说明如下:

image:输入图像

corners:输入角点的初始坐标以及精准化后的坐标用于输出。

winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为的搜索窗口将被使用。

zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程


2018-01-21 13:45:57 fengye2two 阅读数 3835

首先感谢以下两位的博文帮助我的理解。

(1)lwzkiller            http://blog.csdn.net/lwzkiller/article/details/54633670

(2)青雲-吾道乐途   http://blog.csdn.net/qq_37059483/article/details/77852187

1. 何为角点?

下面有两幅不同视角的图像,通过找出对应的角点进行匹配。


再看下图所示,放大图像的两处角点区域:


我们可以直观的概括下角点所具有的特征:

>轮廓之间的交点;

>对于同一场景,即使视角发生变化,通常具备稳定性质的特征;

>该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化;


2. 角点检测算法基本思想是什么?

算法基本思想是使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。


3.如何用数学方法去刻画角点特征?

当窗口发生[u,v]移动时,那么滑动前与滑动后对应的窗口中的像素点灰度变化描述如下:


公式解释:

>[u,v]是窗口的偏移量

>(x,y)是窗口内所对应的像素坐标位置,窗口有多大,就有多少个位置

>w(x,y)是窗口函数,最简单情形就是窗口内的所有像素所对应的w权重系数均为1。但有时候,我们会将w(x,y)函数设定为以窗口中心为原点的二元正态分布。如果窗口中心点是角点时,移动前与移动后,该点的灰度变化应该最为剧烈,所以该点权重系数可以设定大些,表示窗口移动时,该点在灰度变化贡献较大;而离窗口中心(角点)较远的点,这些点的灰度变化几近平缓,这些点的权重系数,可以设定小点,以示该点对灰度变化贡献较小,那么我们自然想到使用二元高斯函数来表示窗口函数,这里仅是个人理解,大家可以参考下。

所以通常窗口函数有如下两种形式:


根据上述表达式,当窗口处在平坦区域上滑动,可以想象的到,灰度不会发生变化,那么E(u,v) = 0;如果窗口处在比纹理比较丰富的区域上滑动,那么灰度变化会很大。算法最终思想就是计算灰度发生较大变化时所对应的位置,当然这个较大是指针任意方向上的滑动,并非单指某个方向。


4.E(u,v)表达式进一步演化

首先需要了解泰勒公式,任何一个函数表达式,均可有泰勒公式进行展开,以逼近原函数,我们可以对下面函数进行一阶展开(如果对泰勒公式忘记了,可以翻翻本科所学的高等数学)


那么,






所以E(u,v)表达式可以更新为:


这里矩阵M为,


5.矩阵M的关键性

难道我们是直接求上述的E(u,v)值来判断角点吗?Harris角点检测并没有这样做,而是通过对窗口内的每个像素的x方向上的梯度与y方向上的梯度进行统计分析。这里以Ix和Iy为坐标轴,因此每个像素的梯度坐标可以表示成(Ix,Iy)。针对平坦区域,边缘区域以及角点区域三种情形进行分析:


下图是对这三种情况窗口中的对应像素的梯度分布进行绘制:


如果使用椭圆进行数据集表示,则绘制图示如下:


不知道大家有没有注意到这三种区域的特点,平坦区域上的每个像素点所对应的(IX,IY)坐标分布在原点附近,其实也很好理解,针对平坦区域的像素点,他们的梯度方向虽然各异,但是其幅值都不是很大,所以均聚集在原点附近;边缘区域有一坐标轴分布较散,至于是哪一个坐标上的数据分布较散不能一概而论,这要视边缘在图像上的具体位置而定,如果边缘是水平或者垂直方向,那么Iy轴方向或者Ix方向上的数据分布就比较散;角点区域的x、y方向上的梯度分布都比较散。我们是不是可以根据这些特征来判断哪些区域存在角点呢?

虽然我们利用E(u,v)来描述角点的基本思想,然而最终我们仅仅使用的是矩阵M。让我们看看矩阵M形式,是不是跟协方差矩阵形式很像,像归像,但是还是有些不同,哪儿不同?一般协方差矩阵对应维的随机变量需要减去该维随机变量的均值,但矩阵M中并没有这样做,所以在矩阵M里,我们先进行各维的均值化处理,那么各维所对应的随机变量的均值为0,协方差矩阵就大大简化了,简化的最终结果就是矩阵M,是否明白了?我们的目的是分析数据的主要成分,相信了解PCA原理的,应该都了解均值化的作用。

如果我们对协方差矩阵M进行对角化,很明显,特征值就是主分量上的方差,这点大家应该明白吧?不明白的话可以复习下PCA原理。如果存在两个主分量所对应的特征值都比较大,说明什么? 像素点的梯度分布比较散,梯度变化程度比较大,符合角点在窗口区域的特点;如果是平坦区域,那么像素点的梯度所构成的点集比较集中在原点附近,因为窗口区域内的像素点的梯度幅值非常小,此时矩阵M的对角化的两个特征值比较小;如果是边缘区域,在计算像素点的x、y方向上的梯度时,边缘上的像素点的某个方向的梯度幅值变化比较明显,另一个方向上的梯度幅值变化较弱,其余部分的点都还是集中原点附近,这样M对角化后的两个特征值理论应该是一个比较大,一个比较小,当然对于边缘这种情况,可能是呈45°的边缘,致使计算出的特征值并不是都特别的大,总之跟含有角点的窗口的分布情况还是不同的。

注:M为协方差矩阵,需要大家自己去理解下,窗口中的像素集构成一个矩阵(2*n,假设这里有n个像素点),使用该矩阵乘以该矩阵的转置,即是协方差矩阵

因此可以得出下列结论:

>特征值都比较大时,即窗口中含有角点

>特征值一个较大,一个较小,窗口中含有边缘

>特征值都比较小,窗口处在平坦区域


6. 如何度量角点响应?

(1)Harris用下面表达式进行度量:


其中k是常量,一般取值为0.04~0.06,这个参数仅仅是这个函数的一个系数,它的存在只是调节函数的形状而已。

但是为什么会使用这样的表达式呢?一下子是不是感觉很难理解?其实也不难理解,函数表达式一旦出来,我们就可以绘制它的图像,而这个函数图形正好满足上面几个区域的特征。 通过绘制函数图像,直观上更能理解。绘制的R函数图像如下:



所以说难点不在于理解这个函数表达式,而在于如何创造出这个函数表达式。Harris对很多函数模型非常了解,对于创造出这样的一个函数表达式,易如反掌。最后设定R的阈值,进行角点判断。当然其中还有些后处理步骤,比如说角点的极大值抑制(dilate)。

(2)Shi-Tomasi用下面表达式进行度量:

如果打分超过阈值,我们就认为它是一个角点。我们可以把它绘制到 λ1 ~λ2 空间中,就会得到下图:


从这幅图中,我们可以看出来只有当 λ1 和 λ2 都大于最小值时,才被认为是角点(绿色区域)。 
  OpenCV 提供了函数: goodFeaturesToTrack()。这个函数可以帮我们使用 Shi-Tomasi 方法获取图像中 N 个最好的角点(也可以通过改变参数来使用 Harris 角点检测算法)。通常情况下,输入的应该是灰度图像。然后确定你想要检测到的角点数目。再设置角点的质量水平, 0到 1 之间。它代表了角点的最低质量,低于这个数的所有角点都会被忽略(阈值为maxEigVal*qualityLevel)。最后在设置两个角点之间的最短欧式距离。根据这些信息,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略,然后再把合格角点按角点质量进行降序排列。函数会采用角点质量最高的那个角点(排序后的第一个),然后将它附近(最小距离之内)的角点删掉。按着这样的方式最后返回 N 个最佳角点。

  1. //goodFeaturesToTrack有比cornerHarries更多的控制参数,函数原型:  
  2.   
  3. void goodFeaturesToTrack( InputArray image, OutputArray corners,  
  4.                                     int maxCorners, double qualityLevel, double minDistance,  
  5.                                     InputArray mask=noArray(), int blockSize=3,  
  6.                                     bool useHarrisDetector=falsedouble k=0.04);  
  7.   
  8. /*第一个参数image:8位或32位单通道灰度图像; 
  9.   第二个参数corners: 位置点向量,保存的是检测到角点的坐标; 
  10.   第三个参数maxCorners: 定义可以检测到的角点的数量的最大值; 
  11.   第四个参数qualityLevel: 检测到的角点的质量等级,角点特征值小于qualityLevel*最大特征 
  12.   值的点将被舍弃; 
  13.   第五个参数minDistance: 两个角点间最小间距,以像素为单位; 
  14.   第六个参数mask: 指定检测区域,若检测整幅图像,mask置为空Mat(); 
  15.   第七个参数blockSize: 计算协方差矩阵时窗口大小; 
  16.   第八个参数useHarrisDector: 是否使用Harris角点检测,为false,则使用Shi-Tomasi算子; 
  17.   第九个参数K: 留给Harris角点检测算子用的中间参数,一般取经验值0.04~0.06.第8个参数为false时,改参数不起作用; 
  18. */  

goodFeaturesToTrack函数的定义在imgproc文件的featureselect.cpp中,下面给出了goodFeaturesToTrack函数的详细注释。

  1. void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,  
  2.                                 int maxCorners, double qualityLevel, double minDistance,  
  3.                                 InputArray _mask, int blockSize,  
  4.                                 bool useHarrisDetector, double harrisK )  
  5. {  
  6.     //如果需要对_image全图操作,则给_mask传入cv::Mat(),否则传入感兴趣区域  
  7.     Mat image = _image.getMat(), mask = _mask.getMat();  
  8.   
  9.     CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 ); //对参数有一些基本要求  
  10.   
  11.     CV_Assert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()) );  
  12.   
  13.     Mat eig, tmp;  //eig存储每个像素协方差矩阵的最小特征值,tmp用来保存经膨胀后的eig  
  14.     if( useHarrisDetector )  
  15.         cornerHarris( image, eig, blockSize, 3 ,harrisk ); //blockSize是计算2*2协方差矩阵的窗口大小,sobel算子窗口为3,harrisk是计算Harris角点时需要的值  
  16.     else  
  17.         cornerMinEigenVal( image, eig, blockSize, 3);   //计算每个像素对应的协方差矩阵的最小特征值,保存在eig中  
  18.   
  19.     double maxVal = 0;  
  20.     minMaxLoc( eig, 0, &maxVal, 0, 0, mask );   //maxVal保存了eig的最大值  
  21.     threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO);  //阈值设置为maxVal乘以qualityLevel,大于此阈值的保持不变,小于此阈值的都设为0  
  22.   
  23.     //默认用3*3的核膨胀,膨胀之后,除了局部最大值点和原来相同,其他非局部最大值点被  
  24.     //3*3邻域内的最大值点取代,如不理解,可看一下灰度图像的膨胀原理  
  25.     //为什么膨胀操作:膨胀的本质是用当前像素周围的最大值替代当前像素值,  
  26.     //因此,通过膨胀前后比较能取得局部角点响应最大的点。  

  27.       
  28.     dilate( eig, tmp, Mat() );   //tmp中保存了膨胀之后的eig  
  29.   
  30.     Size imgsize = image.size();  
  31.   
  32.     vector<const float*> tmpCorners;  //存放粗选出的角点地址  
  33.   
  34.     // collect list of pointers to features - put them into temporary image  
  35.     forint y = 1; y < imgsize.height -1; y++)  
  36.     {  
  37.         const float* eig_data = (const float*)eig.ptr(y);  //获得eig第y行的首地址  
  38.         const float* tmp_data = (const float*)tmp.ptr(y);  //获得tmp第y行的首地址  
  39.         const uchar* mask_data = mask.data ? mask.ptr(y) : 0;  
  40.   
  41.         forint x =1; x < imgsize.width - 1; x++)  
  42.         {  
  43.             float val = eig_data[x];  
  44.             if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) ) //val == tmp_data[x], 说明这是局部极大值  
  45.                 tmpCorners.push_back(eig_data + x);  //保存其位置  
  46.         }  
  47.     }  
  48.   
  49.     //--------------此分割线以上是根据特征值粗选出的角点,我们称之为弱角点-----------//  
  50.     //--------------此分割线以下还要根据minDistance进一步筛选角点,仍然能存活下来的我们称之为强角点------------//  
  51.       
  52.     sort( tmpCorners, greaterThanPtr<float>() ); //按特征值降序排列,注意这一步很重要,后面的很多编程思路都是建立在这给降序排列的基础上  
  53.     vector<Point2f> corners;  
  54.     size_t i, j, total = tmpCorners.size(), ncorners = 0;  
  55.   
  56.     //下面的程序有点稍微理解,需要自己想想  
  57.     if(minDistance >= 1)  
  58.     {  
  59.         //Partition the image into larger grids  
  60.         int w = image.cols;  
  61.         int h = image.rows;  
  62.   
  63.         const int cell_size = cvRound(minDistance);  //向最近的整数取整  
  64.   
  65.         //这里根据cell_size构建了一个矩形窗口grid(虽然下卖弄的grid定义的是vector<vector>,而并不是我们这里说的矩形窗口,但是为了便于理解,还是将grid想象成一个grid_width*grid_width的矩形窗口比较好),除以cell_size说明grid窗口里相差一个像素相当于_image里相差minDistance个像素,至于为什么加上cell_size - 1,后面会讲  
  66.           
  67.         const int grid_width = (w + cell_size - 1) / cell_size;  
  68.         const int grid_width = (h + cell_size - 1) / cell_size;  
  69.   
  70.         std::vector<std::vector<Point2f> > grid(grid_width*grid_width); //vector里面是vector,grid用来保存获得的强角点坐标  
  71.   
  72.         minDistance *= minDistance;  //平方,方便后面计算,省的开根号  
  73.   
  74.         for( i = 0; i < total; i++)  
  75.          {  
  76.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  //tmpCorners中保存了角点的地址,eig.data返回了内存块的地址  
  77.             int y = (int)(ofs / eig.step);  //角点在原图像中的行  
  78.             int x = (int)((ofs - y*eig.step)/sizeof(float)); //在原图像中的列  
  79.   
  80.             bool good = true;  //先认为当前角点能接收考验,即能被保留下来  
  81.   
  82.             int x_cell = x / cell_size;  //x_cell, y_cell是角点(y,x)在grid中的坐标  
  83.             int y_cell = y_cell - 1;     //现在知道为什么前面grid_width定义时要加上cell_size - 1了吧,这是为了使得(y,x)在grid中的4邻域像素都存在,也就是说(y_cell, x_cell)不会成为边界像素  
  84.             int x2 = x_cell + 1;  
  85.             int y2 = y_cell + 1;  
  86.   
  87.             // boundary check,再次确认x1,y1,x2,或y2不会超出grid边界  
  88.             x1 = std::max(0, x1);   //比较0和x1的大小  
  89.             y1 = std::max(0, y1);  
  90.             x2 = std::min(grid_width-1, x2);  
  91.             y2 = std::min(grid_height-1, y2);  
  92.   
  93.             //记住grid相差一个像素,相当于_image相差了minDistance个像素  
  94.             forint yy = y1; yy <= y2; yy++)  //行  
  95.             {  
  96.                 forint xx = x1; xx <= x2; xx++)  
  97.                 {  
  98.                     vector <Point2f> &m = grid[yy*grid_width + xx]; //引用  
  99.                     if( m.size() )  //如果(y_cell,x_cell的4邻域像素,也就是(y,x)的minDistance邻域像素中已有被保留的强角点)  
  100.                     {  
  101.                         for(j = 0; j < m.size(); j++)  //当前角点周围的强角点都拉出来跟当前角点比一比  
  102.                         {  
  103.                             float dx = x - m[j].x;  
  104.                             float dy = y - m[j].y;  
  105.                             //注意如果(y,x)的minDistance邻域像素中已有被保留的强角点,则说明该强角点是在(y,x)之前就被测试过的,又因为tmpCorners中已按照特征值降序排列(特征值越大说明角点越好),这说明先测试的一定是更好的角点,也就是已保存的角点一定好于当前角点,所以这里只要比较距离,如果距离满足条件,可以立马扔掉当前测试的角点  
  106.                             if( dx*dx + dy*dy < minDistance )  
  107.                             {  
  108.                                 good = false;  
  109.                                 goto break_out;  
  110.                             }  
  111.                         }  
  112.                     }  
  113.                 } //列  
  114.             } //行  
  115.             break_out;  
  116.             if(good)  
  117.             {  
  118.                 //printf("%d: %d%d ->%d %d, %d, %d -- %d %d %d %d, %d %d, c=%d\n",  
  119.                 //  i,x, y, x_cell, y_cell, (int)minDistance, cell_size,x1,y1,x2,y2,grid_width,grid_height,c);  
  120.                 grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x,(float)y));  
  121.   
  122.                 corners.push_back(Point2f((float)x, (float)y));  
  123.                 ++ncorners;  
  124.   
  125.                 if(maxCorners > 0 && (int)ncorners == maxCorners ) //用于前面已按降序排列,当ncorners超过maxCorners的时候跳出循环直接忽略tmpCorners中剩下的角点,反正剩下的角点越来越弱  
  126.                     break;  
  127.             }  
  128.          }     
  129.     }  
  130.     else    //除了像素本身,没有哪个邻域像素能与当前像素满足minDistance < 1,因此直接保存粗选的角点  
  131.     {  
  132.         for( i = 0; i < total; i++)  
  133.         {  
  134.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  
  135.             int y = (int)(ofs / eig.step);  //粗选的角点在原图像中的行  
  136.             int x = (int)((ofs - y*eig.step)/sizeof(float));  //在图像中的列  
  137.   
  138.             corner.push_back(Point2f((float)x, (float)y));  
  139.             ++ncorners;  
  140.             if( maxCorners > 0 && (int)ncorners == maxCorners )  
  141.                 break;  
  142.         }      
  143.     }  
  144.   
  145.     Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type : CV_32F);  
  146.     /* 
  147.     for( i = 0; i < total; i++ )  
  148.     {  
  149.         int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  
  150.         int y = (int)(ofs / eig.step);  
  151.         int x = (int)((ofs - y*eig.step)/sizeof(float));  
  152.   
  153.         if( minDistance > 0 )  
  154.         {  
  155.             for( j = 0; j < ncorners; j++ )  
  156.             {  
  157.                 float dx = x - corners[j].x;  
  158.                 float dy = y - corners[j].y;  
  159.                 if( dx*dx + dy*dy < minDistance )  
  160.                     break;  
  161.             }  
  162.             if( j < ncorners )  
  163.                 continue;  
  164.         }  
  165.   
  166.         corners.push_back(Point2f((float)x, (float)y));  
  167.         ++ncorners;  
  168.         if( maxCorners > 0 && (int)ncorners == maxCorners )  
  169.             break;  
  170.     }  
  171.      */  
  172. }  

7. 亚像素检测

根据输入的阈值T大于该阈值的R对应像素点即为图像中角点位置坐标。此刻坐标往往都是整数出现,而在真实的世界中坐标多数时候都不是整数,假设我们计算出来的角点位置P(34, 189)而实际上准确角点位置是P(34.278, 189.706)这样带小数的位置,而这样的准确位置寻找过程就叫做子像素定位或者亚像素定位。这一步在SURF与SIFT算法中都有应用而且非常重要。常见的亚像素级别精准定位方法有三类:

1. 基于插值方法

2. 基于几何矩寻找方法

3. 拟合方法 - 比较常用

拟合方法中根据使用的公式不同可以分为高斯曲面拟合与多项式拟合等等。以高斯拟合为例


这样就求出了亚像素的位置。使用亚像素位置进行计算得到结果将更加准确,对图像特征提取、匹配结果效果显著。





2014-04-24 23:44:34 sunbright 阅读数 2936

《视觉测量原理与方法》P9:

亚像素图像处理:质心法、基于灰度相关匹配法、基于边缘拟合法。能够稳定实现0.01像素的细分精度。

1、质心法:用图像特征区域的质心作为特征点。

65页还提供了2种质心法:

阈值质心法:,T为灰度阈值

平方加权质心法:

2、基于灰度相关匹配:已知图像特征模板像素灰度M和待处理的图像像素灰度I作相关运算。抗干扰能力和精度都比较好,计算量大,需采用金字塔。

3、基于边缘的拟合法:采用边缘提取算法提取特征点的图像边缘,并跟踪边缘曲线,用曲线拟合得到特征点的位置坐标。对图像质量和场景有严格要求,边缘提取要求图像有足够的对比度和很好的信噪比,边缘跟踪需要场景模式尽量简单,算法稳健。比质心法和相关匹配法抗干扰性较差,只能用在图像质量理想且编码模式简单的场合。


视觉测量常用坐标系:

常用坐标系:


1、图像坐标系:图像平面上,分为像素坐标系和像面坐标系。

      像素坐标系:原点-图像左上角,坐标单位-像素,像素点坐标(u,v)-该像素点在图像的行数和列数;

     像面坐标系:原点-光轴与像平面的交点,坐标单位-公制(如mm),x轴和y轴与图像像素坐标系的u,v平行。


2、摄像机坐标系(内部方位):固定在摄像机上,原点-光心,坐标轴-传感器的两个轴向,x和y轴与图像坐标系x和y轴平行,z轴与摄像机光轴重合,垂直于图像平面。

3、世界坐标系(外部方位):独立于摄像机坐标系。根据环境场景和对象条件自行定义,用来描述环境中物体位置。

4、物体坐标系:世界坐标系中,为物体建立的子坐标系。

转换关系


1、图像坐标——>摄像机坐标系下的空间三维坐标

,c是摄像机有效焦距。

2、空间物点P在摄像机坐标系下的坐标,转换为世界坐标系下

可描述为一个3*3的旋转矩阵R和一个3*1的平移矩阵T,

3、由1和2推出图像坐标系与世界坐标系之间的关系:M1是内参矩阵(由摄像机光心坐标u,v和焦距c决定),M2是外参矩阵是摄像机相对世界坐标系的转换(由旋转矩阵R和平移矩阵T决定)




关于图像特征点提取

博文 来自: sdkdslx
没有更多推荐了,返回首页