二值化轮廓发现之后,下一步最主要的就是轮廓特征分析了,常见的特征有:
轮廓面积
轮廓周长
轮廓几何矩、质心
轮廓的最大外接矩形
轮廓的凸包
轮廓的最小外接矩形
轮廓的最小外接三角形
轮廓的最小外接椭圆
轮廓的多边形逼近
轮廓的最小外接圆
轮廓几何矩的质心
轮廓的最大外接矩形
轮廓的凸包
轮廓的最小外接矩形
轮廓的最小外接三角形
轮廓的最小外接椭圆
CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false );
CV_EXPORTS_W double arcLength( InputArray curve, bool closed );
CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false );
CV_EXPORTS_W Rect boundingRect( InputArray array );
详细介绍可参考之前文章:https://blog.csdn.net/weixin_44901043/article/details/123242496?spm=1001.2014.3001.5502
CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
bool clockwise = false, bool returnPoints = true );
CV_EXPORTS_W RotatedRect minAreaRect( InputArray points );
CV_EXPORTS_W double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle );
CV_EXPORTS_W RotatedRect fitEllipse( InputArray points );
CV_EXPORTS_W void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
CV_OUT Point2f& center, CV_OUT float& radius );
void test_contours_feature()
{
cv::Mat src;
src = cv::imread("D:\\QtProject\\Opencv_Example\\contours_feature\\contours_feature.png", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
cout << "Cannot load image" << endl;
return;
}
cv::imshow("src", src);
cv::Mat binary;
cv::threshold(src, binary, 150, 255, cv::THRESH_BINARY_INV);
cv::Mat k55 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1));
cv::morphologyEx(binary, binary, cv::MORPH_OPEN, k55, cv::Point(-1, -1), 1);
cv::namedWindow("binary",cv::WINDOW_NORMAL);
cv::imshow("binary", binary);
std::vector<std::vector<cv::Point> > defectContours;
std::vector<cv::Vec4i> hierarchyDefect;
cv::findContours(binary, defectContours, hierarchyDefect, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
cv::Mat contours = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawCenterPos = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawRect = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawHull = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawRotateRect = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawMinEnclosingCircle = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawMinEnclosingTriangle = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Mat drawEllipse = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
std::vector<std::vector<cv::Point>> PolyDPs;
cv::Mat drawPolyDP = cv::Mat(src.size(), CV_8UC3, cv::Scalar(0,0,0));
std::vector<std::vector<cv::Point> > hull(defectContours.size());
for (int i = 0; i < defectContours.size(); i++)
{
//轮廓 白色
cv::drawContours(contours, defectContours, i, cv::Scalar(255,255,255), 1);
//求面积
double fArea = cv::contourArea(defectContours[i]);
qDebug()<<QStringLiteral("面积")<<fArea;
//求周长
double fLength = cv::arcLength(defectContours[i],true);
qDebug()<<QStringLiteral("周长")<<fLength;
//矩求质心 红色
cv::Moments mu = cv::moments(defectContours[i]);
cv::Point2d centerPos = cv::Point2d( mu.m10/mu.m00 , mu.m01/mu.m00);
cv::circle(drawCenterPos, centerPos, 10, cv::Scalar(0, 0, 255));
//最大外接矩形 蓝色
cv::Rect rect = cv::boundingRect(defectContours[i]);
cv::rectangle(drawRect, rect, cv::Scalar(255,0,0));
//凸包 黄色
cv::convexHull(cv::Mat(defectContours[i]), hull[i], false);
cv::drawContours(drawHull, hull, i, cv::Scalar(0,255,255), 1);
//最小外接矩形 青色
cv::RotatedRect rotateRect = cv::minAreaRect(defectContours[i]);
vector<cv::Point2f> boxPts(4);
rotateRect.points(boxPts.data());
for (int j = 0; j < 4; j++)
{
cv::line(drawRotateRect, boxPts[j], boxPts[(j + 1) % 4], cv::Scalar(128, 128, 0), 1, 8); //绘制最小外接矩形每条边
}
//最小外接圆 棕褐色
cv::Point2f center;
float fRadius = 0;
cv::minEnclosingCircle(defectContours[i], center, fRadius);
cv::circle(drawMinEnclosingCircle, center, fRadius, cv::Scalar(140,180,210),1);
//最小外界三角形 粉色
std::vector<cv::Point2f> points;
cv::minEnclosingTriangle(defectContours[i], points);
cv::line(drawMinEnclosingTriangle, points[0], points[1], cv::Scalar(193, 182, 255), 1, 8); //绘制最小外接三角形每条边
cv::line(drawMinEnclosingTriangle, points[2], points[1], cv::Scalar(193, 182, 255), 1, 8); //绘制最小外接三角形每条边
cv::line(drawMinEnclosingTriangle, points[2], points[0], cv::Scalar(193, 182, 255), 1, 8); //绘制最小外接三角形每条边
//椭圆 紫罗兰色
cv::RotatedRect ellipse = cv::fitEllipse(defectContours[i]);
cv::ellipse(drawEllipse, ellipse, cv::Scalar(226,43,138));
//多边形逼近 洋红色
std::vector<cv::Point> PolyDP;
//cv::Mat PolyDP ;
cv::approxPolyDP(defectContours[i],PolyDP, 0.5, true);
PolyDPs.push_back(PolyDP);
//cv::drawContours(drawPolyDP, PolyDP, 0, cv::Scalar(23,56,234));
//drawPolyDP += cv::Mat(PolyDP);
}
//画多边形逼近
for(int j = 0; j < PolyDPs.size(); j++)
{
cv::drawContours(drawPolyDP, PolyDPs, j, cv::Scalar(255,0,255),1);
}
drawCenterPos += contours;
drawRect += contours;
drawHull += contours;
drawRotateRect += contours;
drawMinEnclosingCircle += contours;
drawMinEnclosingTriangle += contours;
drawEllipse += contours;
cv::namedWindow("contours",cv::WINDOW_NORMAL);
cv::imshow("contours", contours);
cv::namedWindow("drawRect",cv::WINDOW_NORMAL);
cv::imshow("drawRect", drawRect);
cv::namedWindow("drawHull",cv::WINDOW_NORMAL);
cv::imshow("drawHull", drawHull);
cv::namedWindow("drawRotateRect",cv::WINDOW_NORMAL);
cv::imshow("drawRotateRect", drawRotateRect);
cv::namedWindow("drawCenterPos",cv::WINDOW_NORMAL);
cv::imshow("drawCenterPos", drawCenterPos);
cv::namedWindow("drawMinEnclosingCircle",cv::WINDOW_NORMAL);
cv::imshow("drawMinEnclosingCircle", drawMinEnclosingCircle);
cv::namedWindow("drawMinEnclosingTriangle",cv::WINDOW_NORMAL);
cv::imshow("drawMinEnclosingTriangle", drawMinEnclosingTriangle);
cv::namedWindow("drawEllipse",cv::WINDOW_NORMAL);
cv::imshow("drawEllipse", drawEllipse);
cv::namedWindow("drawPolyDP",cv::WINDOW_NORMAL);
cv::imshow("drawPolyDP", drawPolyDP);
}