OpenCV 4.x API 详解与C++实例-特征检测

第十一节 特征检测

OpenCV的imgproc模块提供了图像特征检测函数,包括Canny边缘检测、直线检测、角点检测等。

1、cv::Canny


使用Canny算法对输入图像进行边缘检测。

void cv::Canny(InputArray image,OutputArray edges,double threshold1,double threshold2,int apertureSize = 3,bool L2gradient = false)

该函数使用Canny算法在输入图像中找到边缘,并在输出地图边缘中对其进行标记。 threshold1和threshold2之间的最小值用于边缘链接。 最大值用于查找强边的初始段。 参见http://en.wikipedia.org/wiki/Canny_edge_detector

参数名称 参数描述
image 8位输入图像。
edges 输出边缘图; 单通道8位图像,其大小与image相同。
threshold1 第一个阈值
threshold2 第二个阈值
apertureSize Sobel操作员的光圈尺寸。
L2gradient 标志,指示是否使用更加准确的 L 2 L_2 L2规范化 L 2 n o r m = ( d I / d x ) 2 + ( d I / d y ) 2 L_2 norm =\sqrt{(dI/dx)^2 + (dI/dy)^2} L2norm=(dI/dx)2+(dI/dy)2 应该用于计算图像梯度幅度,或者使用默认的$L_1==
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/lena.png");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }

    // 转换成灰度图像
    cv::Mat gray;
    cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);

    // 计算Canny边缘
    cv::Mat edges;
    cv::Canny(gray,edges,128,255);

    // 显示图像
    cv::imshow("src",src);
    cv::imshow("dst",edges);

    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}

在这里插入图片描述

其他重载形式函数如下:

  • void cv::Canny(InputArray dx,InputArray dy,OutputArray edges,double threshold1,double threshold2,bool L2gradient = false):通过图像梯度查找边缘。

2、cv::cornerEigenValsAndVecs、cv::cornerMinEigenVal


1)cv::cornerEigenValsAndVecs:计算图像块的特征值和特征向量以进行角点检测。

void cv::cornerEigenValsAndVecs(InputArray src,OutputArray dst,int blockSize,int ksize,int borderType = BORDER_DEFAULT)

对于每个像素 p p p,函数cornerEigenValsAndVecs考虑一个 b l o c k S i z e × b l o c k S i z e blockSize×blockSize blockSize×blockSize邻域 S ( p ) S(p) S(p)。 它将邻域上的导数的协方差矩阵计算为:

M = [ ∑ S ( p ) ( d I / d x ) 2 ∑ S ( p ) d I / d x d I / d y ∑ S ( p ) d I / d x d I / d y ∑ S ( p ) ( d I / d y ) 2 ] M = \begin{bmatrix} \sum _{S(p)}(dI/dx)^2 & \sum _{S(p)}dI/dx dI/dy \\ \sum _{S(p)}dI/dx dI/dy & \sum _{S(p)}(dI/dy)^2 \end{bmatrix} M=[S(p)(dI/dx)2S(p)dI/dxdI/dyS(p)dI/dxdI/dyS(p)(dI/dy)2]

其中的导数是使用Sobel运算符计算的。

然后,它找到 M M M的特征向量和特征值,并将它们存储在目标图像中为 ( λ 1 , λ 2 , x 1 , y 1 , x 2 , y 2 ) (\lambda1,\lambda2,x1,y1,x2,y2) (λ1,λ2,x1,y1,x2,y2)其中:

  • λ 1 , λ 2 \lambda1,\lambda2 λ1λ2是M的非排序特征值
  • x 1 , y 1 x_1, y_1 x1,y1是对应于 λ 1 \lambda1 λ1的特征向量
  • x 2 , y 2 x_2, y_2 x2,y2是对应于 λ 1 \lambda1 λ1的特征向量

该函数的输出可用于鲁棒的边缘或拐角检测。参数如下:

参数名称 参数描述
src 输入单通道8位或浮点图像。
dst 用于存储结果的图像。 它具有与src相同的大小,并且类型为 CV_32FC(6)
blockSize 邻域规模(请参阅下面的详细信息)。
ksize Sobel运算符的光圈参数。
borderType 像素外推法. 请参考BorderTypes。不支持 BORDER_WRAP 类型
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/leaf.jpg");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }

    // 转换成灰度图像
    cv::Mat gray;
    cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);

    // 计算特征值
    cv::Mat dst;
    int blockSize = 4,kernlSize = 5;
    cv::cornerEigenValsAndVecs(gray,dst,blockSize,kernlSize);

    cout << dst.at<cv::Vec6f>(0,0) << endl;

    return 0;
}

