Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Horizontal Line detection with OpenCV

I am trying to find horizontal and vertical lines from an image which came from a "document". The documents are scanned pages from contracts and so the lines look like what you would see in a table or in a contract block.

I have been trying OpenCV for the job. The Hough transform implementation in OpenCV seemed useful for the job, but I could not find any combination of parameters that would allow it to cleanly find the vertical and horizontal lines. I tried with and without edge detection. No luck. If anyone has done anything similar I'm interested in knowing how.

See here an image of my before and after experimentation with HoughP in OpenCV. It's the best I could do, http://dl.dropbox.com/u/3787481/Untitled%201.png

So now I'm wondering whether there is another kind of transform I could use which would allow me to reliably find horizontal and vertical lines (and preferably dashed lines too).

I know this problem is solvable because I have Nuance and ABBYY OCR tools which can both reliably extract horizontal and vertical lines and return me the bounding box of the lines.

Thanks! Patrick.

like image 672
Patrick Collins Avatar asked Aug 29 '11 07:08

Patrick Collins


People also ask

What algorithm is used to detect lines in OpenCV?

The Hough Transform is a method that is used in image processing to detect any shape, if that shape can be represented in mathematical form. It can detect the shape even if it is broken or distorted a little bit.

How do you find a horizontal line?

The equation of a horizontal line passing through a point (a, b) is y = b, where 'b' is constant because in the equation y = mx + b, where 'b' is the y-intercept, there is no change in the value of y on the horizontal line and the slope is zero, therefore, the equation of a horizontal line is y = b.

What is getStructuringElement in OpenCV?

getStructuringElement(). You just pass the shape and size of the kernel, you get the desired kernel. We use the function: cv.getStructuringElement (shape, ksize, anchor = new cv.Point(-1, -1)) Parameters. shape. element shape that could be one of cv.MorphShapes.


2 Answers

Have you seen a code sample from HoughLinesP function documentation?

I think you can use it as starting point for your algorithm. To pick horizontal an vertical lines you just need to filter out other lines by line angle.

UPDATE:

As I see you need to find not the lines but horizontal an vertical edges on the page. For this task you need to combine several processing steps to get good results.

For your image I'm able to get good results by combining Canny edge detection with HoughLinesP. Here is my code (I've used python, but I think you see the idea):

img = cv2.imread("C:/temp/1.png") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 80, 120) lines = cv2.HoughLinesP(edges, 1, math.pi/2, 2, None, 30, 1); for line in lines[0]:     pt1 = (line[0],line[1])     pt2 = (line[2],line[3])     cv2.line(img, pt1, pt2, (0,0,255), 3) cv2.imwrite("C:/temp/2.png", img) 

Result looks like:

like image 138
Andrey Kamaev Avatar answered Oct 09 '22 11:10

Andrey Kamaev


Here's a complete OpenCV solution using morphological operations.

  • Obtain binary image
  • Create horizontal kernel and detect horizontal lines
  • Create vertical kernel and detect vertical lines

Here's a visualization of the process. Using this input image:

Binary image

import cv2  # Load image, convert to grayscale, Otsu's threshold image = cv2.imread('1.png') result = image.copy() gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] 

Detected horizontal lines highlighted in green

# Detect horizontal lines horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1)) detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2) cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] for c in cnts:     cv2.drawContours(result, [c], -1, (36,255,12), 2) 

Detected vertical lines highlighted in green

# Detect vertical lines vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10)) detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2) cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] for c in cnts:     cv2.drawContours(result, [c], -1, (36,255,12), 2) 

Result

Here's the output using another input image

Input -> Binary -> Detected Horizontal -> Detected Vertical -> Result


Note: Depending on the image, you may have to modify the kernel size. For instance to capture longer horizontal lines, it may be necessary to increase the horizontal kernel from (40, 1) to say (80, 1). If you wanted to detect thicker horizontal lines, then you could increase the width of the kernel to say (80, 2). In addition, you could increase the number of iterations when performing cv2.morphologyEx(). Similarly, you could modify the vertical kernels to detect more or less vertical lines. There is a trade-off when increasing or decreasing the kernel size as you may capture more or less of the lines. Again, it all varies depending on the input image

Full code for completeness

import cv2  # Load image, convert to grayscale, Otsu's threshold image = cv2.imread('1.png') result = image.copy() gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]  # Detect horizontal lines horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1)) detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2) cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] for c in cnts:     cv2.drawContours(result, [c], -1, (36,255,12), 2)  # Detect vertical lines vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10)) detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2) cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = cnts[0] if len(cnts) == 2 else cnts[1] for c in cnts:     cv2.drawContours(result, [c], -1, (36,255,12), 2)  cv2.imshow('result', result) cv2.waitKey() 
like image 32
nathancy Avatar answered Oct 09 '22 12:10

nathancy