Python exemplarisch - RPi Tutorial
deutsch     english

KAMERA

 

Alle Programme können von hier heruntergeladen werden.


 

Schnappschuss erstellen und Bild speichern

 
Kleine und billige Foto- und Filmkameras können problemlos am Kameraport des Raspberry Pi angeschlossen werden. Im Internet gibt es viele gute Tutorials, die beschreiben, wie man die Kamera-Software installiert. (Sie ist in unserer SD-Karten-Distribution bereits enthalten.) Für die Programmierung gibt es ein hervorragendes Python-Modul picamera, das wir in unseren Programmen verwenden. Wir strukturieren allerdings den Code nochmals durch die Definition einiger gut dokumentierter Funktionen in der Datei camera.py, die Sie von hier herunterladen und im Verzeichnis, in dem sich ihr Programm befindet, speichern müssen.
camera1

Programm:[►]

# Camera1.py

import camera

print "Capturing image..."
img = camera.captureJPEG(300, 200)
print "size", len(img)
camera.saveData(img, "/home/pi/test.jpg")
print "JPEG saved"
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Um zu vermeiden, dass bei jeder Aufnahme eine temporäre Disk-Datei entsteht, werden die Bilddaten mit Hilfe der Funktion captureJPEG() unter Verwendung von StringIO in einem "Memory-File" data gespeichert.

Ziel:
Schliessen Sie eine Kamera an, erstellen Sie einen Schnappschuss und speichern Sie die JPEG-Datei im lokalen Dateisystem des Raspberry Pi.

Programm:[►]

# Camera1.py

import camera

print "Capturing image..."
img = camera.captureJPEG(300, 200)
print "size", len(img)
camera.saveData(img, "/home/pi/test.jpg")
print "JPEG saved"
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Sie haben mehrere Möglichkeiten, das Ergebnis zu überprüfen: Beispielsweise können Sie VNC starten und das Bild test.jpg mit einem Doppelklick im Raspberry Pi JPEG-Viewer anzeigen. Oder Sie können die Datei mit SFTP oder SSH auf Ihren PC herunter laden und das Bild mit einem beliebigen Imageviewer anzeigen.

Es gibt viele interessante Projekte, die eine Kamera verwenden, beispielsweise ein Überwachungssystem, welches erkennt, wenn eine Person in den Aufnahmebereich eintritt.

 

 

Schnappschuss erstellen und auf das Remote-System transferieren

 

Wir nutzen die Gelegenheit um zu zeigen, wie man Daten vom Raspberry Pi zum einem Remote-Client unter der Verwendung des TCP-Client/Server-Verfahrens übertragen kann. Der Programmcode wird nicht im Detail erklärt, aber mit der Hilfe des vorangehenden Kapitels über TCP/IP und von zusätzlichen Informationen, die Sie im Internet finden, sollten Sie in der Lage sein, ihn vollständig zu verstehen.

Ziel:
Implementieren Sie einen Socket-Server auf dem Raspberry Pi, so dass man über TCP und einen benutzerdefinierten IP-Port (hier Port 22000) auf den Server zugreifen kann. Schreiben Sie einen Python-Client, der auf irgendeinem Remote-System ausgeführt wird. Nachdem dieser die Verbindung zum Server aufgebaut hat, sendet er den Befehl "go", der auf dem Server einen Kamera-Schnappschuss auslöst. Die JPEG-Datei wird nachfolgend ohne Benutzeraktion vom Raspberry Pi auf den Client transferiert und dort angezeigt.

Das Hauptprogramm startet auf dem Server eine Listening-Loop, in der die blockierende Funktion serverSocket.accept() auf eine Verbindungsanforderung eines Clients wartet. Ist die Verbindung hergestellt, startet auf dem Server (in einem neuen Thread) der SocketHandler, welcher für die Kommunikation mit den Clients zuständig ist.

Ein eigener Thread wäre in unserer Situation nicht unbedingt notwendig, da nur ein Client vorhanden ist. Wir wählen trotzdem dieses Standardvorgehen, welches in vielen anderen Client-Server-Anwendungen eingesetzt wird.

Programm:[►]

# CameraServer.py

from threading import Thread
import socket
import time
import camera

VERBOSE = False
IP_PORT = 22000

def debug(text):
    if VERBOSE:
        print "Debug:---", text

# ---------------------- class SocketHandler ------------------------
class SocketHandler(Thread):
    def __init__(self, conn):
        Thread.__init__(self)
        self.conn = conn

    def run(self):
        global isConnected
        debug("SocketHandler started")
        isRunning = True
        while isRunning:
            cmd = ""
            try:
                debug("Calling blocking conn.recv()")
                cmd = self.conn.recv(1024)
            except:
                debug("exception in conn.recv()") 
                # happens when connection is reset from the peer
                break
            debug("Received cmd: " + cmd + " len: " + str(len(cmd)))
            if len(cmd) == 0:
                break
            rc = self.executeCommand(cmd)
            if not rc:
                isRunning = False
        conn.close()
        print "Client disconnected. Waiting for next client..."
        isConnected = False
        debug("SocketHandler terminated")

    def executeCommand(self, cmd):
        debug("Calling executeCommand() with  cmd: " + cmd)
        if cmd == "go":
            print "Taking snapshot and transfer to client..."
            jpg = camera.captureJPEG(640, 480)
            return self.sendData(jpg)
        if cmd == "disconnect":
            debug("Client request disconnection")
            return False

    def sendData(self, data):
        debug("sendData() with len: " + str(len(data)))
        try:
            self.conn.sendall(data)
        except:
            return False
        return True
