Opencv学习之角点检测

article/2025/11/9 22:53:33

Opencv学习之角点检测

角点检测

在图像处理和计算机视觉领域,兴趣点(interest points),也被称作关键点(key points)、特征点(feture points)。它被大量用于解决物体识别、图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题,如果能检测到足够多特殊的点,同时它们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就具有使用价值。
图像特征类型被分为以下三种:
(1)边缘
(2)角点(感兴趣关键点):如果某一点在任意方向的一个微小变动都会引起灰度很大的变化。
(3)斑点(Blobs)(感兴趣区域)
关于角点的具体描述:
*一阶导数(灰度的梯度)的局部最大所对应的像素点。
*两条及两条以上边缘的交点。
*图像中梯度值和梯度方向的变化速率都很高的点。
*角点处的一阶导数最大,二阶导数为零,它指示了物体边缘变化不连续的方向。
目前图像处理领域角点检测算法归纳:
*基于灰度图像的角点检测(又分为,基于梯度、基于模板(图像亮度的变化,将邻点亮度对比足够大的点定义为角点)、基于模板梯度组合三类方法)
*基于二值图像的角点检测
*基于轮廓曲线的角点检测

Harris角点检测–cornerHarris函数

Harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。
void cornerHarris(inputArray src,outputArray dst,int blockSize,int ksize,double k, int borderType=BORDER_DEFAULT)
*第一个参数,输入图像,须为单通道8位或者浮点型图像。
*第二个参数,用于存放Harris角点检测的输出结果,和源图像有一样的尺寸和类型。
*第三个参数,表示邻域的大小。
*第四个参数,表示Sobel()算子的孔径大小。
*第五个参数,Harris焦点响应函数中的参数。
*第六个参数,图像像素的边界模式,默认值BORDER_DEFAULT。

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
//宏定义部分
#define WINDOW_NAME1 "image[procedure window1]"
#define WINDOW_NAME2 "image[procedure window2]"
//全局变量声明
Mat g_srcImage,g_srcImage1,g_grayImage;
int g_nThresh=30;//当前阈值
int g_nThresh_max=175;//最大阈值
//全局函数声明
void on_CornerHarris(int ,void *);//回调函数
//主函数
int main()
{//载入源图像显示并进行克隆保存g_srcImage=imread("/Users/new/Desktop/2.jpg");if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}imshow(WINDOW_NAME1, g_srcImage);g_srcImage1=g_srcImage.clone();//转换为灰度图像cvtColor(g_srcImage1, g_grayImage, COLOR_BGR2GRAY);//创建窗口和阈值滑动条namedWindow(WINDOW_NAME1);createTrackbar("threshold: ", WINDOW_NAME1, &g_nThresh, g_nThresh_max,on_CornerHarris);on_CornerHarris(0, 0);waitKey(0);return 0;
}
//回调函数定义
void on_CornerHarris(int ,void *)
{//定义一些局部变量Mat dstImage;//目标图Mat normImage;//归一化后图Mat scaledImage;//线性变换后的八位无符号整型的图//初始化//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值dstImage=Mat::zeros(g_srcImage.size(), CV_32FC1);g_srcImage1=g_srcImage.clone();//正式检测//进行角点检测cornerHarris(g_grayImage, dstImage, 2, 3, 0.04);//归一化normalize(dstImage, normImage,0,255,NORM_MINMAX,CV_32FC1,Mat());//将归一化后的图线性变换成8位无符号整型convertScaleAbs(normImage, scaledImage);//进行绘制for(int j=0;j<normImage.rows;j++)for(int i=0;i<normImage.cols;++i){if((int)normImage.at<float>(j,i)>g_nThresh+80){circle(g_srcImage1, Point(i,j), 3, Scalar(10,10,255),2,8,0);circle(scaledImage, Point(i,j), 3, Scalar(10,10,255),2,8,0);}}//显示最终结果imshow(WINDOW_NAME1,g_srcImage1);imshow(WINDOW_NAME2,scaledImage);
}

这里写图片描述

