Search code examples
pythonopencvfilteringimaging

switching image filters on webcam using python opencv


i have a project where i'm trying to imitate the filtering system for snapchat. supposedly, after clicking on the filter buttons on the side of the window, the webcam would show whatever the filter is. however, the window just ends up freezing. if i try to implement the filters separately, they do actually work. but when i try to implement it here, the window freezes.

here's the code: (Functions detect(), get_cam_frame(), show_frame() and phone_filter() are not mine. I just retrieved them from the internet)

# Import Libraries
import numpy as np
import Tkinter as tk
import tkMessageBox
import cv2
import sys
from PIL import Image, ImageTk
from video import create_capture


# Initialize Window

root = tk.Tk()
root.wm_title("Filter App")
root.config(background="#000000")
canvas = tk.Canvas(root, width=600, height=700)
canvas.pack()
canvas.grid(row=0, column=0, padx=5, pady=20)

lmain = tk.Label(canvas)
lmain.grid(row=0, column=0, padx=85, pady=119)
cap = cv2.VideoCapture(0)

def detect(img, cascade):
    rects = cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)
    if len(rects) == 0:
        return []
    rects[:, 2:] += rects[:, :2]
    return rects

def get_cam_frame(cam):
    ret, img = cam.read()
    # smaller frame size - things run a lot smoother than a full screen img
    img = cv2.resize(img, (800, 470))
    return img



def show_frame():
    _, frame = cap.read()
    frame = cv2.flip(frame, 1)
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    img = Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    lmain.after(10, show_frame)

sliderFrame = tk.Frame(root, width=500, height=50)
sliderFrame.grid(row = 500, column=0, padx=10, pady=2)
show_frame()

def phone_filter(img):
    show_frame()
    print img
    if img == "dog":
        filter_img = cv2.imread("img/face/dogfilter.png", cv2.IMREAD_COLOR)
    elif img == "dalmatian":
        filter_img = cv2.imread("img/face/dalmatianfilter.png", cv2.IMREAD_COLOR)
    elif img == "anime":
        filter_img = cv2.imread("img/face/animefilter.png", cv2.IMREAD_COLOR)
    elif img == "catears":
        filter_img = cv2.imread("img/face/catearsfilter.png", cv2.IMREAD_COLOR)
    elif img == "mustache":
        filter_img = cv2.imread("img/face/mustachefilter.png", cv2.IMREAD_COLOR)
    elif img == "pig":
        filter_img = cv2.imread("img/face/pigfilter.png", cv2.IMREAD_COLOR)
    elif img == "shaider":
        filter_img = cv2.imread("img/face/shaiderfilter.png", cv2.IMREAD_COLOR)
    elif img == "none":
        filter_img = cv2.imread("img/Empty.png", cv2.IMREAD_COLOR)
    else:
        filter_img = cv2.imread("img/Empty.png", cv2.IMREAD_COLOR)
    haar_classifier = "data/haarcascade_frontalface_default.xml"
    # use the haar classifier for now, it seems to work a little bit better
    cascade = cv2.CascadeClassifier(haar_classifier)
    print cascade
    while True:
        print "."
        cam = cv2.VideoCapture(0)
        print cam
        bw = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        bw = cv2.equalizeHist(bw)
        rects = detect(bw, cascade)
        final = img.copy()
        # for x1, y1, x2, y2 in rects:
        #     cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2)
        if len(rects) >= 1:
            allFaces = rects
            #rect = (ax1, ay1, ax2, ay2)
            for index, (ax1, ay1, ax2, ay2) in enumerate(allFaces):
                deltaY = abs(ay2) - abs(ay1)
                stretchFactor = 0.2
                stretchAmount = int(stretchFactor * deltaY)
                ay2 = ay2 + stretchAmount
                ay1 = ay1 - stretchAmount
                height, width, _ = img.shape
                if ax1 > stretchAmount and ax2 < width - stretchAmount and ay1 > stretchAmount and ay2 < height - stretchAmount:
                    face = img[ay1:ay2, ax1:ax2]
                    filter_scale = []
                    if index % 2 == 0:
                        #dog_scale = cv2.resize(dog_img, (ax2 - ax1, ay2 - ay1))
                        filter_scale = cv2.resize(filter_img, (ax2 - ax1, ay2 - ay1))
                    else:
                        filter_scale = cv2.resize(filter_img, (ax2 - ax1, ay2 - ay1))
                    # my_scaled = np.where(dog_scale == 0, face, dog_scale)
                    my_scaled = np.where(filter_scale == 0, face, filter_scale)
                    # faceB = cv2.resize(
                    # img[by1:by2, bx1:bx2].copy(), (ax2 - ax1, ay2 - ay1))
                    final[ay1:ay2, ax1:ax2] = my_scaled
                    #final[by1:by2, bx1:bx2] = faceA
        cv2.imshow(final)

def dogOp():
    phone_filter("dog")
    # tkMessageBox.showinfo("Face Filter", "Dog Filter")

