# Fernando Paulino project for 16264
# Collection of opencv projects that interface with a camera's live video stream
import cv2
import numpy
import sys
from tkinter import *

# OpenCV projects includes facial detection, edge imaging, 
# background removal and video blurring 
CAPTURE_INDEX = 1;

def faceDetect():
    capture = cv2.VideoCapture(CAPTURE_INDEX)
    face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

    while(capture.isOpened()):
        ret, img = capture.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, 1.3, 5)

        for (x,y,w,h) in faces:
            cv2.rectangle(img, (x,y), (x+w, y+h),(50, 205, 154) , 3)

        cv2.imshow('Detecting Face', img)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    capture.release()
    cv2.destroyAllWindows()
    return
    
def videoEdges():

    # Default video capture index 0
    # Use other numbers if other cameras are connected
    capture = cv2.VideoCapture(CAPTURE_INDEX)
    while(capture.isOpened()):

        ret, frame = capture.read()

        if ret:
            #Change to hue, saturation, variation from RGB
            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

            red_low  = numpy.array([30, 150, 50])
            red_high = numpy.array([255,255, 180])

            mask   = cv2.inRange(hsv, red_low ,red_high)
            result = cv2.bitwise_and(frame, frame, mask= mask)
            edges = cv2.Canny(frame, 100, 200)

            #cv2.imshow('Original', frame)
            cv2.imshow('Edges', edges)

            # Wait for user to enter 'q' key to end 
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    capture.release()
    cv2.destroyAllWindows()
    return

def videoBlur():
    capture = cv2.VideoCapture(CAPTURE_INDEX)
    while(capture.isOpened()):
        ret, frame = capture.read()
        blur = cv2.GaussianBlur(frame, (5,5), cv2.BORDER_DEFAULT)

        cv2.imshow('Original', frame)
        cv2.imshow('Blurring Video', blur)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    capture.release()
    cv2.destroyAllWindows();
    return

def showAnImage():
    img = cv2.imread('lena.jpg')
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    cv2.imshow('This is Lena', hsv)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def removeBackground():
    capture = cv2.VideoCapture(CAPTURE_INDEX)
    success, ref_img = capture.read()
    flag = 0

    print('Stabilize camera')
    print('Move away from the camera to capture the background w/ d key')
    while(True):
        success, img = capture.read()
        if flag == 0:
            ref_img = img
        diff1 = cv2.subtract(img, ref_img)
        diff2 = cv2.subtract(ref_img, img)
        diff  = diff1 + diff2
        diff[abs(diff) < 13.0] = 0

        gray = cv2.cvtColor(diff.astype(numpy.uint8), cv2.COLOR_BGR2GRAY)
        gray[numpy.abs(gray) < 10] = 0

        fgmask = gray.astype(numpy.uint8)
        fgmask[fgmask>0] = 255

        fgimg = cv2.bitwise_and(img, img, mask = fgmask)
        kernel = numpy.ones((5,5), numpy.uint8)
        fgimg_eroded = cv2.erode(fgimg, kernel, iterations = 1)
        cv2.imshow('Edges', fgimg_eroded)

        key = cv2.waitKey(5) & 0xFF
        if ord('q') == key:
            break
        elif ord('d') == key:
            flag = 1
            print ('Background Captured')
        elif ord('r') == key:
            flag = 0
            print ('Ready to Capture Now')
    capture.release()
    cv2.destroyAllWindows()
    return
####################################
# init
####################################

def init(data):
    # Modes are:
    # - faceDetect
    # - videoBlur
    # - videoEdges
    # - removeBackground
    data.mode = "splashScreen"


####################################
# mode dispatcher
####################################

def mousePressed(event, data):
    pass

def keyPressed(event, data):
    if (data.mode == "splashScreen"): 
        splashScreenKeyPressed(event, data)
    elif (data.mode == "openCamera"): 
        openCameraKeyPressed(event, data)

def timerFired(data):
    pass

def redrawAll(canvas, data):
    if (data.mode == "splashScreen"): splashScreenRedrawAll(canvas, data)
    elif (data.mode == "openCamera"):   openCameraRedrawAll(canvas, data)

####################################
# splashScreen mode
####################################

def splashScreenMousePressed(event, data):
    pass

def splashScreenKeyPressed(event, data):
    if (event.char == "r"):
        data.mode = "openCamera"
        removeBackground()
    elif (event.char == "f"):
        data.mode = "openCamera"
        faceDetect()
    elif (event.char == "e"):
        data.mode = "openCamera"
        videoEdges()
    elif (event.char == "b"):
        data.mode = "openCamera"
        videoBlur()

def splashScreenTimerFired(data):
    pass

def splashScreenRedrawAll(canvas, data):
    canvas.create_text(150,20,text="Welcome to Fernando's OpenCV project.")
    canvas.create_text(150,40,text="To activate backgroud removal: Press R")
    canvas.create_text(150,60,text="To activate face detection:    Press F")
    canvas.create_text(150,80,text="To activate edge rendering:    Press E")
    canvas.create_text(150,100,text="To activate video blurring:    Press B")

####################################
# help mode
####################################

def helpMousePressed(event, data):
    pass

def helpKeyPressed(event, data):
    data.mode = "playGame"

def helpTimerFired(data):
    pass

def helpRedrawAll(canvas, data):
    canvas.create_text(data.width/2, data.height/2-40,
                       text="This is help mode!", font="Arial 26 bold")
    canvas.create_text(data.width/2, data.height/2-10,
                       text="How to play:", font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+15,
                       text="Do nothing and score points!", font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+40,
                       text="Press any key to keep playing!", font="Arial 20")

####################################
# openCamera mode
####################################

def openCameraKeyPressed(event, data):
    if (event.char == "q"):
        data.mode = "splashScreen"

def openCameraRedrawAll(canvas, data):
    canvas.create_text(150,100,text="To go back to main menu:    Press q again")

####################################
# use the run function as-is
####################################

def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()    

    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)

    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)

    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    init(data)
    # create the root and the canvas
    root = Tk()
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")

run(500, 300)