Fotobox

Inhaltsverzeichnis

Ausgangslage

Offenbar gibt es einen  aufsteigenden Trends für Verwendung einer Fotoboxe. Vor allem Fotoboxen mit Sofortdruckfunktion sind aufgrund ihres Unterhaltungswerts und wegen der tollen Erinnerungen auf fast keiner Feierlichkeit beinahe unverzichtbar.

Diese Erkenntnis überbrachte mir eine Kundin mit dem Auftrag mit Raspberry Pi(RasPi) die Software dazu zu realisieren. Die Hardware wurde mir zur Verfügung gestellt und die Box wurde auch von ihr gebaut. Für mich war das Neuland, aber solche Herausforderungen reizen mich bei meinen Arbeiten.

Funktion

Der Wunsch war, dass von der Kamera ein Live-Bild an RasPi gesendet und im Display angezeigt wird. Darin sollen drei Buttons für Aufnahme, Drucken und Live Bild eingeblendet sein. Mit dem Druck auf „Aufnahme“ soll das Live-Bild eingefroren und bei „Live Bild“wieder zurückgeschaltet werden.

Material

Zur Verfügung hatte ich neben Raspberry Pi folgende Teile:

  • 10,1 Zoll HDMI Display
  • Nikon D5200
  • DNP DS-40

Das Display anzuschliessen war nicht so ein Problem. Der RasPi benötigte noch eine Treiber-Installation, da die Proportion noch nicht stimmte:

cd /boot/
wget https://www.waveshare.com/w/upload/1/1e/LCD-show-180817.tar.gz
tar xzvf /boot/LCD-show-*.tar.gz  
cd LCD-show/
chmod +x LCD101-1024x600-show
./LCD101-1024x600-show

Nach dem Neustart stimmt nun die Auflösung

Druckerinstallation

Das Einrichten des Druckers lief auch ohne Probleme mit Hilfe von Cups und

sudo apt-get install cups
sudo usermod -a -G lpadmin pi

Nun kann der Drucker angeschlossen und eingeschaltet werden. Cups erkennt dieser automatisch. Wenn RasPi im Grafikmodus läuft, kann über den Webbrowser mit der Adresse http://127.0.0.1:631 der Drucker konfiguriert werden.

Kamerasteuerung

Für die Ansteuerung der Kamera erweist sich GPhoto als hilfreich.

sudo apt-get install gphoto

Software

Als erste war die Überlegung, in welcher Programmier-Umgebung möchte ich das umsetzen. Aufgrund des System kam eigentlich Python in Frage. Die nächste frage war, da Python standardmässig keine grafische Elemente kennt, was verwende ich dafür und entschied mich für pygame als Erweiterung.

Die grosse Herausforderung war, die Live-Bilder von der Kamera angezeigt zu bekommen, den das mit dem Streamen funktioniert leider nicht per Python. Die Dokumentation war nicht sehr hilfreich und im Netz war dazu nur wenig Info auffindbar. Der Kamera Einzelbilder zu entlocken war nicht klug, da der Spiegel sich jeweils öffnete (die Mechanik lässt grüssen). Nach dem ich duzende Code durch gestrählt bin, fand ich endlich den Befehl, was den Spiegel offen lässt. Somit lade ich Einzelbilder von der Kamera und zeige sie dann im Display an.

Der letzte Knackpunkt war eher ein versteckter, wo man nicht daran denkt: Das Betriebssystem und damit auch der Code und die Ablage der Einzelbilder vom Live-Bild liegen auf der SD-Karte. Diese Karten haben nur eine begrenzte Anzahl an Schreibzyklus, danach verabschieden sie sich almälich. Die Lösung war, da RaspPi genügend Reserve hat, ein Ram-Disk einzurichten, sodass die Ablage des Live-Bildes im Arbeitsspeicher zu liegen kommt.

Code V1.0 Beta

import piggyphoto
import pygame
import os
import time
import subprocess as sub
from shutil import copyfile


usecamera         = True
cameraonline      = False
running           = True
freeze            = False
pos               = [0,0]
click             = [0,0,0]
width             = 1024
height            = 600
button_width      = 200
button_hight      = 50

white             = (255,255,255)
black             = (0,0,0)
save_button       = (200,100,50)
save_button_light = (255,155,105)
print_button      = (200,200,0)
print_button_light= (255,255,0)
live_button       = (34,177,76)
live_button_light = (90,230,130)

