Introduction: Haunted Magic Mirror
For a while now I have wanted to make a Magic Mirror. I decided to make a haunted one for Halloween that would follow people walking by my desk at work.
Parts I used:
1. Raspberry Pi 2 or 3
2. SD Card with Raspbian Lite
3. Pi Camera
4. Old LCD Monitor - Preferably with HDMI
5. Acrylic see-through Mirror
6. 8' of Molding
7. 8' of 4x1 Wood
8. Power Cables(2)
9. HDMI Cable
10. Ghost pictures (36)
11. Spray paint
1. Hammered Copper
2. Sea Glass
3. Black
4. Clear Coat
Tools I used:
• Wood glue
• Clamps
• Miter saw
• Table saw
• Router
Step 1: Build the Frame
You will first need to remove the LCD panel from the plastic case. Once you get the monitor disassembled measure the outside edges.
- Use the router to cut a groove along the entire inside edge of the baseboard. I did this to get an even edge along the entire edge of the board. The depth of the cut should be the width of the mirror so that it doesn't move once the LCD is behind it.
- Cut the baseboard on a 45 degree. The top and bottom pieces inside edge should be 1 1/2 inches longer than the screen. I made the sides 3 inches longer so that I could hide the camera behind the mirror.
- Use wood glue and corner clamps to assemble the frame.
Now we need to create back pieces to cover the Electronics and hold everything in..
- Cut 4 pieces of the 4x1 boards so that they will lineup and allow the LCD panel to fit snugly.
- Once you have the 4 boards cut use the wood glue and corner clamps to attach them together.
I placed the LCD into it position so that the Board were square and then removed them once the corner clamps were in place.
- I chose to glue them down to the baseboard at this time and used regular clamps to hole them in place.
Step 2: Paint the Frame
My theme this year is recovered sunken treasure so I wanted a cooper covered in green patina look.
- I covered the frame in a solid covering of Hammered Copper.
- I sprayed a light coating of Black.
- Before the Black had a chance to dry I used the Acetone to remove the Black from the high areas. You want to do this lightly so that you do not remove the Copper coat underneath. I used a couple of spare pieces of baseboard to test the right amount of black to use and remove.
- I sprayed the Sea Glass onto a spare piece of cardboard and used a paintbrush to dab the green onto the frame. If you feel like you used too much of the green you can dab the copper on top of the green to remove it.
- I then applied a Clear Coat to seal everything in.
Step 3: Put the Mirror Together
Once the frame is dried I cut the mirror to fit inside the grooves I cut with the router. Now we need to assemble the components.
- I cut a strip of black vinyl to blackout the part of the mirror that is not part of the Monitor where the Camera will be sitting.
- I cut a hole in the vinyl where the camera will go.
- After testing the motion tracking the camera could not see very will through the mirror so I used some acetone and a Q-tip to remove the reflective coating around the hole.
- Place the LCD panel and its controls into the Mirror.
- I cut a crossbar for the top to hold the LCD panel in. I used pocket screws to hold it into place.
- I used adhesive Velcro and masking tape to hold the Pi, camera, and cables down.
Step 4: The Code
Here is the code for ghost_2.0.py.
Note that python is a very specific TAB syntax you will need to use the included file.
import picamera.array
import picamera
import cv2
import numpy as np
import os
from imutils.object_detection import non_max_suppression
from imutils import paths
import imutils
import time
import pygame
from pygame.locals import *
import sys
import random
### ESC key to Exit Program ###
def event_handler():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
### The Main Loop ###
def main():
# setup for Camera and OpenCV
### Start the Camera ###
camera = picamera.PiCamera()
stream = picamera.array.PiRGBArray(camera, size=(640, 480))
### set the resolution I used 640x480 so I could use 36 Pictures ###
camera.resolution = (640, 480)
### Set frame rate at 16 to reduce the stress on the Raspberry Pi ###
camera.framerate = 16
### set the inital values of the tracking variables ###
motionCounter = 0
avg = None
cntSleep = 0
imgNum = 1
# setup for Pygame
### import the Ghost images. You can change the info here. ###
### if you have have a differnt number of Pictues change the xrange ###
Ghost = list([pygame.image.load('Ghost/Ghost_{0}.jpg'.format(i)) for i in xrange(1,36)])
CLOCK = pygame.time.Clock()
### This is to setup the Gui-less display ###
driver = 'fbcon'
os.putenv('SDL_VIDEODRIVER', driver)
### initialize pygame ###
pygame.display.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
### display the size of the screen ###
print("Framebuffer size: %d x %d" % (size[0], size[1]))
### Turn off the Mouse ###
pygame.mouse.set_visible(0)
### set the Dsiplay to Fullscreen ###
DS = pygame.display.set_mode(size, pygame.FULLSCREEN)
### Initially fill the sceen Black ###
DS.fill((0,0,0))
pygame.display.update()
#main while loop
while cv2.waitKey(1) != 27:
### Capture the stream from the camera ###
for f in camera.capture_continuous(stream, format='bgr', use_video_port=True):
### Check for ESC ###
event_handler()
### Capture the image and compare it to the previous image ###
imgOriginal = f.array stream.truncate(0)
imgOriginal = imutils.resize(imgOriginal, width=500)
imgGray = cv2.cvtColor(imgOriginal, cv2.COLOR_BGR2GRAY)
imgGray = cv2.GaussianBlur(imgGray, (21, 21), 0)
if avg is None:
avg = imgGray.copy().astype("float")
stream.truncate(0)
continue
cv2.accumulateWeighted(imgGray, avg, 0.5)
frameDelta = cv2.absdiff(imgGray, cv2.convertScaleAbs(avg))
thresh = cv2.threshold(frameDelta, 5, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
### Motion Tracking ###
(_, cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
if cv2.contourArea(c) < 5000:
continue
### The bounds of the Changed area ####
(x, y, w, h) = cv2.boundingRect(c)
if x > 0:
### Take the x cordinate add half of the Width ###
### This gets the center of the motion on the x-axis ###
### Divde by 18 and add 1 to get the Picture number ###
### Int() make it a whole number Round() rounds down ###
z = int(round((x + (w/2))/18)) + 1
### Rather than allow the Ghost to jump from center ###
### we have it cycle through till we hit new center ###
while imgNum <> z:
### If it is new motion just diplay the fist ###
if imgNum == 0:
imgNum = z
### Debug ###
#print("Picture Start = Ghost_" + str(imgNum) + ".jpg")
DS.blit(Ghost[imgNum], (336, 0))
### If its to the right go up 1 image ###
elif z > imgNum:
imgNum += 1
### Debug ###
#print("Picture + Ghost_" + str(imgNum)+ ".jpg")
DS.blit(Ghost[imgNum], (336, 0))
### if its to the left go down 1 image ###
else:
imgNum -= 1
### Debug ###
#print("Picture - Ghost_" + str(imgNum) + ".jpg")
DS.blit(Ghost[imgNum], (336, 0))
pygame.display.update()
### Debug ###
#print("Picture = " + str(z) + ", x = " + str(x) + ", w = " + str(w))
cntSleep = 0
continue
### Make the Ghost Disapear after 10 rounds with no Movement###
if cntSleep > 10:
### Debug ###
#print("Black Screen")
DS.fill((0,0,0))
pygame.display.update()
### Set the next movement to the First movement ###
imgNum = 0
### Debug ###
#print("Sleep = " + str(cntSleep))
cntSleep += 1
### Debug ####
#timerCnt += 1
#print("Timer = " + str(timerCnt))
#if timerCnt >= 100:
# quit()
return
if __name__ == "__main__": main()
Attachments
Step 5: The Ghost
You now need to capture your subject.
- Apply makeup to your subject. I chose a White ghoul make up scheme hoping it would show up better behind the mirror.
- I took a bunch of picture while the subject was looking slowly from left to right. I ended up using 36 pictures.
- If your black background is not even like mine there will be some post production. I used Pixlr and Gimp to blackout the background.
- You will want the images turned 90 degrees to the left so it they will be right side up in the frame.
- Name the images Ghost_1.jpg to Ghost_36.jpg looking to the right (1) to the left side (36) of the frame.
Step 6: Installing the Software
I am assuming you now how to get Raspbian onto the Raspberry Pi so I will not be covering it in the tutorial. If you need help you can use this tutorial. I used the lite version to avoid the overhead of the gui. Used the following software:
- python 2 - already installed
- picamera
- OpenCV
- pygame - should already be installed in Wheezy
Before we get to installing the software we need to enable a few items in the raspi-config.
sudo raspi-config
- Item 5 - Enable the PiCamera module.
- Item 7 - Enable Overclocking.
- This will come in handy when installing OpenCV.
I used the tutorial by Adrian at PyImagesearch to install OpenCV. I did skip the virtual environments because I could not get it working with sudo which is required to display images without a GUI. I also skipped the Python 3 steps because everything worked with the python already installed within Raspbian. You know it worked if you can so the following without an error.
sudo python
import cv2
Once you get OpenCV installed and working you need to install the python-pycamera module.
sudo apt-get update
sudo apt-get install python-picamera
The python module for pygame should already be installed you can test that everything is working together with following commands. If you dont get any errors then everything is working.
sudo python
import cv2
import picamera
import pygame
If pygame errors out try the following commands.
sudo apt-get update
sudo apt-get install python-pygame
Step 7: Setup and Copy the Files
You will need to copy the files and setup the folders.
- Copy python files to /home/pi/ on the Pi. Files Needed
- ghost_2.0.py
- Make the folder for the Images
- cd ~
- mkdir Ghost
- Copy the images from another computer to the /home/pi/Ghost folder
- Test the script and make sure it runs.
- sudo python ghost_2.0.py
- If the file works then create the launcher script to start on boot up. I used this tutorial.
Thank you for Viewing this tutorial. Have fun creating.

Participated in the
Halloween Decor Contest 2016
2 Comments
Question 1 year ago
This is so awesome! Could I pay you to make this for me or at least do the computer part? Let me know!
Thanks.
6 years ago
Pretty cook,