原文地址:https://docs.opencv.org/3.4/d5/daa/tutorial_js_contours_begin.html
目标
- 什么是“轮廓”
- 学会寻找轮廓、绘制轮廓
- cv.findContours(), cv.drawContours()
“轮廓”是什么
轮廓可以简单地解释为连接所有连续点(沿着边界)的曲线,具有相同的颜色或强度。轮廓是形状分析和物体检测与识别的有用工具。
- 为了更好的准确性,使用二进制图像。因此在找到轮廓之前,应先用阈值或canny算法进行边缘检测。
- 从opencv3.2开始,原图像不会再被这个函数修改。
- 在OpenCV中,找到轮廓一般都是从黑色背景中找到白色物体。所以要记住,要找到的对象应该是白色,背景应该是黑色。
如何绘制轮廓
可以使用cv.drawContours()绘制轮廓。只要给出了边界点集,这个函数就可以绘制轮廓。
函数介绍
cv.findContours (image, contours, hierarchy, mode, method, offset = new cv.Point(0, 0))
参数说明
image | 一个8位单通道的二值图像。非零像素被视为1。零像素保持为0。 |
contours | 检测到的轮廓 |
hierarchy | 包含有关图像拓扑的信息。它有与轮廓数量同样多的元素。 |
mode | 轮廓检索模式(参见 cv.RetrievalModes). |
method | 轮廓逼近算法(参见 cv.ContourApproximationModes). |
offset | 每个轮廓点移动的可选偏移量。如果从图像ROI中提取轮廓然后应在整个图像上下文中分析轮廓,则非常有用。 |
cv.drawContours (image, contours, contourIdx, color, thickness = 1, lineType = cv.LINE_8, hierarchy = new cv.Mat(), maxLevel = INT_MAX, offset = new cv.Point(0, 0))
参数说明
image | 输出图像 |
contours | 所有输入的轮廓 |
contourIdx | 表示要绘制的轮廓的参数。如果是负数,则绘制所有轮廓。 |
color | 轮廓颜色 |
thickness | 绘制轮廓的线条粗细。如果是负数,则绘制轮廓内部。 |
lineType | line connectivity(参见 cv.LineTypes). |
hierarchy | 有关层次结构的可选信只有在想要绘制一些轮廓时才需要它(参见 maxLevel). |
maxLevel | 绘制轮廓的最大级别。如果为0,则仅绘制指定的轮廓。如果为1,则该函数绘制轮廓和所有嵌套轮廓。如果为2,则该函数绘制轮廓、所有嵌套轮廓、所有嵌套到嵌套的轮廓等等。仅当有可用的层次结构时才考虑此参数。 |
offset | (可选)轮廓移位参数。 |
下面是案例代码和结果:
let src = cv.imread('canvasInput');
let dst = cv.Mat.zeros(src.cols, src.rows, cv.CV_8UC3);
cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0);
cv.threshold(src, src, 120, 200, cv.THRESH_BINARY);
let contours = new cv.MatVector();
let hierarchy = new cv.Mat();
// 可以试着改变一下参数
cv.findContours(src, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);
// 用随机颜色绘制轮廓线
for (let i = 0; i < contours.size(); ++i) {
let color = new cv.Scalar(Math.round(Math.random() * 255), Math.round(Math.random() * 255),
Math.round(Math.random() * 255));
cv.drawContours(dst, contours, i, color, 1, cv.LINE_8, hierarchy, 100);
}
cv.imshow('canvasOutput', dst);
src.delete(); dst.delete(); contours.delete(); hierarchy.delete();
轮廓逼近算法
method是cv.findContours()函数中的第五个参数。它实际上表示什么呢?
前面我说过轮廓是具有相同强度的形状的边界。它存储形状边界的(x,y)坐标。但是它存储了所有坐标吗?这由该轮廓近似方法指定。
如果传递cv.ContourApproximationModes.CHAIN_APPROX_NONE.value,则存储所有边界点。但实际上我们需要所有的点吗?例如,你找到了一条笔直的轮廓线,那是否需要用线上所有的点来表示这条线?不,我们只需要该线的两个端点。这就是cv.CHAIN_APPROX_SIMPLE的作用。它删除所有冗余点并压缩轮廓,从而节省内存。