zoukankan      html  css  js  c++  java
  • OpenCV开发笔记(六十二):红胖子8分钟带你深入了解亚像素角点检测(图文并茂+浅显易懂+程序源码)

     

    前言

      红胖子,来也!
      本篇主要说亚像素角点,又叫次级像素角点,其实通俗来说,就是输入整数坐标,使用最小二乘法迭代计算所需精度的实数坐标。

     

    Demo

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

     

    亚像素角点

    概述

      进行图像处理提取用于识别的特征点进行几何测量,这通常需要更高的精度,而函数goodFeaturesToTrack()只能提供简单的像素的坐怀值,也就是说,有时候会需要要实数坐标值而不是整数坐标值。
    亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重被功跟踪目标的三维结构时候,是一个基本的测量值 。

    Shi-Tomasi角点检测

    亚像素角点检测原理

      在这里插入图片描述
      其中,(a)点p附近的图像是均匀的,其梯度为0,(b)边缘的梯度与沿地缘方向的q-p向量正交。在图中的两种情况下,p点梯度与q-p向量的点积均为0。
      假设起始角点q在实际亚像素级角点的附近。检测所有的q-p向量。若点p位于一个均匀的区域,则点p处的梯度为0。若q-p向量的方向与边缘的方向一致,则此边缘上p点处的梯度与q-p向量正交,在这两种情况下,p点处的梯度与q-p向量的点积为0。可以在p点周围找到很多组梯度以及相关的向量q节,令其点集为0,然后可以通过求解方程组,方程组的解即为角点q的亚像素级精度的位置,也就是精确的角点位置。

    TermCriteri迭代标注类的构造函数

    TermCriteria(int type,
                 int maxCount,
                 double epsilon);
    
    • 参数一:int类型的type,枚举为TermCriteria::Type类型,终止标准的类型:
        在这里插入图片描述
    • 参数二:int类型的maxCount,最大迭代次数/元素数;
    • 参数三:double类型的epsilon,所需的精度。

    函数原型

    void cornerSubPix( InputArray image,
                       InputOutputArray corners,
                       Size winSize,
                       Size zeroZone,
                       TermCriteria criteria );
    
    • 参数一:InputArray类型的image,输入图像,即源图像,填Mat类的对象,必须为单通道图像;
    • 参数二:InputOutputArray类型的corners,提供输入角点的初始坐标和精确的输出坐标;
    • 参数三:Sjze类型的winSjze,搜索窗口的一半尺寸。若winSize=Size(5,5),那么就表示使用(52+1)x(52+1)=11x11大小的搜索窗口。
    • 参数四:Size类型的zeroZone,表示死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
    • 参数五:TermCriteria类型的criteria,求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精到达某个设定值。cirteria可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。
     

    Demo源码

    void OpenCVManager::testCornerSubPix()
    {
        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 qualityLevel = 1;
        int minDistance = 10;
        int iterations = 5;
        int epsilon = 1;
        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);
                cv::Mat grayMat2;
                cv::cvtColor(grayMat, grayMat2, cv::COLOR_GRAY2BGR);
                cv::addWeighted(mat, 0.0f, grayMat2, 1.0f, 0.0f, mat);
    
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 10, "qualityLevel / 100.0f");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 30, 200, &qualityLevel, 1, 99);
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 80, "minDistance");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 100, 200, &minDistance, 1, 100);
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 150, "iterations");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 170, 200, &iterations, 1, 100);
                cvui::printf(windowMat, width * 1 + 20, height * 0 + 220, "epsilon/100.0f");
                cvui::trackbar(windowMat, width * 1 + 20, height * 0 + 240, 200, &epsilon, 1, 1000);
    
                // 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 tempMat4 = srcMat.clone();
                for (int i = 0; i < corners.size(); i++)
                {
                    cv::circle(tempMat4, corners[i], 5, cv::Scalar(0, 255, 0), 2, 8, 0);
                    cvui::printf(windowMat, width * 0 + 20, height * 1 + 20 * i, "%f,%f", corners[i].x, corners[i].y);
                }
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, tempMat4, 1.0f, 0.0f, mat);
                //指定亚像素计算迭代标注
                cv::TermCriteria criteria = cv::TermCriteria(
                                cv::TermCriteria::EPS,
                                iterations,         // 迭代次数
                                epsilon);           // 精度
    
                //亚像素检测
    
                cv::cornerSubPix(grayMat,
                                 corners,
                                 cv::Size(5, 5),
                                 cv::Size(-1, -1),
                                 criteria);
    
                //将检测到的亚像素角点绘制到原图上
                cv::Mat tempMat3 = srcMat.clone();
                for (int i = 0; i < corners.size(); i++)
                {
                    cv::circle(tempMat3, corners[i], 5, cv::Scalar(0, 255, 0), 2, 8, 0);
                    cvui::printf(windowMat, width * 1 + 20, height * 1 + 20 * i, "%f,%f", corners[i].x, corners[i].y);
                }
                // copy
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, tempMat3, 1.0f, 0.0f, mat);
    
            }
            // 更新
            cvui::update();
            // 显示
            cv::imshow(windowName, windowMat);
            // esc键退出
            if(cv::waitKey(25) == 27)
            {
                break;
            }
        }
    }
    
     

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

      对应版本号v1.56.0

     
     
  • 相关阅读:
    C#基础篇——泛型
    基于.NetCore3.1系列 —— 使用Swagger导出文档 (补充篇)
    基于.NetCore3.1系列 —— 使用Swagger导出文档 (番外篇)
    springboot深入浅出系列(16章97节)-看了都说好
    小书MybatisPlus第5篇-Active Record模式精讲
    小书MybatisPlus第4篇-表格分页与下拉分页查询
    使用位运算、值交换等方式反转java字符串-共四种方法
    有效提高java编程安全性的12条黄金法则
    小书MybatisPlus第3篇-自定义SQL
    结合SpEL使用@Value-基于配置文件或非配置的文件的值注入-Spring Boot
  • 原文地址:https://www.cnblogs.com/qq21497936/p/13096048.html
Copyright © 2011-2022 走看看