Opencv之角点 Harris、Shi-Tomasi 检测详解

  • 角点,即图像中某些属性较为突出的像素点

  • 常用的角点有以下几种:

    • 梯度最大值对应的像素点
    • 两条直线或者曲线的交点
    • 一阶梯度的导数最大值和梯度方向变化率最大的像素点
    • 一阶导数值最大,但是二阶导数值为0的像素点

在这里插入图片描述

  • API简介:
void  cornerHarris (
    InputArray      src,   // 输入图像 (单通道,8位或浮点型)
    OutputArray     dst,   // 输出图像 (类型 CV_32FC1,大小同 src)
    int      blockSize,    // 邻域大小
    int      ksize,        // Sobel 算子的孔径大小
    double   k,            // 经验参数,取值范围 0.04 ~ 0.06
    int      borderType = BORDER_DEFAULT    // 边界模式
)     
void  goodFeaturesToTrack (     
        InputArray      image,     // 输入图像 (单通道,8位或浮点型32位)
        OutputArray     corners,   // 检测到的角点
        int         maxCorners,    // 最多允许返回的角点数量
        double      qualityLevel,  //  质量水平
        double      minDistance,   // 角点间的最小欧拉距离
        InputArray  mask = noArray(), //
        int         blockSize = 3,    //
        bool        useHarrisDetector = false,  //
        double      k = 0.04  // 
    )     
void  cornerSubPix(
        InputArray          image,  // 输入图象(单通道,8位或浮点型)
        InputOutputArray  corners,  // 亚像素精度的角点坐标
        Size              winSize,  // 搜索窗口尺寸的 1/2
        Size             zeroZone,  //
        TermCriteria     criteria   // 迭代终止准则
)     

1. Harris角点

  • 主要用于检测图像中线段的端点或者两条线段的交点

  • 在图像中定义一个局部小窗口,然后沿各个方向移动这个窗口,则会出现 a) b) c) 三种情况,分别对应平坦区、边缘和角点,下图给出了检测思路:

    • 窗口内的图像强度,在窗口向各个方向移动时,都没有发生变化,则窗口内都是 “平坦区”,不存在角点
    • 窗口内的图像强度,在窗口向某一个 (些) 方向移动时,发生较大变化;而在另一些方向不发生变化,那么,窗口内可能存在 “边缘”
    • 窗口内的图像强度,在窗口向各个方向移动时,都发生了较大的变化,则认为窗口内存在 “角点”

 a)  flat region              b)  edge                     c)  corner

  • 其主要理论如下:
    在这里插入图片描述
    在这里插入图片描述
  • demo
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src, src_gray;
int thresh = 200;
int max_thresh = 255;
const char* source_window = "Source image";
const char* corners_window = "Corners detected";
void cornerHarris_demo( int, void* );
int main( int argc, char** argv )
{
 CommandLineParser parser( argc, argv, "{@input | building.jpg | input image}" );
 src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
 if ( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }
 cvtColor( src, src_gray, COLOR_BGR2GRAY );
 namedWindow( source_window );
 createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
 imshow( source_window, src );
 cornerHarris_demo( 0, 0 );
 waitKey();
 return 0;
}
void cornerHarris_demo( int, void* )
{
 int blockSize = 2;
 int apertureSize = 3;
 double k = 0.04;
 Mat dst = Mat::zeros( src.size(), CV_32FC1 );
 cornerHarris( src_gray, dst, blockSize, apertureSize, k );
 Mat dst_norm, dst_norm_scaled;
 normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
 convertScaleAbs( dst_norm, dst_norm_scaled );
 for( int i = 0; i < dst_norm.rows ; i++ )
 {
 for( int j = 0; j < dst_norm.cols; j++ )
 {
 if( (int) dst_norm.at<float>(i,j) > thresh )
 {
 circle( dst_norm_scaled, Point(j,i), 5, Scalar(0), 2, 8, 0 );
 }
 }
 }
 namedWindow( corners_window );
 imshow( corners_window, dst_norm_scaled );
}

在这里插入图片描述

