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.
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
A simple solution is reducing the resolution of Hough Grid by increasing the value of rho
, and decreasing the value of theta
.
rho
applies less dense gird, so a thick line is not going to be found as two close lines.theta
reduces the number of resolution of angles to search, so a thick line is not going to be interpreted as a small angled line (from top left corner to the bottom right corner).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
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