点云提取角点: https://blog.csdn.net/xx970829/article/details/123233029
思路
本文目的为提取红外图像中的角点,据红外热像仪成像特性对角点处进行了简单处理,然后识别其角点和轮廓,最后从多个角点中区分出标定板角点。
源码
//--------------------------------------------------------------------------------------------
// source /home/xx/catkin_ws/devel/setup.bash && rosrun my_cam_lidar_calib image 1.png
//
// 提取图像中的标定板
//--------------------------------------------------------------------------------------------
#include <iostream>
#include <string>
//图像类
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
void contourExtraction(cv::Mat image, cv::Mat otsuMat, cv::Mat matContours,int row,int col);
void cornerDetection(cv::Mat grayMat,cv::Mat image,vector<Point2f> &corners);
void minMaxValue(cv::Mat matContours,vector<Point2f> &corners,vector<Point2f> &minMaxVec);
bool contourEdgeCorner(cv::Mat matContours,Point2f &thisPoint);
int main(int argc,char** argv)
{
Mat image = cv::imread(argv[1], IMREAD_UNCHANGED);
Mat imageCorners =image.clone();
Mat imagecontour =image.clone();
int row=image.rows;
int col=image.cols;
cout<<"row:"<<row<<" col:"<<col<< endl;
// 阈值操作
Mat grayMat;
Mat otsuMat;
cvtColor(image, grayMat, CV_BGR2GRAY);
threshold(grayMat, otsuMat, 0, 255, CV_THRESH_BINARY + CV_THRESH_OTSU);
cv::imwrite("threshold.png",otsuMat);
Mat matContours=Mat::zeros(otsuMat.size(),CV_8UC1); //边缘点
//轮廓提取
contourExtraction(imagecontour, otsuMat ,matContours,row, col);
cv::imwrite("contourExtraction.png",matContours);
imshow("contourExtraction",matContours);
//角点检测
vector<Point2f> corners;//存储角点位置
cornerDetection(grayMat,imageCorners, corners);
cv::imwrite("cornerDetection.png",imageCorners);
imshow("cornerDetection",imageCorners);
//角点提取
vector<Point2f> minMaxVec;//存储角点A、B、C、D
minMaxValue( matContours, corners, minMaxVec);
for (int i = 0; i < minMaxVec.size(); i++)
{
circle(image, minMaxVec[i], 5, cv::Scalar(0, 255, 0), 2, 8, 0);
}
cv::imwrite("corners.png",image);
imshow("corners",image);
waitKey();
return 0;
}
//轮廓提取
void contourExtraction(cv::Mat image,cv::Mat otsuMat, cv::Mat matContours,int row,int col)
{
vector<vector<Point>> contours; //每一组Point点集就是一个轮廓
vector<Vec4i> hierarchy;
// hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],
// 分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号
findContours(otsuMat,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE,Point());
//CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
//CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
Mat imageContours=Mat::zeros(otsuMat.size(),CV_8UC1);
//contours[i]代表的是第i个轮廓,contours[i].size()代表的是第i个轮廓上所有的像素点数
int maxEdge=100;
int maxEdgeIndex=-1;
for(int i=0;i<contours.size();i++)
{
if(maxEdge<contours[i].size())
{
maxEdge=contours[i].size();
maxEdgeIndex=i;
}
}
if(maxEdgeIndex<0)
return ;
for(int i=0; i<maxEdge; i++)
{
//绘制出contours向量内所有的像素点
Point P=Point(contours[maxEdgeIndex][i].x,contours[maxEdgeIndex][i].y);
matContours.at<uchar>(P)=255;
}
for(int r=0; r<row; r++)
{
for(int c=0 ; c<col ; c++)
{
if(matContours.at<uchar>(r,c)==255)
{
image.at<Vec3b>(r,c)[0]=0;
image.at<Vec3b>(r,c)[1]=0;
image.at<Vec3b>(r,c)[2]=255;
}
}
}
//imshow("Point of Contours",matContours);
}
void cornerDetection(cv::Mat grayMat,cv::Mat image,vector<Point2f> &corners)
{
//角点识别
//https://jingyan.baidu.com/article/2c8c281d9d8e7f0008252a88.html
//设置角点检测参数
int max_corners = 20; //最大角点数量
double quality_level = 0.01;//最小可接受的向量值
double min_distance = 10; //两个角点之间的最小距离
int block_size = 3;
bool use_harris = false;
double k = 0.04;
goodFeaturesToTrack(grayMat,
corners,
max_corners,
quality_level,
min_distance,
Mat(),
block_size,
use_harris,
k);
//将检测到的角点绘制到原图上
for (int i = 0; i < corners.size(); i++)
{
circle(image, corners[i], 1, Scalar(0, 0, 255), 2, 8, 0);
}
//namedWindow("goodTrack corner",CV_WINDOW_NORMAL);
//imshow("goodTrack corner", image);
//指定亚像素计算迭代标注
TermCriteria criteria = TermCriteria(
TermCriteria::MAX_ITER + TermCriteria::EPS,
40,
0.01);
//亚像素检测
cornerSubPix(grayMat, corners, Size(5, 5), Size(-1, -1), criteria);
//将检测到的亚像素角点绘制到原图上
for (int i = 0; i < corners.size(); i++)
{
circle(image, corners[i], 5, cv::Scalar(50, 200, 0), 2, 8, 0);
}
//imshow("角点", image);
}
//查找四个极值点
void minMaxValue(cv::Mat matContours,vector<Point2f> &corners,vector<Point2f> &minMaxVec)
{
vector<Point2f> allVec;
for(int i=0 ; i<corners.size(); i++)
{
if(contourEdgeCorner(matContours,corners[i]))
{
allVec.push_back( corners[i] );
}
}
if(allVec.size()<4)
return;
Point2f A=allVec[0]; //y-min
Point2f B=allVec[0]; //x-max
Point2f C=allVec[0]; //y-max
Point2f D=allVec[0]; //x-min
for(int i=1; i<allVec.size(); i++)
{
if(A.y>allVec[i].y)
A=allVec[i];
if(B.x<allVec[i].x)
B=allVec[i];
if(C.y<allVec[i].y)
C=allVec[i];
if(D.x>allVec[i].x)
D=allVec[i];
}
minMaxVec.push_back(A);
minMaxVec.push_back(B);
minMaxVec.push_back(C);
minMaxVec.push_back(D);
}
//判断是否为边缘角点
bool contourEdgeCorner(cv::Mat matContours,Point2f &thisPoint)
{
for(int r=thisPoint.y-10; r<thisPoint.y+10; r++)
{
for(int c=thisPoint.x-10; c<thisPoint.x+10; c++)
{
if(matContours.at<uchar>(r,c)==255)
return true;
}
}
return false;
}
效果
数据
下载:https://download.csdn.net/download/xx970829/85161423
【转载请标明出处:https://blog.csdn.net/xx970829/article/details/123233609】