2

I know that there are many threads here about this issue, but I'm not able to solve my problem with those answers. I tried many times with different codes to remove the background of an image like this: With Background

to this:

Without Background

with the following code:

img2 = Image.open("foo.jpg")
c_red, c_green, c_blue = cv2.split(img2)
img2 = cv2.merge((c_red, c_green, c_blue, mask.astype('float32') / 255.0))
img.paste(img2,(0,0))

or with this code:

img2 = Image.open("foo.jpg")
img2 = img2.convert("RGBA")
datas = img2.getdata()

newData = []
for item in datas:
    if item[0] == 255 and item[1] == 255 and item[2] == 255:
        newData.append((255, 255, 255, 0))
    else:
        newData.append(item)
img2.putdata(newData)

or:

threshold=100
dist=5
img2 = Image.open("foo.jpg")
img2 = img2.convert("RGBA")

arr=np.array(np.asarray(img2))
r,g,b,a=np.rollaxis(arr,axis=-1)    
mask=((r>threshold)
      & (g>threshold)
      & (b>threshold)
      & (np.abs(r-g)<dist)
      & (np.abs(r-b)<dist)
      & (np.abs(g-b)<dist)
      )
arr[mask,3]=0
img2=Image.fromarray(arr,mode='RGBA')

But none of them does not work. The thing I want to do is remove the background of any color (transparent) and change the borders of an image to its object's border as I showed above. Any help is appreciated.

Mahdi
  • 967
  • 4
  • 18
  • 34
  • 1
    You are trying to save alpha channel information to a `.jpg`, which is [not supported by `python-pillow`](https://github.com/python-pillow/Pillow/issues/2609). Consider changing `img2 = Image.open("foo.jpg")` to `img2 = Image.open("foo.png")` – metatoaster Feb 03 '20 at 04:24
  • I changed to what you said but still unsuccessful – Mahdi Feb 03 '20 at 04:34
  • 1
    Another thing to note: the "white" pixels in the image you provided is not actually consistently of the value `#ffffff`. Quite a large number are `#fefefe`. You may wish to check that for all values greater than `254` instead (e.g. `if all(i >= 254 for i in item)` – metatoaster Feb 03 '20 at 04:42
  • 2
    To be clear: the code you are borrowing (presumably from [this question](https://stackoverflow.com/questions/765736/using-pil-to-make-all-white-pixels-transparent)) only changes pure white pixels to transparent. It will not work for "any" color. – metatoaster Feb 03 '20 at 04:44
  • 1
    If you want arbitrary removal of backgrounds, consider [reading this SO question and answer](https://stackoverflow.com/questions/35531932/pil-remove-background-image-from-image). – metatoaster Feb 03 '20 at 04:46

1 Answers1

4

Here's one way to do it using OpenCV. The idea is to obtain a binary image then use cv2.boundingRect to obtain the bounding rectangle coordinates. We can crop the image using Numpy slicing then add an alpha channel. Here's the results:

Input image

enter image description here

Binary image and region to extract

enter image description here

Extracted ROI

enter image description here

Code

import cv2
import numpy as np

# Load image, convert to grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Obtain bounding rectangle and extract ROI
x,y,w,h = cv2.boundingRect(thresh)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
ROI = original[y:y+h, x:x+w]

# Add alpha channel
b,g,r = cv2.split(ROI)
alpha = np.ones(b.shape, dtype=b.dtype) * 50
ROI = cv2.merge([b,g,r,alpha])

cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.imshow('ROI', ROI)
cv2.waitKey()
nathancy
  • 42,661
  • 14
  • 115
  • 137
  • Thank you for your comment, but the background is not transparent – Mahdi Feb 04 '20 at 08:50
  • You can simply add an alpha layer to the image to make the image transparent. Also you may want to update the post, removing the image background could mean cropping all the white space – coffeewin Feb 04 '20 at 09:11
  • I showed my final image above. Could you please show how to add alpha layer ? – Mahdi Feb 04 '20 at 15:22
  • @Mahdi check the update, you can change the alpha value depending on how transparent you want the image – nathancy Feb 04 '20 at 20:48
  • @nathancy, in this case the transparency channel is merged with the whole image. Is there an easy way to make just the background transparent? – Ryu Kent Apr 06 '22 at 06:39
  • @RyuKent Yes its possible, you should open a new question for that – nathancy Apr 06 '22 at 08:54