引言
在实际应用中,常常会有将检测到的轮廓用多边形表示出来的需求。这里为大家讲解如何用多边形表示出轮廓,或者说如何根据轮廓提取出多边形。
API:
approxPolyDP ()主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。
原理图:对比之前黑点连线,之后蓝色连线:
boundingRect()函数计算并返回指定点集最外面的矩形边界。
minEnclosingCircle()函数利用一种迭代算法,对给定的2D点集,去寻找面积最小的可包围它们的圆形。
minAreaRect()函数对于给定的2D点集,寻找可旋转的最小面积的包围矩形。
fitEllipse()函数用椭圆拟合二维点集。
代码示例:
1.通过移动滑动块控制生成随机点的数量,对于生成的随机点的点集,寻找最外面的矩阵边界、面积最小的可包围它们的圆形、
可旋转的最小面积的包围矩形,用椭圆拟合二维点集。
#include "pch.h"
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int pointNum = 50;
int pointMax = 100;
void PolyBound_demo(int pos, void* userdata);
RNG rng(getTickCount());
int mode = 0;
int main()
namedWindow("PolyBound", WINDOW_AUTOSIZE);
createTrackbar("pointNum", "PolyBound", &pointNum, pointMax, PolyBound_demo);
createTrackbar("mode", "PolyBound", &mode,1, PolyBound_demo);
setTrackbarMin("pointNum", "PolyBound", 5);
PolyBound_demo(pointNum, 0);
waitKey(0);
void PolyBound_demo(int pos, void* userdata)
Mat img = Mat::zeros(Size(500, 500), CV_8UC3);
//产生随机点并绘制
vector<Point> points;
for (int i = 0; i < pointNum; i++)
Point point;
point.x = rng.uniform(img.cols / 4, img.cols * 3 / 4);
point.y = rng.uniform(img.rows / 4, img.rows * 3 / 4);
points.push_back(point);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
circle(img, point, 2, color , FILLED, LINE_AA);
if (mode == 0)
//对于以上生成的点集,寻找最外面的矩形边界
Rect poly_rect = boundingRect(points);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
rectangle(img, poly_rect, color, 2, LINE_AA);
//对给定的2D点集,去寻找面积最小的可包围它们的圆形
Point2f center;
float radius;
minEnclosingCircle(points, center, radius);
color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
circle(img, center, radius, color, 2, LINE_AA);
//对于给定的2D点集,寻找可旋转的最小面积的包围矩形
RotatedRect poly_rotateRect =minAreaRect(points);
Point2f pts[4];
poly_rotateRect.points(pts);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (int i = 0; i < 4; i++)
line(img, pts[i], pts[(i + 1) % 4], color, 2, LINE_AA);
//用椭圆拟合二维点集
RotatedRect poly_ellipse = fitEllipse(points);
color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
ellipse(img, poly_ellipse, color, 2, LINE_AA);
imshow("PolyBound", img);
}
代码中出现void cv::RotatedRect::points(Point2f pts[]) const介绍如下:
效果如下:
2.创建包含轮廓的边界:
代码示例:
#include "pch.h"
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int thresholdVal = 86;
int thresholdMax = 255;
void PolyBound_demo(int pos, void* userdata);
RNG rng(getTickCount());
int contourmode = 0;
int ploymode = 0;
Mat src;
Mat blursrc;
int main()
src = imread("F:\\visual studio\\Image\\hand4.jpg");
if (src.empty())
cout << "Can't load the image " << endl;
resize(src, src, Size(), 0.5, 0.5);
//扩大图像的边界易于边框显示
copyMakeBorder(src, src, 0, 50, 0, 0, BORDER_WRAP);
imshow("src", src);
Mat graysrc;
cvtColor(src, graysrc, COLOR_BGR2GRAY);
GaussianBlur(graysrc, blursrc, Size(5, 5),3,3);
namedWindow("PolyBound", WINDOW_AUTOSIZE);
createTrackbar("threshold", "PolyBound", &thresholdVal , thresholdMax, PolyBound_demo);
createTrackbar("ploymode", "PolyBound", &ploymode, 1, PolyBound_demo);
createTrackbar("contourmode", "PolyBound", &contourmode, 3, PolyBound_demo);
PolyBound_demo(thresholdVal, 0);
waitKey(0);
void PolyBound_demo(int pos, void* userdata)
//阈值化
Mat bin;
threshold(~blursrc, bin, thresholdVal, thresholdMax, CV_THRESH_BINARY);
imshow("bin", bin);
//轮廓检测
vector<vector<Point>> contours;
vector<Vec4i> hiearchy;
findContours(bin, contours, hiearchy, contourmode, CV_CHAIN_APPROX_SIMPLE, Point());
//对图像轮廓点进行多边形拟合
vector<vector<Point>> contours_ploy(contours.size());
for (int i = 0; i < contours.size(); i++)
approxPolyDP(contours[i], contours_ploy[i], 3, true);
Mat dst;
src.copyTo(dst);
if (ploymode == 0)
for (int i = 0; i < contours_ploy.size(); i++)
//对于以上生成的点集,寻找最外面的矩形边界
Rect poly_rect = boundingRect(contours_ploy[i]);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
rectangle(dst, poly_rect, color, 2, LINE_AA);
//对给定的2D点集,去寻找面积最小的可包围它们的圆形
Point2f center;
float radius;
minEnclosingCircle(contours_ploy[i], center, radius);
color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
circle(dst, center, radius, color, 2, LINE_AA);
for (int i = 0; i < contours_ploy.size(); i++)
//对于给定的2D点集,寻找可旋转的最小面积的包围矩形
RotatedRect poly_rotateRect = minAreaRect(contours_ploy[i]);
Point2f pts[4];
poly_rotateRect.points(pts);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (int i = 0; i < 4; i++)
line(dst, pts[i], pts[(i + 1) % 4], color, 2, LINE_AA);
//用椭圆拟合二维点集 至少需要有5个点
if (contours_ploy[i].size() >= 5)
RotatedRect poly_ellipse = fitEllipse(contours_ploy[i]);
color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
ellipse(dst, poly_ellipse, color, 2, LINE_AA);
imshow("PolyBound", dst);
}
效果如下:
ploymode=0时,显示轮廓最外面的矩阵边界、面积最小的可包围轮廓的圆形:
contourmode=0时,只查找最外层的轮廓:
ploymode=1时,显示可旋转的最小面积的包围矩形,用椭圆拟合轮廓,注意:用椭圆拟合至少需要5个点。