2)cv::cornerMinEigenVal:计算用于角点检测的梯度矩阵的最小特征值。

void cv::cornerMinEigenVal(InputArray src,OutputArray dst,int blockSize,int ksize = 3,int borderType = BORDER_DEFAULT)

该函数类似于cornerEigenValsAndVecs,但是它只计算和存储导数协方差矩阵的最小特征值,即cornerEigenValsAndVecs描述中的公式的 m i n ( λ 1 , λ 2 ) min(\lambda_1,\lambda_2) min(λ1λ2)

参数名称 参数列表
src 输入单通道8位或浮点图像。
dst 用于存储最小特征值的图像。 它的类型为CV_32FC1,大小与src相同。
blockSize 邻域大小 (请参考 cornerEigenValsAndVecs )。
ksize Sobel运算符的光圈参数。
borderType 像素外推法. 请参考BorderTypes。不支持 BORDER_WRAP 类型

3、cv::cornerHarris


哈里斯(Harris)角点检测。

void cv::cornerHarris(InputArray src,OutputArray dst,int blockSize,int ksize,double k,int borderType = BORDER_DEFAULT)

该函数对输入图像运行哈里斯角检测,类似于cornerMinEigenVal和cornerEigenValsAndVecs,对于每个像素 ( x , y ) (x,y) (xy),它在 b l o c k S i z e × b l o c k S i z e blockSize×blockSize blockSize×blockSize邻域上计算 2 × 2 2×2 2×2梯度协方差矩阵 M ( x , y ) M(x,y) M(xy)。 然后,它计算出以下特征:

dst ( x , y ) = d e t M ( x , y ) − k ⋅ ( t r M ( x , y ) ) 2 \texttt{dst} (x,y) = \mathrm{det} M^{(x,y)} - k \cdot \left ( \mathrm{tr} M^{(x,y)} \right )^2 dst(x,y)=detM(x,y)k(trM(x,y))2

输入图像中的角点可以为该响应图的局部最大值。参数如下:

参数名称 参数描述
src 输入单通道8位或浮点图像。
dst 用于存储哈里斯探测器响应的图像。 它的类型为CV_32FC1,大小与src相同。
blockSize 邻域大小 (具体请参考 cornerEigenValsAndVecs ).
ksize Sobel运算符的光圈参数。
k 哈里斯探测器自由参数。 请参阅上面的公式。
borderType 像素外推法。 请参考BorderTypes.。BORDER_WRAP 不支持。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 读取图像
    cv::Mat src = cv::imread("images/chessboard.png");
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat srcResized;
    cv::resize(src,srcResized,cv::Size(src.cols/6,src.rows/6));

    // 转换成灰度图
    cv::Mat gray;
    cv::cvtColor(srcResized,gray,cv::COLOR_BGR2GRAY);

    // 计算角点
    cv::Mat gray32f,dst;
    gray.convertTo(gray32f,CV_32FC1);
    // 最后一个参数在 0.04 到 0.05 之间
    cv::cornerHarris(gray32f,dst,2,5,0.04);

    cv::dilate(dst,dst,cv::Mat());

    // 阈值分割
    cv::threshold(dst,dst, 200,255,cv::THRESH_BINARY);

    // 归范化
    cv::Mat dstNorm,dstNormScaled;
    cv::normalize(dst,dstNorm,0,255,cv::NORM_MINMAX, CV_32FC1, cv::Mat());
    cv::convertScaleAbs( dstNorm, dstNormScaled );


    // 绘制角点
    cv::Mat dstDrawing = srcResized.clone();
    for( int j = 0; j < dstNorm.rows ; j++ )
    { for( int i = 0; i < dstNorm.cols; i++ )
        {
            if( (int) dstNorm.at<float>(j,i) >= 200 )
            {
                cv::drawMarker(dstDrawing,cv::Point(i,j),cv::Scalar(0,0,255),cv::MARKER_CROSS,20,1);
            }
        }
    }

    cv::imshow("dst",dstDrawing);
    cv::imshow("src",srcResized);

    cv::waitKey();


    return 0;
}

在这里插入图片描述

4、cv::goodFeaturesToTrack


