pythonopencvimage-processingstraight-line-detection

How can I combine HoughlinesP coordinates for a single line in OpenCV Python?


I'm using cv2.HoughlinesP to find the coordinates for each line in a document image. I'm getting 2 line coordinates when using HoughlinesP when there is only 1 line. How can I combine these two coordinates or treat them as if they are for the same line instead of treating them as separate lines.

The issue, is that because I'm using coordinates obtained from houghlinesp, the lines are treated as if they are rectangular boxes, as in the top endpoints of the line are separate, and the bottom endpoints of the line are treated separately. Therefore 2 separate line coordinates are given instead of coordinates for 1 line.

So in this case where this form has 7 lines being detected, when I use HoughlinesP to get these line coordinates, I get 14 different coordinates.

enter image description here

import cv2
from google.colab.patches import cv2_imshow
import numpy as np
#image preprocessing

img= cv2.imread('page9.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#First, get the gray image and process GaussianBlur.
kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

#Second, process edge detection use Canny.
low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

#Then, use HoughLinesP to get the lines.
rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 25  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 150  # minimum number of pixels making up a line
max_line_gap = 3  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

print(len(lines))

and this is the output I get showing the number of line coordinates : 13


Solution

  • A simple solution is reducing the resolution of Hough Grid by increasing the value of rho, and decreasing the value of theta.

    Updated code sample:

    import cv2
    #from google.colab.patches import cv2_imshow
    import numpy as np
    #image preprocessing
    
    img= cv2.imread('page9.png')
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    #First, get the gray image and process GaussianBlur.
    kernel_size = 5
    blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
    
    #Second, process edge detection use Canny.
    low_threshold = 50
    high_threshold = 150
    edges = cv2.Canny(blur_gray, low_threshold, high_threshold)
    
    #Then, use HoughLinesP to get the lines.
    rho = 7 #1  # distance resolution in pixels of the Hough grid
    theta = np.pi / 30 # np.pi / 180  # angular resolution in radians of the Hough grid
    threshold = 25  # minimum number of votes (intersections in Hough grid cell)
    min_line_length = 150  # minimum number of pixels making up a line
    max_line_gap = 3  # maximum gap in pixels between connectable line segments
    line_image = np.copy(img) * 0  # creating a blank to draw lines on
    
    # Run Hough on edge detected image
    # Output "lines" is an array containing endpoints of detected line segments
    lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                        min_line_length, max_line_gap)
    
    print(len(lines))  # 7
    
    # Draw lines with red color for testing
    if lines is not None:
        for i in range(len(lines)):
            l = lines[i][0]
            cv2.line(line_image, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 1, cv2.LINE_AA)
    
    cv2.imwrite('line_image.png', line_image)  # Save line_image testing
    

    line_image.png:
    enter image description here


    Note:
    In general, using HoughLines is not a robust solution.
    Instead of using GaussianBlur, Canny and HoughLinesP, a more robust solution may be using threshold, findContours and then analysing the contours shapes for finding horizontal lines.

    Example:

    import cv2
    import numpy as np
    
    img= cv2.imread('page9.png')
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    # Apply binary thresholding with automatic threshold (with polarity inversion).
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
    
    line_image = np.copy(img) * 0  # creating a blank to draw lines on
    lines_counter = 0
    for c in contours:
        x, y, w, h = cv2.boundingRect(c)
        if w >= 100 and h <= 5:
            cv2.drawContours(line_image, [c], 0, (0, 0, 255), cv2.FILLED)
            lines_counter += 1
    
    cv2.imwrite('line_image.png', line_image)  # Save line_image testing