Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Dominick Marino
Published © CC BY

Possessed Portrait - Updated

DIY jump scare portrait from scratch using Raspberry Pi 3 B, Python and AtmosFX Videos unliving portraits.

IntermediateFull instructions provided8 hours80,720
Possessed Portrait - Updated

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
Used 19" LCD Monitor
×1
Camera Module
Raspberry Pi Camera Module
×1

Hand tools and fabrication machines

Miter Box
Cheap Miter box from Walmart to cut the frame pieces.
Brad Nailer
Used to assemble the frame pieces.
Tape Measure
Drill

Story

Read more

Code

scareRandom.py

Python
This version of the scare script will allow you to rotate between the list of videos in a random order.
This version of the script will allow you to easily add additional videos or use different videos altogether.
Read the comment in the top of the script for details.
#!/usr/bin/python
import subprocess as sp
import time
import os
import datetime
from pirDetect import *
import sys
import random
"""
This script will play any of the videos listed in a random order.
Usage: python ./scareRandom.py [VideoName] [Minutes]

[VideoName] is any of video prefixes in the video_prefix list.
[Minutes] is the time value in minutes of how often you want to rotate to a different video.

Example usage would be : python ./scareRandom.py Male 5.

After each trigger of the on_motion event the script will check and determine if the time elapsed is greater than the 
value you provided in argument 2 and if the elapsed time is longer than your time setting it will randomly pick a new
video prefix and will recursively attempt to choose one that is NOT the current video prefix so it doesn't play the same 
video more than one time in sequence.

To add more or different videos just add to or modify the video_prefix list below.
If adding more videos or changing the defaults you will have to create a start image for each additional video.
The naming structure for the start images and videos are as follows.

[Prefix]ScareV.m4v (MaleScareV.m4v) or [Prefix]ScareV.mp4 (MaleScareV.mp4)
[Prefix]Start.png (MaleStart.png) 
"""


#  initialize variables

video_prefix = ["Male", "Female", "Child"] # This is the list of videos prefixes, you can add additional video
# prefixes here.
video = ["omxplayer", "filename", "-o", "both", "--win", "0 0 1280 720", "--aspect-mode", "fill", "--no-osd",
         "--orientation", "180", "--vol", "-600"]
record = ["raspivid", "-o", "filename", "-n", "-t", "5000", "-rot", "180"]
scare_file = ""
current_prefix = ""
new_prefix = ""
image_name = ""
start_time = time.time()


def change_video():
    global start_time
    global scare_file
    global current_prefix
    global new_prefix
    elapsed_time = time.time() - start_time
    print(str("\nTime since last rotation: {0}".format(datetime.timedelta(seconds=elapsed_time))))
    if elapsed_time > (int(sys.argv[2]) * 60):
        while new_prefix == current_prefix:  # make sure we don't choose the same video
            new_prefix = video_prefix[random.randrange(len(video_prefix))]
        current_prefix = new_prefix
        scare_file = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.m4v".format(current_prefix)
        start_time = time.time()
        show_image(current_prefix)
        print("\nUpdating Video to: {0}\n".format(current_prefix))


def getfilename():
    return "/home/pi/Projects/Halloween/Recordings/" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S.h264")


def sub_proc_wait(params):
    sub = sp.Popen(params)
    while sub.poll() is None:
        time.sleep(.1)


def on_motion(curr_state):
    if curr_state:
        auto_file_name = getfilename()  # Get a time stamped file name
        record[2] = auto_file_name
        sub_record = sp.Popen(record)  # Start recording to capture their fright
        video[1] = scare_file
        sub_proc_wait(video)  # Play the video to scare them
        video[1] = auto_file_name
        sub_proc_wait(video)  # Play back the video we just recorded
        change_video()


def show_image(_image_name):
    os.system("sudo fbi -T 1 -d /dev/fb0 -noverbose -once /home/pi/Projects/Halloween/ScareMedia/{0}Start.png".format(
        _image_name))


def start_up():
    global scare_file
    global image_name
    image_name = arg1
    scare_file = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.m4v".format(image_name)
    show_image(image_name)
    obj_detect = detector(7)
    obj_detect.subscribe(on_motion)
    obj_detect.start()
    os.system("sudo killall -9 fbi")