Shi-Tomasi角点检测–goodFeaturesToTrack函数

Shi-Tomasi算法是Harris算法的改进,此算法最原始的定义是将矩阵M的行列式与M的迹相减,再将差值同预先给定的阈值进行比较,后来Shi和Tomasi提出了改进方法,若两个特征值中较小的一个大于最小的阈值的话,则会得到强角点。
goodFeaturesToTrack函数就是确定图像强角点的函数。
void goodFeaturesToTrack(inputArray image,outputArray corners,int maxCorners,double qualityLevel,double minDistance,inputArray mask=noArray(),int blockSize=3,bool useHarrisDetector=false,double k=0.4)
*第一个参数,输入图像,须为8位或浮点型32位单通道图像。
*第二个参数,检测到的角点的输出向量。
*第三个参数,角点的最大数量。
*第四个参数,角点检测可接受的最小特征值。其实实际用于过滤角点的最小特征值时qualityLevel与图像中最大特征值的乘积。所以qualityLevel通常不会超过1(常用的值为0.10或者0.01),而检测完所有的角点后,还要进一步剔除掉一些距离较近的角点。
*第五个参数,角点之间的最小距离,此参数用于保证返回的角点之间的距离不小于此参数个像素。
*第六个参数,可选参数,表示感兴趣区域,默认值noArray(),若此参数非空(需为CV_8UC1类型,且和第一个参数image有相同的尺寸),便用于指定角点检测区域。
*第七个参数,默认值3,是计算导数自相关矩阵时指定的邻域范围。
*第八个参数,默认值false,只是是否使用Harris角点检测。
*第九个参数,默认值0.04,为用于设置Hessian自相关矩阵行列式的相对权重的权重系数。
另外值得一提,goodFeaturesToTrack函数可以用来初始化一个基于点的对象跟踪操作。

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//宏定义部分
#define WINDOW_NAME1 "image[Shi-Tomasi corner dtector]"
//全局变量声明
Mat g_srcImage,g_grayImage;
int g_maxCornerNumber=33;
int g_maxTrackbarNumber=500;
RNG g_rng(12345);
//全局函数声明
void on_GoodFeaturesToTrack(int ,void *);//回调函数
//主函数
int main()
{//载入源图像并将其转换为灰度图g_srcImage=imread("/Users/new/Desktop/2.jpg");if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);//创建窗口和滑动条namedWindow(WINDOW_NAME1);createTrackbar("max corners_num: ", WINDOW_NAME1, &g_maxCornerNumber, g_maxTrackbarNumber,on_GoodFeaturesToTrack);imshow(WINDOW_NAME1,g_srcImage);on_GoodFeaturesToTrack(0, 0);waitKey(0);return 0;
}
//回调函数定义
void on_GoodFeaturesToTrack(int ,void *)
{//对变量小于等于1时的处理if(g_maxCornerNumber<=1){g_maxCornerNumber=1;}//Shi-Tomasi算法的参数准备vector<Point2f>corners;double qualityLevel=0.01;//角点检测可接受的最小特征double minDistance=10;//角点之间的最小距离int blockSize=3;//计算导数自相关矩阵时指定的邻域范围double k=0.04;//权重系数Mat copy=g_srcImage.clone();//复制源图像到一个临时变量中,作为感兴趣区域//进行Shi-Tomasi角点检测goodFeaturesToTrack(g_grayImage, corners, g_maxCornerNumber, qualityLevel, minDistance,Mat(),blockSize,false,k);//输出文字信息cout<<">此次检测到的角点数量为:"<<corners.size()<<endl;//绘制检测到的角点int r=4;for(unsigned int i=0;i<corners.size();++i){//以随机颜色绘制角点circle(copy, corners[i], r, Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),-1,8,0);}imshow(WINDOW_NAME1, copy);
}

这里写图片描述

亚像素级角点检测–cornerSubPix函数

