轮廓发现与绘制
findContours()用于检测轮廓信息和轮廓之间的结构信息。
输入二值化图像
输出vector<vector<Point>> 类型的轮廓像素坐标和vector<Vec4i>类型的轮廓结构信息。
轮廓结构信息:[同层下一个轮廓索引,同层上一个轮廓索引,下一层第一个子轮廓索引,上层父轮廓索引]
0:[-1,-1,1,-1] 1:[2,-1,3,0] 2:[-1,1,,3,0] 3:[-1,-1,-1,2]
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
system("color F0");
Mat img = imread("D:/images/circles.png");
imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gary,binary;
cvtColor(img, gary, COLOR_BGR2GRAY);
GaussianBlur(gary, gary, Size(9, 9), 2, 2);
threshold(gary, 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 t = 0; t < contours.size(); t++) {
drawContours(img, contours, t, Scalar(0, 0, 255), 2, 8);
}
//输出轮廓结构描述
for (int i = 0; i < hierarchy.size(); i++) {
cout << hierarchy[i] << endl;
}
//显示图像
imshow("轮廓检测结果", img);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
轮廓面积
contourArea(contours[t])用于统计轮廓像素点围成区域的面积,便于区分物体大小、识别物体种类。
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
system("color F0");
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("D:/images/circles.png");
imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gary,binary;
cvtColor(img, gary, COLOR_BGR2GRAY);
GaussianBlur(gary, gary, Size(9, 9), 2, 2);
threshold(gary, 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 t = 0; t < contours.size(); t++) {
double area1 = contourArea(contours[t]);
cout << "第" << t << "轮廓面积=" << area1 << endl;
}
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
轮廓周长
arcLength(contours[t], true)
参数一:输入像素点,数据类型为Mat或者vector<Point>
参数二:轮廓是否闭合。true--计算周长;false--计算像素点连线长度
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
system("color F0");
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("D:/images/circles.png");
imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gary,binary;
cvtColor(img, gary, COLOR_BGR2GRAY);
GaussianBlur(gary, gary, Size(9, 9), 2, 2);
threshold(gary, 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 t = 0; t < contours.size(); t++) {
double length2 = arcLength(contours[t], true);
cout << "第" << t << "轮廓长度=" << length2 << endl;
}
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
轮廓外接矩形
作用:将物体轮廓不规则的形状拟合成矩形或者多边形
轮廓外接最大矩形
寻找轮廓x方向和y方向两端的像素,该矩形的长、宽分别与图像的两条轴平行。
boundingRect(contours[t]);
输入灰度图像或二位点集; 输出Rect类型的变量(左上角坐标,宽,高)
轮廓外接最小矩形
4个边与轮廓相交,矩形旋转角度与轮廓形状有关,多数情况下不与图像的两条轴平行。
minAreaRect(contours[t]);
输入灰度图像或二位点集; 输出RotatedRect类型变量(矩形中心位置,宽、高、旋转角度)
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
Mat img = imread("D:/images/rect.png");
imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat img1, img2;
img.copyTo(img1); //深拷贝用来绘制最大外接矩形
img.copyTo(img2); //深拷贝用来绘制最小外接矩形
//去噪和二值化
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 t = 0; t < contours.size(); t++) {
//最大外接矩形
Rect rect = boundingRect(contours[t]);
rectangle(img1, rect, Scalar(0, 0, 255), 2, 8, 0);
//最大外接矩形
RotatedRect rrect=minAreaRect(contours[t]);
Point2f points[4];
rrect.points(points); //读取最小外接矩形的4个顶点
Point2f cpt = rrect.center; //最小外接矩形的中心
//绘制旋转矩形与中心位置
for (int i = 0; i < 4; i++) {
if (i == 3) {
line(img2, points[i], points[0], Scalar(0, 255, 0), 2, 8, 0);
break;
}
line(img2, points[i], points[i + 1], Scalar(0, 255, 0), 2, 8, 0);
}
//绘制矩形中心
circle(img, cpt, 2, Scalar(255, 0, 0), 2, 8, 0);
}
//输出绘制结果
imshow("max", img1);
imshow("min", img2);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
轮廓外接多边形
用矩形逼近轮廓也时候会产生较大的误差,寻找逼近轮廓的多边形,多边形围成的面积会更加接近真实形状。
approxPolyDP(contours[t], result, 4, true);
输入灰度图像或二位点集; 输出多边形顶点坐标的Mat类矩阵
可通过顶点数初步判断轮廓的几何形状
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
void drawapp(Mat result, Mat img2) {
for (int i = 0; i < result.rows; i++) {
if (i = result.rows - 1) {
Vec2i point1 = result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(0);
line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
break;
}
Vec2i point1= result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(i+1);
line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
}
}
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
Mat img = imread("D:/images/angl.png");
//imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
//去噪和二值化
Mat canny;
Canny(img, canny, 80, 160, 3, false);
//imshow("canny", canny);
//轮廓发现与绘制
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓结构变量
findContours(canny, contours, hierarchy, 0, 2, Point());
//绘制多边形
for (int t = 0; t < contours.size(); t++) {
//用最小外接矩形求轮廓中心
RotatedRect rrect=minAreaRect(contours[t]);
Point2f center = rrect.center; //最小外接矩形的中心
circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0);
Mat result;
approxPolyDP(contours[t], result, 4, true); //多边形逼近
drawapp(result, img);
cout << "corners:" << result.rows << endl;
//判断形状和轮廓绘制
if (result.rows == 3) {
putText(img, "triangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
}
if (result.rows == 4) {
putText(img, "rectangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
}
if (result.rows == 8) {
putText(img, "poly-8", center, 0, 1, Scalar(0, 255, 0), 1, 8);
}
if (result.rows > 12) {
putText(img, "circle", center, 0, 1, Scalar(0, 255, 0), 1, 8);
}
}
//输出绘制结果
imshow("resule", img);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
点到轮廓的距离
作用:可用于计算轮廓在图像中的位置、两个轮廓之间的距离、确定图像上某一点是否在轮廓内部。
double dis = pointPolygonTest(contours[t], point, true);
输入:轮廓和像素点坐标 输出:像素点距离轮廓的最小距离
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
void drawapp(Mat result, Mat img2) {
for (int i = 0; i < result.rows; i++) {
if (i = result.rows - 1) {
Vec2i point1 = result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(0);
line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
break;
}
Vec2i point1= result.at<Vec2i>(i);
Vec2i point2 = result.at<Vec2i>(i+1);
line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);
}
}
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
system("color F0");
Mat img = imread("D:/images/angl.png");
//imshow("img", img);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
//去噪和二值化
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());
//创建图像中的一个像素点并绘制圆形
Point point = Point(250, 200);
circle(img, point, 2, Scalar(0, 0, 255), 2, 8, 0);
//绘制多边形
for (int t = 0; t < contours.size(); t++) {
//用最小外接矩形求轮廓中心
RotatedRect rrect=minAreaRect(contours[t]);
Point2f center = rrect.center; //最小外接矩形的中心
circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0);
//轮廓外部点距离轮廓的距离
double dis = pointPolygonTest(contours[t], point, true);
//轮廓内部点距离轮廓的距离
double dis2 = pointPolygonTest(contours[t], center, true);
//输出结果
cout << "轮廓外部点距离轮廓的距离" << dis << endl;
cout << "轮廓内部点距离轮廓的距离" << dis2 << endl;
}
imshow("img", img);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
凸包检测
作用:对轮廓进行多边形逼近,但逼近结果一定是凸多边形
凸包:将二维平面上的点集最外层的点连接起来构成的凸多边形
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
//system("color F0");
Mat img = imread("D:/images/hand.jpg");
imshow("img", img);
resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gary, binary;
cvtColor(img, gary, COLOR_BGR2GRAY);
//GaussianBlur(gary, gary, Size(9, 9), 2, 2);
threshold(gary, binary, 105, 255, THRESH_BINARY);
//开运算消除细小区域
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(binary, binary, MORPH_OPEN, k);
imshow("binary", binary);
//轮廓发现与绘制
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //存放轮廓结构变量
findContours(binary, contours, hierarchy, 0, 2, Point());
for (int t = 0; t < contours.size(); t++) {
//计算凸包
vector<Point> hull;
convexHull(contours[t], hull);
//绘制凸包
for (int i = 0; i < hull.size(); i++) {
//绘制凸包顶点
circle(img, hull[i], 4, Scalar(255, 0, 0), 2, 8, 0);
//连接凸包
if (i == hull.size() - 1) {
line(img, hull[i], hull[0], Scalar(0,0 , 255), 2, 8, 0);
break;
}
line(img, hull[i], hull[i+1], Scalar(0, 0, 255), 2, 8, 0);
}
}
imshow("img", img);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}