Updated and modifed the code
|
Before Width: | Height: | Size: 67 KiB |
|
|
@ -4,7 +4,7 @@ All contributions are assumed to be licensed under the same licence as the sourc
|
|||
|
||||
## Issues
|
||||
|
||||
If you find a mistake, bug or other problem, please [open an issue](https://github.com/raspberrypilearning/the-all-seeing-pi/issues) in this repository.
|
||||
If you find a mistake, bug or other problem, please [open an issue](https://github.com/mdegrazia/Pi-Photobooth) in this repository.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ Unless otherwise specified, everything in this repository is covered by the foll
|
|||
|
||||
[](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
***The All-Seeing Pi*** by the [Raspberry Pi Foundation](http://www.raspberrypi.org) is licenced under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
***The All-Seeing Pi*** by the [Raspberry Pi Foundation](http://www.raspberrypi.org) is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
Based on a work at https://github.com/raspberrypilearning/the-all-seeing-pi
|
||||
|
||||
***Pi-Photobooth*** by the [Mari DeGrazia](https://anotherpiblog.blogspot.com) is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
Based on a work at https://github.com/raspberrypilearning/the-all-seeing-pi
|
||||
|
|
|
|||
26
README.md
|
|
@ -1,8 +1,26 @@
|
|||
# The All-Seeing Pi
|
||||
# The Pi Photobooth
|
||||
|
||||

|
||||

|
||||
|
||||
The project is based of the All Seeing Pi project by the Raspberry Pi Foundation.
|
||||
|
||||
In general, the changes/additions I made were:
|
||||
|
||||
|
||||
1) Added in the ability to print the photo to a bluetooth printer
|
||||
|
||||
2) Added in the ability to email the photo
|
||||
|
||||
3) Added in support for the Wii Remote which includes the ability to go to previous/next overlay, remove the overlays, take a picture and start a new picture
|
||||
|
||||
4) Added in some sound effects
|
||||
|
||||
5) Added in some graphics to the GUI
|
||||
|
||||
6) Added in some "snap chat" type filters to overlay the pictures (note googly eyes on dog below)
|
||||
|
||||
7) Uses a kill button to stop the Photobooth since it runs full screen
|
||||
|
||||
In this resource, you will make a tweeting touchscreen photo booth using a Raspberry Pi.
|
||||
|
||||
## Licence
|
||||
|
||||
|
|
@ -10,6 +28,6 @@ Unless otherwise specified, everything in this repository is covered by the foll
|
|||
|
||||
[](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
***The All-Seeing Pi*** by the [Raspberry Pi Foundation](http://www.raspberrypi.org) is licenced under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
***The Pi Photobooth*** by the [Mari DeGrazia](http://anotherpiblog.blogspot.com/) is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
Based on a work at https://github.com/raspberrypilearning/the-all-seeing-pi
|
||||
|
|
|
|||
BIN
audio/CameraClick.mp3
Normal file
BIN
audio/mk64_luigi03.wav
Normal file
BIN
audio/mk64_mario09.wav
Normal file
BIN
audio/slide_and_click.mp3
Normal file
|
|
@ -1,32 +0,0 @@
|
|||
from picamera import PiCamera
|
||||
from gpiozero import Button
|
||||
from overlay_functions import *
|
||||
from time import gmtime, strftime
|
||||
|
||||
# Tell the next overlay button what to do
|
||||
def next_overlay():
|
||||
global overlay
|
||||
overlay = next(all_overlays)
|
||||
preview_overlay(camera, overlay)
|
||||
|
||||
# Tell the take picture button what to do
|
||||
def take_picture():
|
||||
camera.capture(output)
|
||||
camera.stop_preview()
|
||||
|
||||
# Set up buttons
|
||||
next_overlay_btn = Button(23)
|
||||
next_overlay_btn.when_pressed = next_overlay
|
||||
take_pic_btn = Button(25)
|
||||
take_pic_btn.when_pressed = take_picture
|
||||
|
||||
# Set up camera (with resolution of the touchscreen)
|
||||
camera = PiCamera()
|
||||
camera.resolution = (800, 480)
|
||||
camera.hflip = True
|
||||
|
||||
# Start camera preview (delete alpha=128 once you know your code works)
|
||||
camera.start_preview(alpha=128)
|
||||
|
||||
# Set up filename
|
||||
output = strftime("/home/pi/allseeingpi/image-%d-%m %H:%M.png", gmtime())
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
from picamera import PiCamera
|
||||
from gpiozero import Button
|
||||
from overlay_functions import *
|
||||
from time import gmtime, strftime
|
||||
from guizero import App, PushButton, Text, Picture
|
||||
from twython import Twython
|
||||
from auth import (
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
access_token,
|
||||
access_token_secret
|
||||
)
|
||||
|
||||
# Tell the next overlay button what to do
|
||||
def next_overlay():
|
||||
global overlay
|
||||
overlay = next(all_overlays)
|
||||
preview_overlay(camera, overlay)
|
||||
|
||||
# Tell the take picture button what to do
|
||||
def take_picture():
|
||||
global output
|
||||
output = strftime("/home/pi/allseeingpi/image-%d-%m %H:%M.png", gmtime())
|
||||
camera.capture(output)
|
||||
camera.stop_preview()
|
||||
remove_overlays(camera)
|
||||
output_overlay(output, overlay)
|
||||
|
||||
# Save a smaller gif
|
||||
size = 400, 400
|
||||
gif_img = Image.open(output)
|
||||
gif_img.thumbnail(size, Image.ANTIALIAS)
|
||||
gif_img.save(latest_photo, 'gif')
|
||||
|
||||
# Set the gui picture to this picture
|
||||
your_pic.set(latest_photo)
|
||||
|
||||
|
||||
def new_picture():
|
||||
camera.start_preview(alpha=128)
|
||||
preview_overlay(camera, overlay)
|
||||
|
||||
|
||||
def send_tweet():
|
||||
twitter = Twython(
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
access_token,
|
||||
access_token_secret
|
||||
)
|
||||
|
||||
# Send the tweet
|
||||
message = "The All Seeing Pi saw you!"
|
||||
with open(output, 'rb') as photo:
|
||||
twitter.update_status_with_media(status=message, media=photo)
|
||||
|
||||
# Set up buttons
|
||||
next_overlay_btn = Button(23)
|
||||
next_overlay_btn.when_pressed = next_overlay
|
||||
take_pic_btn = Button(25)
|
||||
take_pic_btn.when_pressed = take_picture
|
||||
|
||||
# Set up camera (with resolution of the touchscreen)
|
||||
camera = PiCamera()
|
||||
camera.resolution = (1024, 768)
|
||||
camera.hflip = True
|
||||
|
||||
# Start camera preview
|
||||
camera.start_preview(alpha=128)
|
||||
|
||||
# Set up filename
|
||||
output = ""
|
||||
|
||||
latest_photo = '/home/pi/allseeingpi/latest.gif'
|
||||
|
||||
app = App("The All Seeing Pi", 800, 480)
|
||||
#app.attributes("-fullscreen", True)
|
||||
message = Text(app, "I spotted you!")
|
||||
your_pic = Picture(app, latest_photo)
|
||||
new_pic = PushButton(app, new_picture, text="New picture")
|
||||
tweet_pic = PushButton(app, send_tweet, text="Tweet picture")
|
||||
app.display()
|
||||
BIN
cover.png
|
Before Width: | Height: | Size: 61 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
You'll also need:
|
||||
|
||||
- A cardboard box (optional)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# Hardware Requirements
|
||||
|
||||
If you want to use larger buttons, you could try these [arcade buttons](https://www.modmypi.com/raspberry-pi/arcade/arcade-buttons/arcade-button-30mm-translucent-red/?search=buttons&limit=100) and accompanying [quick-connect wires](https://www.modmypi.com/raspberry-pi/arcade/arcade-wiring/arcade-button-quick-connect-wires-set-of-10-pairs).
|
||||
12
hardware.yml
|
|
@ -1,12 +0,0 @@
|
|||
- name: Raspberry Pi Camera Module
|
||||
img: camera
|
||||
- name: Raspberry Pi touchscreen display or standard monitor
|
||||
img: monitor
|
||||
- name: 2 x tactile push buttons
|
||||
img: tactile-push-button
|
||||
- name: Breadboard
|
||||
img: breadboard
|
||||
- name: 4 x Male-female jumper leads
|
||||
img: jumper-male-to-female
|
||||
- name: 2 x large buttons (optional, to replace tactile push buttons)
|
||||
img: big-dome-push-button
|
||||
|
Before Width: | Height: | Size: 872 KiB |
|
Before Width: | Height: | Size: 609 KiB |
BIN
images/button_email.gif
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
images/button_new.gif
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
images/button_print.gif
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 512 KiB |
292
images/keyboard.xml
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<keyboard>
|
||||
|
||||
<layout id="default keyboard">
|
||||
|
||||
<row>
|
||||
|
||||
<key width="1300">
|
||||
<default display="image:/usr/local/share/matchbox-keyboard/arrows.png" action="tab" />
|
||||
</key>
|
||||
|
||||
<space width="50" extended="true"/>
|
||||
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="q" />
|
||||
<shifted display="Q" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="w" />
|
||||
<shifted display="W" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="e" />
|
||||
<shifted display="E" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="r" />
|
||||
<shifted display="R" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="t" />
|
||||
<shifted display="T" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="y" />
|
||||
<shifted display="Y" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="u" />
|
||||
<shifted display="U" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="i" />
|
||||
<shifted display="I" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="o" />
|
||||
<shifted display="O" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="p" />
|
||||
<shifted display="P" />
|
||||
|
||||
</key>
|
||||
<key width="1000">
|
||||
<default display="image:/usr/local/share/matchbox-keyboard/key-backspace.png" action="backspace" />
|
||||
</key>
|
||||
|
||||
<space width="50" extended="true"/>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="7" />
|
||||
<shifted display="&" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="8" />
|
||||
<shifted display="*" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="9" />
|
||||
<shifted display="image:/usr/local/share/matchbox-keyboard/degree.png" action="°" />
|
||||
</key>
|
||||
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
||||
<space width="5" extended="true"/>
|
||||
<key width="1500">
|
||||
<default display="image:/usr/local/share/matchbox-keyboard/shift.png" action="modifier:shift" />
|
||||
<caps display="image:/usr/local/share/matchbox-keyboard/shift_caps.png" action="modifier:shift" />
|
||||
</key>
|
||||
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="a" />
|
||||
<shifted display="A" />
|
||||
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="s" />
|
||||
<shifted display="S" />
|
||||
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="d" />
|
||||
<shifted display="D" />
|
||||
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="f" />
|
||||
<shifted display="F" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="g" />
|
||||
<shifted display="G" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="h" />
|
||||
<shifted display="H" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="j" />
|
||||
<shifted display="J" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="k" />
|
||||
<shifted display="K" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="l" />
|
||||
<shifted display="L" />
|
||||
</key>
|
||||
<key width="2150">
|
||||
<default display="image:/usr/local/share/matchbox-keyboard/return.png" action="return"/>
|
||||
</key>
|
||||
|
||||
<space width="50" extended="true"/>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="4" />
|
||||
<shifted display="$" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="5" />
|
||||
<shifted display="%" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="6" />
|
||||
<shifted display="^" />
|
||||
</key>
|
||||
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
||||
|
||||
<space width="1500" extended="true"/>
|
||||
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="z" />
|
||||
<shifted display="Z" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="x" />
|
||||
<shifted display="X" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="c" />
|
||||
<shifted display="C" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="v" />
|
||||
<shifted display="V" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="b" />
|
||||
<shifted display="B" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="n" />
|
||||
<shifted display="N" />
|
||||
</key>
|
||||
<key obey-caps='true' fill='false'>
|
||||
<default display="m" />
|
||||
<shifted display="M" />
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display="," />
|
||||
<shifted display="<" action="xkeysym:less"/>
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display="." />
|
||||
<shifted display=">" action="xkeysym:greater"/>
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="'" />
|
||||
<shifted display='"' />
|
||||
</key>
|
||||
|
||||
<space width="200" extended="true"/>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="1" />
|
||||
<shifted display="!" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="2" />
|
||||
<shifted display="@" />
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="3" />
|
||||
<shifted display="#" />
|
||||
</key>
|
||||
|
||||
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
||||
|
||||
|
||||
<key fill='false'>
|
||||
<default display="(" />
|
||||
<shifted display="{" />
|
||||
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display=")" />
|
||||
<shifted display="}" />
|
||||
</key>
|
||||
|
||||
<space width="200" extended="true"/>
|
||||
|
||||
<key width="5000">
|
||||
<default display=" " action="space" />
|
||||
</key>
|
||||
|
||||
<space width="1100" extended="true"/>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="@"/>
|
||||
<shifed display="@"/>
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="\" />
|
||||
<shifted display="|" />
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display="/" />
|
||||
<shifted display="?" />
|
||||
<mod1 display="¿" />
|
||||
<mod2 display="¡" />
|
||||
</key>
|
||||
|
||||
|
||||
<key fill='false'>
|
||||
<default display="." />
|
||||
<shifted display="√" />
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display=";" />
|
||||
<shifted display=":" />
|
||||
</key>
|
||||
|
||||
<space width="100" extended="true"/>
|
||||
<key fill='false'>
|
||||
<default display="-" />
|
||||
<shifted display="_" />
|
||||
</key>
|
||||
<key fill='false'>
|
||||
<default display="+" />
|
||||
<shifted display="±" />
|
||||
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="0" />
|
||||
<shifted display="∞"/>
|
||||
|
||||
</key>
|
||||
|
||||
<key fill='false'>
|
||||
<default display="=" />
|
||||
<shifted display="≠" />
|
||||
<mod1 display="~" />
|
||||
<mod2 display="→" />
|
||||
</key>
|
||||
|
||||
|
||||
</row>
|
||||
|
||||
</layout>
|
||||
|
||||
</keyboard>
|
||||
BIN
images/latest.gif
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
images/loading.gif
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
images/photo-booth.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
images/photo_booth_background.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 525 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 883 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 387 KiB |
15
learn.md
|
|
@ -1,15 +0,0 @@
|
|||
By creating the All-Seeing Pi with your Raspberry Pi, you will learn:
|
||||
|
||||
- How to set up a Raspberry Pi Camera Module
|
||||
- How to connect buttons and a touchscreen display
|
||||
- How to control GPIO pins with Python code
|
||||
- How to control the Camera Module with Python code
|
||||
- How to tweet a picture taken with the Camera Module
|
||||
|
||||
This resource covers elements from the following strands of the [Raspberry Pi Digital Making Curriculum](https://www.raspberrypi.org/curriculum/):
|
||||
|
||||
- [Design multiple and integrating assets for use in complex finished projects and models](https://www.raspberrypi.org/curriculum/design/maker)
|
||||
- [Apply higher-order programming techniques to solve real-world problems](https://www.raspberrypi.org/curriculum/programming/maker)
|
||||
- [Create automated systems to solve complex real-world problems](https://www.raspberrypi.org/curriculum/physical-computing/maker)
|
||||
- [Independently use fabrication systems to produce complex finished projects](https://www.raspberrypi.org/curriculum/manufacture/maker)
|
||||
- [Engage and share with the digital making community](https://www.raspberrypi.org/curriculum/community-and-sharing/creator)
|
||||
2
meta.yml
|
|
@ -1,2 +0,0 @@
|
|||
title: The All-Seeing Pi
|
||||
category: make
|
||||
28
monitor_gpio_buttons.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#Written by Mari DeGrazia
|
||||
#arizona4n6@gmail.com
|
||||
#This script will monitor a button on GPIO 13
|
||||
#When pressed it will kill the photobooth python script
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
import time
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)
|
||||
|
||||
while True:
|
||||
input_state = GPIO.input(13)
|
||||
if input_state == False:
|
||||
time.sleep(0.3)
|
||||
cmd = "sudo pkill -f photobooth.py"
|
||||
k = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
message = k.communicate(input)
|
||||
print message[0]
|
||||
|
||||
|
||||
cmd = "sudo pkill -f photobooth_wii.py"
|
||||
k = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
message = k.communicate(input)
|
||||
print message[0]
|
||||
|
||||
|
|
@ -2,14 +2,14 @@
|
|||
# -------------------------------------------------------------
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
from itertools import cycle
|
||||
|
||||
# EDIT THESE VALUES ------------------------
|
||||
overlays_dir = "/home/pi/allseeingpi/overlays"
|
||||
overlays = ['girl', 'cowboy', 'top', 'pink', 'glassesnose', 'moustache', 'sunglasses', 'elvis', 'emo', 'blackhat', 'emo2', 'baseball', 'flowers', 'santa', 'alps', 'mop', 'glasses']
|
||||
overlays_dir = "/home/pi/Pi-Photobooth/overlays"
|
||||
overlays = ['bald_head','bunny1','bunny2','dog','monster','unicorn','mouth','ears','dog_face','eyes','girl', 'cowboy', 'top', 'pink', 'glassesnose', 'wings', 'moustache', 'angel','sunglasses', 'elvis', 'emo', 'blackhat_sm', 'emo2', 'baseball_sm', 'flowers', 'santa_sm', 'alps_sm', 'mop', 'glasses',""]
|
||||
# ------------------------------------------
|
||||
|
||||
|
||||
overlay = overlays[0] # Starting value
|
||||
|
||||
def _get_overlay_image(overlay):
|
||||
|
|
@ -44,21 +44,27 @@ def preview_overlay(camera=None, overlay=None):
|
|||
|
||||
# Pad it to the right resolution
|
||||
pad = Image.new('RGB', _pad(camera.resolution))
|
||||
pad.paste(overlay_img, (0, 0))
|
||||
pad.paste(overlay_img, (0, 0),overlay_img)
|
||||
|
||||
# Add the overlay
|
||||
camera.add_overlay(pad.tobytes(), alpha=128, layer=3)
|
||||
camera.add_overlay(pad.tobytes(),alpha=150, layer=3)
|
||||
|
||||
def output_overlay(output=None, overlay=None):
|
||||
|
||||
# Take an overlay Image
|
||||
|
||||
# Take an overlay Image
|
||||
overlay_img = _get_overlay_image(overlay)
|
||||
|
||||
background = Image.open(output)
|
||||
# ...and a captured photo
|
||||
output_img = Image.open(output).convert('RGBA')
|
||||
|
||||
#output_img = Image.open(output).convert('RGBA')
|
||||
overlay_img = ImageOps.mirror(overlay_img)
|
||||
# Combine the two and save the image as output
|
||||
new_output = Image.alpha_composite(output_img, overlay_img)
|
||||
new_output.save(output)
|
||||
background.paste(overlay_img,(0,0),overlay_img)
|
||||
background.save(output)
|
||||
|
||||
|
||||
def output_no_overlay(output=None):
|
||||
background = Image.open(output)
|
||||
background.save(output)
|
||||
|
||||
all_overlays = cycle(overlays)
|
||||
BIN
overlays/alps_sm.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
overlays/angel.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
overlays/bald_head.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
overlays/baseball_sm.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
overlays/blackhat_sm.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
overlays/bunny1.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
overlays/bunny2.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
overlays/dog.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
overlays/dog_face.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
overlays/ears.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
overlays/eyes.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
overlays/monster.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
overlays/mouth.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
overlays/santa_sm.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
overlays/unicorn.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
overlays/wings.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
|
|
@ -1 +0,0 @@
|
|||
In this resource, you will make a tweeting touchscreen photo booth using a Raspberry Pi.
|
||||
308
photobooth.py
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#!/usr/local/bin/python
|
||||
#Written by Mari DeGrazia
|
||||
#arizona4n6@gmail.com
|
||||
#Based off the All Seeing Pi by the Rasbperry Pi Foundation
|
||||
|
||||
from gpiozero import Button
|
||||
from picamera import PiCamera
|
||||
from time import gmtime, strftime,sleep
|
||||
from overlay_functions import *
|
||||
from shutil import copyfile
|
||||
import subprocess
|
||||
import Tkinter
|
||||
import tkMessageBox
|
||||
import ttk
|
||||
from PIL import Image, ImageTk
|
||||
import pygame
|
||||
from smtplib import SMTP
|
||||
from smtplib import SMTPException
|
||||
import smtplib
|
||||
from email.mime.image import MIMEImage
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
import time
|
||||
from multiprocessing import Process
|
||||
import cwiid
|
||||
|
||||
###############CHANGE ME###########################
|
||||
printer_MAC = "C4:30:00:00:7E:41"
|
||||
my_email = 'myemail@gmail.com' # must be gmail account.
|
||||
my_email_password = 'strongpassword'
|
||||
subject = 'Graduation 2017'
|
||||
####################################################
|
||||
|
||||
def start_wii_script():
|
||||
cmd = "sudo python /home/pi/Pi-Photobooth/photobooth_wii.py &"
|
||||
k = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
message = k.communicate(input)
|
||||
print message[0]
|
||||
|
||||
def kill_keyboard():
|
||||
cmd = "sudo pkill -f matchbox-keyboard"
|
||||
k = subprocess.Popen(cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
message = k.communicate(input)
|
||||
return
|
||||
|
||||
def next_overlay():
|
||||
global overlay
|
||||
global current_position
|
||||
pygame.mixer.music.load("/home/pi/Pi-Photobooth/audio/slide_and_click.mp3")
|
||||
pygame.mixer.music.play()
|
||||
current_position = current_position + 1
|
||||
if current_position >= len(overlays):
|
||||
current_position = 0
|
||||
overlay = overlays[current_position]
|
||||
if overlay == "":
|
||||
remove_overlays(camera)
|
||||
else:
|
||||
preview_overlay(camera, overlay)
|
||||
|
||||
def prev_overlay():
|
||||
global overlay
|
||||
global current_position
|
||||
pygame.mixer.music.load("/home/pi/Pi-Photobooth/audio/slide_and_click.mp3")
|
||||
pygame.mixer.music.play()
|
||||
|
||||
current_position = current_position - 1
|
||||
overlay = overlays[current_position]
|
||||
if overlay == "":
|
||||
remove_overlays(camera)
|
||||
else:
|
||||
preview_overlay(camera, overlay)
|
||||
|
||||
def take_picture():
|
||||
global output
|
||||
global latest_photo
|
||||
global current_position
|
||||
global overlay
|
||||
|
||||
pygame.mixer.music.load("/home/pi/Pi-Photobooth/audio/CameraClick.mp3")
|
||||
pygame.mixer.music.play()
|
||||
output = strftime("/home/pi/Pi-Photobooth/photos/image-%d-%m_%H_%M_%S.png", gmtime())
|
||||
time.sleep(.3)
|
||||
camera.stop_preview()
|
||||
|
||||
remove_overlays(camera)
|
||||
camera.hflip = False
|
||||
camera.capture(output)
|
||||
|
||||
if overlay:
|
||||
output_overlay(output, overlays[current_position])
|
||||
else:
|
||||
output_no_overlay(output)
|
||||
size = 400, 400
|
||||
gif_img = Image.open(output)
|
||||
gif_img.thumbnail(size, Image.ANTIALIAS)
|
||||
|
||||
gif_img.save(latest_photo, 'gif')
|
||||
loadImage(latest_photo)
|
||||
camera.hflip = True
|
||||
just_taken = True
|
||||
|
||||
def new_picture():
|
||||
global overlay
|
||||
kill_keyboard()
|
||||
camera.start_preview()
|
||||
time.sleep(1)
|
||||
copyfile('/home/pi/Pi-Photobooth/images/loading.gif', '/home/pi/Pi-Photobooth/images/latest.gif')
|
||||
remove_overlays(camera)
|
||||
current_position = 0
|
||||
loadImage(latest_photo)
|
||||
overlay = ""
|
||||
|
||||
def remove():
|
||||
global overlay
|
||||
remove_overlays(camera)
|
||||
overlay = ""
|
||||
|
||||
def print_photo():
|
||||
global printer_MAC
|
||||
kill_keyboard()
|
||||
|
||||
top = Tkinter.Tk()
|
||||
top.title("Printing")
|
||||
msg = Tkinter.Label(top, text="Sending to printer. Please wait 1-2 minutes.",width=50,background='#B1B1B1')
|
||||
msg.pack()
|
||||
top.geometry("%dx%d%+d%+d" % (400, 100, 250, 125))
|
||||
top.configure(background='#B1B1B1')
|
||||
center(top)
|
||||
|
||||
master.config(cursor="watch")
|
||||
top.config(cursor="watch")
|
||||
master.update()
|
||||
top.update()
|
||||
|
||||
print ("Print photo")
|
||||
pp = subprocess.Popen(["obexftp --nopath --noconn --uuid none --bluetooth " + printer_MAC + " --channel 4 -p " + output],shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
message = pp.communicate(input)
|
||||
master.config(cursor="")
|
||||
top.destroy()
|
||||
msg = "failed"
|
||||
if msg.encode('utf-8') in message[0]:
|
||||
tkMessageBox.showerror("Error", "Print failed. Check paper or make sure printer is on and paired and print again")
|
||||
|
||||
else:
|
||||
tkMessageBox.showinfo("Printing", "Photo successfully sent. Now printing...")
|
||||
|
||||
return True
|
||||
|
||||
def get_email_address():
|
||||
|
||||
top = Tkinter.Tk()
|
||||
top.title("Email")
|
||||
msg = Tkinter.Label(top, text="Enter Email Address.",width=50,background='#B1B1B1')
|
||||
msg.pack()
|
||||
ttk.e = Tkinter.Entry(top,width=50)
|
||||
ttk.e.pack()
|
||||
|
||||
b4 = Tkinter.Button(top, text='Send', command=lambda: send_picture(top))
|
||||
b4.pack()
|
||||
|
||||
top.geometry("%dx%d%+d%+d" % (400, 100, 250, 125))
|
||||
top.configure(background='#B1B1B1')
|
||||
lower(top)
|
||||
subprocess.Popen(['matchbox-keyboard'])
|
||||
return
|
||||
|
||||
def send_picture(toplevel):
|
||||
global output
|
||||
global my_email
|
||||
global my_email_password
|
||||
global subject
|
||||
master.config(cursor="watch")
|
||||
master.config(cursor="watch")
|
||||
|
||||
#kill keyboard if its still there
|
||||
email_address = ttk.e.get()
|
||||
toplevel.destroy()
|
||||
kill_keyboard()
|
||||
print email_address
|
||||
|
||||
toaddr = email_address
|
||||
me = my_email # redacted
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = my_email
|
||||
msg['To'] = toaddr
|
||||
msg.preamble = "Photo @ "
|
||||
|
||||
fp = open(output, 'rb')
|
||||
img = MIMEImage(fp.read())
|
||||
fp.close()
|
||||
msg.attach(img)
|
||||
|
||||
try:
|
||||
s = smtplib.SMTP('smtp.gmail.com',587)
|
||||
s.ehlo()
|
||||
s.starttls()
|
||||
s.ehlo()
|
||||
s.login(user = my_email,password = my_email_password)
|
||||
|
||||
s.sendmail(me, toaddr, msg.as_string())
|
||||
s.quit()
|
||||
master.config(cursor="")
|
||||
tkMessageBox.showinfo("Info", "Email sent")
|
||||
|
||||
except SMTPException as error:
|
||||
master.config(cursor="")
|
||||
tkMessageBox.showerror("Error", "Error: unable to send email : {err}".format(err=error))
|
||||
print "Error: unable to send email : {err}".format(err=error)
|
||||
|
||||
def center(toplevel):
|
||||
toplevel.update_idletasks()
|
||||
w = toplevel.winfo_screenwidth()
|
||||
h = toplevel.winfo_screenheight()
|
||||
size = tuple(int(_) for _ in toplevel.geometry().split('+')[0].split('x'))
|
||||
x = w/2 - size[0]/2
|
||||
y = h/2 - size[1]/2
|
||||
|
||||
toplevel.geometry("%dx%d+%d+%d" % (size + (x, y)))
|
||||
|
||||
def lower(toplevel):
|
||||
toplevel.update_idletasks()
|
||||
w = toplevel.winfo_screenwidth()
|
||||
h = toplevel.winfo_screenheight()
|
||||
size = tuple(int(_) for _ in toplevel.geometry().split('+')[0].split('x'))
|
||||
x = w/2 - size[0]/2
|
||||
y = h/2 - size[1]/2
|
||||
|
||||
toplevel.geometry("%dx%d+%d+%d" % (size + (x, y+150)))
|
||||
|
||||
|
||||
def loadImage(latest_photo):
|
||||
global picture3
|
||||
picture3 = Tkinter.PhotoImage(file=latest_photo)
|
||||
c.itemconfigure(picture2, image = picture3)
|
||||
|
||||
|
||||
current_position = 0
|
||||
pygame.mixer.init()
|
||||
|
||||
|
||||
#reset image
|
||||
copyfile('/home/pi/Pi-Photobooth/images/loading.gif', '/home/pi/Pi-Photobooth/images/latest.gif')
|
||||
|
||||
|
||||
overlay = ""
|
||||
next_overlay_btn = Button(23)
|
||||
take_pic_btn = Button(11)
|
||||
|
||||
|
||||
next_overlay_btn.when_pressed = next_overlay
|
||||
take_pic_btn.when_pressed = take_picture
|
||||
|
||||
camera = PiCamera()
|
||||
camera.resolution = (800, 480)
|
||||
camera.hflip = True
|
||||
|
||||
|
||||
camera.start_preview()
|
||||
output = ""
|
||||
latest_photo = '/home/pi/Pi-Photobooth/images/latest.gif'
|
||||
|
||||
p = Process(target=start_wii_script)
|
||||
p.start()
|
||||
|
||||
|
||||
next_overlay_Wii_button = Button(17)
|
||||
picture_Wii_button = Button(9)
|
||||
prev_overlay_Wii_Button = Button(21)
|
||||
newpic_Wii_button = Button(20)
|
||||
rem_overlays_Wii_button = Button(16)
|
||||
|
||||
newpic_Wii_button.when_pressed = new_picture
|
||||
next_overlay_Wii_button.when_pressed = next_overlay
|
||||
prev_overlay_Wii_Button.when_pressed = prev_overlay
|
||||
picture_Wii_button.when_pressed = take_picture
|
||||
|
||||
|
||||
rem_overlays_Wii_button.when_pressed = remove
|
||||
master = Tkinter.Tk()
|
||||
master.wm_title(subject)
|
||||
master.attributes("-fullscreen", True)
|
||||
c = Tkinter.Canvas(master, width=400, height=300)
|
||||
|
||||
picture = Tkinter.PhotoImage(file=latest_photo)
|
||||
picture2 = c.create_image(200,150,image=picture)
|
||||
c.pack()
|
||||
|
||||
photo1 = Tkinter.PhotoImage(file='/home/pi/Pi-Photobooth/images/button_new.gif')
|
||||
photo2 = Tkinter.PhotoImage(file='/home/pi/Pi-Photobooth/images/button_print.gif')
|
||||
photo3 = Tkinter.PhotoImage(file='/home/pi/Pi-Photobooth/images/button_email.gif')
|
||||
|
||||
b1 = Tkinter.Button(master, text="New Picture",command=new_picture,image=photo1)
|
||||
b2 = Tkinter.Button(master, text="Print Picture",command=print_photo,image=photo2)
|
||||
b3= Tkinter.Button(master, text="Email Picture",command=get_email_address,image=photo3)
|
||||
|
||||
b1.image = photo1
|
||||
b2.image = photo2
|
||||
b3.image = photo3
|
||||
|
||||
b1.pack()
|
||||
b2.pack()
|
||||
b3.pack()
|
||||
|
||||
|
||||
Tkinter.mainloop( )
|
||||
|
||||
|
||||
BIN
photobooth_small.png
Normal file
|
After Width: | Height: | Size: 673 KiB |
82
photobooth_wii.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#Written by Mari DeGrazia
|
||||
#arizona4n6@gmail.com
|
||||
#This script will synch with the Wii remote and monitor the buttons
|
||||
#Once a button is pressed, it will write out to a specified GPIO pin
|
||||
#The photobooth script monitors the corresponding GPIO pins and once the GPIO state changes acts accordingly
|
||||
|
||||
import cwiid
|
||||
import time
|
||||
import pygame
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
#these will be the GPIO pins that the photobooth will monitor for changes
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
GPIO.setup(17, GPIO.OUT) #right, next overlay
|
||||
GPIO.setup(9, GPIO.OUT) #1 button, print picture
|
||||
GPIO.setup(21, GPIO.OUT) #left, previous overlay
|
||||
GPIO.setup(20, GPIO.OUT) #2 button, new picture
|
||||
GPIO.setup(16, GPIO.OUT) #A button, remove overlays
|
||||
pygame.mixer.init()
|
||||
|
||||
pygame.mixer.music.load("/home/pi/Pi-Photobooth/audio/mk64_luigi03.wav")
|
||||
pygame.mixer.music.play()
|
||||
|
||||
print 'Press button 1 + 2 on your Wii Remote...'
|
||||
time.sleep(1)
|
||||
|
||||
wm = None
|
||||
while not wm:
|
||||
try:
|
||||
wm=cwiid.Wiimote()
|
||||
except:
|
||||
print "Still looking for Wii remote..."
|
||||
pass
|
||||
|
||||
print 'Wii Remote connected...'
|
||||
pygame.mixer.music.load("/home/pi/Pi-Photobooth/audio/mk64_mario09.wav")
|
||||
pygame.mixer.music.play()
|
||||
wm.rumble=True
|
||||
time.sleep(1)
|
||||
wm.rumble= False
|
||||
|
||||
Rumble = False
|
||||
wm.rpt_mode = cwiid.RPT_BTN
|
||||
|
||||
while True:
|
||||
print wm.state['buttons']
|
||||
direction = wm.state['buttons']
|
||||
|
||||
# 1 button pressed
|
||||
if direction == 1:
|
||||
print "picture"
|
||||
GPIO.output(9, GPIO.LOW)
|
||||
time.sleep(.3)
|
||||
GPIO.output(9, GPIO.HIGH)
|
||||
|
||||
# 2 button
|
||||
if direction == 2:
|
||||
print "new picture"
|
||||
GPIO.output(20, GPIO.LOW)
|
||||
time.sleep(.3)
|
||||
GPIO.output(20, GPIO.HIGH)
|
||||
|
||||
# A button
|
||||
if direction == 8:
|
||||
print "remove overlays"
|
||||
GPIO.output(16, GPIO.LOW)
|
||||
time.sleep(.3)
|
||||
GPIO.output(16, GPIO.HIGH)
|
||||
|
||||
#to make it turn, make one wheel move slower then the other
|
||||
if direction == 1024:
|
||||
print "next overlay"
|
||||
GPIO.output(17, GPIO.LOW)
|
||||
time.sleep(.3)
|
||||
GPIO.output(17, GPIO.HIGH)
|
||||
|
||||
if direction == 2048:
|
||||
print "previous overlay"
|
||||
GPIO.output(21, GPIO.LOW)
|
||||
time.sleep(.3)
|
||||
GPIO.output(21, GPIO.HIGH)
|
||||
23
software.md
|
|
@ -1,23 +0,0 @@
|
|||
# Software Installation
|
||||
This resource requires a number of additional software libraries. You will need to be connected to the internet to install these extra libraries.
|
||||
|
||||
To install the software you need, run the following commands in a terminal window:
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y python3-picamera python3-pip
|
||||
sudo pip3 install guizero
|
||||
sudo pip3 install twython
|
||||
sudo apt-get install -y python3-PIL
|
||||
```
|
||||
|
||||
This will install the necessary software to control the Camera Module, create a GUI, and tweet and manipulate images.
|
||||
|
||||
If you are using the Raspberry Pi touchscreen to make this resource, you will also need to enter the following commands:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
sudo apt-get dist-upgrade
|
||||
sudo apt-get install -y raspberrypi-ui-mods
|
||||
sudo apt-get install -y raspberrypi-net-mods
|
||||
```
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
- Python 3 picamera
|
||||
- Python 3 guizero
|
||||
- Python 3 twython
|
||||
- Python 3 PIL
|
||||
42
worksheet.md
|
|
@ -1,42 +0,0 @@
|
|||
# The All-Seeing Pi
|
||||
|
||||
In this resource, you will make a tweeting touchscreen photo booth using a Raspberry Pi.
|
||||
|
||||
## Making the photo booth housing
|
||||
You can create your All-Seeing Pi using any housing you like. Our first iteration used a humble cardboard box, but you might want to create a masterpiece of carpentry or laser-cutting wizardry to hold your All-Seeing Pi.
|
||||
|
||||

|
||||
|
||||
If you don't have fancy buttons or a touchscreen, that's OK too: you can still make the All-Seeing Pi! Here is a picture of the setup we used when creating this resource: the only extra hardware requirements are two tactile push buttons, four jumper leads, a breadboard, and a Camera Module. You can use your usual monitor, keyboard, and mouse.
|
||||
|
||||

|
||||
|
||||
## Connecting the Raspberry Pi touchscreen
|
||||
|
||||
1. Set up your Raspberry Pi touchscreen. There is a [good tutorial](https://thepihut.com/blogs/raspberry-pi-tutorials/45295044-raspberry-pi-7-touch-screen-assembly-guide) available to help you. Ensure that you power the touchscreen via the Raspberry Pi with jumper leads as shown in the tutorial, and that the power supply you use is a good quality one.
|
||||
|
||||
1. Situate your screen within the housing of your choice, ensuring you can still access the Raspberry Pi.
|
||||
|
||||
## Connecting the Camera Module
|
||||
|
||||
1. Connect the Camera Module to the Raspberry Pi with the blue side of the connector facing the USB ports:
|
||||
|
||||

|
||||
|
||||
1. Situate the Camera Module in your chosen housing. If you are using glue to secure it in place, be careful not to get any glue onto the connectors or camera components or they may stop working.
|
||||
|
||||
## Connecting the buttons
|
||||
|
||||
1. Using jumper leads, wire one button to **GPIO 23** and any ground pin, and the other button to **GPIO 25** and any ground pin.
|
||||
|
||||
1. Situate the buttons in your chosen housing. In the software, the button connected to GPIO 23 will select the next overlay, and the button connected to GPIO 25 will take the picture. (Don't worry if you accidentally wire your buttons up the other way around: you can simply swap the pin numbers in the code!)
|
||||
|
||||

|
||||
|
||||
## Finishing the setup
|
||||
|
||||
1. To write the software, you will also need to connect a keyboard and mouse to your Raspberry Pi, as well as a display if you are not using the touchscreen.
|
||||
|
||||
1. Power your Raspberry Pi on and, if you are using one, check that the touchscreen works.
|
||||
|
||||
Once you have set up your hardware, head over to [worksheet 2](worksheet2.md) to find out how to write the software to control your All-Seeing Pi.
|
||||
378
worksheet2.md
|
|
@ -1,378 +0,0 @@
|
|||
# The All-Seeing Pi Worksheet 2: Software
|
||||
|
||||
With the hardware set up, we can begin to program the software that will make everything work.
|
||||
|
||||
## Test the buttons
|
||||
|
||||
1. To begin, open the **File Explorer**, then right click on a blank space inside the File Explorer window.
|
||||
|
||||

|
||||
|
||||
1. Select **Create New** and then click **Folder**.
|
||||
|
||||

|
||||
|
||||
1. Type in the name of the folder where you will store the code and the photographs. We chose to call ours `allseeingpi`. Double click on the `allseeingpi` folder and make a note of the path to it (this is displayed in the bar at the top), which should be `/home/pi/allseeingpi`.
|
||||
|
||||
1. From the **Programming** menu, open up **Python 3**.
|
||||
|
||||

|
||||
|
||||
1. Create a new Python file by clicking on **File > New File**.
|
||||
|
||||
1. Click on **File > Save** and save your file into the `allseeingpi` folder you just created, with the filename `allseeingpi.py`.
|
||||
|
||||
1. We will need the `gpiozero` library. At the start of your Python file, add an import statement:
|
||||
|
||||
```python
|
||||
from gpiozero import Button
|
||||
```
|
||||
|
||||
1. Next we will set up the buttons. On the [previous worksheet](worksheet.md), we wired our buttons to GPIO 23 and GPIO 25. Let's go ahead and set both buttons up.
|
||||
|
||||
```python
|
||||
next_overlay_btn = Button(23)
|
||||
take_pic_btn = Button(25)
|
||||
```
|
||||
|
||||
1. Now we will use gpiozero to tell the buttons what to do when pressed. In the code below, `next_overlay` and `take_picture` are functions which will be called when the corresponding button is pressed:
|
||||
|
||||
```python
|
||||
next_overlay_btn.when_pressed = next_overlay
|
||||
take_pic_btn.when_pressed = take_picture
|
||||
```
|
||||
|
||||
1. We will write these two functions so that the buttons know what to do when they are pressed. Functions are usually written at the start of a program, immediately after the import statements. Add the following two functions immediately after the import statement, with some placeholder code to print a message when they are pressed, so we can test them.
|
||||
|
||||
```python
|
||||
def next_overlay():
|
||||
print("Next overlay")
|
||||
|
||||
def take_picture():
|
||||
print("Take a picture")
|
||||
```
|
||||
|
||||
1. Press **F5** to run your program. Try pressing each button and check that a different message pops up for each in the Python shell.
|
||||
|
||||

|
||||
|
||||
If your buttons do not produce this result, check that they are wired up correctly, and that they are connected to GPIO pins 23 and 25 on the Raspberry Pi. The button pins should be in **different** rows of the breadboard, like this:
|
||||
|
||||

|
||||

|
||||
|
||||
You may have buttons with two legs on each side. These should be placed across the gap on your breadboard with the jumper wires both attached into one side. Take care to ensure the jumper wires are in the same rows as the legs of the button.
|
||||
|
||||

|
||||
|
||||
## Set up the camera
|
||||
|
||||
1. Now that we know the buttons work, let's set up the code for the camera. First add an import statement to the existing ones at the top of the program:
|
||||
|
||||
```python
|
||||
from picamera import PiCamera
|
||||
```
|
||||
|
||||
1. Locate the existing line `take_pic_btn.when_pressed = take_picture` and, below it, add the following code to set up the camera object:
|
||||
|
||||
```python
|
||||
camera = PiCamera()
|
||||
camera.resolution = (800, 480)
|
||||
camera.hflip = True
|
||||
camera.start_preview(alpha=128)
|
||||
```
|
||||
|
||||
This code creates a 'PiCamera' object with the resolution set to 800 × 480, which is the resolution of the Raspberry Pi touchscreen. We also tell the camera to flip the preview horizontally (`hflip`): if we don't do this, the preview image will be mirrored, which makes it hard for people to align themselves with the overlays! We then start the preview with alpha set to `128` so that it is semi-transparent; this is in case we get an error and need to see what is happening underneath. When you are confident that your code works, you can remove the `alpha=128` to make the preview fully opaque.
|
||||
|
||||
|
||||
## Take a picture when the button is pressed
|
||||
|
||||
1. Since we will probably take lots of pictures with the All-Seeing Pi, we will put the date and time at which the picture was taken within the filename to avoid a picture being overwritten each time a new one is taken. To do this, we will need the `gmtime` and `strftime` functions from the `time` library, so add this line to the other import statements:
|
||||
|
||||
```python
|
||||
from time import gmtime, strftime
|
||||
```
|
||||
|
||||
1. Underneath the code to set up the camera, add the following line:
|
||||
|
||||
```python
|
||||
output = strftime("/home/pi/allseeingpi/image-%d-%m %H:%M.png", gmtime())
|
||||
```
|
||||
|
||||
This will create a variable called `output` which contains the location and filename of where the captured photo will be saved. The `%d`, `%m` (etc) characters are how we specify the time format: `%d` means the day and `%m` means the month, for example. If you would like the date format in your filename to be different, there is a full [reference guide](https://docs.python.org/2/library/time.html#time.strftime) to `strftime` available. The current date and time is provided by calling the function `gmtime()`.
|
||||
|
||||
1. Now let's revisit the `take_picture()` function and add some new code so that it actually takes a picture instead of just printing a message. Locate the line `def take_picture()`. Delete the line `print("Take a picture")` and in its place, add the following lines, making sure they are indented:
|
||||
|
||||
```python
|
||||
def take_picture():
|
||||
camera.capture(output)
|
||||
camera.stop_preview()
|
||||
```
|
||||
|
||||
This code captures a picture, saving it to the location we just defined in the variable `output`. It then stops the camera preview.
|
||||
|
||||
1. Press **F5** to run your program, then press the button to take a picture.
|
||||
|
||||
1. Navigate to the folder `/home/pi/allseeingpi` and check that the picture you just took has saved correctly.
|
||||
|
||||
## Working with overlays
|
||||
|
||||
1. The All-Seeing Pi is no ordinary photo booth! The second button we set up, `next_overlay_btn`, is used to change between 'overlays': these are fun pictures such as hats, beards, and glasses which appear on the screen as if you are wearing them. Here is an example of a picture taken with an overlay:
|
||||
|
||||

|
||||
|
||||
You can make your own overlays, or use the ready-made ones we have provided for you to download. If you are creating your own overlays, make sure that they are saved at 800 × 480 resolution as PNG files, with the background set to transparent.
|
||||
|
||||
1. Create a subfolder called `overlays` within your `allseeingpi` folder, and place your overlay images inside it.
|
||||
|
||||
1. Navigate to the [overlays folder](https://github.com/raspberrypilearning/the-all-seeing-pi/tree/master/overlays) of the GitHub repo for this project. Click on the filename of the overlay you would like to use, then right-click on the download link and save the image into the `overlays` folder you just created. Repeat this process until you have saved all of the overlays you would like to use.
|
||||
|
||||
1. Now [right-click here](code/overlay_functions.py) and save this file as `overlay_functions.py`. Make sure you save this file in your `allseeingpi` directory (where the `allseeingpi.py` script is also saved). If you would like to see a full explanation of what these functions do, or you would prefer to write them yourself, head to the [overlay functions explanation page](worksheet3.md) to find out how to do this, then resume the tutorial at the next step.
|
||||
|
||||
1. In the `overlay_functions.py` file, find this comment:
|
||||
|
||||
```
|
||||
# EDIT THESE VALUES ------------------------
|
||||
```
|
||||
|
||||
You will need to change this code to specify two things:
|
||||
|
||||
- Set the `overlays_dir` to the directory where your overlays are stored. If you are following this tutorial exactly, you will **not** need to change this directory location.
|
||||
- Set the `overlays` to be a list of the filenames of the overlays (without extension), surrounded by quotes and separated by commas. For example, if you had overlay images called `rock.png`, `paper.png`, and `scissors.png`, your line of code would look like this:
|
||||
|
||||
```python
|
||||
overlays = ['rock', 'paper', 'scissors']
|
||||
```
|
||||
|
||||
1. Now go back to your `allseeingpi.py` program. Underneath the other import statements in your program, add another one to import this file:
|
||||
|
||||
```python
|
||||
from overlay_functions import *
|
||||
```
|
||||
|
||||
This will allow us to use all of the overlay functions defined in the `overlay_functions.py` file from within our `allseeingpi.py` file.
|
||||
|
||||
## Change overlays with a button
|
||||
|
||||
1. The other button you wired up to your All-Seeing Pi (called `next_overlay_btn`) will be the one we use to switch between the various overlays. Locate the function `def next_overlay():` and delete the indented line `print ("Next overlay")`. In its place, add the following code, making sure the lines are indented to show that they are part of the function:
|
||||
|
||||
```python
|
||||
def next_overlay():
|
||||
global overlay
|
||||
overlay = next(all_overlays)
|
||||
preview_overlay(camera, overlay)
|
||||
```
|
||||
|
||||
First, we have to declare that we want to use the global variable, `overlay`. This means that when we change the overlay, that value is saved so that we can access it and use it from anywhere, and the change isn't lost when we exit this function.
|
||||
|
||||
The second line gets the next overlay from the list of `all_overlays` (defined within the `overlay_functions.py` file), and sets this as the current `overlay`. Then, the function `preview_overlay()` is called to display the new overlay.
|
||||
|
||||
1. Save your program, and run it by pressing **F5**. Check that when you press the button to change between overlays, the overlays change. Ensure you have at least one overlay image in your overlays folder to be able to change between them!
|
||||
|
||||
Here is the [program so far](code/change_overlays_and_take_picture.py) if you want to check your progress.
|
||||
|
||||
1. You will notice that, when you take a picture, two things happen. Firstly, the overlay does not disappear and probably makes it quite difficult to see what you are doing: close the Python shell window to get rid of the overlay. Secondly, people can see a camera preview and can choose a silly hat from the overlays, but, when they take the photograph, the overlay disappears. We need to add code to remove the overlay from the screen once the picture is taken, and superimpose it onto the saved photograph.
|
||||
|
||||
## Save an overlay on your picture
|
||||
|
||||
1. Locate the function `def take_picture():` and add two lines of code at the end of the function:
|
||||
|
||||
```python
|
||||
def take_picture():
|
||||
camera.capture(output)
|
||||
camera.stop_preview()
|
||||
remove_overlays(camera) # Add this line
|
||||
output_overlay(output, overlay) # Add this line
|
||||
```
|
||||
|
||||
Here we are using two more functions from the `overlay_functions` file. The function `remove_overlays` does exactly what it says, and removes all of the overlays so they don't hang around after we take a photograph. The `output_overlay` function takes the photograph and the overlay and glues them together so the resulting final output is a photograph with the chosen overlay superimposed.
|
||||
|
||||
1. Once again, save your file and run it using **F5** to check that you can now change between overlays, and that, when you take a photograph, your chosen overlay is saved as part of the picture.
|
||||
|
||||
## Create a GUI
|
||||
|
||||
We have an almost-working All-Seeing Pi. However, when a picture is taken, the camera preview disappears and the user is left staring at the Python shell and the Raspbian desktop. You probably don't want your selfie-takers to have to restart the Python program every time someone takes a picture. We will create a very simple GUI to display the picture that was taken and allow them to take another picture.
|
||||
|
||||
1. To create the GUI we will use a library called **guizero**, which you should have already installed in the [software installation](software.md) step. Add another import line with the others at the start of your program to bring in the guizero functions we need:
|
||||
|
||||
```python
|
||||
from guizero import App, PushButton, Text, Picture
|
||||
```
|
||||
|
||||
1. At the bottom of your current program, create the beginning of your GUI.
|
||||
|
||||
```python
|
||||
app = App("The All-Seeing Pi", 800, 480)
|
||||
message = Text(app, "I spotted you!")
|
||||
app.display()
|
||||
```
|
||||
|
||||
First, we create an **app**, which is the basic container for the GUI. The dimensions are 800 × 480 because that is the resolution of the touchscreen, and the title bar will contain the text "The All-Seeing Pi". It is possible to make the GUI full-screen, but we will not do this for now because it can be difficult for testing. We also create a message, `"I spotted you!"`, and add it to the app before displaying everything.
|
||||
|
||||
1. Save and run your program again. Check that, when you press the button to take the photo, the camera preview exits and you see a mostly blank GUI with a message saying "I spotted you!".
|
||||
|
||||
1. Now, between the message line and the `app.display()` line, add another line of code to create a button.
|
||||
|
||||
```python
|
||||
new_pic = PushButton(app, new_picture, text="New picture")
|
||||
```
|
||||
|
||||
Examining the arguments passed to this `PushButton` object, we have three parts:
|
||||
|
||||
- `app`: tells the button to add itself to the app
|
||||
- `new_picture`: this is the **command**. When the button is pushed, it will call the function `new_picture()` (which we haven't written yet!)
|
||||
- `text="New picture"`: this is the text which will appear on the button
|
||||
|
||||
1. Now write the `new_picture` function so that the button knows what to do when it is pressed. Write this code after the `take_picture()` function, but before the code where we set up the buttons. **Ensure that your cursor is not indented**, otherwise the code you write now will become part of the `take_picture()` function, which we do not want.
|
||||
|
||||
```python
|
||||
def new_picture():
|
||||
camera.start_preview(alpha=128)
|
||||
preview_overlay(camera, overlay)
|
||||
```
|
||||
|
||||
This function is very straightforward: it simply tells the camera to restart the preview, and to display the overlay (which will be the last overlay we used).
|
||||
|
||||
1. Save your program, and run it using **F5** once again. Check that you can press your physical button to take a picture, and that the GUI displays once the camera preview disappears. Check that you can press the on-screen button to restart the camera preview and take another picture.
|
||||
|
||||
## Stop the picture overwriting
|
||||
|
||||
Now that we have introduced the ability to run the program only once but take multiple pictures, we have a problem. The filename for the picture is generated by this existing line of code:
|
||||
|
||||
```python
|
||||
output = strftime("/home/pi/allseeingpi/image-%d-%m %H:%M.png", gmtime())
|
||||
```
|
||||
|
||||
However, we only execute this line of code once during the program. This means that, every time the button is pressed to take a picture, it is saved to the same location, with the same filename. To fix this, we need to regenerate the filename every time we take a picture.
|
||||
|
||||
1. Locate this line of code and copy it so you can paste it somewhere else shortly. Then, change the output to be equal to an empty string:
|
||||
|
||||
```python
|
||||
output = ""
|
||||
```
|
||||
|
||||
1. Now find your `take_picture()` function. At the start of the code within the function, add the line `global output` and then paste in the line you copied. The altered function should look like this:
|
||||
|
||||
```python
|
||||
def take_picture():
|
||||
global output
|
||||
output = strftime("/home/pi/allseeingpi/image-%d-%m %H:%M.png", gmtime())
|
||||
camera.capture(output)
|
||||
camera.stop_preview()
|
||||
# .... code continues...
|
||||
```
|
||||
|
||||
We are dealing with **scoping** here: this is an important concept for programmers to understand. Why did we bother to create the variable `output` in the main part of the program, and **initialise** it as a blank string, when we could have just created it within the `take_picture()` function? The answer is that if we only created it within the `take_picture()` function, once the function finished executing, the variable would no longer exist. By declaring that we are talking about the `global` version of the `output` variable, we are telling the program that we want to use the variable `output` which we created in the main part of the program. This means that once the function exits, the variable `output` with the location of the saved picture will still exist. We need to have a permanent record where the picture was saved because it is used in other places within the program.
|
||||
|
||||
## Display the picture
|
||||
|
||||
You probably don't want your photo booth participants to have to go digging through the Raspbian filesystem to see the picture they took either, so let's display the picture they took on the GUI.
|
||||
|
||||
1. Locate the line of code where you intialise the `output` variable:
|
||||
|
||||
```python
|
||||
output = ""
|
||||
```
|
||||
|
||||
Immediately underneath it, add a new line of code to define the location where we will store the `latest-photo`, i.e. the photo most recently taken using the booth.
|
||||
|
||||
```python
|
||||
latest_photo = '/home/pi/allseeingpi/latest.gif'
|
||||
```
|
||||
1. Now locate the line of code where you added the `PushButton` to your GUI. Immediately **before** that line, insert a line of code to display an image on the GUI:
|
||||
|
||||
```
|
||||
your_pic = Picture(app, latest_photo)
|
||||
```
|
||||
|
||||
1. The file we are referring to, `latest.gif`, does not yet exist, so if you run your program now you will not see a photograph displayed on the GUI. We must add code inside the `take_picture()` function to generate this image so that it can be displayed. Locate the `take_picture()` function and, underneath the other code in the function, add the following lines (remembering to ensure that the new lines of code are also indented):
|
||||
|
||||
```python
|
||||
size = 400, 400
|
||||
gif_img = Image.open(output)
|
||||
gif_img.thumbnail(size, Image.ANTIALIAS)
|
||||
gif_img.save(latest_photo, 'gif')
|
||||
|
||||
your_pic.set(latest_photo)
|
||||
```
|
||||
|
||||
This code opens the `output` image (the image containing the photo combined with the overlay), creates a smaller thumbnail of that image in **gif** format, and saves it at the location set up in `latest_photo`. It then sets the image on the GUI (`your_pic`) to be that latest photo image using the `set()` function which is part of the guizero library.
|
||||
|
||||
1. Save your code and test whether, when you take a photograph, it is displayed on the GUI. You may find that there is a short delay between the camera preview exiting and the image displaying on the GUI while it is saving.
|
||||
|
||||

|
||||
|
||||
You may notice that the picture quality of the image displayed on screen is not optimal. This is because the picture has been converted to gif format to be displayed on the GUI. The full-quality png version of the photograph will still be saved in the `allseeingpi` folder.
|
||||
|
||||
## Tweet picture
|
||||
|
||||
If you just want a fun photo booth to take and save pictures, you could stop there. Alternatively, you could go one step further and make your All-Seeing Pi tweet the photo that was taken.
|
||||
|
||||
1. You will need to set up a Twitter account and create an app for your All-Seeing Pi. Follow steps 1-4 on the [Getting started with the Twitter API](https://www.raspberrypi.org/learning/getting-started-with-the-twitter-api/worksheet/) resource in a separate file, and check that you can successfully send a textual tweet from Python.
|
||||
|
||||
1. Save a copy of the `auth.py` file containing your Twitter API keys (which you created during the 'Getting started' tutorial) inside your `/home/pi/allseeingpi` folder.
|
||||
|
||||
1. Go back to your `allseeingpi.py` file and, after the other import statements, import Twython:
|
||||
|
||||
```python
|
||||
from twython import Twython
|
||||
```
|
||||
|
||||
1. Immediately after importing Twython, add the following code to import your Twitter API credentials from your `auth.py` file:
|
||||
|
||||
```python
|
||||
from auth import (
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
access_token,
|
||||
access_token_secret
|
||||
)
|
||||
```
|
||||
|
||||
1. Create a new function after the `new_picture()` function, called `send_tweet()`:
|
||||
|
||||
```python
|
||||
def send_tweet():
|
||||
```
|
||||
|
||||
1. Inside the function, instantiate a Twitter object:
|
||||
|
||||
```python
|
||||
def send_tweet():
|
||||
twitter = Twython(
|
||||
consumer_key,
|
||||
consumer_secret,
|
||||
access_token,
|
||||
access_token_secret
|
||||
)
|
||||
```
|
||||
|
||||
1. Add some more code inside the `send_tweet()` function to tweet the `output` picture. You can change the text in your `message` code if you want your tweet to say something different:
|
||||
|
||||
```python
|
||||
message = "The All-Seeing Pi saw you!"
|
||||
with open(output, 'rb') as photo:
|
||||
twitter.update_status_with_media(status=message, media=photo)
|
||||
```
|
||||
|
||||
1. Now, find the code for the GUI where you create the `PushButton` for a new picture, and add another `PushButton` underneath it which will call the `send_tweet()` function when it is pressed:
|
||||
|
||||
```python
|
||||
tweet_pic = PushButton(app, send_tweet, text="Tweet picture")
|
||||
```
|
||||
|
||||

|
||||
|
||||
1. Save and run your program. Test whether, when you take a picture and press the **Tweet picture** button on the GUI, the picture is tweeted from your Twitter account.
|
||||
|
||||

|
||||
|
||||
|
||||
The finished code is [here](code/finished_allseeingpi.py): you can check it against your code if you need to.
|
||||
|
||||
Once you are happy that your All-Seeing Pi works, you may wish to remove the `alpha=128` command from the camera preview to make it fully opaque. You can also make the GUI full-screen: locate the line `app = App("The All-Seeing Pi", 800, 480)` and, immediately after it, add the line `app.attributes("-fullscreen", True)`.
|
||||
|
||||
## What next?
|
||||
- Can you add a text box or perhaps a touchscreen keyboard to your GUI to allow someone to enter their Twitter handle?
|
||||
- Can you use this Twitter handle to add an `@username` mention to the tweet text?
|
||||
- Could you make a more imaginative housing for your All-Seeing Pi?
|
||||
114
worksheet3.md
|
|
@ -1,114 +0,0 @@
|
|||
# The All Seeing Pi (Overlay Functions)
|
||||
|
||||
This page is for more advanced learners as it explains in detail what the code and functions inside `overlay_functions.py` do. It is possible to make the All Seeing Pi without understanding what these functions do - simply save a copy of the file [overlay_functions.py](code/overlay_functions.py) into the folder with your code and they will be available.
|
||||
|
||||
## Importing necessary libraries
|
||||
|
||||
These statements import functions from the `PIL` library to process and save the images and the `itertools` library so that we can cycle through the overlays.
|
||||
|
||||
```python
|
||||
from PIL import Image
|
||||
from itertools import cycle
|
||||
```
|
||||
|
||||
|
||||
## Setting up the variables
|
||||
|
||||
This part sets up the directory where the overlays are saved, and the names of the various overlays. The overlay variable is initialised with the first value in the list.
|
||||
|
||||
```python
|
||||
# EDIT THESE VALUES ------------------------
|
||||
overlays_dir = "/home/pi/allseeingpi/overlays"
|
||||
overlays = ['girl', 'cowboy', 'top', 'pink', 'glassesnose', 'moustache', 'sunglasses', 'elvis', 'emo', 'blackhat', 'emo2', 'baseball', 'flowers', 'santa', 'alps', 'mop', 'glasses']
|
||||
# ------------------------------------------
|
||||
overlay = overlays[0] # Starting value
|
||||
```
|
||||
|
||||
## Get the overlay as a PIL Image
|
||||
|
||||
This function is only used within other functions in this file. Given the name of an overlay as a string, it creates a PIL Image object of that overlay and returns it.
|
||||
|
||||
```python
|
||||
def _get_overlay_image(overlay):
|
||||
|
||||
# Open the overlay as an Image object
|
||||
return Image.open(overlays_dir + "/" + overlay + ".png")
|
||||
```
|
||||
|
||||
## Pad the overlay
|
||||
|
||||
This function ensures that the overlay is padded correctly so it can be displayed on the preview.
|
||||
|
||||
```python
|
||||
def _pad(resolution, width=32, height=16):
|
||||
# Pads the specified resolution
|
||||
# up to the nearest multiple of *width* and *height*; this is
|
||||
# needed because overlays require padding to the camera's
|
||||
# block size (32x16)
|
||||
return (
|
||||
((resolution[0] + (width - 1)) // width) * width,
|
||||
((resolution[1] + (height - 1)) // height) * height,
|
||||
)
|
||||
```
|
||||
## Remove all overlays
|
||||
|
||||
This function iterates over all overlays attached to the `camera` object, and removes them.
|
||||
|
||||
```python
|
||||
def remove_overlays(camera):
|
||||
|
||||
# Remove all overlays from the camera preview
|
||||
for o in camera.overlays:
|
||||
camera.remove_overlay(o)
|
||||
```
|
||||
|
||||
## Put the overlay on the camera preview
|
||||
|
||||
This function is passed a `PiCamera` object (`camera`) and a string (`overlay`). It removes all overlays currently associated with the camera object, creates a PIL Image object of the chosen overlay called `overlay_img`, pads that image to display correctly and then adds it to the camera preview. The alpha of the preview is set to 128 so that the overlay is semi transparent. If the overlay was made fully opaque it would obscure the camera preview.
|
||||
|
||||
```python
|
||||
def preview_overlay(camera=None, overlay=None):
|
||||
|
||||
# Remove all overlays
|
||||
remove_overlays(camera)
|
||||
|
||||
# Get an Image object of the chosen overlay
|
||||
overlay_img = _get_overlay_image(overlay)
|
||||
|
||||
# Pad it to the right resolution
|
||||
pad = Image.new('RGB', _pad(camera.resolution))
|
||||
pad.paste(overlay_img, (0, 0))
|
||||
|
||||
# Add the overlay
|
||||
camera.add_overlay(pad.tobytes(), alpha=128, layer=3)
|
||||
```
|
||||
|
||||
## Save picture with overlay
|
||||
|
||||
This function takes the location of the photograph (`output`) and the given overlay (`overlay`), both as strings. It then creates a PIL Image object of the specified overlay, also creates a blank PIL Image to save the finished output to, and then combines the photograph with the overlay, re-saving the finished photograph at the `output` location.
|
||||
|
||||
```python
|
||||
def output_overlay(output=None, overlay=None):
|
||||
|
||||
# Take an overlay Image
|
||||
overlay_img = _get_overlay_image(overlay)
|
||||
|
||||
# ...and a captured photo
|
||||
output_img = Image.open(output).convert('RGBA')
|
||||
|
||||
# Combine the two and save the image as output
|
||||
new_output = Image.alpha_composite(output_img, overlay_img)
|
||||
new_output.save(output)
|
||||
```
|
||||
|
||||
## Overlays cycle
|
||||
|
||||
This code creates a `cycle`. We use the `next()` function on this cycle when the `next_overlay_btn` is pressed in order to receive the next overlay in the list. A cycle is needed because when the end of the list of overlays is reached, we want to automatically begin again with the first overlay.
|
||||
|
||||
```python
|
||||
all_overlays = cycle(overlays)
|
||||
```
|
||||
|
||||
## What next?
|
||||
|
||||
Now you know what all of the overlay functions do, head back to "Working with overlays" in [worksheet 2](worksheet2.md) to find out how to create the software.
|
||||