2. Shi-Tomasi 角点

  • 后来在1994年,J. Shi和C. Tomasi在他们的论文Good Features to Track中对其进行了小的修改,与Harris Corner Detector相比,显示出更好的结果。哈里斯角探测器中的评分函数由下式给出:
    在这里插入图片描述

  • OpenCV有一个函数 cv.goodFeaturesToTrack()。它通过Shi-Tomasi方法(或Harris角点检测,如果您指定的话)在图像中找到N个最强的角落。首先图像是灰度图像。提前指定要查找的角点数,然后,指定质量值,该值介于 0-1 之间,表示角的最低质量,低于该质量,每个都被拒绝。然后,我们提供检测到的拐角之间的最小欧氏距离。

  • 有了所有这些信息,该函数就会在图像中找到角落。所有低于质量水平的角落都将被剔除。然后,它根据质量按降序对剩余的角进行排序。然后函数取第一个最强角,丢弃最小距离范围内的所有附近角,并返回 N 个最强角。

在下面的示例中,我们将尝试找到 25 个最佳角:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('blox.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
corners = cv.goodFeaturesToTrack(gray,25,0.01,10)
corners = np.int0(corners)
for i in corners:
 x,y = i.ravel()
 cv.circle(img,(x,y),3,255,-1)
plt.imshow(img),plt.show()

在这里插入图片描述

  • 使用实例:
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src, src_gray;
int maxCorners = 23;
int maxTrackbar = 100;
RNG rng(12345);
const char* source_window = "Image";
void goodFeaturesToTrack_Demo( int, void* );
int main( int argc, char** argv )
{
 CommandLineParser parser( argc, argv, "{@input | pic3.png | input image}" );
 src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
 if( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }
 cvtColor( src, src_gray, COLOR_BGR2GRAY );
 namedWindow( source_window );
 createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo );
 imshow( source_window, src );
 goodFeaturesToTrack_Demo( 0, 0 );
 waitKey();
 return 0;
}
void goodFeaturesToTrack_Demo( int, void* )
{
 maxCorners = MAX(maxCorners, 1);
 vector<Point2f> corners;
 double qualityLevel = 0.01;
 double minDistance = 10;
 int blockSize = 3, gradientSize = 3;
 bool useHarrisDetector = false;
 double k = 0.04;
 Mat copy = src.clone();
 goodFeaturesToTrack( src_gray,
 corners,
 maxCorners,
 qualityLevel,
 minDistance,
 Mat(),
 blockSize,
 gradientSize,
 useHarrisDetector,
 k );
 cout << "** Number of corners detected: " << corners.size() << endl;
 int radius = 4;
 for( size_t i = 0; i < corners.size(); i++ )
 {
 circle( copy, corners[i], radius, Scalar(rng.uniform(0,255), rng.uniform(0, 256), rng.uniform(0, 256)), FILLED );
 }
 namedWindow( source_window );
 imshow( source_window, copy );
}

在这里插入图片描述

3. 角点检测器

  • 使用 OpenCV 函数 cv::cornerEigenValsAndVecs 查找特征值和特征向量,以确定像素是否为角。
  • 使用 OpenCV 函数 cv::cornerMinEigenVal 查找角检测的最小特征值。
  • 通过使用上述两个函数实现我们自己的哈里斯检测器版本以及 Shi-Tomasi 检测器。
  • 使用实例:
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src, src_gray;
Mat myHarris_dst, myHarris_copy, Mc;
Mat myShiTomasi_dst, myShiTomasi_copy;
int myShiTomasi_qualityLevel = 50;
int myHarris_qualityLevel = 50;
int max_qualityLevel = 100;
double myHarris_minVal, myHarris_maxVal;
double myShiTomasi_minVal, myShiTomasi_maxVal;
RNG rng(12345);
const char* myHarris_window = "My Harris corner detector";
const char* myShiTomasi_window = "My Shi Tomasi corner detector";
void myShiTomasi_function( int, void* );
void myHarris_function( int, void* );
int main( int argc, char** argv )
{
 CommandLineParser parser( argc, argv, "{@input | building.jpg | input image}" );
 src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
 if ( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }
 cvtColor( src, src_gray, COLOR_BGR2GRAY );
 int blockSize = 3, apertureSize = 3;
 cornerEigenValsAndVecs( src_gray, myHarris_dst, blockSize, apertureSize );
 /* calculate Mc */
 Mc = Mat( src_gray.size(), CV_32FC1 );
 for( int i = 0; i < src_gray.rows; i++ )
 {
 for( int j = 0; j < src_gray.cols; j++ )
 {
 float lambda_1 = myHarris_dst.at<Vec6f>(i, j)[0];
 float lambda_2 = myHarris_dst.at<Vec6f>(i, j)[1];
 Mc.at<float>(i, j) = lambda_1*lambda_2 - 0.04f*((lambda_1 + lambda_2) * (lambda_1 + lambda_2));
 }
 }
 minMaxLoc( Mc, &myHarris_minVal, &myHarris_maxVal );
 /* Create Window and Trackbar */
 namedWindow( myHarris_window );
 createTrackbar( "Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myHarris_function );
 myHarris_function( 0, 0 );
 cornerMinEigenVal( src_gray, myShiTomasi_dst, blockSize, apertureSize );
 minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal );
 /* Create Window and Trackbar */
 namedWindow( myShiTomasi_window );
 createTrackbar( "Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function );
 myShiTomasi_function( 0, 0 );
 waitKey();
 return 0;
}
void myShiTomasi_function( int, void* )
{
 myShiTomasi_copy = src.clone();
 myShiTomasi_qualityLevel = MAX(myShiTomasi_qualityLevel, 1);
 for( int i = 0; i < src_gray.rows; i++ )
 {
 for( int j = 0; j < src_gray.cols; j++ )
 {
 if( myShiTomasi_dst.at<float>(i,j) > myShiTomasi_minVal + ( myShiTomasi_maxVal - myShiTomasi_minVal )*myShiTomasi_qualityLevel/max_qualityLevel )
 {
 circle( myShiTomasi_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED );
 }
 }
 }
 imshow( myShiTomasi_window, myShiTomasi_copy );
}
void myHarris_function( int, void* )
{
 myHarris_copy = src.clone();
 myHarris_qualityLevel = MAX(myHarris_qualityLevel, 1);
 for( int i = 0; i < src_gray.rows; i++ )
 {
 for( int j = 0; j < src_gray.cols; j++ )
 {
 if( Mc.at<float>(i,j) > myHarris_minVal + ( myHarris_maxVal - myHarris_minVal )*myHarris_qualityLevel/max_qualityLevel )
 {
 circle( myHarris_copy, Point(j,i), 4, Scalar( rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256) ), FILLED );
 }
 }
 }
 imshow( myHarris_window, myHarris_copy );
}

