EmguCV学习笔记 C# 7.1 角点检测

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。

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

)

参数说明:

  1. src:要进行线性变换的源图像。
  2. dst:变换后的目标图像。
  3. scale:变换中的缩放比例。
  4. 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

)

参数说明:

  1. src:要进行归一化处理的源图像。
  2. dst:归一化处理后的图像。
  3. alpha:下限范围。
  4. beta:上限范围,只在normType =NormType.MinMax时起作用。。
  5. normType:归一化处理中的算法类型,这是一个NormType枚举类型,可以选择NormType.MinMax、NormType.L1、NormType.L2等。当设置为MinMax时,Normalize()会把原矩阵中的值范围从最小值-最大值,按比例变换到alpha-beta的范围。
  6. dType:归一化处理后的目标图像的深度类型。
  7. 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

)

参数说明:

  1. image:待检测的图像。必须是CV8U或者CV32F的单通道图像。
  2. harrisResponse:输出的角点响应图像,存放Harris评价系数的矩阵,这是一个CV32F的单通道图像,和image图像大小一致。
  3. blockSize:以当前像素为中心的邻域大小。通常是一个大于1的奇数。
  4. apertureSize:Sobel算子的大小。通常是一个大于1的奇数。
  5. k:Harris评价系数的权重系数,通常取值范围为0.04- 0.06。
  6. 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

)

参数说明:

  1. image:待处理的图像。
  2. corners:角点的位置数组,通常是通过CornerHarris等方法检测到的初始角点。这个参数既是输入也是输出。
  3. win:表示搜索窗口的大小。通常是一个大于1的奇数,表示在某个角点周围的邻域内进行亚像素级别的优化。
  4. zeroZone:表示搜索窗口中心的边界区域,通常是一个Size对象,用于将搜索窗口的中心排除在优化过程之外。
  5. criteria:表示优化过程的迭代终止准则,它是一个McvTermCriteria类,可以设置最大迭代次数和最小变化量的阈值。其中:
    1. maxIter:最大迭代次数,当迭代次数达到该值时,迭代终止。
    2. 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

            //这里m32F,所以使用Single

            Matrix<Single> matr = new Matrix<Single>(m.Size);

            //MatCopyTo方法

            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

        //直接CornerHarrisCornerSubPix的比较

        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后的比较

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值