??本文匯總了OpenCV的特征檢測API,并補充一個Harris算法之前的Shi-Tomasi算法霞势,提供局部非最大角點抑制處理。這個算法在OpenCV實現(xiàn)就是goodFeaturesToTrack函數(shù)。我們實現(xiàn)的算法比OpenCV的算法性能存在差距,但我們的目的從理解出發(fā)阳欲,理解后可以能很快實現(xiàn)與OpenCV一樣的優(yōu)化。
??程序在OpenCV4.2環(huán)境下可以運行獲得結(jié)果陋率。
序言
- 在OpenCV中特征檢測主要提供三種特征檢測的實現(xiàn):
- 邊緣Edge
- 角點Corner
- 線與線段Line
- 圓檢測Circle
-
邊緣檢測
- Candy檢測
void cv::Canny (InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
- Candy檢測變形函數(shù)(使用x,y差分)
void cv::Canny (InputArray dx, InputArray dy, OutputArray edges, double threshold1, double threshold2, bool L2gradient=false)
- Candy檢測
-
角點檢測
- 特征值與特征向量計算:
void cv::cornerEigenValsAndVecs (InputArray src, OutputArray dst, int blockSize, int ksize, int borderType=BORDER_DEFAULT)
- Harris檢測算法:基于特征值R響應值計算的判定方法
void cv::cornerHarris (InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType=BORDER_DEFAULT)
- Shi-Tomasi檢測算法:基于特征值的最小特征值判斷方法
void cv::cornerMinEigenVal (InputArray src, OutputArray dst, int blockSize, int ksize=3, int borderType=BORDER_DEFAULT)
- 強角點檢測算法:在Harris與Shi-Tomasi算法上提供更加準確的角點位置的算法秽晚。
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)
-
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)
- 可以指定Sobel差分核大小瓦糟。
- 亞像素級的角點位置檢測(更加精確的角點位置計算)
void cv::cornerSubPix (InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)
- 基于復空間導數(shù)的檢測算法:
void cv::preCornerDetect (InputArray src, OutputArray dst, int ksize, int borderType=BORDER_DEFAULT)
- 特征值與特征向量計算:
-
線條與線段檢測
- 線段檢測算法:
Ptr< LineSegmentDetector > cv::createLineSegmentDetector (int _refine=LSD_REFINE_STD, double _scale=0.8, double _sigma_scale=0.6, double _quant=2.0, double _ang_th=22.5, double _log_eps=0, double _density_th=0.7, int _n_bins=1024)
- Hough檢測算法:
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)
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)
- 概率Hough檢測算法:
void cv::HoughLinesP (InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0)
- 線段檢測算法:
-
圓檢測
- Hough檢測算法:
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)
- Hough檢測算法:
- 角點檢測的說明:
- 這里的幾個角點檢測方法都是基于差分(導數(shù))。
Shi-Tomasi檢測算法
- 計算特征值與特征向量
- 這個特征值與特征向量是差分(Sobel)的協(xié)方差矩陣
void cv::cornerEigenValsAndVecs (
InputArray src, // 輸入圖像
OutputArray dst, // 輸出的特征值與特征向量赴蝇,6通道菩浙,類型是CV_32FC(6)
// 因為沒有定義CV_32FC6,所有只能調(diào)用宏CV_32FC,指定通道參數(shù)劲蜻。
// 6個通道的數(shù)據(jù)格式分別是陆淀,特征值λ1,特征值λ2先嬉,特征向量1(u1轧苫, u2)特征向量(v1, v2)
int blockSize, // 差分和的領域窗口大小(沒有采用高斯模糊疫蔓,而是box模糊)
int ksize, // Sobel核大小
int borderType=BORDER_DEFAULT)
-
協(xié)方差矩陣定義
-
Shi-Tomasi角點檢測算法
- 取最小特征值作為輸出就是Shi-Tomasi檢測算法含懊;
- OpenCV函數(shù)cornerMinEigenVal就是返回最小特征值。
-
Shi-Tomasi角點檢測算法實現(xiàn)代碼
- 包含OpenCV的標準實現(xiàn)算法衅胀。
- 下面的實現(xiàn)與OpenCV的實現(xiàn)效果完全一樣岔乔。 效果證明,實際上上面求特征值與特征向量的函數(shù)還是進行了高斯模糊處理的滚躯。
#include <iostream>
#include <cmath>
#include <climits>
#include <opencv2/opencv.hpp>
/*
Shi_Tomasi角點檢測算法
*/
int main(int argc, char **argv){
cv::Mat img = cv::imread("corner.jpg");
cv::Mat img_src;
cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY); // OpenCV讀取圖像的顏色是BGR雏门,灰度是8位圖像
/*
* 計算特征值與特征向量,
* void cv::cornerEigenValsAndVecs (
* InputArray src,
* OutputArray dst,
* int blockSize,
* int ksize,
* int borderType = BORDER_DEFAULT
* )
*/
cv::Mat eig_vecs;
cv::cornerEigenValsAndVecs(img_src, eig_vecs, 5, 9);
/*
* 使用特征值實現(xiàn)Harris角點檢測與Shi-Tomasi角點檢測算法
*/
cv::Mat harris(eig_vecs.rows, eig_vecs.cols, CV_32FC1);
cv::Mat shi_tomasi(eig_vecs.rows, eig_vecs.cols, CV_32FC1);
float k =0.04;
for(int y = 0; y < eig_vecs.rows; y++){
for(int x = 0; x < eig_vecs.cols; x++){
cv::Vec6f val = eig_vecs.at<cv::Vec6f>(y, x);
harris.at<float>(y, x) = val[0] * val[1] - k * (val[0] + val[1]) * (val[0] + val[1]);
shi_tomasi.at<float>(y, x) = val[0] < val[1] ? val[0] : val[1];
}
}
cv::imwrite("harris.jpg", harris);
cv::imwrite("shi_tomasi.jpg", shi_tomasi);
/*
* OpenCV實現(xiàn)的實現(xiàn)Harris角點檢測與Shi-Tomasi角點檢測算法
*
*/
cv::Mat harris_cv, shi_tomasi_cv;
cv::cornerHarris(img_src, harris_cv, 5, 9, 0.04);
cv::cornerMinEigenVal(img_src, shi_tomasi_cv, 5, 9);
cv::imwrite("harris_cv.jpg", harris_cv);
cv::imwrite("shi_tomasi_cv.jpg", shi_tomasi_cv);
return 0;
}
強角點檢測算法
- 函數(shù)定義
void cv::goodFeaturesToTrack(
InputArray image, // 原圖像
OutputArray corners, // 輸出的角點坐標
int maxCorners, // 控制輸出角點的最大數(shù)掸掏,對焦點排序嗎茁影,取前面maxCorners個角點。
double qualityLevel, //
double minDistance, //
InputArray mask = noArray(), // 用來控制計算角點的區(qū)域
int blockSize = 3, // 滑動窗體的大小
bool useHarrisDetector = false, // 角點的偵測算法
double k = 0.04 // Harris才需要的參數(shù)阅束,上面一個參數(shù)為false(Shi-Tomasi算法)呼胚,這個參數(shù)無效。
)
- 重載的參數(shù)擴展函數(shù)
void cv::goodFeaturesToTrack(
InputArray image,
OutputArray corners,
int maxCorners,
double qualityLevel,
double minDistance,
InputArray mask,
int blockSize,
int gradientSize, // 比另外一個重載函數(shù)多的參數(shù)息裸,Sobel卷積核大小
bool useHarrisDetector = false,
double k = 0.04
)
-
兩個重要參數(shù)解釋
- qualityLevel
- 這個參數(shù)是爭對最大角點而言的蝇更,小于
的角點被放棄,
- 源代碼中基本上是簡單暴力使用呼盆、:
minMaxLoc( eig, 0, &maxVal, 0, 0, _mask );
threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO );
- 這個參數(shù)是爭對最大角點而言的蝇更,小于
- minDistance
- 會丟棄帶最強角點距離小于minDistance的角點年扩。
- 如果為0或者小于0,就是不限制距離访圃。
- corners
- 輸出的是角點的坐標數(shù)組厨幻。(列向量)
- 維度dims = 2,cols = 1, type = CV_32FC2, depth = CV_32F
- 可以使用通用類型cv::Mat, 也可以直接使用std::vector<cv::Point2f>類型返回最強角點腿时。
- qualityLevel
-
最強角點篩選算法
- 首先需要計算出角點的檢測值(注意:源代碼中采用了網(wǎng)格算法况脆,可以降低循環(huán)次數(shù),這里為了理解批糟,就采用了沒有優(yōu)化的算法)
- 局部非最大抑制(最強角點)
- 計算角點檢測值的最大值 maxVal格了;
- 根據(jù)qualityLevel條件,對 小于maxVal*qualityLevel的角點檢測值置0.
- 對閾值過濾后的角點檢測值做3 * 3的膨脹(這是局部非最大抑制的方法)
- 循環(huán)判定徽鼎,所有像素的角點檢測值沒有膨脹就是最強角點盛末。
- 距離與最大角點數(shù)條件篩選
- 對最強角點排序弹惦;
- 循環(huán)排序后的最強角點,最強角點分成兩個部分:滿足條件的最強角點悄但,待判定的最強角點棠隐;
- 每次待判定的最強角點循環(huán)與滿足條件的最強角點計算距離,滿足就添加到滿足條件的最強角點檐嚣,否則繼續(xù)下一個待檢測最強角點助泽。
- 判定滿足條件的最強角點是否超過設置的最大數(shù),超過就終止處理净嘀,否則繼續(xù)报咳。
- 算法實現(xiàn)代碼
- 代碼參考了和源代碼(其中局部非最大抑制就是參考源代碼的,挖藏,否則就要全部自己擼代碼)
- 其中使用了源代碼中的內(nèi)存塊的使用暑刃,以及通過指針的方式來處理,巧妙的堅決了角點檢測值與角點坐標的存儲問題膜眠。
- 代碼沒有實現(xiàn)遮罩功能岩臣,只能對整幅圖像處理,對局部圖像的無法處理宵膨。
- 代碼參考了和源代碼(其中局部非最大抑制就是參考源代碼的,挖藏,否則就要全部自己擼代碼)
void good_features(
cv::Mat &image,
cv::Mat &corners,
int maxCorners, // 允許為負數(shù)架谎,表示所有強角點
double qualityLevel,
double minDistance, // 允許為負數(shù),表示沒有距離
int blockSize,
int gradientSize,
bool useHarrisDetector,
double k){
// -------------------------手工實現(xiàn)
// 1. 計算角點辟躏,根據(jù)useHarrisDetector選擇Harris算法還是Shi-Tomasi算法
cv::Mat eig;
if(useHarrisDetector){
cornerHarris( image, eig, blockSize, gradientSize, k);
}else{
cornerMinEigenVal(image, eig, blockSize, gradientSize);
}
// 2. 局部非最大抑制
double maxVal = 0;
cv::Mat tmp;
// 計算最大特征值
cv::minMaxLoc(eig, NULL, &maxVal); // 第二個參數(shù)是返回最小值谷扣,使用空指針表示不需要返回
// qualityLevel參數(shù)進行閾值過濾
cv::threshold( eig, eig, maxVal*qualityLevel, 0, cv::THRESH_TOZERO);
cv::dilate(eig, tmp, cv::Mat()); // 使用cv::Mat()表示3 * 3的膨脹 ,取周圍鄰域中最大值作為元素值
// 循環(huán)判定捎琐,沒有膨脹的就是最強角點
std::vector<const float*> tmpCorners; // 這里需要存房角點判別值與坐標会涎,源代碼中提供了一個巧妙地方式,直接使用原來eig中地址
// 地址的好處就是既可以計算出坐標瑞凑,還可以獲取角點的判別值末秃。
cv::Size imgsize = image.size();
// 循環(huán)判定膨脹值是否變化
for( int y = 1; y < imgsize.height - 1; y++ ){ // 行
const float* eig_data = (const float*)eig.ptr(y);
const float* tmp_data = (const float*)tmp.ptr(y);
for( int x = 1; x < imgsize.width - 1; x++ ){ // 列
float val = eig_data[x];
if( val != 0 && val == tmp_data[x])
tmpCorners.push_back(eig_data + x); // 存放地址,這是比較精妙的使用方式
}
}
// --- 根據(jù)距離籽御、角點最大個數(shù)等條件過濾角點
std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr()); // 進行降序排序
std::vector<cv::Point2f> vec_corners; // 存放結(jié)果
size_t total = tmpCorners.size(); // 上面計算的強角點個數(shù)
size_t ncorners = 0; // 計數(shù)器(用來記錄已經(jīng)挑選的強角點數(shù)量)
if(minDistance < 1 ){ // 距離沒有設置
for(int i = 0; i < total; i++ ){
// 計算偏移地址
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
// 根據(jù)偏移地址計算強角點的坐標
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
// 超過設置的最大值练慕,則終止處理
if( maxCorners > 0 && ncorners == maxCorners ){
break;
}
}
}
else{
// 過濾掉距離范圍內(nèi)的強角點(源代碼采用網(wǎng)格管理,比對所有點進行循環(huán)性能要優(yōu)化)
/*
* 第一個點肯定是強角點技掏,后面的點铃将,必須與選擇出來的強角點循環(huán)判定距離是否在指定范圍內(nèi),范圍內(nèi)的拋棄
* 這個算法是雙重全循環(huán)哑梳,效率比較低劲阎,可以采用網(wǎng)格管理(只對網(wǎng)格周邊的網(wǎng)格中的元素計算,這樣不用所有都編譯一次)涧衙。
*/
minDistance *= minDistance; // 為了不做平方根運算哪工,這里把距離變成平方。
for(int i = 0; i < total; i++){
// 計算坐標
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
bool is_corrner = true; // 默認是滿足條件的強角點
if(vec_corners.size() == 0){ // 第一個強角點弧哎,直接push
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
}else{
// 開始循環(huán)判定距離
for(int j = 0; j < vec_corners.size(); j++){
// 計算距離
float dx = x - vec_corners[j].x;
float dy = y - vec_corners[j].y;
if(dx*dx + dy*dy < minDistance ){
is_corrner = false; // 只要有一個在范圍雁比,就不是滿足條件的強角點
break;
}
}
if(is_corrner){ // 循環(huán)完畢都還是強角點,說明是滿足條件的強角點撤嫩。
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
}
}
// 檢測是否超過最大角點數(shù)限制
if( maxCorners > 0 && ncorners == maxCorners ){
break; // 超過指定的角點數(shù)終止處理
}
}
}
// 返回數(shù)據(jù)偎捎,把vector轉(zhuǎn)換為Mat,并輸出
cv::Mat(vec_corners).convertTo(corners, CV_32F);
}
- 效果比較
- 我們的實現(xiàn)結(jié)果與OpenCV的計算效果完全一樣序攘。當然效率要差不少茴她。這種密集型計算,不適合在GPU上采用并發(fā)計算來提高效率程奠。
- 運算后的圖片這里不貼圖了丈牢,直接貼實現(xiàn)完整源代碼,可以在OpenCV4.2的C++環(huán)境下運行
#include <iostream>
#include <cmath>
#include <climits>
#include <opencv2/opencv.hpp>
/*
* 強角點檢測算法
*/
struct greaterThanPtr{ // Functor對象瞄沙,使用指針形式比較兩個值的大小
bool operator () (const float * a, const float * b) const
{ return (*a > *b) ? true : (*a < *b) ? false : (a > b); }
};
void good_features(
cv::Mat &image,
cv::Mat &corners,
int maxCorners,
double qualityLevel,
double minDistance,
int blockSize,
int gradientSize,
bool useHarrisDetector=false,
double k=0.04);
void call_opencv(){
cv::Mat img = cv::imread("corner.jpg");
cv::Mat img_src;
cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY); // OpenCV讀取圖像的顏色是BGR己沛,灰度是8位圖像
/*
* OpenCV標準的強角點檢測算法
*/
cv::Mat corners;
// std::vector<cv::Point2f> corners;
cv::goodFeaturesToTrack(img_src, corners, 150, 0.01, 10, cv::noArray(), 5, 11, false, 0.04);
std::cout << corners.dims << "," << corners.rows << "," << corners.cols << std::endl;
std::cout << corners.type() << "->CV_32FC2:" << CV_32FC2 << std::endl;
std::cout << corners.depth() << "->CV32F:"<< CV_32F << std::endl;
std::cout << corners.channels() << std::endl;
for(int i = 0; i < corners.rows; i++){
cv::circle(img, corners.at<cv::Point2f>(i), 4, cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
cv::imwrite("corner_cv.jpg", img);
}
void call_myimpl(){
cv::Mat img = cv::imread("corner.jpg");
cv::Mat img_src;
cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY); // OpenCV讀取圖像的顏色是BGR,灰度是8位圖像
/*
* 手工實現(xiàn)強角點檢測算法
*/
cv::Mat corners;
// std::vector<cv::Point2f> corners;
good_features(img_src, corners, 150, 0.01, 10, 5, 11, false, 0.04);
std::cout << corners.dims << "," << corners.rows << "," << corners.cols << std::endl;
std::cout << corners.type() << "->CV_32FC2:" << CV_32FC2 << std::endl;
std::cout << corners.depth() << "->CV32F:"<< CV_32F << std::endl;
std::cout << corners.channels() << std::endl;
for(int i = 0; i < corners.rows; i++){
cv::circle(img, corners.at<cv::Point2f>(i), 4, cv::Scalar(255, 0, 0, 255), 2, 8, 0);
}
cv::imwrite("corner_good.jpg", img);
}
int main(int argc, char **argv){
call_opencv();
std::cout << "------------------------------" << std::endl;
call_myimpl();
return 0;
}
void good_features(
cv::Mat &image,
cv::Mat &corners,
int maxCorners, // 允許為負數(shù)距境,表示所有強角點
double qualityLevel,
double minDistance, // 允許為負數(shù)申尼,表示沒有距離
int blockSize,
int gradientSize,
bool useHarrisDetector,
double k){
// -------------------------手工實現(xiàn)
// 1. 計算角點,根據(jù)useHarrisDetector選擇Harris算法還是Shi-Tomasi算法
cv::Mat eig;
if(useHarrisDetector){
cornerHarris( image, eig, blockSize, gradientSize, k);
}else{
cornerMinEigenVal(image, eig, blockSize, gradientSize);
}
// 2. 局部非最大抑制
double maxVal = 0;
cv::Mat tmp;
// 計算最大特征值
cv::minMaxLoc(eig, NULL, &maxVal); // 第二個參數(shù)是返回最小值垫桂,使用空指針表示不需要返回
// qualityLevel參數(shù)進行閾值過濾
cv::threshold( eig, eig, maxVal*qualityLevel, 0, cv::THRESH_TOZERO);
cv::dilate(eig, tmp, cv::Mat()); // 使用cv::Mat()表示3 * 3的膨脹 师幕,取周圍鄰域中最大值作為元素值
// 循環(huán)判定,沒有膨脹的就是最強角點
std::vector<const float*> tmpCorners; // 這里需要存房角點判別值與坐標诬滩,源代碼中提供了一個巧妙地方式霹粥,直接使用原來eig中地址
// 地址的好處就是既可以計算出坐標,還可以獲取角點的判別值碱呼。
cv::Size imgsize = image.size();
// 循環(huán)判定膨脹值是否變化
for( int y = 1; y < imgsize.height - 1; y++ ){ // 行
const float* eig_data = (const float*)eig.ptr(y);
const float* tmp_data = (const float*)tmp.ptr(y);
for( int x = 1; x < imgsize.width - 1; x++ ){ // 列
float val = eig_data[x];
if( val != 0 && val == tmp_data[x])
tmpCorners.push_back(eig_data + x); // 存放地址蒙挑,這是比較精妙的使用方式
}
}
// --- 根據(jù)距離、角點最大個數(shù)等條件過濾角點
std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr()); // 進行降序排序
std::vector<cv::Point2f> vec_corners; // 存放結(jié)果
size_t total = tmpCorners.size(); // 上面計算的強角點個數(shù)
size_t ncorners = 0; // 計數(shù)器(用來記錄已經(jīng)挑選的強角點數(shù)量)
if(minDistance < 1 ){ // 距離沒有設置
for(int i = 0; i < total; i++ ){
// 計算偏移地址
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
// 根據(jù)偏移地址計算強角點的坐標
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
// 超過設置的最大值愚臀,則終止處理
if( maxCorners > 0 && ncorners == maxCorners ){
break;
}
}
}
else{
// 過濾掉距離范圍內(nèi)的強角點(源代碼采用網(wǎng)格管理忆蚀,比對所有點進行循環(huán)性能要優(yōu)化)
/*
* 第一個點肯定是強角點,后面的點姑裂,必須與選擇出來的強角點循環(huán)判定距離是否在指定范圍內(nèi)馋袜,范圍內(nèi)的拋棄
* 這個算法是雙重全循環(huán),效率比較低舶斧,可以采用網(wǎng)格管理(只對網(wǎng)格周邊的網(wǎng)格中的元素計算欣鳖,這樣不用所有都編譯一次)。
*/
minDistance *= minDistance; // 為了不做平方根運算茴厉,這里把距離變成平方泽台。
for(int i = 0; i < total; i++){
// 計算坐標
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
bool is_corrner = true; // 默認是滿足條件的強角點
if(vec_corners.size() == 0){ // 第一個強角點什荣,直接push
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
}else{
// 開始循環(huán)判定距離
for(int j = 0; j < vec_corners.size(); j++){
// 計算距離
float dx = x - vec_corners[j].x;
float dy = y - vec_corners[j].y;
if(dx*dx + dy*dy < minDistance ){
is_corrner = false; // 只要有一個在范圍,就不是滿足條件的強角點
break;
}
}
if(is_corrner){ // 循環(huán)完畢都還是強角點怀酷,說明是滿足條件的強角點稻爬。
vec_corners.push_back(cv::Point2f((float)x, (float)y));
++ncorners;
}
}
// 檢測是否超過最大角點數(shù)限制
if( maxCorners > 0 && ncorners == maxCorners ){
break; // 超過指定的角點數(shù)終止處理
}
}
}
// 返回數(shù)據(jù),把vector轉(zhuǎn)換為Mat蜕依,并輸出
cv::Mat(vec_corners).convertTo(corners, CV_32F);
}