在二值图像(binary image)中寻找轮廓(contour)
cv::findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
cv::findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())
1、InputOutputArray image:参数必须为二值单通道图像,定义黑色为背景,白色为识别目标;
即单通道图像、灰度图像,更常用的为canny边缘检测算子处理后的图像;
2、OutputArrayOfArrays contours:contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示,每一个元素为一个3维数组(其形状为(n,1,2),其中n表示轮廓点个数,2表示像素点坐标),表示一个轮廓;
3、OutputArray hierarchy:hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
4、int mode:mode表示轮廓的检索模式,具体相关如下表所示:
CV_RETR_EXTERNAL:只检测外轮廓
CV_RETR_LIST:检测的轮廓不建立等级关系,都是同级。不存在父轮廓或内嵌轮廓
CV_RETR_CCOMP:建立两个等级的轮廓。上面一层为外边界,里面一层为内孔的边界信息
CV_RETR_TREE:建立一个等级树结构的轮廓
5、int method:method表示为轮廓的近似办法。具体参数如下表所示:
CV_CHAIN_APPROX_NONE:
存储所有的轮廓点,相邻的两个点的像素位置差不超过1。
即max(abs(x1-x2),abs(y2-y1))==1
CV_CHAIN_APPROX_SIMPLE
压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
CV_CHAIN_APPROX_TC89_L1
使用teh-Chinl chain 近似算法
CV_CHAIN_APPROX_TC89_KCOS
使用teh-Chinl chain 近似算法
6、Point offset=Point():offset:轮廓点的偏移量,格式为tuple,如(-10,10)表示轮廓点沿X负方向偏移10个像素点,沿Y正方向偏移10个像素点。
7、返回值:
contours:std::vector
hiararchy:std::vectorcv::Vec4i
=提示:findContours函数处理后会对输入图像进行修改。若保留原图像,应先copy一份图像,对copy的图像进行处理,这里一般先用Mat来存放一张临时图像。=
用来将轮廓绘制出来
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, intthickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
image:表示目标图像,
contours:表示输入的轮廓组,每一组轮廓由点vector构成,
contourIdx:指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
color:为轮廓的颜色,
thickness:为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
lineType:为线型,
hierarchy:为轮廓结构信息,
maxLevel:为maxLevel
offset:offset:轮廓点的偏移量,格式为tuple,如(-10,10)表示轮廓点沿X负方向偏移10个像素点,沿Y正方向偏移10个像素点。
计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
Rect boundingRect(InputArray points)
points:
二维点集,点的序列或向量 (Mat)
对图像轮廓点进行多边形拟合
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
InputArray curve:一般是由图像的轮廓点组成的点集
OutputArray approxCurve:表示输出的多边形点集
double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,
bool closed:表示输出的多边形是否封闭
主要用于计算图像轮廓的面积
double contourArea(InputArray contour, bool oriented=false )
InputArray contour:输入的点,一般是图像的轮廓点
bool oriented=false:表示某一个方向上轮廓的的面积值,顺时针或者逆时针,一般选择默认false
主要是计算图像轮廓的周长
double arcLength(InputArray curve, bool closed)
InputArray curve:表示图像的轮廓
bool closed:表示轮廓是否封闭的
上述函数代码示例:
void MotionAnalyze::MoveObjDetect(cv::Mat& frontGray, cv::Mat& afterGray, std::vector<cv::Rect> &detectResult)
{
cv::Mat diff;
//1 灰度处理 目的 RGB三通道转灰度单通道 压缩到原图片三分之一大小
//2 帧差处理 目的 找到帧与帧之间的差异(正在运动的物体)
cv::absdiff(frontGray, afterGray, diff);
//imshow("diff",diff);
//3 二值化处理 目的 将灰度图继续识别转换为黑白分明的图像
cv::threshold(diff, diff, 20, 255, CV_THRESH_BINARY);
//imshow("threshold",diff);
//4 图像降噪
//4-1 腐蚀处理 目的 去除白色噪点
Mat element = cv::getStructuringElement(MORPH_RECT, Size(1, 1));//小于3*3方块的白色噪点都会被腐蚀
cv::erode(diff, diff, element);
//imshow("erode",diff);
//4-2 膨胀 目的 把白色区域变大
Mat element2 = cv::getStructuringElement(MORPH_RECT, Size(10, 10));
cv::dilate(diff, diff, element2);
//imshow("dilate",diff);
//5 提取关键点
//5-1 查找特征点
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
AIUtils::findContours(diff, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
//5-2 提取关键点
//5-3 确定下四个点来用于框选目标物体
int num = contours.size();
for (int i = 0; i < num; i++)
{
if (cv::contourArea(contours[i]) < 600)
continue;
vector<cv::Point> c_poly(contours[i].size());
cv::approxPolyDP(Mat(contours[i]), c_poly, 3, true);
//多边拟合
cv::Rect rect;
rect = cv::boundingRect(Mat(c_poly));
detectResult.push_back(rect);
}
}
主要求得包含点集最小面积的矩形,,这个矩形是可以有偏转角度的,可以与图像的边界不平行
RotatedRect minAreaRect(InputArray points)
InputArray points:表示输入的点集
返回值:矩形的四个顶点坐标
旋转的长方形
C++: RotatedRect:: RotatedRect ( )
C++: RotatedRect:: RotatedRect (const Point2f& center, const Size2f& size, float angle )
C++: RotatedRect:: RotatedRect (const CvBox2D& box )
Parameters:
center – The rectangle mass center.
size – Width and height of the rectangle.
angle – The rotation angle in a clockwise direction. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle.
box – The rotated rectangle parameters as the obsolete CvBox2D structure.
C++: void RotatedRect:: points (Point2f pts[] ) const //! returns 4 vertices of the rectangle
C++: Rect RotatedRect:: boundingRect ( ) const
C++: RotatedRect:: operator CvBox2D ( ) const
Parameters:
pts – The points array for storing rectangle vertices.
Mat image(200, 200, CV_8UC3, Scalar(0));
RotatedRect rRect = RotatedRect(Point2f(100,100), Size2f(100,50), 30);
Point2f vertices[4];
rRect.points(vertices);//获取矩形的四个点
for (int i = 0; i < 4; i++)
line(image, vertices[i], vertices[(i+1)%4], Scalar(0,255,0));
Rect brect = rRect.boundingRect();
rectangle(image, brect, Scalar(255,0,0));
imshow("rectangles", image);
waitKey(0);
得到包含二维点集的最小圆
void minEnclosingCircle(InputArray points, Point2f& center, float& radius)
InputArray points:输入的二维点集
Point2f& center:表示输出的圆形的中心坐标,是float型
float& radius:输出的最小圆的半径,是float型
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
/// 函数声明
void thresh_callback(int, void*);
/** @主函数 */
int main(int argc, char** argv)
{
/// 载入原图像, 返回3通道图像
src = imread("D:6.jpg", 1);
/// 转化成灰度图像并进行平滑
cvtColor(src, src_gray, CV_BGR2GRAY);
blur(src_gray, src_gray, Size(3, 3));
/// 创建窗口
char* source_window = "Source";
namedWindow(source_window, CV_WINDOW_AUTOSIZE);
imshow(source_window, src);
createTrackbar(" Threshold:", "Source", &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
waitKey(0);
return(0);
}
/** @thresh_callback 函数 */
void thresh_callback(int, void*)
{
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// 使用Threshold检测边缘
threshold(src_gray, threshold_output, thresh, 255, THRESH_BINARY);
/// 找到轮廓
findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
/// 多边形逼近轮廓 + 获取矩形和圆形边界框
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point2f>center(contours.size());
vector<float>radius(contours.size());
//for (int i = 0; i < contours.size(); i++)
//{
// approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
// boundRect[i] = boundingRect(Mat(contours_poly[i]));
// minEnclosingCircle(contours_poly[i], center[i], radius[i]);
//}
for (int i = 0; i < contours.size(); i++)
{
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
boundRect[i] = boundingRect(Mat(contours[i]));
minEnclosingCircle(contours[i], center[i], radius[i]);
}
/// 画多边形轮廓 + 包围的矩形框 + 圆形框
Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());
rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);
circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);
}
/// 显示在一个窗口
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing);
}
参考链接:https://blog.csdn.net/qq_18343569/article/details/48000071