Improving canny+contours image segmentation? [closed]

asked 2017-01-15 21:14:02 -0600

errcw gravatar image

updated 2017-01-18 22:56:04 -0600

I am working on an image segmentation project to identify playing cards on varying backgrounds. For my use case I care most that I accurately extract all the cards in the image; false-positives are inconvenient but not fatal. I also care that I can run the segmentation without user interaction (i.e., an algorithm like grabcut does not work for me).

So far I have the following simple algorithm based on Canny edge detection and contour selection:

# Pre-processing: Convert frame to standard size, 1024x768
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 9)
edges = cv2.Canny(gray, 10, 25)
_, contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
    rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rect)
    area = cv2.contourArea(box)
    ratio = area / size
    if ratio < 0.015:
        continue
    # Mark box as segment

For some (many) inputs it works well enough, or even very well: good example

However for other inputs the contour detection does not successfully disambiguate cards. The contours are grouped in ways that link cards or result in undesirable bounding boxes: bad example

I've tried various improvements such as altering all the parameters, adding a thresholding step, etc. but without much success. I would love to hear your suggestions on what else I should try!

For reference (or, better yet, if you want to play along at home) I have a set of example inputs and the outputs of my current algorithm: https://goo.gl/photos/HCYeiFqhG5pVNWnY9

EDIT:

For the record, a few more things I've tried:

  • Using a bilateral filter instead of median blur. Might have improved results a bit, e.g., in the bad example above the first column third row card is now correctly segmented.
  • Using histogram equalization to enhance contrast. Generally obscured some necessary edges.
  • Attempting to automatically determine Canny thresholds for each image based on the median. Generally this approach missed many of the noisy card edges that we need to consider.
def auto_canny(image, sigma=0.5):
    v = np.median(image)
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    return cv2.Canny(image, lower, upper)
  • Attempting to use watershed segmentation. The fundamental problem is automatically determining the initial segmentation markers, which turns out to be roughly equivalent to identifying the cards in the first place. Finding "white" is particularly hard because its color varies with the ambient lighting conditions.
edit retag flag offensive reopen merge delete

Closed for the following reason question is not relevant or outdated by sturkmen
close date 2020-10-06 04:47:48.826851

Comments

Using Canny and then a distance transform will give you all centers as peaks, then you simply have your seed points and you can combine watershed with canny edges.

StevenPuttemans gravatar imageStevenPuttemans ( 2017-01-19 09:54:09 -0600 )edit