1

I was wondering if I can translate this opencv-python method into Pillow as I am forced furtherly to process it in Pillow.

A workaround I thought about would be to just save it with OpenCV and load it after with Pillow but I am looking for a cleaner solution, because I am using the remove_background() method's output as input for each frame of a GIF. Thus, I will read and write images N * GIF_frames_count times for no reason.

The method I want to convert from Pillow to opencv-python:

def remove_background(path):
    img = cv2.imread(path)

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

    # Threshold input image as mask
    mask = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]

    # Negate mask
    mask = 255 - mask

    # Apply morphology to remove isolated extraneous noise
    # Use border constant of black since foreground touches the edges
    kernel = np.ones((3, 3), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    # Anti-alias the mask -- blur then stretch
    # Blur alpha channel
    mask = cv2.GaussianBlur(mask, (0, 0), sigmaX=2, sigmaY=2, borderType=cv2.BORDER_DEFAULT)

    # Linear stretch so that 127.5 goes to 0, but 255 stays 255
    mask = (2 * (mask.astype(np.float32)) - 255.0).clip(0, 255).astype(np.uint8)

    # Put mask into alpha channel
    result = img.copy()
    result = cv2.cvtColor(result, cv2.COLOR_BGR2BGRA)
    result[:, :, 3] = mask

    return result

Code taken from: how to remove background of images in python

Valentin Popescu
  • 174
  • 2
  • 12
  • 1
    Why not keep this and just convert the result to a PIL Image? https://stackoverflow.com/a/65075430/2836621 – Mark Setchell Mar 27 '22 at 12:55
  • @MarkSetchell this should work. I have tried it out though, and it writes the image with a blueish tint. Just to be clear, I have returned `Image.fromarray(result)` and then called `.save("new-image.png")` on it. – Valentin Popescu Mar 27 '22 at 13:14
  • 1
    Change the last `cv2.COLOR_BGR2BGRA` to `cv2.COLOR_BGR2RGBA` as PIL uses RGB ordering and OpenCV uses BGRA. – Mark Setchell Mar 27 '22 at 13:17
  • Sorry, I should have thought to mention that. – Mark Setchell Mar 27 '22 at 13:17
  • @MarkSetchell nice! If you'd like to give an answer so I can mark it as solution would be great! – Valentin Popescu Mar 27 '22 at 13:20
  • I can't at the moment, but you're welcome to summarise it and add it as an answer yourself - then you can accept it and grab the points. – Mark Setchell Mar 27 '22 at 13:25
  • 2
    You might reference the person who wrote that code you are borrowing or link to their answer and give it an up-vote, if you have not already. – fmw42 Mar 27 '22 at 15:26

1 Answers1

1

Rather than re-writing all the code using PIL equivalents, you could adopt the "if it ain't broke, don't fix it" maxim, and simply convert the Numpy array that the existing code produces into a PIL Image that you can use for your subsequent purposes.

That is this described in this answer, which I'll paraphrase as:

# Make "PIL Image" from Numpy array
pi = Image.fromarray(na)

Note that the linked answer refers to scikit-image (which uses RGB ordering like PIL) rather than OpenCV, so there is the added wrinkle that you will also need to reorder the channels from BGRA to RGBA, so the last couple of lines will look like:

...
...
result = cv2.cvtColor(result, cv2.COLOR_BGR2RGBA)
result[:, :, 3] = mask
pi = Image.fromarray(result)
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432