Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing long horizontal/vertical lines from edge image using OpenCV

How can I use standard image processing filters (from OpenCV) to remove long horizontal and vertical lines from an image?

The images are B&W so removing means simply painting black.

Illustration:

illustration of required filter

I'm currently doing it in Python, iterating over pixel rows and cols and detecting ranges of consecutive pixels, removing those that are longer than N pixels. However, it's really slow in comparison to the OpenCV library, and if there's a way of accomplishing the same with OpenCV functions, that'll likely be orders of magnitude faster.

I imagine this can be done by convolution using a kernel that's a row of pixels (for horizontal lines), but I'm having a hard time figuring the exact operation that would do the trick.

like image 873
Assaf Lavie Avatar asked Sep 30 '13 12:09

Assaf Lavie


3 Answers

To remove Horizontal Lines from an image you can use an edge detection algorithm to detect edges and then use Hough's Transform in OpenCV to detect lines and color them white:

import cv2
import numpy as np
img = cv2.imread(img,0)
laplacian = cv2.Laplacian(img,cv2.CV_8UC1) # Laplacian Edge Detection
minLineLength = 900
maxLineGap = 100
lines = cv2.HoughLinesP(laplacian,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(img,(x1,y1),(x2,y2),(255,255,255),1)
cv2.imwrite('Written_Back_Results.jpg',img)
like image 192
Anindita Bhowmik Avatar answered Sep 23 '22 09:09

Anindita Bhowmik


if your lines are truly horizontal/vertical, try this

import cv2
import numpy as np
img = cv2.imread('c:/data/test.png')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
linek = np.zeros((11,11),dtype=np.uint8)
linek[5,...]=1
x=cv2.morphologyEx(gray, cv2.MORPH_OPEN, linek ,iterations=1)
gray-=x
cv2.imshow('gray',gray)
cv2.waitKey(0)    

result

enter image description here

You can refer OpenCV Morphological Transformations documentation for more details.

like image 24
Zaw Lin Avatar answered Sep 22 '22 09:09

Zaw Lin


How long is "long". Long, as in, lines that run the length of the entire image, or just longer than n pixels?

If the latter, then you could just use an n+1 X n+1 median or mode filter, and set the corner coefficients to zero, and you'd get the desired effect.

If you're referring to just lines that run the width of the entire image, just use the memcmp() function against a row of data, and compare it to a pre-allocated array of zeros which is the same length as a row. If they are the same, you know you have a blank line that spans the horizontal length of the image, and that row can be deleted.

This will be MUCH faster than the element-wise comparisons you are currently using, and is very well explained here:

Why is memcpy() and memmove() faster than pointer increments?

If you want to repeat the same operation for vertical lines, just transpose the image, and repeat the operation.

I know this is more of a system-optimization level approach than an openCV filter like you requested, but it gets the job done fast and safely. You can speed up the calculation even more if you manage to force the image and your empty array to be 32-bit aligned in memory.

like image 34
Cloud Avatar answered Sep 25 '22 09:09

Cloud