Course Notes: Background removal

Preamble

Notes from Background Removal Like Zoom | OpenCV Python CVZone

See also

Courses

Required Files

Required Packages

  • opencv-python
  • mediapipe
  • cvzone — booo!!!

Notes

 

From https://google.github.io/mediapipe/solutions/selfie_segmentation,

# SelfieSegmentation.py


import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_selfie_segmentation = mp.solutions.selfie_segmentation

# For static images:
IMAGE_FILES = []
BG_COLOR = (192, 192, 192) # gray
MASK_COLOR = (255, 255, 255) # white
with mp_selfie_segmentation.SelfieSegmentation(
    model_selection=0) as selfie_segmentation:
  for idx, file in enumerate(IMAGE_FILES):
    image = cv2.imread(file)
    image_height, image_width, _ = image.shape
    # Convert the BGR image to RGB before processing.
    results = selfie_segmentation.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

    # Draw selfie segmentation on the background image.
    # To improve segmentation around boundaries, consider applying a joint
    # bilateral filter to "results.segmentation_mask" with "image".
    condition = np.stack((results.segmentation_mask,) * 3, axis=-1) > 0.1
    # Generate solid color images for showing the output selfie segmentation mask.
    fg_image = np.zeros(image.shape, dtype=np.uint8)
    fg_image[:] = MASK_COLOR
    bg_image = np.zeros(image.shape, dtype=np.uint8)
    bg_image[:] = BG_COLOR
    output_image = np.where(condition, fg_image, bg_image)
    cv2.imwrite('/tmp/selfie_segmentation_output' + str(idx) + '.png', output_image)

# For webcam input:
BG_COLOR = (192, 192, 192) # gray
cap = cv2.VideoCapture(0)
with mp_selfie_segmentation.SelfieSegmentation(
    model_selection=1) as selfie_segmentation:
  bg_image = None
  while cap.isOpened():
    success, image = cap.read()
    if not success:
      print("Ignoring empty camera frame.")
      # If loading a video, use 'break' instead of 'continue'.
      continue

    # Flip the image horizontally for a later selfie-view display, and convert
    # the BGR image to RGB.
    image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
    # To improve performance, optionally mark the image as not writeable to
    # pass by reference.
    image.flags.writeable = False
    results = selfie_segmentation.process(image)

    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    # Draw selfie segmentation on the background image.
    # To improve segmentation around boundaries, consider applying a joint
    # bilateral filter to "results.segmentation_mask" with "image".
    condition = np.stack(
      (results.segmentation_mask,) * 3, axis=-1) > 0.1
    # The background can be customized.
    #   a) Load an image (with the same width and height of the input image) to
    #      be the background, e.g., bg_image = cv2.imread('/path/to/image/file')
    #   b) Blur the input image by applying image filtering, e.g.,
    #      bg_image = cv2.GaussianBlur(image,(55,55),0)
    if bg_image is None:
      bg_image = np.zeros(image.shape, dtype=np.uint8)
      bg_image[:] = BG_COLOR
    output_image = np.where(condition, image, bg_image)

    cv2.imshow('MediaPipe Selfie Segmentation', output_image)
    if cv2.waitKey(5) & 0xFF == 27:
      break
cap.release()

Start code

# BackgroundRemoval.py


import cv2
import cvzone
from cvzone.SelfiSegmentationModule import SelfiSegmentation
import os



cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
while True:
    success, image = cap.read()

    cv2.imshow("Image", image)
    cv2.waitkey(1)

model number:

0 generalised (slower)

1 – landscape (faster)

segmentor = SelfiSegmentation(model=1)

removeBG() takes a colour or an image. There is a threshold too. thresh = 1 everything is the colour. 0.95 if you move your hand the hand will be cut out. Keep at 0.8

# BackgroundRemoval.py

import cv2
import cvzone
from cvzone.SelfiSegmentationModule import SelfiSegmentation
import os

segmentor = SelfiSegmentation(mo)


cap = cv2.VideoCapture(0)
cap.set(3, 640)
cap.set(4, 480)
while True:
    success, image = cap.read()
    imageOut = segmentor.removeBG(image, (255.0.255), threshold=0.8)

    imageStacked = cvzone.stackImages([image, imageOut], 2, 1)
    cv2.imshow("Image", image)
    cv2.imshow("ImageOut", imageOut)
    cv2.imshow("ImageStacked", imageStacked)
    cv2.waitkey(1)

Note: stackimages() has the scale parameter at the end again. It moves from the first to the last parameter each video. Also an extra parameter, cols.

Add the FPS module to display FPS.