在这里插入图片描述

4. 使用亚像素精度

  • 使用 OpenCV 函数 cv::cornerSubPix 查找更精确的角位置(比整数像素更精确)。
  • 使用实例:
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src, src_gray;
int maxCorners = 10;
int maxTrackbar = 25;
RNG rng(12345);
const char* source_window = "Image";
void goodFeaturesToTrack_Demo( int, void* );
int main( int argc, char** argv )
{
 CommandLineParser parser( argc, argv, "{@input | pic3.png | input image}" );
 src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
 if( src.empty() )
 {
 cout << "Could not open or find the image!\n" << endl;
 cout << "Usage: " << argv[0] << " <Input image>" << endl;
 return -1;
 }
 cvtColor( src, src_gray, COLOR_BGR2GRAY );
 namedWindow( source_window );
 createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_Demo );
 imshow( source_window, src );
 goodFeaturesToTrack_Demo( 0, 0 );
 waitKey();
 return 0;
}
void goodFeaturesToTrack_Demo( int, void* )
{
 maxCorners = MAX(maxCorners, 1);
 vector<Point2f> corners;
 double qualityLevel = 0.01;
 double minDistance = 10;
 int blockSize = 3, gradientSize = 3;
 bool useHarrisDetector = false;
 double k = 0.04;
 Mat copy = src.clone();
 goodFeaturesToTrack( src_gray,
 corners,
 maxCorners,
 qualityLevel,
 minDistance,
 Mat(),
 blockSize,
 gradientSize,
 useHarrisDetector,
 k );
 cout << "** Number of corners detected: " << corners.size() << endl;
 int radius = 4;
 for( size_t i = 0; i < corners.size(); i++ )
 {
 circle( copy, corners[i], radius, Scalar(rng.uniform(0,255), rng.uniform(0, 256), rng.uniform(0, 256)), FILLED );
 }
 namedWindow( source_window );
 imshow( source_window, copy );
 Size winSize = Size( 5, 5 );
 Size zeroZone = Size( -1, -1 );
 TermCriteria criteria = TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 40, 0.001 );
 cornerSubPix( src_gray, corners, winSize, zeroZone, criteria );
 for( size_t i = 0; i < corners.size(); i++ )
 {
 cout << " -- Refined Corner [" << i << "] (" << corners[i].x << "," << corners[i].y << ")" << endl;
 }
}

在这里插入图片描述

参考

1. https://blog.csdn.net/fengweichangzi/article/details/119001661?spm=1001.2014.3001.5506
2. https://docs.opencv.org/4.x/d9/d97/tutorial_table_of_content_features2d.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月醉窗台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值