Dies ist eine alte Version des Dokuments!
Nach der Festlegung der Ziele für die heutige Laborsitzung, richteten wir zunächst endlich die tubcloud ein und sorgten dafür, dass openCV auf allen PCs installiert ist. Dies hatte zuvor zu ein paar Problemen geführt, die aber behoben werden konnten. Anschließend teilten wir die Arbeit auf: Jette beschäftigte sich mit der Fourier-Analysis, Robin übernahm die Haarcascaden, Max schaute sich das Videoaufnahmeprogramm nocheinmal genauer an und Henriette versuchte herauszufinden, wie ein mit openCV aufgenommenes Video gespeichert werden kann.
Die Haarcascaden, welche zur Gesichtserkennung verwendet werden, konnten schnell zum Laufen gebracht werden. Problem war zuvor gewesen, dass die Datein beschädigt waren. Stefan stellte uns daraufhin seine vollständigen Haarcascade-Dateien zur Verfügung. Mit diesen funktionierte das Programm zur Gesichteserkennung problemlos, weshalb wir ein wenig mit diesem herumprobierten. Durch eine Eläuterung von Stefan zum Ende des Labors wurde uns dann auch klarer wie genau Haarcascade eigentlich funktioniert.
Mithilfe des folgenden Codes wird ein Video mit der Webcam erzeugt und Gesichter erkannt:
from __future__ import division from scipy import misc, ndimage from mpl_toolkits.mplot3d import Axes3D import math as m import random import time import matplotlib.pyplot as plt import numpy as np import cv2 import sys cascPath = sys.argv[1] faceCascade = cv2.CascadeClassifier(cascPath) cap = cv2.VideoCapture(0) while(True): # Capture frame-by-frame ret, frame = cap.read() # Our operations on the frame come here gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.cv.CV_HAAR_SCALE_IMAGE ) # Draw a rectangle around the faces for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # Display the resulting frame cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # When everything done, release the capture cap.release() cv2.destroyAllWindows()
Die Lösung des Videospeicherungsproblem dauerte hingegen etwas länger. Mit unserem ersten Code wurde zwar eine Videodatei erstellt, jedoch war diese leer und lies sich nicht durch die gängigen Videoplayer öffnen. Nach weiterer Recherche über fourcc und codec meinten wir die Lösung gefunden zu haben, aber immer noch wurde keine Videodatei erstellt. Das Speichern des Videos konnte schließlich durch eine Erweiterung des Codes um eine Zeile erreicht werden. Auch einigten wir uns auf den codec FFMpeg („FMP4“), da dieser auf den meisten Rechnern vorhanden ist.
Hier der Code:
cap = cv2.VideoCapture(0) # Define the codec and create VideoWriter object # Syntax Videowriterobjekt: cv2.cv.CV_FOURCC([filename, fourcc, fps, frameSize[, isColor]]) fps = frames per second #speichert video mit dateinamen output in laufendem ordner fourcc = cv2.cv.CV_FOURCC('F','M','P','4') #legt codec des videos fest out = cv2.VideoWriter('output.avi', fourcc, 24.0, (640,480)) while(cap.isOpened()): ret, frame = cap.read() #ret= returnvalue if ret==True: cv2.imshow('frame',frame) out.write(frame) if cv2.waitKey(1) & 0xFF == ord('q'): break else: break # Release everything if job is finished cap.release() out.release() cv2.destroyAllWindows()
Zusätzlich erreichen wir, dass aus einem Video, dass die Kamera aufnimmt, in Echtzeit ein Kantenbild erzeugt wird.
Im Zuge der Arbeit haben sich zwei Gruppen gebildet. Eine Untergruppe beschäftigte sich dabei mit der Bewegungserkennung (Jette und Henriette) und die zweite Gruppe (Max und Robin) mit der Erkennung von Kopf und Händen über Haarcascades.
Zu Beginn wurde nach entsprechenden Haarcascades im Internet gesucht. Auf Github standen solche Cascaden für Fäuste, Handflächen und Hände zur Verfügung. Nach dem erlernen der rudimentären Benutzung von git konnten diese heruntergeladen und verwendet werden. Zur Erkennung von Gesicht und Händen wurde das Programm weiter genutzt, welches in der letzten Sitzung bereits geschrieben wurde, um Gesichter zu erkennen. Das Programm wurde dementsprechend weiterentwickelt, um sowohl Gesicht, als auch Hände zu erkennen. Das weiteren wurde in das Programm eine Funktion integriert, welche die Koordinaten eines Objektes (z.B. Hand) über die Zeit plotten kann.
Als Ergebnis ist folgender Codeschnipsel zu nennen.
from __future__ import division from scipy import misc, ndimage from mpl_toolkits.mplot3d import Axes3D import math as m import random import time import matplotlib.pyplot as plt import numpy as np import cv2 import sys faceCascade = cv2.CascadeClassifier("face.xml") handCascade = cv2.CascadeClassifier("fist.xml") fi=0 cap = cv2.VideoCapture(0) head=[] right=[] left=[] def plotten(liste): fig = plt.figure() ax = fig.add_subplot(111, projection='3d') x =[] y =[] z =[] for i in liste: x_an, z_an, y_an = i x.append(x_an) y.append(y_an) z.append(z_an) ax.scatter(x, y, z, c='r', marker='o') ax.set_xlabel('X-Koordinate') ax.set_ylabel('Zeit') ax.set_zlabel('Y-Koordinate') plt.show() while(True): # Capture frame-by-frame ret, frame = cap.read() # Our operations on the frame come here gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.cv.CV_HAAR_SCALE_IMAGE ) lhand = handCascade.detectMultiScale( gray[:,320:], scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.cv.CV_HAAR_SCALE_IMAGE ) rhand = handCascade.detectMultiScale( gray[:,:320], scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.cv.CV_HAAR_SCALE_IMAGE ) if fi%4==0: for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) xcf=(x+w//2) ycf=(y+h//2) head.append((xcf,ycf,fi)) else: pass fi+=1 for (x, y, w, h) in rhand: cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) xch=(x+w//2) ych=(y+h//2) right.append((xch,ych,fi)) for (x, y, w, h) in lhand: x+=320 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2) xch=(x+w//2) ych=(y+h//2) left.append((xch,ych,fi)) # Display the resulting frame cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF == ord(' '): break plotten(left) # When everything done, release the capture cap.release() cv2.destroyAllWindows()
Der erstellte Plot zu einem Objekt sieht zum Beispiel wie folgt aus.
-Verhindern, dass Gesicht und Hand bei der Erkennung verwechselt werden. -Erkennung von rechter und linker Hand -Erkennung einer Hand und nicht nur einer Faust -optischer Fluss Vektor für Bewegungserkennung verwenden
Der erste Teil des Labors bestand heute zunächst aus den Zwischenpräsentationen. Wir zeigten den anderen Gruppen, was unsere bisherigen Ergebnisse sind und führten auch das Programm, welches Gesichter und Hände erkennt und den optischen Fluss vor. Danach erfuhren wir noch, was die anderen Gruppen momentan bearbeiten. Des Weiteren gab es einen kleinen Input von Stefan zu den Themen Debugging und Threads.
In der zweiten Hälfte arbeiteten wir dann weiter an unserem Projekt. Robin machte sich daran die Körpererkennung zu verbessern und Max und Henriette setzten sich an die Bewegungserkennung.
Zu Beginn legten wir uns darauf fest zunächst eine Bewegung nach oben oder unten zu erkennen und überlegten uns, was die Merkmale für diese Bewegungen sind. Zu Hilfe kam dabei auch das Objekterkennungsprogramm der vergangenen Woche, mit Hilfe dessen wir uns die Koordinaten einer geraden Bewegung nach oben/unten anschauen konnten. Dieses Programm erweiterten wir schließlich auch. Wenn eine Hand erkannt wird, werden jeweils die y-Koordinaten zweier aufeinanderfolgender Frames voneinander abgezogen. Ist diese Differenz positiv, haben wir eine Abwärtsbewegung und der Kasten, um die Hand färbt sich rot. Ist die Differenz hingegen negativ, haben wir eine Bewegung nach unten und der Kasten um die Hand färbt sich grün.
Folgender Code realisiert die Bewegungserkennung
from __future__ import division from scipy import misc, ndimage from mpl_toolkits.mplot3d import Axes3D import math as m import random import time import matplotlib.pyplot as plt import numpy as np import cv2 import sys handCascade = cv2.CascadeClassifier("fist.xml") cap = cv2.VideoCapture(0) i=0 while(True): # Capture frame-by-frame ret, frame = cap.read() # Our operations on the frame come here gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) hand = handCascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60), flags=cv2.cv.CV_HAAR_SCALE_IMAGE ) #Bewegungserkennung for (x, y, w, h) in hand: if i == 0: a = y i+=1 else: b = y #wir ziehen die beiden y-Koordinaten der Handposition voneinander ab diff = b-a #wenn die Differenz > 0 haben wir eine Abwärtsbewegung, der Kasten wird rot if diff > 0: cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) #wenn die Differenz < 0 haben wir eine Bewegung nach oben, der Kasten wird grün else: cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) a = b # Display the resulting frame cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF == ord(' '): break # When everything done, release the capture cap.release() cv2.destroyAllWindows()
Zusätzlich zu unserer ersten eigenen Bewegungserkennung erreichten wir durch Veränderung einiger Parameter eine bessere Erkennung von Hand und Gesichtern und, dass rechte und linke Händer erkannt werden.
Da aufgrund der Präsentationen leider nur wenig Zeit zur eingenen Projektarbeit geblieben war, beschlossen wir uns am Wochenende nocheinmal privat zur Weiterarbeit zu treffen.
Bei unserem Treffen außerhalb des Labors haben wir ein Programm entwickelt, welches es uns ermöglicht, Objekte bestimmter Farben zu erkennen. Hierbei spielt die Funktion cv2.inRange() eine wesentliche Rolle. Sie hilft uns eine Maske zu erstellen, die allen Werten der Quelle, welche innerhalb festgelegter Werte liegen, ein True zuzuordnen und allen Werten, die außerhalb liegen ein False. Hierbei ist es weitaus einfacher das zu analysierende Bild nicht im BGR-Farbraum zu betrachten, sondern im HSV-Farbraum. Dieser besteht ebenfalls aus 3 Werten, dem Hue, der Saturation und dem Value. Hiermit ist es uns einfacher möglich, die inRange-Funktion zu verwenden. Die verwendeten Werte müssen regelmäßig an die Umgebungsbedingungen angepasst werden, da das Programm sonst nicht anständig funktioniert. Die bitwise_and-Funktion ermöglicht es uns dann letztlich aus dem Originalbild nur die Pixelwerte anzeigen zu lassen, die auf der gleichen Position, wie ein True der Maske liegen. Um die Position des getrackten Objektes in einer Koordinate zu bestimmen nutzen wir noch numpy.mean(), da die Koordinate annähernd immer dem Mittelwert des Arrays der True-Postitionen entpricht (siehe Code). Hierbei spielt die Genauigkeit der zu Beginn gewählten Range-Werte eine nicht zu vernachlässigende Rolle.
# -*- coding: utf-8 -*- from __future__ import division from scipy import misc, ndimage import numpy as np import cv2 cap = cv2.VideoCapture(0) while True: _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) blur = cv2.blur(hsv,(5,5)) lower = np.array([170,120,90]) upper = np.array([180,255,255]) mask = cv2.inRange(blur, lower, upper) maskinv = cv2.bitwise_not(mask) res = cv2.bitwise_and(frame, frame, mask=mask) y, x = np.where(mask==255) if len(x)>150: px = np.mean(x) #mittelwert der x koordinaten px = int(px) py = np.mean(y) #mittelwert der y koordinaten py = int(py) cv2.circle(frame, (px,py), 6, (255,255,255) , -1) cv2.circle(frame, (px,py), 7, (0,0,0) , 2) else: pass cv2.imshow("frame", cv2.flip(frame,1)) #frame wird gespiegelt if cv2.waitKey(1) & 0xFF == ord(' '): break cap.release() cv2.destroyAllWindows()
''
Zwischen dem Letzten und dem entsprechenden Treffen sind einige Weiterentwicklungen zu vermeken. Es kam während letzten Treffens die grobe Idee auf, sich mit einer Art Ableitung der Bewegung zu beschäftigen. Um zu untersuchen, ob sich mit dieser etwas anfangen lässt, wurde ein Programmteil entwickelt, welcher sowohl die X-Koordinatendifferenz, als auch die Y-Koordinatendifferenz zwischen zwei aufeinanderfolgenden Bildern berechnet (die Koordinaten des detektierten Taktstocks) und in einer Liste speichert.
if position > 1: #Ableitungen bestimmen stab_x_old, stab_y_old = stab[position-2] dx_nach_dt.append(stab_x_old-stab_x) dy_nach_dt.append(stab_y_old-stab_y)
Sowohl die dektektierten Koordinaten das Taktstockes, als auch die ermittelten Differenzen werden in Form von Listen an eine Funktion übergeben, welche diese Plotten kann.
def plotten2d_ableitung(daten, art, ableitung): ableitung.append(0) #daten entpacken x_head = [] y_head = [] x_stab = [] y_stab = [] z = range(len(daten)) for i in daten: head_x, head_y, stab_x, stab_y = i x_head.append(head_x) y_head.append(head_y) x_stab.append(stab_x) y_stab.append(stab_y) #verarbeitung der daten auswahl = [x_head, y_head, x_stab, y_stab] auswahl_text = ['Kopf-X','Kopf-Y','Stab-X','Stab-Y'] x1 = auswahl[art] x2 = ableitung y1 = z y2 = z plt.subplot(2, 1, 1) plt.plot(y1, x1, 'bo-') plt.title('Bewegung und ihre Ableitung im Vergleich ' + auswahl_text[art]) plt.ylabel('Original-Bewegung ' + auswahl_text[art]) plt.grid() plt.subplot(2, 1, 2) plt.plot(y2, x2, 'r.-') plt.xlabel('Zeit (frame)') plt.ylabel('Ableitung '+ auswahl_text[art]) plt.grid() plt.show()
Bei der Untersuchung dieser Graphen fällt auf, dass ein Extremum im Koordinatenplot zu einem Vorzeichenwechsel in der Ableitung führt. Aus der Analysis ist der Zusammenhang in ähnlicher Weise bekannt. Es ist bekannt, dass ein Extremum vorliegen könnte, wenn die erste Ableitung gleich Null ist. In unserem Fall können wir uns jedoch diesea Erkenntnis nicht direkt zu nutze machen, da die Differenz zweier Punkte in der Regel nicht Null wird. Der Dirigent verharrt nicht in seiner Bewegung. Allerdings kann der Vorzeichenwechsel der Steigung durchaus detektiert werden. Diese Überlegungen wurden mit in das nächste Treffen genommen.
Zu erwähnen ist an dieser Stelle noch, dass die Bewegungsrichtung aufgrund der Array-Struktur tatsächlich gespiegelt ist.
Die Überlegungen zu der Bewegung des Dirigenten wurden in Code umgesetzt. Zu Beginn wurde ein if-Anweisung geschrieben, welche den Vorzeichenwechsel in der Ableitung von - zu + detektiert.
if dx_nach_dt[position-1] > 0 and dx_nach_dt[position-2] <= 0: cv2.putText(frame,'Hallo', (head_x, head_y), 1, 10, (255,0,0))
Sollte die Bedingung erfüllt sein würde das Wort 'Hallo' im Video an der Position des Kopfes angezeigt werden. Dies gelang. Der nächste Schritt war den Codeteil um die Bedingungen für die drei anderen Extrema zu erweitern. Würde eine Bedingung erfüllt sein, würde auf der entsprechenden Seite des Bildes ein blauer Kreis erscheinen. Auch dies gelang.
if dx_nach_dt[position-1] > 0 and dx_nach_dt[position-2] <= 0: cv2.circle(frame, (x_shape-20,y_shape//2), 10, (255,0,0),10) if dx_nach_dt[position-1] < 0 and dx_nach_dt[position-2] >= 0: cv2.circle(frame, (20,y_shape//2), 10, (255,0,0),10) if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] <= 0: cv2.circle(frame, (x_shape//2,y_shape-20), 10, (255,0,0),10) if dy_nach_dt[position-1] < 0 and dy_nach_dt[position-2] >= 0: cv2.circle(frame, (x_shape//2,20), 10, (255,0,0),10)
Der nächste Schritt war nun die Frequenz der Aufkommens eines bestimmten Extremas zu ermittel. Zu diesem Zweck wurde eine horizontale Bewegung gewählt, also in Y-Richtung. Das entsprechende Extremum tritt auf, wenn die Ableitung von - nach + wechselt. Um die Dauer zwischen zwei solcher Extrema zu bestimmen wurde die Systemzeit verwendet. Es wurde also time importiert und eine Liste angelegt, welche die Zeitpunkte enthält, zu denen ein Extremum auftritt. Wenn ein solches Auftritt wird an die Liste der entsprechende Zeitpunkt angehängt. Diese Aktion wurde in den oben aufgeführten Code eingebaut. Ebenfalls wurde sie Variable beat_position angelegt, welche angibt, wie viele solcher Extrema bereits registriert wurden.
if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] <= 0: cv2.circle(frame, (x_shape//2,y_shape-20), 10, (255,0,0),10) beat.append(time()) beat_position += 1
Um die Schläge Pro Minute (BPM) zu emitteln, welche in der Musik oft als Schnelligkeitsangabe verwendet werden, werden die Zeitpunkte von zwei aufeinanderfolgenden Extrema von einander abgezogen. Das Ergebnis wird dann in eine Frequenz umgewandelt (von der Periodenlänge) und mal 60 genommen, um den gewünschten Wert zu bestimmen.
bpm = (beat[beat_position]-beat[beat_position-1])*60
Diese Variable wurde erst im Terminal zum testen ausgegeben und soll nun auf dem Video erscheinen. Hierzu wurde die bereits am Anfang der Sitzung verwendete Funktion cv2.putText() verwendet und entsprechend modifiziert, dass diese den Wert in der unteren rechten Ecke ausgibt.
cv2.putText(frame, str(int(bpm)), (x_shape-250, y_shape-5), 3, 6, (255,0,0),10)
Dies gelang und es konnten aus dem Dirigat in Y-Richtung die Schläge pro Minute abgeleitet werden.
(siehe Bild)
Die cv2.flip()-Funktion, welche das Bild spiegelt wurde vor die cv2.putText()-Funktion gesetzt, damit der Wert ungespiegelt ausgegeben wird.
Und nun der gesamte Code zum nachvollziehen und eingliedern der einzelnen Code Teile.
# -*- encoding: utf-8 -*- # Technische Universität Berlin # mathematisch-naturwissenschaftliches Labor Mathesis # die 'Dirigieren'-Gruppe from __future__ import division from scipy import misc, ndimage from mpl_toolkits.mplot3d import Axes3D import math as m import random from time import * import matplotlib.pyplot as plt import numpy as np import cv2 import sys def plotten3d_gesamt(daten): #Funktion, welche die Daten plotten kann fig = plt.figure() ax = fig.add_subplot(111, projection='3d') x_head = [] y_head = [] x_stab = [] y_stab = [] z = range(len(daten)) for i in daten: head_x, head_y, stab_x, stab_y = i x_head.append(head_x) y_head.append(head_y) x_stab.append(stab_x) y_stab.append(stab_y) ax.scatter(x_head, z, y_head, c='b', marker='x') ax.scatter(x_stab, z, y_stab, c='g', marker='^') ax.set_xlabel('X-Koordinate') ax.set_ylabel('Zeit') ax.set_zlabel('Y-Koordinate') plt.show() def face_rec(cascade, bild): #Funktion, welch Gesichter erkennt und sich für eines entscheidet gray = cv2.cvtColor(bild, cv2.COLOR_BGR2GRAY) #Konvertierung nach Grau faces = cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60), flags=cv2.cv.CV_HAAR_SCALE_IMAGE) temp = [] x_old, y_old, w_old, h_old = head[position-1] for i in range(len(faces)): x_new, y_new, w_new, h_new = list(faces[i]) temp.append(np.sqrt(x_old*x_new+y_old*y_new)) if temp == []: comp_position = head[position] elif min(temp) < 300: comp_position = faces[temp.index(min(temp))] else: comp_position = head[position] return comp_position def zeichne_rechteck(frame, position, farbe, dicke=2): #Funktion, welche ein Rechteck zeichnet x, y, w, h = position cv2.rectangle(frame, (x, y), (x+w, y+h), farbe, dicke) def color_rec(frame): #Funktion, welche eine nach Farbe definierte Stelle ermittelt # -> Die Werte können nun wie im Zeichenprogramm (z.B. GIMP) gewählt werden hue_low = 95 #0-360 hue_up = 120 #0-360 sat_low = 50 #0-100 sat_up = 75 #0-100 val_low = 30 #0-100 val_up = 75 #0-100 -> Hier eingestellt für grünen Stift hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) #konvertiert frame in hsv lower = np.array([hue_low/2,255*sat_low/100,255*val_low/100]) #legt untere schranke fest upper = np.array([hue_up/2,255*sat_up/100,255*val_up/100]) #legt obere schranke fest mask = cv2.inRange(hsv, lower, upper) #erstellt maske, welche für werte innerhalb des intervalls #den wert 1 annimmt, sonst 0 y_werte, x_werte = np.where(mask == 255) #Es werden die x- und y-Werte ausgelesen, welche ein True (255) bekomen haben if len(x_werte) != 0 and len(y_werte) != 0: y_mittel = int(np.mean(y_werte)) #Es wird der Mittelwert aus allen y-Werten gebildet x_mittel = int(np.mean(x_werte)) #Es wird der Mittelwert aus allen x-Werten gebildet position = (x_mittel, y_mittel) #Die Mittlere Position aller Trues entspricht dem Tupel beider Mittelwerte else: position = (0,0) if position == (0,0): #Sollten keine Trues gefunden werden (Gesamtmittelwert = (0,0)) x_shape, y_shape, _ = frame.shape #Es werden die Bildmaße ausgelesen position = (x_shape//2,y_shape//2) #Als Position wird der Bildmittelpunkt gewählt return position #Ergebnis wird zurückgegeben def zeichne_kreis(frame, position): #Funktion, welche einen Kreis zeichnet cv2.circle(frame,position, 25, (0,0,255), 4) def plotten2d_ableitung(daten, art, ableitung): ableitung.append(0) #daten entpacken x_head = [] y_head = [] x_stab = [] y_stab = [] z = range(len(daten)) for i in daten: head_x, head_y, stab_x, stab_y = i x_head.append(head_x) y_head.append(head_y) x_stab.append(stab_x) y_stab.append(stab_y) #verarbeitung der daten auswahl = [x_head, y_head, x_stab, y_stab] auswahl_text = ['Kopf-X','Kopf-Y','Stab-X','Stab-Y'] x1 = auswahl[art] x2 = ableitung y1 = z y2 = z plt.subplot(2, 1, 1) plt.plot(y1, x1, 'bo-') plt.title('Bewegung und ihre Ableitung im Vergleich ' + auswahl_text[art]) plt.ylabel('Original-Bewegung ' + auswahl_text[art]) plt.grid() plt.subplot(2, 1, 2) plt.plot(y2, x2, 'r.-') plt.xlabel('Zeit (frame)') plt.ylabel('Ableitung '+ auswahl_text[art]) plt.grid() plt.savefig("dirigieren_plot1") plt.show() faceCascade = cv2.CascadeClassifier("face.xml") #Cascade fürs Gesicht wird geladen global position #Variable, welche die Frames durchläuft position = 0 cap = cv2.VideoCapture(0) #Angeschlossene Kamera wird geöffnet ret, frame = cap.read() #Erster Frame wird abgerufen y_shape, x_shape, z_shape = frame.shape #Videomaße für den Kopfentwicklungspunkt werden ausgelesen global head #Liste der Kopfpositionen head=[(x_shape//2,0,0,0)] #Entwicklungspunkt für den Kopf global stab #Liste der Taktstockpositionen stab = [] global daten #Liste der wichtigsten Daten daten = [] dx_nach_dt = [0] #Liste für Ableitungen dy_nach_dt = [0] #Liste für Ableitungen beat = [1] #Liste für Zeitpunkte beat_position = 0 #Extremazähler while(True): ret, frame = cap.read() #Holt Frame head.append(face_rec(faceCascade,frame)) #Ermittelte Kopfposition wird registriert (mit Hoehe und Breite) zeichne_rechteck(frame,head[position],(255,0,0),2) #Rechteck wird an aktueller Kopfposition gezeichnet stab.append(color_rec(frame)) #Ermittelte Taktstockposition wird registriert zeichne_kreis(frame,stab[position]) #Kreis wird an aktueller Taktstockposition gezeichnet head_x, head_y,_,_ = head[position] #die 'wichtigen' Daten (die aktuelle Position) werden aus der Kopfposition extrahiert stab_x, stab_y = stab[position-1] #die aktuellen Daten werden aus der Taktstockposition extrahiert daten.append((head_x, head_y, stab_x, stab_y)) #die extrahierten Daten werden zur weiteren verarbeitung gespeichert if position > 1: #Ableitungen bestimmen stab_x_old, stab_y_old = stab[position-2] dx_nach_dt.append(stab_x_old-stab_x) dy_nach_dt.append(stab_y_old-stab_y) if dx_nach_dt[position-1] > 0 and dx_nach_dt[position-2] <= 0: #Auf Extremum Testen cv2.circle(frame, (x_shape-20,y_shape//2), 10, (255,0,0),10) #Entsprechenden Kreis ausgeben if dx_nach_dt[position-1] < 0 and dx_nach_dt[position-2] >= 0: cv2.circle(frame, (20,y_shape//2), 10, (255,0,0),10) if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] <= 0: cv2.circle(frame, (x_shape//2,y_shape-20), 10, (255,0,0),10) beat.append(time()) #Zeitpunkt registrieren beat_position += 1 #Extremazähler erhöhen if dy_nach_dt[position-1] < 0 and dy_nach_dt[position-2] >= 0: cv2.circle(frame, (x_shape//2,20), 10, (255,0,0),10) frame = cv2.flip(frame,1) bpm = (beat[beat_position]-beat[beat_position-1])*60 #Schläge pro Minute ermitteln cv2.putText(frame, str(int(bpm)), (x_shape-250, y_shape-5), 3, 6, (255,0,0),10) position +=1 #Framenummer wird um 1 erhöht cv2.imshow('frame',frame) #Ergebnis wird angezeigt if cv2.waitKey(1) & 0xFF == ord('q'): #'q' zum Beenden drücken break plotten3d_gesamt(daten) #die Daten werden geplottet plotten2d_ableitung(daten, 2, dx_nach_dt) plotten2d_ableitung(daten, 3, dy_nach_dt) cap.release() cv2.destroyAllWindows() #THE ENDE
Für die Programmierarbeit teilten wir uns wieder in zwei Teilgruppen auf. Leider stellten sich diese Mal die technischen Probleme als größte Herausforderung heraus, sodass ein Teil der Gruppe leider aufgrund von fehlenden Netzkabeln, nicht funktionierender Webcam und nicht installiertem CV2Programm auf den Laborrechnern relativ wenig arbeiten konnte.
Nach der letzten Laborsitzung hat unser Programm einen großen Fortschritt erziehlt. Robin hat sich zu Hause hingesetzt und einige tolle Erweiterungen hinzugefügt. So wird zu Beginn jetzt eine Kalibrierung des Dirigentenstabes vorgenommen,
Nach der Festlegung der Zeile teilten wir uns in Gruppen auf. Max nahm sich der Tempoerkennung an. Der Rest, Jette, Robin und Henriette, beschäftigten sich mit den MIDI-Dateien. Als Quelle für unsere Musikdateien fanden wir relativ schnell die Seite www.midiworld.com, welche MIDI-Dateien bekannter Musikstücke gratis zum download zur Verfügung stellt. Im folgenden versuchten wir mehr über den Aufbau und die Manipulation von MIDI-Dateien herauszufinden. Leider findet an hierzur nur sehr wenige Informationen online. Da am Montag die Präsentation unseres Projektes im Wissenschaftsfenster bevor stand, begannen Robin und Henriette schließlich mit der Ausarbeitung der Präsentation. Jette schrieb weiter am Programm und erreichte, dass dieses nun die einzelnen Taktschläge von eins bis vier zählen kann.
Am Montag war im Wissenschaftsfenster die Präsentation der Projekte aus den Laboren. Robin, Max und Henriette stellten unser Projekt „Das Orchester ist Programm“ den anderen MINTgrünlern vor. In der Präsentation stellten wir die Entstehung unseres Programmes dar von unseren ersten Versuchen bis zur aktuellsten Version. Dabei kam die Präsentation und unsere Idee gut an. Nur dem Triport mussten wir uns in der Endabstimmung geschlagen geben und bekamen den zweiten Platz in der Abstimmung über das beste Projekt.
Heute war wieder eine eher technische Sitzung. Stefan hatte uns ein Programm zur Verfügung gestellt, mit Hilfe dessen man MIDI-Dateien abspielen und verändern kann. Jedoch musste hierfür zunächst auf allen Rechnern die notwendigen Pythonpakete geladen werden. Erforderlich waren pyaudio, pygame, python midi und music21. Leider verlief die Installation wie meist nicht s reibungslos und brauchte Zeit. Aber jetzt können wir mit allen Laptops Midi-Dateien abspielen und auch bearbeiten. Ab der nächsten Sitzung kommen wir also unserem Ziel näher: Das Anpassen der Musik nach den Dirigierbewegungen.