精华内容
下载资源
问答
  • 主要为大家详细介绍了python+opencv实现霍夫变换检测直线,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • OpenCV-Python 霍夫变换 检测直线,圆形

    万次阅读 多人点赞 2018-10-04 17:00:41
    霍夫变换 Hough变换是经典的检测直线的算法。 其最初用来检测图像中的直线,同时也可以将其扩展,以用来检测图像中简单的结构。 OpenCV提供了两种用于直线检测的Hough变换形式。 其中基本的版本是cv2.HoughLines...

    一、直线表示

    一条直线在图像二维空间可由两个变量表示

    • 在 笛卡尔坐标系: 可由参数: ( m , b m,b m,b) 斜率和截距表示.
    • 在 极坐标系: 可由参数: ( r , θ r,\theta r,θ) 极径和极角表示
    • 对于霍夫变换, 我们将用 极坐标系 来表示直线. 因此, 直线的表达式可为下图:
      在这里插入图片描述
    • 一般来说对于点 ( x 0 , y 0 x_{0}, y_{0} x0,y0), 我们可以将通过这个点的一族直线统一定义为:
      r θ = x 0 ⋅ c o s θ + y 0 ⋅ c o s θ r_{\theta} = x_0 ·cos\theta + y_0 ·cos\theta rθ=x0cosθ+y0cosθ
      这就意味着每一对 ( r θ , θ r_{\theta},\theta rθ,θ) 代表一条通过点 ( x 0 , y 0 x_{0}, y_{0} x0,y0) 的直线.

    二、霍夫直线检测

    Hough变换是经典的检测直线的算法。
    用来检测图像中的直线,也可以用来检测图像中简单的结构。

    OpenCV的中用函数 HoughLines(标准) 和 HoughLinesP(基于统计) 来检测图像中的直线.

    • 基本的版本是cv2.HoughLines。其输入一幅含有点集的二值图(由非0像素表示),其中一些点互相联系组成直线。通常这是通过如Canny算子获得的一幅边缘图像。
    • cv2.HoughLines()输出的是[float, float]形式的ndarray,
      检测到的线(ρ , θ)中浮点点值的参数。
    • 下面的例子首先使用Canny算子获得图像边缘,然后使用Hough变换检测直线。其中HoughLines函数的参数3和4对应直线搜索的步长。
      在本例中:函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线。最后一个参数是经过某一点曲线的数量的阈值,超过这个阈值,就表示这个交点所代表的参数对(ρ , θ)在原图像中为一条直线。
    """
    cv2.HoughLines()
    	dst:   输出图像. 它应该是个灰度图 (但事实上是个二值化图)
    	lines: 储存着检测到的直线的参数对 (r,\theta) 的容器 
    	rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素.
    	theta: 参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
    	threshold:    设置阈值: 一条直线所需最少的的曲线交点
    	srn and stn:  参数默认为0
    
    cv2.HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 )
    	dst:    输出图像. 它应该是个灰度图 (但事实上是个二值化图) 
    	lines:  储存着检测到的直线的参数对 (x_{start}, y_{start}, x_{end}, y_{end}) 的容器
    	rho :   参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素.
    	theta:  参数极角 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
    	threshold:    设置阈值: 一条直线所需最少的的曲线交点。超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。
    	minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.
    	maxLineGap:   能被认为在一条直线上的两点的最大距离。
    """
    import cv2
    import numpy as np  
     
    
    original_img= cv2.imread("jianzhu.png", 0)
    img = cv2.resize(original_img,None,fx=0.8, fy=0.8, 
                     interpolation = cv2.INTER_CUBIC)
     
    img = cv2.GaussianBlur(img,(3,3),0)
    edges = cv2.Canny(img, 50, 150, apertureSize = 3)
    lines = cv2.HoughLines(edges,1,np.pi/180,118) #这里对最后一个参数使用了经验型的值
    result = img.copy()
    for line in lines:
    	rho = line[0][0]  #第一个元素是距离rho
    	theta= line[0][1] #第二个元素是角度theta
    	print (rho)
    	print (theta)
    	if  (theta < (np.pi/4. )) or (theta > (3.*np.pi/4.0)): #垂直直线
    		pt1 = (int(rho/np.cos(theta)),0)               #该直线与第一行的交点
    		#该直线与最后一行的焦点
    		pt2 = (int((rho-result.shape[0]*np.sin(theta))/np.cos(theta)),result.shape[0])
    		cv2.line( result, pt1, pt2, (255))             # 绘制一条白线
    	else:                                                  #水平直线
    		pt1 = (0,int(rho/np.sin(theta)))               # 该直线与第一列的交点
    		#该直线与最后一列的交点
    		pt2 = (result.shape[1], int((rho-result.shape[1]*np.cos(theta))/np.sin(theta)))
    		cv2.line(result, pt1, pt2, (255), 1)           # 绘制一条直线
    
    cv2.imshow('Canny', edges )
    cv2.imshow('Result', result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    在这里插入图片描述

    import cv2
    import numpy as np  
     
    img = cv2.imread("jianzhu.png")
     
    img = cv2.GaussianBlur(img,(3,3),0)
    edges = cv2.Canny(img, 50, 150, apertureSize = 3)
    lines = cv2.HoughLines(edges,1,np.pi/180,118)
    result = img.copy()
     
    #经验参数
    minLineLength = 200
    maxLineGap = 15
    lines = cv2.HoughLinesP(edges,1,np.pi/180,80,minLineLength,maxLineGap)
    for x1,y1,x2,y2 in lines[0]:
    	cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
    
    cv2.imshow('Result', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    在这里插入图片描述

    二、霍夫圆形检测

    cv2.HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) → circles

    参数说明:
    image- 8位,单通道, 灰度输入图像。
    circles- 找到的圆的输出向量。每个向量被编码为3元素的浮点向量 (x,y,半径)。
    circle_storage - 在C函数中,这是一个将包含找到的圆的输出序列的内存存储。
    method- 使用检测方法。目前,唯一实现的方法是 CV_HOUGH_GRADIENT,基本上是 21HT,在[Yuen90]中有描述 。
    dp - 累加器分辨率与图像分辨率的反比。例如,如果 dp = 1,则累加器具有与输入图像相同的分辨率。如果 dp = 2,则累加器的宽度和高度都是一半。
    minDist -检测到的 圆的中心之间的最小距离。如果参数太小,除了真正的参数外,可能会错误地检测到多个邻居圈。如果太大,可能会错过一些圈子。
    param1 - 第一个方法特定的参数。在CV_HOUGH_GRADIENT的情况下, 两个传递给Canny()边缘检测器的阈值较高(较小的两个小于两倍)。
    param2 - 第二种方法参数。在CV_HOUGH_GRADIENT的情况下
    ,它是检测阶段的圆心的累加器阈值。越小,可能会检测到越多的虚假圈子。首先返回对应于较大累加器值的圈子。
    minRadius -最小圆半径。
    maxRadius - 最大圆半径。

    我测试了一些图片,发现此方法只能检测图片背景干净一点的图片,我本来想检测印章的,可是纸张上有许多字,干扰严重,想了许多的办法(使用各种滤波等),但是效果还是不好,以下代码也是借鉴别人的。仅供参考。

    import cv2
    import numpy as np
    
    
    def detect_circle_demo(image):
        # dst = cv2.bilateralFilter(src=image, d=0, sigmaColor=100, sigmaSpace=5) # 高斯双边滤波(慢)
        dst = cv2.pyrMeanShiftFiltering(image, 10, 100)                           # 均值偏移滤波(稍微快)
        dst = cv2.cvtColor(dst, cv2.COLOR_BGRA2GRAY)
    
        cv2.imshow("adapt_image", dst)
        circle = cv2.HoughCircles(dst, cv2.HOUGH_GRADIENT, 1, 200, param1=50, param2=30, minRadius=50, maxRadius=300)
        if not circle is None:
            circle = np.uint16(np.around(circle))
            print(circle)
            for i in circle[0, :]:
                cv2.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 1)
                cv2.imshow("circle", image)
    
    
    if __name__ == "__main__":
        src = cv2.imread("C:\\Users\\xxxx\\Desktop\\piaoju\\201920100013253001_30302_01.jpg")
        src = cv2.resize(src, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC)
    
        detect_circle_demo(src)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    

    直接查找圆形

    import  cv2
    
    
    src = cv2.imread('C:\\Users\\SongpingWang\\Desktop\\piaoju\\xingqiu.png')
    cv2.imshow('src_img',src)
    gray=cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
    
    # 输出图像大小,方便根据图像大小调节minRadius和maxRadius
    # 演示 minDist 的值对检测效果的影响
    minDists = [100,125,150]
    imgcopy = [src.copy(),src.copy(),src.copy()]
    for minDist,imgcopy in zip(minDists,imgcopy):
        circles= cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,dp=1,minDist=minDist,param1=100,param2=30,minRadius=20,maxRadius=300)
    
        print('circles',circles)                         # 查看返回值
        print('len(circles[0])',len(circles[0]))         # 输出检测到圆的个数
    
        print('-------------------------------------')
    
        for circle in circles[0]:
            x=int(circle[0])                             # 坐标行列
            y=int(circle[1])
            r=int(circle[2])                             # 半径
            img=cv2.circle(imgcopy,(x,y),r,(0,0,255),2)  # 在原图用指定颜色标记出圆的位置
        cv2.imshow('circle_img_'+str(minDist),img)       # 显示新图像
        cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    '''
    circles输出如下:
    circles [[[ 89.5 279.5  84.5]
    		  [328.5 277.5  76.5]
    		  [305.5 100.5  86.2]
    		  [101.5  95.5  87.7]
    		  [157.5 190.5 123.4]
    		  [261.5 201.5 105.8]
    		  [256.5   6.5 174.8]
    		  [381.5 174.5  91. ]]]
    '''
    

    在这里插入图片描述
    从上图也能得出,此图的背景还算干净,若是参数稍微有所差异,检测效果天壤之别,我这里还只是调节了一个参数,最小半径与最大半径还是人为指定的。

    鸣谢
    https://blog.csdn.net/sunny2038/article/details/9253823
    https://blog.csdn.net/zx_good_night/article/details/88715206
    https://blog.csdn.net/jfztaq/article/details/83857062

    展开全文
  • 霍夫变换,是将坐标由直角坐标系变换到极坐标系,然后再根据数学表达式检测某些形状(如直线和圆)的方法。当 l1直线 上的某些点变换到极坐标系下时,表现为某些线(和前面点数量一致),这些线交于一...

    创作不易,如果对您有帮助,帮忙点赞哦!


    一. 霍夫变换理解:

        可参考:https://www.cnblogs.com/hellcat/p/9896426.html


    二. 霍夫变换简介:

        霍夫变换,是将坐标由直角坐标系变换到极坐标系,然后再根据数学表达式检测某些形状(如直线和圆)的方法。当 l1直线 上的某些点变换到极坐标系下时,表现为某些线(和前面点数量一致),这些线交于一点,通过该点的坐标就能表示原先的 l1直线。


    三. 霍夫变换用于检测图像直线算法实现:

        ① 提取图像边缘(可使用Canny算法等)[我也实现了它,前面Canny算法有问题可以参考我的另一篇文章:https://www.cnblogs.com/wojianxin/p/12533526.html]

        ② 实现二值图像霍夫变换

            1. 求出图像对角线长:r_max

            2. 在边缘点 (x,y) 处,t 取[0,180),步长设为1,根据下式进行霍夫变换

              

              霍夫变换,(r_ho,t) 表示极坐标,(x,y) 表示直角坐标 ↑

            3. 做一个大小为 r_max * 180 的表,变换后一个值落在表内某坐标,就将该坐标表内值 + 1,简言之,就是在进行投票,统计通过哪个点的直线的数量最多(即在原图像上越趋近于一条直线)。

        ③ 进行非极大值抑制(NMS)操作,使找出的直线落在不同的地点

            NMS 的算法如下:

            1. 遍历该表,如果遍历到的像素的投票数大于其8近邻的像素投票值,则它不变。

            2. 如果遍历到的像素的投票数小于其8近邻的像素投票值,则将其设置为0。

        ④ 找到20个投票数最多的点(即:直角坐标系下20条直线)准备进行输出

            1. np.ravel   将多维数组降为1维

            2. np.argsort   将数组元素从小到大排序,返回索引值

            3. [::-1]   数组反序 -> 得到从大到小索引值

            4. [:20]   前20个最大投票值的索引

            5. 根据索引得到坐标(r,t)

        ⑤ 霍夫反变换后,画出原图中的20条直线,输出图像

              

              霍夫逆变换公式 ↑


    四. 纯手工实现 ——> 利用霍夫变换检测图像中的直线

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # Canny算法:提取图像边缘
    def Canny(img):
    
        # Gray scale
        def BGR2GRAY(img):
            b = img[:, :, 0].copy()
            g = img[:, :, 1].copy()
            r = img[:, :, 2].copy()
    
            # Gray scale
            out = 0.2126 * r + 0.7152 * g + 0.0722 * b
            out = out.astype(np.uint8)
    
            return out
    
    
        # Gaussian filter for grayscale
        def gaussian_filter(img, K_size=3, sigma=1.3):
    
            if len(img.shape) == 3:
                H, W, C = img.shape
                gray = False
            else:
                img = np.expand_dims(img, axis=-1)
                H, W, C = img.shape
                gray = True
    
            ## Zero padding
            pad = K_size // 2
            out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float)
            out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
    
            ## prepare Kernel
            K = np.zeros((K_size, K_size), dtype=np.float)
            for x in range(-pad, -pad + K_size):
                for y in range(-pad, -pad + K_size):
                    K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma * sigma))
            #K /= (sigma * np.sqrt(2 * np.pi))
            K /= (2 * np.pi * sigma * sigma)
            K /= K.sum()
    
            tmp = out.copy()
    
            # filtering
            for y in range(H):
                for x in range(W):
                    for c in range(C):
                        out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x + K_size, c])
    
            out = np.clip(out, 0, 255)
            out = out[pad : pad + H, pad : pad + W]
            out = out.astype(np.uint8)
    
            if gray:
                out = out[..., 0]
    
            return out
    
    
        # sobel filter
        def sobel_filter(img, K_size=3):
            if len(img.shape) == 3:
                H, W, C = img.shape
            else:
                H, W = img.shape
    
            # Zero padding
            pad = K_size // 2
            out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
            out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
            tmp = out.copy()
    
            out_v = out.copy()
            out_h = out.copy()
    
            ## Sobel vertical
            Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]
            ## Sobel horizontal
            Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]
    
            # filtering
            for y in range(H):
                for x in range(W):
                    out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x + K_size]))
                    out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x + K_size]))
    
            out_v = np.clip(out_v, 0, 255)
            out_h = np.clip(out_h, 0, 255)
    
            out_v = out_v[pad : pad + H, pad : pad + W]
            out_v = out_v.astype(np.uint8)
            out_h = out_h[pad : pad + H, pad : pad + W]
            out_h = out_h.astype(np.uint8)
    
            return out_v, out_h
    
    
        def get_edge_angle(fx, fy):
            # get edge strength
            edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2))
            edge = np.clip(edge, 0, 255)
    
            fx = np.maximum(fx, 1e-10)
            #fx[np.abs(fx) <= 1e-5] = 1e-5
    
            # get edge angle
            angle = np.arctan(fy / fx)
    
            return edge, angle
    
    
        def angle_quantization(angle):
            angle = angle / np.pi * 180
            angle[angle < -22.5] = 180 + angle[angle < -22.5]
            _angle = np.zeros_like(angle, dtype=np.uint8)
            _angle[np.where(angle <= 22.5)] = 0
            _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45
            _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90
            _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135
    
            return _angle
    
    
        def non_maximum_suppression(angle, edge):
            H, W = angle.shape
            _edge = edge.copy()
            
            for y in range(H):
                for x in range(W):
                        if angle[y, x] == 0:
                                dx1, dy1, dx2, dy2 = -1, 0, 1, 0
                        elif angle[y, x] == 45:
                                dx1, dy1, dx2, dy2 = -1, 1, 1, -1
                        elif angle[y, x] == 90:
                                dx1, dy1, dx2, dy2 = 0, -1, 0, 1
                        elif angle[y, x] == 135:
                                dx1, dy1, dx2, dy2 = -1, -1, 1, 1
                        if x == 0:
                                dx1 = max(dx1, 0)
                                dx2 = max(dx2, 0)
                        if x == W-1:
                                dx1 = min(dx1, 0)
                                dx2 = min(dx2, 0)
                        if y == 0:
                                dy1 = max(dy1, 0)
                                dy2 = max(dy2, 0)
                        if y == H-1:
                                dy1 = min(dy1, 0)
                                dy2 = min(dy2, 0)
                        if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) != edge[y, x]:
                                _edge[y, x] = 0
    
            return _edge
    
        def hysterisis(edge, HT=100, LT=30):
            H, W = edge.shape
    
            # Histeresis threshold
            edge[edge >= HT] = 255
            edge[edge <= LT] = 0
    
            _edge = np.zeros((H + 2, W + 2), dtype=np.float32)
            _edge[1 : H + 1, 1 : W + 1] = edge
    
            ## 8 - Nearest neighbor
            nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32)
    
            for y in range(1, H+2):
                    for x in range(1, W+2):
                            if _edge[y, x] < LT or _edge[y, x] > HT:
                                    continue
                            if np.max(_edge[y-1:y+2, x-1:x+2] * nn) >= HT:
                                    _edge[y, x] = 255
                            else:
                                    _edge[y, x] = 0
    
            edge = _edge[1:H+1, 1:W+1]
                                    
            return edge
    
        # grayscale
        gray = BGR2GRAY(img)
    
        # gaussian filtering
        gaussian = gaussian_filter(gray, K_size=5, sigma=1.4)
    
        # sobel filtering
        fy, fx = sobel_filter(gaussian, K_size=3)
    
        # get edge strength, angle
        edge, angle = get_edge_angle(fx, fy)
    
        # angle quantization
        angle = angle_quantization(angle)
    
        # non maximum suppression
        edge = non_maximum_suppression(angle, edge)
    
        # hysterisis threshold
        out = hysterisis(edge, 100, 30)
    
        return out
    
    # 霍夫变换实现检测图像中的20条直线
    def Hough_Line(edge, img):
        ## Voting
        def voting(edge):
            H, W = edge.shape
            
            drho = 1
            dtheta = 1
    
            # get rho max length
            rho_max = np.ceil(np.sqrt(H ** 2 + W ** 2)).astype(np.int)
    
            # hough table
            hough = np.zeros((rho_max, 180), dtype=np.int)
    
            # get index of edge
            # ind[0] 是 符合条件的纵坐标,ind[1]是符合条件的横坐标
            ind = np.where(edge == 255)
    
            ## hough transformation
            # zip函数返回元组
            for y, x in zip(ind[0], ind[1]):
                    for theta in range(0, 180, dtheta):
                            # get polar coordinat4s
                            t = np.pi / 180 * theta
                            rho = int(x * np.cos(t) + y * np.sin(t))
    
                            # vote
                            hough[rho, theta] += 1
                                
            out = hough.astype(np.uint8)
    
            return out
    
        # non maximum suppression
        def non_maximum_suppression(hough):
            rho_max, _ = hough.shape
    
            ## non maximum suppression 
            for y in range(rho_max):
                for x in range(180):
                    # get 8 nearest neighbor
                    x1 = max(x-1, 0)
                    x2 = min(x+2, 180)
                    y1 = max(y-1, 0)
                    y2 = min(y+2, rho_max-1)
                    if np.max(hough[y1:y2, x1:x2]) == hough[y,x] and hough[y, x] != 0:
                        pass
                        #hough[y,x] = 255
                    else:
                        hough[y,x] = 0
    
            return hough
    
        def inverse_hough(hough, img):
            H, W, _= img.shape
            rho_max, _ = hough.shape
    
            out = img.copy()
    
            # get x, y index of hough table
            # np.ravel 将多维数组降为1维
            # argsort  将数组元素从小到大排序,返回索引
            # [::-1]   反序->从大到小
            # [:20]    前20个
            ind_x = np.argsort(hough.ravel())[::-1][:20]
            ind_y = ind_x.copy()
            thetas = ind_x % 180
            rhos = ind_y // 180
    
            # each theta and rho
            for theta, rho in zip(thetas, rhos):
                # theta[radian] -> angle[degree]
                t = np.pi / 180. * theta
    
                # hough -> (x,y)
                for x in range(W):
                    if np.sin(t) != 0:
                        y = - (np.cos(t) / np.sin(t)) * x + (rho) / np.sin(t)
                        y = int(y)
                        if y >= H or y < 0:
                            continue
                        out[y, x] = [0,255,255]
                for y in range(H):
                    if np.cos(t) != 0:
                        x = - (np.sin(t) / np.cos(t)) * y + (rho) / np.cos(t)
                        x = int(x)
                        if x >= W or x < 0:
                            continue
                        out[y, x] = [0,0,255]
                    
            out = out.astype(np.uint8)
    
            return out
    
    
        # voting
        hough = voting(edge)
    
        # non maximum suppression
        hough = non_maximum_suppression(hough)
    
        # inverse hough
        out = inverse_hough(hough, img)
    
        return out
    
    
    # Read image
    img = cv2.imread("../paojie.jpg").astype(np.float32)
    
    # Canny
    edge = Canny(img)
    
    # Hough
    out = Hough_Line(edge, img)
    
    out = out.astype(np.uint8)
    
    # Save result
    cv2.imwrite("out.jpg", out)
    cv2.imshow("result", out)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    五. 实验结果:

              

              原图 ↑

              

              霍夫变换检测到的直线 ↑


    六. 参考内容:

        ① https://www.cnblogs.com/wojianxin/p/12539886.html

        ② https://www.jianshu.com/p/64c8c696441a


    七. 版权声明:

        未经作者允许,请勿随意转载抄袭,抄袭情节严重者,作者将考虑追究其法律责任,创作不易,感谢您的理解和配合!

    展开全文
  • 1 原理2 检测步骤将参数空间(ρ,θ) 量化成m*n(m为ρ的等份数,n为θ的等份数)个单元,并设置累加器矩阵,初始值为0;对图像边界上的每一个点(x,y)带入ρ=xcosθ+ysinθ,求得每个θ对应的ρ值,并在ρ和θ所对应的...

    1 原理

    d3db5f520670c05ab75e868703a75f6a.png

    2 检测步骤

    将参数空间(ρ,θ) 量化成m*n(m为ρ的等份数,n为θ的等份数)个单元,并设置累加器矩阵,初始值为0;

    对图像边界上的每一个点(x,y)带入ρ=xcosθ+ysinθ,求得每个θ对应的ρ值,并在ρ和θ所对应的单元,将累加器加1,即:Q(i,j)=Q(i,j)+1;

    检验参数空间中每个累加器的值,累加器最大的单元所对应的ρ和θ即为直角坐标系中直线方程的参数。

    3 接口

    a8b92466e8c11ebfedf6c343a818f02f.png

    image:二值图像,canny边缘检测输出。这里是result。

    rho: 以像素为单位的距离精度,这里为1像素。如果想要检测的线段更多,可以设为0.1。

    theta: 以弧度为单位的角度精度,这里为numpy.pi/180。如果想要检测的线段更多,可以设为0.01 * numpy.pi/180。

    threshod: 阈值参数,int类型,超过设定阈值才被检测出线段,这里为10。

    minLineLength:线段以像素为单位的最小长度。

    maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔。

    4 代码及结果

    import os

    import numpy as np

    import cv2

    from PIL import Image, ImageEnhance

    import math

    def img_processing(img):

    # 灰度化

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)

    # canny边缘检测

    edges = cv2.Canny(binary, 50, 150, apertureSize=3)

    return edges

    def line_detect(img):

    img = Image.open(img)

    img = ImageEnhance.Contrast(img).enhance(3)

    # img.show()

    img = np.array(img)

    result = img_processing(img)

    # 霍夫线检测

    lines = cv2.HoughLinesP(result, 1, 1 * np.pi/180, 10, minLineLength=10, maxLineGap=5)

    # print(lines)

    print("Line Num : ", len(lines))

    # 画出检测的线段

    for line in lines:

    for x1, y1, x2, y2 in line:

    cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 1)

    pass

    img = Image.fromarray(img, 'RGB')

    img.show()

    if __name__ == "__main__":

    line_detect("1.jpg")

    pass

    原图如下:

    c23e112ce453bad39a72c967543cee41.png

    检测结果:

    a53b5869ca660394db833ee8f47d0876.png

    feda52b42d824a7c9ec406327ec50a0e.png

    您可能感兴趣的文章:python+opencv实现霍夫变换检测直线

    展开全文
  • Python OpenCV 365 天学习计划,与橡皮擦一起进入图像...霍夫直线变换函数原型在 OpenCV 中提供了两个霍夫直线检测的函数,一个是标准霍夫变换,另一个是概率霍夫变换。先学习一下标准霍夫变换吧,该变化方式也叫...

    Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 34 篇。

    基础知识铺垫

    上篇博客 咱们一起学习了霍夫直线检测的原理,本篇就从应用层对其进行学习啦。

    学习了原理之后,在查看函数原型,发现确实简单了许多。

    霍夫直线变换函数原型

    在 OpenCV 中提供了两个霍夫直线检测的函数,一个是标准霍夫变换,另一个是概率霍夫变换。

    先学习一下标准霍夫变换吧,该变化方式也叫做多尺度霍夫变换。

    该方法使用的函数是 cv2.HoughLines,函数原型如下

    lines = cv2.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])

    参数说明:

    image:输入 8 位灰度图像;

    rho:生成极坐标时像素扫描步长;

    theta:生成极坐标时候的角度步长;

    threshold:阈值;

    lines:返回值,极坐标表示的直线;

    sen:是否应用多尺度的霍夫变换,如果不是设置 0 表示经典霍夫变换;

    stn:是否应用多尺度的霍夫变换,如果不是设置 0 表示经典霍夫变换;

    min_theta:角度扫描范围最小值;

    max_theta:角度扫描范围最大值。

    该函数的返回值就是上篇博客提及的 ( θ,ρ ),其中 ρ 的单位是像素, θ 的单位是弧度。

    既然是直线检测,那我们先把图像的边缘检测出来,通过之前学习的知识即可,加入滑动条,方便调参。

    import cv2 as cv

    import numpy as np

    src = cv.imread("./331.jpg")

    gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

    def nothing():

    pass

    cv.namedWindow("bar")

    cv.createTrackbar("threshold1", "bar", 0, 255, nothing)

    cv.createTrackbar("threshold2", "bar", 0, 255, nothing)

    dst = cv.equalizeHist(gray_img)

    # 高斯滤波降噪

    gaussian = cv.GaussianBlur(dst, (9, 9), 0)

    cv.imshow("gaussian", gaussian)

    while True:

    threshold1 = cv.getTrackbarPos("threshold1", "bar")

    threshold2 = cv.getTrackbarPos("threshold2", "bar")

    # 边缘检测

    edges = cv.Canny(gaussian, threshold1, threshold2)

    cv.imshow("edges", edges)

    if cv.waitKey(1) & 0xFF == 27:

    break

    cv.destroyAllWindows()

    运行代码之后,获取到的边缘如下,下面就可以对直线进行获取了,通过cv2.HoughLines函数。

    8575bb126350f838a0c1029463c05790.png

    修改代码如下:

    import cv2 as cv

    import numpy as np

    src = cv.imread("./331.jpg")

    gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

    dst = cv.equalizeHist(gray_img)

    # 高斯滤波降噪

    gaussian = cv.GaussianBlur(dst, (9, 9), 0)

    # cv.imshow("gaussian", gaussian)

    # 边缘检测

    edges = cv.Canny(gaussian, 70, 150)

    cv.imshow("edges", edges)

    # Hough 直线检测

    # 重点注意第四个参数 阈值,只有累加后的值高于阈值时才被认为是一条直线,也可以把它看成能检测到的直线的最短长度(以像素点为单位)

    # 在霍夫空间理解为:至少有多少条正弦曲线交于一点才被认为是直线

    lines = cv.HoughLines(edges, 1.0, np.pi/180, 150)

    # 将检测到的直线通过极坐标的方式画出来

    print(lines.ndim)

    print(lines.shape)

    for line in lines:

    # line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的,theta是弧度

    rho, theta = line[0]

    # 下述代码为获取 (x0,y0) 具体值

    a = np.cos(theta)

    b = np.sin(theta)

    x0 = a*rho

    y0 = b*rho

    # 下图 1000 的目的是为了将线段延长

    # 以 (x0,y0) 为基础,进行延长

    x1 = int(x0+1000*(-b))

    y1 = int(y0+1000*a)

    x2 = int(x0-1000*(-b))

    y2 = int(y0-1000*a)

    cv.line(src, (x1, y1), (x2, y2), (0, 255, 0), 2)

    cv.imshow("src", src)

    cv.waitKey()

    cv.destroyAllWindows()

    上述代码中,相关说明如下:

    第二个参数为半径的步长,第三个参数为每次偏转的角度,即我们提及的 ρ 和 θ;

    rho 参数为极径 r, 以像素值为单位, 本案例使用 1 像素;

    theta参数极角 θ 以弧度为单位,本案例使用 1 度 (即 np.pi/180);

    threshold 参数随便给的。

    还有计算具体坐标用到的公式其实是这样的:(p,q) = (rcosθ,rsinθ)

    运行效果基本满意,达到预期。

    db8f0cfa136e545f0751127808075ded.png

    概率霍夫变换(Probabilistic Hough Transform)

    概率霍夫变换是一种概率直线检测,它是针对于上文标准霍夫检测的优化,核心点是采取概率挑选机制,选取一些点出来进行计算,相当于降采样。

    函数名称与原型如下:

    lines = cv2.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])

    参数说明:

    src:输入 8-bit 的灰度图像;

    rho:像素为单位的距离精度,double 类型的,推荐用 1.0;

    theta:以弧度为单位的角度精度,推荐用 numpy.pi/180;

    threshold:阈值;

    lines:输出的极坐标来表示直线;

    minLineLength:最短长度阈值,比这个长度短的线会被排除;

    maxLineGap:最大间隔,如果小于此值,这两条直线就被看成是一条直线。

    该函数还有一个优点,返回值是一条条线段,不用我们在进行转换了

    修改上文代码如下,参数赋值部分直接写的,你可以自由修改,也可以通过滑动条调参:

    import cv2 as cv

    import numpy as np

    src = cv.imread("./331.jpg")

    gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

    dst = cv.equalizeHist(gray_img)

    # 高斯滤波降噪

    gaussian = cv.GaussianBlur(dst, (9, 9), 0)

    # cv.imshow("gaussian", gaussian)

    # 边缘检测

    edges = cv.Canny(gaussian, 70, 150)

    cv.imshow("edges", edges)

    lines = cv.HoughLinesP(edges, 1.0, np.pi/180, 100,

    minLineLength=100, maxLineGap=6)

    print(type(lines))

    for line in lines:

    x1, y1, x2, y2 = line[0]

    cv.line(src, (x1, y1), (x2, y2), (0, 255, 0), 2)

    cv.imshow("src", src)

    cv.waitKey()

    cv.destroyAllWindows()

    运行代码之后,可以获取到相应的线段。

    6d952fc6959c819e76fa3651faf08849.png

    橡皮擦的小节

    OpenCV 的文章阶段性的难度开始爬升了,有些地方概念还是有点模糊,需要学习的地方太多了,一起加油吧

    希望今天的 1 个小时(今天内容有点多,不一定可以看完),你有所收获,我们下篇博客见~

    相关阅读

    技术专栏

    今天是持续写作的第 74 / 100 天。

    如果你有想要交流的想法、技术,欢迎在评论区留言。

    如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。

    博主 ID:梦想橡皮擦,希望大家点赞、评论、收藏。

    本文同步分享在 博客“梦想橡皮擦”(CSDN)。

    如有侵权,请联系 support@oschina.cn 删除。

    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    展开全文
  • OpenCV霍夫变换直线检测Python

    千次阅读 2020-06-01 00:53:08
    中的直线检测出来并用红线标记: import cv2 import numpy as np if __name__ == "__main__": original_img = cv2.imread("p.jpg") gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY) blur_img = cv2....
  • 一、原理(摘自《数字图像处理冈萨雷斯》) 即在xy平面上一条直线上的点在ab... lines: 储存着检测到的直线的参数对 (r,\theta) 的容器 rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素. theta: 参
  • 主要介绍了详解利用python+opencv识别图片中的圆形(霍夫变换),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 基于霍夫变换概率函数的直线检测方法,示例用于试卷区域检测
  • OpenCV+python霍夫变换直线检测

    千次阅读 2019-06-08 08:40:24
    最基本的霍夫变换是从黑白图像中检测直线(线段)。 霍夫变换最简单的是检测直线。,直线的方程表示可以由斜率和截距表示(这种表示方法,称为斜截式),如下所示: 但是这样会参数问题,垂直线的斜率不存在(或无限...
  • c++ 霍夫变换检测直线

    2021-05-22 16:43:29
    在下例中我们首先应用 Canny算子获取图像轮廓,然后基于霍夫变换检测直线这个函数的的表达直线的方式用的不习惯的话可以用下面这个。HoughLinesP函数的原型为:void HoughLinesP(InputArray image,...
  • 这篇文章介绍在Python中使用OpenCV的霍夫变换检测直线。提示:转载请详细注明原作者及出处,谢谢!本文介绍在OpenCV-Python中使用霍夫变换检测直线的方法。本文不介详细的理论知识,读者可从其他资料中获取相应的...
  • 直线可以表达成y=kx+by=kx+by=kx+b的形式,这是用斜率和截距表达出来的。也可以用“点法式”来表示,
  • 目录 0 原理 1 OpenCV中的霍夫变换 ...我们下面就看看如何使用霍夫变换检测直线。 首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条...
  • 霍夫变换是一种特征检测(feature extraction),被广泛应用在图像分析(image analysis)、计算机视觉(computer vision)以及数位影像处理(digital image processing)。霍夫变换是用来辨别找出物件中的特征,例如:...
  • 参考:https://blog.csdn.net/fengjiexyb/article/details/78075888霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。该过程在一个参数空间中通过计算累计结果...
  • 第三类 图像特征分析和检测课程项目的设计和实现.基于霍夫变换的图像直线检测——以车道线检测为例

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,338
精华内容 535
关键字:

python霍夫变换检测直线

python 订阅