Rafay Khan
Rafay Khan

Reputation: 1240

Expanding background color to connected components(Flood fill) - Image Processing

kinda stuck trying to figure out how I can expand the background color inwards. I have this image that has been generated through a mask after noisy background subtraction.

enter image description here

I am trying to make it into this:

enter image description here

So far I have tried to this, but to no avail:

import cv2
from PIL import Image
import numpy as np

Image.open("example_of_misaligned_frame.png") # open poor frame

img_copy = np.asanyarray(img).copy()

contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX) # find contours 

# create bounding box around blob and figure out the row.cols to iterate over
x,y,w,h = cv2.boundingRect(max(contours, key = cv2.contourArea)) 

# flood fill the entire region with back, hoping that off-white region gets filled due to connected components.
for row in range(y, y+h):
    for col in range(x, x+w):
        cv2.floodFill(img_copy, None, seedPoint=(col,row), newVal=0)

This results in a completely black image :(

Any help, pointing me in the right direction, is greatly appreciated.

Upvotes: 1

Views: 1179

Answers (2)

fmw42
fmw42

Reputation: 53081

In Python/OpenCV, you can simply extract a binary mask from your flood filled process image and erode that mask. Then reapply it to the input or to your flood filled result.

Input:

enter image description here

import cv2

# read image
img = cv2.imread("masked_image.png")

# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# make anything not black into white
gray[gray!=0] = 255

# erode mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (51,51))
mask = cv2.morphologyEx(gray, cv2.MORPH_ERODE, kernel)

# make mask into 3 channels
mask = cv2.merge([mask,mask,mask])

# apply new mask to img
result = img.copy()
result = cv2.bitwise_and(img, mask)

# write result to disk
cv2.imwrite("masked_image_original_mask.png", gray)
cv2.imwrite("masked_image_eroded_mask.png", mask)
cv2.imwrite("masked_image_eroded_image.png", result)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)


Mask:

enter image description here

Eroded Mask:

enter image description here

Result:

enter image description here

Adjust the size of the circular (elliptical) morphology kernel as desired for more or less erosion.

Upvotes: 1

Rotem
Rotem

Reputation: 32084

You can solve it by using floodFill twice:

  • First time - fill the black pixels with Off-White color.
  • Second time - fill the Off-White pixels with black color.

There is still an issue for finding the RGB values of the Off-White color.
I found an improvised solution for finding the Off-White color (I don't know the exact rules for what color is considered to be background).

Here is a working code sample:

import cv2
import numpy as np

#Image.open("example_of_misaligned_frame.png") # open poor frame
img = cv2.imread("example_of_misaligned_frame.png")

#img_copy = np.asanyarray(img).copy()
img_copy = img.copy()

#Improvised way to find the Off White color (it's working because the Off White has the maximum color components values).
tmp = cv2.dilate(img, np.ones((50,50), np.uint8), iterations=10)

# Color of Off-White pixel
offwhite = tmp[0, 0, :]

# Convert to tuple
offwhite = tuple((int(offwhite[0]), int(offwhite[1]), int(offwhite[2])))

# Fill black pixels with off-white color
cv2.floodFill(img_copy, None, seedPoint=(0,0), newVal=offwhite)

# Fill off-white pixels with black color
cv2.floodFill(img_copy, None, seedPoint=(0,0), newVal=0, loDiff=(2, 2, 2, 2), upDiff=(2, 2, 2, 2))

cv2.imshow("img_copy", img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result of cv2.dilate:
enter image description here

Result of first cv2.floodFill:
enter image description here

Result of second cv2.floodFill:
enter image description here

Upvotes: 2

Related Questions