Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

ss16:dirigieren_protokolle

Dies ist eine alte Version des Dokuments!


Protokolle Sitzungen

26. Mai 2016: erste Koordinierung

  • Materialsichtung
  • Kurze Einführung in die Fourier-Analysis durch Stefan und erhalten weiterführender Texte.
  • Installation des Programmes opencv: Erstellen eines Programmes, welches auf die Webcam zugreift um Videos aufzunehmen.
  • Erste Versuche mit einer Erweiterung des Programmes um Gesichter zu erkennen und die Videos auch zu speichern.

2. Juni 2016: Gesichtserkennung und weitere Analyse-Verfahren

Ziele

  • Haarcascaden zum funktionieren bringen
  • Haarcascaden verstehen
  • Mathematik hinter der Bewegungsanalyse verstehen
  • openCV auf allen Computern installieren
  • tubcloud einrichten
  • Video speichern

Protokoll

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.

9. Juni: Bewegung- und Objekterkennung

Ziele

  • Recherchieren nach Haarcascaden für Hände
  • Programm zur Bewegungserkennung entwickeln mithilfe des optischen Flusses

Arbeit

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.

Bewegungserkennung

Schoneinmal ein Ergebnisbild zum optischen Fluss:

Objekterkennung (Kopf und Hände)

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.

16. Juni: Zwischenpräsentation und ein bisschen programmieren

Ziele

-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

Arbeit

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.

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.

19. Juni: Extratermin (Sonntag)

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()

