文章目录
- 一、轮廓(find contour)
- findContours发现轮廓
- drawContours绘制轮廓
- 二、凸包(Convex Hull)
- 函数 convexHull
一、轮廓(find contour)
轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。
所以边缘提取的阈值选定会影响最终轮廓发现结果
findContours发现轮廓
在二值图像上发现轮廓使用
InputOutputArray binImg, 输入图像,非0的像素被看成1,0的像素值保持不变,8-bit
OutputArrayOfArrays contours, 全部发现的轮廓对象
OutputArray, hierachy 图该的拓扑结构
std::vector<cv::Vec4i>
,可选,该轮廓发现算法正是基于图像拓扑结构实现。它的元素与轮廓的数量一样多。对于每个第 i 个轮廓轮廓[i],元素hierarchy[i][0]、hierarchy[i][1]
int mode, 轮廓返回的模式
int method, 发现方法
Point offset=Point() 轮廓像素的位移,默认(0, 0)没有位移
)
drawContours绘制轮廓
在二值图像上发现轮廓cv::findContours之后对发现的轮廓数据进行绘制显示
InputOutputArray binImg, 输出图像
OutputArrayOfArrays
contours, 全部发现的轮廓对象
Int contourIdx 轮廓索引号
const Scalar & color, 绘制颜色
int thickness,/ 绘制线宽
int lineType , 线的类型LINE_8
InputArray hierarchy, 拓扑结构图
int maxlevel, 最大层数, 0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓
Point offset=Point() 轮廓位移,可选
头文件 quick_opencv.h:声明类与公共函数
#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;
class QuickDemo {
public:
...
void contours_Demo(Mat& image1);
void convexhull_Demo(Mat& image1);
};
主函数调用该类的公共成员函数
#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;
int main(int argc, char** argv) {
Mat src1 = imread("D:\\Desktop\\ttt.png");
if (src1.empty()) {
printf("Could not load images1...\n");
return -1;
}
imshow("input1", src1);
QuickDemo qk;
qk.contours_Demo(src1);
qk.convexhull_Demo(src1);
waitKey(0);
destroyAllWindows();
return 0;
}
源文件 quick_demo.cpp:实现类与公共函数
#include <quick_opencv.h>
using namespace cv;
using namespace std;
void QuickDemo::contours_Demo(Mat& image) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
int threshold_val = 100;
Mat gray_img, edges_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
Canny(gray_img, edges_img, threshold_val, threshold_val * 2, 3, false);
if (edges_img.empty()) {return;}
findContours(edges_img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
if (contours.size() == 0) {return;}
Mat drawImg = Mat::zeros(edges_img.size(), CV_8UC3);
RNG rng(1234);
for (int i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawImg, contours, i, color, 2, 8, hierarchy, 0, Point(0, 0));
}
imshow("gray_img", gray_img);
imshow("edges_img", edges_img);
imshow("drawImg", drawImg);
}
二、凸包(Convex Hull)
什么是凸包(Convex Hull),在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部。即:包含点集合S中所有点的最小凸多边形称为凸包
检测算法 - Graham扫描法
- 首先选择Y方向最低的点作为起始点p0
- 从p0开始极坐标扫描,依次添加
(排序顺序是根据极坐标的角度大小,逆时针方向)
- 对每个点pi来说,如果添加pi点到凸包中导致一个左转向(逆时针方法)则添加该点到凸包, 反之如果导致一个右转向(顺时针方向)删除该点从凸包中
函数 convexHull
InputArray points, // 输入候选点,来自findContours
OutputArray hull, // 凸包
bool clockwise, // default true, 顺时针方向
bool returnPoints // true 表示返回点个数,如果第二个参数是 vector则自动忽略
)
演示示例:
流程:
- 转成灰度图
- 模糊降噪
- 边缘轮廓检测
- 查找轮廓
- 生成凸包
void QuickDemo::convexhull_Demo(Mat& image) {
Mat gray_img, edges_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
blur(gray_img, gray_img, Size(3, 3));
Canny(gray_img, edges_img, 100, 100 * 2);
vector<vector<Point>> contours;
findContours(edges_img, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
if (contours.size() == 0) { return; }
vector<vector<Point>> hulls(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
convexHull(contours[i], hulls[i]);
}
RNG rng(1234);
Mat dst = Mat::zeros(image.size(), CV_8UC3);
for (size_t k = 0; k < contours.size(); k++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, (int)k, color, 2, 8);
drawContours(dst, hulls, (int)k, color, 2, 8);
}
imshow("edges_img", edges_img);
imshow("dst", dst);
}