版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。
教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客
教程C#版本请访问:EmguCV学习笔记 C# 目录-CSDN博客
笔者的博客网址:https://blog.csdn.net/uruseibest
教程配套文件及相关说明以及如何获得pdf教程和代码,请移步:EmguCV学习笔记
学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客
学习C#知识,请移步:C# 教程 目录_c#教程目录-CSDN博客
7.1 角点检测
角点是特征点的一种,是指在图像中具有尖锐变化的像素点,例如像素值最大或最小的点、线段的顶点、两直线的交点等,它通常具有一些特征,如灰度值变化较大、梯度变化较大、边缘交汇处等。在计算机视觉领域中,角点通常被用作特征点的一种,用于目标检测、图像配准、三维重建等任务中。
7.1.1 ConvertScaleAbs
CvInvoke.ConvertScaleAbs用于对图像进行线性变换并将结果转换为无符号的8位整型图像。它可以将图像中的像素值进行缩放和偏移,常用于图像增强、特征提取等任务中。该方法的声明如下:
public static void ConvertScaleAbs(
IInputArray src,
IOutputArray dst,
double scale,
double shift
)
参数说明:
- src:要进行线性变换的源图像。
- dst:变换后的目标图像。
- scale:变换中的缩放比例。
- shift:变换中的偏移量。
注意:ConvertScaleAbs方法只能将结果转换为无符号的8位整型图像,如果需要转换为其他类型的图像(比如CV32F、Cv16U等),可以使用CvInvoke.ConvertScale方法。
【代码位置:frmChapter7】Button1_Click
//ConvertScaleAbs
private void Button1_Click(object sender, EventArgs e)
{
Mat msrc = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.Color);
ImageBox1.Image = msrc;
Mat mdst = new Mat();
CvInvoke.ConvertScaleAbs(msrc, mdst, 1.5, 10);
ImageBox2.Image = mdst;
}
输出结果如下图所示:
图7-1 图像线性变换结果
可以看出,ConvertScaleAbs实际有两个作用:1、对图像像素点进行线性变换;2、对于非CV8U的图像,转为CV8U。
7.1.2 Normalize
CvInvoke.Normalize用于对图像进行归一化处理。它可以将图像中的像素值按照一定的规则进行缩放,使得像素值的范围在指定的范围内,常用于图像增强、特征提取等任务中。该方法的声明如下:
public static void Normalize(
IInputArray src,
IOutputArray dst,
double alpha = 1,
double beta = 0,
NormType normType = NormType.L2,
DepthType dType = DepthType.Default,
IInputArray mask = null
)
参数说明:
- src:要进行归一化处理的源图像。
- dst:归一化处理后的图像。
- alpha:下限范围。
- beta:上限范围,只在normType =NormType.MinMax时起作用。。
- normType:归一化处理中的算法类型,这是一个NormType枚举类型,可以选择NormType.MinMax、NormType.L1、NormType.L2等。当设置为MinMax时,Normalize()会把原矩阵中的值范围从最小值-最大值,按比例变换到alpha-beta的范围。
- dType:归一化处理后的目标图像的深度类型。
- mask:可选参数,表示要进行归一化处理的像素点的掩码图像,只有掩码图像中像素值为非零的像素点才会进行归一化处理。
【代码位置:frmChapter7】Button2_Click
//Normalize
private void Button2_Click(object sender, EventArgs e)
{
Mat msrc = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.Color);
ImageBox1.Image = msrc;
Mat mdst = new Mat();
CvInvoke.Normalize(msrc, mdst, 50, 200, NormType.MinMax);
ImageBox2.Image = mdst;
}
输出结果如下图所示:
图7-2 图像归一化处理
7.1.3 CornerHarris
Harris角点检测算法是一种经典的角点检测算法,由Chris Harris和Mike Stephens在1988年提出。该算法通过计算图像中每个像素的响应函数值,来确定图像中的角点位置。
在使用EmguCV进行图像处理时,CvInvoke.CornerHarris方法用于检测图像中的角点。该方法基于Harris角点检测算法,可以识别出图像中的角点位置,其声明如下:
public static void CornerHarris(
IInputArray image,
IOutputArray harrisResponse,
int blockSize,
int apertureSize = 3,
double k = 0.04,
BorderType borderType = BorderType.Reflect101
)
参数说明:
- image:待检测的图像。必须是CV8U或者CV32F的单通道图像。
- harrisResponse:输出的角点响应图像,存放Harris评价系数的矩阵,这是一个CV32F的单通道图像,和image图像大小一致。
- blockSize:以当前像素为中心的邻域大小。通常是一个大于1的奇数。
- apertureSize:Sobel算子的大小。通常是一个大于1的奇数。
- k:Harris评价系数的权重系数,通常取值范围为0.04- 0.06。
- borderType:边界模式,用于处理图像边界情况。
【代码位置:frmChapter7】Button3_Click
//CornerHarris
private void Button3_Click(object sender, EventArgs e)
{
Mat m = new Mat("c:\\learnEmgucv\\chess.jpg", ImreadModes.Color);
//获得灰度图像
Mat mgray = new Mat();
CvInvoke.CvtColor(m, mgray, ColorConversion.Bgr2Gray);
ImageBox1.Image = mgray;
//大小同mgray,存放评价系数的CV32F矩阵
Mat mhr = new Mat();
//检测角点
CvInvoke.CornerHarris(mgray, mhr, 2, 3, 0.04);
//归一化处理,之后处理的是存放评价系数的CV32F矩阵
Mat mn = new Mat();
CvInvoke.Normalize(mhr, mn, 0, 255, NormType.MinMax);
//图像转为CV8U
Mat mabs = new Mat();
CvInvoke.ConvertScaleAbs(mn, mabs, 1, 0);
//二值化
Mat madp = new Mat();
CvInvoke.Threshold(mabs, madp, 100, 255, ThresholdType.Binary);
//由于之前二值化了,这里只需要获得非0值的点
VectorOfPoint ps = new VectorOfPoint();
CvInvoke.FindNonZero(madp, ps);
//绘制出角点
Mat mdst = new Mat();
mdst = m.Clone();
for (int i = 0; i < ps.Size; i++)
CvInvoke.Circle(mdst, ps[i], 20, new MCvScalar(0, 0, 255), -1);
ImageBox2.Image = mdst;
}
输出结果如下图所示:
图7-3 棋盘上的角点
7.1.4 CornerSubPix
在数字图像处理领域中,亚像素级别(Subpixel Level)指的是比像素级别更细小的精度。像素级别指的是图像上的最小单位,通常是一个正方形的颜色区域。而亚像素级别则指的是像素内部的更细小的精度,例如像素内部的小数点位数。使用亚像素级别的精度可以提高图像处理的精度和准确度。例如,在角点检测任务中,使用亚像素级别的定位可以提高角点定位的精度,从而提高图像特征匹配的准确度。使用亚像素级别的精度需要借助于一些图像处理算法,例如插值算法、滤波算法等。
在使用EmguCV进行图像处理时,CvInvoke.CornerSubPix方法用于在亚像素级别上对角点进行精确化处理。该方法通过迭代优化提高角点的精度和准确性。该方法声明如下:
public static void CornerSubPix(
IInputArray image,
IInputOutputArray corners,
Size win,
Size zeroZone,
MCvTermCriteria criteria
)
参数说明:
- image:待处理的图像。
- corners:角点的位置数组,通常是通过CornerHarris等方法检测到的初始角点。这个参数既是输入也是输出。
- win:表示搜索窗口的大小。通常是一个大于1的奇数,表示在某个角点周围的邻域内进行亚像素级别的优化。
- zeroZone:表示搜索窗口中心的边界区域,通常是一个Size对象,用于将搜索窗口的中心排除在优化过程之外。
- criteria:表示优化过程的迭代终止准则,它是一个McvTermCriteria类,可以设置最大迭代次数和最小变化量的阈值。其中:
- maxIter:最大迭代次数,当迭代次数达到该值时,迭代终止。
- epsilon:迭代精度,当每一步迭代的变化量小于该值时,迭代终止。
下面的代码表示最大迭代次数为30次,每一步迭代的变化量小于0.01时,迭代终止:
Dim criteria As New MCvTermCriteria(30, 0.01)
【代码位置:frmChapter7】Button4_Click、getMax、outputMatdata32F1C、PointFToPoint
//CornerSubPix
private void Button4_Click(object sender, EventArgs e)
{
Mat m = new Mat("c:\\learnEmgucv\\chess.jpg", ImreadModes.Color);
//获得灰度图像
Mat mgray = new Mat();
CvInvoke.CvtColor(m, mgray, ColorConversion.Bgr2Gray);
ImageBox1.Image = mgray;
//大小同mgray,存放评价系数的CV32F矩阵
Mat mhr = new Mat();
//检测角点
CvInvoke.CornerHarris(mgray, mhr, 2, 3, 0.04);
VectorOfPointF vopf = new VectorOfPointF();
vopf = outputMatdata32F1C(mhr);
CvInvoke.CornerSubPix(mgray, vopf, new Size(5, 5), new Size(-1, -1), new MCvTermCriteria(100, 0.01));
//绘制出角点
Mat mdst = new Mat();
mdst = m.Clone();
for (int i = 0; i < vopf.Size; i++)
CvInvoke.Circle(mdst, PointFToPoint(vopf[i]), 20, new MCvScalar(0, 0, 255), -1);
ImageBox2.Image = mdst;
}
//获得矩阵中的最大值,用来后面计算阈值
private Double getMax(Mat m )
{
Double[] maxvalues;
Double[] minvalues;
Point[] maxposes;
Point[] minposes;
//只能查找每个通道的最大值和最小值,而不是查找每个通道的所有最大值和最小值
//最大值和最小值的位置,也只是返回最先的那个。
m.MinMax(out minvalues,out maxvalues, out minposes, out maxposes);
//返回矩阵中的最大值
return maxvalues[0];
}
//输出符合条件的点
private VectorOfPointF outputMatdata32F1C(Mat m)
{
//设置阈值
Double threshold = 0.01 * getMax(m);
//为了计算方便,将m转为Matrix
//这里m是32F,所以使用Single
Matrix<Single> matr = new Matrix<Single>(m.Size);
//Mat的CopyTo方法
m.CopyTo(matr);
VectorOfPointF corners = new VectorOfPointF();
int colcount = matr.Cols;
int rowcount = matr.Rows;
List<PointF> lstPf = new List<PointF>();
for (int i = 0; i < rowcount; i++)
{
for (int j = 0; j < colcount; j++)
{
Single outSingle = matr[i, j];
//存储大于阈值的点坐标
if (outSingle > threshold)
lstPf.Add(new PointF(j, i));
}
}
//获得VectorOfPointF
corners.Push(lstPf.ToArray());
return corners;
}
输出结果如下图所示:
图7-4 棋盘上的角点
【代码位置:frmChapter7】Button5_Click
//直接CornerHarris和CornerSubPix的比较
private void Button5_Click(object sender, EventArgs e)
{
Mat m = new Mat("c:\\learnEmgucv\\lena.jpg", ImreadModes.Color);
//获得灰度图像
Mat mgray = new Mat();
CvInvoke.CvtColor(m, mgray, ColorConversion.Bgr2Gray);
//大小同mgray,存放评价系数的CV32F矩阵
Mat mhr = new Mat();
//检测角点
CvInvoke.CornerHarris(mgray, mhr, 2, 3, 0.04);
//设定阈值
Double threshold = 0.01 * getMax(mhr);
//二值化,分离不符合阈值的点
Mat mtsd = new Mat();
CvInvoke.Threshold(mhr, mtsd, threshold, 255, ThresholdType.Binary);
//获得非0值的点
VectorOfPoint ps = new VectorOfPoint();
CvInvoke.FindNonZero(mtsd, ps);
//绘制出角点
Mat mdst = new Mat();
mdst = m.Clone();
for (int i = 0; i < ps.Size; i++)
CvInvoke.Circle(mdst, ps[i], 2, new MCvScalar(0, 0, 255), -1);
ImageBox1.Image = mdst;
VectorOfPointF vopf = new VectorOfPointF();
vopf = outputMatdata32F1C(mhr);
CvInvoke.CornerSubPix(mgray, vopf, new Size(5, 5), new Size(-1, -1), new MCvTermCriteria(100, 0.01));
//绘制出角点
Mat mdst2 = new Mat();
mdst2 = m.Clone();
for (int i = 0; i < vopf.Size; i++)
CvInvoke.Circle(mdst2, PointFToPoint(vopf[i]), 2, new MCvScalar(0, 0, 255), -1);
ImageBox2.Image = mdst2;
}
输出结果如下图所示:
图7-5 直接CornerHarris和CornerSubPix后的比较