Videotagebuch

Du hast eine Idee für ein Projekt?
Antworten
fiberkill
User
Beiträge: 10
Registriert: Dienstag 5. Februar 2013, 20:29

Hallo zusammen,
Ich bin noch Anfänger, was die Programmierung in Python betrifft, also entschuldigt bitte meinen "Quick & dirty" Code.
Ich möchte ein Art Video-Tagebuch schreiben.
Ablauf:
1. der User drückt die Taste "r" um die Aufnahme zu starten.
2. Es läuft ein 5-Skunden Video mit einem Countdown ab.
3. Die Aufnahme startet und es wird im Aufnahmefenster ein Countdown von 20 Sek. angezeigt.
4. nach der Aufnahme wird ein Video angezeig, das die Aufnahme bestätigt.

Das Ganze funktioniert bereits alles, bis auf die Aufnahme des Tons.

Dazu verwende ich Hauptsächlich das OpenCV Modul.
Leider ist es mit diesem Modul nicht möglich den Sound meiner Webcam aufzunehmen.
Ich stoße momentan an meine Grenzen, was die Programmierung betrifft.

Nun meine Frage:
Wie kann ich den Ton und das Video meiner Webcam gleichzeitig Aufzeichnen, denn ein Video-Tagebuch ohne Ton ist nichts anderes als ein Stummfilm :(


Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

while True:
	
	# Module fuer den Timer und die Kamera laden
	from time import time, sleep, localtime
	import threading, cv, cv2, sys
	
	# Funktionen definieren
	def lifestream():
		# Kameraeinstellungen definieren
		capture = cv.CaptureFromCAM(0)
		fps = 25
		w, h = 720, 540

		while True:
			# Texteinblendung
			## Farbdefinition des eingeblendeten Textes
			r=0 # Rotwerte
			g=255 # Gelbwerte
			b=0 # Blauwerte

			## Textposition
			tx = 40 # X-Position von Links
			ty = 40 # Y-Position von Oben
			
			frame = cv.QueryFrame(capture)
			font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, 20)
			cv.PutText(frame, "Zur Aufnahme Taste r druecken", (tx,ty),font, (b,g,r))
		
			cv.NamedWindow('capture', 0)
			cv.ResizeWindow( "capture", 720, 540 )
			cv.ShowImage("capture", frame)
			cv.MoveWindow ("capture", 550,400)
			key=cv.WaitKey(10)
			
			# Tastatureingaben pruefen
			# pruefen ob "r" gedrueckt wurde
			if key & 255 == 114:
				break 
			# pruefen ob "ESC" gedrueckt wurde
			elif key & 255 == 27:
				sys.exit()		
			# pruefen ob "q" gedrueckt wurde
			elif key & 255 == 113:
				sys.exit()
				
	def countdown5():
		vidFile = cv.CaptureFromFile( 'Countdown.mpg' )
		nFrames = int(  cv.GetCaptureProperty( vidFile, cv.CV_CAP_PROP_FRAME_COUNT ) )
		fps = cv.GetCaptureProperty( vidFile, cv.CV_CAP_PROP_FPS )
		waitPerFrameInMillisec = int( 1/fps * 1000/1 )

		for f in xrange( nFrames ):
			frameImg = cv.QueryFrame( vidFile )
			cv.ShowImage( "capture",  frameImg )
			cv.ResizeWindow( "capture", 720, 540 )
			cv.MoveWindow ( "capture", 550,400 )
			key = cv.WaitKey( waitPerFrameInMillisec  )
			if key & 255 == 27:
				sys.exit()
	
	def recording():
		
		# Startzeit ermitteln und in lesbaren String umwandeln
		zeit = localtime()
		s = "%02i:%02i:%02i   %02i-%02i-%02i" % (zeit[3],zeit[4],zeit[5],zeit[2],zeit[1],zeit[0])
		global fn
		fn = "%02i_%02i_%02i__%02i_%02i_%02i" % (zeit[3],zeit[4],zeit[5],zeit[2],zeit[1],zeit[0])
		aufnahmezeit=str(s)
		
		# Video-Verzeichis muss mit abschiessendem / abgegeben werden
		videodir="./Tagebuchvideos/"
		
		
		# Kameraeinstellungen definieren
		capture = cv.CaptureFromCAM(0)
		fourcc = cv.CV_FOURCC('M','J','P','G')
		fps = 25
		w, h = 720, 540
		stream = cv.CreateVideoWriter(videodir + fn + "_Video.avi", fourcc, fps, (w, h))

		# Hier wird die Dauer der Aufnahme in Sek. eingestellt
		recordingtime = 20
		recstart = int(time())

		while True:
				now = int(time())
				recdiff = now - recstart
				counter = recdiff - recordingtime
				if counter <= 0 :
					# hier wir der Timer und die Aufnahme gestartet.
					rectime = recordingtime
					frame = cv.QueryFrame(capture)
			
					# Videostream schreiben
					# Texteinblendung in Datei
					## Farbdefinition des eingeblendeten Textes
					r=255 # Rotwerte
					g=255 # Gelbwerte
					b=255 # Blauwerte

					## Textposition
					tx = 450 # X-Position von Links
					ty = 330 # Y-Position von Oben
					font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, .7, .7, 0, 1, 20)
					cv.PutText(frame, aufnahmezeit, (ty,tx),font, (b,g,r))
					cv.WriteFrame(stream, frame)
	
					## Farbdefinition des eingeblendeten Textes
					r=255 # Rotwerte
					g=0 # Gelbwerte
					b=0 # blauwerte

					## Textposition
					tx = 450 # X-Position von Links
					ty = 50 # Y-Position von Oben
					font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 2, 2, 0, 4, 20)
					cv.PutText(frame, str(counter)[1:], (ty,tx),font, (b,g,r))
		
					# Videofenster anzeigen
					cv.ShowImage("capture", frame)
		
					# Fenster positionieren
					cv.MoveWindow ("capture", 550,400)
					key = cv.WaitKey(10)
					if key & 255 == 27:
						sys.exit()
		
				else :
					break

	def confirm_rec():
		vidFile = cv.CaptureFromFile( 'VielenDank.mpg' )
		nFrames = int(  cv.GetCaptureProperty( vidFile, cv.CV_CAP_PROP_FRAME_COUNT ) )
		fps = cv.GetCaptureProperty( vidFile, cv.CV_CAP_PROP_FPS )
		waitPerFrameInMillisec = int( 1/fps * 1000/1 )

		#print 'Num. Frames = ', nFrames
		#print 'Frame Rate = ', fps, ' frames per sec'

		for f in xrange( nFrames ):
			frameImg = cv.QueryFrame( vidFile )
			cv.ShowImage( "capture",  frameImg )
			cv.ResizeWindow( "capture", 720, 576 )
			cv.MoveWindow ( "capture", 550, 400 )
			key = cv.WaitKey( waitPerFrameInMillisec  )
			if key & 255 == 27:
				sys.exit()
				
	# Ausgabe
	lifestream()
	countdown5()
	recording()
	confirm_rec()
