Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())

时间:2023-03-08 22:16:43
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃
//2017-04-21更新:
很多网友希望能得到源码,由于在公司做的,所以不太方便传出来。而且我估计很多人可能都是对OpenCV在AndroidStudio环境下配置的问题,给大家推荐一本书《Mastering OpenCV Android Application Programming》,中文版叫《深入OpenCV Android应用开发》,某宝有卖正版,书中有详细代码(第二章有霍夫变换),可以到www.packtpub.com下载,如果不会可以私信邮箱给我,我可以发链接给你。
霍夫检测是Paul Hough1962年提出的图像中几何图形识别算法,霍夫线检测是基于图像二值化的变换,利用二值化图像中的点集合来发现候选直线集合。原理是利用坐标空间变换,将笛卡尔坐标系(直角坐标系)下的点(图1)映射到极坐标系(图2),这种点到曲线的映射变换称为霍夫变换,对极坐标进行峰值统计,对统计规则满足直线要求的点进行归类。
Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())(图1)
Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())(图2)
Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())(图3)
Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())(图4)
      如图1,直角坐标系中直线方程表示为:y = kx+b,其中的一个点(x0,y0)映射到极坐标中表示为:r = cos(θ)*x0 + sin(θ )*y0,r是半径,θ是角度,当x0和y0确定时,可以出现很多满足条件的r和θ,如果将图绘制出来就是图2的弦波。多个点就会出现多个弦波,交点存在唯一的r0和θ0,对应到直角坐标系即图4的直线,满足r0和θ0的点即为一条直线。
      Opencv提供了两种霍夫线检测算法:HoughLines和HoughLinesP,在Android中Java代码为:
HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn)
HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
image:输入图像,要求必须是二值化图像,通常是用canny后的图片作为输入图;
lines:输出结果,在HoughLines方法中,输出结果是(r,θ),lines是1行(row)n列(col)的Mat,通过get(0,i)可以获取到(r,θ)数组;在HoughLinesP方法中,输出结果是P1(x1,y1),P2(x2,y2),即端点的坐标值,lines是1行(row)n列(col)的Mat,通过get(0,i)可以获取到(x1, y1, x2, y2)数组;
rho:极坐标系中半径r的搜索步长(累加值),如果为1表示每次累计1个像素;
theta:极坐标系中角度θ的搜索步长,通常设置为π/180,表示每次累加角度为π/180;
threshold:累加器的阈值参数,只有满足阈值数量的点的直线才会被检测出来;
srn/stn:对于多尺度霍夫变换,rho为极坐标系中累加值的粗粒度表示,srn/stn为细粒度的累加值;
minLineLength:HoughLinesP方法中,只有长度超过minLineLength的线段才能被检测出来;
maxLineGap:HoughLinesP方法中,线段跨越的最大空隙为maxLineGap。
//Opencv在Android中,大部分的vector、指针、数组都是以Mat来呈现的
Imgproc.HoughLinesP(matIn, line, 1, Math.PI / 180, 20, 150, 10);
if (line.rows() > 0 && line.cols() > 0) {
    for (int i = 0; i < line.cols(); i++) {
        double[] l = line.get(0, i);
        if (l.length == 4) {
            if (Math.abs(l[1] - l[3]) < 2) {
                Point p1 = new Point(l[0], l[1]);
                Point p2 = new Point(l[2], l[3]);
                Core.line(matIn, p1, p2, new Scalar(255, 255, 255), 2);
            }
        }
    }
}
注意Mat变量的结构,通过get(x,y)获取坐标点值,坐标点的各通道在java中通过数组呈现。
//2017-04-06更新
忘记说检测效果了,首先一定要先canny,针对图片的检测问题还是不大的,但是在实时camera的场景下,检测到的直线是跳跃的,尤其是在复杂背景下,不要奢望能够得到一条稳定的直线。