def dalmatianOp():
    phone_filter("dalmatian")
    # tkMessageBox.showinfo("Face Filter", "Dalmatian Filter")

def animeOp():
    phone_filter("anime")
    # tkMessageBox.showinfo("Face Filter", "Anime Filter")

def catearsOp():
    phone_filter("catears")
    # tkMessageBox.showinfo("Face Filter", "Cat Ears Filter")

def mustacheOp():
    phone_filter("mustache")
    # tkMessageBox.showinfo("Face Filter", "Mustache Filter")

def pigOp():
    phone_filter("pig")
    # tkMessageBox.showinfo("Face Filter", "Pig Filter")

def shaiderOp():
    phone_filter("shaider")
    # tkMessageBox.showinfo("Face Filter", "Shaider Pulis Pangkalawakan")

initializing background

image = Image.open('img/phone_bg.png')
image = image.resize((820, 700), Image.ANTIALIAS)
tk_img = ImageTk.PhotoImage(image)
canvas.create_image(400, 360, image=tk_img)

initializing face filters

dogfilter = Image.open("img/face/dogfilter.png")
dogfilter = dogfilter.resize((50, 50), Image.ANTIALIAS)
dog = ImageTk.PhotoImage(dogfilter)

dalmatianfilter = Image.open("img/face/dalmatianfilter.png")
dalmatianfilter = dalmatianfilter.resize((50, 50), Image.ANTIALIAS)
dalmatian = ImageTk.PhotoImage(dalmatianfilter)

animefilter = Image.open("img/face/animefilter.png")
animefilter = animefilter.resize((50, 50), Image.ANTIALIAS)
anime = ImageTk.PhotoImage(animefilter)

catearsfilter = Image.open("img/face/catearsfilter.png")
catearsfilter = catearsfilter.resize((50, 50), Image.ANTIALIAS)
catears = ImageTk.PhotoImage(catearsfilter)

mustachefilter = Image.open("img/face/mustachefilter.png")
mustachefilter = mustachefilter.resize((50, 50), Image.ANTIALIAS)
mustache = ImageTk.PhotoImage(mustachefilter)

pigfilter = Image.open("img/face/pigfilter.png")
pigfilter = pigfilter.resize((50, 50), Image.ANTIALIAS)
pig = ImageTk.PhotoImage(pigfilter)

shaiderfilter = Image.open("img/face/shaiderfilter.png")
shaiderfilter = shaiderfilter.resize((50, 50), Image.ANTIALIAS)
shaider = ImageTk.PhotoImage(shaiderfilter)


face filter buttons

dogbtn = tk.Button(root, width=30, height=30, image = dog, command=dogOp)
dogbtn_window = canvas.create_window(100,150, anchor='nw', window=dogbtn)

dalmatianbtn = tk.Button(root, width=30, height=30, image = dalmatian, command=dalmatianOp)
dalmatianbtn_window = canvas.create_window(100,190, anchor='nw', window=dalmatianbtn)

animebtn = tk.Button(root, width=30, height=30, image = anime, command=animeOp)
animebtn_window = canvas.create_window(100,230, anchor='nw', window=animebtn)

catearsbtn = tk.Button(root, width=30, height=30, image = catears, command=catearsOp)
catearsbtn_window = canvas.create_window(100,270, anchor='nw', window=catearsbtn)

mustachebtn = tk.Button(root, width=30, height=30, image = mustache, command=mustacheOp)
mustachebtn_window = canvas.create_window(100,310, anchor='nw', window=mustachebtn)

pigbtn = tk.Button(root, width=30, height=30, image = pig, command=pigOp)
pigbtn_window = canvas.create_window(100,350, anchor='nw', window=pigbtn)

shaiderbtn = tk.Button(root, width=30, height=30, image = shaider, command=shaiderOp)
shaiderbtn_window = canvas.create_window(100,390, anchor='nw', window=shaiderbtn)

quit_button = tk.Button(root, text = "X", command = root.quit, anchor = 'w',
                    width = 2, bg="red")
quit_button_window = canvas.create_window(680,120, anchor='nw', window=quit_button)


root.mainloop()

Solution

  • The camerafeed freezes whenever you press a button because your phone_filter() function never ends. Indeed, it contains a while True loop.

    Moreover, in this very function, you try to re-open VideoCapture(0), which is cleary wrong given the fact that you have already opened it in your initialization step (cap = cv2.VideoCapture(0)). Use cap variable instead.

    Two lines bellow, with bw = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), you try to convert img which is your input string!


    Anyways, I don't think your code structure is right according to what you are trying to achieve. You should do something in this taste:

    Inside a while True loop:

    • Retrieve a frame from the camera.
    • Then, call a face recognition function on that frame that will return the position of the detected face(s).
    • Draw the selected filter on top the detected face(s).
    • Finally, display the resulting image.

    The functions bound to the buttons should only change a variable (that you could call selected_filter). Then, in the while True loop, you'd read this variable to draw the right filter.