# ----------------- End of SocketHandler -----------------------

serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# close port when process exits:
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
debug("Socket created")
HOSTNAME = "" # Symbolic name meaning all available interfaces
try:
    serverSocket.bind((HOSTNAME, IP_PORT))
except socket.error as msg:
    print "Bind failed", msg[0], msg[1]
    sys.exit()
serverSocket.listen(10)

print "Waiting for a connecting client..."
isConnected = False
while True:
    debug("Calling blocking accept()...")
    conn, addr = serverSocket.accept()
    print "Connected with client at " + addr[0]
    isConnected = True
    socketHandler = SocketHandler(conn)
    # necessary to terminate it at program termination:
    socketHandler.setDaemon(True)  
    socketHandler.start()
    t = 0
    while isConnected:
        print "Camera ready at", t, "s"
        time.sleep(10)
        t += 10
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Um den Code im vollen Umfang zu verstehen, müssen Sie ein Tutorial über Socket-Programmierung konsultieren. Wir legen besonderen Wert auf ein sauberes Beenden des Handler-Threads.

Im Programm des Clients wird ein eigener Thread als Empfänger verwendet, der die eingehenden Daten unabhängig vom Hauptprogramm verarbeitet. Auch dies ist ein übliches Implementierungs-Szenario für Client-Server-Anwendungen. Die Daten werden in Blöcken mit maximal 4096 Bytes gelesen bis das Zeichen end-of-file erkannt wird. Für JPEG-Dateien sind es die beiden Bytes 0xFF und 0xD9.

Nach Ende der Datenübertragung wird das JPEG-Bild in einem TigerJython GPanel-Grafikfenster angezeigt. (Es könnte auch eine andere Grafik-Engine verwendet werden.) Beim Schliessen des Grafikfensters durch Klicken auf das Close-Symbol der Titelleiste wird die Verbindung zum Server getrennt und der Server wartet im Listening-Zustand auf die nächste Client-Verbindung.

Programm:[►]

# CameraClient1.py
# Display image in TigerJython's GPanel graphics window

from threading import Thread
import socket, time
from gpanel import *

VERBOSE = False
IP_ADDRESS = "192.168.0.12"
IP_PORT = 22000

def debug(text):
    if VERBOSE:
        print "Debug:---", text

# --------------------- class Receiver ---------------------------
class Receiver(Thread):
    def run(self):
        debug("Receiver thread started")
        while True:
            try:
                rxData = self.readServerData()
            except:    
                debug("Exception in Receiver.run()")
                isReceiverRunning = False
                closeConnection()
                break
        debug("Receiver thread terminated")

    def readServerData(self):
        global isJPEG
        debug("Calling readResponse")
        bufSize = 4096
        data = ""
        while data[-2:] != "\xff\xd9":
        # eof tag for jpeg files (both chars must be in same block)
        # We are not sure 100% that this sequence is never embedded in image
        # but it is improbable to happen at the end of the data block
            try:
                blk = sock.recv(bufSize)
                if blk != None:
                    debug("Received data block, len: " + str(len(blk)))
                else:
                    debug("sock.recv() returned with None")
            except:
                raise Exception("Exception from blocking sock.recv()")
            data += blk
            print "JPEG received. Displaying it..."
            display(data)
# -------------------- End of Receiver ---------------------

def startReceiver():
    debug("Starting Receiver thread")
    receiver = Receiver()
    receiver.start()

def sendCommand(cmd):
    debug("sendCommand() with cmd = " + cmd)
    try:
        sock.sendall(cmd)
    except:
        debug("Exception in sendCommand()")
        closeConnection()

def closeConnection():
    debug("Closing socket")
    sock.close()

def connect():
    global sock
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    debug("Connecting...")
    try:
        sock.connect((IP_ADDRESS, IP_PORT))
    except:
        debug("Connection failed.")
        return False
    startReceiver()
    return True
 
def display(data):
    img = readImage(data)
    if img != None:
        image(img, 0, 0)   

def onExit():
    global isRunning
    isRunning = False
    dispose()

width = 640
height = 480
makeGPanel(Size(width, height))  
addExitListener(onExit)
sock = None
isRunning = True   

if connect():
    print "Connection established"
    time.sleep(1)
    while isRunning:
        print "Sending command 'go'..."
        sendCommand("go")
        time.sleep(2)
    print "Disconnecting now..."    
#    sendCommand("disconnect")    
    closeConnection()
else:
    print "Connection to %s:%d failed" %(IP_ADDRESS, IP_PORT)
print "done"    
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

Bemerkungen:
Wenn Sie TigerJython nicht installiert haben, können Sie die empfangene Bilddatei auf die Festplatte kopieren und mit einem Standard-Grafikprogramm (zum Beispiel in einem Webbrowser) anzeigen. Verwenden Sie einfach die Funktion saveData() statt display().

Programm:[►]

# CameraClient1.py

import webbrowser

def saveData(data, filename):
    file = open(filename, "wb")
    file.write(data)
    file.close()

def display(data):
    jpgFile = "c:/scratch/test.jpg"
    saveData(data, jpgFile)
    webbrowser.open(jpgFile)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)