Initializing the Addressable LEDs
Before doing any programming, one important step of the process is to initialize the the WS2812B as they will turn on each time power is connect to the Raspberry Pi. The easiest thing to do here is to write a simple Python program that will be executed every time the Pi boots, initializing (turning off) the Addressable RGB LEDs without requiring a manual script run.
The rc utility is the command script which controls the automatic boot process, rc.local is traditionally executed after all the normal system services are started, this is the perfect place to add the call to the initialization script. The rc.local file should be edited with root, for example:
sudo nano /etc/rc.local
sudo vi /etc/rc.local
Then I added the commands below before exit 0 at the end of rc.local:
file="/home/pi/picasso/init_ws2812b.py" if [ -f "$file" ] then python3 $file fi
Initializing the WS2812B LEDs with Python
The Addressable LEDs could be initialized turning them off -or in other words lighting them up "black" RGB (0, 0, 0). Below Python script does the trick:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Turns Off (initializes) the WS2812B Addressable LEDs # # Luis Ortiz - luislab.com # May 19, 2019 import board import neopixel import subprocess numLEDs = 5 ledStrip = neopixel.NeoPixel(board.D18, numLEDs, brightness=0.2, auto_write=True) ledPos = 0 ledStrip.fill((0, 0, 0))
I added the script above to a new file /home/pi/picasso/init_ws2812b.py, then assigned execution permissions with the command chmod u+x /home/pi/picasso/init_ws2812b.py, and that is it!, the script will run every time the Pi is restarted turning off the addressable RGB leds.
The Holograms can be anything: a static Image, an animated GIF, a video file or a complex made animation. In my case I chose to make few hologram videos in mp4 format, which I will be reproducing in a loop with OpenCV, a widely-used library for video and image processing.
The way OpenCV works when playing video is by reading each frame of the video one-at-a-time and displaying each frame as an image in sequence.
To use use OpenCV library on Python, it needs to be installed along with some dependencies with the following commands (no particular order is required):
sudo pip3 install opencv-python sudo apt-get install libcblas-dev sudo apt-get install libatlas-base-dev sudo apt-get install libjasper-dev sudo apt-get install libqtgui4 sudo apt-get install libqt4-test
Playing video in Python
Below Python script will play a video in a loop, full-screen mode without any visible window or borders and with a safe way to end the script (ESC key)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Tests WS2812B Addressable LED with basic color pattern and some random RGB # # Luis Ortiz - luislab.com # May 19, 2019 import cv2 print("OpenCV", cv2.__version__) vidFile = "Element14.mp4" winName = "Element14" vid = cv2.VideoCapture(vidFile) if not vid.isOpened(): print("Error: Could not open ", vidFile) exit() vidFPS = vid.get(cv2.CAP_PROP_FPS) waitPerFrame = int(1000 / vidFPS) # in milliseconds print("fps:", vidFPS) print("Time between frames:", waitPerFrame, "ms") cv2.namedWindow(winName, cv2.WND_PROP_FULLSCREEN) cv2.setWindowProperty(winName, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) try: while True: retVal, frame = vid.read() if retVal: cv2.imshow(winName, frame) else: vid.set(cv2.CAP_PROP_POS_FRAMES, 0) #End of video if cv2.waitKey(waitPerFrame) == 27: # ESC to quit print("Aborted at frame:", vid.get(cv2.CAP_PROP_POS_FRAMES)) break finally: vid.release() cv2.destroyAllWindows() print("Bye!")
Webcam motion detection
On a fresh Raspbian installation is necessary to enable the camera support using sudo raspi-config, then with the cursor keys navigating to the options Interfacing Options > Camera > Yes > Finish to activate the camera and finally it will ask to reboot.
raspistill -v -o webcamtest.jpg
Then I verified that the image was created by looking at the files it the current directory.
Detecting motion in Python
Once more we will be using OpenCV, this time for motion detection. Basically this script captures from the webcam a still image at regular intervals and does a series of image filtering to convert it to gray scale, removes some noise, and then compares the difference between two consecutive frames with a given threshold, a difference greater than such threshold will be considered as motion detection.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Webcam motion detection # # Luis Ortiz - luislab.com # May 19,2019 import cv2 #import numpy as np from datetime import datetime def resizeImg(image, width = 0, height = 0, inter=cv2.INTER_AREA): if width <= 0 and height <= 0: return image (oldH, oldW) = image.shape[:2] if height > 0: ratio = height / float(oldH) newDim = (int(oldW * ratio), height) elif width > 0: ratio = width / float(oldW) newDim = (width, int(oldH * ratio)) resizedImg = cv2.resize(image, newDim, interpolation=inter) return resizedImg camera = cv2.VideoCapture(0) ## Webcam capture prevFrame = None frameWidth = camera.get(cv2.CAP_PROP_FRAME_WIDTH) frameHeight = camera.get(cv2.CAP_PROP_FRAME_HEIGHT) print("Camera resolution:", frameWidth, "x", frameHeight) sensivityVal = 5 gaussKern = 21 ## Gaussian kernel minContour = 4000 while (camera.isOpened()): capSuccess, currFrame = camera.read() MOTION = False if currFrame is None: break currFrame = resizeImg(currFrame, width=320) grayFrame = cv2.cvtColor(currFrame, cv2.COLOR_BGR2GRAY) grayFrame = cv2.GaussianBlur(grayFrame, (gaussKern, gaussKern), 0) if prevFrame is None: prevFrame = grayFrame continue # Difference between previous and current frame frameDiff = cv2.absdiff(prevFrame, grayFrame) thresh = cv2.threshold(frameDiff, sensivityVal, 0xff, cv2.THRESH_BINARY) # If changes are greater than sensivityVal it will show white color (FF) dilate = cv2.dilate(thresh, None, iterations=2) # Finds contour of moving object _, cnts, _ = cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in cnts: # Ignores too small contours if cv2.contourArea(c) < minContour: continue MOTION = True (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(currFrame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.imshow("Current frame", currFrame) if cv2.waitKey(100) == 27: print("Aborted") break prevFrame = grayFrame if MOTION: print("Motion", datetime.now()) camera.release() cv2.destroyAllWindows()
For illustration purposes the scrip above displays an image indicating the area where motion was detected, also displays a timestamp when the motion occurred
Displaying Holograms, Finally!
Combining video display, webcam motion detection and addressable LEDs Holograms can be displayed adding awesome effects, since everything is done in Python the video, motion detection and the addressable LED effects can be combined with ease.
Blogs in this series
- Hologram Pi-ramid - Intro and initial design
- Hologram Pi-ramid - 3D CAD/CAM design
- Hologram Pi-ramid - 3D printed parts and initial assembly
- Hologram Pi-ramid - Plexiglass Pyramid
- Hologram Pi-ramid - My name is Automan
- Hologram Pi-ramid - PCB Design
- Hologram Pi-ramid - Painting the 3D printed parts
- Hologram Pi-ramid - Electronic Parts
- Hologram Pi-ramid - Displaying Holograms
- Hologram Pi-ramid - Project complete!