intro_file        = "images/intro.jpg"
preview_file      = "/mnt/RAMDisk/preview.jpg"
capture_file      = "/mnt/RAMDisk/capture.jpg"
discon_file       = "images/offline.jpg"
photo_dest_file   = "photos/capture_"

def DrawCenterMessage(message,width,height,x,y):
    #displays notification messages onto the screen

    backgroundCenterSurface = pygame.Surface((width,height))#size
    backgroundCenterSurface.fill(black)

    main_surface.blit(backgroundCenterSurface,(x,y))#position
    main_surface.blit(pygame.font.SysFont("freeserif",40,bold=1).render(message, 1, white),(x+10,y+10))
    pygame.display.update()




def text_objects(text, color):
    textSurface = pygame.font.SysFont("comicsansms", 50).render(text, True, color)
    return textSurface, textSurface.get_rect()




def text_to_button(msg, color, buttonx, buttony, buttonwidth, buttonheight):
    textSurf, textRect = text_objects(msg,color)
    textRect.center = ((buttonx+(buttonwidth/2)), buttony+(buttonheight/2))
    main_surface.blit(textSurf, textRect)





def button(text, x, y, width, height, inactive_color, active_color, action = None):
    global pos
    global click
    if x + width > pos[0] > x and y + height > pos[1] > y:
        pygame.draw.rect(main_surface, active_color, (x,y,width,height))
        if click[0] == 1 and action != None:
            if action != None:
                action()
                click = [0,0,0]
            
    else:
        pygame.draw.rect(main_surface, inactive_color, (x,y,width,height))

    text_to_button(text,black,x,y,width,height)



def keypressed_event():
    global pos
    global click
    for event in pygame.event.get():

        if event.type == pygame.QUIT:
            return "quit"

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                return "photoshut"
            if event.key == pygame.K_RIGHT:
                return "photoshut"
            if event.key == pygame.K_LEFT:
                return "photoshut"
            if event.key == pygame.K_q:
                return "quit"

        if event.type == pygame.MOUSEBUTTONDOWN: 
            pos = pygame.mouse.get_pos()
            click = [1,0,0]




def show(file):
    picture = pygame.image.load(file)
    picture = pygame.transform.scale(picture, (width, height))
    main_surface.blit(picture, (0, 0))
 




def doPhotoShut():
    if cameraonline:
        DrawCenterMessage("CHEEEEESSSSSE....",400,70,((width/2)-220),((height/2)-2))
        print C.abilities
        C.capture_image(capture_file)
        show(preview_file)


def doCopy():
    DrawCenterMessage("BILD GESPEICHERT!",400,70,((width/2)-220),((height/2)-2))
    date_string = time.strftime("%Y-%m-%d-%H:%M")
    copyfile(capture_file, photo_dest_file + date_string + ".jpg")



def doPrint():
    DrawCenterMessage("PRINT....",400,70,((width/2)-220),((height/2)-2))
    cmd = "lpr -P DNP_DP-DS620 -o PageSize=w288h432 " + capture_file
    p = sub.Popen(cmd,stdout=sub.PIPE,stderr=sub.PIPE,shell=True)
    show(preview_file)



def doCatch():
    global freeze
    freeze = False



def initCamera():
    global main_surface
    global usecamera
    global cameraonline
    global C
    if usecamera:
        try:
            C = piggyphoto.camera()
            C.leave_locked()
            C.capture_preview(preview_file)
            cameraonline = True
        except:
            print "PiggyPhoto Fehler: Keine Kammera verbunden oder nicht eingeschaltet!"
            cameraonline = False
    else:
        cameraonline = False

    picture = pygame.image.load(intro_file)
    picture = pygame.transform.scale(picture, (width, height))
    pygame.display.set_mode(picture.get_size(), pygame.FULLSCREEN)
    main_surface = pygame.display.get_surface()

pygame.init()
initCamera()

while running:
    if cameraonline:
        if freeze == False:
            try:
              C.capture_preview(preview_file)
              show(preview_file)
            except:
                cameraonline = False

    else:
        initCamera()
        show(discon_file)

    if freeze:
        button("Speichern", 150,500,button_width,button_hight, save_button, save_button_light, action = doCopy)
        button("Drucken",   400,500,button_width,button_hight, print_button, print_button_light, action = doPrint)
        button("Neu",       650,500,button_width,button_hight, live_button, live_button_light, action = doCatch)

    pygame.display.update()

    event = keypressed_event()
    if event == "quit":
        running = False
    if event == "photoshut":
        doPhotoShut()
        freeze = True

So, damit ist das Projekt auch gesichert  😆