191 lines
6.0 KiB
Python
191 lines
6.0 KiB
Python
import cv2
|
|
import time
|
|
import numpy as np
|
|
import sys
|
|
import RPi.GPIO as GPIO
|
|
|
|
#timers
|
|
timeTV = 5
|
|
timeGhost = 25
|
|
timeNoGhost = 10
|
|
|
|
|
|
GPIO.setwarnings(False)
|
|
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
|
|
GPIO.setup(12, GPIO.IN)# Remote Button B - Take picture
|
|
GPIO.setup(16, GPIO.IN)# Remote button D - Armed & Ready
|
|
|
|
GPIO.setup(22, GPIO.OUT)# RED
|
|
GPIO.setup(24, GPIO.OUT)# GREEN
|
|
GPIO.setup(26, GPIO.OUT)# BLUE
|
|
|
|
GPIO.setup(32, GPIO.OUT)# Relay - TV on/off
|
|
|
|
|
|
|
|
#If you want to use a custom background you can use this function to resize it.
|
|
def resize(dst,img):
|
|
width = img.shape[1]
|
|
height = img.shape[0]
|
|
dim = (width, height)
|
|
resized = cv2.resize(dst, dim, interpolation = cv2.INTER_AREA)
|
|
return resized
|
|
|
|
|
|
presenceTriggered = False
|
|
cooldown = False #cooldown state after the haunt is over
|
|
ghostVisible = False #toggles the visiblity of the ghost
|
|
|
|
GPIO.output(32, GPIO.HIGH) #Turn TV on at the beginning for setup mode.
|
|
|
|
|
|
window_name = "window"
|
|
face_cascade = cv2.CascadeClassifier('/home/pi/Documents/haarcascade_frontalface_default.xml')
|
|
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
|
|
|
|
#inialize fullscreen
|
|
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
|
|
cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
|
|
|
#capture video from the camera if you want to test this on your computer, change 0 with 1
|
|
cam = cv2.VideoCapture(0)
|
|
|
|
#custom resolution for CRT TV
|
|
cam.set(3,768)
|
|
cam.set(4,576)
|
|
|
|
success, ref_img = cam.read() #reference image taken for background removal
|
|
success2, bg = cam.read()
|
|
success3, bg2 = cam.read()
|
|
flag = 0
|
|
|
|
|
|
#star the timers
|
|
coolDownStart = time.time() #this will reset after the haunt is done.
|
|
faceTimeStart = time.time() #the time it takes to detect a new face.
|
|
ghostTime = time.time()
|
|
faceFreqCounter = 0 #face frequency counter to filter out false positives
|
|
|
|
while True:
|
|
|
|
ret, img = cam.read()
|
|
if ret:
|
|
img=cv2.flip(img,0) #flips the image vertically
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
detected = face_cascade.detectMultiScale(gray, 1.3, 5)
|
|
#erase the '#' below if you want to detect eyes instead of faces
|
|
#detected = eye_cascade.detectMultiScale(gray)
|
|
|
|
#If the setup is armed, execute facedetection.
|
|
if flag==3:
|
|
if not cooldown: #the cooldown timer prevents the facedetection from running
|
|
for (x,y,w,h) in detected:
|
|
#draw rectangles around faces/eyes for debugging
|
|
#cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
|
|
|
|
faceTimeEnd = time.time()
|
|
elapsedFaceTime = faceTimeEnd - faceTimeStart
|
|
|
|
if (elapsedFaceTime<1):
|
|
faceFreqCounter+=1
|
|
print(faceFreqCounter)
|
|
|
|
if (elapsedFaceTime>5):
|
|
faceFreqCounter=0
|
|
|
|
faceTimeStart = time.time()
|
|
|
|
faceDetectionElapsed=time.time()- faceTimeStart
|
|
|
|
if(faceFreqCounter>timeTV) & (not ghostVisible) & (not presenceTriggered):
|
|
GPIO.output(32, GPIO.HIGH)
|
|
if(faceFreqCounter>timeGhost) & (faceDetectionElapsed>0.5) & (not ghostVisible):
|
|
|
|
ghostVisible = True
|
|
faceFreqCounter=0
|
|
bg = bgHaunted
|
|
|
|
if(ghostVisible & (faceFreqCounter>timeNoGhost) & (faceDetectionElapsed>0.5)) & (not presenceTriggered):
|
|
print("NO GHOST")
|
|
ghostTime = time.time()
|
|
ghostVisible = False
|
|
faceFreqCounter=0
|
|
presenceTriggered = True
|
|
bg=bg2
|
|
|
|
end2 = time.time()
|
|
elapsedTime2 = end2 - ghostTime
|
|
print(elapsedTime2)
|
|
if (elapsedTime2>5) and presenceTriggered:
|
|
GPIO.output(32, GPIO.LOW)
|
|
presenceTriggered = False
|
|
cooldown = True
|
|
if (elapsedTime2>20) and cooldown:
|
|
print("cooldown done")
|
|
faceFreqCounter=0
|
|
cooldown = False
|
|
|
|
|
|
#background removal based on https://github.com/misbah4064/backgroundRemoval
|
|
#success, img = cam.read()
|
|
|
|
if flag==0:
|
|
ref_img = img
|
|
flag=1
|
|
|
|
if flag==2:
|
|
success3, bgHaunted = cam.read()
|
|
flag=1
|
|
# create a mask
|
|
diff1=cv2.subtract(img,ref_img)
|
|
diff2=cv2.subtract(ref_img,img)
|
|
diff = diff1+diff2
|
|
#orig value =13
|
|
diff[abs(diff)<20.0]=0
|
|
gray = cv2.cvtColor(diff.astype(np.uint8), cv2.COLOR_BGR2GRAY)
|
|
gray[np.abs(gray) < 10] = 0
|
|
fgmask = gray.astype(np.uint8)
|
|
fgmask[fgmask>0]=255
|
|
#invert the mask
|
|
fgmask_inv = cv2.bitwise_not(fgmask)
|
|
#use the masks to extract the relevant parts from FG and BG
|
|
fgimg = cv2.bitwise_and(img,img,mask = fgmask)
|
|
bgimg = cv2.bitwise_and(bg,bg,mask = fgmask_inv)
|
|
#combine both the BG and the FG images
|
|
dst = cv2.add(fgimg,bgimg)
|
|
dst=cv2.flip(dst, 0)
|
|
cv2.imshow(window_name,dst)
|
|
|
|
|
|
|
|
|
|
key = cv2.waitKey(5) & 0xFF
|
|
if ord('q') == key:
|
|
break
|
|
if ord('a') == key or (GPIO.input(16) == GPIO.HIGH):
|
|
flag = 3
|
|
print("Setup armed and ready!")
|
|
GPIO.output(24, GPIO.LOW)
|
|
GPIO.output(22, GPIO.HIGH)
|
|
GPIO.output(32, GPIO.LOW)
|
|
|
|
if (ord('h') == key) or (GPIO.input(12) == GPIO.HIGH):
|
|
flag = 2
|
|
print("Haunted Picture Taken!")
|
|
GPIO.output(24, GPIO.HIGH)
|
|
GPIO.output(22, GPIO.LOW)
|
|
elif ord('d') == key:
|
|
flag = 1
|
|
print("Background Captured")
|
|
elif ord('r') == key:
|
|
flag = 0
|
|
print("Ready to Capture new Background")
|
|
elif ord('t') == key:
|
|
bg = bgHaunted
|
|
print("Image is now haunted!")
|
|
|
|
|
|
GPIO.cleanup()
|
|
cv2.destroyAllWindows()
|
|
cam.release()
|