Search code examples
pythonnumpyopencvcontouredge-detection

shrink and enlarge contour image with Python OpenCV


I have an image with an object like below:

enter image description here

I can detect the contour and get a mask with only ball region, but my ROI is the edge region, that means I need a bigger and a smaller mask which combine to get this:

enter image description here

so my question is: how can I shrink/enlarge the mask of contour around contour's center?


Solution

  • Here is one way to do that in Python/OpenCV.

     - Read the input
     - Convert to grayscale
     - Threshold
     - Use morphology close and open to clean up noise and small regions to form a mask
     - Dilate the mask
     - Erode the mask
     - Merge the input and dilated mask
     - Merge the eroded mask with the previous result
     - Save the result
    

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # read image
    img = cv2.imread("basketball.png")
    
    # convert img to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # make anything not white into black
    mask = gray.copy()
    mask[mask!=255] = 0
    
    # invert mask so center is white and outside is black
    mask = 255 - mask
    
    # close open mask to clean up small regions and make 3 channels
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
    
    # erode mask
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (51,51))
    erode = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
    
    # dilate mask and make 3 channels
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (51,51))
    dilate = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)
    
    # merge image onto dilated mask using mask
    result = np.where(mask==(255,255,255), img, dilate)
    
    # merge inverted erode onto result using erode
    result = np.where(erode==(255,255,255), (255-erode), result)
    
    # write result to disk
    cv2.imwrite("basketball_mask.png", mask)
    cv2.imwrite("basketball_eroded_mask.png", erode)
    cv2.imwrite("basketball_dilate_mask.png", dilate)
    cv2.imwrite("basketball_dilate_mask.png", dilate)
    cv2.imwrite("basketball_result.png", result)
    
    # display it
    cv2.imshow("image", img)
    cv2.imshow("mask", mask)
    cv2.imshow("erode", erode)
    cv2.imshow("dilate", dilate)
    cv2.imshow("result", result)
    cv2.waitKey(0)
    

    Mask:

    enter image description here

    Erode mask:

    enter image description here

    Dilate mask:

    enter image description here

    Result:

    enter image description here

    Note: If you dilate too much, you reach the edges of the image and then the shape changes. To avoid that, pad the input with background color enough to contain the dilated size.