class ScaleNoteSheetPreprocessor(IPreprocessingUnit): def __init__(self, averageHeight, targetLineHeight=100, interpolation=cv2.INTER_CUBIC, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("Preprocessing_Component_Logger") self.__logger.info("Starting __init__()", "ScaleNoteSheetPreprocessor:__init__") self.__averageHeight = averageHeight self.__targetLineHeight = targetLineHeight self.__interpolation = interpolation self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finished __init__()", "ScaleNoteSheetPreprocessor:__init__") def preProcess(self, mat): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! Example ------- >>> ScaleNoteSheetPreprocessor.preProcess(mat) """ self.__logger.info("Starting preProcess()", "ScaleNoteSheetPreprocessor:preProcess") # Berechnung der neuen Bildgroesse currentImageHeight = mat.shape[0] currentImageWidth = mat.shape[1] newImageHeight = int(currentImageHeight / (-self.__averageHeight) * self.__targetLineHeight) newImageWidth = int(currentImageWidth / (-self.__averageHeight) * self.__targetLineHeight) # Fuehrt eine Skalierung auf die angegeben Groesse aus resizeMat = cv2.resize(mat, (newImageWidth, newImageHeight), interpolation=self.__interpolation) # Binarisieren des Bildes, da durch die Skalierung und das damit # stattfindende Interpolieren zwischen Schwarz und Weiß verschiedene Graustufen entstehen _, resultMat = cv2.threshold(resizeMat, 127, 255, cv2.THRESH_BINARY) # Anzeigen des Ergebnisses Note if (self.__showImagesInWindow): cv2.imshow("ScaleNoteSheetPreprocessor", resultMat) cv2.waitKey(0) self.__logger.info("Finished preProcess() with following Image: ", "ScaleNoteSheetPreprocessor:preProcess", resultMat) return resultMat
class GaussianBlurNoiseFilterPreprocessor(IPreprocessingUnit): def __init__(self, ksize, sigmaX, sigmaY=0, borderType=cv2.BORDER_DEFAULT, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("Preprocessing_Component_Logger") self.__logger.info("Starting __init__()", "GaussianBlurNoiseFilterPreprocessor:__init__") self.__ksize = ksize self.__sigmaX = sigmaX self.__sigmaY = sigmaY self.__borderType = borderType self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finished __init__()", "GaussianBlurNoiseFilterPreprocessor:__init__") def preProcess(self, mat): """ Führt den Rauschfilter gaussian blur auf eine Bildmatrix aus. Parameters ---------- mat : Mat Die Matrix auf welche der Rauschfilter gaussian blur ausgeführt werden soll. Returns ------- gaussianBlurMat : Mat Die Ergebniss-Matrix auf die der Rauschfilter gaussian blur ausgeführt wurde. Example ------- >>> gaussianBlurNoiseFilterPreprocessor.preProcess() """ self.__logger.info("Starting preProcess()", "GaussianBlurNoiseFilterPreprocessor:preProcess") gaussianBlurMat = cv2.GaussianBlur(src=mat, ksize=self.__ksize, sigmaX=self.__sigmaX, sigmaY=self.__sigmaY, borderType=self.__borderType) # Anzeigen des Ergebnisses Note if (self.__showImagesInWindow): cv2.imshow("GaussianBlurNoiseFilterPreprocessor", gaussianBlurMat) cv2.waitKey(0) self.__logger.info("Finished preProcess() with following Image: ", "GaussianBlurNoiseFilterPreprocessor:preProcess", gaussianBlurMat) return gaussianBlurMat
class BitwiseNotPreprocessor(IPreprocessingUnit): def __init__(self, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("Preprocessing_Component_Logger") self.__logger.info("Starting __init__()", "BitwiseNotPreprocessor:__init__") self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finished __init__()", "BitwiseNotPreprocessor:__init__") def preProcess(self, mat): """ Führt eine Inversierung (bit-weises not) auf eine Bildmatrix aus. Parameters ---------- mat : Mat Die Matrix auf welche die Operation ausgeführt werden soll. Returns ------- bitwiseNot : Mat Die Ergebniss-Matrix auf die die Operation ausgeführt wurde. Example ------- >>> bitwiseNotPreprocessor.preProcess() """ self.__logger.info("Starting preProcess()", "BitwiseNotPreprocessor:preProcess") bitwiseNot = cv2.bitwise_not(mat) # Anzeigen des Ergebnisses Note if (self.__showImagesInWindow): cv2.imshow("BitwiseNotPreprocessor", bitwiseNot) cv2.waitKey(0) self.__logger.info("Finished preProcess() with following Image: ", "GaussianBlurNoiseFilterPreprocessor:preProcess", bitwiseNot) return bitwiseNot
class Preprocessor(IPreprocessingUnit): def __init__(self, orderedPreprocessingUnits): """ Constructor, initialisiert Membervariablen Parameters ---------- orderedPreprocessingUnits: list Eine geordnete Liste von PreprrcessingUnits in Ausführungsreihenfolge. """ self.__logger = State().getLogger("Preprocessing_Component_Logger") self.__logger.info("Starting __init__()", "Preprocessor:__init__()") self.__orderedPrePorcessingUnits = orderedPreprocessingUnits self.__logger.info("Finished __init__()", "Preprocessor:__init__()") def preProcess(self, mat): """ Führt die entpsrechene Preprocessing-Schritte auf eine Bildmatrix aus. Parameters ---------- imgMatrix : Mat Die Matrix auf welcher die Preprocessing-Schritte ausgeführt werden soll Returns ------- imgMatrix : Mat Die Ergebniss-Matrix auf welcher alle Preprocessing-Schritte ausgeführt wurde. Example ------- >>> preprocessor.preProcess(mat) """ self.__logger.info("Starting preProcess()", "Preprocessor:preProcess") currentMat = mat for unit in self.__orderedPrePorcessingUnits: newMat = unit.preProcess(currentMat) currentMat = newMat self.__logger.info("Finished preProcess()", "Preprocessor:preProcess") return currentMat
class ConfigInput(IConfigInput): def __init__(self, configPath): """ Constructor, initialisiert Membervariablen Parameters ---------- configPath : string Path to config file. Example ------- >>> ConfigInput("/path/to/config") """ self.__logger = State().getLogger("ConfigInput_Component_Logger") self.__logger.info( "Starting __init__() with ConfigPath: " + configPath, "Input:__init__") self.__configParser = JsonParser(configPath) self.__logger.info( "Finished __init__() with ConfigPath: " + configPath, "Input:__init__") def execute(self): """ Führt den entsprechenden Einlesevorgang über einen Parser durch und erstellt die Config. Parameters ---------- Returns ------- config : Config Die erstellte Konfiguration. Example ------- >>> configInput.execute() """ self.__logger.info("Starting execute()", "Input:execute") config = self.__configParser.parse() self.__logger.info("Finished execute()", "Input:execute") return config
class PipeConstructor(IPipeConstructor): def __init__(self, config): """ Constructor, initialisiert Membervariablen :param Config : config Die Konfiguration. :example: PipeConstructor(config) """ self.__logger = State().getLogger("PipeController_Component_Logger") self.__logger.info("Starting __init__()", "PipeConstructor:__init__") self.__config = config self.__logger.info("Finished __init__()", "PipeConstructor:__init__") def constructPipe(self): """ Konstruiert die Pipe für die Klassifizierung und gibt diese zurück. :return: successful : boolean :return: pipe : dictionary Die Pipe als <name,object> Paar. :example: pipeConstructor.constructPipe() """ self.__logger.info("Starting to constructPipe()", "PipeConstructor:constructPipe") pipe = { "Input": Input(self.__config.getConfigForComponent("Input")), "Preprocessing": Preprocessing( self.__config.getConfigForComponent("Preprocessing")), "DetectionCore": DetectionCore( self.__config.getConfigForComponent("DetectionCore")), "Postprocessing": Postprocessing( self.__config.getConfigForComponent("Postprocessing")), "Output": Output(self.__config.getConfigForComponent("Output")) } self.__logger.info("Finished to constructPipe()", "PipeConstructor:constructPipe") return pipe
class PipeController(IPipeController): def __init__(self, configFilePath): """ Constructor, initialisiert Membervariablen :param configFilePath : string Pfad zum Configurations-File :example: PipeController("my/path/to/a/config/File") """ print("PipeController: Starting __init__()") self.__configFilePath = configFilePath self.__config = 0 self.__controllerConfig = 0 self.__pipe = 0 self.__pipeConstructor = 0 self.__configInput = 0 self.__state = 0 print("PipeController: Finished __init__()") def init(self): """ Baut mithilfe eines PipeContructors, die Pipe zur Klassifizierung auf. :return: successful : boolean Initialisierung erfolgreich? :example: pipeController.init() """ print("PipeController: Starting init() components with Config" + self.__configFilePath) """Hier kann der ConfigInput mit einer anderen Realisierung des IConfigInput ausgetauscht werden.""" self.__configInput = ConfigInput(self.__configFilePath) self.__config = self.__configInput.execute() self.__controllerConfig = self.__config.getConfigForComponent( "PipeController") """Todo: Check if Config ok """ """Hier kann der ConfigInput mit einer anderen Realisierung des IConfigInput ausgetauscht werden.""" self.__state = State() self.__state.init(self.__config.getConfigForComponent("State")) self.__logger = State().getLogger("PipeController_Component_Logger") """Todo: Check if init ok """ """Hier kann der PipeConstructor mit einer anderen Realisierung des IPipeConstructors ausgetauscht werden.""" self.__pipeConstructor = PipeConstructor(self.__config) self.__pipe = self.__pipeConstructor.constructPipe() """Todo: Check if pipe ok """ self.__logger.info( "Finished init() components with Config-Path: " + self.__configFilePath, "PipeController:init") return True def execute(self): """ Führt die Befehle über die Pipe aus und steuert diese. :return: successful : boolean War die Klassifizierung erfolgreich? :example: pipeController.execute() """ self.__logger.info("Starting execute()", "PipeController:execute") if self.__controllerConfig["executeBuildTraindata"]: outPutOk = self.executeBuildTraindata() if self.__controllerConfig["executeTrain"]: outPutOk = self.executeTrain() if self.__controllerConfig["executeClassification"]: outPutOk = self.executeClassification() self.__logger.info("Finished execute()", "PipeController:execute") return outPutOk def executeClassification(self): """ Führt die Klassifizierung über die Pipe aus und steuert diese. :return: successful : boolean War die Klassifizierung erfolgreich? :example: pipeController.executeClassification() """ self.__logger.info("Starting executeClassification()", "PipeController:executeClassification") inputMat = self.__pipe["Input"].execute() preprocessingMats = self.__pipe["Preprocessing"].execute(inputMat) classification = self.__pipe["DetectionCore"].execute( preprocessingMats) postProcessedClassification = self.__pipe["Postprocessing"].execute( classification) outPutOk = self.__pipe["Output"].execute(postProcessedClassification) self.__logger.info("Finished executeClassification()", "PipeController:executeClassification") return outPutOk def executeBuildTraindata(self): """ Erstellt ein Trainingsdatenset über die Pipe und speichert diese. :return: successful : boolean War der Aufbau erfolgreich? :example: pipeController.executeBuildTraindata() """ self.__logger.info("Starting executeBuildTraindate()", "PipeController:executeBuildTraindate") while True: inputMat, lable = self.__pipe["Input"].readNextImgXMLPair() if inputMat is None: break if lable is not None: preprocessingMats = self.__pipe["Preprocessing"].execute( inputMat) trainMats = self.__pipe["DetectionCore"].buildNextTraindata( preprocessingMats) self.__pipe["Output"].writeNextTraindata(trainMats, lable) self.__logger.info("Finished executeBuildTraindate()", "PipeController:executeBuildTraindate") return True def executeTrain(self): """ Führt das Training der Classifier im DetectionCore über die Pipe aus. :return: successful : boolean War dir Aufbau erfolgreich? :example: pipeController.executeTrain() """ self.__logger.info("Starting executeTrain()", "PipeController:executeTrain") inputMatArray, lableArray = self.__pipe["Input"].readNoteTrainData() self.__pipe["DetectionCore"].executeTrain(inputMatArray, lableArray) self.__logger.info("Finished executeTrain()", "PipeController:executeTrain") return True
class ImageFiller: def __init__(self, fillRows=True, fillColumns=True, targetNumberOfRows=110, targetNumberOfColumns=32, appendRowsTop=False, appendColumnsRight=True, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "ImageFiller:__init__") self.__fillRows = fillRows self.__fillColumns = fillColumns self.__targetNumberOfRows = targetNumberOfRows self.__targetNumberOfColumns = targetNumberOfColumns self.__appendRowsTop = appendRowsTop self.__appendColumnsRight = appendColumnsRight self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finsihed __init__()", "ImageFiller:__init__") def coreProcess(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting coreProcess()", "ImageFiller:coreProcess") # Erstelle eine Liste mit i leeren Listen. # Jede leere Liste i wird fuer einen Matrix-Typen angelegt newMats = [] for i in range(0, len(mats)): newMats.append([]) # Iteriere ueber jeden Matrix-Typen for i in range(0, len(mats)): # Iteriere ueber jedes Objekt der Matrix-Typen-Liste for matObject in mats[i]: # Speichere die Matrix als neues Objekt ab newMatObject = matObject.copy() # Zwischenspeichern der Zeilen und Spaltenanzahl rows, columns = newMatObject.shape # Wenn die Anzahl der Zeilen angepasst werden sollen ... if(self.__fillRows): # dann ueberpruefe, ob die urspruengliche Matrix # zu wenig Zeilen besitzt. if(rows < self.__targetNumberOfRows): # Berechne die Anzahl der zusaetzlich benoetigten Zeilen. numberOfAdditionalRows = self.__targetNumberOfRows - rows # Erstellen der Zeilen die zusaetzlich hinzugefuegt werden sollen additionalRows = np.full((numberOfAdditionalRows, columns), 255, dtype=newMatObject.dtype) # Fuege die zusaetzlichen Zeilen oben an, wenn True if(self.__appendRowsTop): newMatObject = np.append(additionalRows, newMatObject, axis=0) # ansonsten fuege die Zeilen unten an. else: newMatObject = np.append(newMatObject, additionalRows, axis=0) # Wenn nicht, dann ueberpruefe, ob die urspruengliche # Matrix zu viele Zeilen besitzt. elif(self.__targetNumberOfRows < rows): # Loesche die Zeilen von der obersten Zeile beginnend # nach unten, wenn True if(self.__appendRowsTop): # Berechne von oben her die Indices der zu loeschenden Zeilen # und loesche diese Zeilen indicesToDeleteRows = range(0, rows - self.__targetNumberOfRows) newMatObject = np.delete(newMatObject, indicesToDeleteRows, axis=0) # ansonsten loesche die Zeilen von der # untersten Zeile beginnend nach oben. else: # Berechne von unten her die Indices der zu loeschenden Zeilen # und loesche diese Zeilen indicesToDeleteRows = range(self.__targetNumberOfRows, rows) newMatObject = np.delete(newMatObject, indicesToDeleteRows, axis=0) # Aktualisieren der Zeilen und Spaltengroesse rows, columns = newMatObject.shape # Wenn die Anzahl der Spalten angepasst werden sollen ... if (self.__fillColumns): # dann ueberpruefe, ob die urspruengliche Matrix # zu wenig Spalten besitzt. if (columns < self.__targetNumberOfColumns): # Berechne die Anzahl der zusaetzlich benoetigten Spalten. numberOfAdditionalColumns = self.__targetNumberOfColumns - columns # Erstellen der Spalten die zusaetzlich hinzugefuegt werden sollen. additionalColumns = np.full((rows, numberOfAdditionalColumns), 255, dtype=newMatObject.dtype) # Fuege die zusaetzlichen Spalten rechts an, wenn True if(self.__appendColumnsRight): newMatObject = np.append(newMatObject, additionalColumns, axis=1) # ansonsten fuege die Spalten links an. else: newMatObject = np.append(additionalColumns, newMatObject, axis=1) # Wenn nicht, dann ueberpruefe, ob die urspruengliche # Matrix zu viele Spalten besitzt. elif (self.__targetNumberOfColumns < columns): # Loesche die Spalten von der rechtesten Spalte beginnend # nach links, wenn True if(self.__appendColumnsRight): # Berechne von rechts her die Indices der zu loeschenden Spalten # und loesche diese Spalten indicesToDeleteColumns = range(self.__targetNumberOfColumns, columns) newMatObject = np.delete(newMatObject, indicesToDeleteColumns, axis=1) # ansonsten loesche die Spalten von der linkesten Spalte beginnend # nach rechts. else: # Berechne von links her die Indices der zu loeschenden Spalten # und loesche diese Spalten indicesToDeleteColumns = range(0, columns - self.__targetNumberOfColumns) newMatObject = np.delete(newMatObject, indicesToDeleteColumns, axis=1) # Fuege die Matrix der newMats[i].append(newMatObject) # Anzeigen des Mat-Objektes dem Zeilen bzw. Spalten # abgezogen bzw. hinzugefuegt wurden. if (self.__showImagesInWindow): cv2.imshow("ImageFiller_Object", newMatObject) cv2.waitKey(0) # Abspeichern aller Noten von jedem Matrix-Typ for matType in newMats: for note in matType: self.__logger.info("Filled following Object: ", "ImageFiller:coreProcess", note) self.__logger.info("Finished coreProcess()", "ImageFiller:coreProcess") return mats
class MusicXmlOutput(ISheetMusicOutput): def __init__(self, outputPath=None, fillwithrests=True): if (outputPath == None): self.__outputPath = "output_musicxml.xml" else: self.__outputPath = outputPath self.compare_lenght = 0.0 self.__fillwithrests = fillwithrests self.__logger = State().getLogger("Output_Component_Logger") #MUSS DANN GELÖSCHT WERDEN #self.__outputPath = "output_mxml.xml" #pass def write(self, sheetMusic): """ Interface Methode: Jeder SheetMusicOutput muss diese realisieren. Führt den entsprechenden Ausgabevorgang durch und erstellt ausgebene Datau. Parameters ---------- sheetMusic: SheetMusic Returns ------- successful : boolean War die Ausgabe erfolgreich? """ ########Erstelle Test-SheetMusic #self.create_testdata() ########Ausgabe Test-SheetMusic #self.print_ausgabe_sheetMusic() #Übernehme eingehendes sheetMusic self.sheetMusic = sheetMusic # Öffnet neues XML-Dokument und füllt den Header der Datei temp = minidom.DOMImplementation() doctype = temp.createDocumentType( 'score-partwise', "-//Recordare//DTD MusicXML 3.0 Partwise//EN", "http://www.musicxml.org/dtds/partwise.dtd") self.__doc = temp.createDocument(None, 'score-partwise', doctype) #Erstellt root-Element der XML-Datei root = self.__doc.documentElement #Schreibt Headerinformationen in das root-Element self.write_header(root) #Schreibt die part-list in das root-Element self.write_part_list(root) # Schreibt den erzeugten stream in das Xml-Dokument xml_str = self.__doc.toprettyxml(indent=" ") with open(self.__outputPath, "w") as f: #with open("output_mxml.xml", "w") as f: f.write(xml_str) return None ################################################################################# #Schreiben von Standard-Header mit Seitenformatierung etc. def write_header(self, input_root): """ <work> <work-title>Title</work-title> </work> <identification> <encoding> <software>Score Scanner 1.0</software> <encoding-date>2017-12-13</encoding-date> </encoding> </identification> <defaults> <scaling> <millimeters>7.05556</millimeters> <tenths>40</tenths> </scaling> <page-layout> <page-height>1683.78</page-height> <page-width>1190.55</page-width> <page-margins type="even"> <left-margin>56.6929</left-margin> <right-margin>56.6929</right-margin> <top-margin>56.6929</top-margin> <bottom-margin>113.386</bottom-margin> </page-margins> <page-margins type="odd"> <left-margin>56.6929</left-margin> <right-margin>56.6929</right-margin> <top-margin>56.6929</top-margin> <bottom-margin>113.386</bottom-margin> </page-margins> </page-layout> </defaults> :param input_root: Das root-Element der XML-Datei :return: """ work = self.__doc.createElement('work') work_title = self.__doc.createElement('work-title') work_title_text = self.__doc.createTextNode( 'ScoreScan 1.0 SheetMusic MusicXML') work_title.appendChild(work_title_text) work.appendChild(work_title) input_root.appendChild(work) identification = self.__doc.createElement('identification') encoding = self.__doc.createElement('encoding') identification.appendChild(encoding) software = self.__doc.createElement('software') software_text = self.__doc.createTextNode('Score Scanner 1.0') software.appendChild(software_text) encoding.appendChild(software) input_root.appendChild(identification) import datetime as dt encoding_date = self.__doc.createElement('encoding-date') encoding_date_text = self.__doc.createTextNode(str(dt.date.today())) encoding_date.appendChild(encoding_date_text) encoding.appendChild(encoding_date) defaults = self.__doc.createElement('defaults') scaling = self.__doc.createElement('scaling') defaults.appendChild(scaling) millimeters = self.__doc.createElement('millimeters') millimeters_text = self.__doc.createTextNode('7.05556') millimeters.appendChild(millimeters_text) scaling.appendChild(millimeters) tenths = self.__doc.createElement('tenths') tenths_text = self.__doc.createTextNode('40') tenths.appendChild(tenths_text) scaling.appendChild(tenths) input_root.appendChild(defaults) page_layout = self.__doc.createElement('page-layout') defaults.appendChild(page_layout) page_height = self.__doc.createElement('page-height') page_height_text = self.__doc.createTextNode('1683.78') page_height.appendChild(page_height_text) page_layout.appendChild(page_height) page_width = self.__doc.createElement('page-width') page_width_text = self.__doc.createTextNode('1190.55') page_width.appendChild(page_width_text) page_layout.appendChild(page_width) page_margins = self.__doc.createElement('page-margins') page_margins.setAttribute('type', 'even') page_layout.appendChild(page_margins) left_margin = self.__doc.createElement('left-margin') left_margin_text = self.__doc.createTextNode('56.6929') left_margin.appendChild(left_margin_text) page_margins.appendChild(left_margin) right_margin = self.__doc.createElement('right-margin') right_margin_text = self.__doc.createTextNode('56.6929') right_margin.appendChild(right_margin_text) page_margins.appendChild(right_margin) top_margin = self.__doc.createElement('top-margin') top_margin_text = self.__doc.createTextNode('56.6929') top_margin.appendChild(top_margin_text) page_margins.appendChild(top_margin) bottom_margin = self.__doc.createElement('bottom-margin') bottom_margin_text = self.__doc.createTextNode('113.386') bottom_margin.appendChild(bottom_margin_text) page_margins.appendChild(bottom_margin) page_margins_2 = self.__doc.createElement('page-margins') page_margins_2.setAttribute('type', 'odd') left_margin_2 = self.__doc.createElement('left-margin') left_margin_2_text = self.__doc.createTextNode('56.6929') left_margin_2.appendChild(left_margin_2_text) page_margins_2.appendChild(left_margin_2) right_margin_2 = self.__doc.createElement('right-margin') right_margin_2_text = self.__doc.createTextNode('56.6929') right_margin_2.appendChild(right_margin_2_text) page_margins_2.appendChild(right_margin_2) top_margin_2 = self.__doc.createElement('top-margin') top_margin_2_text = self.__doc.createTextNode('56.6929') top_margin_2.appendChild(top_margin_2_text) page_margins_2.appendChild(top_margin_2) bottom_margin_2 = self.__doc.createElement('bottom-margin') bottom_margin_2_text = self.__doc.createTextNode('113.386') bottom_margin_2.appendChild(bottom_margin_2_text) page_margins_2.appendChild(bottom_margin_2) page_layout.appendChild(page_margins_2) #Schreiben der part-list in MusicXML def write_part_list(self, input_root): """Gibt die part-list wieder und ruft hierfür für jeden Part die f_newpart() Funktion auf <part-list> ... </part-list> ... :param input_root: Das root-Element der XML-Datei :return: """ part_list = self.__doc.createElement('part-list') input_root.appendChild(part_list) self.write_newpart(part_list, 'P1') #self.write_newpart(part_list, 'P2') self.write_part(input_root, self.sheetMusic.sheetMusicClassificationUnits, 'P1') #write_part(root, list2, 'P2') #Schreiben eines parts innerhalb der part-list in MusicXML def write_newpart(self, input_part_list, input_part_id): """Erstellt innerhalb des "part-list" Elements einen neuen Part <score-part id="P1"> <part-name>Music</part-name> </score-part> :param input_part_list: Das XML Part-list-Element in das die zu erzeugenden Elemente geschrieben werden sollen. :param input_part_id: Die ID der Stimme :return: """ score_part = self.__doc.createElement('score-part') score_part.setAttribute('id', str(input_part_id)) input_part_list.appendChild(score_part) part_name = self.__doc.createElement('part-name') part_name_text = self.__doc.createTextNode('Music') part_name.appendChild(part_name_text) score_part.appendChild(part_name) #Schreiben einer Stimme in MusicXML def write_part(self, input_root, input_list, input_part_id): """Gibt den eigentlichen Part wieder, wobei ein Part eine einzelne "Stimme" ist <part id="P1"> ... </part> :param input_root: Das root-Element der XML-Datei :param input_list: Die Liste mit den Takten von sheetMusic :param input_part_id: Die ID der Stimme :return: """ part = self.__doc.createElement('part') part.setAttribute('id', str(input_part_id)) counter = 0 #Startet für jeden Takt die write_measure Funktion while (counter < (len(input_list))): self.write_measure(part, input_list[counter], counter) counter += 1 input_root.appendChild(part) #Schreiben eines Taktes in MusicXML def write_measure(self, input_part, input_tact, input_measure_counter): """Gibt den jeweiligen Takt wieder <measure number="1"> ... </measure> :param input_part: Das XML Part-Element in das die zu erzeugenden Elemente geschrieben werden sollen. :param input_tact: Der Takt der in XML geschrieben werden soll. :param input_measure_counter: Die ID des Taktes :return: """ """ """ measure = self.__doc.createElement('measure') measure.setAttribute('number', str(input_measure_counter + 1)) counter = 0 #if measure_counter == 0: #self.write_clef(measure, list1) #Durchläuft die Liste tactElements und startet die jeweilige write Funktion ausgabe = 0.0 while counter < len(input_tact.tactElements): if (type(input_tact.tactElements[counter]) == Chord): ausgabe += self.write_chord(measure, input_tact.tactElements[counter]) elif (type(input_tact.tactElements[counter]) == Clef): self.write_clef(measure, input_tact.tactElements[counter]) elif (type(input_tact.tactElements[counter]) == KeySignatur): self.write_key_signatur(measure, input_tact.tactElements[counter]) elif (type(input_tact.tactElements[counter]) == TimeSignature): self.write_timeSignature(measure, input_tact.tactElements[counter]) #schreibt Vergleichstaktlänge zur überprüfung self.compare_lenght = input_tact.tactElements[counter].beats * ( 4 / input_tact.tactElements[counter].beatType) elif (type(input_tact.tactElements[counter]) == Rest): self.write_rest(measure, input_tact.tactElements[counter]) counter += 1 input_part.appendChild(measure) #if (input_measure_counter == 0): #TODO: bei korrekter erkennung der Taktangabe -1 entfernen #self.compare_lenght = (ausgabe-1.0) #outputmessage = str("Takt Nr.: ", (input_measure_counter + 1), " ist ", ausgabe, " lang") #self.__logger.info("Takt Nr.: " + str(input_measure_counter + 1) + " ist " + str(ausgabe) + " lang", "Output:MusicXML execute") #überprüfung der taktlänge mit solltaktlänge if ((self.compare_lenght != ausgabe) & (self.compare_lenght != 0.0)): if ((self.compare_lenght > ausgabe) & self.__fillwithrests): #test_rest = Rest() test_rest = input_tact.creatRest( input_measure_counter, (len(input_tact.tactElements) + 1)) test_rest.duration = (self.compare_lenght - ausgabe) #füllen mit pausen bei zu kurzen takten self.write_rest(measure, test_rest) self.__logger.info( "Takt Nr.: " + str(input_measure_counter + 1) + " hat Abweichung! (Soll:" + str(self.compare_lenght) + "/Ist:" + str(ausgabe) + ") und wurde mit Pause aufgefüllt", "Output:MusicXML execute") else: self.__logger.info( "Takt Nr.: " + str(input_measure_counter + 1) + " hat Abweichung! (Soll:" + str(self.compare_lenght) + "/Ist:" + str(ausgabe), "Output:MusicXML execute") #print("Takt Nr.: ", (input_measure_counter+1), " ist ", ausgabe, " Lang") #Schreiben eines Schlüssels in MusicXML def write_clef(self, input_measure, input_clef): """Gibt einen Notenschllüssel z.B. Violinschlüssel <attributes> <clef> <sign>G</sign> <line>2</line> </clef> </attributes> :param input_measure: Das XML Element in das geschrieben werden soll :param input_clef: Das Clef Element, das erzeugt werden soll :return: """ attributes = self.__doc.createElement('attributes') input_measure.appendChild(attributes) #divisions = self.__doc.createElement('divisions') #divisions_text = self.__doc.createTextNode('1') #divisions.appendChild(divisions_text) #attributes.appendChild(divisions) clef = self.__doc.createElement('clef') attributes.appendChild(clef) sign = self.__doc.createElement('sign') sign_text = self.__doc.createTextNode(input_clef.sign) sign.appendChild(sign_text) clef.appendChild(sign) line = self.__doc.createElement('line') line_text = self.__doc.createTextNode(str(input_clef.line)) line.appendChild(line_text) clef.appendChild(line) #Schreiben einer Note in MusicXML def write_chord(self, input_measure, input_list): """Gibt ein einzelnes Chord-Element wieder, dass in der Regel eine Note enthält z.B. ein C in der vierten Oktave mit länge einer Viertel Note <note> <pitch> <step>C</step> <alter>0.0</alter> <octave>4</octave> </pitch> <duration>1</duration> <type>quarter</type> </note> :param input_measure: Das XML Element in das geschrieben werden soll :param input_list: Die Liste des Chord-Elements, welches die Note Elemente enthält :return: """ min_chord_duration = 0.0 counter = 0 #Durchläuft jede Note der Liste des Chord-Elements while counter < len(input_list.chordElements): note = self.__doc.createElement('note') input_measure.appendChild(note) if counter >= 1: chord = self.__doc.createElement('chord') note.appendChild(chord) pitch = self.__doc.createElement('pitch') note.appendChild(pitch) step = self.__doc.createElement('step') step_text = self.__doc.createTextNode( str(input_list.chordElements[counter].pitch["step"])) step.appendChild(step_text) pitch.appendChild(step) alter = self.__doc.createElement('alter') alter_text = self.__doc.createTextNode( str(input_list.chordElements[counter].pitch["alter"])) alter.appendChild(alter_text) pitch.appendChild(alter) octave = self.__doc.createElement('octave') octave_text = self.__doc.createTextNode( str(input_list.chordElements[counter].pitch["octave"])) octave.appendChild(octave_text) pitch.appendChild(octave) temp_float = 0.0 duration = self.__doc.createElement('duration') if (input_list.chordElements[counter].duration == "0.0"): # Liest aus .type -> .duration if input_list.chordElements[counter].type == 'quarter': duration_text = self.__doc.createTextNode(str(1.0)) temp_float = 1.0 elif input_list.chordElements[counter].type == 'half': duration_text = self.__doc.createTextNode(str(2.0)) temp_float = 2.0 elif input_list.chordElements[counter].type == 'whole': duration_text = self.__doc.createTextNode(str(4.0)) temp_float = 4.0 elif input_list.chordElements[counter].type == 'eighth': duration_text = self.__doc.createTextNode(str(0.5)) temp_float = 0.5 elif input_list.chordElements[counter].type == '16th': duration_text = self.__doc.createTextNode(str(0.25)) temp_float = 0.25 elif input_list.chordElements[counter].type == '32nd': duration_text = self.__doc.createTextNode(str(0.125)) temp_float = 0.125 elif input_list.chordElements[counter].type == '64th': duration_text = self.__doc.createTextNode(str(0.0625)) temp_float = 0.0625 elif input_list.chordElements[counter].type == '128th': duration_text = self.__doc.createTextNode(str(0.03125)) temp_float = 0.03125 else: duration_text = self.__doc.createTextNode( str(input_list.chordElements[counter].duration)) temp_float = float(input_list.chordElements[counter].duration) duration.appendChild(duration_text) note.appendChild(duration) type = self.__doc.createElement('type') if input_list.chordElements[counter].type != "": type_text = self.__doc.createTextNode( input_list.chordElements[counter].type) # Liest aus .duration -> .type elif input_list.chordElements[counter].duration == 1.0: type_text = self.__doc.createTextNode('quarter') elif input_list.chordElements[counter].duration == 2.0: type_text = self.__doc.createTextNode('half') elif input_list.chordElements[counter].duration == 4.0: type_text = self.__doc.createTextNode('whole') elif input_list.chordElements[counter].duration == 0.5: type_text = self.__doc.createTextNode('eighth') elif input_list.chordElements[counter].duration == 0.25: type_text = self.__doc.createTextNode('16th') elif input_list.chordElements[counter].duration == 0.125: type_text = self.__doc.createTextNode('32nd') elif input_list.chordElements[counter].duration == 0.0625: type_text = self.__doc.createTextNode('64th') elif input_list.chordElements[counter].duration == 0.03125: type_text = self.__doc.createTextNode('128th') type.appendChild(type_text) note.appendChild(type) if ((min_chord_duration == 0.0) or (min_chord_duration > input_list.chordElements[counter].duration)): #if (float(input_list.chordElements[counter].duration) == 0.0): min_chord_duration = temp_float #else: # min_chord_duration = float(input_list.chordElements[counter].duration) counter += 1 return float(min_chord_duration) #Schreiben eines KeySignature in MusicXML def write_key_signatur(self, input_measure, input_key_signature): """ Erzeugt ein KeySignature Element in XML. Also die Vorzeichen der jeweiligen Tonart, z.B. fifths = 0 --> C-Dur <attributes> <key> <fifths>0</fifths> </key> </attributes> :param input_measure: Das XML Element in das geschrieben werden soll :param input_key_signature: Das KeySignature Element von sheetMusic, zu dem ein jeweiliges XML Element erzeugt werden soll :return: """ attributes = self.__doc.createElement('attributes') input_measure.appendChild(attributes) key = self.__doc.createElement('key') attributes.appendChild(key) fifths = self.__doc.createElement('fifths') fifths_text = self.__doc.createTextNode(str( input_key_signature.fifths)) fifths.appendChild(fifths_text) key.appendChild(fifths) #Schreiben einer TimeSignature in MusicXML def write_timeSignature(self, input_measure, input_timesignature): """Schreibt das XML TimeSignature Element, welches den Taktart angiebt, z.B. 4/4 Takt mit beats = 4 und beat-type = 4 <attributes> <time> <beats>4</beats> <beat-type>4</beat-type> </time> </attributes> :param input_measure: Das XML Element in das geschrieben werden soll :param input_timesignature: Das TimeSignature Element aus sheetMusic, zu dem das jeweilige XML Gegenstück erzeugt werden soll :return: """ """""" attributes = self.__doc.createElement('attributes') input_measure.appendChild(attributes) time = self.__doc.createElement('time') attributes.appendChild(time) beats = self.__doc.createElement('beats') beats_text = self.__doc.createTextNode(str(input_timesignature.beats)) beats.appendChild(beats_text) time.appendChild(beats) beat_type = self.__doc.createElement('beat-type') beat_type_text = self.__doc.createTextNode( str(input_timesignature.beatType)) beat_type.appendChild(beat_type_text) time.appendChild(beat_type) #Schreiben einer Pause in MusicXML def write_rest(self, input_measure, input_rest): """Schreibt eine Pause in XML <note> </rest> <duration>2.0</duration> </note> :param input_measure: Das XML Element in das geschrieben werden soll :param input_rest: Das Rest Element von sheetMusic, zu dem ein XML Element erzeugt werden soll :return: """ note = self.__doc.createElement('note') input_measure.appendChild(note) rest = self.__doc.createElement('rest') note.appendChild(rest) duration = self.__doc.createElement('duration') duration_text = self.__doc.createTextNode(str(input_rest.duration)) duration.appendChild(duration_text) note.appendChild(duration) type = self.__doc.createElement('type') # Liest aus .duration -> .type if input_rest.duration == 1.0: type_text = self.__doc.createTextNode('quarter') elif input_rest.duration == 2.0: type_text = self.__doc.createTextNode('half') elif input_rest.duration == 4.0: type_text = self.__doc.createTextNode('whole') elif input_rest.duration == 0.5: type_text = self.__doc.createTextNode('eighth') elif input_rest.duration == 0.25: type_text = self.__doc.createTextNode('16th') elif input_rest.duration == 0.125: type_text = self.__doc.createTextNode('32nd') elif input_rest.duration == 0.0625: type_text = self.__doc.createTextNode('64th') elif input_rest.duration == 0.03125: type_text = self.__doc.createTextNode('128th') else: type_text = self.__doc.createTextNode('half') type.appendChild(type_text) note.appendChild(type) ################################################################################ #Print Ausgabe def print_ausgabe_sheetMusic(self): print("\n") self.ausgabe_takt_element( 0, 0, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 0, 1, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 0, 2, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 0, 3, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 1, 0, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 1, 1, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 2, 0, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 2, 1, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 2, 2, self.sheetMusic.sheetMusicClassificationUnits) self.ausgabe_takt_element( 2, 3, self.sheetMusic.sheetMusicClassificationUnits) #Print Ausgabe eines Elements eines Taktes def ausgabe_takt_element(self, number_takt, number_element, liste): print( "Ausgabe erstes TaktElement(Chord) : \t", liste[number_takt]. tactElements[number_element].chordElements[0].pitch["step"]) #print("Ausgabe erstes TaktElement(Chord) : \t", liste[number_takt].tactElements[number_element].chordElements[0].pitch["octave"]) print( "Ausgabe erstes TaktElement(Chord) : \t", liste[number_takt]. tactElements[number_element].chordElements[0].duration) ################################################################################# #Funktionen um TestSheetMusic zu erstellen def create_testdata(self): # TEST_ERSTELLUNG alle meine Entchen self.sheetMusic = SheetMusic() # Erster Takt test_tact = Tact() test_tact.addChord(self.testing_add_Note("C", 4, 1.0, 0.0)) test_tact.addChord(self.testing_add_Note("D", 4, 1.0, 0.0)) test_tact.addChord(self.testing_add_Note("E", 4, 1.0, 0.0)) test_tact.addChord(self.testing_add_Note("F", 4, 1.0, 0.0)) test_tact.tactNumber = 1 self.sheetMusic.addTact(test_tact) # Zweiter Takt test_tact_2 = Tact() test_clef = Clef() test_tact_2.addClef(test_clef) test_keysignatur = KeySignatur() test_keysignatur.fifths = 3 test_tact_2.addKeySignatur(test_keysignatur) test_tact_2.addChord(self.testing_add_Note("G", 4, 2.0, 0.0)) test_tact_2.addChord(self.testing_add_Note("G", 4, 2.0, 0.0)) test_tact_2.tactNumber = 2 self.sheetMusic.addTact(test_tact_2) # Dritter Takt test_tact_3 = Tact() test_tact_3.addChord(self.testing_add_Note("A", 4, 1.0, 0.0)) test_tact_3.addChord(self.testing_add_Note("A", 4, 1.0, 0.0)) test_tact_3.addChord(self.testing_add_Note("A", 4, 1.0, 0.0)) test_tact_3.addChord(self.testing_add_Note("A", 4, 1.0, 0.0)) test_tact_3.tactNumber = 3 self.sheetMusic.addTact(test_tact_3) # Dritter Takt test_tact_4 = Tact() test_tact_4.addChord(self.testing_add_Note("A", 4, 1.0, 0.0)) test_tact_4.addChord( self.testing_add_chord("A", 4, 1.0, 0.0, "C", 5, 1.0, 0.0)) test_rest = Rest() test_rest.duration = 2 test_tact_4.addRest(test_rest) test_tact_4.tactNumber = 4 self.sheetMusic.addTact(test_tact_4) testliste = [] testliste.append(test_tact) testliste.append(test_tact_2) testliste.append(test_tact_3) testliste.append(test_tact_4) #print(len(testliste)) #print("testliste ist :", type(testliste)) #Hinzufügen einer Note zum TestSheetMusic def testing_add_Note(self, pitch, octave, duration, semitones): """Gibt die entsprechende Note als ein Chord-Element zurück""" test_chord = Chord() test_note = Note() test_note.duration = duration test_note.pitch["step"] = pitch test_note.pitch["octave"] = octave test_note.pitch["alter"] = semitones test_chord.addNote(test_note) return test_chord #Hinzufügen eines Chord-Elements mit zwei Noten zum TestSheetMusic def testing_add_chord(self, pitch, octave, duration, semitones, pitch2, octave2, duration2, semitones2): """Gibt die entsprechende Note als ein Chord-Element zurück""" test_chord = Chord() test_note = Note() test_note.duration = duration test_note.pitch["step"] = pitch test_note.pitch["octave"] = octave test_note.pitch["alter"] = semitones test_chord.addNote(test_note) test_note_2 = Note() test_note_2.duration = duration2 test_note_2.pitch["step"] = pitch2 test_note_2.pitch["octave"] = octave2 test_note_2.pitch["alter"] = semitones2 test_chord.addNote(test_note_2) return test_chord
class HorizontalLineRemoveDetector(IDetector): def __init__(self, indexOfProcessMat=0, anchorPoint=(-1, -1), kernelWidth=1, kernelHeight=3, morphOfKernel=cv2.MORPH_RECT, showImagesInWindow=True): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "HorizontalLineRemoveDetector:__init__") self.__indexOfProcessMat = indexOfProcessMat self.__anchorPoint = anchorPoint self.__kernelWidth = kernelWidth self.__kernelHeight = kernelHeight self.__morphOfKernel = morphOfKernel self.__logger.info("Starting __init__()", "HorizontalLineRemoveDetector:__init__") self.__showImagesInWindow = showImagesInWindow def detect(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting detect()", "HorizontalLineRemoveDetector:detect") # Auswahl der Matrix, die vom Detektor bearbeitet werden soll mat = mats[self.__indexOfProcessMat] # Erstelle eine Kopie der zu bearbeitenden Matrix vertical = mat.copy() if (self.__showImagesInWindow): cv2.imshow("Input_HorizontalLineRemoveDetector", vertical) cv2.waitKey(0) # Es wird ein verticaler Kernel erstellt, # d. h. viele Zeilen und wenig Spalten verticalStructure = cv2.getStructuringElement( self.__morphOfKernel, (self.__kernelWidth, self.__kernelHeight)) # Beim Erodieren wird fuer jede Kernelposition der kleinste Wert zurueckgegeben # Auf diese Weise werden Features wie horizontale Linien zerstoert, da eine langer # vertikaler Kernel verwendet wird und invertiertes Bild erwartet wird. # Es werden also flache Objekte verworfen. vertical = cv2.erode(vertical, verticalStructure, self.__anchorPoint) self.__logger.info("Vertical_erode:", "HorizontalLineRemoveDetector:detect", vertical) if (self.__showImagesInWindow): cv2.imshow("Input_HorizontalLineRemoveDetector", vertical) cv2.waitKey(0) # Beim Delatieren wird fuer jede Kernelposition der groesste Wert zurueckgegeben # Auf diese Weise werden die beim Erodieren flach gedrueckte Objekte wieder ausgedeht, # da der selbe Kernel verwendet und das selbe invertierte Bild erwartet wird. # Es werden also flache Objekte nach unten und oben ausgedeht. vertical = cv2.dilate(vertical, verticalStructure, self.__anchorPoint) if (self.__showImagesInWindow): cv2.imshow("Input_HorizontalLineRemoveDetector", vertical) cv2.waitKey(0) self.__logger.info("Vertical_dialte:", "HorizontalLineRemoveDetector:detect", vertical) # Da der Linienentferner ein invertiertes Bild erwartet, # muss dieses nach der Verarbeitung wieder zurueck invertiert werden vertical = cv2.bitwise_not(vertical) # Ueberschreibe die Urspruengliche Input Matrix # mit der Ergebnis Matrix mats[self.__indexOfProcessMat] = vertical self.__logger.info("Finished detect() with following image:", "HorizontalLineRemoveDetector:detect", vertical) return mats
class TimeTemplateClassifier(ATempalteMatcher): """ Klasse zur Klassifizierung von Rythmusangaben in einem Takt. Erkennt 2/4, 3/4, 4/4, 5/4, 6/4, 3/8, 6/8, 9/8, 12/8. """ def __init__(self, config): """ Konstruktor der BeatTemplateClassifier Klasse. Initialisiert alle Membervariablen. :param config: Die Config für diesen TemplateMatcher """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__templateFolder = config["templateFolder"] self.__templateMethod = config["templateMethod"] self.__removeIfFound = config["removeIfFound"] self.__timeTemplates = [] self.__timeTemplates.append(self.__loadTemplate(2, 4)) self.__timeTemplates.append(self.__loadTemplate(3, 4)) self.__timeTemplates.append(self.__loadTemplate(4, 4)) self.__timeTemplates.append(self.__loadTemplate(5, 4)) self.__timeTemplates.append(self.__loadTemplate(6, 4)) self.__timeTemplates.append(self.__loadTemplate(3, 8)) self.__timeTemplates.append(self.__loadTemplate(6, 8)) self.__timeTemplates.append(self.__loadTemplate(9, 8)) self.__timeTemplates.append(self.__loadTemplate(12, 8)) self.__timeRatio = 47 / 97 self.__timeThreshold = 0.57 # max: 0.70 min: 0.44 mid: 0.57 for template in self.__timeTemplates: self.__logger.info( "Loaded Time Signature " + str(template[1]) + "/" + str(template[2]) + " Template", "TimeTemplateClassifier::__init__", template[0]) def classify(self, classUnit, matArray): """ Interface Methode: Jeder TempalteMatcher muss diese realisieren. Führt die Klassifizierung auf ein Array von Bildmatrizen anhand TemplateMatching aus und gibt die erkannten Objekte zurück. Parameters ---------- classUnit : Die Klassifikation als Out-Parameter matArray : Die Bildmatrizen auf welche die Klassifizierung ausgeführt werden soll. Returns ------- [True] wenn Taktangabe erkannt wurde, [False] falls nicht """ maxMatch = -1 maxLoc = (0, 0) for i in range(0, len(self.__timeTemplates)): match, loc = self.__hasTime(matArray, self.__timeTemplates[i][0], self.__timeRatio, self.__timeThreshold) if match > classUnit.recognitionPercentage: classUnit.recognitionPercentage = match classUnit.beats = self.__timeTemplates[i][1] classUnit.beatType = self.__timeTemplates[i][2] maxMatch = i maxLoc = loc if maxMatch == -1: return False self.__logger.info( "Time Signature found " + str(classUnit.beats) + "/" + str(classUnit.beatType) + " " + str(classUnit.recognitionPercentage), "TimeTemplateClassifier::classify", matArray[0]) if self.__removeIfFound: self._removeHit(matArray, self.__timeTemplates[maxMatch][0], self.__timeRatio, maxLoc) return True def __hasTime(self, mats, template, templateMatRatio, threshold): """ Führt den eigentlichen Templatematch aus. :param mats: Takt als Bildmatrix ([0] ohne Linien, [1] mit Linien, [2] nur skaliert). :param template: Template, welches zum Templatematching verwendet wird. :param templateMatRatio: Höhenverhältnis von Template zum Bildausschnitt. :param threshold: Grenzwert, welcher erreicht werden muss, damit das Template als gefunden gilt. :return: Maximal erreichten Matchwert. """ # Template auf Bildgröße skalieren resizedTemplate = ATempalteMatcher._resizeTemplate( self, mats[0], template, templateMatRatio) if (mats[0].shape[1] >= resizedTemplate.shape[1]): # Templatematching mit Bild OHNE Linen matchNoLine = cv2.matchTemplate(mats[0], resizedTemplate, self.__templateMethod) # Templatematching mit Bild MIT Linen matchLine = cv2.matchTemplate(mats[1], resizedTemplate, self.__templateMethod) # Templatematching mit nur skaliertem Bild matchSkal = cv2.matchTemplate(mats[2], resizedTemplate, self.__templateMethod) # Verwende das Maximum von beiden Matches match = cv2.max(matchLine, matchNoLine, matchSkal) # Hole den maximalen Wert und die zugehörige Position __, maxVal, __, maxLoc = cv2.minMaxLoc(match) # Wenn Threshold erreicht wurde if maxVal >= threshold: return maxVal, maxLoc return 0, (0, 0) def __loadTemplate(self, beat, beatType): return [ cv2.imread( self.__templateFolder + "beat_" + str(beat) + "_" + str(beatType) + ".jpg", 0), beat, beatType ]
class Input(IInput): def __init__(self, config): """ Konstruktor :param config: """ self.__logger = State().getLogger("Input_Component_Logger") self.__logger.info("Starting __init__()", "Input:__init__") self.__matParser = None self.__lableParser = None self.__matXMLPairCounter = 0 self.__matFileNamesForTestDataCreation = 0 self.__xmlFileNamesForTestDataCreation = 0 self.__createTraindataPdfDir = config["pathToCreateTrainDataFolder"] self.__createTraindataXmlDir = config[ "pathToCreateTrainDataLabelFolder"] self.__traindataDir = config["pathToTrainDataFolder"] self.__pathToFileToClassify = config["pathToClassifiactionImage"] # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_1.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_10.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_15.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_rest_9.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_middle_5.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_middle_7.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_elements.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_38.pdf" # self.__pathToFileToClassify = "./Input_Component\\Input\\clear_basic_41.pdf" self.__logger.info("Finished __init__()", "Input:__init__") def execute(self): """ Führt den entsprechenden Einlesevorgang über einen Parser durch und erstellt die Bildmatrix. :return: mat Die Matrix des eingelesenen Bildes """ self.__logger.info("Starting execute()", "Input:execute") filename, file_extension = os.path.splitext( self.__pathToFileToClassify) if file_extension == ".pdf": self.__logger.info("File is detected as PDF.", "Input:execute") self.__matParser = PdfToMatParser(self.__pathToFileToClassify) else: self.__logger.info("File looks like an image-datatype.", "Input:execute") self.__matParser = PngToMatParser(self.__pathToFileToClassify) mat = self.__matParser.parseToMat() self.__logger.info("Readed following Image:", "Input:execute", mat) self.__logger.info("Finished execute()", "Input:execute") return mat def readNextImgXMLPair(self): """ Führt den entsprechenden Einlesevorgang für die nächsten Files im Ordner über einen Parser durch und erstellt die Bildmatrix sowie das Lable. :return: mat Die Matrix des eingelesenen Bildes. :return: lable Das Lable zur Bildmatrix. """ self.__logger.info("Starting readNextImgXMLPair()", "Input:readNextImgXMLPair") # Falls es der erste Schritt ist, Filenamen einlesen if not self.__matXMLPairCounter: self.__matFileNamesForTestDataCreation = glob.glob( self.__createTraindataPdfDir + "*.pdf") self.__xmlFileNamesForTestDataCreation = glob.glob( self.__createTraindataXmlDir + "*.xml") self.__logger.info( "Found following files to create testdata:" + str(self.__matFileNamesForTestDataCreation) + str(self.__xmlFileNamesForTestDataCreation), "Input:readNextImgXMLPair") # Falls es der letzte Schritt ist, Counter zurücksetzen if (self.__matXMLPairCounter >= self.__matFileNamesForTestDataCreation.__len__()) \ or (self.__matXMLPairCounter > self.__xmlFileNamesForTestDataCreation.__len__()): self.__logger.debug( "Max Filecount reached: reset matXMLPairCounter to 0", "Input:readNextImgXMLPair") self.__matXMLPairCounter = 0 return None, None # Lese Mat und Lable und erhöhe Counter self.__matParser = PdfToMatParser( self.__matFileNamesForTestDataCreation[self.__matXMLPairCounter]) # test with png: # self.__matParser = PngToMatParser(self.__pathToFileToClassify) self.__lableParser = XmlToLableParser( self.__xmlFileNamesForTestDataCreation[self.__matXMLPairCounter]) mat = self.__matParser.parseToMat() lable = self.__lableParser.parseToLable() self.__matXMLPairCounter = self.__matXMLPairCounter + 1 self.__logger.info("Finished readNextImgXMLPair()", "Input:readNextImgXMLPair") return mat, lable def readNoteTrainData(self): """ Führt den entsprechenden Einlesevorgang für einen Ordner über einen Parser durch und gibt die Notentrainingsdaten mit ihrem Label zurück. :return: matArray Die MAtrizen des eingelesenen Bildes. :return: lableArray Die Lable zu den Bildmatrizen. """ self.__logger.info("Starting readNoteTrainData()", "Input:readNoteTrainData") # Reading Filenames from traindata dir fileNames = glob.glob(self.__traindataDir + "*.png") matArray = [] lableArray = [] # For each Testdate Parse Mat and Lable for fileName in fileNames: # Prepare LableString for Parser name = fileName.split("\\")[-1] name = name.split(".")[0] lableArray = lableArray + FilenameToLableParser( name).parseToLable() matArray.append(PngToMatParser(fileName).parseToMat()) self.__logger.info("Finished readNoteTrainData()", "Input:readNoteTrainData") return matArray, lableArray
class NoteDetector(IDetector): def __init__(self, indexOfProcessMat=0, minNoteWidth_WithStem=15, maxNoteWidth_WithStem=8, minNoteHeight_WithStem=30, maxNoteHeight_WithStem=80, minNoteWidth_WithoutStem=8, maxNoteWidth_WithoutStem=30, minNoteHeight_WithoutStem=8, maxNoteHeight_WithoutStem=23, noteImageWidth=20, findCountersMode=cv2.RETR_LIST, findCountersMethode=cv2.CHAIN_APPROX_NONE, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "NoteDetector:__init__") self.__indexOfProcessMat = indexOfProcessMat self.__minNoteWidth_WithStem = minNoteWidth_WithStem self.__maxNoteWidth_WithStem = maxNoteWidth_WithStem self.__minNoteHeight_WithStem = minNoteHeight_WithStem self.__maxNoteHeight_WithStem = maxNoteHeight_WithStem self.__minNoteWidth_WithoutStem = minNoteWidth_WithoutStem self.__maxNoteWidth_WithoutStem = maxNoteWidth_WithoutStem self.__minNoteHeight_WithoutStem = minNoteHeight_WithoutStem self.__maxNoteHeight_WithoutStem = maxNoteHeight_WithoutStem self.__noteImageWidth = noteImageWidth self.__findCountersMode = findCountersMode self.__findCountersMethode = findCountersMethode self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finsihed __init__()", "NoteDetector:__init__") def detect(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting detect()", "NoteDetector:detect") # Variablen zum Zeichnen lineThickness = 1 rectangleColorGreen = (0, 255, 0) rectangleColorRed = (0, 0, 255) # Merke die Anzahl von Noten pro Takt noteCountPerTact = [] # Erstelle eine Liste mit i leeren Listen. # Jede leere Liste i wird fuer einen Matrix-Typen angelegt noteMats = [] for i in range(0, len(mats)): noteMats.append([]) # Speichern der Liste, die die Takte ohne Notenlinien enthaelt matsOfTactsWithoutLines = mats[self.__indexOfProcessMat] # Itteriere ueber jeden Takt ohne Notenlinien for j in range(0, len(matsOfTactsWithoutLines)): # Setz die Notecount für diesen Takt initial auf 0 noteCountPerTact.append(0) # Extrahiere den j-ten Takt tactWithoutLines = matsOfTactsWithoutLines[j] # Anlegen einer Koordinatenliste, die die x-Koordianten # der Noten enthaelt. xCoordinates = [] if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines) cv2.waitKey(0) # Abholung der Breite und Höhe einer Notenzeile xRight = tactWithoutLines.shape[1] yBottom = tactWithoutLines.shape[0] # Erstellen einer weißen Zeile additionalRow = np.full((1, xRight), 255, dtype=tactWithoutLines.dtype) # Die Methode findContours findet keine Objekte, die sich in der ersten oder/und letzten Zeile # oder/und in der ersten oder/und letzten Zeile befinden. # Daher werden diese für die findCountours Methode temporäre auf den Wert 255 (weiß) gesetzt. tactWithoutLines[0, :] = additionalRow tactWithoutLines[yBottom - 1, :] = additionalRow # Erodiere horizontal um Loecher in unausgefuellte Noten zu zerstoeren tactWithoutLines_erode_dilate = cv2.erode(tactWithoutLines, np.ones((1, 9))) if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines_erode_dilate) cv2.waitKey(0) # Dilatiere auf die urspruengliche Groesse zurueck tactWithoutLines_erode_dilate = cv2.dilate( tactWithoutLines_erode_dilate, np.ones((1, 9))) if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines_erode_dilate) cv2.waitKey(0) # Dilatiere vertical um Verbindungen zwischen verbundene Noten zu zerstoeren tactWithoutLines_erode_dilate = cv2.dilate( tactWithoutLines_erode_dilate, np.ones((9, 1))) if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines_erode_dilate) cv2.waitKey(0) # Erodiere auf die urstpruengliche Groesse zurueck tactWithoutLines_erode_dilate = cv2.erode( tactWithoutLines_erode_dilate, np.ones((9, 1))) if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines_erode_dilate) cv2.waitKey(0) # An dieser Stelle trennen sich manchmal der Notenkopf und der Notenhals voneinander. # Mit diesem Erode werden sie wieder zusammengefügt. tactWithoutLines_erode_dilate = cv2.erode( tactWithoutLines_erode_dilate, np.ones((1, 4))) if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Tact", tactWithoutLines_erode_dilate) cv2.waitKey(0) # Finde alle Konturen im Takt _, contours, _ = cv2.findContours( image=tactWithoutLines_erode_dilate, mode=self.__findCountersMode, method=self.__findCountersMethode) # Erstelle eine Darstellungsbild indem die # gefundenen Elemente eingezeichnet werden imgShowTact = cv2.cvtColor(tactWithoutLines, cv2.COLOR_GRAY2RGB) # Iteriere ueber allen gefundenen Elemente eines Taktes for i in range(0, len(contours)): # Erstelle aus einem Konturelement ein Rechteck # und speichere die benoetigten parameter x, y, w, h = cv2.boundingRect(contours[i]) # Der Rahmen der gefundenen Elemente muss eine Mindesthoehe # und eine Minimal- und Mintestbreite besitzen if (((self.__minNoteWidth_WithStem < w and w < self.__maxNoteWidth_WithStem) and (self.__minNoteHeight_WithStem < h and h < self.__maxNoteHeight_WithStem)) or ((self.__minNoteWidth_WithoutStem < w and w < self.__maxNoteWidth_WithoutStem) and (self.__minNoteHeight_WithoutStem < h and h < self.__maxNoteHeight_WithoutStem))): # Zeichnen der Rechtecke um die gefundenen Noten cv2.rectangle(img=imgShowTact, pt1=(x, y), pt2=(x + w, y + h), color=rectangleColorGreen, thickness=lineThickness) # Fuege die Rahmen-Koordinaten der gefundenen Noten der Liste hinzu xCoordinates.append([x, x + w]) #Erhöhe die Notenanzahl dieses Taktes um eins noteCountPerTact[j] += 1 else: # Zeichnen der Rechtecke um die restlichen gefundenen Elemente cv2.rectangle(img=imgShowTact, pt1=(x, y), pt2=(x + w, y + h), color=rectangleColorRed, thickness=lineThickness) # Anzeigen der Notenzeilen indem die gefunden Taktstriche # gruen und alle anderen Elemente rot umrahmt sind. if (self.__showImagesInWindow): cv2.imshow("NoteDetector_NoteRow", imgShowTact) cv2.waitKey(0) # self.__logger.info("Detect following rectangle: ", "NoteDetector:detect", tactWithoutLines) # Sortieren der gefundenen Elemente von links nach rechts xCoordinates.sort(key=itemgetter(0)) # Ueberpruefe alle gefundenen Noten auf ihre Mindestbreite for i in range(0, len(xCoordinates)): # Extrahieren der rechten Koordinate einer gefunden Note x0 = xCoordinates[i][0] # Extrahieren der linken Koordinate einer gefunden Note x1 = xCoordinates[i][1] # Berechnung des Zentrums der Note bzgl. x-Koordinate center = int((x0 + x1) * 0.5) # Berechnung der neuen x-Koordinaten, um die Sollgroesse zu erhalten x0 = center - int(self.__noteImageWidth * 0.5) x1 = center + int(self.__noteImageWidth * 0.5) + ( (self.__noteImageWidth) % 2) # Gehe jeden eingegangene Matrix-Typen des j-ten Taktes durch for k in range(0, len(mats)): # Extrahiere den j-ten Takt aus der Liste des k-ten Matrix-Typen tact = mats[k][j] # Ueberpruefe ob die linke x-Koordinate # außerhalb des linken Randes ragt if (x0 < 0): # Berechnen der Anzahl der zusaetzlich benoetigten Spalten numberOfAdditionalColumns = abs(x0) # Erstellen einer Kopie der ersten Spalte firstColumn = tact[:, 0] # Erstellen der Spalten die am Anfang hinzugefuegt werden sollen, # indem die erste Spalte des Taktes mehrfach kopiert wird. additionalColumns = np.repeat( firstColumn[:, np.newaxis], repeats=numberOfAdditionalColumns, axis=1) # Setze die zusaetzlichen Spalten vor den Takt tact = np.append(additionalColumns, tact, axis=1) # Erweitere die x-Koordinaten um die Anzahl # der zusaetzlich hinzugefuegten Spalten x0 += numberOfAdditionalColumns x1 += numberOfAdditionalColumns # Extrahiere die Anzahl der Spalten des aktuellen Taktes numberOfColumnsOfTact = tact.shape[1] # Ueberpruefe ob die rechte x-Koordinate # außerhalb des rechten Randes ragt if (numberOfColumnsOfTact < x1): # Berechnen der Anzahl der zusaetzlich benoetigten Spalten numberOfAdditionalColumns = abs(x1 - numberOfColumnsOfTact) # Erstellen einer Kopie der letzten Spalte lastColumn = tact[:, numberOfColumnsOfTact - 1] # Erstellen der Spalten die am Ende zusaetzlich hinzugefuegt werden sollen, # indem die letzte Spalte des Taktes mehrfach kopiert wird. additionalColumns = np.repeat( lastColumn[:, np.newaxis], repeats=numberOfAdditionalColumns, axis=1) # Setze die zusaetzlichen Spalten hinter den Takt tact = np.append(tact, additionalColumns, axis=1) # Extrahieren einer Note mit den x-Koordinaten x0 und x1 # aus dem j-ten Takt des k-ten Matrix-Typen note = tact[:, x0:x1] # Fuege die Note der k-ten Liste des k-ten Matrix-Typen an noteMats[k].append(note) # Anzeigen der hinzugefuegten Note if (self.__showImagesInWindow): cv2.imshow("NoteDetector_Note", note) cv2.waitKey(0) # Abspeichern aller Noten von jedem Matrix-Typ for matType in noteMats: for note in matType: self.__logger.info("Detect following Note: ", "NoteDetector:detect", note) self.__logger.info("Finished detect()", "NoteDetector:detect") return noteMats, noteCountPerTact
class ClefDetector(IDetector): def __init__(self, delimitWidthMax=30, delimitWidthMin=20, delimitHeightMin=20, templateThreshold=0.55, templateMethod=cv2.TM_CCOEFF_NORMED, findCountersMode=cv2.RETR_LIST, findCountersMethode=cv2.CHAIN_APPROX_NONE): """ Konstruktor für den ClefDetector. :param delimitWidthMax: Maximale Breite eines Notenschlüssels. :param delimitWidthMin: Minimale Breite eines Notenschlüssels. :param delimitHeightMin: Minimale Höhe eines Notenschlüssels. :param templateThreshold: Der Wert den das Template-Matching mindestens erreichen muss, damit ein Bildauschnitt als der jeweilige Notenschlüssel erkannt wird :param templateMethod: Methode des Template-Matchers :param findCountersMode: Modus des Konturfindungs-Algorithmus :param findCountersMethode: Methode des Konturfindungs-Algorithmus """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "ClefDetector:__init__") self.__delimitWidthMax = delimitWidthMax self.__delimitWidthMin = delimitWidthMin self.__delimitHeightMin = delimitHeightMin self.__templateThreshold = templateThreshold self.__templateMethod = templateMethod self.__findCountersMode = findCountersMode self.__findCountersMethode = findCountersMethode self.__logger.info("Finished __init__()", "ClefDetector:__init__") def detect(self, matArray): """ Detection Funktion. Geerbt von IDetector. :param matArray: Liste von Bilder, welche die Takte enthalten :return: Liste alles gefundenen Notenschlüssel mit und ohne Notenlinien """ self.__logger.info("Starting detect()", "ClefDetector:detect") # Initialierung der Taktlisten: # 1. List ohne und die 2. Liste mit Notenlinien clefMatsNoLines = [] clefMatsLines = [] # Variablen zum Zeichnen lineThickness = 1 rectangleColor = (0, 255, 0) for j in range(0, len(matArray[0])): mat = matArray[0][j].copy() # Takt ohne Linien matLine = matArray[1][j].copy() xCoordinates = [] xCoordinates.append([0, 0]) self.__logger.debug("Starting detect Clefs for image", "ClefDetector:detect", mat) # Finde alle Konturen im Takt _, contours, _ = cv2.findContours( image=mat, mode=self.__findCountersMode, method=self.__findCountersMethode) found = False for i in range(0, len(contours)): # Erstelle aus einem Konturelement ein Rechteck # und speichere die benoetigten parameter x, y, w, h = cv2.boundingRect(contours[i]) if (self.__delimitWidthMin <= w and w < self.__delimitWidthMax and h >= self.__delimitHeightMin): found = True # Falls es sich um einen Violinschlüssel handelt, im Bild markieren und koordinaten merken if self.__isViolin(mat[:, x:x + w]): xCoordinates.append([x, x + w]) cv2.rectangle(img=mat, pt1=(x, y), pt2=(x + w, y + h), color=rectangleColor, thickness=lineThickness) # TODO an dieser Stelle die gefundenen prüfen ob vll ein Bassschlüssel if found: self.__logger.info("Detect following rectangle: ", "ClefDetector:detect", mat) # Sortieren der gefundenen Elemente von links nach rechts xCoordinates.sort(key=itemgetter(0)) # Ueberpruefe alle gefundenen Noten auf ihre Mindestbreite for i in range(1, len(xCoordinates)): x0 = xCoordinates[i][0] x1 = xCoordinates[i][1] clefMatsNoLines.append(mat[:, x0:x1]) clefMatsLines.append(matArray[1][j][:, x0:x1]) for i in range(0, len(clefMatsNoLines)): self.__logger.info("Detected following clef: ", "ClefDetector:detect", clefMatsNoLines[i]) self.__logger.info("Finished detect()", "ClefDetector:detect") return clefMatsNoLines, clefMatsLines def __isViolin(self, image): """ Funktion, welche feststellt ob es sich bei einem gegebenen Bildauschnitt um einen Violinschlüssel handelt oder nicht. :param image: Bildauschnitt, welcher untersucht wird. :return: [True] wenn ein Violinschlüssel, [False] falls nicht. """ # Lade Template als Graustufenbild #template = cv2.cvtColor(cv2.imread("DetectionCore_Component/Detector/templates/clef_violin.jpg"), cv2.COLOR_BGR2GRAY) template = cv2.imread( "DetectionCore_Component/Detector/templates/clef_violin.jpg", 0) # Wenn Bild kleiner als Template ist, brich ab mit False if image.shape[0] >= template.shape[0] and image.shape[ 1] >= template.shape[1]: match = cv2.matchTemplate(image, template, self.__templateMethod) else: self.__logger.warning("Template to small", "ClefDetector:__isViolin") return False # Wenn einer der Werte im Match größer als der vordefinierte Threshold ist, handelt es sich um einen Violinschlüssel if (match >= self.__templateThreshold).any(): return True else: return False
class TactDetector(IDetector): def __init__(self, indexOfProcessMat=0, tactLineWidthMax=8, tactLineHeightMin=40, minWidthOfTactLine=10, findCountersMode=cv2.RETR_LIST, findCountersMethode=cv2.CHAIN_APPROX_NONE, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "TactDetector:__init__") self.__indexOfProcessMat = indexOfProcessMat self.__tactLineWidthMax = tactLineWidthMax self.__tactLineHeightMin = tactLineHeightMin self.__minWidthOfTactLine = minWidthOfTactLine self.__findCountersMode = findCountersMode self.__findCountersMethode = findCountersMethode self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finished __init__()", "TactDetector:__init__") def detect(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting detect()", "TactDetector:detect") # Variablen zum Zeichnen lineThickness = 1 rectangleColorGreen = (0, 255, 0) rectangleColorRed = (0, 0, 255) # Erstelle eine Liste mit i leeren Listen. # Jede leere Liste i wird fuer einen Matrix-Typen angelegt tactMats = [] for i in range(0, len(mats)): tactMats.append([]) # Extrahier die Liste der Notenzeilen, # deren Notenlinien entfernt wurde matsOfNoteRowsWithoutLines = mats[self.__indexOfProcessMat] # Iteriere ueber alle Notenzeilen for j in range(0, len(matsOfNoteRowsWithoutLines)): # Extrahiere die j-te Notenzeile matNoteRow = matsOfNoteRowsWithoutLines[j] # Anlegen einer Koordinatenliste, die die x-Koordianten # der Taktstriche enthaelt. xCoordinates = [] # Fuege die erste Pixelspalte des Bildes der Notenzeile # zu den Koordinaten hinzu, um den Anfang als ersten Takt erkennen zu koennen, # da der Zeilenanfang of keinen Taktstrich enthaelt xCoordinates.append([0, 0]) # Verbreitern der Elemente in der Notenzeile anchorPoint = (-1, -1) kernelWidth = 3 kernelHeight = 1 morphOfKernel = cv2.MORPH_RECT verticalStructure = cv2.getStructuringElement( morphOfKernel, (kernelWidth, kernelHeight)) matNoteRow_eroded = cv2.erode(matNoteRow, verticalStructure, anchorPoint) # Anzeigen des Notenblattes indem die gefunden Notenzeilen # und Notenlinien farblich markiert sind. if (self.__showImagesInWindow): cv2.imshow("TactDetector_NoteRow", matNoteRow_eroded) cv2.waitKey(0) # Finde alle Konturen in der Notenzeile _, contours, _ = cv2.findContours( image=matNoteRow_eroded, mode=self.__findCountersMode, method=self.__findCountersMethode) # Erstelle eine Darstellungsbild indem die # gefundenen Elemente eingezeichnet werden imgShowNoteRow = cv2.cvtColor(matNoteRow, cv2.COLOR_GRAY2RGB) # Iteriere ueber alle gefundenen Elemente der Notenzeile for i in range(0, len(contours)): # Erstelle aus einem Konturelement ein Rechteck # und speichere die benoetigten parameter x, y, w, h = cv2.boundingRect(points=contours[i]) # Der Rahmen der gefundenen Elemente muss eine Maximalbreite # und eine Mindesthoehe besitzen um als Takt klassifiziert zu werden if (w < self.__tactLineWidthMax) and (self.__tactLineHeightMin < h): # Zeichnen der Rechtecke um die gefundenen Taktstriche cv2.rectangle(img=imgShowNoteRow, pt1=(x, y), pt2=(x + w, y + h), color=rectangleColorGreen, thickness=lineThickness) # Fuege die Rahmen-Koordinaten der gefundenen Taktstriche zu der Liste hinzu xCoordinates.append([x, x + w]) else: # Zeichnen der Rechtecke um die restlichen gefundenen Elemente cv2.rectangle(img=imgShowNoteRow, pt1=(x, y), pt2=(x + w, y + h), color=rectangleColorRed, thickness=lineThickness) # Fuege die letzte Pixelspalte des Bildes der Notenzeile # zu den Koordinaten hinzu, um das Ende als letzten Takt erkennen zu koennen, # da das Zeilenende of keinen Taktstrich enthaelt xCoordinates.append([matNoteRow.shape[1], matNoteRow.shape[1]]) # Anzeigen der Notenzeilen indem die gefunden Taktstriche # gruen und alle anderen Elemente rot umrahmt sind. if (self.__showImagesInWindow): cv2.imshow("TactDetector_NoteRow", imgShowNoteRow) cv2.waitKey(0) # Sortieren der gefundenen Elemente von links nach rechts xCoordinates.sort(key=itemgetter(0)) # Ueberpruefe alle gefundenen Takte auf ihre Mindestbreite mit Hilfe der Koordinaten # Iteriere hierzu ueber die Koordinaten for i in range(0, len(xCoordinates) - 1): # Extrahieren der rechten Koordinate eines gefunden Taktstriches x0 = xCoordinates[i][1] # Extrahieren der linken Koordinate des darauffolgenden Taktstriches x1 = xCoordinates[i + 1][0] # Der Inhalt zwischen den Koordinatn x0 und x1 ist ein Tak newMatWithoutLines = matNoteRow[:, x0:x1] # Finde alle Konturen in der Notenzeile _, contours, _ = cv2.findContours( image=newMatWithoutLines, mode=self.__findCountersMode, method=self.__findCountersMethode) # Speichern der Anzahl der Elemente numberOfElementsInTact = len(contours) # Falls der gefundene Takt breit genug ist und mindestens zwei Elemente besitzt, # dann ist es ein Takt. Der gesamte Takt an sich ist auch ein Element, # daher muss der Takt mindestens zwei Elemente besitzen. if (self.__minWidthOfTactLine < x1 - x0) and (2 <= numberOfElementsInTact): for k in range(0, len(mats)): # Gehe jeden eingegangene Matrix-Typen der j-ten Zeile durch # Extrahieren eines Taktes mit den x-Koordinaten x0 und x1 # aus j-ten Notenzeile des k-ten Matrix-Typen tact = mats[k][j][:, x0:x1] # Fuege den Takt der k-ten Liste des k-ten Matrix-Typen an tactMats[k].append(tact) # Anzeigen des hinzugefuegten Taktes if (self.__showImagesInWindow): cv2.imshow("TactDetector_Tact", tact) cv2.waitKey(0) # Abspeichern aller Takte von jedem Matrix-Typ for matType in tactMats: for tact in matType: self.__logger.info("Detecting Tact:", "TactDetector:detect", tact) self.__logger.info("Finished detect()", "TactDetector:detect") return tactMats
class ObjectCentering: def __init__(self, indexOfProcessMat=0, targetNumberOfRows=110, targetNumberOfColumns=32, useDeletingVerticalSpaces=True, useDeletingHorizontalSpaces=True, findCountersMode=cv2.RETR_LIST, findCountersMethode=cv2.CHAIN_APPROX_NONE, colorOfBorder=0, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "ObjectCentering:__init__") self.__indexOfProcessMat = indexOfProcessMat self.__targetNumberOfRows = targetNumberOfRows self.__targetNumberOfColumns = targetNumberOfColumns self.__useDeletingVerticalSpaces = useDeletingVerticalSpaces self.__useDeletingHorizontalSpaces = useDeletingHorizontalSpaces self.__findCountersMode = findCountersMode self.__findCountersMethode = findCountersMethode self.__colorOfBorder = colorOfBorder self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finsihed __init__()", "ObjectCentering:__init__") def coreProcess(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting coreProcess()", "ObjectCentering:coreProcess") # Variablen zum Zeichnen lineThickness = 1 rectangleColorGreen = (0, 255, 0) rectangleColorRed = (0, 0, 255) # Erstelle eine Liste mit i leeren Listen. # Jede leere Liste i wird fuer einen Matrix-Typen angelegt newMats = [] for i in range(0, len(mats)): newMats.append([]) # Speichern der Liste, die die Objekte ohne Notenlinien enthaelt matsOfObjectsWithoutLines = mats[self.__indexOfProcessMat] # Iteriere ueber jeden Matrix-Typen for i in range(0, len(matsOfObjectsWithoutLines)): # Extrahiere die i-te Objekt-Matrix matObject = matsOfObjectsWithoutLines[i] # Abholung der Breite und Höhe einer Notenzeile xRight = matObject.shape[1] yBottom = matObject.shape[0] # Erstellen einer weißen Spalte additionalColumn = np.full((yBottom), 255, dtype=matObject.dtype) # Die Methode findContours findet keine Objekte, die sich in der ersten oder/und letzten Zeile # oder/und in der ersten oder/und letzten Zeile befinden. # Daher werden diese für die findCountours Methode temporäre auf den Wert 255 (weiß) gesetzt. matObject[:, 0] = additionalColumn matObject[:, xRight - 1] = additionalColumn # Finde alle Konturen (Elemente) in der Objekt-Matrix _, contours, _ = cv2.findContours( image=matObject, mode=self.__findCountersMode, method=self.__findCountersMethode) # Erstelle eine Darstellungsbild indem die # gefundenen Elemente eingezeichnet werden imgShowObject = cv2.cvtColor(matObject, cv2.COLOR_GRAY2RGB) # Finde das kleinste Rechteck, welches alle Elemente binhaltet. # Hierzu werden die Minimalwerte zu Beginn mit den hoechst moeglichen # Werten initialisiert und umbekehrt. minX = matObject.shape[1] minY = matObject.shape[0] maxX = 0 maxY = 0 # Entferne das letzte Element. # Das letzte Element ist die Kontur, welche das gesamte Feld einschliesst. contours.pop() # Iteriere ueber alle gefundenen Elemente des Matrix-Objektes for j in range(0, len(contours)): # Erstelle aus einem Konturelement ein Rechteck # und speichere die benoetigten Parameter xLeft, yTop, w, h = cv2.boundingRect(points=contours[j]) xRight = xLeft + w yBottom = yTop + h # Zeichnen der Rechtecke um die gefundenen Elemente cv2.rectangle(img=imgShowObject, pt1=(xLeft, yTop), pt2=(xRight, yBottom), color=rectangleColorRed, thickness=lineThickness) # Ueberpruefe, ob das aktuelle Rechteck, das gefundene Element beinhaltet, # wenn nicht, dann passe die Koordinaten des alles umschließenden Rechtecks an. if xLeft < minX: minX = xLeft if yTop < minY: minY = yTop if maxX < xRight: maxX = xRight if maxY < yBottom: maxY = yBottom # Zeichnen des kleinst moeglichen Rechtecks, # welches alle gefundenen Elemente beinhaltet. cv2.rectangle(img=imgShowObject, pt1=(minX, minY), pt2=(maxX, maxY), color=rectangleColorGreen, thickness=lineThickness) # Anzeigen des Bildes in dem die gefundenen # Rechtecke eingezeichnet werden. if (self.__showImagesInWindow): cv2.imshow("ObjectCentering_FoundedContours", imgShowObject) cv2.waitKey(0) # Initialisiere die Werte der zu herausnehmenden Teilmatrix auf die gesamte Bildgroesse. xLeft = 0 xRight = matObject.shape[1] yTop = 0 yBottom = matObject.shape[0] # Falls ueber und unter den gefundenen Elementen die urspruenglichen Bildzeilen # zerstoert werden sollen, muessen die y-Koordinaten der zu herausnehmenden Teilmatrix # auf die y-Werte des Rahmens gesetzt werden. if (self.__useDeletingVerticalSpaces): yTop = minY yBottom = maxY # Falls neben den gefundenen Elementen die urspruenglichen Bildspalten # zerstoert werden sollen, muessen die x-Koordinaten der zu herausnehmenden Teilmatrix # auf die x-Werte des Rahmens gesetzt werden. if (self.__useDeletingHorizontalSpaces): xLeft = minX xRight = maxX # Schneide aus allen Eingangslisten, welche jeweils einen anderen Bildtypen enthaelt # die Teilmatrix mit den gefundenen Objekten aus und fuege sie in eine neue groessen-normierte Matrix ein. # Itteriere hierzu ueber alle Listen. for k in range(0, len(mats)): # Erstelle eine neue leere Matrix, in der die neue Matrix eingefuegt werden soll. newMatObject = np.full( (self.__targetNumberOfRows, self.__targetNumberOfColumns), fill_value=self.__colorOfBorder, dtype=matObject.dtype) # Schneide aus der urspruenglichen Matrix, die Teilmatrix aus, # die alle enthaltenen Objekte enthaelt. newCroppedMatObject = mats[k][i][yTop:yBottom, xLeft:xRight] # Anzeige der ausgeschnittenen Teilmatrix if (self.__showImagesInWindow): cv2.imshow("ObjectCentering_CropedMatrix", newCroppedMatObject) cv2.waitKey(0) # Speichern der Zeilen- und Spaltenzahl der Teilmatrix # und der Matrix in der diese eingefuegt werden soll. shapeCroppedMatObject_rows = newCroppedMatObject.shape[0] shapeCroppedMatObject_columns = newCroppedMatObject.shape[1] shapeNewMatObject_rows = newMatObject.shape[0] shapeNewMatObject_columns = newMatObject.shape[1] # Berechnung der Koordinaten in der die Teilmatrix in die neue Matrix eingefuegt werden soll. xLeft_insertion = int((shapeNewMatObject_columns / 2) - (shapeCroppedMatObject_columns / 2)) xRight_insertion = int((shapeNewMatObject_columns / 2) + (shapeCroppedMatObject_columns / 2)) yTop_insertion = int((shapeNewMatObject_rows / 2) - (shapeCroppedMatObject_rows / 2)) yBottom_insertion = int((shapeNewMatObject_rows / 2) + (shapeCroppedMatObject_rows / 2)) # Fuege die Teilmatrix in die Mitte der neuen Matrix hinzu, # um fuer alle Matrizen die selbe Groesse zu erhalten. newMatObject[ yTop_insertion:yBottom_insertion, xLeft_insertion:xRight_insertion] = newCroppedMatObject # Fuege die Matrix der k-ten Liste hinzu newMats[k].append(newMatObject) # Anzeige der Ergebnismatrix mit der normierten Groesse if (self.__showImagesInWindow): cv2.imshow("ObjectCentering_ResultMatrix", newMatObject) cv2.waitKey(0) # Abspeichern aller Objekte von jedem Matrix-Typ for matType in newMats: for object in matType: self.__logger.info("Centered/Resized following Object: ", "ObjectCentering:coreProcess", object) self.__logger.info("Finished coreProcess()", "ObjectCentering:coreProcess") return mats
class ImageResizer: def __init__(self, targetNumberOfRows=110, targetNumberOfColumns=32, interpolation=cv2.INTER_LINEAR, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "ImageResizer:__init__") self.__targetNumberOfRows = targetNumberOfRows self.__targetNumberOfColumns = targetNumberOfColumns self.__interpolation = interpolation self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finsihed __init__()", "ImageResizer:__init__") def coreProcess(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting coreProcess()", "ImageResizer:coreProcess") # Erstelle eine Liste mit i leeren Listen. # Jede leere Liste i wird fuer einen Matrix-Typen angelegt newMats = [] for i in range(0, len(mats)): newMats.append([]) # Iteriere ueber jeden Matrix-Typen for i in range(0, len(mats)): # Iteriere ueber jedes Objekt der Matrix-Typen-Liste for matObject in mats[i]: # Skaliere das Mat-Objekt mit der angegeben Interpolations-Methode newMatObject = cv2.resize(src=matObject, dsize=(self.__targetNumberOfColumns, self.__targetNumberOfRows), interpolation=self.__interpolation) # Fuege die Matrix der newMats[i].append(newMatObject) # Anzeigen des Mat-Objektes dem Zeilen bzw. Spalten # abgezogen bzw. hinzugefuegt wurden. if (self.__showImagesInWindow): cv2.imshow( "ImageFiller_Object: " + str(newMatObject.shape), newMatObject) cv2.waitKey(0) # Abspeichern aller Noten von jedem Matrix-Typ for matType in newMats: for note in matType: self.__logger.info( "Resized following Object to: " + str(note.shape), "ImageResizer:coreProcess", note) self.__logger.info("Finished coreProcess()", "ImageResizer:coreProcess") return newMats
class NoteHeightBlobClassifier(IClassifier): def __init__(self, indexOfProcessMatWithoutLines=0, indexOfProcessMatWithLines=1, maxGradeOfLinesInPx=2, marginTop=0.5, marginBottom=0.5, cannyThreshold1=50, cannyThreshold2=150, cannyApertureSize=3, houghLinesRho=1, houghLinesTheta=np.pi / 180, houghLinesThreshold=5, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "NoteHeightBlobClassifier:__init__") self.__indexOfProcessMatWithoutLines = indexOfProcessMatWithoutLines self.__indexOfProcessMatWithLines = indexOfProcessMatWithLines self.__firstLineHeight = 0 self.__stepHeight = 0 self.__maxGradeOfLinesInPx = maxGradeOfLinesInPx self.__marginTop = marginTop self.__marginBottom = marginBottom self.__cannyThreshold1 = cannyThreshold1 self.__cannyThreshold2 = cannyThreshold2 self.__cannyApertureSize = cannyApertureSize self.__houghLinesRho = houghLinesRho self.__houghLinesTheta = houghLinesTheta self.__houghLinesThreshold = houghLinesThreshold self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finsihed __init__()", "NoteHeightBlobClassifier:__init__") def classify(self, matArray): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting classify()", "NoteHeightBlobClassifier:classify") # Iterate about every note for i in range(0, len(matArray[self.__indexOfProcessMatWithoutLines])): imageWithLines = matArray[self.__indexOfProcessMatWithLines][i] cv2.imshow("Mit Linien", imageWithLines) # Apply edge detector canny edges = cv2.Canny(imageWithLines, threshold1=self.__cannyThreshold1, threshold2=self.__cannyThreshold2, apertureSize=self.__cannyApertureSize) # Apply hough line detection lines = cv2.HoughLines(edges, rho=self.__houghLinesRho, theta=self.__houghLinesTheta, threshold=self.__houghLinesThreshold) # Extract array of hough line coordinates lines_coordinates = [] for x in range(0, len(lines)): for rho, theta in lines[x]: # Calculation of line coordinates a = np.cos(theta) b = np.sin(theta) y0 = b * rho y1 = int(y0 + 1000 * (a)) y2 = int(y0 - 1000 * (a)) # Extract horizontal lines if (abs(y1 - y2) <= self.__maxGradeOfLinesInPx): lines_coordinates.append([y1, y2]) print([y1, y2]) # Sort coordinates of hough line lines_coordinates.sort(key=itemgetter(0)) # yPositionFirsLine = ( ((lines_coordinates[0][0] + lines_coordinates[0][1]) / 2) + ((lines_coordinates[1][0] + lines_coordinates[1][1]) / 2)) / 2 yPositionLastLine = ( ((lines_coordinates[len(lines_coordinates) - 1][0] + lines_coordinates[len(lines_coordinates) - 1][1]) / 2) + ((lines_coordinates[len(lines_coordinates) - 2][0] + lines_coordinates[len(lines_coordinates) - 2][1]) / 2)) / 2 # Berechne die Anzahl der Pixel von einer Notenhoehe zur naechsten Notenhoehe numberOfLines = 5 noteStep = (yPositionFirsLine + yPositionLastLine) / (numberOfLines - 1) / 2 # Draw hough lines to image showMat = cv2.cvtColor( src=matArray[self.__indexOfProcessMatWithoutLines][i], code=cv2.COLOR_GRAY2BGR) # Create array for mats and line groups noteLineCoordinates = [] line_groupe = [] lineThickness = 1 lineColor = (0, 255, 0) for j in range(0, len(lines_coordinates) - 1): """ Get line coordinates of line """ line = lines_coordinates[j] """ Draw line """ cv2.line(showMat, (0, line[0]), (showMat.shape[1] - 1, line[1]), lineColor, lineThickness) # Read image imageWithoutLines = matArray[ self.__indexOfProcessMatWithoutLines][i] imageWithoutLines = 255 - imageWithoutLines #cv2.waitKey(0) cv2.imshow("Test1", imageWithoutLines) #cv2.waitKey(0) imageWithoutLines = 255 - cv2.dilate( imageWithoutLines, np.ones((1, 10)), iterations=1) cv2.imshow("Test2", imageWithoutLines) imageWithoutLines = cv2.dilate(imageWithoutLines, np.ones((1, 16)), iterations=1) cv2.imshow("Test3", imageWithoutLines) cv2.waitKey(0) # Setup SimpleBlobDetector parameters. params = cv2.SimpleBlobDetector_Params() # Change thresholds # params.minThreshold = 0 # params.maxThreshold = 255 # Filter by Area. params.filterByArea = True params.minArea = 20 # params.maxArea = 500000 # Filter by Color # params.filterByColor = True # params.blobColor = 0 # Filter by Circularity # params.filterByCircularity = False # params.minCircularity = 0.0 # Filter by Convexity params.filterByConvexity = False # params.minConvexity = 0.87 # Filter by Inertia # params.filterByInertia = False # params.minInertiaRatio = 0.01 # Create a detector with the parameters detector = cv2.SimpleBlobDetector_create(params) # Detect blobs. keypoints = detector.detect(imageWithoutLines) if (not (len(keypoints) < 1)): # Draw detected blobs as red circles. # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures # the size of the circle corresponds to the size of blob im_with_keypoints = cv2.drawKeypoints( imageWithoutLines, keypoints, np.array([]), (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) print("Row", imageWithoutLines.shape[0]) print("Column", imageWithoutLines.shape[1]) for point in keypoints: print("x=", point.pt[0], "y=", point.pt[1]) cv2.circle(showMat, (int(point.pt[0]), int(point.pt[1])), 1, (0, 255, 255), 3) difference = yPositionFirsLine - keypoints[0].pt[1] note_nr = round(difference / noteStep * 2, 0) txt = "" if (note_nr == 0): txt = "f''" + str(note_nr) elif (note_nr == -1): txt = "e''" + str(note_nr) elif (note_nr == -2): txt = "d''" + str(note_nr) elif (note_nr == -3): txt = "c''" + str(note_nr) elif (note_nr == -4): txt = "h'" + str(note_nr) elif (note_nr == -5): txt = "a'" + str(note_nr) elif (note_nr == -6): txt = "g'" + str(note_nr) elif (note_nr == -7): txt = "f'" + str(note_nr) elif (note_nr == -8): txt = "e'" + str(note_nr) elif (note_nr == -9): txt = "d'" + str(note_nr) else: txt = "-" + str(note_nr) #cv2.addText(showMat, str(note_nr), (5, 5), font, 4, (243, 127, 53), 2, cv2.LINE_AA) cv2.putText(showMat, txt, (0, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.35, 255) # Show blobs cv2.imshow("Keypoints", im_with_keypoints) cv2.imshow("ShowMat", showMat) cv2.waitKey(0)
class KeyDetector(IDetector): def __init__(self, templateThreshold=0.75, templateMethod=cv2.TM_CCOEFF_NORMED, findCountersMode=cv2.RETR_LIST, findCountersMethode=cv2.CHAIN_APPROX_NONE): """ Konstruktor für den KeyDetector. :param templateThreshold: Der Wert den das Template-Matching mindestens erreichen muss, damit ein Bildauschnitt als der jeweilige Key erkannt wird :param templateMethod: Methode des Template-Matchers :param findCountersMode: Modus des Konturfindungs-Algorithmus :param findCountersMethode: Methode des Konturfindungs-Algorithmus """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "KeyDetector:__init__") self.__templateThreshold = templateThreshold self.__templateMethod = templateMethod self.__findCountersMode = findCountersMode self.__findCountersMethode = findCountersMethode self.__logger.info("Finished __init__()", "KeyDetector:__init__") def detect(self, matArray): """ Detection Funktion. Geerbt von IDetector. :param matArray: Liste von Bilder, welche die Takte enthalten :return: Liste alles gefundenen Keys mit und ohne Notenlinien """ self.__logger.info("Starting detect()", "KeyDetector:detect") # Initialierung der Taktlisten: # 1. List ohne und die 2. Liste mit Notenlinien clefMatsNoLines = [] clefMatsLines = [] # Variablen zum Zeichnen lineThickness = 1 rectangleColor = (0, 255, 0) for j in range(0, len(matArray[0])): mat = matArray[0][j].copy() # Takt ohne Linien matLine = matArray[1][j].copy() # Takt mit Linien self.__logger.debug("Starting detect Keys for image", "KeyDetector:detect", mat) found = False template = cv2.imread( "DetectionCore_Component/Detector/templates/key_b.jpg", 0) w, h = template.shape[::-1] res = cv2.matchTemplate(mat, template, cv2.TM_CCOEFF_NORMED) loc = np.where(res >= self.__templateThreshold) for pt in zip(*loc[::-1]): found = True cv2.rectangle(mat, pt, (pt[0] + w, pt[1] + h), rectangleColor, lineThickness) cv2.rectangle(matLine, pt, (pt[0] + w, pt[1] + h), rectangleColor, lineThickness) clefMatsNoLines.append(mat[:, pt[0]:pt[0] + w]) clefMatsLines.append(matLine[:, pt[0]:pt[0] + w]) if found: self.__logger.info("Detect following rectangle: ", "KeyDetector:detect", mat) for i in range(0, len(clefMatsNoLines)): self.__logger.info("Detected following clef: ", "KeyDetector:detect", clefMatsNoLines[i]) self.__logger.info("Finished detect()", "KeyDetector:detect") return clefMatsNoLines, clefMatsLines
class ClefTemplateClassifier(ATempalteMatcher): """ Klasse zur Klassifizierung von Notenschlüsseln in einem Takt. Erkennt Violin-, Bass- und Altschlüssel. Höhe der Schlüssel wird nicht erkannt!!!!! """ def __init__(self, config): """ Konstruktor der ClefTemplateClassifier Klasse. Initialisiert alle Membervariablen. :param config: Die Config für diesen TemplateMatcher """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__templateFolder = config["templateFolder"] self.__templateMethod = config["templateMethod"] self.__removeIfFound = config["removeIfFound"] self.__clefTemplates = [] self.__clefRatios = [] self.__clefThresholds = [] # Violin self.__clefTemplates.append(self.__loadTemplate("G", 2)) self.__clefRatios.append(83 / 97) self.__clefThresholds.append(0.52) # max: 0.77 min: 0.27 mid: 0.52 # Bass self.__clefTemplates.append(self.__loadTemplate("F", 4)) self.__clefRatios.append(36 / 97) self.__clefThresholds.append(0.64) # max: 0.76 min: 0.52 mid: 0.64 # Alt self.__clefTemplates.append(self.__loadTemplate("C", 3)) self.__clefRatios.append(50 / 97) self.__clefThresholds.append(0.535) # max: 0.58 min: 0.49 mid: 0.535 for template in self.__clefTemplates: self.__logger.info( "Loaded Clef " + template[1] + str(template[2]) + " Template", "ClefTemplateClassifier::__init__", template[0]) def classify(self, classUnit, matArray): """ Interface Methode: Jeder TempalteMatcher muss diese realisieren. Führt die Klassifizierung auf ein Array von Bildmatrizen anhand TemplateMatching aus und gibt die erkannten Objekte zurück. Parameters ---------- classUnit : Die Klassifikation als Out-Parameter matArray : Die Bildmatrizen auf welche die Klassifizierung ausgeführt werden soll. Returns ------- [True] wenn Notenschlüssel erkannt wurde, [False] falls nicht """ maxMatch = -1 maxLoc = (0, 0) for i in range(0, len(self.__clefTemplates)): match, loc = self.__hasClef(matArray, self.__clefTemplates[i][0], self.__clefRatios[i], self.__clefThresholds[i]) if match > classUnit.recognitionPercentage: classUnit.recognitionPercentage = match classUnit.sign = self.__clefTemplates[i][1] classUnit.line = self.__clefTemplates[i][2] maxMatch = i maxLoc = loc if maxMatch == -1: return False self.__logger.info( "Clef found " + classUnit.sign + str(classUnit.line) + " " + str(classUnit.recognitionPercentage), "ClefTemplateClassifier::classify", matArray[0]) if self.__removeIfFound: self._removeHit(matArray, self.__clefTemplates[maxMatch][0], self.__clefRatios[maxMatch], maxLoc) return True def __hasClef(self, mats, template, templateMatRatio, threshold): """ Führt den eigentlichen Templatematch aus. :param mats: Takt als Bildmatrix ([0] ohne Linien, [1] mit Linien, [2] nur skaliert). :param template: Template, welches zum Templatematching verwendet wird. :param templateMatRatio: Höhenverhältnis von Template zum Bildausschnitt. :param threshold: Grenzwert, welcher erreicht werden muss, damit das Template als gefunden gilt. :return: [True] wenn Threshold erreicht, [False] wenn nicht. """ # Template auf Bildgröße skalieren resizedTemplate = ATempalteMatcher._resizeTemplate( self, mats[0], template, templateMatRatio) if (mats[0].shape[1] >= resizedTemplate.shape[1]): # Templatematching mit Bild OHNE Linen matchNoLine = cv2.matchTemplate(mats[0], resizedTemplate, self.__templateMethod) # Templatematching mit Bild MIT Linen matchLine = cv2.matchTemplate(mats[1], resizedTemplate, self.__templateMethod) # Templatematching mit nur skaliertem Bild matchSkal = cv2.matchTemplate(mats[2], resizedTemplate, self.__templateMethod) # Verwende das Maximum von allen Matches match = cv2.max(matchLine, matchNoLine, matchSkal) # Hole den maximalen Wert und die zugehörige Position __, maxVal, __, maxLoc = cv2.minMaxLoc(match) # Wenn Threshold erreicht wurde if maxVal >= threshold: return maxVal, maxLoc return 0, (0, 0) def __loadTemplate(self, sign, line): return [ cv2.imread( self.__templateFolder + "clef_" + sign + "_" + str(line) + ".jpg", 0), sign, line ]
class CnnNoteClassifier(IClassifier): def __init__(self, modelDir, gdLearnRateForTypeModel, gdLearnRateForHightModel, trainSteps, evalInterval, evalDataSize, testDataSize, trainTypeModel=True, trainHightModel=True): """ Constructor, initialisiert Membervariablen. :param modelDir : string Pfad unter welchem die trainierten CnnModelle gespeichert werden. :example: CnnNoteClassifier("my/path/to/a/model/Dir") """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__noteTypeCnn = CnnModelSuit( cnnNotetypeModel, predictionToNotetypeClassificationFn, modelDir + "/noteTypeModel", gdLearnRateForTypeModel) self.__noteHightCnn = CnnModelSuit( cnnNoteHightModel, predictionToNoteHightClassificationFn, modelDir + "/noteHightModel", gdLearnRateForHightModel) self.__gdLearnRateForTypeModel = gdLearnRateForTypeModel self.__gdLearnRateForHightModel = gdLearnRateForHightModel self.__trainSteps = trainSteps self.__evalInterval = evalInterval self.__evalDataSize = evalDataSize self.__testDataSize = testDataSize self.__trainTypeModel = trainTypeModel self.__trainHightModel = trainHightModel def classify(self, inputDataWithLines, inputDataWithoutLines): """ Bereitet die Inputdaten für das neuronale Netz vor und klassifiziert mit diesem den Input zu einer Note. :param inputDataWithLines : Eine Mat welche ein Bild einer Note mit Linien enthält. :param inputDataWithoutLines : Eine Mat welche ein Bild einer Note ohne Linien enthält. :return: note : Note() Die Klassifizierung einer Musiknote. :example: cnnNoteClassifier.classify(myInputDataWithLines, myInputDataWithoutLines) """ #Converting the Input cnnNoteTypeInput = [] cnnNoteHightInput = [] for i in range(0, len(inputDataWithLines)): cnnNoteTypeInput.append(inputDataWithoutLines[i].ravel()) cnnNoteHightInput.append(inputDataWithLines[i].ravel()) cnnNoteTypeInput, _ = self.convDataForNet(cnnNoteTypeInput) cnnNoteHightInput, _ = self.convDataForNet(cnnNoteHightInput) #Classify the Input noteTypes = self.__noteTypeCnn.classify(cnnNoteTypeInput) noteSteps, noteOctaves = self.__noteHightCnn.classify( cnnNoteHightInput) #Converting the Classification into internal representation notens = [] for i in range(0, len(noteTypes)): # One of i ClassinifcationUnit to classify note = Note() note.type = noteTypes[i] note.pitch["step"] = noteSteps[i] note.pitch["octave"] = noteOctaves[i] notens.append(note) self.__logger.info( "Classified following notetype: " + str(note.type) + " step: " + str(note.pitch["step"]) + " octave: " + str(note.pitch["octave"]), "CnnNoteClassifier:classify", inputDataWithLines[i]) #cv2.imshow(note.type, inputDataWithLines[i]) #cv2.waitKey() return notens def train(self, inputTrainData, inputTrainLabels): """ Bereitet die Inputdaten für das neuronale Netz vor und trainiert dieses mit den Daten. Führt alle n Schritte eine Evaluation auf dem Netz durch. :param inputTrainData : Eine Array von Mat welche ein Bild einer Note mit und daneben eins ohne Linien enthält. :param inputTrainLabels : Eine Array von Labels für die Bilder von inputTrainData (Gleiche Länge gleiche Sortierung) :example: cnnNoteClassifier.train(myInputTrainData, myInputTrainaLables) """ self.__logger.info("Starting train()", "CnnNoteClassifier:train") #Inputs for the Cnns to train noteTypeTrainData = [] noteTypeTrainLable = [] noteHightTrainData = [] noteHightTrainLable = [] #Converting the Input for i in range(0, len(inputTrainData)): noteTypeTrainData.append(inputTrainData[i][0:96, 28:57].ravel()) noteHightTrainData.append(inputTrainData[i][0:96, 0:28].ravel()) noteTypeTrainLable.append( noteTypeToInt(inputTrainLabels[i]["type"])) noteHightTrainLable.append( noteHightToInt(inputTrainLabels[i]["step"], inputTrainLabels[i]["octave"])) noteTypeTrainData, noteTypeTrainLable = self.convDataForNet( noteTypeTrainData, noteTypeTrainLable) noteHightTrainData, noteHightTrainLable = self.convDataForNet( noteHightTrainData, noteHightTrainLable) #Shuffel the Input via Permutation. This is an inital shuffel neeedet to split the data into train, eval, test #The Train Shuffel will be made after each step in the train Methode of CnnModelSuit p = np.random.permutation(len(noteTypeTrainData)) noteTypeTrainData = noteTypeTrainData[p] noteTypeTrainLable = noteTypeTrainLable[p] noteHightTrainData = noteHightTrainData[p] noteHightTrainLable = noteHightTrainLable[p] #Training the model noTrainDataSize = self.__evalDataSize + self.__testDataSize if self.__trainTypeModel: #Train the model for NoteType for i in range(0, self.__trainSteps, self.__evalInterval): self.__noteTypeCnn.train( noteTypeTrainData[0:-noTrainDataSize], noteTypeTrainLable[0:-noTrainDataSize], self.__evalInterval) self.__noteTypeCnn.eval( noteTypeTrainData[-noTrainDataSize:-self.__testDataSize], noteTypeTrainLable[-noTrainDataSize:-self.__testDataSize]) #Testing the model self.__noteTypeCnn.eval(noteTypeTrainData[-self.__testDataSize:], noteTypeTrainLable[-self.__testDataSize:]) if self.__trainHightModel: #Train the model for NoteHight for i in range(0, self.__trainSteps, self.__evalInterval): self.__noteHightCnn.train( noteHightTrainData[0:-noTrainDataSize], noteHightTrainLable[0:-noTrainDataSize], self.__evalInterval) self.__noteHightCnn.eval( noteHightTrainData[-noTrainDataSize:-self.__testDataSize], noteHightTrainLable[-noTrainDataSize:-self.__testDataSize]) #Testing the model self.__noteHightCnn.eval( noteHightTrainData[-self.__testDataSize:], noteHightTrainLable[-self.__testDataSize:]) self.__logger.info("Finished train()", "CnnNoteClassifier:train") def convDataForNet(self, inputData, lables=None): """ Convertiert die Inputdaten und Lables für das neuronale Netz. :param inputData : Eine Array von Mats welche ein Bild einer Note enthält. :param lables : Eine Array von Labels für die Bilder von inputData (Gleiche Länge gleiche Sortierung) :example: cnnNoteClassifier.convDataForNet(myInpuData, mLables) """ #Converting the Input into one Image - each row represents a note inputData = np.row_stack(inputData) inputData = inputData.astype(np.float32) inputData = inputData / 255 if lables: lables = np.array(lables) # print(lables) # print(type(lables)) # print(lables.shape) # print(inputData) # print(type(inputData)) # print(inputData.shape) return inputData, lables
class SheetMusicTemplateClassifier(IFullClassifier): def __init__(self, config): """ Constructor, initialisiert Membervariablen. :param config: Die Config für den SheetMusicClassifier """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "SheetMusicTemplateClassifier:__init__") self.__config = config lineRemoveConfig = self.__config["HorizontalLineRemoveDetector"] self.__horizontalLineRemoveDetector = HorizontalLineRemoveDetector \ (indexOfProcessMat=lineRemoveConfig["indexOfProcessMat"], anchorPoint=(lineRemoveConfig["anchorPointX"], lineRemoveConfig["anchorPointY"]), kernelWidth=lineRemoveConfig["kernelWidth"], kernelHeight=lineRemoveConfig["kernelHeight"], morphOfKernel=getattr(cv2, lineRemoveConfig["morphOfKernel"]), showImagesInWindow=lineRemoveConfig["showImagesInWindow"]) lineDetectorConfig = self.__config["NoteLineDetector"] self.__noteLineDetector = NoteLineDetector \ (indexOfProcessMat=lineDetectorConfig["indexOfProcessMat"], maxGradeOfLinesInPx=lineDetectorConfig["maxGradeOfLinesInPx"], minDistanceToNextNoteRow=lineDetectorConfig["minDistanceToNextNoteRow"], marginTop=lineDetectorConfig["marginTop"], marginBottom=lineDetectorConfig["marginBottom"], cannyThreshold1=lineDetectorConfig["cannyThreshold1"], cannyThreshold2=lineDetectorConfig["cannyThreshold2"], cannyApertureSize=lineDetectorConfig["cannyApertureSize"], houghLinesRho=lineDetectorConfig["houghLinesRho"], houghLinesTheta=np.pi / 180 * lineDetectorConfig["houghLinesThetaInDegree"], houghLinesThreshold=lineDetectorConfig["houghLinesThreshold"], showImagesInWindow=lineDetectorConfig["showImagesInWindow"]) tactDetectorConfig = self.__config["TactDetector"] self.__tactDetector = TactDetector \ (indexOfProcessMat=tactDetectorConfig["indexOfProcessMat"], tactLineWidthMax=tactDetectorConfig["tactLineWidthMax"], tactLineHeightMin=tactDetectorConfig["tactLineHeightMin"], minWidthOfTactLine=tactDetectorConfig["minWidthOfTactLine"], findCountersMode=getattr(cv2, tactDetectorConfig["findCountersMode"]), findCountersMethode=getattr(cv2, tactDetectorConfig["findCountersMethode"]), showImagesInWindow=tactDetectorConfig["showImagesInWindow"]) self.__clefClassifier = ClefTemplateClassifier( self.__config["ClefTemplateClassifier"]) self.__timeClassifier = TimeTemplateClassifier( self.__config["TimeTemplateClassifier"]) self.__keyClassifier = KeyTemplateClassifier( self.__config["KeyTemplateClassifier"]) noteDetectorConfig = self.__config["NoteDetector"] self.__noteDetector = NoteDetector \ (indexOfProcessMat=noteDetectorConfig["indexOfProcessMat"], minNoteWidth_WithStem=noteDetectorConfig["minNoteWidth_WithStem"], maxNoteWidth_WithStem=noteDetectorConfig["maxNoteWidth_WithStem"], minNoteHeight_WithStem=noteDetectorConfig["minNoteHeight_WithStem"], maxNoteHeight_WithStem=noteDetectorConfig["maxNoteHeight_WithStem"], minNoteWidth_WithoutStem=noteDetectorConfig["minNoteWidth_WithoutStem"], maxNoteWidth_WithoutStem=noteDetectorConfig["maxNoteWidth_WithoutStem"], minNoteHeight_WithoutStem=noteDetectorConfig["minNoteHeight_WithoutStem"], maxNoteHeight_WithoutStem=noteDetectorConfig["maxNoteHeight_WithoutStem"], noteImageWidth=noteDetectorConfig["noteImageWidth"], findCountersMode=getattr(cv2, noteDetectorConfig["findCountersMode"]), findCountersMethode=getattr(cv2, noteDetectorConfig["findCountersMethode"]), showImagesInWindow=noteDetectorConfig["showImagesInWindow"]) # NoteHeightBlobClassifier wird nicht mehr benoetigt, da die # Notenhoehe von einem CNN klassifiziert wird. """ noteDetectorConfig = self.__config["NoteDetector"] self.__noteHeightBlobClassifier = NoteHeightBlobClassifier \ (indexOfProcessMatWithoutLines=noteDetectorConfig["indexOfProcessMatWithoutLines"], indexOfProcessMatWithLines=noteDetectorConfig["indexOfProcessMatWithLines"], maxGradeOfLinesInPx=noteDetectorConfig["maxGradeOfLinesInPx"], marginTop=noteDetectorConfig["marginTop"], marginBottom=noteDetectorConfig["marginBottom"], cannyThreshold1=noteDetectorConfig["cannyThreshold1"], cannyThreshold2=noteDetectorConfig["cannyThreshold2"], cannyApertureSize=noteDetectorConfig["cannyApertureSize"], houghLinesRho=noteDetectorConfig["houghLinesRho"], houghLinesTheta=np.pi / 180 * noteDetectorConfig["houghLinesThetaInDegree"], houghLinesThreshold=noteDetectorConfig["houghLinesThreshold"], showImagesInWindow=noteDetectorConfig["showImagesInWindow"]) """ fillerConfig = self.__config["ImageFiller"] self.__imageFiller = ImageFiller \ (fillRows=fillerConfig["fillRows"], fillColumns=fillerConfig["fillColumns"], targetNumberOfRows=fillerConfig["targetNumberOfRows"], targetNumberOfColumns=fillerConfig["targetNumberOfColumns"], appendRowsTop=fillerConfig["appendRowsTop"], appendColumnsRight=fillerConfig["appendColumnsRight"], showImagesInWindow=fillerConfig["showImagesInWindow"]) centeringConfig = self.__config["ObjectCentering"] self.__objectCentering = ObjectCentering \ (indexOfProcessMat=centeringConfig["indexOfProcessMat"], targetNumberOfRows=centeringConfig["targetNumberOfRows"], targetNumberOfColumns=centeringConfig["targetNumberOfColumns"], useDeletingVerticalSpaces=centeringConfig["useDeletingVerticalSpaces"], useDeletingHorizontalSpaces=centeringConfig["useDeletingHorizontalSpaces"], findCountersMode=getattr(cv2, centeringConfig["findCountersMode"]), findCountersMethode=getattr(cv2, centeringConfig["findCountersMethode"]), colorOfBorder=centeringConfig["colorOfBorder"], showImagesInWindow=centeringConfig["showImagesInWindow"]) resizerConfig = self.__config["ImageResizer"] self.__imageResizerForCnn = ImageResizer \ (targetNumberOfRows=resizerConfig["targetNumberOfRows"], targetNumberOfColumns=resizerConfig["targetNumberOfColumns"], interpolation=getattr(cv2, resizerConfig["interpolation"]), showImagesInWindow=resizerConfig["showImagesInWindow"]) dirname, _ = os.path.split(os.path.abspath(__file__)) cnnConfig = self.__config["CnnNoteClassifier"] self.__cnnNoteClassifier = CnnNoteClassifier \ (modelDir=dirname + cnnConfig["modelDir"], gdLearnRateForTypeModel=cnnConfig["gdLearnRateForTypeModel"], gdLearnRateForHightModel=cnnConfig["gdLearnRateForHightModel"], trainSteps=cnnConfig["trainSteps"], evalInterval=cnnConfig["evalInterval"], evalDataSize=cnnConfig["evalDataSize"], testDataSize=cnnConfig["testDataSize"], trainTypeModel = cnnConfig["trainTypeModel"], trainHightModel = cnnConfig["trainHightModel"]) self.__logger.info("Finished __init__()", "SheetMusicTemplateClassifier:__init__") def executeClassification(self, mat): """ Führt die Klassifizierung auf einer Bildmatrix aus und gibt die erkannte IClassification zurück. Parameters ---------- mat : Die Bildmatrix auf welche die Klassifizierung ausgeführt werden soll. Returns ------- classification : IClassification Aus dem Eingabematrix erkannte IClassification (z.B. Note, Slur, Rest). Example ------- >>> sheetMusicTemplateClassifier.executeClassification(mat) """ self.__logger.info( "Starting executeClassification()", "SheetMusicTemplateClassifier:executeClassification") classification = SheetMusic() noteLinesWithoutHLines = self.__horizontalLineRemoveDetector.detect( mat) noteLines = self.__noteLineDetector.detect(noteLinesWithoutHLines) tacts = self.__tactDetector.detect(noteLines) # --- Takt Klassifikation --- elementCounter = 1 lastClefLine = 0 lastClefSign = "" # Führt für jeden erkannten Takt aus for i in range(0, len(tacts[0])): # self.__logger.debug("Analyzing Tact", image = tacts[0][i]) tactInternalCounter = 1 tactClassification = classification.creatTact( i + 1, elementCounter) # Neuer Takt elementCounter += 1 # ----- Clef ----- clefClassification = tactClassification.creatClef( tactInternalCounter, elementCounter) if self.__clefClassifier.classify( clefClassification, [tacts[0][i], tacts[1][i], tacts[2][i]]): if (clefClassification.sign != lastClefSign or clefClassification.line != lastClefLine): tactClassification.addClef( clefClassification ) # Füge Notenschlüssel zu Takt hinzu elementCounter += 1 tactInternalCounter += 1 lastClefSign = clefClassification.sign lastClefLine = clefClassification.line # ----- KeySignature ----- keyClassification = tactClassification.creatKeySignature( tactInternalCounter, elementCounter) if self.__keyClassifier.classify( keyClassification, [tacts[0][i], tacts[1][i], tacts[2][i]]): tactClassification.addKeySignatur( keyClassification) # Füge Tonhöheangabe zu Takt hinzu elementCounter += 1 tactInternalCounter += 1 # ----- Time ----- timeClassification = tactClassification.creatTimeSignature( tactInternalCounter, elementCounter) if self.__timeClassifier.classify( timeClassification, [tacts[0][i], tacts[1][i], tacts[2][i]]): tactClassification.addTimeSignatur( timeClassification) # Füge Rythmus zu Takt hinzu elementCounter += 1 tactInternalCounter += 1 classification.addTact( tactClassification) # Füge Takt zur Klassifikation hinzu # --- Takt Klassifikation --- notes, noteCountPerTact = self.__noteDetector.detect(tacts) self.__logger.debug( "Return from noteDetector 0", "SheetMusicTemplateClassifier::executeClassification", notes[0][0]) # Ohne Linien self.__logger.debug( "Return from noteDetector 1", "SheetMusicTemplateClassifier::executeClassification", notes[1][0]) # Mit Linien filledNotes = self.__imageFiller.coreProcess(notes) centeredNotes = self.__objectCentering.coreProcess(filledNotes) cnnNotes = self.__imageResizerForCnn.coreProcess(centeredNotes) # notesHeight = self.__noteHeightBlobClassifier.classify(notes) # self.__logger.debug("Return from noteHeightBlobClassifier 0", "SheetMusicTemplateClassifier::executeClassification", notesHeight[0][0]) # Ohne Linien # self.__logger.debug("Return from noteHeightBlobClassifier 1", "SheetMusicTemplateClassifier::executeClassification", notesHeight[1][0]) # Mit Linien # ----- Classify Notes ----- notes = self.__cnnNoteClassifier.classify(cnnNotes[0], cnnNotes[1]) # Fülle die Takte mit den erkannten Noten und nutze zur Vermeidung von Fehlerfortpflanzung noteCountPerTact noteIndex = 0 for t in range(0, len(noteCountPerTact)): for n in range(0, noteCountPerTact[t]): chord = classification.tacts[t].creatChord( classification.tacts[t].tactNumber, n) note = chord.creatNote(classification.tacts[t].tactNumber, n, chord.chordInternalNumber) note.pitch = notes[noteIndex].pitch note.type = notes[noteIndex].type chord.addNote(note) classification.tacts[t].addChord(chord) noteIndex += 1 self.__logger.debug( ("Tact " + str(t) + " added Note on pos " + str(n) + " type: " + note.type + " pitch: " + str(note.pitch)), "SheetMusicTemplateClassifier:executeClassification") self.__logger.info( "Finished executeClassification()", "SheetMusicTemplateClassifier:executeClassification") return classification def buildTraindata(self, mat): """ Interface Methode: Jeder FullClassifier muss diese realisieren. Erstellt Trainingdaten für das Trainieren seiner Classifier :param Bildmatrix : mat Die Bildmatrix aus welcher die Trainingdaten erstellt werden soll. :return: trainData : matArray Aus dem Eingabematrix erstellte Trainingdaten. """ self.__logger.info("Starting buildTraindata()", "SheetMusicTemplateClassifier:buildTraindata") noteLinesWithoutHLines = self.__horizontalLineRemoveDetector.detect( mat) noteLines = self.__noteLineDetector.detect(noteLinesWithoutHLines) tacts = self.__tactDetector.detect(noteLines) notes, _ = self.__noteDetector.detect(tacts) self.__logger.debug("Return from noteDetector 0", "SheetMusicTemplateClassifier::buildTraindata", notes[0][0]) # Ohne Linien self.__logger.debug("Return from noteDetector 1", "SheetMusicTemplateClassifier::buildTraindata", notes[1][0]) # Mit Linien filledNotes = self.__imageFiller.coreProcess(notes) centeredNotes = self.__objectCentering.coreProcess(filledNotes) cnnNotes = self.__imageResizerForCnn.coreProcess(centeredNotes) self.__logger.info("Finished executeClassification()", "SheetMusicTemplateClassifier:buildTraindata") return cnnNotes def train(self, inputMatArray, lableArray): """ Trainiert alle trainierbaren Classifier mit den gegebenen Trainingsdaten :param Bildmatrizen: inputMatArray Die Bildmatrizen für das Training. :param Lable: lableArray Die Lable zu den Bildmatrizen für das Training. :return: successful : boolean War das Training erfolgreich? """ self.__logger.info("Started train()", "SheetMusicTemplateClassifier:train") self.__cnnNoteClassifier.train(inputMatArray, lableArray) self.__logger.info("Finished train()", "SheetMusicTemplateClassifier:train")
class CnnModelSuit: def __init__(self, cnnModelFn, preditcionToClassificationFn, modelDir="/tmp/cnn_model", learnRate=0.001): """ Constructor, initialisiert Membervariablen. :param cnnModelFn : function Ein CnnModel mit den Methoden train, predict, evaluate. :param predictedClassesToClassificationFn : function Eine Funktion um die Klassifizierung der cnnModelFn zu interpretieren, :param modelDir : string Pfad unter welchem das trainierte CnnModel gespeichert wird. :example: CnnModelSuit(cnnNotetypeModel, predictedClassesToNotetypeClassificationFn, "my/path/to/a/model/Dir") """ # Set model params modelParams = {"learning_rate": learnRate} #Setting up the Loggers tf.logging.set_verbosity(tf.logging.INFO) self.__logger = State().getLogger("DetectionCore_Component_Logger") #The Function to interprete the Classification Vector self.preditcionToClassificationFn = preditcionToClassificationFn #The Cnn to be trained and to classify the inputs self.__cnnClassifier = tf.estimator.Estimator(model_fn=cnnModelFn, model_dir=modelDir, params=modelParams) # Set up logging for predictions # Log the values in the "Softmax" tensor with label "probabilities" self.__tensorsToLog = {"probabilities": "softmax_tensor"} self.__loggingHook = tf.train.LoggingTensorHook( tensors=self.__tensorsToLog, every_n_iter=50) def classify(self, inputData): """ Bereitet die Inputdaten für das neuronale Netz vor und klassifiziert mit diesem den Input. :param inputData : Eine Mat welche den zu klassifizierenden Input enthält. :return: predictedClass : Die Klassifizierung welche durch die predictedClassesToClassificationFn bestimmt wird. :example: cnnModelSuit.classify(myInputData) """ self.__logger.info("Starting classify()", "CnnModelSuit:classify") #Classify Input with the trained model predictInputFn = tf.estimator.inputs.numpy_input_fn(x={"x": inputData}, num_epochs=1, shuffle=False) predictions = list( self.__cnnClassifier.predict(input_fn=predictInputFn)) self.__logger.info("Finished classify()", "CnnModelSuit:classify") return self.preditcionToClassificationFn(predictions) def train(self, trainData, trainLabels, steps): """ Bereitet die Inputdaten für das neuronale Netz vor und trainiert dieses mit den Daten. :param trainData : Eine Array von Mats welche traineirt werden sollen. :param trainLabels : Eine Array von Labels für die Mats von trainData (Gleiche Länge gleiche Sortierung) :param steps : Anzahl der Trainingsschritte :example: cnnNoteClassifier.train(myTrainData, myInputTrainLables) """ self.__logger.info("Starting train()", "CnnModelSuit:train") #Train the model trainInputFn = tf.estimator.inputs.numpy_input_fn(x={"x": trainData}, y=trainLabels, batch_size=100, num_epochs=None, shuffle=True) self.__cnnClassifier.train(input_fn=trainInputFn, steps=steps, hooks=[self.__loggingHook]) self.__logger.info("Finished train()", "CnnModelSuit:train") def eval(self, evalData, evalLabels): """ Bereitet die Inputdaten für das neuronale Netz vor und evaluiert dieses mit den Daten. :param evalData : Eine Array von Mats mit welchen evaluiert werden sollen. :param evalLabels : Eine Array von Labels für die Mats von evalData (Gleiche Länge gleiche Sortierung) :example: cnnNoteClassifier.eval(myEvalData, myEvalLabels) """ self.__logger.info("Starting eval()", "CnnModelSuit:eval") #Evaluate the model and print results evalInputFn = tf.estimator.inputs.numpy_input_fn(x={"x": evalData}, y=evalLabels, num_epochs=1, shuffle=False) evalResults = self.__cnnClassifier.evaluate(input_fn=evalInputFn) self.__logger.info("Finished eval()", "CnnModelSuit:eval")
class NoteLineDetector(IDetector): def __init__(self, indexOfProcessMat=0, maxGradeOfLinesInPx=2, minDistanceToNextNoteRow=20, marginTop=0.5, marginBottom=0.5, cannyThreshold1=50, cannyThreshold2=150, cannyApertureSize=3, houghLinesRho=1, houghLinesTheta=np.pi / 180, houghLinesThreshold=200, showImagesInWindow=False): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "NoteLineDetector:__init__") self.__indexOfProcessMat = indexOfProcessMat self.__maxGradeOfLinesInPx = maxGradeOfLinesInPx self.__minDistanceToNextNoteRow = minDistanceToNextNoteRow self.__marginTop = marginTop self.__marginBottom = marginBottom self.__cannyThreshold1 = cannyThreshold1 self.__cannyThreshold2 = cannyThreshold2 self.__cannyApertureSize = cannyApertureSize self.__houghLinesRho = houghLinesRho self.__houghLinesTheta = houghLinesTheta self.__houghLinesThreshold = houghLinesThreshold self.__showImagesInWindow = showImagesInWindow self.__logger.info("Finished __init__()", "NoteLineDetector:__init__") def detect(self, mats): """ To-Do: Bitte Kommentar bzw. Dokumentaion erstellen! """ self.__logger.info("Starting detect()", "NoteLineDetector:detect") mat = mats[self.__indexOfProcessMat] # Anwendung des Canny Filters um als Kantendetektor, # um über den HoughLines Detektor Kanten zu detektieren edges = cv2.Canny(mat, threshold1=self.__cannyThreshold1, threshold2=self.__cannyThreshold2, apertureSize=self.__cannyApertureSize) self.__logger.info("Detected following Canny-Edges", "NoteLineDetector:detect", edges) # Verwendung des HoughLines Detektors um Notenlinien zu detektieren lines = cv2.HoughLines(edges, rho=self.__houghLinesRho, theta=self.__houghLinesTheta, threshold=self.__houghLinesThreshold) # Gehe alle Linien durch, um aus rho und theta # die Linienkoordinaten zu berechnen lines_coordinates = [] for x in range(0, len(lines)): for rho, theta in lines[x]: # Berechnung der Linienkoordinaten # Die X Koordinaten gehen über die komplette Seite, # daher 0 und mat.shape[1] a = np.cos(theta) b = np.sin(theta) x0 = a * rho y0 = b * rho x1 = int(x0 + 0 * (-b)) y1 = int(y0 + 1000 * (a)) x2 = int(x0 - mat.shape[1] * (-b)) y2 = int(y0 - 1000 * (a)) # Speichere die Koordinaten in eine Liste, wenn # eine definierte Steigung nicht überschritten wird if (abs(y1 - y2) <= self.__maxGradeOfLinesInPx): lines_coordinates.append([x1, y1, x2, y2]) # Sortieren die Linien von oben nach unten lines_coordinates.sort(key=itemgetter(1)) # Erstelle eine Matrix, welche als Ausgabe verwendete wird # auf welcher die Linien farbig gezeichnet werden koennen showMat = cv2.cvtColor(src=mat, code=cv2.COLOR_GRAY2BGR) # Die noteLineCoordinates ist ein Array, welches spaeter # die Rechteckkoordinaten der Notenzeilen enthaelt, # um diese als eigenstaendige Matrizen extrahieren zu koennen. # Eine Liniengruppe definiert Linien die zur selben Zeile gehoeren noteLineCoordinates = [] line_groupe = [] lineThickness = 2 lineColor = (0, 255, 0) rectangleColor = (255, 255, 0) # Gehe allen Notenlinien durch for i in range(0, len(lines_coordinates) - 1): # Erhalte die Koordinaten einer Linie und Speichere sie in einer Liniengruppe line_coordinates = lines_coordinates[i] line_groupe.append(line_coordinates) # Zeichne die Linie auf die Debug-Matrix cv2.line(showMat, (line_coordinates[0], line_coordinates[1]), (line_coordinates[2], line_coordinates[3]), lineColor, lineThickness) # Falls der Abstand zwischen zwei Notenzinien gering genug ist, # dann ist die Linie ein Teil einer Liniengruppe und somit Teil einer Notenzeile if (not (abs(line_coordinates[1] - lines_coordinates[i + 1][1]) < self.__minDistanceToNextNoteRow)): # Berechne einen Rahmen zu der aktuellen Notenzeile marginTop = int( abs(line_groupe[0][1] - line_groupe[len(line_groupe) - 1][1]) * self.__marginTop) marginBorder = int( abs(line_groupe[0][1] - line_groupe[len(line_groupe) - 1][1]) * self.__marginBottom) coordinateTop = line_groupe[0][1] - marginTop coordinateBottom = line_groupe[len(line_groupe) - 1][1] + marginBorder # Zeichne einen Rahmen um die aktuelle Notenzeile cv2.rectangle(img=showMat, pt1=(0, coordinateTop), pt2=(mat.shape[1], coordinateBottom), color=rectangleColor, thickness=lineThickness) # Speichern der Koordinaten der Notenzeile noteLineCoordinates.append( [coordinateTop, coordinateBottom, 0, mat.shape[1]]) # Loesche den Inhalt, um Platz fuer eine neue Liniengruppe zu sorgen line_groupe.clear() else: # Erhalte die Koordinaten einer Linie und Speichere sie in einer Liniengruppe line_coordinates = lines_coordinates[len(lines_coordinates) - 1] line_groupe.append(line_coordinates) # Zeichne die Linie auf die Debug-Matrix cv2.line(showMat, (line_coordinates[0], line_coordinates[1]), (line_coordinates[2], line_coordinates[3]), lineColor, lineThickness) # Berechne einen Rahmen zu der aktuellen Notenzeile marginTop = int( abs(line_groupe[0][1] - line_groupe[len(line_groupe) - 1][1]) * self.__marginTop) marginBorder = int( abs(line_groupe[0][1] - line_groupe[len(line_groupe) - 1][1]) * self.__marginBottom) coordinateTop = line_groupe[0][1] - marginTop coordinateBottom = line_groupe[len(line_groupe) - 1][1] + marginBorder # Zeichne einen Rahmen um die aktuelle Notenzeile cv2.rectangle(img=showMat, pt1=(0, coordinateTop), pt2=(mat.shape[1], coordinateBottom), color=rectangleColor, thickness=lineThickness) self.__logger.info("Detected following Line:", "NoteLineDetector:detect", showMat) # Speichern der Koordinaten der Notenzeile noteLineCoordinates.append( [coordinateTop, coordinateBottom, 0, mat.shape[1]]) # Loesche den Inhalt, um Platz fuer eine neue Liniengruppe zu sorgen line_groupe.clear() # Anzeigen des Notenblattes indem die gefunden Notenzeilen # und Notenlinien farblich markiert sind. if (self.__showImagesInWindow): cv2.imshow("NoteLineDetector_MusicSheet", showMat) cv2.waitKey(0) # Extrahierung und Abspeichern der Notenzeilen # aus allen eingegeben Matrizen (Notenblaetter). noteLinesMats = [] for mat_Bla in mats: noteRows = [] for line_coordinates in noteLineCoordinates: if (line_coordinates[0] != line_coordinates[1]): noteRow = mat_Bla[line_coordinates[0]:line_coordinates[1], line_coordinates[2]:line_coordinates[3]] noteRows.append(noteRow) if (self.__showImagesInWindow): cv2.imshow("NoteLineDetector_NoteRow", noteRow) cv2.waitKey(0) # noteRow = np.zeros(noteRow.shape) noteLinesMats.append(noteRows) # Abspeichern der Notenzeilen aller Matrizen for i in range(0, len(noteLinesMats[0])): for j in range(0, len(noteLinesMats)): self.__logger.info("Detected following Notelines:", "NoteLineDetector:detect", noteLinesMats[j][i]) return noteLinesMats
class DetectionCore(IDetectionCore): def __init__(self, config): """ Constructor, initialisiert Membervariablen :param Config: config Die Konfiguration. :example: DetectionCore(config) """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__logger.info("Starting __init__()", "DetectionCore:__init__") self.__fullClassifier = SheetMusicTemplateClassifier( config["SheetMusicClassifier"]) """ Todo: self.__classificationCombiner = some ClassificationCombiner Mehr fullClassifier können hier erstellt werden """ self.__logger.info("Finished __init__()", "DetectionCore:__init__") def execute(self, mat): """ Führt die Klassifiezierung auf die BildMatrix aus und gibt die Klassifiezierung zurück. :param Bildmatrix: mat Die Bildmatrix auf welcher die Klassifiezierung stattfinden soll. :return: IClassification : classification Die Klassifizierung :example: detectionCore.execute(mat) """ self.__logger.info("Starting execute()", "DetectionCore:execute") classification = self.__fullClassifier.executeClassification(mat) """ Todo: Dann könnten noch weitere fullClassifier klassifizieren und ein Combiner die Klassifizierungen bewerten und kombinieren. """ self.__logger.info("Finished execute()", "DetectionCore:execute") return classification def buildNextTraindata(self, mat): """ Interface Methode: Jeder DetectionCore muss diese realisieren. Erstellt Trainingsdaten und fügt diese einem Ordner hinzu. :param Bildmatrix: mat Die Bildmatrix aus welcher die Trainingsdaten erstell werden sollen. :return: nextTrainData : Trainingsdaten zu gegebenem Bild und Lable. """ self.__logger.info("Starting buildNextTraindata()", "DetectionCore:buildNextTraindata") trainDataArray = self.__fullClassifier.buildTraindata(mat) self.__logger.info("Finished buildNextTraindata()", "DetectionCore:buildNextTraindata") return trainDataArray def executeTrain(self, inputMatArray, lableArray): """ Trainiert alle FullClassifier und deren Classifier mit den gegebenen Trainingsdaten :param Bildmatrizen: inputMatArray Die Bildmatrizen für das Training. :param Lable: lableArray Die Lable zu den Bildmatrizen für das Training. :return: successful : boolean War das Training erfolgreich? """ self.__logger.info("Starting executeTrain()", "DetectionCore:executeTrain") self.__fullClassifier.train(inputMatArray, lableArray) self.__logger.info("Finished executeTrain()", "DetectionCore:executeTrain") return True
class Preprocessing(IPreprocessing): def __init__(self, config): """ Constructor, initialisiert Membervariablen Parameters ---------- config : Config Die Konfiguration. Example ------- >>> Preprocessing(config) """ self.__logger = State().getLogger("Preprocessing_Component_Logger") self.__logger.info("Starting __init__()", "Preprocessing:__init__()") self.__config = config # Erstelle eine Liste von Preprozessoreinheiten, # die eine Matrix bearbeiten ordererPreporcessingUnitList = [] noiseFilterConfig = self.__config["GaussianBlurNoiseFilterPreprocessor"] self.__gaussianBlurNoiseFilterPreprocessor = GaussianBlurNoiseFilterPreprocessor \ (ksize=(noiseFilterConfig["ksizeWidth"], noiseFilterConfig["ksizeHeight"]), sigmaX=noiseFilterConfig["sigmaX"], showImagesInWindow=noiseFilterConfig["showImagesInWindow"]) ordererPreporcessingUnitList.append(self.__gaussianBlurNoiseFilterPreprocessor) binarizationConfig1 = self.__config["AdaptiveThresholdBinarizationPreprocessor_FirstPreprocessor"] self.__adaptiveThresholdBinarizationPreProcessor1 = AdaptiveThresholdBinarizationPreprocessor \ (maxValue=binarizationConfig1["maxValue"], adaptiveMethode=getattr(cv2, binarizationConfig1["adaptiveMethode"]), thresholdType=getattr(cv2, binarizationConfig1["thresholdType"]), blockSize=binarizationConfig1["blockSize"], C=binarizationConfig1["C"], showImagesInWindow=binarizationConfig1["showImagesInWindow"]) ordererPreporcessingUnitList.append(self.__adaptiveThresholdBinarizationPreProcessor1) # Erstelle einen Preprozessor, der die unterschiedelichen Preprozessoreinheiten # der ersten Liste nacheineander auf eine Matrix ausfuehrt self.__preProcessor1 = Preprocessor(ordererPreporcessingUnitList) # Erstelle eine weitere Liste mit anderen Preprozessoreinheiten, # die eine Matrix bearbeiten ordererPreporcessingUnitList2 = [] bitwiseNotConfig = self.__config["BitwiseNotPreprocessor"] self.__bitwiseNotPreprocessor = BitwiseNotPreprocessor \ (showImagesInWindow=bitwiseNotConfig["showImagesInWindow"]) ordererPreporcessingUnitList2.append(self.__bitwiseNotPreprocessor) binarizationConfig2 = self.__config["AdaptiveThresholdBinarizationPreprocessor_SecondPreprocessor"] self.__adaptiveThresholdBinarizationPreProcessor2 = AdaptiveThresholdBinarizationPreprocessor \ (maxValue=binarizationConfig2["maxValue"], adaptiveMethode=getattr(cv2, binarizationConfig2["adaptiveMethode"]), thresholdType=getattr(cv2, binarizationConfig2["thresholdType"]), blockSize=binarizationConfig2["blockSize"], C=binarizationConfig2["C"], showImagesInWindow=binarizationConfig2["showImagesInWindow"]) ordererPreporcessingUnitList2.append(self.__adaptiveThresholdBinarizationPreProcessor2) # Erstelle einen Preprozessor, der die unterschiedelichen Preprozessoreinheiten # der zweiten Liste nacheineander auf eine Matrix ausfuehrt self.__preProcessor2 = Preprocessor(ordererPreporcessingUnitList2) self.__logger.info("Finished __init__()", "Preprocessing:__init__()") def execute(self, mat): """ Führt Preprocessingschritte auf die BildMatrix aus und gibt die bearbeitet Matrix zurück. Parameters ---------- mat : Die Bildmatrix auf welche Preprocessingschritte angewendet werden sollen. Returns ------- imgMatrix : mat Die "preprocesste" Matrix. Example ------- >>> preprocessing.execute() """ self.__logger.info("Starting execute", "Preprocessing:execute") # Ausfuehren der Preprozessoren preprocessedMat1 = self.__preProcessor1.preProcess(mat) preprocessedMat2 = self.__preProcessor2.preProcess(mat) # Erstelle einen Calculator, der einen Skalierungsfaktor # fuer die Notenblattskalierung berechnet scaleCalculatorConfig = self.__config["NoteSheetScaleConstCalculator"] self.__noteSheetScaleConstCalculator = NoteSheetScaleConstCalculator \ (maxGradeOfLinesInPx=scaleCalculatorConfig["maxGradeOfLinesInPx"], marginTop=scaleCalculatorConfig["marginTop"], marginBottom=scaleCalculatorConfig["marginBottom"], cannyThreshold1=scaleCalculatorConfig["cannyThreshold1"], cannyThreshold2=scaleCalculatorConfig["cannyThreshold2"], cannyApertureSize=scaleCalculatorConfig["cannyApertureSize"], houghLinesRho=scaleCalculatorConfig["houghLinesRho"], houghLinesTheta=np.pi / 180 * scaleCalculatorConfig["houghLinesThetaInDegree"], houghLinesThreshold=scaleCalculatorConfig["houghLinesThreshold"], minDistanceToNextNoteRow=scaleCalculatorConfig["minDistanceToNextNoteRow"], minHeightOfNoteRow=scaleCalculatorConfig["minHeightOfNoteRow"], showImagesInWindow=scaleCalculatorConfig["showImagesInWindow"]) # Ausfuehren der Konstantenberechnung auf eine Matrix (Notenblatt) averageHeight = self.__noteSheetScaleConstCalculator.preProcess(preprocessedMat1) # Initialisierung eines Preprozessors mit der aktuellen Durchschnitthoehe der Zeilen und der Zielhoehe sheetScalerConfig = self.__config["ScaleNoteSheetPreprocessor"] self.__scaleNoteSheetPreprocessor = ScaleNoteSheetPreprocessor \ (averageHeight=averageHeight, targetLineHeight=sheetScalerConfig["targetLineHeight"], interpolation=getattr(cv2, sheetScalerConfig["interpolation"])) # Skalieren der im Preprozessor bearbeiteten Matrizen preprocessedMat1 = self.__scaleNoteSheetPreprocessor.preProcess(preprocessedMat1) preprocessedMat2 = self.__scaleNoteSheetPreprocessor.preProcess(preprocessedMat2) preprocessedMat3 = self.__scaleNoteSheetPreprocessor.preProcess(mat) self.__logger.info("Finished execute", "Preprocessing:execute") return [preprocessedMat1, preprocessedMat2, preprocessedMat3]
class KeyTemplateClassifier(ATempalteMatcher): """ Klasse zur Klassifizierung von Tonhöhen in einem Takt. Erkennt b un #. """ def __init__(self, config): """ Konstruktor der KeyTemplateClassifier Klasse. Initialisiert alle Membervariablen. :param config: Die Config für diesen TemplateMatcher """ self.__logger = State().getLogger("DetectionCore_Component_Logger") self.__templateFolder = config["templateFolder"] self.__templateMethod = config["templateMethod"] self.__bTemplate = self.__loadTemplate(-1) self.__bRatio = 29 / 97 self.__bThreshold = 0.61 # Max: 0.65 Min: 0.57 Mid: 0.61 self.__CrossTemplate = self.__loadTemplate(1) self.__CrossRatio = 36 / 97 self.__CrossThreshold = 0.635 # Max: 0.68 Min: 0.59 Mid: 0.635 self.__logger.info( "Loaded Key " + str(self.__bTemplate[1]) + " Template", "KeyTemplateClassifier::__init__", self.__bTemplate[0]) self.__logger.info( "Loaded Key " + str(self.__CrossTemplate[1]) + " Template", "KeyTemplateClassifier::__init__", self.__CrossTemplate[0]) def classify(self, clasUnit, matArray): """ Interface Methode: Jeder TempalteMatcher muss diese realisieren. Führt die Klassifizierung auf ein Array von Bildmatrizen anhand TemplateMatching aus und gibt die erkannten Objekte zurück. Parameters ---------- classUnit : Die Klassifikation als Out-Parameter matArray : Die Bildmatrizen auf welche die Klassifizierung ausgeführt werden soll. Returns ------- [True] wenn Notenschlüssel erkannt wurde, [False] falls nicht """ # b Vorzeichen match, count = self.__hasKey(matArray, self.__bTemplate[0], self.__bRatio, self.__bThreshold) if match > clasUnit.recognitionPercentage: clasUnit.recognitionPercentage = match clasUnit.fifths = self.__bTemplate[1] * count # Kreuz Vorzeichen match, count = self.__hasKey(matArray, self.__CrossTemplate[0], self.__CrossRatio, self.__CrossThreshold) if match > clasUnit.recognitionPercentage: clasUnit.recognitionPercentage = match clasUnit.fifths = self.__CrossTemplate[1] * count if clasUnit.recognitionPercentage == 0: return False self.__logger.info( "Key found " + str(clasUnit.fifths) + " " + str(clasUnit.recognitionPercentage), "KeyTemplateClassifier::classify", matArray[0]) return True def __hasKey(self, mats, template, templateMatRatio, threshold): """ Führt den eigentlichen Templatematch aus. :param mats: Takt als Bildmatrix ([0] ohne Linien, [1] mit Linien, [2] nur skaliert). :param template: Template, welches zum Templatematching verwendet wird. :param templateMatRatio: Höhenverhältnis von Template zum Bildausschnitt. :param threshold: Grenzwert, welcher erreicht werden muss, damit das Template als gefunden gilt. :return: [True] wenn Threshold erreicht, [False] wenn nicht. """ # Template auf Bildgröße skalieren resizedTemplate = ATempalteMatcher._resizeTemplate( self, mats[0], template, templateMatRatio) if (mats[0].shape[1] >= resizedTemplate.shape[1]): # Templatematching mit Bild OHNE Linen matchNoLine = cv2.matchTemplate(mats[0], resizedTemplate, self.__templateMethod) # Templatematching mit Bild MIT Linen matchLine = cv2.matchTemplate(mats[1], resizedTemplate, self.__templateMethod) # Templatematching mit nur skaliertem Bild matchSkal = cv2.matchTemplate(mats[2], resizedTemplate, self.__templateMethod) # Verwende das Maximum von beiden Matches match = cv2.max(matchLine, matchNoLine, matchSkal) # Erzeuge ein binarisiertes Bild anhand eines Thresholds __, bin = cv2.threshold(match, threshold, 255, cv2.THRESH_BINARY) # Zähle die Konturen im Binarisierten Bild im2, contours, hierarchy = cv2.findContours( np.uint8(bin), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Wenn Threshold mindestens einmal erreicht wurde if (bin > 0).any(): return match.max(), len(contours) return 0, 0 def __loadTemplate(self, fiths): return [ cv2.imread(self.__templateFolder + "key_" + str(fiths) + ".jpg", 0), fiths ]