(0)轮廓分析概述及作用
通过将 Canny 边缘提取或者二值化结果作为输入图像来实现
轮廓发现与绘制
,
可是这些并不是我们想要的最终结果,
我们一般根据获取到的轮廓求出它们的外接矩形或者最小外接矩形,
并计算外接矩形的
横纵比例, 轮廓面积, 周长等数据
,
然后使用这些数据实现
特定几何形状轮廓
的查找与过滤,
为
后续的处理与分析
剔除不正确的区域而保留候选对象.
(1)边界框
最常见的获取
轮廓的外接矩形
是边界框,
获取每个轮廓的边界框,
通过它可以
得到与各个轮廓相对应的高度与宽度
,
并能通过它计算出轮廓的纵横比.
通过轮廓点集合得到轮廓边界框的 API 如下:
boundingRect(MatOfPoint points)
其中, points 是轮廓所有点的集合对象. 注意其数据类型.
调用该 API 会返回一个 Rect 对象实例, 它是 OpenCV 关于矩形的数据结构,
从中可以得到外界矩形 (边界框) 的宽高,
然后就可以计算出轮廓的横纵比了.
这种情况下得到的边界框不一定满足条件, 有时候我们还需要获取轮廓的最小边界框.
(2)最小边界框
与上面边界框不同的是,
获取到的最小边界框有时候不是一个水平或者垂直的矩形,
而是一个旋转了一定角度的矩形,
但是最小外接矩形 (最小边界框) 能够更加真实地反映出轮廓的几何结构大小,
而横纵比结果更能反映出轮廓的真实几何特征,
所以有些时候我们计算的经常是最小外接矩形,
相关 API 函数如下:
RotatedRect minAreaRect(MatOfPoint2f points)
其中, points 是轮廓的所有点的集合对象. 注意其数据类型.
调用该 API 会返回一个
RotatedRect 对象实例
,
它是 OpenCV 关于
旋转矩形的数据结构
,
其包含了
旋转角度, 矩形的宽, 高及四个顶点等信息
,
通过相关的 API 都可以查询获得,
绘制旋转矩形对象的时候,
首先需要得到四个顶点,
然后
通过 OpenCV 绘制直线的 API
来
完成旋转矩形的绘制
.
(3)面积与周长
轮廓分析中包含了
轮廓大小的度量
,
这些度量最常见的就是计算轮廓的面积大小与长度大小,
这些数据对分析轮廓与
过滤掉一些不符合条件的轮廓
十分有用.
计算轮廓面积的 API 如下:
contourArea(Mat contour, boolean oriented)
contour: 轮廓的所有点的集合对象.
oriented: 表示轮廓的方向, 当 oriented = true 时返回的面积是一个有符号值, 默认为 false, 返回的是绝对值.
计算轮廓周长的 API 如下:
arcLength(MatOfPoint2f curve, boolean closed)
curve: 轮廓的所有点的集合对象. 注意数据类型.
closed: 表示是否为闭合曲线, 默认是 true.
完整的发现获取轮廓, 外接轮廓, 最小外接轮廓, 横纵比, 面积与长度的代码演示如下:
- private void measureContours(Mat src, Mat dst) {
- Mat gray= new Mat();
- Mat binary = new Mat();
- // 二值
- Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
- Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
- // 轮廓发现
- List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
- Mat hierarchy = new Mat();
- Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
- // 测量轮廓
- dst.create(src.size(), src.type());
- for(int i=0; i<contours.size(); i++) {
- Rect rect = Imgproc.boundingRect(contours.get(i));
- double w = rect.width;
- double h = rect.height;
- double rate = Math.min(w, h)/Math.max(w, h);
- Log.i("Bound Rect", "rate:" + rate);// 一个轮廓元素打印一次
- RotatedRect minRect = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(i).toArray()));
- w = minRect.size.width;
- h = minRect.size.height;
- rate = Math.min(w, h)/Math.max(w, h);
- Log.i("Min Bound Rect", "rate:" + rate);
- double area = Imgproc.contourArea(contours.get(i), false);
- double arclen = Imgproc.arcLength(new MatOfPoint2f(contours.get(i).toArray()), true);
- Log.i("contourArea", "area:" + rate);
- Log.i("arcLength", "arcLength:" + arclen);
- Imgproc.drawContours(dst, contours, i, new Scalar(0, 0, 255), 1);
- }
- // 释放内存
- gray.release();
- binary.release();
- }
运行结果(左侧是原图, 右侧是轮廓发现与绘制, 计算结果参见 logcat):
上述的代码是求取图像的全部轮廓,
修改上述程序, 把返回轮廓改为返回最外层轮廓 RETR_EXTERNAL,
同时修改阈值化方法, 将其改为 THRESH_BINARY_INV,
则运行结果如下:
感兴趣的小伙伴可以进一步细化该方法,
将计算得到的轮廓几何属性值如长度, 面积等
通过 putText 函数显示到输出的图像上
参考材料
《OpenCV Android 开发实战》(贾志刚 著)
关于《OpenCV Android 开发实战》作者的 GitHub 项目
笔者基于作者 GitHub 维护的 App
来源: http://www.jianshu.com/p/9ac334cbf7f9