===== Fortschritt außerhalb des Labors ===== 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. <code python> 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) </code> Sowohl die dektektierten Koordinaten das Taktstockes, als auch die ermittelten Differenzen werden in Form von Listen an eine Funktion übergeben, welche diese Plotten kann. <code python> 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() </code> 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. ===== 23. Juni: erste Analyse des Dirigenten ===== ==== Ziele ==== * Extrema der Dirigierbewegungen erkennen und Signalisieren * Die Frequenz aus den Extrema extrahieren * Die Frequenz ausgeben ==== Arbeit ==== 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. <code python> 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)) </code> 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. <code python> if dx_nach_dt[position-1] > 0 and dx_nach_dt[position-2] ⇐ 0: cv2.circle(frame, (x_shape-20,y_shape2), 10, (255,0,0),10) if dx_nach_dt[position-1] < 0 and dx_nach_dt[position-2] >= 0: cv2.circle(frame, (20,y_shape2), 10, (255,0,0),10) if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] ⇐ 0: cv2.circle(frame, (x_shape2,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_shape2,20), 10, (255,0,0),10) </code> 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. <code python> if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] ⇐ 0: cv2.circle(frame, (x_shape2,y_shape-20), 10, (255,0,0),10) beat.append(time()) beat_position += 1 </code> 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. <code python> bpm = (beat[beat_position]-beat[beat_position-1])*60 </code> 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. <code python> cv2.putText(frame, str(int(bpm)), (x_shape-250, y_shape-5), 3, 6, (255,0,0),10) </code> 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. <code python> # -*- 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_shape2,y_shape2) #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_shape2,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.append1) #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_shape2), 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_shape2), 10, (255,0,0),10) if dy_nach_dt[position-1] > 0 and dy_nach_dt[position-2] ⇐ 0: cv2.circle(frame, (x_shape2,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_shape2,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 </code> ===== 30.06. Weiter gehts ===== ==== Ziele ==== * Oberziel: Takterkennung, notwendige Schritte hierfür: * Verbindung x- und y-Plot (bzw. von zwei Plots im allgemeinen) * Schwellenwerte für Richtungsänderungen festlegen * Plotanalyse erweitern * Charakteristische Piks in Taktdirigierbewegung finden *Kalibrierungsprogramm, sodass Erkennung des Dirigierstabes auf verschiedene Lichtverhältnisse angepasst werden kann *Erstes Mal verbinden der Dirigierbewegung mit Tonänderung ==== Arbeit ==== 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. ===== Außerhalb des Labors ===== 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, ===== 07.07. Erfassung weiterer Dirigierparameter ===== ==== Ziele ==== * Robins geniales Programm verstehen * Dynamik und Tempo erkennen * Dirigiermaßstäbe setzten * erkannte Dirigierbewegungen mit Musik verbinden * weitere Taktarten erkennen ===== 14.07. Musik? Musik! ===== ==== Ziele ==== *Dirigieren mit Musik verbinden. Hierfür ist nötig: *MIDI-Dateien verstehen *Schnittstelle definieren *Verbesserung des Ablesens des Tempos aus der Frequenz der Dirigierbewegungen *Vorbereitung der Präsentation fürs Wissenschaftsfenster am 18.07. ==== Protokoll ==== 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. ===== 18.07. Projektpräsentation im Wissenschaftsfenster ===== 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. ===== 21.07. endlich Musik! ===== 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. ====== Der Block in der Vorlesungsfreien Zeit ====== ===== 31.08. Tag 1 ===== ===== 01.09. Tag 2 ===== ===== 02.09. Tag 3 ===== Heute war nun der letzte Tag des Labors. Zu Beginn des Tages stellten wir fest, das die gestrigen Erweiterungen im Bereich der Umrechnung von der Dirigierfrequenz (Beats per Minute) in die Dauer der Miditicks und deren Weiterverarbeitung in ein regelmäßiges Signal zur Ansteuerung der Musik. Die Ansteuerung funktionierte nun ebenfalls. Probleme sind augetreten im Bereich der Beendigung von Stücken während des Stücks unter Windows. Max hat die Benutzeroberfläche entsprechend angepasst, so dass diese deutlich beutzerfreundlicher ist und besser aussieht.  Neue Benutzeroberfläche Zusätzlich zur Optimierung der Ringe und der Ausgabe von Frequenz, Taktart und Taktposition ist nun auch ein Hinweismenü integriert, welches bei Berührung einer Schaltfläche mit dem Taktstock erscheint. Die Ringe werden in diesem Fall ausgeblendet. Neues Informationsmenü Henriette hat unter anderem verschiedene Midi-Stücke für diverse Taktarten herausgesucht und es wurde eine Funktion geschrieben, welche automatisch per Zufall ein Stück zur entprechenden Taktart auswählt. Dies funzt soweit. Die letze Version des Programms innerhalb der offiziellen Projektarbeit ist im folgenden zu sehen.: <code python> # -*- coding: utf-8 -*- 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 from copy import deepcopy import pickle import midi import fluidsynth import threading import os #=====Object from Stefan=====# def test1(): fs = fluidsynth.Synth() fs.start(driver=„alsa“) sfid = fs.sfload(„FluidR3_GM.sf2“) fs.program_select(0, sfid, 0, 0) fs.noteon(0, 60, 50) fs.noteon(0, 67, 30) fs.noteon(0, 76, 30) time.sleep(1.0) fs.noteoff(0, 60) fs.noteoff(0, 67) fs.noteoff(0, 76) time.sleep(1.0) fs.delete() class Player(object): def init(self): self.d=[] self.fs = fluidsynth.Synth() self.fs.start(driver=„alsa“) self.sfid = self.fs.sfload(„FluidR3_GM.sf2“) self.fs.program_select(0, self.sfid, 0, 0) self.tick_duration = 0.01 def load(self, filename): 'lädt ein Midi-File mit Namen filename.' with open(filename,'rb') as f: self.d=midi.fileio.read_midifile(f) self.d.make_ticks_abs() def play(self): 'spielt die Datei in Echtzeit ab, die zugrundeliegende

	Zeiteinheit self.tick_duration lässt sich während des Abspielens
	ändern.'''
	self.stop = False
	for voice in self.d:
		voice_thread = threading.Thread(target=self.play_voice, args=(voice,))
		voice_thread.start()
	
def stopit(self):
	self.stop = True
	
def play_voice(self,voice):
	voice.sort(key=lambda e: -e.tick)
	tick_now = 0

	time_now = time.time()
	
	while len(voice)>0 and not self.stop :
		event=voice.pop()
		while event.tick>tick_now:
			#print event
			n_ticks= max(int((time.time()-time_now)//self.tick_duration)+1,event.tick-tick_now)
			#print n_ticks, n_ticks*self.tick_duration-time.time()+time_now
			time.sleep(n_ticks*self.tick_duration-time.time()+time_now)
			tick_now +=n_ticks
			time_now=time.time()
		
		self.send(event)	
		
def send(self, event):
	'''Diese Methode kommuniziert mit dem Synthesizer. Bisher sind
	nur drei Ereignistypen implementiert.'''
	if type(event) is midi.NoteOnEvent:
		self.fs.noteon(event.channel,event.data[0],event.data[1])
	elif type(event) is midi.NoteOffEvent:
		self.fs.noteoff(event.channel, event.data[0])
	elif type(event) is midi.ProgramChangeEvent:
		self.fs.program_change(event.channel, event.data[0])
	elif type(event) is midi.ControlChangeEvent:
		self.fs.cc(event.channel, event.data[0],event.data[1])
		

#=====Functions=====#

def waehle_stueck(taktart, lager_verzeichnis):

if taktart == "4_4":
	stuecke = os.listdir(lager_verzeichnis + "/4_4")
elif taktart == "2_2":	
	stuecke = os.listdir(lager_verzeichnis + "/2_2")
elif taktart == "3_4":
	stuecke = os.listdir(lager_verzeichnis + "/3_4")
else:
	print "Ungueltige Taktart"
auswahl = random.choice(stuecke)
return "/"+ lager_verzeichnis +  "/" + taktart + "/" + auswahl

def detect_color(frame): #Funktion, welche eine nach Farbe definierte Stelle ermittelt

hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)   #konvertiert frame in hsv
lower = np.array([hue_min, sat_min, val_min])  #legt untere schranke fest
upper = np.array([hue_max, sat_max, val_max])  #legt obere schranke fest
#Die Wert aus den Schranken werden aus globalen Variablen gezogen
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) > 20:												###es reicht eine bedingung zu erfüllen + schwellenwert
	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:																###if-bedingung unnötig, mittelpunkt erhält man durch tausch von x und y
	y_shape, x_shape, _ = frame.shape								#Es werden die Bildmaße ausgelesen
	position = (int(x_shape//2),int(y_shape//2))					#Als Position wird der Bildmittelpunkt gewählt
	
return position														#Ergebnis wird zurückgegeben

def get_values(cap):

while(True): 
	_, frame = cap.read()
	y_shape, x_shape, _ = frame.shape
	cv2.circle(frame,(x_shape//2, y_shape//2),25,(0,0,255),4)
	frame = cv2.flip(frame,1) #Spiegelung des frames an der Horizontalen
	cv2.putText(frame, "Bereit? [B]", (200,450), 2, 1, (255,255,255), 0)
	cv2.imshow('frame', frame)
	if cv2.waitKey(1) & 0xFF == ord('b'): #Signal, dass in Position, über das Drücken von k
		break
	if cv2.waitKey(1) & 0xFF == ord(' '): 
		cap.release()
		sys.exit()
ret, frame = cap.read() #frame ohne Kreis wird geholt
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) #Konvertierung in hsv
radius = 25 #Radius des zu untersuchenden Kreises
kreisliste = [] #diese Liste soll alle Pixel enthalten, die innerhalb des Kreises liegen
for i in range(x_shape): #das Bild wird pixelweise durchgegangen. Wenn der Pixel im Bild liegt, wir dieser registriert
	for j in range(y_shape):
		if np.sqrt((x_shape//2-i)**2+(y_shape//2-j)**2) < radius: #Hier Satz des Pythagoras zur Entfernungsbestimmung Pixel--Kreismittelpunkt
			kreisliste.append(hsv[j][i])
npkreisliste = np.array(kreisliste) #Konvertierung in Array zur weiteren Verarbeitung
hue = npkreisliste[:,0] #Array mit allen hue-Werten wird angelegt
sat = npkreisliste[:,1] #Array mit allen sat-Werten wird angelegt
val = npkreisliste[:,2] #Array mit allen val-Werten wird angelegt
	#Berechung der Grenzen erfolgt, wie folgt. Zuerst wird der Mittelwert aller Werte einer Eigenschaft gebildet.
	#Als nächstes wird jeweils die Standartabweichung ermittelt.
	#Die untere Schranke ist dann einfach der Mittelwert - die Standartabweichun
	#Die obere Schranke ist einfach der Mittelwert + die Standartabweichung.
	#Die Ergebnisse werden zur direkten Weiterverwendung in globale Variablen geschrieben.
	#Zur verwendung nach einem Programmneustart werden sie in eine datei ausgelagert.
global hue_min #globale Variable wird angelegt
hue_min = int(np.mean(hue) - np.std(hue)) #Wert wird ermittelt
global hue_max
hue_max = int(np.mean(hue) + np.std(hue))
global sat_min
sat_min = int(np.mean(sat) - np.std(sat))
global sat_max
sat_max = int(np.mean(sat) + np.std(sat))
global val_min
val_min = int(np.mean(val) - np.std(val))
global val_max
val_max = int(np.mean(val) + np.std(val))

sicherung = (hue_min,hue_max,sat_min,sat_max,val_min,val_max) #erzeuge Datensatztupel zur Abspeicherung für Pickle
output = open('hsv_werte.pkl', 'w') #die Ausgabedatei wird vorbereitet
pickle.dump(sicherung, output) #die Daten werden geschrieben
output.close() #der Output wird geschlossen

def schwellenwerte_einlesen():

global hue_min #die Schwellenwerte werden als global definiert
global hue_max
global sat_min
global sat_max
global val_min
global val_max
try: #es wird versucht / festgestellt, ob es bereits eine Datei mit gespeicherten Schwellenwerten gibt
	f = open("hsv_werte.pkl") #falls ja wird diese geoeffnet
	sicherung = pickle.load(f)
	hue_min, hue_max, sat_min, sat_max, val_min, val_max = sicherung #und die Daten entpackt
except: #falls nein, werden sehr komische Werte festgelegt, damit der Benutzter das Programm kalibriert
	val_max = 1
	val_min = 0
	sat_max = 1
	sat_min = 0
	hue_max = 1
	hue_min = 0

def detect_gesture(zu_untersuchen, figur):

figurlaenge = len(figur)

zu_untersuchen_kurz = [] #Diese Liste soll die letzten dirigierten Vektoren enthalten, die wichtig sind
for i in range(figurlaenge,0,-1):	#von index -1 bis index 0
	zu_untersuchen_kurz.append(zu_untersuchen[-i]) #letzten Eintraege werden ermittelt
zu_untersuchen_kurz = np.array(zu_untersuchen_kurz) #zur weiteren Verarbeitung Konvertierung in Array
	
figurensammlung = [] #verschiedene Kombinationen werden angelegt, schwierig sich das ohne zeichnung vorzustellen. Bsp.: aus [1,2,3] wird [[1,2,3],[2,3,1],[3,2,1]]
tmp = figur
for i in range(figurlaenge):
	tmp.append(tmp[0])
	del(tmp[0])
	anhaengen = deepcopy(tmp)
	figurensammlung.append(anhaengen)
figurensammlung = np.array(figurensammlung) #zur weiteren Verarbeitung Konvertierung in Array
for i in range(figurlaenge): #Der Vergleich findet statt
	if np.array_equiv(figurensammlung[i],zu_untersuchen_kurz): #elementweises Vergleichen von letztem Dirigat und verschiedenen Figur-Kombinationen
		return True #falls Figur gefunden

return False #falls Figur nicht gefunden

def detect_bpm(l):

global a
global b
global bpm
if l > 2:
	b = time.clock()
	bpm = 60/(b-a)
	a = b
else:
	a = time.clock()

def get_vec(old, new):

if new == None:
	return None
elif old == new:
	return None
elif old == 1:
	if new == 2:
		return (1,0)
	elif new == 3:
		return (1,-1)
	elif new == 4:
		return (0,-1)
elif old == 2:
	if new == 1:
		return (-1,0)
	elif new == 3:
		return  (0,-1)
	elif new == 4:
		return (-1,-1)
elif old == 3:
	if new == 1:
		return (-1,1)
	elif new == 2:
		return (0,1)
	elif new == 4: 
		return (-1,0)
elif old == 4:
	if new == 1:
		return (0,1)
	elif new == 2:
		return (1,1)
	elif new == 3:
		return (1,0)

def get_visual(pos):

if new == 1:
	cv2.circle(frame, c1, cthresh, color, 3)
	cv2.circle(clean, c1, cthresh, color, 3)
if new == 2:
	cv2.circle(frame, c2, cthresh, color, 3)
	cv2.circle(clean, c2, cthresh, color, 3)
if new == 3:
	cv2.circle(frame, c3, cthresh, color, 3)
	cv2.circle(clean, c3, cthresh, color, 3)
if new == 4:
	cv2.circle(frame, c4, cthresh, color, 3)
	cv2.circle(clean, c4, cthresh, color, 3)
	
cv2.circle(frame, c1, cthresh, color, 3)
cv2.circle(frame, c2, cthresh, color, 3)
cv2.circle(frame, c3, cthresh, color, 3)
cv2.circle(frame, c4, cthresh, color, 3)
cv2.circle(frame, pos, 4, (0,0,255), -1)	
cv2.circle(clean, pos, 4, (0,0,255), -1)
cv2.circle(frame, (640,480), cthresh, (0,255,255), -1)	

def get_length(a,b): #berechnet länge eines vektors von 2 punkten

ax, ay = a
bx, by = b
cx = ax - bx
cy = ay - by
l = np.sqrt(cx**2+cy**2)
return l

def in_circle(pos):

vec1 = get_length(c1, pos)		#sammlung der längen der vektoren von den mittelpunkten zur position
vec2 = get_length(c2, pos)
vec3 = get_length(c3, pos)
vec4 = get_length(c4, pos)
vecmenu = get_length(cmenu, pos)

lengths = [cthresh+1, vec1, vec2, vec3, vec4, vecmenu]

for x in lengths:
	if x < cthresh:
		ret = lengths.index(x)
		if ret == 5:
			return "menu"
		return ret
	

def detect_gesture(zu_untersuchen, figur): #diese Funktion versucht Figuren im Dirigat zu erkennen. Dafuer bekommt sie die Figurvektoren in richtiger Reihenfolge, sowie die Liste mit den dirigierten Vektoren uebergeben

figurlaenge = len(figur)

zu_untersuchen_kurz = [] #Diese Liste soll die letzten dirigierten Vektoren enthalten, die wichtig sind
for i in range(figurlaenge,0,-1):
	zu_untersuchen_kurz.append(zu_untersuchen[-i]) #letzten Eintraege werden ermittelt
zu_untersuchen_kurz = np.array(zu_untersuchen_kurz) #zur weiteren Verarbeitung Konvertierung in Array
	
figurensammlung = [] #verschiedene Kombinationen werden angelegt, schwierig sich das ohne zeichnung vorzustellen. Bsp.: aus [1,2,3] wird [[1,2,3],[2,3,1],[3,2,1]]
tmp = figur
for i in range(figurlaenge):
	tmp.append(tmp[0])
	del(tmp[0])
	anhaengen = deepcopy(tmp)
	figurensammlung.append(anhaengen)
figurensammlung = np.array(figurensammlung) #zur weiteren Verarbeitung Konvertierung in Array
for i in range(figurlaenge): #Der Vergleich findet statt
	if np.array_equiv(figurensammlung[i],zu_untersuchen_kurz): #elementweises Vergleichen von letztem Dirigat und verschiedenen Figur-Kombinationen
		return True #falls Figur gefunden

return False #falls Figur nicht gefunden

def beat_number(vec,figure):

a,b = vec

if figure == 'triangleA':
	if vec == (0,-1):
		return 1
	if vec == (-1,0):
		return 2 
	else:
		return 3
if figure == 'triangleB':
	if vec == (1,0):
		return 1
	if vec == (0,1):
		return 2 
	else:
		return 3
if figure == 'triangleC':
	if vec == (-1,0):
		return 1
	if vec == (0,1):
		return 2 
	else:
		return 3
if figure == 'triangleD':
	if vec == (0,-1):
		return 1
	if vec == (1,0):
		return 2 
	else:
		return 3
if figure == 'rectangleCCW':
	if vec == (0,-1):
		return 1
	if vec == (1,0):
		return 2
	if vec == (0,1):
		return 3
	if vec == (-1,0):
		return 4
if figure == 'rectangleCW':
	if vec == (0,-1):
		return 4
	if vec == (1,0):
		return 3
	if vec == (0,1):
		return 2
	if vec == (-1,0):
		return 1
if figure == 'line':
	if vec == (0,-1):
		return 1
	else:
		return 2
else:
	return False	
	

def check_gestures():

global figure
if len(veclist) > 4:
	if detect_gesture(veclist, rectangleCW):
			figure = "rectangleCW"
			counter()
	if detect_gesture(veclist, rectangleCCW):
			figure = "rectangleCCW"
			counter()
	if detect_gesture(veclist, triangleA):
			figure = "triangleA"
			counter()
	if detect_gesture(veclist, triangleB):
			figure = "triangleB"
			counter()
	if detect_gesture(veclist, triangleC):
			figure = "triangleC"
			counter()
	if detect_gesture(veclist, triangleD):
			figure = "triangleD"
			counter()
	if detect_gesture(veclist, line):
			figure = "line"
			counter()
	

def counter():

f = len(figure)				#bescheuertes verfahren, nötig da detect_gesture die länge der figurenliste ändert
if f > 10:
	r = 4 
else:
	r = int(np.sqrt(f))
	
cv2.putText(mirror, str(beat_number(veclist[-1],figure)) + "/" + str(r), (40,40), 2, 1, (255,255,255), 0)
return r

def bpm_mean(bpm):

if bpm > 40:
	bpmlist.append(bpm)
	if len(bpmlist) < 10:
		npbpm = np.array(bpmlist)
		mean = int(np.mean(npbpm))/10
		mean = int(mean)*20
		
	else:
		rel = bpmlist[-10:]
		npbpm = np.array(rel)
		mean = int(np.mean(npbpm))/10
		mean = int(mean)*10
	return mean
return bpm
	

def tick_laenge_ermitteln(bpm):

if (60/ bpm)/192 > 0:				#1 tick 0,1s# 1 viertel 19,2s#
	l=(60/ bpm)/192
	return l
	

def menu_visual(cap):

while(True): 
	_, frame = cap.read()
	position = detect_color(frame)
	
	new = in_circle(position)
	if new != "menu":
		break
		
	frame = cv2.flip(frame,1) 
	
	#===Menü===#
	cv2.circle(frame, (0,480), cthresh, (0,255,255), -1)	
	cv2.putText(frame, "INFO", (200,80), 2, 1, (255,255,255), 0)
	cv2.putText(frame, "Kalibrieren [K]", (200,130), 2, 1, (255,255,255), 0)
	cv2.putText(frame, "Pause/Start [P]", (200,170  ), 2, 1, (255,255,255), 0)
	cv2.putText(frame, "Beenden [Leer]", (200,210  ), 2, 1, (255,255,255), 0)
	#===Ende===#
	
	cv2.imshow('frame', frame)
	
	if cv2.waitKey(1) & 0xFF == ord(' '): 
		cap.release()
		sys.exit()
	

def waehle_stueck(taktart, lager_verzeichnis):

if taktart == 4:
	stuecke = os.listdir(lager_verzeichnis + "/4_4")
	art = "4_4"
elif taktart == 2:	
	stuecke = os.listdir(lager_verzeichnis + "/2_2")
	art = "2_2"
elif taktart == 3:
	stuecke = os.listdir(lager_verzeichnis + "/3_4")
	art = "3_4"
else:
	print "Ungueltige Taktart"
auswahl = random.choice(stuecke)
return  lager_verzeichnis +  "/" + art + "/" + auswahl

#=======gestures=======#

rectangleCW = [(1,0),(0,-1),(-1,0),(0,1)] rectangleCCW = [(1,0),(0,1),(-1,0),(0,-1)] triangleA = [(1,1),(0,-1),(-1,0)] triangleB = [(-1,-1),(1,0),(0,1)] triangleC = [(1,-1),(-1,0),(0,1)] triangleD = [(-1,1),(0,-1),(1,0)] line = [(0,1),(0,-1)] gestures = [rectangleCW, rectangleCCW, triangleA, triangleB, triangleC, triangleD, line]

#=======circles=======#

c1 = 2) c3 = 3) c2 = 4) c4 = 5) cmenu = (640,480) color = (10,10,255)

#=======variables=======#

global fnr, bpm, new

fnr = 0 bpm = 0 cthresh = 95 ref = (320,240) old = 0 new = 5 pp = 0 last_state = False

#=======lists=======#

veclist = [None] #liste beinhaltet richtungsänderungen bpmlist = []

#=======program=======#

cap = cv2.VideoCapture(0) schwellenwerte_einlesen()

while True:

_, frame = cap.read()	
clean = np.copy(frame)
position = detect_color(frame)

get_visual(position)

new = in_circle(position)
if new == "menu":
	menu_visual(cap)
	continue

vec = get_vec(old, new)

if vec != None and vec != veclist[-1]:
	veclist.append(vec)
	detect_bpm(len(veclist))
	
if new != None and new != old:
	old = new

bpm = bpm_mean(bpm)
	
frame = cv2.addWeighted(frame, 0.5, clean, 0.5, 0)
mirror = cv2.flip(frame,1)

check_gestures()
			
cv2.putText(mirror, str(int(bpm)), (560,40), 2, 1, (255,255,255), 0)

cv2.imshow("frame", mirror)
fnr += 1
if fnr == 100:
	p = Player()
	p.load(waehle_stueck(counter(), "../Midi"))
	p.play()
if fnr > 100:
	if bpm > 0:
		p.tick_duration = tick_laenge_ermitteln(bpm)
	
if cv2.waitKey(1) & 0xFF == ord('k'):
	get_values(cap)
"""if cv2.waitKey(1) & 0xFF == ord('p') and fnr > 100 and last_state == False:
	if pp == 0:
		p.stopit()
		pp = 1
	if pp == 1:
		p.play()
		pp = 0
	last_state = True
else:
	last_state = False"""
if cv2.waitKey(1) & 0xFF == ord(' '):
	if fnr > 100:
		p.stopit()
	print "herunterfahren"
	break	

cap.release() cv2.destroyAllWindows() </code>

1) head_x, head_y, stab_x, stab_y
2) 320+120),(240-120
3) 320-120),(240+120
4) 320-120),(240-120
5) 320+120),(240+120
ss16/dirigieren_protokolle.1472830007.txt.gz · Zuletzt geändert: 2016/09/02 17:26 von robinkrueger