Vielleicht fällt Euch was ein.
Solltet Ihr irgend eine Idee haben, wäre es nett, wenn Ihr es mir verständlich erklären könntet.

Vielen Dank im Voraus

Gruß FK
Zuletzt geändert von Anonymous am Montag 18. Februar 2013, 13:56, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@fiberkill: Bevor Du das noch um Ton erweiterst, könntest Du den vorhandenen Quelltext überarbeiten. Am besten nach der Lektüre vom Style Guide for Python Code. Einrückung, Namensgebung, und Leerzeichensetzung halten sich da zum Beispiel nicht dran.

Importe sollten am Anfang des Moduls stehen.

Überhaupt wird *alles* in der äussersten ``while``-Schleife unnötigerweise immer wieder ausgeführt, also nicht nur die Importe, sondern auch die ganzen Funktionsdefinitionen. Die ändern sich ja nicht, also macht das keinen Sinn.

Dann sind Funktionen nicht einfach Namen für Stücke von Quelltext die als Sprungziele dienen. Werte ausser Konstanten sollten Funktionen nur als Argumente und Rückgabewerte betreten und verlassen. Du hast kein einziges Argument und kein einziges ``return`` in deinem Quelltext. Das ist definitiv ein „code smell”. Da es mindestens ein ``global`` gibt, müsste es auch Argumente geben um eben dieses ``global`` los zu werden. Das Schlüsselwort ``global`` solltest Du am besten erst einmal vergessen, denn es gibt in 99,9% der Fälle eine sauberere Lösung mit Funktionsargumenten (oder Klassen).

