Laser graffiti

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Nko
User
Beiträge: 13
Registriert: Sonntag 19. Oktober 2008, 18:02

Hallo zusammen,

"laser graffiti" ist die (mir nicht eigene) Idee, die Projektion eines Projektors mit einer Videokamera zu erfassen und darauf erfasste Laserreflexionen in Pinselstriche umzuwandeln (und zu projizieren). So kann man mit einem Laserpointer auf Hauswänden "malen". (Es gibt ein paar schöne Videos bei DuRohr.com)

Eine "Billigversion" in Python mit der Hilfe von "motion":
Das Programm "motion" (http://www.lavrsen.dk/twiki/bin/view/Motion/WebHome) kann in erfassten Bildern Pixeländerungen erkennen. Mit den folgenden Angaben (in ~/motion.conf)

Code: Alles auswählen

# Target base directory for pictures and films
# Recommended to use absolute path. (Default: current working directory)
target_dir /home/....../pics/
# Output pictures with only the pixels moving object (ghost images) (default: off)
output_motion on
# %K and %L = X and Y coordinates of motion center
jpeg_filename motionCaps%H%M%S_%q__%K_%L
kann "motion -c motion.conf" gestartet werden und jpegs mit dem gewünschten Filenamen werden generiert. Die Position des Bewegungszentrums ist im Filenamen enthalten (ist billig, aber motion kann sonst nur über seriell Informationen schicken und Das wirkliche Bild zu parsen soll die nächste Version werden).

Ich habe folgendes zusammengefrickelt, um die Positionen auszulesen und per pygame zu "malen":

Code: Alles auswählen

import pygame
from glob import glob
from os import system 

picturePath="./pics/motionCap*.jpg"

def drawMyCircle(window,center):
    """draws a white circle on 'window' at position 'center' with size 3""" 
    pygame.draw.circle(window, (255, 255, 255),center,3)
    pygame.display.flip()
    
def calcCenterList(filelist):
    """receives positions of movement by parsing all filenames in 'filelist'
    returns a list of positions
    ATTENTION: removes all files in 'filelist' from disc!!!"""    
    centers=[]
    if (filelist!=[]):
        for i in filelist:
            newx=int(i.split("_")[3])
            newy=int(i.split("_")[4].split("m")[0])
            centers.append((newx,newy))
            system("rm "+i+" &")
    return centers

if __name__=="__main__":
    pygame.init() 
    window = pygame.display.set_mode((640, 480)) 
 
    while True: 
        filelist=glob(picturePath)
        centers=calcCenterList(filelist)
        for i in centers:
            drawMyCircle(window,i)
Es funktioniert, leider bringt mir motion nur 8 Bilder pro Sekunde, obwohl er mehr können müsste. Ich glaube, das liegt aber an meiner (internen) USB-webcam. Aber mit acht Punkten pro Sekunde kann man an der Zimmerdecke langsam herummalen.
Vielleicht hat ja einer von Euch Lust, das Programm zu testen.

Obwohl "rm" nur ausgeführt wird, wenn filelist nicht leer ist, meldet das Betriebssystem manchmal: "rm: kann file nicht löschen, da nicht vorhanden". Das verstehe ich nicht, wie das sein kann.
Irgendwelche Kommentare zum Code?

Liebe Grüße, Nko
BlackJack

@Nko: Als erstes fällt natürlich auf, dass Namensgebung und Leerzeichensetzung nicht PEP8-Konform ist.

Klammern braucht man um Bedingungen nicht und `filelist` auf eine leere Liste zu prüfen ist auch überflüssig. Wenn die Liste leer ist, dann wird die ``for``-Schleife halt nicht durchlaufen, auch ohne dass man das vorher prüft.

`i` in einer Schleife an etwas anderes als ganze Zahlen zu binden ist keine gute Idee. Das erwarten Programmierer nicht. Und mehrfach das gleiche `split()`\en könnte man sich sparen.

Du hast es ja selbst schon als Warnung im Docstring geschrieben: Das berechnen von Mittelpunkten sollte nicht auch gleichzeitig Dateien löschen. Das ist unerwartet. Und eine Shell mit einem externen Programm zum Löschen von Dateien!? Dafür gibt es `os.remove()`. Das Problem könnte entstehen, wenn Du ein `glob()` ausführst und das einen Dateinamen erwischt, für den schon eine asynchrone "Lösch-Shell" läuft. Übrigens weiss ich jetzt gerade nicht, ob `glob()` eine Sortierung garantiert!?

Der Name `filelist` ist problematisch weil es erstens keine Liste von Dateien, sondern Datei*namen* ist, und zweitens sollte man vermeiden Typen in die Namen zu schreiben. Ein besserer Name wäre `filenames`. Das gleiche gilt für Funktionsnamen. `calculateCenters()` sagt genau so viel aus, man kann die Methode aber zu einer Generator-Funktion umbauen, ohne dass der Name falsch wird.
Nko
User
Beiträge: 13
Registriert: Sonntag 19. Oktober 2008, 18:02

BlackJack hat geschrieben:@Nko: Als erstes fällt natürlich auf, dass Namensgebung und Leerzeichensetzung nicht PEP8-Konform ist.
Danke, räusper, das mit dem Pep8 hab ich noch nicht so verinnerlicht. Da ist viel C-style dabei gewesen. Hab's also mal überarbeitet und auch einiges geändert. Jetzt läufts auch recht rund und ich hoffe es ist auch schöner zu lesen.

Jetzt hab ich noch was neues eingebaut: Bildschirm löschen ('n') und Quit ('q'). Ausserdem muss "motion" jetzt nicht mehr extern gestartet werden. Aber "killall" zum beenden ist nicht elegant. Ich check das nicht, wie ich den Prozess beenden soll. Einen Hinweis vielleicht?

Code: Alles auswählen

import pygame
import os
from glob import glob

picturePath = "/dev/shm/pics/motionCap*.jpg"
motionConfigFilename = "/.../motion.conf"

def drawMyCircle(window, center):
    """draws a white circle on 'window' at position 'center' with size 3""" 
    pygame.draw.circle(window, (255, 255, 255), center, 3)
    pygame.display.flip()
    
def parsePositionsFromFilenames(filenames):
    """receives positions of movement by parsing all filenames in 'fileNames'
    returns a list of positions"""    
    centers = []
    for name in filenames:
        splitName = name.split("_")
        newx = int(splitName[3])
        newy = int(splitName[4].split("m")[0])
        centers.append((newx, newy))
    return centers

def deleteFiles(filenames):
    """deletes all files in 'fileNames' from disc
    use with care!!!"""
    for filename in filenames:
        os.remove(filename)
    return

def main(window,path):
    """reads constantly all files from wildcarded string 'path'
    draws a circle on 'window' and reads some events"""
    quit = False
    while not quit: 
        filenames = glob(path)
        centres = parsePositionsFromFilenames(filenames)
        for coordinates in centres:
            drawMyCircle(window, coordinates)
        deleteFiles(filenames)
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_n:
                    window.fill((0, 0, 0))
                    pygame.display.flip()
                if event.key == pygame.K_q:
                    quit = True

if __name__ == "__main__":
    pygame.init() 
    window = pygame.display.set_mode((640, 480)) 
    pid = os.fork()
    if not pid:
        os.execvp("/usr/bin/motion",["motion", "-c", motionConfigFilename])
    main(window,picturePath)
    os.execvp("killall", ["killall", "motion"])
BlackJack

`os.kill()` hast Du jetzt nicht gefunden? Und selbst wenn Du ein externes Programm verwendest, warum dann nicht ``kill`` mit der PID füttern, die Du ja hast!?

Programme starten sollte man mit dem `subprocess`-Modul und nicht mit den diversen "low level"-Möglichkeiten in `os`.
iceman21
User
Beiträge: 41
Registriert: Sonntag 25. März 2007, 20:45
Kontaktdaten:

HALLO
Ich würde dein Programm rein aus interesse gerne mal testen. Hast du vielleicht ein kleines Beispiel video das du mal hochladen kannst? ich habe keine kamera hier. dann kann ich das mal laufen lassen :)
Danke
Antworten