Canny等边缘检测算可以根据求导等计算差异来检测边缘,但是无法将轮廓合成一个整体,轮廓发现是奖边缘像素合成轮廓。
轮廓发现findContours
void findContours( InputOutputArray image, //输入图像8bit单通道二值化图像
OutputArrayOfArrays contours, //找到的轮廓 vector<vector<Point>>
OutputArray hierachy //可选 图像拓扑结构 vector<Vec4i>
int mode, //轮廓返回模式
int method, //发现方法
Point offset = Point()); //轮廓像素的位移,默认(0,0)无位移
mode类型 | |
RETR_EXTERNAL | 只检测最外层轮廓 |
RETR_LIST | 检索所有轮廓并存到List中,无上下结构,全部同级 |
RETR_CCOMP | 检索所有轮廓,并组成双层结构 |
RETR_TREE | 检索所有轮廓,并重新建立网状轮廓结构。 |
method类型 | |
CHAIN_APPROX_NONE | 返回轮廓中所有点 |
CHAIN_APPROX_SIMPLE | 水平、垂直、斜 只返回最后一个点 |
CHAIN_APPROX_TC89_KCOS CHAIN_APPROX_TC89_L1 | 使用Teh-Chin链逼近算法 |
hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓 同级的下一条轮廓、同级的前一条轮廓、下级的第一个子节点、上级父节点。如果当前轮廓没有 同级的下一条轮廓、同级的前一条轮廓、下级的第一个子节点、上级父节点,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。
轮廓绘制
void drawContours( InputOutputArray image, //输出图像
InputArrayOfArrays contours, //全部发现的轮廓对象
int contourIdx, //轮廓索引号
const Scalar& color, //颜色
int thickness = 1, //线宽
int lineType = LINE_8, //线类型
InputArray hierarchy = noArray(), //拓扑结构图
int maxLevel = INT_MAX, //最大层数,0绘制当前,1绘制当前及内嵌轮廓
Point offset = Point() ); //轮廓位移
实战演练:
#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;
int threshold_value = 153;
int threshild_max = 255;
RNG rng;
void Demo_Contours(int, void*);
Mat src,dst;
const char* output_image = "findcountours_demo";
int main()
{
src = imread("C:/Users/LBJ/Desktop/OpenCVTest/fish.jpg");
if (!src.data )
{
cout << "The iamge is empty" << endl;
return -1;
}
namedWindow(output_image, WINDOW_AUTOSIZE);
namedWindow("Input_Image", WINDOW_AUTOSIZE);
imshow("Input_Image", src);
cvtColor(src, src, CV_BGR2GRAY);
const char* tracker_label = "Threshold Value:";
createTrackbar(tracker_label, output_image, &threshold_value, threshild_max, Demo_Contours);
Demo_Contours(0, 0);
waitKey(0);
return 0;
}
void Demo_Contours(int, void*)
{
Mat canny_output;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);
findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
dst = Mat::zeros(src.size(), CV_8UC3);
RNG rng(12345);
for (size_t i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));
}
for (size_t i = 0; i < hierachy.size(); i++)
{
cout << hierachy[i] << endl;
}
imshow(output_image, dst);
}
可以用轮廓间的关系定位二维码的三个定位点,他们都有两个子轮廓
#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;
int threshold_value = 153;
Mat src, dst;
const char* output_image = "findcountours_demo";
int main()
{
src = imread("C:/Users/LBJ/Desktop/OpenCVTest/二维码.jpg");
if (!src.data)
{
cout << "The iamge is empty" << endl;
return -1;
}
namedWindow(output_image, WINDOW_AUTOSIZE);
namedWindow("Input_Image", WINDOW_AUTOSIZE);
imshow("Input_Image", src);
GaussianBlur(src, src, Size(5, 5), 0, 0);
cvtColor(src, src, CV_BGR2GRAY);
threshold(src, src, 0, 255, THRESH_BINARY | THRESH_OTSU);
vector<Point> point;
Mat canny_output;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);
imshow("Canny_imge", canny_output);
findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
int k=0;
dst = Mat::zeros(src.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++)
{
int j = i, temp = 0;
while (hierachy[j][2] != -1)
{
temp++;
j = hierachy[j][2];
}
if (temp >= 4)
{
drawContours(dst, contours, i, Scalar(0, 0, 255), -1, 8, hierachy, 0, Point(0, 0));
cout << contours[i] << endl;
}
else
{
drawContours(dst, contours, i, Scalar(255, 255, 255), 1, 8, hierachy, 0, Point(0, 0));
}
}
imshow(output_image, dst);
waitKey(0);
return 0;
}
凸包
void convexHull( InputArray points, OutputArray hull,//输入点来自findContours 输出凸包
bool clockwise = false, bool returnPoints = true );
//顺时针方向 //true表示返回点个数
测试点是否落在轮廓内
double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
//输入轮廓,点,
//true 发返回实际距离
//false 1边上在内面,0在边界,-1在外部
多边形逼近
用RDP算法,使轮廓的顶点数变少。
void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, //两点间的最小距离
bool closed ); //是否形成闭合曲线
轮廓几何及特性概括
//获得长度
double arcLength( InputArray curve, bool closed ); //轮廓及轮廓是否闭合
//获得矩形包围框
Rect boundingRect( InputArray array );
//获得旋转的矩形包围框
RotatedRect minAreaRect( InputArray points );
//获得最小包围圆
void minEnclosingCircle( InputArray points, //输入点集
CV_OUT Point2f& center, //圆心位置
CV_OUT float& radius ); //圆半径
//最小椭圆
RotatedRect fitEllipse( InputArray points );
图像矩
中心位置 x=m10/m00 y=y01/y00
//计算矩,返回的位Moments结构体,包括0-3阶 m,mu,nu。
Moments moments( InputArray array, bool binaryImage = false );//输入数据,是否是二值图像
//计算面积
double contourArea( InputArray contour, bool oriented = false ); //输入轮廓数据,返回绝对值
//计算长度
double arcLength( InputArray curve, bool closed ); //输入轮廓曲线,是否是封闭曲线。