OpenCV Index Error While Building a Face Masking App

Viewed 85

What I'm trying to make: A OpenCV program that can record a video and mask my face by using an image of a mask.

My Code:

import numpy as np
import cv2
import os
import time
import face_recognition as fr
import pkg_resources

filename = "THIS_IS_A_TEST.mp4"
frames_per_seconds = 24.0
my_res = "720p"

face_cascade = cv2.CascadeClassifier('C:\\Users\\jack\\Desktop\\haarcascade_frontalface_default.xml')
#eyes_cascade = cv2.CascadeClassifier('C:\\Users\\jack\\Desktop\\frontalEyes35x16.xml')

mask = cv2.imread("C:\\Users\\jack\\Desktop\\Blogger_Video_Recorder\\TheMask.png", -1)

   
def change_res(cap, width, height):
    cap.set(3, width)
    cap.set(4, height)

STD_DIMENSIONS = {
    "480p": (640,480),
    "720p": (1280, 720),
    "1080p": (1920, 1080),
    "4k": (3840, 2160),
}
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]
    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image
    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)
    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)
    # return the resized image
    return resized
def get_dims(cap, res="1080p"):
    width, height = STD_DIMENSIONS["480p"]
    if res in STD_DIMENSIONS:
        width, height = STD_DIMENSIONS[res]
    change_res(cap, width, height)
    return width, height

VIDEO_TYPE = {
    "mp4": cv2.VideoWriter_fourcc(*"XVID")
}

def get_video_type(filename):
    filename, ext = os.path.splitext(filename)
    if ext in VIDEO_TYPE:
        return VIDEO_TYPE[ext]
    return VIDEO_TYPE["mp4"]

cap = cv2.VideoCapture(0)
dims = get_dims(cap, res = my_res)
video_type_cv2 = get_video_type(filename)

out = cv2.VideoWriter(filename, video_type_cv2, frames_per_seconds, dims)

while True: 
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.5, minNeighbors=5)
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2BGRA)
    for (x, y, w, h) in faces:
        roi_gray = gray[y:y+h, x:x+h]
        roi_color = frame[y:y+h, x:x+h]
        roi_faces = roi_gray[y:y+h, x:x+w]
        mask2 = image_resize(mask.copy(), width=w)

        mw, mh, mc = mask2.shape
        for i in range(0,mw):
            for j in range(0, mh):
                if mask2[i, j][3] != 0:
                    roi_color[y + i, x + j] = mask2[i, j]
        
    frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR)
    out.write(frame)
    cv2.imshow("frame", frame)
    if cv2.waitKey(20) & 0xFF == ord("q"):
        break

cap.release()
out.release()
cv2.destroyAllWindows()

Traceback (most recent call last):

File "C:\Users\jack\Desktop\Blogger_Video_Recorder\tutorial#1.py", line 93, in <module>
roi_color[y + i, x + j] = mask2[i, j]
IndexError: index 426 is out of bounds for axis 0 with size 273

BTW I am also running this on the Python IDLE, so any help is appreciated :)

1 Answers

You do not have to do most of the existing phases. You can use the following method:

draw[y:y+h, x:x+w] = mask2

But in reality there are several errors, the first of which is that the height of the mask is less than the height of the face, so we will not be able to use the values surrounding the face that were found However, it is possible to know the mask height and reset the print dimensions on the basic frame. The next line

mask2 = image_resize(mask.copy(), width=w)
mw, mh, mc = mask2.shape

draw[y:y+mw, x:x+w] = mask2

Note Please do not use 'cv2.COLOR_BGR2BGRA' Or the conversion line:

frame = cv2.cvtColor(frame, cv2.COLOR_BGR2BGRA)

Except for necessity, because it increases the depth of the image to 4 dimensions, and an error will occur when you start printing the mask with the three dimensions,Like:

ValueError: could not broadcast input array from shape (273,410,3) into shape (273,410,4)

Of course it is possible to find another solution but it remains unlikely because you did not use BGRA at all in this code.

Imagine ->full code:

import numpy as np
import cv2
import os
import time
import face_recognition as fr
import pkg_resources

filename = "THIS_IS_A_TEST.mp4"
frames_per_seconds = 24.0
my_res = "720p"

face_cascade = cv2.CascadeClassifier(os.path.join(cv2.data.haarcascades ,'haarcascade_frontalface_default.xml'))
#eyes_cascade = cv2.CascadeClassifier(os.path.join(cv2.data.haarcascades ,'frontalEyes35x16.xml')

mask = cv2.imread("test.jpg", -1)

   
def change_res(cap, width, height):
    cap.set(3, width)
    cap.set(4, height)

STD_DIMENSIONS = {
    "480p": (640,480),
    "720p": (1280, 720),
    "1080p": (1920, 1080),
    "4k": (3840, 2160),
}
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]
    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image
    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)
    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)
    # return the resized image
    return resized
def get_dims(cap, res="1080p"):
    width, height = STD_DIMENSIONS["480p"]
    if res in STD_DIMENSIONS:
        width, height = STD_DIMENSIONS[res]
    change_res(cap, width, height)
    return width, height

VIDEO_TYPE = {
    "mp4": cv2.VideoWriter_fourcc(*"XVID")
}

def get_video_type(filename):
    filename, ext = os.path.splitext(filename)
    if ext in VIDEO_TYPE:
        return VIDEO_TYPE[ext]
    return VIDEO_TYPE["mp4"]

cap = cv2.VideoCapture(0)
dims = get_dims(cap, res = my_res)
video_type_cv2 = get_video_type(filename)

out = cv2.VideoWriter(filename, video_type_cv2, frames_per_seconds, dims)

while True: 
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.5, minNeighbors=5)
    draw  = frame.copy()

    for (x, y, w, h) in faces:
        roi_gray  = gray[y:y+h, x:x+w]
        roi_color = frame[y:y+h, x:x+w]
        roi_faces = roi_gray[y:y+h, x:x+w]
        mask2 = image_resize(mask.copy(), width=w)

        mw, mh, mc = mask2.shape

        draw[y:y+mw, x:x+w] = mask2
        
    out.write(draw)
    cv2.imshow("frame", draw)
    if cv2.waitKey(5) & 0xFF == ord("q"):
        break

cap.release()
out.release()
cv2.destroyAllWindows()

Side note: It is best to use the expression cv2.data.haarcascades to find the path of the xml files included in the library

can be used os.path.join(cv2.data.haarcascades ,'u_file.xml') to find a valid path for most operating systems.

example of this

import os
import cv2

root_data = cv2.data.haarcascades
face_cascade = cv2.CascadeClassifier(os.path.join(root_data ,'haarcascade_frontalface_default.xml'))
eyes_cascade = cv2.CascadeClassifier(os.path.join(root_data ,'frontalEyes35x16.xml'))

Done..

Related