1、轮廓发现与绘制
图像的轮廓不但能提供物体的边缘,而且能够提供物体边缘之间的层次关系以及拓扑关系。我们可以将图像轮廓的发现简单理解为带有结构关系的边缘检测。下面就是一个简单的关系图:
为了描述不同轮廓之间的结构关系,定义由外到内的轮廓级别越来越低,也就是高一层级的轮廓包围着较低层级的轮廓,被同一个轮廓包围的多个不互相包含的轮廓是同一层级的轮廓。上图的结构关系可以用下图表示:
为了更好的描述各个轮廓之间的层级关系,常用4个参数来描述不同层级之间的结果关系,这4个参数分别是同层下一个轮廓索引、同层上一个轮廓索引、下层第一个字轮廓索引和上层父轮廓索引。根据这种描述方式,上图中0号轮廓没有同级轮廓和父轮廓,需要用-1表示,其第一个子轮廓为1号轮廓,因此可用[-1 -1 1 -1]描述该轮廓结构。
在OpenCV中提供了可以在二值图像中检测图像所有轮廓并生成不同轮廓结构关系的findContours()函数
void cv::findContours(InputArray img,
OutputArray contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point());
//image:输入图像
//contours:检测到的轮廓,每个轮廓中存放着像素的坐标
//hierarchy:轮廓结构关系描述向量
//mode:轮廓检测模式标志
//method:轮廓逼近方式标志
//offset:每个轮廓点移动的可选偏移量
在提取了轮廓之后,要做的自然是把它绘制出来,同样的OpenCV中也提供了绘制图像轮廓的函数drawContours()
void cv::drawContours(InputArray image,
InputArray contours,
int contourIdx,
const Scalar& color,
int thickness = 1,
int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX,
Point offset = Point());
//image:绘制轮廓的目标图像
//contours:所有将要绘制的轮廓
//contourIdx:要绘制的轮廓数目
//color:绘制轮廓的颜色
//tickness:绘制轮廓的线条粗细
//lineType:边界线的连接类型
//hierarchy:可选的结果相关信息
//maxLevel:表示绘制轮廓的最大等级
//offset:可选的轮廓偏移参数
检测案例:
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
system("color F0");
Mat img = imread("F:\\图像处理\\图片\\rice.png");
if (img.empty())
{
return -1;
}
imshow("原图", img);
Mat gray, binary;
cvtColor(img, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(13, 13), 4, 4);
threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU); //自适应二值化
//轮廓发现与绘制
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓变量
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//绘制轮廓
for (int i = 0; i < contours.size(); i++)
{
drawContours(img, contours, i, Scalar(0, 0, 255), 2, 8);
}
//输出轮廓结果
for (int i = 0; i < hierarchy.size(); i++)
{
cout << hierarchy[i] << endl;
}
//显示结果
imshow("轮廓检测结果", img);
waitKey(0);
return 0;
}
检测结果:
2、轮廓面积
轮廓面积是轮廓的重要统计特性之一,通过轮廓面积的大小可以进一步分析每个轮廓隐含的信息,例如通过轮廓面积区分物体大小、识别不同的物体等。在OpenCV中提供了检测轮廓面积的contourArea()函数
double cv::contourArea(InputArray contour,
bool oriented = false);
//contour:轮廓的像素点
//oriented:区域面积是否具有方向的标志,true表示面积具有方向性,false表示不具有方向性
检测案例:
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
vector<Point> contour;
contour.push_back(Point2f(0, 0));
contour.push_back(Point2f(10, 0));
contour.push_back(Point2f(10, 10));
contour.push_back(Point2f(5, 5));
double area = contourArea(contour);
cout << "area = " << area << endl;
Mat img = imread("F:\\图像处理\\图片\\rice.png");
if (img.empty())
{
cout << "请确认文件名称是否正确!" << endl;
return -1;
}
imshow("原图", img);
Mat gray, binary;
cvtColor(img, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(9, 9), 2, 2);
threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU); //自适应二值化
//轮廓检测
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓结果变量
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
cout << "size" << contours.size() << endl;
//输出轮廓面积
for (int i = 0; i < contours.size(); i++)
{
double areal = contourArea(contours[i]);
cout << "第" << i << "轮廓面积=" << areal << endl;
}
waitKey(0);
return 0;
}
检测结果:
3、轮廓周长
轮廓周长是轮廓的重要统计特性之一,在OpenCV中提供了检测轮廓面积的arcLength()函数
double cv::arcLength(InputArray curve,
bool closed);
//curve:轮廓或者曲线的二维像素点
//closed:轮廓或者曲线是否闭合的标志,true表示闭合
检测案例:
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
//用四个点表示三角形
vector<Point> contour;
contour.push_back(Point2f(0, 0));
contour.push_back(Point2f(10, 0));
contour.push_back(Point2f(10, 10));
contour.push_back(Point2f(5, 5));
double length0 = arcLength(contour, true);
double length1 = arcLength(contour, false);
cout << "length0=" << length0 << endl;
cout << "length1=" << length1 << endl;
Mat img = imread("F:\\图像处理\\图片\\rice.png");
if (img.empty())
{
cout << "请检查文件是否有误!" << endl;
return -1;
}
imshow("原图", img);
Mat gray, binary;
cvtColor(img, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(9, 9), 2, 2);
threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU);
//轮廓检测
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓变量
findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//输出轮廓长度
for (int i = 0; i < contours.size(); i++)
{
double length2 = arcLength(contours[i], true);
cout << "第" << i << "个轮廓的长度=" << length2 << endl;
}
waitKey(0);
return 0;
}
检测结果:
4、轮廓外接多边形
在OpenCV中还提供了检测最大外接轮廓和最小外接轮廓的coundingRect()函数和minAreaRect()函数
Rect cv::boundingRect(InputArray array)
RotatedRect cv::minAreaRect(InputArray points)
检测案例:
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("F:\\图像处理\\图片\\rice.png");
if (img.empty())
{
cout << "请确认文件名称是否有误!" << endl;
return -1;
}
Mat img1, img2;
img.copyTo(img1); //深拷贝用来绘制最大外接矩形
img.copyTo(img2); //浅拷贝用来绘制最小矩形
imshow("img", img);
//去噪声与二值化
Mat canny;
Canny(img, canny, 80, 160, 3, false);
imshow("Canny", canny);
//膨胀运算
Mat kernel = getStructuringElement(0, Size(3, 3));
dilate(canny, canny, kernel);
//轮廓发现与绘制
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(canny, contours, hierarchy, 0, 2, Point());
//寻找轮廓的外接矩形
for (int i = 0; i < contours.size(); i++)
{
//最大外接矩形
Rect rect = boundingRect(contours[i]);
rectangle(img1, rect, Scalar(0, 0, 255), 2, 8, 0);
//最小外接矩形
RotatedRect rrect = minAreaRect(contours[i]);
Point2f points[4];
rrect.points(points); //读取最小外接矩形的4个顶点
Point2f cpt = rrect.center; //最小外接矩形的中心
//绘制旋转矩形与中心位置
for (int j = 0; j < 4; j++)
{
if (j == 3)
{
line(img2, points[j], points[0], Scalar(0, 0, 255), 2, 8, 0);
break;
}
line(img2, points[j], points[j + 1], Scalar(0, 0, 255), 2, 8, 0);
}
//绘制矩形中心
circle(img, cpt, 2, Scalar(0, 0, 255), 2, 8, 0);
}
//输出绘制的外接矩形的结果
imshow("max", img1);
imshow("min", img2);
waitKey(0);
return 0;
}
检测结果:
本文内容到这里就结束了,如果您觉得以上内容对你有所帮助,不妨给个3连,感谢您的支持!