确定图像上的强角。

void cv::goodFeaturesToTrack(InputArray image,OutputArray corners,int maxCorners,double qualityLevel,double minDistance,InputArray mask = noArray(),int blockSize = 3,bool useHarrisDetector = false,double k = 0.04)

该函数可以找到图像中或指定图像区域中最突出的角,如[Jianbo Shi and Carlo Tomasi. Good features to track. In Computer Vision and Pattern Recognition, 1994. Proceedings CVPR’94., 1994 IEEE Computer Society Conference on, pages 593–600. IEEE, 1994.]中所述。

  • 函数使用cornerMinEigenVal或cornerHarris计算每个源图像像素的拐角质量度量。
  • 函数执行非最大值抑制(保留3 x 3邻域中的局部最大值)。
  • 最小特征值小于 qualityLevel ⋅ max ⁡ x , y q u a l i t y M e a s u r e M a p ( x , y ) \texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y) qualityLevelmaxx,yqualityMeasureMap(x,y)的角点将被拒绝。
  • 其余的角点按质量度量按降序排序。
  • 函数会丢弃距离小于maxDistance的拐角处较强的每个角点。

注意:如果使用参数qualityLevel参数的不同值A和B以及A> B调用函数,则返回带有qualityLevel = A的角的向量将是带有qualityLevel = B的输出向量的前缀。

参数如下:

