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