Nick Rogers
Nick Rogers

Reputation: 328

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.

Upvotes: 2

Views: 5734

Answers (2)

fmw42
fmw42

Reputation: 53081

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

Upvotes: 3

fmw42
fmw42

Reputation: 53081

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

  • Read the input
  • Threshold on the orange color
  • Find the (largest) contour and get its bounding box
  • Draw a white filled contour on a black background as a mask
  • Create a copy of the input with an alpha channel
  • Copy the mask into the alpha channel
  • Crop the ROI
  • Save the results

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)

# put mask into alpha channel of input
new_image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
new_image[:,:,3] = mask[:,:,0]

# 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_image.jpg',new_image)
cv2.imwrite('frame50_roi.png',ROI)

# show images
cv2.imshow('thresh',thresh)
cv2.imshow('mask',mask)
cv2.imshow('new_image',new_image)
cv2.imshow('ROI',ROI)
cv2.waitKey(0)

Threshold image:

enter image description here

Mask Image:

enter image description here

New image with mask in alpha channel:

enter image description here

Cropped ROI

enter image description here

Upvotes: 1

Related Questions