参数名称 参数描述
image 输入8位或浮点32位单通道图像。
corners 检测到的角的输出向量。
maxCorners 返回的最大角数。 如果角落多于找到的角落,则返回最强的角落。 “ maxCorners <= 0”表示没有设置最大值限制,并且返回了所有检测到的角。
qualityLevel 表征图像角的最低可接受质量的参数。 参数值乘以最佳拐角质量度量,即最小特征值(请参阅cornerMinEigenVal或 哈里斯函数响应(请参阅cornerHarris。 质量度量低于产品的角被拒绝。 例如,如果最佳角的质量量度= 1500,而qualityLevel = 0.01,则拒绝质量量度小于15的所有角。
minDistance 返回的角之间的最小可能欧几里得距离。
mask 可选的感兴趣区域。 如果图像不为空(它的类型必须为CV_8UC1且大小与image相同),则它指定检测到拐角的区域。
blockSize 用于计算每个像素邻域上的导数协方差矩阵的平均块的大小。 参见cornerEigenValsAndVecs。
useHarrisDetector 指示是否使用哈里斯探测器的参数(请参见cornerHarris或[cornerMinEigenVal](https:// docs.opencv.org/4.5.1/dd/d1a/group__imgproc__feature.html#ga3dbce297c1feb859ee36707e1003e0a8)
k 哈里斯(Harris)角点探测器的自由参数。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
using namespace cv;
Mat src, cornerImg, grayImg;
int mxCorners = 10;
RNG rngs = { 12345 };


int main() {
    // 读取图像
    cv::Mat src = cv::imread("images/chessboard.png", cv::IMREAD_COLOR);
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat image_color;
    cv::resize(src,image_color,cv::Size(src.rows / 6,src.cols/6));

    cv::Mat image_copy = image_color.clone();
    // 转换成灰度图像
    cv::Mat image_gray;
    cv::cvtColor(image_color, image_gray, cv::COLOR_BGR2GRAY);

    // 设置触点检测参数
    std::vector<cv::Point2f> corners;
    int max_corners = 100;
    double quality_level = 0.01;
    double min_distance = 10;
    int block_size = 3;
    bool use_harris = false;
    double k = 0.04;

    // 触点检测
    cv::goodFeaturesToTrack(image_gray,
                            corners,
                            max_corners,
                            quality_level,
                            min_distance,
                            cv::Mat(),
                            block_size,
                            use_harris,
                            k);

    // 绘制触点
    for (int i = 0; i < corners.size(); i++)
    {
        cv::circle(image_color, corners[i], 5, cv::Scalar(0, 0, 255), 2, 8, 0);
    }

    cv::imshow("corner", image_color);

    cv::waitKey(0);
}

在这里插入图片描述

其他重载函数形式:

  • void cv::goodFeaturesToTrack(InputArray image,OutputArray corners,int maxCorners,double qualityLevel,double minDistance,InputArray mask,int blockSize,int gradientSize,bool useHarrisDetector = false,double k = 0.04)

5、cv::cornerSubPix


细化角点位置。

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

该函数会反复进行迭代,以找到[W FORSTNER. A fast operator for detection and precise location of distincs points, corners and center of circular features. In Proc. of the Intercommission Conference on Fast Processing of Photogrammetric Data, Interlaken, Switzerland, 1987, pages 281–305, 1987.]中所述的拐角或径向鞍点的子像素准确位置,如下图所示:

在这里插入图片描述

亚像素精确角定位器基于以下观察结果:从中心 q q q到位于 q q q附近的点 p p p的每个矢量都正交于 p p p处受图像和测量噪声影响的图像梯度。考虑以下表达式:

ϵ i = D I p i T ⋅ ( q − p i ) \epsilon _i = {DI_{p_i}}^T \cdot (q - p_i) ϵi=DIpiT(qpi)

其中 D I p i T ⋅ {DI_{p_i}}^T \cdot DIpiT q q q附近的点 p i p_i pi之一上的图像梯度。将找到 q q q的值,以使 ϵ i \epsilon _i ϵi最小。 可以将 ϵ i \epsilon _i ϵi设置为零来建立方程组:

∑ i ( D I p i ⋅ D I p i T ) ⋅ q − ∑ i ( D I p i ⋅ D I p i T ⋅ p i ) \sum _i(DI_{p_i} \cdot {DI_{p_i}}^T) \cdot q - \sum _i(DI_{p_i} \cdot {DI_{p_i}}^T \cdot p_i) i(DIpiDIpiT)qi(DIpiDIpiTpi)

梯度在 q q q的邻域(“搜索窗口”)内求和。 将第一梯度项 G G G和第二梯度项 b b b称为: q = G − 1 ⋅ b q = G^{-1} \cdot b q=G1b

该算法将邻域窗口的中心设置在此新中心 q q q处,然后进行迭代,直到中心保持在设置的阈值内。参数如下:

参数名称 参数描述
image 输入单通道,8位或浮点图像。
corners 输入角的初始坐标和提供给输出的精确坐标。
winSize 搜索窗口边长的一半。比如, 如果 winSize=Size(5,5) , 则 搜索窗口大小为(5∗2+1)×(5∗2+1)=11×11
zeroZone 搜索区域中间的盲区大小的一半,未完成以下公式中的求和。 有时使用它来避免自相关矩阵可能的奇异性。 值(-1,-1)表示没有这样的大小。
criteria 终止角点优化迭代过程的条件。 也就是说,角点位置细化的过程在criteria.maxCount迭代之后或在某个迭代中拐角位置移动小于criteria.epsilon时停止。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
using namespace cv;
Mat src, cornerImg, grayImg;
int mxCorners = 10;
RNG rngs = { 12345 };


int main() {
    // 读取图像
    cv::Mat src = cv::imread("images/chessboard.png", cv::IMREAD_COLOR);
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat image_color;
    cv::resize(src,image_color,cv::Size(src.rows / 6,src.cols/6));

    cv::Mat image_copy = image_color.clone();
    // 转换成灰度图像
    cv::Mat image_gray;
    cv::cvtColor(image_color, image_gray, cv::COLOR_BGR2GRAY);

    // 设置触点检测参数
    std::vector<cv::Point2f> corners;
    int max_corners = 100;
    double quality_level = 0.01;
    double min_distance = 10;
    int block_size = 3;
    bool use_harris = false;
    double k = 0.04;

    // 触点检测
    cv::goodFeaturesToTrack(image_gray,
                            corners,
                            max_corners,
                            quality_level,
                            min_distance,
                            cv::Mat(),
                            block_size,
                            use_harris,
                            k);

    // 绘制触点
    for (int i = 0; i < corners.size(); i++)
    {
        cv::circle(image_color, corners[i], 5, cv::Scalar(0, 0, 255), 2, 8, 0);
    }

    // 计算迭代
    cv::TermCriteria criteria = cv::TermCriteria(
                cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,
                40,
                0.01);

    //来像素触点检测
    cv::cornerSubPix(image_gray, corners, cv::Size(5, 5), cv::Size(-1, -1), criteria);

    //绘制触点
    for (int i = 0; i < corners.size(); i++)
    {
        cv::circle(image_copy, corners[i], 5, cv::Scalar(0, 255, 0), 2, 8, 0);
    }

    cv::imshow("corner", image_color);
    cv::imshow("sub pixel corner", image_copy);

    cv::imwrite("corner.jpg", image_color);
    cv::imwrite("corner_sub.jpg", image_copy);
    cv::waitKey(0);
}

在这里插入图片描述

6、cv::HoughCircles


使用霍夫变换在灰度图像中查找圆。

void cv::HoughCircles(InputArray image,OutputArray circles,int method,double dp,double minDist,double param1 = 100,double param2 = 100,int minRadius = 0,int maxRadius = 0)

该函数使用霍夫变换的修改来找到灰度图像中的圆。

注意:通常该函数可以很好地检测圆心。但是,它可能找不到正确的半径。 您可以通过指定半径范围(minRadius和maxRadius)来辅助该功能(如果您知道的话)。或者,对于HOUGH_GRADIENT方法,可以将maxRadius设置为负数,以便仅返回中心而不进行半径搜索,并使用其他过程查找正确的半径。

除非图像已经很柔和,否则还可以使图像稍微平滑一些。 例如,具有7x7内核和1.5x1.5 sigma或类似模糊效果的GaussianBlur()可能会有所帮助。

参数如下:

参数名称 参数描述
image 8位单通道灰度输入图像。
circles 找到的圆的输出向量。 每个向量被编码为3或4个元素浮点向量(x,y,radius)或(x,y,radius,votes)。
method 检测方法, 请参考 HoughModes. 有效的方法为 HOUGH_GRADIENTHOUGH_GRADIENT_ALT
dp 累加器分辨率与图像分辨率的反比。 例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。 如果dp = 2,则累加器的宽度和高度是其一半。 对于HOUGH_GRADIENT_ALT的建议值是dp = 1.5,除非需要很小。
minDist 检测到的圆心之间的最小距离。 如果参数太小,除了真实的圆圈外,还可能会错误地检测到多个邻居圆圈。 如果太大,可能会错过一些圆圈。
param1 第一个方法特定的参数。 如果是HOUGH_GRADIENT,它是传递给Canny边缘检测器的两个阈值中的较高阈值(较低的阈值是较小的两倍)。 请注意HOUGH_GRADIENT_ALT使用的是Scharr算法来计算图像导数,因此阈值通常较高,例如300或正常曝光和对比图像。
param2 第二种方法特定的参数。 如果是HOUGH_GRADIENT,则是检测到的圆心的累加器阈值。 它越小,可能会检测到更多的假圆圈。 与较大的累加器值相对应的圆将首先返回。 对于HOUGH_GRADIENT_ALT算法,此圆为“完美”。 它越接近1,则选择的形状更好的圆形算法。 在大多数情况下,0.9应该可以。 如果要更好地检测小圆圈,可以将其降低到0.85、0.8甚至更低。 但是,然后还要尝试限制搜索范围[minRadius,maxRadius],以避免出现许多错误的圆圈。
minRadius 最小圆半径。
maxRadius 最大圆半径. If <= 0,使用最大图像尺寸. If < 0, HOUGH_GRADIENT 返回中心但未找到半径. HOUGH_GRADIENT_ALT始终计算圆半径。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat img, gray;
    img = cv::imread("images/coints.jpg");
    cvtColor(img, gray, COLOR_BGR2GRAY);
    // 平滑图像
    GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
    vector<Vec3f> circles;
    // 查找圆
    HoughCircles(gray, circles, HOUGH_GRADIENT,
                 2, gray.rows/4, 200, 100 );
    // 绘制圆
    for( size_t i = 0; i < circles.size(); i++ )
    {
        Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        // 绘制圆心
        circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
        // 绘制圆轮廓
        circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    namedWindow( "circles", 1 );
    imshow( "circles", img );
    waitKey(0);
    return 0;
}

在这里插入图片描述

7、cv::HoughLines、cv::HoughLinesP


1)cv::HoughLines:使用标准霍夫变换在二进制图像中查找线。

void cv::HoughLines (InputArray image,OutputArray lines,double rho,double theta,int threshold,double srn = 0,double stn = 0,double min_theta = 0,double max_theta = CV_PI)

该函数实现用于线检测的标准或标准多尺度霍夫变换算法。有关霍夫变换的详细说明,请参见http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm。参数如下:

参数名称 参数描述
image 8位单通道二进制源图像。 该图像可以通过该功能进行修改。
lines 线的输出向量。 每条线由2或3个元素向量(ρ,θ)或(ρ,θ,votes)表示。 ρ是距坐标原点(0,0)(图像的左上角)的距离。 θ是弧度的线旋转角(0〜垂直线,π/ 2〜水平线)。 票数是累加器的价值。
rho 累加器的距离分辨率(以像素为单位)。
theta 累加器的角度分辨率(以弧度为单位)。
threshold Accumulator阈值参数。 仅返回获得足够投票的那些行(> threshold)。
srn 对于多尺度霍夫变换,它是距离分辨率rho的除数。 粗略的累加器距离分辨率为rho,准确的累加器分辨率为rho / srn。 如果srn = 0和stn = 0都使用经典的Hough变换。 否则,这两个参数都应为正。
stn 对于多尺度霍夫变换,它是距离分辨率θ的除数。
min_theta 对于标准和多尺度霍夫变换,请使用最小角度检查线条。 必须介于0和max_theta之间。
max_theta 对于标准和多尺度霍夫变换,请使用最大角度来检查线条。 必须介于min_theta和CV_PI之间。

2)cv::HoughLinesP:使用概率霍夫变换在二进制图像中查找线段。

该函数实现了用于行检测的概率霍夫变换算法,在[Jiri Matas, Charles Galambos, and Josef Kittler. Robust detection of lines using the progressive probabilistic hough transform. Computer Vision and Image Understanding, 78(1):119–137, 2000.]中进行了描述。参数如下:

参数名称 参数描述
image 8位单通道二进制源图像。 该图像可以通过该功能进行修改。
lines 线的输出向量。 每条线由一个4元素向量(x1,y1,x2,y2)表示,其中(x1,y1)和(x2,y2)是每个检测到的线段的终点。
rho 累加器的距离分辨率(以像素为单位)。
theta 累加器的角度分辨率(以弧度为单位)。
threshold Accumulator阈值参数。 仅返回获得足够投票的那些行(> threshold)。
minLineLength 最小线长。 短于此的线段将被拒绝。
maxLineGap 连接同一条线上的点之间的最大允许间隙。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
    // 定义输出变量
    Mat dst, cdst, cdstP;

    // 读取图像
    Mat src = imread("images/building-3.jpg", IMREAD_GRAYSCALE );
    if(src.empty()){
        cerr << "cannot open image.\n";
        return EXIT_FAILURE;
    }
    // 边缘检测
    Canny(src, dst, 180, 200, 3);
    // 转换图像颜色空间
    cvtColor(dst, cdst, COLOR_GRAY2BGR);
    cdstP = cdst.clone();
    // 标准Hough直线检测
    vector<Vec2f> lines; // will hold the results of the detection
    HoughLines(dst, lines, 1, CV_PI/180, 150, 0, 0 ); // runs the actual detection
    //绘制直线
    for( size_t i = 0; i < lines.size(); i++ )
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000*(-b));
        pt1.y = cvRound(y0 + 1000*(a));
        pt2.x = cvRound(x0 - 1000*(-b));
        pt2.y = cvRound(y0 - 1000*(a));
        line( cdst, pt1, pt2, Scalar(0,0,255), 3, LINE_AA);
    }
    // 概率Hough直线检测
    vector<Vec4i> linesP;
    HoughLinesP(dst, linesP, 1, CV_PI/180, 50, 50, 10 );
    // 绘制直线
    for( size_t i = 0; i < linesP.size(); i++ )
    {
        Vec4i l = linesP[i];
        line( cdstP, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
    }
    // 显示结果
    imshow("Source", src);
    imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst);
    imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP);
    waitKey();
    return 0;
}