import FPSModule
fpsReader = FPS()

...

 fps, imageStacked = fpsReader.update(imageStacked)

The background images are all the same size, 640, 480.  Add the images to a directory Images.

To add one image:

imgBG = cv2.imread('Images/image.png')

and change

imageOut = segmentor.removeBG(image, imgBG, threshold=0.8)

How to change while running the program:

16:17

First make a list of the images:

# For multiple images
listImages = os.listdir(directoryImages)
# print(listImages)
imgList = []
for imgPath in listImages:
    img = cv2.imread(f'{directoryImages}/{imgPath}')
    imgList.append(img)
# print(len(imgList))

Then add an index

imageIndex = 0

and use the list

imageOut = segmentor.removeBG(image, imgList[imageIndex], threshold=0.8)

Change index on key press

key = cv2.waitkey(1)

and

if key == ord('a'):
    imageIndex += 1
elif key == ord('d'):
    imageIndex += 1
elif key == ord('q'):
    break

Add checks so as not to exceed the bounds of the image list

if key == ord('a'):
    if imageIndex > 0:
        imageIndex += 1
elif key == ord('d'):
    if imageIndex < len(imgList) - 1:
        imageIndex += 1

 

The module – SelfieSegmentation

# SelfieSegmentationModule.py


import cv2
import mediapipe as mp
import numpy as np


class SelfieSegmentation():

    def __init__(self, model=1):
        """
        :param model: model type 0 or 1. 0 is general 1 is landscape(faster)
        """
        self.model = model
        self.mpDraw = mp.solutions.drawing_utils
        self.mpSelfieSegmentation = mp.solutions.selfie_segmentation
        self.selfieSegmentation = self.mpSelfieSegmentation.SelfieSegmentation(self.model)

    def removeBG(self, img, imgBg=(255, 255, 255), threshold=0.1):
        """

        :param img: image to remove background from
        :param imgBg: BackGround Image
        :param threshold: higher = more cut, lower = less cut
        :return:
        """
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        results = self.selfieSegmentation.process(imgRGB)
        condition = np.stack(
            (results.segmentation_mask,) * 3, axis=-1) > threshold
        if isinstance(imgBg, tuple):
            _imgBg = np.zeros(img.shape, dtype=np.uint8)
            _imgBg[:] = imgBg
            imgOut = np.where(condition, img, _imgBg)
        else:
            imgOut = np.where(condition, img, imgBg)
        return imgOut


def main():
    cap = cv2.VideoCapture(0)
    cap.set(3, 640)
    cap.set(4, 480)
    segmentor = SelfieSegmentation()
    imgBg = cv2.imread("Segmentaiation test/Images/bg1.jpg")
    while True:
        success, img = cap.read()
        imgOut = segmentor.removeBG(img, imgBg=imgBg, threshold=0.1)

        cv2.imshow("Image", img)
        cv2.imshow("Image Out", imgOut)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break


if __name__ == "__main__":
    main()

The module – FPS

# FPSModule,py

"""
FPS Module
By: Computer Vision Zone
Website: https://www.computervision.zone/
"""

import time
import cv2


class FPS:
    """
    Helps in finding Frames Per Second and display on an OpenCV Image
    """

    def __init__(self):
        self.pTime = time.time()

    def update(self, img=None, pos=(20, 50), color=(255, 0, 0), scale=3, thickness=3):
        """
        Update the frame rate
        :param img: Image to display on, can be left blank if only fps value required
        :param pos: Position on the FPS on the image
        :param color: Color of the FPS Value displayed
        :param scale: Scale of the FPS Value displayed
        :param thickness: Thickness of the FPS Value displayed
        :return:
        """
        cTime = time.time()
        try:
            fps = 1 / (cTime - self.pTime)
            self.pTime = cTime
            if img is None:
                return fps
            else:
                cv2.putText(img, f'FPS: {int(fps)}', pos, cv2.FONT_HERSHEY_PLAIN,
                            scale, color, thickness)
                return fps, img
        except:
            return 0


def main():
    """
    Without Webcam
    """
    fpsReader = FPS()
    while True:
        time.sleep(0.025)  # add delay to get 40 Frames per second
        fps = fpsReader.update()
        print(fps)


def mainWebcam():
    """
    With Webcam
    """
    fpsReader = FPS()
    cap = cv2.VideoCapture(0)
    while True:
        success, img = cap.read()
        fps, img = fpsReader.update(img)
        cv2.imshow("Image", img)
        cv2.waitKey(1)


if __name__ == "__main__":
    # main()
    mainWebcam()

This is the end, my friend

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s