在图像处理中有时不需要使用所有的像素点,比如二维码定位, 计算二维码尺寸时只需要二维码的4个顶点,因此有时我们需要从图像中提取能够表示图像特征性或者局部特性的像素点,这些像素点称为角点或者特征点, 使用特征点可以极大的减少数据量,提高计算速度.
角点检测
常用的角点:
- 灰度梯度的最大值对应的像素点
- 两条直线或者曲线的交点
- 一阶梯度的导数最大值和梯度方向变化率最大的像素点
- 一阶导数值最大, 但是二阶导数值为0的像素点
Harris角点
Harris角点是最经典的角点之一,其从像素值变化的角度对角点进行定义,像素值的局部最大峰值即为Harris角点. 该角点主要用于检测图像中线段的端点或者两条线段的交点.
/**
* @author IYATT-yx
* @date 2021/2/4
* @brief Harris角点检测
*/
#include "opencv2/opencv.hpp"
#include <iostream>
#include <vector>
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cout << "参数: 请输入图片的路径" << std::endl;
exit(EXIT_FAILURE);
}
cv::Mat src = cv::imread(argv[1]);
if(src.empty())
{
std::cout << "图片读取错误,请检查是否存在" << std::endl;
exit(EXIT_FAILURE);
}
cv::Mat gray;
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
cv::Mat harris;
/**
* @brief 计算出图像中每个像素点的Harris评价系数
*
* @param . 待检测的图像 (CV_8U或CV_32F单通道)
* @param . 存放Harris评价系数的矩阵,和输入图像大小,通道数相同 (CV_32F)
* @param . 领域大小,通常为 2
* @param . 计算提取信息的Sobel算子的半径, 需要是奇数, 多使用3或5
* @param . 权重系数, 一般取值 0.02~0.04
*/
cv::cornerHarris(gray, harris, 2, 3, 0.04);
// 上面计算的到的评价系数,由于取值范围较广并且有正有负,常需要将其归一化到指定区域后
// 再通过阈值比较判断该像素点是否为Harris角点
// (在实际项目中判断阈值往往需要根据实际ingrain和工程经验人为给出
// 阈值较大,提取的角点较少;阈值较小,提取的角点较多.
cv::Mat harrisn;
cv::normalize(harris, harrisn, 0, 255, cv::NORM_MINMAX);
// 将图像数据类型变为CV_8U
cv::convertScaleAbs(harrisn, harrisn);
//寻找Harris角点
std::vector<cv::KeyPoint> keyPoints;
for (int row = 0; row < harrisn.rows; ++row)
{
for (int col = 0; col < harrisn.cols; ++col)
{
int R = harrisn.at<uchar>(row, col);
if (R > 125)
{
cv::KeyPoint keyPoint;
keyPoint.pt.y = static_cast<float>(row);
keyPoint.pt.x = static_cast<float>(col);
keyPoints.push_back(keyPoint);
}
}
}
// 绘制角点
cv::drawKeypoints(src, keyPoints, src);
cv::imshow("系数矩阵", harrisn);
cv::imshow("Harris角点", src);
cv::waitKey(0);
}
Shi-Tomas角点
梯度协方差矩阵的两个特征量与Harris角点的判定相关, 但是由于Harris角点评价系数是两个特征向量的组合, 因此通过Harris角点评价系数的大小不能完全地概括两个特征量之间的大小关系,因此Shi和Tmas对Harris角点的判断指标进行了调整,将特征向量的最小值作为角点评价系数. 该角点本质上就是对Harris角点的一种变形.
(关键点: 对图像中含有特殊信息的像素点的一种称呼,主要含有像素点的位置,角度等信息)
/**
* @author IYATT-yx
* @date 2021/2/4
* @brief Harris角点检测
*/
#include "opencv2/opencv.hpp"
#include <iostream>
#include <vector>
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cout << "参数: 请输入图片的路径" << std::endl;
exit(EXIT_FAILURE);
}
cv::Mat src = cv::imread(argv[1]);
if(src.empty())
{
std::cout << "图片读取错误,请检查是否存在" << std::endl;
exit(EXIT_FAILURE);
}
cv::Mat gray;
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
std::vector<cv::Point2f> corners;
/**
* @brief 寻找图像中指定区域的Shi-Tomas角点, 区别于Harris角点检测函数,该函数的阈值与最佳角点相对应,避免了
* 绝对阈值在不同图像中效果不理想的现象,另外,函数可以直接输出角点坐标,不需要根据输出结果再次判断是否为角点
*
* @param . 待检测的图像
* @param . 存放检测到的角点坐标
* @param . 寻找角点数目的最大值
* @param . 角点阈值和最佳角点的关系 - 若最佳角点为 n ,当该参数设置为 k 时, 那么 n * k 就是角点检测的阈值
* @param . 两个角点之间的最小欧式距离
*/
cv::goodFeaturesToTrack(gray, corners, 100, 0.01, 0.04);
// 转存检测到的角点坐标,准备绘出
std::vector<cv::KeyPoint> keyPoints;
for (size_t i = 0; i < corners.size(); ++i)
{
cv::KeyPoint temp;
temp.pt = corners[i];
keyPoints.push_back(temp);
}
// 绘制角点
cv::drawKeypoints(src, keyPoints, src);
cv::imshow("Shi-Tomas角点检测结果", src);
cv::waitKey(0);
}
亚像素界别角点
无论是Harris角点检测还是Shi_Tomas角点检测, OpenCV 4 提供的相关函数都只能得到像素级别的角点 (角点的坐标是整数), 但是有时在实际的项目中或者任务中需要更高精度的亚像素级别的角点坐标,因此需要对像素级别的角点坐标给予进一步优化.
/**
* @author IYATT-yx
* @date 2021/2/4
* @brief 亚像素级别角点检测
*/
#include "opencv2/opencv.hpp"
#include <iostream>
#include <vector>
#include <string>
int main(int argc