计算机视觉—— OpenCV cornerSubPix亚像素角点检测

亚像素

在数字照片中,最小的可见单位是“像素”。尽管我们无法直接看到像素之间的信息,但有些应用场景需要比相机所提供的信息更加精确。例如,在从图像中重建三维(3D)对象时就需要进行精确的测量。为了提高角点识别的准确性,已经开发了一些数学策略。

观察这个放大的正方形图像,你将看到每个像素的详细信息。如果你尝试找到角点,可能会发现角点并不是由单一像素构成的。实际上,要让角点与像素完美对齐几乎是不可能的。

传统的角点检测方法,比如Shi-Tomasi和Harris方法,可能只能给出一个近似的像素坐标,例如(53, 786)。然而,科学家和研究人员通常需要更精细的结果,比如(53.786, 786.110)。
在这里插入图片描述

虽然无法真正达到(53.786, 786.110)这样的精确度,但通过这种方式,能够确认角点就位于这个近似位置。

为什么要如此细致地去确定小数点后的数值呢?原因在于角点是图像中极其关键的部分。在多种应用场景中,额外的精度是非常必要的,例如:

  • 立体视觉
  • 3D重建
  • 相机校准
  • 物体跟踪

因此,为了提升准确性和处理速度,科研人员已经投入了大量的工作。

OpenCV 中的亚像素角点

在OpenCV中,亚像素角点的检测是通过内置的cornerSubPix函数实现的,该函数能够提高角点定位的精度,达到亚像素级别。这种精度的提升对于多种图像处理任务来说是非常关键。
cornerSubPix函数使用点积方法对由角点Harris检测器或其他角点检测算法初步识别的角点进行迭代细化。这个过程会持续进行,直到满足预设的终止条件。
在这里插入图片描述

cornerSubPix函数的基本使用

void cv::cornerSubPix(
    InputArray image, InputOutputArray corners,
    Size winSize, Size zeroZone, TermCriteria criteria
)

cornerSubPix函数的基本参数和它们的用途:

  • image:输入图像。
  • corners:正如名字所示,这个数组在过程开始时存储了大致的角点。作为函数的结果,这个数组会用修正后的角点位置进行修改。
  • winSize:这个函数依赖于一些方程来执行其工作。它还使用了几个像素围绕角点来获得这种效果。如果你使用 winSize,你可以控制从特定窗口中提取多少像素。
  • zeroZone:这个函数也使用同样的方式解决许多方程。当涉及到“解决”问题时,使用矩阵。为了找到这个问题的解决方案,这个矩阵会被求逆。但有些矩阵不能被求逆。为了避免这种情况,忽略了围绕角点的一些像素。那个区域就是 zeroZone。
  • criteria:停止迭代角点细化过程的条件。换句话说,角点角度的细化过程要么在满足一组条件后结束(使用 CV_TERMCRIT_ITER 或 CV_TERMCRIT_EPS 或两者)。
OpenCV - TermCriteria

在OpenCV中,TermCriteria是一种特殊的结构,用于指定算法的终止条件。这在许多需要迭代计算的算法中非常有用,如优化问题、梯度下降、角点检测的亚像素精度细化等。使用TermCriteria可以确保算法不会无限期地运行下去,而是在满足特定条件时停止,这些条件包括:

  1. 迭代次数(CV_TERMCRIT_ITER):算法运行的迭代次数达到用户指定的迭代次数时终止。

  2. 误差精度(CV_TERMCRIT_EPS):当连续迭代的解之间的差异小于用户指定的误差阈值时终止。

TermCriteria结构可以通过多种方式组合这两种条件,以适应不同的应用场景。以下是如何使用TermCriteria的一些示例:

构造TermCriteria

cv::TermCriteria criteria;

设置迭代次数和误差精度

// 只根据迭代次数终止
criteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER, 100, 0.0);

// 只根据误差精度终止
criteria = cv::TermCriteria(cv::TermCriteria::EPS, 30, 0.01);

// 同时根据迭代次数和误差精度终止
criteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 100, 0.01);

在上述代码中:

  • cv::TermCriteria::MAX_ITER表示使用迭代次数作为终止条件。
  • cv::TermCriteria::EPS表示使用误差精度作为终止条件。
  • 第三个参数10030分别是迭代次数和最大迭代次数。
  • 第四个参数0.01是误差精度阈值。

使用TermCriteria的算法示例

cornerSubPix函数为例,可以在调用此函数时传入TermCriteria对象,以控制亚像素角点检测的终止条件:

cv::Mat src_gray; // 假设这是已经加载的灰度图像
std::vector<cv::Point2f> corners; // 假设这是初步检测到的角点
cv::Size winSize(5, 5); // 搜索窗口大小
cv::Size zeroZone(-1, -1); // 通常设置为-1
cv::TermCriteria criteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 40, 0.001);

std::vector<cv::Point2f> refined_corners;
cv::cornerSubPix(src_gray, corners, winSize, zeroZone, criteria);

在上面的代码中,cornerSubPix函数将在满足以下任一条件时停止:迭代次数达到40次,或者连续迭代的角点位置变化小于0.001。这种灵活的终止条件设置对于确保算法的效率和准确性至关重要。

C++ 代码计算亚像素角点

#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>


cv::Mat src, src_gray;

int maxCorners = 10;
const int MAXTRACKBAR = 25;

const std::string source_window = "Source Image";

void trackFeatures( int, void* );

int main( int argc, char** argv )
{

    if(argc < 2 ) {
        std::cout << "Usage: " << argv[0] << " <Input image>\n";
        return -1;
    }

    src = cv::imread(argv[1]) ;

    if( src.empty() )
    {
        std::cout << "Could not open or find the image!\n\n" ;
        std::cout << "Usage: " << argv[0] << " <Input image>\n";
        return -1;
    }
    cv::cvtColor( src, src_gray, cv::COLOR_BGR2GRAY );

    cv::namedWindow( source_window );

    cv::createTrackbar( "Max corners:", source_window, &maxCorners, MAXTRACKBAR, trackFeatures );

    trackFeatures( 0, 0 );

    cv::waitKey();
    return 0;
}

void trackFeatures( int, void* )
{
    maxCorners = MAX(maxCorners, 1);
    std::vector<cv::Point2f> corners;
    double qualityLevel = 0.01;
    double minDistance = 10;
    int blockSize = 3, gradientSize = 3;
    bool useHarrisDetector = true;
    double k = 0.04;

    cv::Mat copy = src.clone();

    cv::goodFeaturesToTrack( src_gray,
                         corners,
                         maxCorners,
                         qualityLevel,
                         minDistance,
                         cv::Mat(),
                         blockSize,
                         gradientSize,
                         useHarrisDetector,
                         k );


    std::cout << "** Number of corners detected: " << corners.size() << "\n";
    int radius = 8;
    for( size_t i = 0; i < corners.size(); i++ )
    {
        std::cout << " -- Original Corner Detected [" << i << "]  (" << corners[i].x << "," << corners[i].y << ")\n" ;
        cv::circle( copy, corners[i], radius, cv::Scalar(0,255,0), cv::FILLED );
    }


    cv::Size winSize =  cv::Size( 5, 5 );
    cv::Size zeroZone = cv::Size( -1, -1 );
    cv::TermCriteria criteria = cv::TermCriteria( cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 40, 0.001 );


    std::vector<cv::Point2f> refinedCorners(corners);
    cv::cornerSubPix( src_gray, refinedCorners, winSize, zeroZone, criteria );

    for( size_t i = 0; i < refinedCorners.size(); i++ )
    {
        std::cout << " -- Refined Corner [" << i << "]  (" << refinedCorners[i].x << "," << refinedCorners[i].y << ")\n" ;
        cv::circle( copy, refinedCorners[i], radius, cv::Scalar(0,0,255), cv::FILLED );
    }

    cv::namedWindow( source_window.c_str() );
    cv::imshow( source_window, copy );

}

代码解释

首先使用 cv::imread() 读取输入图像,并使用 cv::cvtColor() 将其转换为灰度。

然后使用 goodFeaturesToTrack() 函数在图像中查找角点。在这个函数中,使用了角点 Harris 检测算法来检测图像中的角点。图像角点的坐标存储在 corners 变量中。

在 corner 变量中的这些坐标中,使用 cv::cornerSubPix() 函数在亚像素级别上找到角点。

在这里插入图片描述

相关推荐

  1. 关于视觉3d目标检测学习深度的一点理解

    2024-04-25 19:18:03       34 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-25 19:18:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-25 19:18:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-25 19:18:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-25 19:18:03       20 阅读

热门阅读

  1. MyBatis-动态sql常见使用

    2024-04-25 19:18:03       13 阅读
  2. h5 常见面试问题

    2024-04-25 19:18:03       12 阅读
  3. 如何在Chrome中设置无痕模式

    2024-04-25 19:18:03       14 阅读
  4. 云原生周刊:Kubernetes v1.30 发布 | 2024.4.22

    2024-04-25 19:18:03       27 阅读