if __name__ == "__main__":

    try:

        arg1 = sys.argv[1]
        if arg1 not in video_prefix:
            raise ValueError('first argument must be Male,Female or Child')
        if sys.argv[2].isdigit():
            arg2 = int(sys.argv[2])
        else:
            raise ValueError('Second argument must be a number')
    except IndexError:
        print("Usage: python ./scareRandom.py [VideoName] [Minutes]")
        sys.exit(1)
    except ValueError as x:
        print(x.message + "\nUsage: python ./scareRandom.py [VideoName] [Minutes]")
        sys.exit(1)

start_up()

pirDetect.py

Python
This handles all the motion detection and wires up the Montion event that is activated in the scare script.
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import os

class detector(object):
        def __init__(self, sensor):
                self.callBacks = []
                self.sensor = sensor
                self.currState = False
                self.prevState = False

                GPIO.setmode(GPIO.BOARD)
                GPIO.setup(self.sensor, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

        def read(self):
                self.prevState = self.currState
                self.currState = GPIO.input(self.sensor)

        def printState(self):
                print( "GPIO pin {0} is {1}".format(self.sensor, "HIGH" if self.currState else "LOW"))

        def subscribe(self, callBack):
                self.callBacks.append(callBack)

        def callBack(self, state):
                for fn in self.callBacks:
                        fn(state)

        def start(self):
                try:
                        self.read()
                        self.printState()
                        while True:
                                 self.read()
                                 if self.currState != self.prevState:
                                         self.printState()
                                         self.callBack(self.currState)
                                 time.sleep(.1)

                except (KeyboardInterrupt, SystemExit):
	#Since fbi doesn't restore the console correctly when the application is exited we do a little clean up.
						os.system('stty sane')

scare.py

Python
This is the script that gets run to initiate the video when motion is detected.
#!/usr/bin/python
import subprocess as sp
import time
import os
from pirDetect import *
import sys

video = ["omxplayer", "filename", "-o", "both", "--win", "0 0 1280 720", "--aspect-mode", "fill", "--no-osd", "--orientation" ,"180","--vol", "-600"]
scareFile = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.mp4".format(sys.argv[1])
print(scareFile)

def onMotion(currState):
    if currState:
        video[1] = scareFile
        subVideo = sp.Popen(video)
        while subVideo.poll() is None:
            time.sleep(.1)


def showImage():
    os.system("sudo fbi -T 1 -d /dev/fb0 -noverbose -once /home/pi/Projects/Halloween/ScareMedia/{0}Start.png".format(
        sys.argv[1]))


showImage()
objDetect = detector(7)
objDetect.subscribe(onMotion)
objDetect.start()
os.system("sudo killall -9 fbi")

scare2.py

Python
This is the updated version of the scare.py script that includes recording a video of your victim using a Pi camera.
#!/usr/bin/python
import subprocess as sp
import time
import os
import datetime
from pirDetect import *
import sys

video = ["omxplayer", "filename", "-o", "both", "--win", "0 0 1280 720", "--aspect-mode", "fill", "--no-osd", "--orientation" ,"180","--vol", "-600"]
record = ["raspivid", "-o", "filename", "-n", "-t", "5000", "-rot","180"]
scareFile = "/home/pi/Projects/Halloween/ScareMedia/{0}ScareV.mp4".format(sys.argv[1])

def getFileName():
    return "/home/pi/Projects/Halloween/Recordings/" + datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S.h264")

def subProcWait(params):
        sub = sp.Popen(params)
        while sub.poll() is None:
            time.sleep(.1)

def onMotion(currState):
    if currState:
        autoFileName = getFileName()  # Get a time stamped file name
        record[2] = autoFileName
        subRecord = sp.Popen(record)  # Start recording to capture their fright
        video[1] = scareFile
        subProcWait(video)  # Play the video to scare them
        video[1] = autoFileName
        subProcWait(video)  # Play back the video we just recorded

def showImage():
    os.system("sudo fbi -T 1 -d /dev/fb0 -noverbose -once /home/pi/Projects/Halloween/ScareMedia/{0}Start.png".format(
        sys.argv[1]))


showImage()
objDetect = detector(7)
objDetect.subscribe(onMotion)
objDetect.start()
os.system("sudo killall -9 fbi")

Credits

Dominick Marino

Dominick Marino

0 projects • 26 followers
Software Engineer.

Comments