0

This is a follow on to a previously posted question about removing black backgrounds from .png images (Remove Background from Image - Python). The script I used in that Q&A works well but I still get minor inconsistencies in removing the black background exclusively while preserving everything inside the image contours. I tried a different script using the OpenCV grabcut algorithm, but I still get the same issue. I want to be able to remove the black background from the images without removing pixels inside image contours, here is the script I am using along with example input/output images upon running the script.

import numpy as np
import cv2
img = cv2.imread("C:\\users\\mdl518\\Desktop\\overhead_image.png") # Input 8-bit 3-channel image
      
mask = np.zeros(img.shape[:2], np.uint8) # img.shape[:2] = first, second values of img.shape
bgdModel = np.zeros((1,65), np.float64) # temporary array for background
fgdModel = np.zeros((1,65), np.float64) # temporary array for foreground
rect = (1,0,img.shape[1],img.shape[0]) # specify w,h beyond bounds of img.shape

# Grabcut algorithm to extract foreground (5 iterations) within region of interest
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

# Split RGB channels and create new alpha channel
r_channel, g_channel, b_channel = cv2.split(img) 
a_channel = np.where((mask==2)|(mask==0), 0, 255).astype('uint8')  
        
img_RGBA = cv2.merge((r_channel, g_channel, b_channel, a_channel)) # add alpha channel to image
cv2.imwrite(".//output_image.png", img_RGBA)  ## write new image to file

Input Image:

enter image description here

Output Image (using script above): enter image description here Output Image (using "contour" method):

enter image description here

I am attempting to tweak my 'rect' bounds and the mask settings between 0 and 255, but I still cannot rectify this issue - The current settings seem to preserve the image the most without removing additional pixels, but it still does not preserve the whole image. Any assistance is most appreciated!

mdl518
  • 327
  • 3
  • 14
  • Something about the last edit broke the input image, can you fix that? I got my result by downloading the input image you provided and running on that. The mask I use for contouring is expecting exactly black for the background. That blockiness looks like it came about from some kind of compression or maybe anti-aliasing causing the pixels around the image to not be exactly black. Try downloading your input image and running on that and see if you get a different result. – Ian Chu Mar 07 '21 at 12:56
  • Thanks for the catch on the input image issue, I fixed it so it is now viewable again. Regarding the proposed solution, I re-ran your code with the original input image on two separate machines and got the same blockiness in both output images. I will look into the anti-aliasing possibility and otherwise try to tweak your code to get it working correctly, thanks again! – mdl518 Mar 07 '21 at 14:46
  • did you try downloading the input image and running that through? I mean that literally, even if you have the original image, download it off of stack overflow and run that through the code. That should make it the exact same image as what I'm using on my computer. This will help us know if it's a compression issue or if some other weird thing is going on. – Ian Chu Mar 07 '21 at 17:12
  • Ian - I think you are onto something. I re-downloaded the input image as provided and re-ran in your script, and it worked seamlessly! :) For what it's worth, the input image provided is a screenshot (~750KB) of an original 2D image preview (~3MB) of a 3D model. Perhaps the processing of the 3D model to display as a 2D image preview is the source of the inability to preserve the contours? – mdl518 Mar 07 '21 at 18:56

1 Answers1

0

We can make a mask containing all of the not black in the image and use findContours to get the shapes of each black region. By selecting the largest contour we can mask out everything outside of the colored region.

enter image description here

import cv2
import numpy as np

# load image
img = cv2.imread("terrain.png");
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);

# mask
mask = cv2.inRange(gray, 1, 255);

# get contours # OpenCV 3.4, in OpenCV 2* or 4* it returns (contours, _)
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

# get biggest contour
biggest = None;
biggest_size = -1;
for con in contours:
    area = cv2.contourArea(con);
    if area > biggest_size:
        biggest_size = area;
        biggest = con;

# make mask with biggest contour
new_mask = np.zeros_like(mask);
cv2.drawContours(new_mask, [biggest], -1, (255), -1);

# redraw with white
img[new_mask == 0] = (255,255,255);

# show image
cv2.imshow("Image", img);
cv2.imshow("Mask", mask);
cv2.imshow("New Mask", new_mask);
cv2.waitKey(0);
Ian Chu
  • 2,924
  • 9
  • 14
  • Ian - Is the embedded image you posted a result of your script? I copy/pasted the code and ran it on my end but I get blocky edges on the immediate perimeter of the image, even though the image is preserved inside the contours. Was there anything else you did to obtain the image above? Thanks again! – mdl518 Mar 07 '21 at 01:28
  • Yes, I saved the image that I got after running the program. I used the input image you posted. – Ian Chu Mar 07 '21 at 01:30
  • can you post your result after running the code so that I can see what you're talking about? – Ian Chu Mar 07 '21 at 01:34
  • Ian - Is there a way to insert the image within a comment (like this one), or could I just embed it within your initial post? – mdl518 Mar 07 '21 at 02:16
  • I don't think there's a way to add images to comments. – Ian Chu Mar 07 '21 at 04:56
  • Ian - I added the output image I obtain via your contouring method to my original post. Please note that the background is actually white (not gray as depicted), as I could not reduce the image size via print screen/snipping tool. Regarding the blocky effect, is there anything in particular that may be causing this? Thanks again for all your help! – mdl518 Mar 07 '21 at 12:19