<h1 style="font-size: 32px; font-weight: bold; border-bottom: 2px solid rgb(204, 204, 204); padding: 0px 4px 0px 0px; text-align: left; margin: 0px 0px 10px;">亚像素级角点检测</h1><p style="white-space: normal;">视频讲解如下:</p><p><embed src="//player.bilibili.com/player.html?aid=685151870&bvid=BV1TU4y197WA&cid=751638305&page=1&high_quality=1&danmaku=0" width="817" height="460" wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true"/></p><p><br/></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; color: rgb(51, 51, 51); box-sizing: border-box;">当前系列所有demo下载地址:</p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; color: rgb(51, 51, 51); box-sizing: border-box;"><a href="https://github.com/GaoRenBao/OpenCv4-Demo" target="_blank" style="margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(0, 102, 0); transition-duration: 0.2s; transition-property: opacity; outline: none; opacity: 0.8;">https://github.com/GaoRenBao/OpenCv4-Demo</a></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; box-sizing: border-box; color: rgb(51, 51, 51);">不同编程语言对应的OpenCv版本以及<span style="">开发环境</span>信息如下:<span style="font-family: Calibri; font-size: 14px;"> </span></p><table border="1" style="border-right: none; border-bottom: none; border-image: initial; border-left: 1px solid rgb(102, 102, 102); border-top: 1px solid rgb(102, 102, 102);"><tbody><tr class="firstRow"><td width="81" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;">语言</span></strong></p></td><td width="223" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">OpenCv</span>版本</span></strong></p></td><td width="242" valign="top" style="border-color: windowtext rgb(102, 102, 102) rgb(102, 102, 102) windowtext; border-bottom-width: 1px; border-bottom-style: solid; border-right-width: 1px; border-right-style: solid; padding: 5px;"><p style="text-align: center;"><strong><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">IDE</span></span></strong></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">C#</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: Calibri; letter-spacing: 0px;">OpenCvSharp4.4.8.0.20230708</span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Visual Studio 2022</span></span></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">C++</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; letter-spacing: 0px;"><span style="font-family: Calibri;">O</span></span><span style="font-family: Calibri; letter-spacing: 0px;">pen</span><span style="font-family: 宋体; letter-spacing: 0px;"><span style="font-family: Calibri;">C</span></span><span style="font-family: Calibri; letter-spacing: 0px;">v-4.5.5-vc14_vc15</span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Visual Studio 2022</span></span></p></td></tr><tr><td width="81" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">Python</span></span></p></td><td width="223" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">OpenCv-Python (4.6.0.66)</span></span></p></td><td width="242" valign="top" style="border-top: none; border-left-color: windowtext; border-bottom: 1px solid rgb(102, 102, 102); border-right: 1px solid rgb(102, 102, 102); padding: 5px;"><p><span style="font-family: 宋体; font-size: 14px;"><span style="font-family: Calibri;">PyCharm Community Edition 2022.1.3</span></span></p></td></tr></tbody></table><p><br/></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><span style="font-family: sans-serif;">有时我们需要最大精度的角点检测。OpenCV为我们提供了函数cv2.cornerSubPix(),它可以提供亚像素级别的角点检测。下面是--个例子。首先我们要找到Harris角点,然后将角点的重心传给这个函数进行修正。Harris 角点用红色像素标出,绿色像素是修正后的像素。在使用这个函数是我们要定义一个迭代停止条件。当迭代次数达到或者精度条件满足后迭代就会停止。我们同样需要定义进行角点搜索的邻域大小。</span><br/></p><p>源码参考来源:<a href="/api/system/download?file=OpenCV-Python-Tutorial-%E4%B8%AD%E6%96%87%E7%89%88.pdf" target="_blank" title="OpenCV-Python-Tutorial-中文版.pdf (P184)">OpenCV-Python-Tutorial-中文版.pdf (P184)</a><span style="color: rgba(0, 0, 0, 0.85); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; text-wrap: wrap; "> </span></p><p>文档中的python源码:</p><pre class="brush:python;toolbar:false">import cv2
import numpy as np
filename = 'chessboard-2.png'
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# find Harris corners
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
dst = cv2.dilate(dst, None)
ret, dst = cv2.threshold(dst, 0.01 * dst.max(), 255, 0)
dst = np.uint8(dst)
# find centroids
# connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats,
# OutputArray centroids, int connectivity=8, int ltype=CV_32S)
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
# define the criteria to stop and refine the corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
# Python: cv2.cornerSubPix(image, corners, winSize, zeroZone, criteria)
# zeroZone – Half of the size of the dead region in the middle of the search zone
# over which the summation in the formula below is not done. It is used sometimes
# to avoid possible singularities of the autocorrelation matrix. The value of (-1,-1)
# indicates that there is no such a size.
# 返回值由 点坐标组成的一个数组 而 图像
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
# Now draw them
res = np.hstack((centroids, corners))
# np.int0 可以用来省略小数点后的数字,非四舍五入
res = np.int0(res)
img[res[:, 1], res[:, 0]] = [0, 0, 255]
img[res[:, 3], res[:, 2]] = [0, 255, 0]
cv2.imshow('subpixel5.png', img)
# 输出角点信息(默认角点很小,这里我放大一下)
for i in range(len(corners)):
center = (int(corners[i][0]), int(corners[i][1]))
cv2.circle(img, center, 4, (0,0,255) , -1, 8, 0)
cv2.imshow('subpixel5-2', img)
cv2.imwrite('subpixel5.png',img)
cv2.waitKey(0)</pre><p>测试原图<br/></p><p><img src="/upload/image/6383453109442277679102219.png" title="chessboard-2.png" alt="chessboard-2.png"/></p><p>运行效果</p><p><img src="/upload/image/6383453110544245354055598.png" title="subpixel5.png" alt="subpixel5.png"/></p><p><br/></p><p><br/></p><p><span style="font-style: italic; font-weight: bold; color: rgb(51, 153, 204); line-height: 18px; font-size: 20px;">以下是毛星云的工程转换过来的三个语言版本</span></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;">首先呢,我们需要准备一张测试图片。(不想下载工程的童鞋,可直接复制下面的图片和代码)</p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><img src="/upload/image/6379118158228801161893568.jpg" title="1.jpg" alt="1.jpg"/></p><p style="margin-top: 0px; margin-bottom: 10px; text-wrap: wrap; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><br/></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;">运行效果如下(C#):</p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><img src="/upload/image/6379118172576670311105037.gif" title="【OpenCv基础】第57讲 亚像素级角点检测.gif" alt="【OpenCv基础】第57讲 亚像素级角点检测.gif"/></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="font-size: 16px; font-style: italic; font-weight: bold; color: rgb(51, 153, 204); line-height: 18px;">C#版本代码如下:</span></strong></p><pre class="brush:c#;toolbar:false">using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
static string WINDOW_NAME = "【亚像素级角点检测】";
static Mat g_srcImage = new Mat();
static Mat g_grayImage = new Mat();
static int g_maxCornerNumber = 33;
static int g_maxTrackbarNumber = 500;
static RNG rng = new RNG(12345);
static void Main(string[] args)
{
//【1】载入源图像并将其转换为灰度图
g_srcImage = Cv2.ImRead("../../../images/home7.jpg");
Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
//【2】创建窗口和滑动条,并进行显示和回调函数初始化
Cv2.NamedWindow(WINDOW_NAME, WindowFlags.AutoSize);
Cv2.CreateTrackbar("最大角点数:", WINDOW_NAME, ref g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
on_GoodFeaturesToTrack(0, IntPtr.Zero);
Cv2.ImShow(WINDOW_NAME, g_srcImage);
Cv2.WaitKey();
}
static void on_GoodFeaturesToTrack(int pos, IntPtr userData)
{
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = new Mat();
g_srcImage.CopyTo(copy); //复制源图像到一个临时变量中,作为感兴趣区域
//【3】进行Shi-Tomasi角点检测
Point2f[] corners = Cv2.GoodFeaturesToTrack(
g_grayImage,//输入图像
g_maxCornerNumber, //角点的最大数量
qualityLevel,//角点检测可接受的最小特征值
minDistance,//角点之间的最小距离
null,
blockSize,//计算导数自相关矩阵时指定的邻域范围
false,
k);//权重系数
//【4】输出文字信息
System.Diagnostics.Debug.WriteLine($"此次检测到的角点数量为:{corners.Length}");
//【5】绘制检测到的角点
int r = 4;
for (int i = 0; i < corners.Length; i++)
{
//以随机的颜色绘制出角点
byte cb = (byte)rng.Uniform(0, 255);
byte cg = (byte)rng.Uniform(0, 255);
byte cr = (byte)rng.Uniform(0, 255);
Cv2.Circle(copy, (int)corners[i].X, (int)corners[i].Y, r, new Scalar(cb, cg, cr), -1, LineTypes.Link8, 0);
}
//【6】显示(更新)窗口
Cv2.ImShow(WINDOW_NAME, copy);
copy.Dispose();
//【7】亚像素角点检测的参数设置
Size winSize = new Size(5, 5);
Size zeroZone = new Size(-1, -1);
TermCriteria criteria = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.MaxIter, 40, 0.001);
//【8】计算出亚像素角点位置
Cv2.CornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);
//【9】输出角点信息
for (int i = 0; i < corners.Length; i++)
{
System.Diagnostics.Debug.WriteLine($"精确角点坐标:{corners[i].X},{corners[i].Y}");
}
}
}
}</pre><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "></strong><br/></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="font-size: 16px; font-style: italic; font-weight: bold; color: rgb(51, 153, 204); line-height: 18px;">C++版本代码如下:</span></strong></strong></p><pre class="brush:cpp;toolbar:false">#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【亚像素级角点检测】" //为窗口标题定义的宏
Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 33;
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);//初始化随机数生成器
void on_GoodFeaturesToTrack(int, void*)
{
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
vector<Point2f> corners;
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = g_srcImage.clone(); //复制源图像到一个临时变量中,作为感兴趣区域
//【3】进行Shi-Tomasi角点检测
goodFeaturesToTrack(g_grayImage,//输入图像
corners,//检测到的角点的输出向量
g_maxCornerNumber,//角点的最大数量
qualityLevel,//角点检测可接受的最小特征值
minDistance,//角点之间的最小距离
Mat(),//感兴趣区域
blockSize,//计算导数自相关矩阵时指定的邻域范围
false,//不使用Harris角点检测
k);//权重系数
//【4】输出文字信息
cout << "\n\t>-------------此次检测到的角点数量为:" << corners.size() << endl;
//【5】绘制检测到的角点
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);
}
//【6】显示(更新)窗口
imshow(WINDOW_NAME, copy);
//【7】亚像素角点检测的参数设置
Size winSize = Size(5, 5);
Size zeroZone = Size(-1, -1);
TermCriteria criteria = TermCriteria(TermCriteria::EPS | TermCriteria::MAX_ITER, 40, 0.001);
//【8】计算出亚像素角点位置
cornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);
//【9】输出角点信息
for (int i = 0; i < corners.size(); i++)
{
cout << " \t>>精确角点坐标[" << i << "] (" << corners[i].x << "," << corners[i].y << ")" << endl;
}
}
int main()
{
//【1】载入源图像并将其转换为灰度图
g_srcImage = imread("../images/home7.jpg", 1);
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
//【2】创建窗口和滑动条,并进行显示和回调函数初始化
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
imshow(WINDOW_NAME, g_srcImage);
on_GoodFeaturesToTrack(0, 0);
waitKey(0);
return(0);
}</pre><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><br/></strong></p><p style="margin-top: 0px; margin-bottom: 10px; white-space: normal; padding: 0px; list-style: none; border: 0px; overflow-wrap: break-word; word-break: break-all; line-height: 1.5em; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif;"><strong style="white-space: normal; margin: 0px; padding: 0px; list-style: none; border: 0px; color: rgb(51, 51, 51); font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "WenQuanYi Micro Hei", Arial, sans-serif; "><span style="font-size: 16px; font-style: italic; font-weight: bold; color: rgb(51, 153, 204); line-height: 18px;">Python版本代码如下:</span></strong></p><pre class="brush:python;toolbar:false">import cv2
import numpy as np
import random
def on_GoodFeaturesToTrack(x):
global g_srcImage,g_grayImage
# 获取滑动条的值
g_maxCornerNumber = cv2.getTrackbarPos('value', 'WINDOW_NAME')
# 复制源图像到一个临时变量中,作为感兴趣区域
copy = np.copy(g_srcImage)
if g_maxCornerNumber <= 1:
g_maxCornerNumber = 1
# Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
feature_params = dict(maxCorners=g_maxCornerNumber, # 角点的最大数量
qualityLevel=0.01, # 角点检测可接受的最小特征值
minDistance=10, # 角点之间的最小距离
blockSize=3) # 计算导数自相关矩阵时指定的邻域范围
corners = cv2.goodFeaturesToTrack(g_grayImage, mask=None, **feature_params)
# 输出文字信息
print("此次检测到的角点数量为:", corners.size)
for i in range(len(corners)):
# 以随机的颜色绘制出角点
color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
center = (int(corners[i][0][0]), int(corners[i][0][1]))
cv2.circle(copy,center, 4, color , -1, 8, 0)
cv2.imshow("WINDOW_NAME", copy)
# 亚像素角点检测的参数设置
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 40, 0.001)
corners = cv2.cornerSubPix(g_grayImage, np.float32(corners), (5, 5), (-1, -1), criteria)
# 输出角点信息
for i in range(len(corners)):
center = (int(corners[i][0][0]), int(corners[i][0][1]))
print("精确角点坐标:", center)
# 【1】载入源图像并将其转换为灰度图
g_srcImage = cv2.imread("../images/home7.jpg")
g_grayImage = cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
# 创建窗口
cv2.namedWindow('WINDOW_NAME')
cv2.createTrackbar('value', 'WINDOW_NAME', 0, 500, on_GoodFeaturesToTrack)
cv2.setTrackbarPos('value', 'WINDOW_NAME', 0)
# 等待用户按键
cv2.imshow("WINDOW_NAME", g_srcImage)
cv2.waitKey(0)
cv2.destroyAllWindows()</pre>
亚像素级角点检测 视频讲解如下:
当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境 信息如下:
语言
OpenCv 版本
IDE
C#
OpenCvSharp4.4.8.0.20230708
Visual Studio 2022
C++
O pen C v-4.5.5-vc14_vc15
Visual Studio 2022
Python
OpenCv-Python (4.6.0.66)
PyCharm Community Edition 2022.1.3
有时我们需要最大精度的角点检测。OpenCV为我们提供了函数cv2.cornerSubPix(),它可以提供亚像素级别的角点检测。下面是--个例子。首先我们要找到Harris角点,然后将角点的重心传给这个函数进行修正。Harris 角点用红色像素标出,绿色像素是修正后的像素。在使用这个函数是我们要定义一个迭代停止条件。当迭代次数达到或者精度条件满足后迭代就会停止。我们同样需要定义进行角点搜索的邻域大小。
源码参考来源:OpenCV-Python-Tutorial-中文版.pdf (P184)
文档中的python源码:
import cv2
import numpy as np
filename = 'chessboard-2.png'
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# find Harris corners
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
dst = cv2.dilate(dst, None)
ret, dst = cv2.threshold(dst, 0.01 * dst.max(), 255, 0)
dst = np.uint8(dst)
# find centroids
# connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats,
# OutputArray centroids, int connectivity=8, int ltype=CV_32S)
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
# define the criteria to stop and refine the corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
# Python: cv2.cornerSubPix(image, corners, winSize, zeroZone, criteria)
# zeroZone – Half of the size of the dead region in the middle of the search zone
# over which the summation in the formula below is not done. It is used sometimes
# to avoid possible singularities of the autocorrelation matrix. The value of (-1,-1)
# indicates that there is no such a size.
# 返回值由 点坐标组成的一个数组 而 图像
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
# Now draw them
res = np.hstack((centroids, corners))
# np.int0 可以用来省略小数点后的数字,非四舍五入
res = np.int0(res)
img[res[:, 1], res[:, 0]] = [0, 0, 255]
img[res[:, 3], res[:, 2]] = [0, 255, 0]
cv2.imshow('subpixel5.png', img)
# 输出角点信息(默认角点很小,这里我放大一下)
for i in range(len(corners)):
center = (int(corners[i][0]), int(corners[i][1]))
cv2.circle(img, center, 4, (0,0,255) , -1, 8, 0)
cv2.imshow('subpixel5-2', img)
cv2.imwrite('subpixel5.png',img)
cv2.waitKey(0) 测试原图
运行效果
以下是毛星云的工程转换过来的三个语言版本
首先呢,我们需要准备一张测试图片。(不想下载工程的童鞋,可直接复制下面的图片和代码)
运行效果如下(C#):
C#版本代码如下:
using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
static string WINDOW_NAME = "【亚像素级角点检测】";
static Mat g_srcImage = new Mat();
static Mat g_grayImage = new Mat();
static int g_maxCornerNumber = 33;
static int g_maxTrackbarNumber = 500;
static RNG rng = new RNG(12345);
static void Main(string[] args)
{
//【1】载入源图像并将其转换为灰度图
g_srcImage = Cv2.ImRead("../../../images/home7.jpg");
Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
//【2】创建窗口和滑动条,并进行显示和回调函数初始化
Cv2.NamedWindow(WINDOW_NAME, WindowFlags.AutoSize);
Cv2.CreateTrackbar("最大角点数:", WINDOW_NAME, ref g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
on_GoodFeaturesToTrack(0, IntPtr.Zero);
Cv2.ImShow(WINDOW_NAME, g_srcImage);
Cv2.WaitKey();
}
static void on_GoodFeaturesToTrack(int pos, IntPtr userData)
{
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = new Mat();
g_srcImage.CopyTo(copy); //复制源图像到一个临时变量中,作为感兴趣区域
//【3】进行Shi-Tomasi角点检测
Point2f[] corners = Cv2.GoodFeaturesToTrack(
g_grayImage,//输入图像
g_maxCornerNumber, //角点的最大数量
qualityLevel,//角点检测可接受的最小特征值
minDistance,//角点之间的最小距离
null,
blockSize,//计算导数自相关矩阵时指定的邻域范围
false,
k);//权重系数
//【4】输出文字信息
System.Diagnostics.Debug.WriteLine($"此次检测到的角点数量为:{corners.Length}");
//【5】绘制检测到的角点
int r = 4;
for (int i = 0; i < corners.Length; i++)
{
//以随机的颜色绘制出角点
byte cb = (byte)rng.Uniform(0, 255);
byte cg = (byte)rng.Uniform(0, 255);
byte cr = (byte)rng.Uniform(0, 255);
Cv2.Circle(copy, (int)corners[i].X, (int)corners[i].Y, r, new Scalar(cb, cg, cr), -1, LineTypes.Link8, 0);
}
//【6】显示(更新)窗口
Cv2.ImShow(WINDOW_NAME, copy);
copy.Dispose();
//【7】亚像素角点检测的参数设置
Size winSize = new Size(5, 5);
Size zeroZone = new Size(-1, -1);
TermCriteria criteria = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.MaxIter, 40, 0.001);
//【8】计算出亚像素角点位置
Cv2.CornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);
//【9】输出角点信息
for (int i = 0; i < corners.Length; i++)
{
System.Diagnostics.Debug.WriteLine($"精确角点坐标:{corners[i].X},{corners[i].Y}");
}
}
}
}
C++版本代码如下:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【亚像素级角点检测】" //为窗口标题定义的宏
Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 33;
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);//初始化随机数生成器
void on_GoodFeaturesToTrack(int, void*)
{
//【1】对变量小于等于1时的处理
if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
vector<Point2f> corners;
double qualityLevel = 0.01;//角点检测可接受的最小特征值
double minDistance = 10;//角点之间的最小距离
int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
double k = 0.04;//权重系数
Mat copy = g_srcImage.clone(); //复制源图像到一个临时变量中,作为感兴趣区域
//【3】进行Shi-Tomasi角点检测
goodFeaturesToTrack(g_grayImage,//输入图像
corners,//检测到的角点的输出向量
g_maxCornerNumber,//角点的最大数量
qualityLevel,//角点检测可接受的最小特征值
minDistance,//角点之间的最小距离
Mat(),//感兴趣区域
blockSize,//计算导数自相关矩阵时指定的邻域范围
false,//不使用Harris角点检测
k);//权重系数
//【4】输出文字信息
cout << "\n\t>-------------此次检测到的角点数量为:" << corners.size() << endl;
//【5】绘制检测到的角点
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);
}
//【6】显示(更新)窗口
imshow(WINDOW_NAME, copy);
//【7】亚像素角点检测的参数设置
Size winSize = Size(5, 5);
Size zeroZone = Size(-1, -1);
TermCriteria criteria = TermCriteria(TermCriteria::EPS | TermCriteria::MAX_ITER, 40, 0.001);
//【8】计算出亚像素角点位置
cornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);
//【9】输出角点信息
for (int i = 0; i < corners.size(); i++)
{
cout << " \t>>精确角点坐标[" << i << "] (" << corners[i].x << "," << corners[i].y << ")" << endl;
}
}
int main()
{
//【1】载入源图像并将其转换为灰度图
g_srcImage = imread("../images/home7.jpg", 1);
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
//【2】创建窗口和滑动条,并进行显示和回调函数初始化
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
imshow(WINDOW_NAME, g_srcImage);
on_GoodFeaturesToTrack(0, 0);
waitKey(0);
return(0);
}
Python版本代码如下:
import cv2
import numpy as np
import random
def on_GoodFeaturesToTrack(x):
global g_srcImage,g_grayImage
# 获取滑动条的值
g_maxCornerNumber = cv2.getTrackbarPos('value', 'WINDOW_NAME')
# 复制源图像到一个临时变量中,作为感兴趣区域
copy = np.copy(g_srcImage)
if g_maxCornerNumber <= 1:
g_maxCornerNumber = 1
# Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
feature_params = dict(maxCorners=g_maxCornerNumber, # 角点的最大数量
qualityLevel=0.01, # 角点检测可接受的最小特征值
minDistance=10, # 角点之间的最小距离
blockSize=3) # 计算导数自相关矩阵时指定的邻域范围
corners = cv2.goodFeaturesToTrack(g_grayImage, mask=None, **feature_params)
# 输出文字信息
print("此次检测到的角点数量为:", corners.size)
for i in range(len(corners)):
# 以随机的颜色绘制出角点
color = (random.randint(0,255), random.randint(0,255), random.randint(0,255))
center = (int(corners[i][0][0]), int(corners[i][0][1]))
cv2.circle(copy,center, 4, color , -1, 8, 0)
cv2.imshow("WINDOW_NAME", copy)
# 亚像素角点检测的参数设置
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 40, 0.001)
corners = cv2.cornerSubPix(g_grayImage, np.float32(corners), (5, 5), (-1, -1), criteria)
# 输出角点信息
for i in range(len(corners)):
center = (int(corners[i][0][0]), int(corners[i][0][1]))
print("精确角点坐标:", center)
# 【1】载入源图像并将其转换为灰度图
g_srcImage = cv2.imread("../images/home7.jpg")
g_grayImage = cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
# 创建窗口
cv2.namedWindow('WINDOW_NAME')
cv2.createTrackbar('value', 'WINDOW_NAME', 0, 500, on_GoodFeaturesToTrack)
cv2.setTrackbarPos('value', 'WINDOW_NAME', 0)
# 等待用户按键
cv2.imshow("WINDOW_NAME", g_srcImage)
cv2.waitKey(0)
cv2.destroyAllWindows()