Minimum bounding box or convex body for any quad shape?
Note. ... I am trying to create a process that can work with a general range of images, orientations and qualities, not just this image.
I understand that you can use a convex hull to enclose many points with a polygon, and you can use one of several algorithms to create a minimum bounding rectangle for those points. However, I want to do what looks like a minimum bounding box, but without bounding it to a rectangle.
Let's say I have this receipt:
Convex hull:
Minimum bounding rectangle (rotating caliper):
My target: (ms-paint):
As you can see, the minimum bounding box doesn't quite fit, since the receipt is a trapezoid from the point of view. It only gets worse, the lower the perspective. I want to have 4 points and sharp corners, so I cannot use a convex hull.
Is there an algorithm I can use to get something similar to a convex hull or minimum bounding box, but limited to four points and any four-sided shape?
source to share
With some confusion with color filtering and morphological operations, I was able to use the Harris detector with success. You can also expand on this using intersection points like here from Hough Lines, which can be helpful, albeit a little verbose. This works well for this particular image, but it requires a lot of parameters for the pipeline (open and close kernel sizes, iterations).
My implementation is in Python, but this of course could work in C ++ or Java too:
import numpy as np
import cv2
# read image
img = cv2.imread('receipt.png')
# thresholding
blur = cv2.GaussianBlur(img, (5,5), 1)
hls = cv2.cvtColor(blur, cv2.COLOR_BGR2HLS)
low = np.array([0, 70, 0])
high = np.array([255, 255, 85])
thresh = cv2.inRange(hls, low, high)
# morphological operations to get the paper
kclose = np.ones((3,3), dtype=np.uint8)
kopen = np.ones((5,5), dtype=np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kclose, iterations=2)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kopen, iterations=6)
# corner detection
opening = cv2.GaussianBlur(opening, (3,3), 1)
opening = np.float32(opening)
dst = cv2.cornerHarris(opening, 2, 3, 0.04)
# drawing corners
dst = cv2.dilate(dst, None)
img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('Corners', img)
cv2.waitKey(0)
And here are the corners:
Note that you are getting a few pixels from Harris, so you will have to do clustering to get special corner points if you want to use them to deform later.
I applied masks from filtering colors, closing and opening on the image so that you can see the masks after these operations.
Filtration:
Closing:
Opening:
source to share