pythonopencvchromakey

chromakey: opencv make green screen video transparent


I'm using the below script to replace the green screen of the original video with a background image but the result is not what I expected, I've changed u_green array parameters and also l_green but it only gets worst. In the end, I want to make it transparent which mask should I use?

I'd appreciate any help to fix this.

Python script:

import cv2
import numpy as np


video = cv2.VideoCapture("green.mp4")
image = cv2.imread("bg.jpg")

while True:

    ret, frame = video.read()

    frame = cv2.resize(frame, (640, 480))
    image = cv2.resize(image, (640, 480))

    u_green = np.array([104, 153, 70])
    l_green = np.array([30, 30, 0])

    mask = cv2.inRange(frame, l_green, u_green)
    res = cv2.bitwise_and(frame, frame, mask=mask)

    f = frame - res
    f = np.where(f == 0, f, image)

    cv2.imshow("video", frame)
    cv2.imshow("mask", f)

    if cv2.waitKey(25) == 27:
        break

video.release()
cv2.destroyAllWindows()

result : enter image description here

Update Source video: Link


Solution

  • I did my best to mask out the screen using the HSV color space. There's still some green outline, but I can't increase the color margin any more without chopping out bits of clothes.

    Edit: wrapped the code inside of a video loop.

    Edit 2: I added a VideoWriter to save the results and swapped over to using the saturation channel since it had better separation.

    Output Video:

    https://drive.google.com/file/d/1GrECFwFy7JQJT6kUGrfLtlXjcfBsr7fP/view?usp=sharing

    import cv2
    import numpy as np
    
    # open up video
    cap = cv2.VideoCapture("video.mp4");
    
    # grab one frame
    scale = 0.5;
    _, frame = cap.read();
    h,w = frame.shape[:2];
    h = int(h*scale);
    w = int(w*scale);
    
    # videowriter 
    res = (w, h);
    fourcc = cv2.VideoWriter_fourcc(*'XVID');
    out = cv2.VideoWriter('test_vid.avi',fourcc, 30.0, res);
    
    # loop
    done = False;
    while not done:
        # get frame
        ret, img = cap.read();
        if not ret:
            done = True;
            continue;
    
        # resize
        img = cv2.resize(img, res);
    
        # change to hsv
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV);
        h,s,v = cv2.split(hsv);
    
        # get uniques
        unique_colors, counts = np.unique(s, return_counts=True);
    
        # sort through and grab the most abundant unique color
        big_color = None;
        biggest = -1;
        for a in range(len(unique_colors)):
            if counts[a] > biggest:
                biggest = counts[a];
                big_color = int(unique_colors[a]);
    
        # get the color mask
        margin = 50;
        mask = cv2.inRange(s, big_color - margin, big_color + margin);
    
        # smooth out the mask and invert
        kernel = np.ones((3,3), np.uint8);
        mask = cv2.dilate(mask, kernel, iterations = 1);
        mask = cv2.medianBlur(mask, 5);
        mask = cv2.bitwise_not(mask);
    
        # crop out the image
        crop = np.zeros_like(img);
        crop[mask == 255] = img[mask == 255];
    
        # show
        cv2.imshow("Mask", mask);
        cv2.imshow("Blank", crop);
        cv2.imshow("Image", img);
        done = cv2.waitKey(1) == ord('q');
    
        # save
        out.write(crop);
    
    # close caps
    cap.release();
    out.release();