Reputation: 28064
I'm trying out OpenCV to do some image processing. Admittedly I'm a noob at this stuff, but I feel like I'm wrapping my brain around it somewhat. I'm using a mask to detect the lighter areas of the image, then running a canny detector and finally a HoughLinesP detection. Code is below. The result I'm getting is:
What I expected (and desire) is more like below (notice red lines on result):
For what it's worth, my end game is to auto rotate the image so the receipt is straight. If I'm taking the wrong path entirely, advise would be appreciated.
import cv2
import numpy as np
from matplotlib import pyplot
def detect_lines(img):
temp = cv2.cvtColor(img,cv2.COLOR_BGR2HLS)
lower = np.uint8([0, 160, 0])
upper = np.uint8([255, 255, 255])
white_mask = cv2.inRange(temp, lower, upper)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.blur(gray, (3, 3))
canny_low = 100
edges = cv2.Canny(white_mask, canny_low, canny_low * 3, apertureSize=5)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 10, 2, 80)
result = img.copy()
if lines is not None:
for x in range(0, len(lines)):
for x1, y1, x2, y2 in lines[x]:
print(x1, y1, x2, y2)
cv2.line(result, (x1, y1), (x2, y2), (255, 0, 0), 2)
pyplot.subplot(141), pyplot.imshow(img, cmap='gray')
pyplot.title('Original Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(142), pyplot.imshow(white_mask, cmap='gray')
pyplot.title('Gray Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(143), pyplot.imshow(edges, cmap='gray')
pyplot.title('Edge Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(144), pyplot.imshow(result, cmap='gray')
pyplot.title('Result Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.show()
return img
if __name__ == '__main__':
image = cv2.imread('receipt.jpg')
image = detect_lines(image)
cv2.imwrite('output.jpg', image)
Upvotes: 0
Views: 1442
Reputation: 558
I would suggest start looking at different Morphological Transformations which you can apply to your canny edge detection in order to improve the hough line transform.
This is not perfect but it's something to get you started:
import cv2
import numpy as np
from matplotlib import pyplot
def detect_lines(img):
temp = cv2.cvtColor(img,cv2.COLOR_BGR2HLS)
kernel = np.ones((5, 5), np.uint8) # < --- Added a kernel you can differ
lower = np.uint8([0, 160, 0])
upper = np.uint8([255, 255, 255])
white_mask = cv2.inRange(temp, lower, upper)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.blur(gray, (3, 3))
canny_low = 100
edges = cv2.Canny(white_mask, canny_low, canny_low * 3, apertureSize=3)
dilate = cv2.dilate(edges, kernel, iterations=2) # < --- Added a dilate, check link I provided
ero = cv2.erode(dilate, kernel, iterations=1) # < --- Added an erosion, check link I provided
lines = cv2.HoughLinesP(dilate, 1, np.pi/180, 10, 2, 80)
result = img.copy()
if lines is not None:
for x in range(0, len(lines)):
for x1, y1, x2, y2 in lines[x]:
print(x1, y1, x2, y2)
cv2.line(result, (x1, y1), (x2, y2), (255, 0, 0), 2)
pyplot.subplot(151), pyplot.imshow(img, cmap='gray')
pyplot.title('Original Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(152), pyplot.imshow(white_mask, cmap='gray')
pyplot.title('Mask Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(153), pyplot.imshow(edges, cmap='gray')
pyplot.title('Edge Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.subplot(154), pyplot.imshow(ero, cmap='gray')
pyplot.title('Dilate/Erosion Image'), pyplot.xticks([]), pyplot.yticks([]) # <--- Added a display
pyplot.subplot(155), pyplot.imshow(result, cmap='gray')
pyplot.title('Result Image'), pyplot.xticks([]), pyplot.yticks([])
pyplot.show()
return result # <--- You want to return the result right?
if __name__ == '__main__':
image = cv2.imread('receipt.jpg')
image = detect_lines(image)
cv2.imwrite('output.jpg', image)
Another approach could be looking into Corner Detection and then drawing a line between the detected corners (I haven't tried this approach but it's just for inspiration :) ).
Upvotes: 2