在这里插入图片描述

8、cv::HoughLinesPointSet


使用标准霍夫变换在一组点中查找线。

void cv::HoughLinesPointSet(InputArray _point,OutputArray _lines,int lines_max,int threshold,double min_rho,double max_rho,double rho_step,double min_theta,double max_theta,double theta_step)

参数如下:

参数名称 参数描述
_point 点的输入向量。 每个向量都必须编码为点向量(x,y)。 类型必须为CV_32FC2或CV_32SC2。
_lines 找到的行的输出向量。 每个向量都被编码为vector (票数,rho,θ)。 “投票”的值越大,霍夫线的可靠性就越高。
lines_max 霍夫线的最大数量。
threshold Accumulator阈值参数。 仅返回获得足够投票的那些行(> threshold)。
min_rho 累加器的最小距离值(以像素为单位)。
max_rho 累加器的最大距离值(以像素为单位)。
rho_step 累加器的距离分辨率(以像素为单位)。
min_theta 累加器的最小角度值(以弧度为单位)。
max_theta 累加器的最大角度值(以弧度为单位)。
theta_step 累加器的角度分辨率(以弧度为单位)。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;
int main()
{

    Mat lines;
    vector<Vec3d> line3d;
    vector<Point2f> point;
    const static float Points[20][2] = {
    { 0.0f,   369.0f }, { 10.0f,  364.0f }, { 20.0f,  358.0f }, { 30.0f,  352.0f },
    { 40.0f,  346.0f }, { 50.0f,  341.0f }, { 60.0f,  335.0f }, { 70.0f,  329.0f },
    { 80.0f,  323.0f }, { 90.0f,  318.0f }, { 100.0f, 312.0f }, { 110.0f, 306.0f },
    { 120.0f, 300.0f }, { 130.0f, 295.0f }, { 140.0f, 289.0f }, { 150.0f, 284.0f },
    { 160.0f, 277.0f }, { 170.0f, 271.0f }, { 180.0f, 266.0f }, { 190.0f, 260.0f }
    };
    for (int i = 0; i < 20; i++)
    {
        point.push_back(Point2f(Points[i][0],Points[i][1]));
    }
    double rhoMin = 0.0f, rhoMax = 360.0f, rhoStep = 1;
    double thetaMin = 0.0f, thetaMax = CV_PI / 2.0f, thetaStep = CV_PI / 180.0f;
    HoughLinesPointSet(point, lines, 20, 1,
                       rhoMin, rhoMax, rhoStep,
                       thetaMin, thetaMax, thetaStep);
    lines.copyTo(line3d);
    printf("votes:%d, rho:%.7f, theta:%.7f\n",(int)line3d.at(0).val[0], line3d.at(0).val[1], line3d.at(0).val[2]);
    return 0;
}