Die Kommentare sind teilweise trivial — die braucht man dann auch nicht — und teilweise sind die Kommentare nur notwendig weil schlechte Namen gewählt wurden. Abkürzungen bei Namen sollte man vermeiden, wenn die Abkürzung nicht allgemein bekannt ist. Wenn man statt `tx` und `ty` zum Beispiel beide Werte in einem Tupel zusammengefasst an den Namen `text_position` binden würde, könnte man sich die Kommentare an der Stelle sparen. Ähnliches gilt für die Tastentests wenn man sich dafür eine Funktion und Konstanten für die „magischen” Tastenwerte definiert.

Funktionen sollten in der Regel nicht `sys.exit()` aufrufen. Dadurch sind sie nicht flexibel wiederverwendbar, oder interaktiv oder automatisiert testbar wenn die einfach so den gesamten Prozess abbrechen dürfen. Hier könnte man mit Rückgabwerten arbeiten, zum Beispiel ein Wahrheitswert, der angibt ob abgebrochen werden soll oder nicht. `sys.exit()` ist etwas für eine Hauptfunktion oder solche die von ihr aufgerufen werden, und nicht nicht wirklich einzeln verwendbar sind.

Der Quelltext enthält einiges an „magischen” Zahlen was in Konstanten gehört, damit man nicht überall im Programm die Werte suchen muss, wenn man diese irgendwann einmal ändern möchte.

Die Funktionen enthalten viel fast identischen Quelltext. Das verletzt eines der wichtigsten Prinzipien beim Programmieren, dass „DRY”-Prinzip: Don't Repeat Yourself. Wenn an dem wiederkehrenden Muster etwas verändert werden soll oder muss, dann muss man das an vielen verschiedenen Stellen tun und das auch noch konsistent. Das ist eine häufige Fehlerquelle, dass man nicht alles erwischt, oder nicht alles so ändert, dass an jeder Stelle der gleiche Effekt eintritt.

In der `recording()`-Funktion muss `videodir` nur deshalb mit einem abschliessenden '/' definiert werden, weil zum zusammensetzen von Pfaden nicht die `os.path.join()`-Funktion verwendet wird. Pfadkomponenten sollte man deshalb nicht einfach mit ``+`` verbinden.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich denke auch dass ich sowas wie Webcam lesen und Video abspielen wohl am ehesten mit GStreamer machen wuerde... du brauchst ja offenbar keine Verarbeitung die OpenCV bereitstellen wuerde.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
fiberkill
User
Beiträge: 10
Registriert: Dienstag 5. Februar 2013, 20:29

Hallo,
@BlackJack: Du hast vollkommen recht, der Code muss natürlich überarbeitet werden, aber mir war es erst mal wichtig das Ganze zum laufen zu bringen.

@Leonidas:
Hab da noch einige Fragen:
1. Kann man mit den GStreamer-Bindings auch Ton und Video gleichzeit aufnehmen ?
2. Kann ich wärend der Aufnahme Texteinblendungen machen, die nicht mit aufgenommen werden ?


Gruß KF
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich nehme schon an dass man Ton und Bild gleichzeitig aufnehmen kann, das wäre ja sonst ein reichlich sinnloses Multimedia-Framework. In GStreamer kann man sich ja viele Sachen aus Streams zusammensetzen. Ob Einblendungen gehen, keine Ahnung.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
fiberkill
User
Beiträge: 10
Registriert: Dienstag 5. Februar 2013, 20:29

Grundsätzlich gehen Einblendungen schon, aber ob die mit gespeichert werden kann ich noch nicht sagen !
Antworten