结果图:
在介绍形状特征提取之前,先了解几个特征提取的重要函数。
/* 寻找特征函数
findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset=Point());
*/
/*
image:图像必须是8位单通道图像,可以是灰度图像,但更常用的是二值图像,一般是经过Canny,拉普拉斯等边缘检测算子处理过的二值图像;(函数运行时,这个图像会被直接涂改,因此如果是将来还有用的图像,应该复制之后再传给该函数)
contours:定义为vector<vector<Point>> contours;向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓,有多少轮廓,向量contours就有多少元素;
hierarchy:定义为vector<Vec4i> hierarchy;,表示向量内每一个元素包含了4个int型变量——hierarchy[i][0] —hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、第一条子轮廓、父轮廓的索引编号,如果当前轮廓没有对应的后一个轮廓,前一个轮廓、第一条子轮廓或父嵌轮廓的话,则hierarchy[i][0] —hierarchy[i][3]的相应位被设置为默认值-1;
mode:轮廓提取方式
○ cv::RETR_EXTERNAL:只检测最外围轮廓;
○ cv::RETR_LIST:检测所有的轮廓,但是不建立等级关系;
○ cv::RETR_CCOMP:检测所有的轮廓,但所有轮廓只建立两种等级关系,外围为顶层
○ cv::RETR_TREE:检测所有的轮廓,所有轮廓建立一个等级树结构
method:轮廓的近似方法
○ CV_CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到contours向量中
○ CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存到contours向量中
Point:偏移量,默认为(0,0)没有偏移
*/
/*绘制轮廓函数
drawContours(InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color, int thickness=1, int lineType=8,
InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
image:待绘制轮廓的图像;
contours:要绘制的轮廓组,与findContours中输出的contours相同;
contourIdx:指明画第几个轮廓,如果该参数为负值(通常设为-1),则画全部轮廓,
color:指定绘制的颜色或亮度(灰度图像),如scalar(255,0,255)
thickness:指定线段的宽度
lineType:边框线型,可以是4或8,4代表绘制的线是四连通线(不美观),8代表绘制的线是八连通线(较美观)
hierarchy:对应findContours中输出的hierarchy
maxLevel:限制将在图上绘制的轮廓层次深度,为0表示只绘制“第0层”,以此类推
offset:偏移量,默认为(0,0)没有偏移
*/
/*
approxPolyDP函数
函数的作用:
对图像轮廓点进行多边形拟合
2、函数的调用形式
C++: void approxPolyDP (InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
参数详解;
InputArray curve:一般是由图像的轮廓点组成的点集
OutputArray approxCurve:表示输出的多边形点集
double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,
bool closed:表示输出的多边形是否封闭
*/
/*1.cv2.boundingRect()
能够返回包围轮廓的矩形的边界信息。
函数样式:retval = cv2.boundingRect(array)
参数介绍:
retval 表示返回矩形边界左上角顶点的坐标值及矩形边界的宽和高 , 也可以是4个返回值形式
x, y, w, h = cv2.boundingRect(array)
array 是灰度图像或轮廓
然后使用函数cv2.drawContours()来绘制矩形包围框。*/
具体实现:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void getContours(Mat imgDil, Mat img) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255),2);
vector<vector<Point>>conPoly(contours.size());
vector<Rect>boundRect(contours.size());
//排除干扰
for (int i = 0;i < contours.size();i++) {
//计算轮廓面积
int area = contourArea(contours[i]);
string objectType;
cout << area << endl;
if (area > 1000) {
//arcLength(contours[i], true);计算轮廓周长
//InputArray类型的curve,输入的向量,二维点(轮廓顶点),可以为std::vector或Mat类型。
//bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true。
float peri = arcLength(contours[i], true);
//对图像轮廓点进行多边形拟合
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
cout << conPoly[i].size() << endl;
// 表示返回矩形边界左上角顶点的坐标值及矩形边界的宽和高
boundRect[i] = boundingRect(conPoly[i]);
//得到特征点
int objCor = (int)conPoly[i].size();
//判断图像
if (objCor == 3) {
objectType = "Tri";
}
if (objCor == 4) {
float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
cout << aspRatio << endl;
if (aspRatio > 0.95 && aspRatio < 1.05) {
objectType = "Square";
}
else objectType = "Rect";
}
if (objCor > 4) {
objectType = "Circle";
}
//绘制轮廓
drawContours(img, contours, i, Scalar(255, 0, 255), 2);
//绘制矩形框
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
//输出判断文本图像
putText(img, objectType, { boundRect[i].x,boundRect[i].y - 5 },FONT_HERSHEY_PLAIN,1,Scalar(0, 69, 255), 2);
}
}
}
int main() {
string path = "Learn-OpenCV-cpp-in-4-Hours-main\\Resources\\shapes.png";
Mat img = imread(path),imgGray,imgBlur,imgCanny,imgDil;
if (img.empty()) {
printf("could not load image...\n");
return 0;
}
//Preprocessing 预处理
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
imshow("Image", img);
getContours(imgDil, img);
imshow("Image Cont",img);
waitKey(0);
destroyAllWindows();
}