• OpenCV开发笔记(六十):红胖子8分钟带你深入了解Harris角点检测(图文并茂+浅显易懂+程序源码)


     

    前言

      红胖子,来也!
      做识别,有时候遇到需求,比如识别一个三角形,并求得三角形三个顶点的角度,这种属于教育场景类似的,还有其他场景,那么检测角点就显得很重要了,检测出角点并且求出其角度。

     

    Demo

      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

     

    图像特征三大类型

    • 边缘:图像强度发生突变的区域,其实就是高强度梯度区域;
    • 角点:两个边缘相交的地方,看起来像一个角;
    • 斑点:按特征划分的区域,强度特别高、强度特别低或具备特定纹理的区域;
     

    Harris角点

    概述

      Harris角点检测是一种基于灰度图像的角点提取算法,稳定性高,在opencv中harris角点检测的性能相对较低,因为其使用了高斯滤波。
      基于灰度图像的角点检测又分为基于梯度、基于模板和基于模板梯度组合三类型的方法,而Harris算法就是基于灰度图像中的基于模板类型的算法。

    原理

      人眼对角点的识别通常是通过一个局部的小窗口内完成的:如果在各个方向上移动这个小窗口,窗口内的灰度发生了较大的变化,那么说明窗口内存在角点,具体分为以下三种情况:

    • 如果在各个方向移动,灰度几乎不变,说明是平坦区域;
    • 如果只沿着某一个方向移动,灰度几乎不变,说明是直线;
    • 如果沿各个方向移动,灰度均发生变化,说明是角点。
        基本的原理,如下图:
        在这里插入图片描述
        具体的计算公式如下:
      在这里插入图片描述
        在这里插入图片描述
        泰勒展开:
        在这里插入图片描述
        代入得到:
        在这里插入图片描述
        其中:
        在这里插入图片描述
        二次项函数本质上就是一个椭圆函数,椭圆的扁平率和尺寸是由矩阵M的两个特征值决定的。
          在这里插入图片描述
        在这里插入图片描述
        矩阵M的两个特征值与图像中的角点,边缘,平坦区域的关系。
        Harris定义角点响应函数即:
        在这里插入图片描述
        即R=Det(M)-k*trace(M)*trace(M),k为经验常数0.04~0.06 。
        定义当R>threshold时且为局部极大值的点时,定义为角点。

    Harris函数原型

    void cornerHarris(InputArray src,
                      OutputArray dst,
                      int blockSize,
                      int ksize,
                      double k,
                      intborderType=BORDER DEFAULT );
    
    • 参数一:InputArray类型的src,输入图像,即源图像,填Mat类的对象 即可,且须为单通道8位或者浮点型图像;
    • 参数二:OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸,特别注意输出类型是CV_32F;
    • 参数三:int类型的blockSize,表示邻域的大小;
    • 参数四:int类型的ksize,表示Sobel()算子的孔径大小;
    • 参数五:double类型的k,Harris角点响应函数,一般为0.04~0.06;
    • 参数六:int类型的borderType,图像像素的边界模式:
        在这里插入图片描述

    归一化概述

      归一化是指对矩阵cv::Mat进行归一化操作。
      归一化是一种无量纲处理手段,使物理系统数值的绝对值变成某种相对值关系。简化计算,缩小量值的有效办法。例如,滤波器中各个频率值以截止频率作归一化后,频率都是截止频率的相对值,没有了量纲。阻抗以电源内阻作归一化后,各个阻抗都成了一种相对阻抗值,“欧姆”这个量纲也没有了。等各种运算都结束后,反归一化一切都复原了。信号处理工具箱中经常使用的是nyquist频率,它被定义为采样频率的二分之一,在滤波器的阶数选择和设计中的截止频率均使用nyquist频率进行归一化处理。例如对于一个采样频率为500hz的系统,400hz的归一化频率就为400/500=0.8,归一化频率范围在[0,1]之间。
      

    归一化函数原型

    void normalize( InputArray src,
                    InputOutputArray dst,
                    double alpha = 1,
                    double beta = 0,
                    int norm_type = NORM_L2,
                    int dtype = -1,
                    InputArray mask = noArray());
    
    • 参数一:InputArray类型的src,一般为mat;
    • 参数二:InputOutputArray类型的dst,一般为mat,大小与src一样;
    • 参数三:double类型的alpha,归一化的最小值,默认值1;
    • 参数四:double类型的beta,归一化的最大值,默认值0;
    • 参数五:int类型的norm_type,归一化类型,具体查看cv::NormTypes,默认为;
    • 参数六:int类型的dtype,默认值-1,负数时,其输出矩阵与src类型相同,否则它和src有同样的通道数,且此时图像深度为CV_MAT_DEPTH。
    • 参数七:InputArray类型的mask,可选的操作掩膜,默认值为noArray();

    增强图像函数原型

    void convertScaleAbs(InputArray src,
                         OutputArray dst,
                         double alpha = 1,
                         double beta = 0);
    
    • 参数一:InputArray类型的src,一般为mat;
    • 参数二:OutputArray类型的dst,一般为mat,大小与src一样;
    • 参数三:double类型的alpha,归一化的最大值,默认值1;
    • 参数四:double类型的beta,归一化的最大值,默认值0;
     

    Demo源码

    void OpenCVManager::testHarris()
    {
        QString fileName1 =
                "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/16.jpg";
        int width = 400;
        int height = 300;
    
        cv::Mat srcMat = cv::imread(fileName1.toStdString());
        cv::resize(srcMat, srcMat, cv::Size(width, height));
    
        cv::String windowName = _windowTitle.toStdString();
        cvui::init(windowName);
    
        cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, srcMat.rows * 3),
                                    srcMat.type());
    
        int threshold1 = 200;
        int threshold2 = 100;
        while(true)
        {
            windowMat = cv::Scalar(0, 0, 0);
    
            cv::Mat mat;
    
            cv::Mat tempMat;
            // 原图先copy到左边
            mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
    
            {
                // 灰度图
                cv::Mat grayMat;
                cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::Mat grayMat2;
                cv::cvtColor(grayMat, grayMat2, cv::COLOR_GRAY2BGR);
                cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);
    
                // 均值滤波
                cv::blur(grayMat, tempMat, cv::Size(3, 3));
    
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 20, "threshold1");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 40, 200, &threshold1, 0, 255);
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 100, "threshold2");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 120, 200, &threshold2, 0, 255);
    
                // canny边缘检测
                cv::Canny(tempMat, tempMat, threshold1, threshold2);
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::cvtColor(tempMat, grayMat2, cv::COLOR_GRAY2BGR);
                cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);
    
                // harris角点检测
                cv::cornerHarris(grayMat, grayMat2, 2, 3, 0.01);
                // 归一化与转换
                cv::normalize(grayMat2, grayMat2, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
                cv::convertScaleAbs(grayMat2 , grayMat2); //将归一化后的图线性变换成 8U位元符号整
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::cvtColor(grayMat2, grayMat2, cv::COLOR_GRAY2BGR);
    
                cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);
    
                // harris角点检测
                cv::cornerHarris(tempMat, tempMat, 2, 3, 0.01);
                // 归一化与转换
                cv::normalize(tempMat, tempMat, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
                cv::convertScaleAbs(tempMat , tempMat); //将归一化后的图线性变换成 8U位元符号整
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::cvtColor(tempMat, tempMat, cv::COLOR_GRAY2BGR);
                cv::addWeighted(mat, 0.0f, tempMat, 1.0f, 0.0f, mat);
            }
            // 更新
            cvui::update();
            // 显示
            cv::imshow(windowName, windowMat);
            // esc键退出
            if(cv::waitKey(25) == 27)
            {
                break;
            }
        }
    }
    
    
     

    工程模板:对应版本号v1.54.0

      对应版本号v1.54.0

     
     
  • 相关阅读:
    给Windows组件添加图标
    C#文件和文件夹操作
    WinForm TreeView 右键菜单
    VC++ New 操作符
    Ext与Jquery的整合
    PowerDesign报表操作
    SQLServer自动建表存储过程
    Visual Studio 2008简体中文正式版下载地址
    WinForm遍历控件
    发布时用直接用源文件部署
  • 原文地址:https://www.cnblogs.com/qq21497936/p/12969368.html
Copyright © 2020-2023  润新知