若进行图像处理的目的不是提取用于识别的特征点而是进行几何测量,这通常需要更高的精度,而函数goodFeaturesToTrack函数只能提供简单的像素的坐标值,也就是说,有时候会需要实数坐标值而不是整数坐标值。
亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重建被跟踪目标的三维结构时,是一个基本的测量值。
cornerSubPix函数用于寻找亚像素角点位置(不是整数类型的位置,而是更精确的浮点类型的位置)。
void cornerSubPix(inputArray image,inputoutputArray corners,Size winSize,Size zeroZone,TermCriteria criteria)
*第一个参数,输入图像。
*第二个参数,提供输入角点的初始坐标和精确的输出坐标。
第三个参数,搜索窗口的一半尺寸,若winSize=Size(5,5),那么就表示使用(5*2+1)(5*2+1)=11x11大小的搜索窗口。
*第四个参数,表示死区的一半尺寸,而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值(-1,-1)表示没有死区。
*第五个参数,求角点的迭代过程的终止条件,即角点位置的确定,要么迭代数大于某个设定值,或者是精确度达到某个设定值,criteria可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//宏定义部分
#define WINDOW_NAME1 "image[Shi-Tomasi corner dtector]"
//全局变量声明
Mat g_srcImage,g_grayImage;
int g_maxCornerNumber=33;
int g_maxTrackbarNumber=500;
RNG g_rng(12345);
//全局函数声明
void on_GoodFeaturesToTrack(int ,void *);//回调函数
//主函数
int main()
{//载入源图像并将其转换为灰度图g_srcImage=imread("/Users/new/Desktop/2.jpg");if(!g_srcImage.data){printf("读取源图像srcImage错误~!\n");return false;}cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);//创建窗口和滑动条namedWindow(WINDOW_NAME1);createTrackbar("max corners_num: ", WINDOW_NAME1, &g_maxCornerNumber, g_maxTrackbarNumber,on_GoodFeaturesToTrack);imshow(WINDOW_NAME1,g_srcImage);on_GoodFeaturesToTrack(0, 0);waitKey(0);return 0;
}
//回调函数定义
void on_GoodFeaturesToTrack(int ,void *)
{//对变量小于等于1时的处理if(g_maxCornerNumber<=1){g_maxCornerNumber=1;}//Shi-Tomasi算法的参数准备vector<Point2f>corners;double qualityLevel=0.01;//角点检测可接受的最小特征double minDistance=10;//角点之间的最小距离int blockSize=3;//计算导数自相关矩阵时指定的邻域范围double k=0.04;//权重系数Mat copy=g_srcImage.clone();//复制源图像到一个临时变量中,作为感兴趣区域//进行Shi-Tomasi角点检测goodFeaturesToTrack(g_grayImage, corners, g_maxCornerNumber, qualityLevel, minDistance,Mat(),blockSize,false,k);//输出文字信息cout<<">此次检测到的角点数量为:"<<corners.size()<<endl;//亚像素角点检测的参数设置Size winSize=Size(5,5);Size zeroZone=Size(-1,-1);TermCriteria criteria=TermCriteria(TermCriteria::EPS+TermCriteria::MAX_ITER,400, 0.001);//计算出亚像素点角点位置cornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);//输出角点信息for(int i=0;i<corners.size();++i){cout<<"\t精确角点坐标["<<i<<"]("<<corners[i].x<<","<<corners[i].y<<")"<<endl;}//绘制检测到的角点int r=4;for(unsigned int i=0;i<corners.size();++i){//以随机颜色绘制角点circle(copy, corners[i], r, Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),-1,8,0);}imshow(WINDOW_NAME1, copy);
}

这里写图片描述

Opencv技巧

(1) //将归一化后的图线性变换成8位无符号整型

 convertScaleAbs(normImage, scaledImage);
(2)定义角点的迭代过程的终止条件:
TermCriteria       criteria=TermCriteria(TermCriteria::EPS+TermCriteria::MAX_ITER,400, 0.001);

http://chatgpt.dhexx.cn/article/cbCQfU9q.shtml

相关文章

VBA-Range.AutoFilter 方法浅析

