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


     

    前言

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

     

    Demo

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

     

    图像特征三大类型

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

    Shi-Tomasi角点

    概述

      Shi-Tomasi角点检测是改进了Harris算法,次算法最原始的定义是将矩阵M的行列式与M的迹想见,再将差值进行同预先给定的预制进行比较,后来再次改进,若两个特征值中较小的一个大于最小阈值,则会得到强角点。

    原理

      由于Harris算法的稳定性和k值有关,Shi-Tomasi发现,角点的稳定性和矩阵M的较小特征值有关,改进的Harris算法即直接计算出矩阵M的特征值,用较小的特征值与阈值比较,大于阈值的即为强特征点。

    Harris角点概述

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

    Harris角点原理

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

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

    Shi-Tomasi检测角点函数原型

    // 重载函数:九个参数
    void goodFeaturesToTrack( InputArray image,
                              OutputArray corners,
                              int maxCorners,
                              double qualityLevel,
                              double minDistance,
                              InputArray mask = noArray(),
                              int blockSize = 3,
                              bool useHarrisDetector = false,
                              double k = 0.04);
    // 重载函数:十个参数
    void goodFeaturesToTrack( InputArray image,
                              OutputArray corners,
                              int maxCorners,
                              double qualityLevel,
                              double minDistance,
                              InputArray mask,
                              int blockSize,
                              int gradientSize,
                              bool useHarrisDetector = false,
                              double k = 0.04 );
    
    • 参数一:InputArray类型的image,输入图像,即源图像,填Mat类的对象 即可,且须为单通道8位或者浮点型32位单通道图像;
    • 参数二:OutputArray类型的corners,检测到的角点的输出向量;
    • 参数三:int类型的maxCorners,角点的最大数据量;
    • 参数四:double类型的qualityLevel,角点检测可接受的最小特征值。其实实际用于过滤角点的最小特征是qualityLevel与图像中最大特征值的乘积。所以qualityLevel通常不会超过1(常用的值为0.10或者0.01)。检测完后所有的交点后,还要进一步提出掉一些距离较近的角点;
    • 参数五:double类型的minDistance,角点之间的最细小距离,次参数用于保证返回的角点之间的距离不小于minDistance;
    • 参数六:InputArray类型的mask,可选参数,表示感情兴的区域,默认为noArray(),若此参数非空则必须需为CV_8UC1类型,且和第一个参数image有相同的尺寸;
    • 参数七:int类型的blockSize,有默认值3,是计算导数自相关矩阵时指定的领域范围;
    • 参数八:bool类型的useHarrisDetector,只是是否使用Harris角点检测,默认为false,不使用;
    • 参数九:double类型的k,为用于设置Hessian自相关矩阵韩烈士的相对权重的权重系数,默认值为0.04;
     

    Demo源码

    void OpenCVManager::testShiTomasi()
    {
        QString fileName1 =
    "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.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;
        int qualityLevel = 1;
        int minDistance = 10;
        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 + 80, "threshold2");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 100, 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);
    
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 140, "qualityLevel / 100.0f");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 160, 200, &qualityLevel, 1, 100);
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 200, "minDistance");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 220, 200, &minDistance, 1, 100);
    
    
                // Shi-Tomasi角点检测
                std::vector<cv::Point2f> corners;
                cv::goodFeaturesToTrack(grayMat,    // 输入图像
                                        corners,    // 输出角点
                                        100,        // 最大输出角点数量
                                        qualityLevel / 100.0f,  // 最小特征值
                                        minDistance,    // 最小间隔距离
                                        cv::noArray(),  // 感兴趣的区域
                                        3,          // 计算矩阵时的领域范围
                                        false,      // 不适用harris角点检测
                                        0.04);      // 权重系数
                // 绘制检测到的角点
                cv::Mat tempMat2 = srcMat.clone();
                for(int index = 0; index < corners.size(); index++)
                {
                    cv::circle(tempMat2, corners.at(index), 2, cv::Scalar(0, 0, 255), 2);
                }
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, tempMat2, 1.0f, 0.0f, mat);
    
                // Shi-Tomasi角点检测
                cv::goodFeaturesToTrack(tempMat,    // 输入图像
                                        corners,    // 输出角点
                                        100,        // 最大输出角点数量
                                        qualityLevel / 100.0f,  // 最小特征值
                                        minDistance,    // 最小间隔距离
                                        cv::noArray(),  // 感兴趣的区域
                                        3,          // 计算矩阵时的领域范围
                                        false,      // 不适用harris角点检测
                                        0.04);      // 权重系数
                // 绘制检测到的角点
                tempMat = srcMat.clone();
                for(int index = 0; index < corners.size(); index++)
                {
                    cv::circle(tempMat, corners.at(index), 2, cv::Scalar(0, 0, 255), 2);
                }
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                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.55.0

  • 相关阅读:
    编程算法
    JDBC连接MySQL数据库及演示样例
    CKEditor&ckfindtor
    从零開始开发Android版2048 (五) 撤销的实现
    TCP/IP之分层
    poj2239 Selecting Courses --- 二分图最大匹配
    英尺到米的换算
    概率dp ZOJ 3640
    Android应用程序注冊广播接收器(registerReceiver)的过程分析
    TsFltMgr.sys系统蓝屏的原因就在于QQ电脑管家!
  • 原文地址:https://www.cnblogs.com/qq21497936/p/12990814.html
Copyright © 2020-2023  润新知