9、cv::preCornerDetect


计算用于拐角检测的特征图。

void cv::preCornerDetect(InputArray src,OutputArray dst,int ksize,int borderType = BORDER_DEFAULT)

该函数计算源图像的基于复杂空间导数的函数:

dst = ( D x src ) 2 ⋅ D y y src + ( D y src ) 2 ⋅ D x x src − 2 D x src ⋅ D y src ⋅ D x y src \texttt{dst} = (D_x \texttt{src} )^2 \cdot D_{yy} \texttt{src} + (D_y \texttt{src} )^2 \cdot D_{xx} \texttt{src} - 2 D_x \texttt{src} \cdot D_y \texttt{src} \cdot D_{xy} \texttt{src} dst=(Dxsrc)2Dyysrc+(Dysrc)2Dxxsrc2DxsrcDysrcDxysrc

其中 D x , D y D_x,D_y DxDy是第一图像导数, D x x , D y y D_{xx},D_{yy} DxxDyy是第二图像导数, D x y D_{xy} Dxy是混合导数。

参数如下:

参数名称 参数描述
src 输入单通道8位浮点图像。
dst 输出图像的类型为CV_32F,大小与src相同。
ksize Sobel算子的光圈大小。
borderType 像素外推法. 请参考BorderTypes.。不支持BORDER_WRAP
#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
using namespace cv;
Mat src, cornerImg, grayImg;
int mxCorners = 10;
RNG rngs = { 12345 };


int main() {
    // 读取图像
    cv::Mat src = cv::imread("images/chessboard.png", cv::IMREAD_COLOR);
    if(src.empty()){
        cerr << "cannot read image.\n";
        return EXIT_FAILURE;
    }
    cv::Mat image_color;
    cv::resize(src,image_color,cv::Size(src.rows / 6,src.cols/6));

    cv::Mat image_copy = image_color.clone();
    // 转换成灰度图像
    cv::Mat image_gray,corners;
    cv::cvtColor(image_color, image_gray, cv::COLOR_BGR2GRAY);

    // 角点检测
//    cv::cornerHarris(image_gray,corners,4,5,0.04);
    cv::Mat dilated_corners;
    cv::preCornerDetect(image_gray, corners, 3);
    cv::dilate(corners, dilated_corners, cv::Mat());
    Mat corner_mask = (corners == dilated_corners);

    cv::imshow("corner", image_color);
    cv::imshow("corner-mask",corner_mask);

    cv::waitKey(0);
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wujuxKkoolerter/article/details/113068453