VBA-Range.AutoFilter 方法的一些“坑” 学到筛选的时候遇到一些小小的“坑”&#xff0c;记录下。 录制出来的宏是这样的&#xff0c; Sub 宏1()宏1 宏Rows("1:1").SelectSelection.AutoFilterActiveSheet.Range("$A$1:$F$1048").AutoFilter field:4, …

Hibernate查询Query By Criterial

提供的检索方式&#xff1a;&#xff08;1&#xff09;导航对象图检索方式 &#xff08;2&#xff09;OID检索方式&#xff08;3&#xff09;HQL检索方式&#xff08;4&#xff09;QBC检索方式[query by Criteria(标准)]&#xff08;5&#xff09;本地SQL检索方式 1、简介 1.…

SLUB和SLAB的区别

转载&#xff1a;http://www.cnblogs.com/tolimit/ 首先为什么要说slub分配器&#xff0c;内核里小内存分配一共有三种&#xff0c;SLAB/SLUB/SLOB&#xff0c;slub分配器是slab分配器的进化版&#xff0c;而slob是一种精简的小内存分配算法&#xff0c;主要用于嵌入式系统。慢…

[linux kernel]slub内存管理分析(7) MEMCG的影响与绕过

文章目录 背景前情回顾描述方法约定 MEMCG总览省流总结简介 slub 相关 memcg机制kernel 5.9 版本之前结构体初始化具体实现 kernel 5.9-5.14kernel 5.14 之后 突破slab限制方法cross cache attackpage 堆风水 总结 背景 前情回顾 关于slab几个结构体的关系和初始化和内存分配…

linux slub分配器,Vi Linux内存 之 Slub分配器(六)

再来看内置式对象&#xff0c;如下图所示。指针位于对象的头部&#xff0c;与对象共用存储空间。这是因为对象被分配出去之前&#xff0c;其存储空间是空闲的可用状态&#xff0c;可用于存放空闲对象指针。对象被分配出去后&#xff0c;也不再需要这个指针了&#xff0c;可以被…

一文给你解决linux内存源码分析- SLUB分配器概述(超详细)

SLUB和SLAB的区别 首先为什么要说slub分配器&#xff0c;内核里小内存分配一共有三种&#xff0c;SLAB/SLUB/SLOB&#xff0c;slub分配器是slab分配器的进化版&#xff0c;而slob是一种精简的小内存分配算法&#xff0c;主要用于嵌入式系统。慢慢的slab分配器或许会被slub取代&…

Linux内存管理(2):SLAB/SLUB系统(基于线性映射)

一、概述 伙伴系统最大限度地解决了内存管理地外碎片问题,但是对于内碎片问题却无能为力。但内核实际使用内存的时候,却大多是小于一个页的单位。为了解决内核自身使用小块内存的碎片问题,Linux引入了基于对象的内存管理(或者叫内存区管理,Memory Area Management),就是SL…

slub分配器学习系列之linux5.10

前言 前一篇文章对 linux5.10 的 slab 分配器底层实现进行了探究与学习。进一步地&#xff0c;本篇文章将对 linux5.10 的 slub 分配器进行探究&#xff0c;对比看看两者的实现有何不同&#xff0c;做了哪些"必要的"改进。 slub 分配器 关于 slub 分配器的基本原理…

linux内核虚拟内存之slub分配器

上一章主要讲述以页为最小单位进行内存分配的伙伴管理算法&#xff0c;较大程度上避免了内存碎片问题。而实际上对内存的申请却不是每次都申请一个页面的&#xff08;比如文件节点&#xff0c;任务描述符等结构体内存&#xff09;&#xff0c;通常是远小于一个内存页面的大小&a…

4.19内核SLUB内存分配器

初始化 内核的大部分管理数据结构都是通过kmalloc分配内存的&#xff0c;那么slab本身结构的内存管理就出现了一个鸡与蛋的问题&#xff0c;slab数据结构所需内存远小于一整页的内存块&#xff0c;这些最适合kmalloc分配&#xff0c;而kmalloc只有在slab初始化完之后才能使用。…

