• 计算机视觉:使用opencv进行直线检测


    1 直线检测介绍

    图像处理中,直线检测是一种常见的算法,它通常获取n个边缘点的集合,并找到通过这些边缘点的直线。其中用于直线检测,最为流行的检测器是基于霍夫变换的直线检测技术。

    1.1 什么是霍夫变换

    霍夫变换(Hough Transform)是图像处理中的一种特征提取方法,可以识别图像中的几何形状。它将在参数空间内进行投票来决定其物体形状,通过检测累计结果找到一极大值所对应的解,利用此解即可得到一个符合特定形状的参数。

    1.2 霍夫变换原理

    一条直线可由两个点A=(X1,Y1)和B=(X2,Y2)确定(笛卡尔坐标)

    另一方面,y = kx + q 也可以写成关于(k,q)的函数表达式(霍夫空间):

    对应的变换可以通过图形直观表示:

    变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点

    反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

     再来看看A、B两个点,对应霍夫空间的情形:

    接下来,再看一下三个点共线的情况: 

     

    可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。

    如果不止一条直线呢?再看看多个点的情况(有两条直线):

    其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式选择由尽可能多直线汇成的点

    霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

    到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

    k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示

    在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是[\rho, \theta ]的参数,给出对比图: 

    2 使用opencv进行直线检测

    在使用霍夫变换侦测直线前,须先利用边缘检测算法来减少图像的数据量、剔掉不相关的信息,保留图像中重要的结构特征。

    2.1 图像灰度化

    原始图像与灰度化的图像如下:

    2.2 边缘检测

    利用边缘检测算法(Canny、Sobel、Laplacian等)来检测物体边缘,代码如下:

    canny = cv2.Canny(gray_img, 30, 150)

    2.3 霍夫变换

    使用霍夫变换来得出直线检测结果,代码如下:

    1. lines = cv2.HoughLines(canny, 1, np.pi / 180, 180)
    2. lines1 = lines[:, 0, :]
    3. for rho, theta in lines1[:]:
    4. a = np.cos(theta)
    5. b = np.sin(theta)
    6. x0 = a * rho
    7. y0 = b * rho
    8. x1 = int(x0 + 3000 * (-b))
    9. y1 = int(y0 + 3000 * (a))
    10. x2 = int(x0 - 3000 * (-b))
    11. y2 = int(y0 - 3000 * (a))
    12. cv2.line(im, (x1, y1), (x2, y2), (0, 0, 255), 2)

    运行结果显示如下:

    2.4 完整代码

    1. import cv2
    2. import numpy as np
    3. im = cv2.imread("../data/ladder.jpg")
    4. cv2.imshow('original', im)
    5. cv2.waitKey(0)
    6. gray_img = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    7. cv2.imshow('gray', gray_img)
    8. cv2.waitKey(0)
    9. canny = cv2.Canny(gray_img, 30, 150)
    10. cv2.imshow('canny', canny)
    11. cv2.waitKey(0)
    12. lines = cv2.HoughLines(canny, 1, np.pi / 180, 180)
    13. lines1 = lines[:, 0, :]
    14. for rho, theta in lines1[:]:
    15. a = np.cos(theta)
    16. b = np.sin(theta)
    17. x0 = a * rho
    18. y0 = b * rho
    19. x1 = int(x0 + 3000 * (-b))
    20. y1 = int(y0 + 3000 * (a))
    21. x2 = int(x0 - 3000 * (-b))
    22. y2 = int(y0 - 3000 * (a))
    23. cv2.line(im, (x1, y1), (x2, y2), (0, 0, 255), 2)
    24. cv2.imshow('original', im)
    25. cv2.waitKey(0)

    3 使用opencv检测倾角

    1. import cv2
    2. import numpy as np
    3. def line_detect(image):
    4. # 将图片转换为HSV
    5. hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    6. # 设置阈值
    7. lowera = np.array([0, 0, 221])
    8. uppera = np.array([180, 30, 255])
    9. mask1 = cv2.inRange(hsv, lowera, uppera)
    10. kernel = np.ones((3, 3), np.uint8)
    11. # 对得到的图像进行形态学操作(闭运算和开运算)
    12. mask = cv2.morphologyEx(mask1, cv2.MORPH_CLOSE, kernel) #闭运算:表示先进行膨胀操作,再进行腐蚀操作
    13. mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) #开运算:表示的是先进行腐蚀,再进行膨胀操作
    14. # 绘制轮廓
    15. edges = cv2.Canny(mask, 50, 150, apertureSize=3)
    16. # 显示图片
    17. cv2.imshow("edges", edges)
    18. # 检测白线 这里是设置检测直线的条件,可以去读一读HoughLinesP()函数,然后根据自己的要求设置检测条件
    19. lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 40,minLineLength=10,maxLineGap=10)
    20. print "lines=",lines
    21. print "========================================================"
    22. i=1
    23. # 对通过霍夫变换得到的数据进行遍历
    24. for line in lines:
    25. # newlines1 = lines[:, 0, :]
    26. print "line["+str(i-1)+"]=",line
    27. x1,y1,x2,y2 = line[0] #两点确定一条直线,这里就是通过遍历得到的两个点的数据 (x1,y1)(x2,y2)
    28. cv2.line(image,(x1,y1),(x2,y2),(0,0,255),2) #在原图上画线
    29. # 转换为浮点数,计算斜率
    30. x1 = float(x1)
    31. x2 = float(x2)
    32. y1 = float(y1)
    33. y2 = float(y2)
    34. print "x1=%s,x2=%s,y1=%s,y2=%s" % (x1, x2, y1, y2)
    35. if x2 - x1 == 0:
    36. print "直线是竖直的"
    37. result=90
    38. elif y2 - y1 == 0 :
    39. print "直线是水平的"
    40. result=0
    41. else:
    42. # 计算斜率
    43. k = -(y2 - y1) / (x2 - x1)
    44. # 求反正切,再将得到的弧度转换为度
    45. result = np.arctan(k) * 57.29577
    46. print "直线倾斜角度为:" + str(result) + "度"
    47. i = i+1
    48. # 显示最后的成果图
    49. cv2.imshow("line_detect",image)
    50. return result
    51. if __name__ == '__main__':
    52. # 读入图片
    53. src = cv2.imread("lines/line6.jpg")
    54. # 设置窗口大小
    55. cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE)
    56. # 显示原始图片
    57. cv2.imshow("input image", src)
    58. # 调用函数
    59. line_detect(src)
    60. cv2.waitKey(0)

  • 相关阅读:
    前端时间格式传入后端负载里面没有东西
    算法小白的心得笔记:比较小数点后五位,而不会受到浮点数精度问题的影响。
    git常用命令 Git常用命令 git常用操作 git 操作
    软件测试实战项目,问题答疑
    Vue基础语法【上】
    可编程 USB 转串口适配器开发板的详细接口与功能
    Docker实践经验:Docker 上部署 mysql8 主从复制
    LR多分类与softmax多少分类,他们的目标函数是什么?他们的区别是?他们分别适用于什么场景?
    c语言之函数篇
    LeetCode—<动态规划专项>剑指 Offer 19、49、60
  • 原文地址:https://blog.csdn.net/lsb2002/article/details/134352796