一、轮廓
一个轮廓代表 一系列的点(像素),这一系列的点构成一个有序的点集,所以可以把一个轮廓理解为 一个有序的点集。
二、程序显示
在使用这些算子的时候一般都要进行图像预处理,其目的是为了查找更加清晰的轮廓
一般的预处理的主要包括:灰度值化、图像去噪(高斯滤波、均值滤波等)、二值化、形态学处理:morphologyEx() 等
findContours
findContours(
InputOutputArray binImg, //输入8bit图像(二值图像)
utputArrayOfArrays contours, //返回的轮廓点集合,一个list,每一个元素是一个轮廓,轮廓是一个N*1*2的ndarray
OutputArray, hierachy // 图像的拓扑结构(轮廓之间的层次关系):是一个N*4的array,hierarchy[i][0]~hierarchy[i][3]分别表示第i个轮廓的后一个轮廓,
int mode, //轮廓返回的模式 轮廓检索模式
RETR_EXTERNAL: 只检索最外层的轮廓 (返回值会设置所有hierarchy[i][2]=hierarchy[i][3]=-1)
RETR_LIST: 检索所有的轮廓,但不建立轮廓间的层次关系(hierarchy relationship)
RETR_CCOMP: 检测所有的轮廓,但只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层,只有内围轮廓不再包含子轮廓时,其为内层。
RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
int method, //发现方法:
CHAIN_APPROX_NONE: 保存物体边界上所有连续的轮廓点到contours中,即点(x1,y1)和点(x2,y2),满足max(abs(x1-x2),abs(y2-y1))==1,则认为其是连续的轮廓点
CHAIN_APPROX_SIMPLE: 仅保存轮廓的拐点信息到contours,拐点与拐点之间直线段上的信息点不予保留
CHAIN_APPROX_TC89_L1: 采用Teh-Chin chain近似算法
CHAIN_APPROX_TC89_KCOS:采用Teh-Chin chain近似算法
Point offset=Point() //轮廓像素的位移(默认没有位移(0, 0))所有的轮廓信息相对于原始图像的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量(在图片裁剪时比较有用)
)
drawContours
drawContours(
InputOutputArray binImg, // 绘制的轮廓的图像矩阵
OutputArrayOfArrays contours, //找到的全部轮廓对象
Int contourIdx, //轮廓索引号 表示指定一个轮廓进行绘制;若为负数,表示绘制所有轮廓
const Scalar & color, //绘制颜色
int thickness, //绘制线宽
int lineType, //线的类型(默认8)
InputArray hierarchy, //拓扑结构图
int maxlevel, //最大层数(0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓)
Point offset=Point(), //轮廓位移
)
代码显示(代码是随便写得,很多细节不考虑):
const char* INPUT_TITLE = "INPUT-IMAGE1";
const char* OUT_TITLE = "OUT_WIN";
Mat base, src, src2, map_x, map_y, dst, src_gary, temp;
int max_Threshod = 255;
int Threshod_value = 100;
void Callback_Canny(int, void*);
void Callback_Canny(int, void*)
{
Mat cannyoutput;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;// 拓补结构层次
// 2 、使用canny 算子来提取边缘 得到一个2值化的图像
Canny(src_gary, cannyoutput, Threshod_value, Threshod_value * 2, 3, false);
imshow("Canny", cannyoutput);
Mat element = getStructuringElement(MORPH_CROSS, Size(10, 1), Point(-1, -1));
dilate(cannyoutput, cannyoutput, element, Point(-1, -1), 2);
imshow("dilate", cannyoutput);
threshold(cannyoutput, cannyoutput,0,255, THRESH_OTSU + THRESH_BINARY);
imshow("threshold", cannyoutput);
// 3、 找到轮廓
findContours(cannyoutput, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
// 4 画出轮廓
dst = Mat::zeros(src_gary.size(), CV_8SC3);
base = src.clone();
RNG r(12345);
for (size_t i = 0; i < contours.size(); i++)
{
Scalar sc = Scalar(r.uniform(0, 255), r.uniform(0, 255), r.uniform(0, 255));
drawContours(dst, contours, i, sc, 2, 8, hierachy, 0, Point(0, 0));
drawContours(base, contours, i, sc, 2, 8, hierachy, 0, Point(0, 0));
}
imshow(OUT_TITLE, dst);
imshow(OUT_TITLE, base);
}
int main(int args, char* arg)
{
//目标图像
src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\501.png");
if (!src.data)
{
printf("could not load image....\n");
}
namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
imshow(INPUT_TITLE, src);
namedWindow(OUT_TITLE, CV_WINDOW_AUTOSIZE);
// 1,将这个图像转换成灰度图像
cvtColor(src, src_gary, CV_BGR2GRAY);
GaussianBlur(src_gary, src_gary,Size(3,3),1);
createTrackbar(" Threshod:", OUT_TITLE, &Threshod_value, max_Threshod, Callback_Canny);
Callback_Canny(0, 0);
waitKey(0);
return 0;
}
三、轮廓的周长与面积
上面我们已经找到了轮廓,是一些点的集合,那么我们要计算面积和周长等如何来计算了,OpenCV 给出了下面这这些函数:
contourArea
contourArea 函数调用形式 主要用于计算图像轮廓的面积
double contourArea(
InputArray contour,://输入的点,一般是图像的轮廓点
bool oriented=false ) //表示某一个方向上轮廓的的面积值,顺时针或者逆时针,一般选择默认false
arcLength
arcLength 主要是计算图像轮廓的周长、
double arcLength(InputArray curve)
InputArray curve:表示图像的轮廓
bool closed:表示轮廓是否封闭的
)
示例如下:
代码显示:
//目标图像
src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\503.jpg");
if (!src.data)
{
printf("could not load image....\n");
}
imshow("原图", src);
cvtColor(src, src_gary, CV_BGR2GRAY);
imshow("cvtColor", src_gary);
threshold(src_gary, src_gary, 177, 255, THRESH_BINARY);
Mat element = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
dilate(src_gary, src_gary, element, Point(-1, -1), 1);
imshow("threshold", src_gary);
vector<vector<Point>> contours_p;
//vector<Vec4i> hierachy;// 拓补结构层次
findContours(src_gary, contours_p, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
int xcon = boundingRect(contours_p[0]).x;
int ycon = boundingRect(contours_p[0]).y;
int width = boundingRect(contours_p[0]).width;
int hight = boundingRect(contours_p[0]).height;
Rect rect = boundingRect(contours_p[0]);
RotatedRect rect2 = minAreaRect(contours_p[0]);
// ==============最小外接圆==========================================
Point2f center;
float radius;
minEnclosingCircle(contours_p[0], center, radius);
src_copy_circle = src.clone();
circle(src_copy_circle, center, radius, Scalar(0, 0, 255), 1, 8);
imshow("circle", src_copy_circle);
//==============最优拟合椭圆===============
RotatedRect box = fitEllipse(contours_p[0]);
src_copy_fitEllipse = src.clone();
ellipse(src_copy_fitEllipse, box, Scalar(0, 0, 255), 1, CV_AA);
imshow("ellipse", src_copy_fitEllipse);
//============外接矩形=================
src_copy = src.clone();
src_copy_sm = src.clone();
rectangle(src_copy, rect, Scalar(0, 0, 255), 1, 8);
imshow("rectangle", src_copy);
//==============旋转矩形===============
Point2f vertices[4];
rect2.points(vertices);
for (int i = 0; i < 4; i++)
{
line(src_copy_sm, vertices[i], vertices[(i + 1) % 4], Scalar::all(255), 1, 8);
}
imshow("RotatedRect", src_copy_sm);
//========================外界三角形======================
//CV_EXPORTS_W double minEnclosingTriangle(InputArray points, CV_OUT OutputArray triangle);
vector<Point2f>triangle;
minEnclosingTriangle(contours_p[0], triangle);
src_triangle = src.clone();
for (int i = 0; i < 3; i++)
{
line(src_triangle, triangle[i], triangle[(i + 1) % 3], Scalar(255, 255, 0), 1, 8);
}
imshow("triangle", src_triangle);
==============拟合多边形===============
//approxPolyDP(contours_p[i], contours_poly[i], 15, true);
// InputArray curve:输入曲线,数据类型可以为vector<Point>
// OutputArray approxCurve:输出折线,数据类型可以为vector<Point>。
// double epsilon:判断点到相对应的line segment 的距离的阈值。(距离大于此阈值则舍弃,小于此阈值则保留,epsilon越小,折线的形状越“接近”曲线。)
// bool closed:曲线是否闭合的标志位。
src_p = src.clone();
vector<vector<Point>> contours_poly(contours_p.size());//用于存放折线点集
for (int i = 0; i < contours_p.size(); i++)
{
approxPolyDP(contours_p[i], contours_poly[i], 10, true);
drawContours(src_p, contours_poly, i, Scalar(0, 255, 0), 2, 8); //绘制
}
imshow("approx", src_p);
waitKey(0);
return 0;
拟合直线:
void cv::fitLine(
InputArray points, // 二维点的数组或vector,可以是二维点的cv::Mat数组,也可以是二维点的STL vector。
OutputArray line, // 输出直线,Vec4f (2d) 或 Vec6f (3d) 输出参数的前半部分给出的是直线的方向,而后半部分给出的是直线上的一点
int distType, // 距离类型,拟合直线时,要使输入点到拟合直线的距离和最小化(即下面公式中的cost最小化),可供选的距离类型如下表所示,ri表示的是输入的点到直线的距离
double param, // 距离参数,与所选的距离类型有关。当此参数被设置为0 时,该函数会自动选择最优值
double reps, // 径向的精度参数,拟合直线所需要的径向精度,通常该值被设定为0.01
double aeps // 角度精度参数,拟合直线所需要的角度精度,通常该值被设定为0.01
);
最小外包三角形
点和轮廓的位置关系:
么空间中任意一点和 这个轮廓无非有三种关系:点在轮廓外、点在轮廓上、点在轮廓内。OpenCV提供的函数:
参数measureDist是bool类 型,当其值为false时,函数pointPolygonTest的返回值有三种,即+1、0、-1,+1代表pt在 点集围成的轮廓内,0代表pt在点集围成的轮廓上,-1代表pt在点集围成的轮廓外;当其 值为true时,则返回值为pt到轮廓的实际距离。
int main()
{
vector<Point> contours;
contours.push_back(Point2f(0, 0));
contours.push_back(Point2f(50, 30));
contours.push_back(Point2f(100, 0));
contours.push_back(Point2f(100, 100));
Mat img2, img3;
Mat img = Mat::zeros(Size(230,230),CV_8UC1);
int num = contours.size(); //点的数量
for (int i = 0; i < num; i++)
{
line(img, contours[i], contours[(i + 1)%4],Scalar(255),1);
}
// 首尾相连
// 标注点的位置
img2 = img.clone();
img3 = img.clone();
circle(img, Point(80, 40), 3, Scalar(255), CV_FILLED);
circle(img2, Point(50, 0), 3, Scalar(255), CV_FILLED);
circle(img3, Point(100, 100), 3, Scalar(255), CV_FILLED);
// 点在轮廓内
double dist1 = pointPolygonTest(contours, Point2f(80, 40),true);
cout << "dist1 " << dist1 << endl;
double dist2 = pointPolygonTest(contours, Point2f(50, 0), true);
cout << "dist2 " << dist2 << endl;
double dist3 = pointPolygonTest(contours, Point2f(100, 100), true);
cout << "dist3 " << dist3 << endl;
imshow("轮廓", img);
imshow("轮廓2", img2);
imshow("轮廓3", img3);
waitKey(0);
return 0;
}
轮廓的凸包缺陷
通过函数convexHull可以得到点集的最小凸包,OpenCV还提 供了一个函数:
void convexityDefects(
InputArray contour,//检测到的轮廓
InputArray convexhull, //检测到的凸包 vector<vector<Point>>和vector<vector<int>>两种类型结果
OutputArrayconvexityDefects)//输出参数,检测到的最终结果 vector<vector<Vec4i>>类型,Vec4i存储了起始点(startPoint),结束点(endPoint),距离convexity hull最远点(farPoint)以及最远点到convexity hull的距离(depth)
vector<Point> contours;
contours.push_back(Point2f(20, 20));
contours.push_back(Point2f(50, 70));
contours.push_back(Point2f(20, 120));
contours.push_back(Point2f(120, 120));
contours.push_back(Point2f(100, 70));
contours.push_back(Point2f(120, 20));
vector<int> hull;
convexHull(contours, hull,false,false);
//计算凸包
vector<Vec4i> defects;
convexityDefects(contours,hull, defects);
//
for (int i = 0; i < defects.size(); i++)
{
cout << defects[i] << endl;
}
waitKey(0);
return 0;