7

I am trying to remove a greyish background from a photo and replace it with a white one

so far I have this code:

image = cv2.imread(args["image"])
r = 150.0 / image.shape[1]
dim = (150, int(image.shape[0] * r))
resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
lower_white = np.array([220, 220, 220], dtype=np.uint8)
upper_white = np.array([255, 255, 255], dtype=np.uint8)
mask = cv2.inRange(resized, lower_white, upper_white) # could also use threshold
res = cv2.bitwise_not(resized, resized, mask)
cv2.imshow('res', res) # gives black background

The problem is that the image now has a black background as I have masked out the grey. How can I replace the empty pixels with white ones?

Before After

5 Answers5

9

You can use the mask to index the array, and assign just the white parts of the mask to white:

coloured = resized.copy()
coloured[mask == 255] = (255, 255, 255)

Screenshot

Alex L
  • 8,748
  • 5
  • 49
  • 75
7

I really recommend you to stick with OpenCV, it is well optimized. The trick is to invert the mask and apply it to some background, you will have your masked image and a masked background, then you combine both. image1 is your image masked with the original mask, image2 is the background image masked with the inverted mask, and image3 is the combined image. Important. image1, image2 and image3 must be of the same size and type. The mask must be grayscale.

foreground and background masked then combined

import cv2
import numpy as np

# opencv loads the image in BGR, convert it to RGB
img = cv2.cvtColor(cv2.imread('E:\\FOTOS\\opencv\\zAJLd.jpg'),
                   cv2.COLOR_BGR2RGB)
lower_white = np.array([220, 220, 220], dtype=np.uint8)
upper_white = np.array([255, 255, 255], dtype=np.uint8)
mask = cv2.inRange(img, lower_white, upper_white)  # could also use threshold
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))  # "erase" the small white points in the resulting mask
mask = cv2.bitwise_not(mask)  # invert mask

# load background (could be an image too)
bk = np.full(img.shape, 255, dtype=np.uint8)  # white bk

# get masked foreground
fg_masked = cv2.bitwise_and(img, img, mask=mask)

# get masked background, mask must be inverted 
mask = cv2.bitwise_not(mask)
bk_masked = cv2.bitwise_and(bk, bk, mask=mask)

# combine masked foreground and masked background 
final = cv2.bitwise_or(fg_masked, bk_masked)
mask = cv2.bitwise_not(mask)  # revert mask to original
lmiguelmh
  • 3,074
  • 1
  • 37
  • 53
2

At first, you need to get the background. To this must be subtracted from the original image with the mask image. And then change the black background to white (or any color). And then back to add with the image of the mask. Look here OpenCV grabcut() background color and Contour in Python

Community
  • 1
  • 1
Tombery
  • 351
  • 4
  • 11
1

First convert to GRAY and then threshold with cv2.threshold and then use numpy masking...

ret, thresh = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 220, 255, cv2.THRESH_BINARY)
img[thresh == 255] = 255

If need black background set RHS to zero instead of 255

barnwaldo
  • 386
  • 3
  • 8
0

Instead of using bitwise_not, I would use

resized.setTo([255, 255, 255], mask)

Before doing that I'd also erode and dilate and the mask, to get rid of the specs in the mask that are part of the image you want to keep. http://docs.opencv.org/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html

J-J21
  • 11
  • 3
  • 2
    I'm getting the error AttributeError: 'numpy.ndarray' object has no attribute 'setTo'. Does this mean anything to you? thanks –  Apr 22 '15 at 23:30