SLUB内存管理之slub初始化

在讲slub内存管理涉及的四个函数之前&#xff0c;先从slub内存分配算法的初始化开始。系统启动时&#xff0c;会进行slub内存分配算法的初始化&#xff0c;函数流程是&#xff1a;start_kernel() -> mm_init()->kmem_cache_init()。在start_kernel()函数中的setup_arch()…

SLUB DEBUG原理

1. 前言 在工作中&#xff0c;经常会遇到由于越界导致的各种奇怪的问题。为什么越界访问导致的问题很奇怪呢&#xff1f;在工作差不多半年的时间里我就遇到了很多越界访问导致的问题&#xff08;不得不吐槽下IC厂商提供的driver&#xff0c;总是隐藏着bug&#xff09;。比如说…

slub allocator工作原理

前言 在Linux中&#xff0c;伙伴系统&#xff08;buddy system&#xff09;是以页&#xff08;1page等于4K&#xff09;为单位管理和分配内存。对于小内存的分配&#xff0c;如果还是使用伙伴系统进行内存分配&#xff0c;就会导致严重浪费内存。此时&#xff0c;slab分配器就应…

SLUB缓存管理

注&#xff1a;本文分析基于linux-4.18.0-193.14.2.el8_2内核版本&#xff0c;即CentOS 8.2 1、关于SLUB 目前大多数系统都不使用slab作为缓存的管理模式&#xff0c;转而使用slub&#xff0c;比如CentOS 7和8默认都是使用slub管理器。slub是基于slab的进一步优化改进&#x…

slub debug(linux4.16.1)

在命令行选项中添加slub_debugUFPZ&#xff0c;以使能相关检测功能&#xff0c;其含义如下&#xff1a; &#xff08;1&#xff09;U&#xff1a;跟踪该slab内存的相关属性&#xff0c;如其创建时所属的cpu、进程、时间 &#xff08;2&#xff09;F&#xff1a;开启sanity检查功…

linux内核内存管理slub

一、概述 linux内存管理核心是伙伴系统&#xff0c;slab&#xff0c;slub&#xff0c;slob是基于伙伴系统之上提供api&#xff0c;用于内核内存分配释放管理&#xff0c;适用于小内存&#xff08;小于&#xff11;页&#xff09;分配与释放&#xff0c;当然大于&#xff11;页…

内存管理 slub算法

内核管理页面使用了2个算法&#xff1a;伙伴算法和slub算法&#xff0c;伙伴算法以页为单位管理内存&#xff0c;但在大多数情况下&#xff0c;程序需要的并不是一整页&#xff0c;而是几个、几十个字节的小内存。于是需要另外一套系统来完成对小内存的管理&#xff0c;这就是s…

linux slub分配器,slub分配器

原标题&#xff1a;slub分配器 概述&#xff1a; Linux的物理内存管理采用了以页为单位的buddy system(伙伴系统)&#xff0c;但是很多情况下&#xff0c;内核仅仅需要一个较小的对象空间&#xff0c;而且这些小块的空间对于不同对象又是变化的、不可预测的&#xff0c;所以需要…

Linux内存管理(八): slub分配器和kmalloc

kernel: 5.10 Arch: aarch64 上文 介绍过&#xff0c; 伙伴系统在分配内存时是以物理页为单位的&#xff0c;但在很多场景下&#xff0c;内存需要分配的大小是以字节为单位的&#xff0c;达不到一个物理页的大小。如果继续使用伙伴系统进行内存分配&#xff0c; 那么就会出现严…

Linux 踩内存 slub,Linux SLUB 内存分配器分析

本文简介 本文主要介绍了Linux SLUB分配的产生原因、设计思路及其代码分析。适合于对Linux内核&#xff0c;特别是对Linux内存分配器感兴趣的读者。 1.为何需要SLUB&#xff1f; Linux SLUB内存分配器合入Linux主分支已经整整10年了&#xff01;并且是Linux目前默认的内存分配器…