Search code examples
pythonimageopencvmaskcontour

How to fill a contour plot mask and retain data inside the contour and blackout the rest


I have an image like so, enter image description here

By using the following code,

import numpy as np
import cv2

# load the image
image = cv2.imread("frame50.jpg", 1)

#color boundaries [B, G, R]
lower = [0, 3, 30]
upper = [30, 117, 253]

# create NumPy arrays from the boundaries
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")

# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)

ret,thresh = cv2.threshold(mask, 50, 255, 0)
if (int(cv2.__version__[0]) > 3):
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
else:
    im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

if len(contours) != 0:
    # find the biggest countour (c) by the area
    c = max(contours, key = cv2.contourArea)
    x,y,w,h = cv2.boundingRect(c)
    ROI = image[y:y+h, x:x+w]
cv2.imshow('ROI',ROI)
cv2.imwrite('ROI.png',ROI)

cv2.waitKey(0)

I get the following cropped image,

enter image description here

I would like to retain all data within the orange hand-drawn boundary including the boundary itself and blackout the rest. In the image above the data outside the orange boundary is still there. I do not want that. How do i fill the mask which is like this now so that i can retain data inside the orange boundary,

enter image description here

I would still like to retain other properties like the rectangular bounding box. I don't want anything else to change. How do I go about this? Thanks.


Solution

  • As you desire (in your comments to my previous answer) to have the outer region to be black rather than transparent, you can do that as follows in Python/OpenCV with a couple of lines changed to multiply the mask by the input rather than put the mask into the alpha channel.

    Input:

    enter image description here

    import numpy as np
    import cv2
    
    # load the image
    image = cv2.imread("frame50.jpg")
    
    #color boundaries [B, G, R]
    lower = (0, 70, 210)
    upper = (50, 130, 255)
    
    # threshold on orange color
    thresh = cv2.inRange(image, lower, upper)
    
    # get largest contour
    contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    big_contour = max(contours, key=cv2.contourArea)
    x,y,w,h = cv2.boundingRect(big_contour)
    
    # draw filled contour on black background
    mask = np.zeros_like(image)
    cv2.drawContours(mask, [big_contour], 0, (255,255,255), -1)
    
    # apply mask to input image
    new_image = cv2.bitwise_and(image, mask)
    
    # crop 
    ROI = new_image[y:y+h, x:x+w]
       
    # save result 
    cv2.imwrite('frame50_thresh.jpg',thresh)
    cv2.imwrite('frame50_mask.jpg',mask)
    cv2.imwrite('frame50_new_image2.jpg',new_image)
    cv2.imwrite('frame50_roi2.jpg',ROI)
    
    # show images
    cv2.imshow('thresh',thresh)
    cv2.imshow('mask',mask)
    cv2.imshow('new_image',new_image)
    cv2.imshow('ROI',ROI)
    cv2.waitKey(0)
    

    new_image after applying the mask:

    enter image description here

    cropped roi image:

    enter image description here