前段时间有一个关于光电图像处理的小竞赛,要求设计一款具备图像图形识别和测量的相机,将制定场景中指定形状目标识别并测量尺寸。
想到可能对大家会有帮助,对于在安卓平台上实现机器视觉算法还是个不错的例子,故将代码和方法放出来。本实例仅用了一点点安卓的知识和opencv4android的函数,供新手参考= =。(当然希望大神们的宝贵意见)
首先,关于平台的配置,大可参考另一篇文章点击这里
我使用的opencv版本是 opencv library-2.4.11
对于我们用到的java api ,可以在这里看官方文档。OpenCV4Android Reference官方文档
现在有关java api的书和资料很少,有本Joseph Howse 的 《Android Application Programming with OpenCV》 2013可以借鉴下。
好,现在开始言归正传。
识别和测量方法介绍
圆形识别方法
对于识别简单曲线,霍夫变换是一个著名通用又有效的方法。本系统就是采用了霍夫圆变换的算法进行圆形的识别。在opencv中提供的了相应的函数,我们可以适当调用其进行圆形的识别。
正方形识别方法
正方形的检测没有很著名和简单的算法,基于霍夫变换来检测方形复杂程度会增加很多而且确实有其他方法可以进行良好的代替。
我们可以先对图像进行预处理,之后找到其轮廓。然后将轮廓逼近成多边形。对于逼近成的多边形。我们可以进行遍历,依次检测,我们若限定多边形有4个顶点且两边夹角为90且两边相等,即可得到我们想要的正方形。
尺寸测量策略
本作品采用了参考物来标定比例尺的方法。首先,我们在被测平台上放一个已知尺寸的且可检测的几何图形,可以计算此时的已知实际长度和像素之比,即为每像素的实际长度。之后只要保持相机位置不动进行测量,即可得到测量的实际长度。
对于圆形直径的测量,在霍夫圆变换返回的曲线里,我们就可以得到圆形的半径。对于正方形的边长,我们采用先计算面积再开平方的策略,可以较准确的测得边长。
(这其实只是最最简陋的一种标定的方式,还可以采用棋盘格,考虑镜头畸变等,详细请自行查阅资料~)
界面设计
界面的设计要对应于产品的功能。
如下是在eclipse 中界面布局的实时预览截图。
坐下角为测量结果的显示,由于要实现实时的检测,这个结果每0.3秒更新一次。
右上角同样是显示的测量结果,但是这是取过平均值后的结果。在平时测量中,考虑到由于相机抖动导致测量值不稳定的现象,我们通过计算平均值得到一个确切的测量结果。
平均值计算方式为每3s计算一次平均值,即取左下角输出的实时结果进行计算。
右下角的按钮为获取比例尺的功能。只有通过此按钮成功获得了比例尺,之前的长度才会进行计算输出。在检测状态时按下此按钮,实现计算比例尺,且重复点击可更新。
点击右上角可以看见软件的主菜单。分别为:
开始检测。检测圆形。检测正方形。开启/关闭平均值计算功能
如下是进行检测和测量时,界面的室机运行截图:
如下是进行校准是,且展开菜单时的实机截图:
程序设计
设计平台与工具
本作品采用android 平台进行开发,故要使用Android开发的特定工具与插件。
我们选择了eclipse 配合 adt 插件的方式进行android 主程序的开发。
对于计算机视觉库,其本身在各个平台上都有相应的支持,故我们选择了OpenCV4android
配合opencv 的java 版本进行开发。
圆形识别类 DetectCircles
此工具类中封装了进行圆形检测的函数。将主要的两个函数列举出来。
findCircles 调用opencv 霍夫圆变换函数进行圆形检测,是圆形检测的关键。
drawCircles 调用circle 函数进行圆形绘制,标记出检测到的边缘。
public class DetectCircles {
public static double radMax=0;
public static double radMin=0;
public static double rad=0;
public static void findCircles(Mat dstImage,Mat circles){
Imgproc.HoughCircles(dstImage,circles,Imgproc.CV_HOUGH_GRADIENT,1.5,0);
}
public static void drawCircles(Mat image,Mat circles){
radMax = 0;
radMin = 0;
rad = 0;
for (int i = 0; i < circles.cols(); i++)
{
double vCircle[] = circles.get(0,i);
double x = vCircle[0];
double y = vCircle[1];
double radius = vCircle[2];
Co