#include #include #include #include #include #include #include #include #include #include #include "CSVReader.h" #include "tinyxml2.h" #include "annotationxml.h" #include "object.h" #include "newsequenz.h" // Lädt eine Bildsequenz // directory: Der Pfad zum Ordner, der die Bildsequenz enthält // status: Im Verlauf des Ladens wird der Statustext in diesem Label // gesetzt AnnotationLoader::AnnotationLoader( QString file, QLabel *status ) { sequenz = 0; error = "Pfad nicht gefunden."; QDir dir( file + "/Annotations" ); QFileInfoList fileInfos = dir.entryInfoList(); int fileCount = fileInfos.count(); QList< Kamera* > cams; QList< Object > objects; QList< QString > classes; QProgressDialog progress( "Annotation wird geladen...", "", 0, fileCount ); progress.setWindowModality(Qt::WindowModal); progress.setCancelButton( 0 ); for( int i = 2; i < fileCount; i++ ) { // Schleife durch alle xml Dateien mit Annotationen status->setText( "frame " + QString::number( i - 1 ) + "/" + QString::number( fileCount - 2 ) + ", found cameras: " + QString::number( cams.count() ) + ", found packets: " + QString::number( objects.count() ) + " . . ." ); status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() ); status->repaint(); // Aktualisiere Status Text tinyxml2::XMLDocument doc; doc.LoadFile(fileInfos.at( i ).absoluteFilePath().toStdString().c_str() ); tinyxml2::XMLElement *camera = doc.FirstChildElement()->FirstChildElement("camera_id"); QString camName = camera->GetText(); Kamera *c = 0; for( auto cam = cams.begin(); cam != cams.end(); cam++ ) { // Suche in den Kameras, ob die Kamera des aktuellen Bildes bereits existiert if( (*cam)->getName() == camName ) c = *cam; } if( !c ) { // Falls die Kamera noch nicht existiert wird diese erstellt c = new Kamera( camName, file + "/SourceMasks/" + QString::number( cams.size() ) + ".jpg", cams.size() ); cams.append( c ); } Frame *f = new Frame( file + "/SourceImages/" + doc.FirstChildElement()->FirstChildElement("filename")->GetText(), doc.FirstChildElement()->FirstChildElement("timestamp")->GetText(), c->getChildCount(), c, doc.FirstChildElement()->FirstChildElement( "noobject" ) != 0 ); c->addFrame( f ); tinyxml2::XMLElement *object = doc.FirstChildElement()->FirstChildElement("object"); if( object ) { do { // Schleife durch alle annotierten Objekte des Bildes int truncated = 0; QString className = object->FirstChildElement("name")->GetText(); int classId = classes.indexOf( className ); if( classId < 0 ) { classId = classes.size(); classes.append( className ); } object->FirstChildElement("truncated")->QueryIntText(&truncated); QList< QPolygon > polygons; tinyxml2::XMLElement *polygon = object->FirstChildElement( "polygon" ); if( polygon ) { do { // Schleife durch alle Polygone des Objektes QPolygon pol; tinyxml2::XMLElement *point = polygon->FirstChildElement( "point" ); if( point ) { do { // Schleife durch alle Punkte des Polygons int x, y; point->FirstChildElement( "x" )->QueryIntText( &x ); point->FirstChildElement( "y" )->QueryIntText( &y ); pol.push_back( QPoint( x, y ) ); } while( (point = point->NextSiblingElement() ) != NULL ); polygons.append( pol ); } } while( (polygon = polygon->NextSiblingElement() ) != NULL ); } QString pName = object->FirstChildElement("id")->GetText(); if( objects.indexOf( Object( pName, classId ) ) < 0 ) objects.append( Object( pName, classId ) ); f->addObject( pName, truncated != 0, polygons ); } while( ( object = object->NextSiblingElement() ) != NULL ); } f->wasChangedSinceLastSave(); progress.setValue( progress.value() + 1 ); } sequenz = new Sequenz( file, cams, objects ); if( !cams.size() ) { sequenz->refRelease(); sequenz = 0; } foreach( QString className, classes ) { sequenz->addClass( className ); } } AnnotationLoader::~AnnotationLoader() { if( sequenz ) sequenz->refRelease(); } // Gibt die geladene Sequenz zurück // 0 falls beim Laden ein Fehler aufgetreten ist Sequenz *AnnotationLoader::getSequenz() { if( sequenz ) sequenz->refNew(); return sequenz; } // Gibt eine Fehlermeldung zurück, falls beim Laden ein Fehler aufgetreten // ist QString AnnotationLoader::getErrorMessage() { return error; } // Erstellt eine neue Sequenz und Lädt diese // directory: Der Pfad zu dem Ordner, welche die Bilder der neuen Sequenz // enthält // status: Im Verlauf des Erstellens wird der Statustext in diesem Label // gesetzt AnnotationCreator::AnnotationCreator( QString directory, QLabel *status ) { QDir dir( directory ); QFileInfoList fileInfos = dir.entryInfoList(); int fileCount = fileInfos.count(); QString pathToCSV = ""; for( int i = 2; i < fileCount; i++ ) { // Suche nach der CSV Datei im angegebenen Ordner if( fileInfos.at( i ).suffix().toLower() == "csv" ) { CSVReader reader( fileInfos.at( i ).absoluteFilePath().toStdString() ); bool ok = false; while( reader.hasNext() ) { // Suche nach dem Info Bild in der csv Datei std::vector< std::string > row = reader.getNextRow(); if( row.size() < 3 ) { // Prüfe, ob die csv Datei das passende Vormat hat ok = false; break; } if( row.at( 1 ) == "Info Bild" ) ok = true; } if( ok ) pathToCSV = fileInfos.at( i ).absoluteFilePath(); } } QString targetDir; if( pathToCSV == "" ) { // Es wurde keine csv Datei gefunden QMessageBox::StandardButton reply = QMessageBox::question(0, "Achtung", "In dem ausgewählten Ordner wurde keine CSV Datei mit Informationen über die neue Sequenz gefunden. Soll der Ordner manuell nach Bildern durchsucht werden? Die Bilder würden der Sequenz in Alphabetischer Reihenfolge hinzugefügt werden.", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) { do { targetDir = QFileDialog::getExistingDirectory(0, "Wo soll die neue Sequenz gespeichert werden?", "/studi-tmp/kstrohm", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if( !QDir( targetDir ).count() ) QMessageBox::critical(0,"Fehler","Bitte wählen sie einen leeren Ordner aus."); } while( !QDir( targetDir ).count() ); status->setText("Suche Nach Bildern ..."); status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() ); status->repaint(); // Aktualisiere Status Text numFrames = 0; for( int i = 2; i < fileCount; i++ ) { // Zähle alle Bilder im angegebenen Ordner numFrames += getImageCount( fileInfos.at( i ) ); } if( !numFrames ) { QMessageBox::critical(0,"Fehler","Es wurden keine Bilder gefunden. Unterstützte Bildformate sind: jpg, png und bmp."); } else { NewSequenz nseq( numFrames ); nseq.exec(); // Frage den Nutzer, welche Bilder er in die Neue Bildsequenz übernehmen möchte int offset = nseq.getOffset(); // Startindex des ersten Bildes int limit = nseq.getLimit(); // Anzahl der zu übernehmenden Bilder numFrames = limit; if( limit > 0 && offset >= 0 ) { // Falls das erstellen nicht abgebrochen wurde status->setText("Erstelle Annotation ..."); status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() ); status->repaint(); int index = 0; // Erzteuge Ordnerstruktur QDir().mkpath( targetDir + "/Annotations" ); QDir().mkpath( targetDir + "/SourceImages" ); QDir().mkpath( targetDir + "/SourceMasks" ); QDir().mkpath( targetDir + "/JPEGImages" ); int nLen = 0; int count = limit; while( count > 0 ) { // Ermittle die maximale Länge des Bildnamens (Die bilder werden 00..01-99..99 nummerriert) count /= 10; nLen++; } QProgressDialog progress( "Annotation wird erstellt...", "", 0, limit ); progress.setWindowModality(Qt::WindowModal); progress.setCancelButton( 0 ); int kamId = 0; for( int i = 2; i < fileCount; i++ ) { // Erzeuge die neue Bildsequenz createAnnotationFor( fileInfos.at( i ), index, "", targetDir, nLen, status, offset, limit, progress, kamId ); } AnnotationLoader loader( targetDir, status ); sequenz = loader.getSequenz(); // lade die Bildsequenz } } } } else { // Falls eine CSV Datei existiert do { targetDir = QFileDialog::getExistingDirectory(0, "Wo soll die neue Sequenz gespeichert werden?", "/studi-tmp/kstrohm", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if( !QDir( targetDir ).count() ) QMessageBox::critical(0,"Fehler","Bitte wählen sie einen leeren Ordner aus."); } while( !QDir( targetDir ).count() ); // Erzeuge die Ordnerstruktur QDir().mkpath( targetDir + "/Annotations" ); QDir().mkpath( targetDir + "/SourceImages" ); QDir().mkpath( targetDir + "/SourceMasks" ); QDir().mkpath( targetDir + "/JPEGImages" ); CSVReader reader( pathToCSV.toStdString() ); status->setText("Suche Nach Bildern ..."); int numFrames = 0; while( reader.hasNext() ) { // Zahle die Anzahl der Bilder (ohne Info Bild) std::vector< std::string > row = reader.getNextRow(); if(row.size() < 3 || row.at( 1 ) == "Info Bild") continue; numFrames++; } NewSequenz nseq( numFrames ); nseq.exec(); // Frage den Nutzer welche Bilder in die neue Sequenz übernommen werden sollen int offset = nseq.getOffset(); int limit = nseq.getLimit(); int max = limit; if( limit > 0 && offset >= 0 ) { // falls die Erstellung nicht abgebrochen wurde reader = CSVReader( pathToCSV.toStdString() ); int nLen = 0; int count = limit; while( count > 0 ) { // Ermittle die Maximale Länge des Bildnamens count /= 10; nLen++; } int index = 0; QProgressDialog progress( "Annotation wird erstellt...", "", 0, limit ); progress.setWindowModality(Qt::WindowModal); progress.setCancelButton( 0 ); int maskCount = -1; QMap< QString, int > cams; while( reader.hasNext() ) { // Schleife durch jede Zeile der CSV Datei std::vector< std::string > row = reader.getNextRow(); if(row.size() < 3 || row.at( 1 ) == "Info Bild") continue; if( offset > 0 ) { offset--; continue; } if( !limit ) break; else limit--; QString name = QString::number( index++ ); while( name.length() < nLen ) name = "0" + name; QString camName; if( !cams.contains( QString( row.at( 1 ).c_str() ) ) ) { cams.insert( QString( row.at( 1 ).c_str() ), ++maskCount ); camName = QString::number( maskCount ); } else { camName = QString::number( cams.take( QString( row.at( 1 ).c_str() ) ) ); } QString sourceImagePath = targetDir + "/SourceImages/" + name + "." + row.at( 0 ).substr(row.at( 0 ).size() - 3).c_str(); QString jpegImagePath = targetDir + "/JPEGImages/" + name + "." + row.at( 0 ).substr(row.at( 0 ).size() - 3).c_str(); QString sourceMaskPath = targetDir + "/SourceMasks/" + camName + ".jpg"; QString imagePath = dir.absolutePath() + "/" + row.at( 0 ).c_str(); QFile( imagePath ).copy( sourceImagePath ); QFile( imagePath ).copy( jpegImagePath ); if( !QFileInfo(sourceMaskPath).exists() ) { // Erzeuge die Maske der Kamera falls sie noch nicht existiert QImage mask( imagePath ); mask.fill( 0xFFFFFFFF ); mask.save( sourceMaskPath ); } Frame currentFrame( sourceImagePath, QString( row.at( 2 ).c_str() ), 0, 0, 1 ); tinyxml2::XMLDocument doc; // Erstelle xml Datei doc.LinkEndChild( doc.NewDeclaration( "xml version=\"1.0\" " ) ); tinyxml2::XMLElement *annotation = doc.NewElement( "annotation"); annotation->LinkEndChild( doc.NewElement( "folder" ) )->LinkEndChild( doc.NewText( "VOC2007" ) ); annotation->LinkEndChild( doc.NewElement( "filename" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) ); tinyxml2::XMLElement *source = doc.NewElement( "source" ); source->LinkEndChild( doc.NewElement( "database" ) )->LinkEndChild( doc.NewText( "The VOC2007 Database" ) ); source->LinkEndChild( doc.NewElement( "annotation" ) )->LinkEndChild( doc.NewText( "PASCAL VOC 2007" ) ); source->LinkEndChild( doc.NewElement( "image" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) ); annotation->LinkEndChild( source ); annotation->LinkEndChild( doc.NewElement( "owner" ) )->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( "Kolja Strohm" ) ); annotation->LinkEndChild( doc.NewElement( "camera_id" ) )->LinkEndChild( doc.NewText( row.at( 1 ).c_str() ) ); tinyxml2::XMLElement *size = doc.NewElement( "size" ); QImage img = currentFrame.getImage(); size->LinkEndChild( doc.NewElement( "width" ) )->LinkEndChild( doc.NewText( std::to_string( img.width() ).c_str() ) ); size->LinkEndChild( doc.NewElement( "height" ) )->LinkEndChild( doc.NewText( std::to_string( img.height() ).c_str() ) ); size->LinkEndChild( doc.NewElement( "depth" ) )->LinkEndChild( doc.NewText( std::to_string( img.depth() ).c_str() ) ); annotation->LinkEndChild( size ); annotation->LinkEndChild( doc.NewElement( "segmented" ) )->LinkEndChild( doc.NewText( "0" ) ); annotation->LinkEndChild( doc.NewElement( "timestamp" ) )->LinkEndChild( doc.NewText( currentFrame.getTimestamp().toStdString().c_str() ) ); annotation->LinkEndChild( doc.NewElement( "noobject" ) )->LinkEndChild( doc.NewText( "need Annotation" ) ); doc.LinkEndChild( annotation ); name = currentFrame.getName(); doc.SaveFile( (targetDir + "/Annotations/" + name.mid( 0, name.lastIndexOf( '.' ) ) + ".xml").toStdString().c_str() ); status->setText( "create Annotation from csv ... " + QString::number((int)((max - limit) / (float)max * 100)) + "% complete" ); status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() ); status->repaint(); progress.setValue( progress.value() + 1 ); } AnnotationLoader loader( targetDir, status ); sequenz = loader.getSequenz(); } } } // Zählt alle Bilder in einem Ordner // f: die Informationen zu dem Ordner int AnnotationCreator::getImageCount( QFileInfo f ) { if( f.isDir() ) { int count = 0; QFileInfoList fileInfos = QDir( f.absoluteFilePath() ).entryInfoList(); for( QFileInfo f : fileInfos ) { if( f.fileName() == "." || f.fileName() == ".." ) continue; count += getImageCount( f ); } return count; } if( f.suffix().toLower() == "jpg" || f.suffix().toLower() == "png" || f.suffix().toLower() == "bmp" ) return 1; return 0; } // Erstellt Annotationen zu allen Bildern in einem Ordner // file: Die Information zur Datei, zu der Annotationen erstellt werden sollen // index: Der Index des Bildes // kamera: Der Name der aktuellen Kamera // targetDir: Der Pfad zum Ziel Ordner // nLen: Die maximale Länge des Namens // status: Ein Zeiger auf den Status Text // offset: Der Index des sersten Bildes der Sequenz // limit: Die Anzahl der Bilder in der Sequenz // progress: Ein Dialog mit einer Fortschrittsleiste // kamId: Die Id der momentanen Kamera void AnnotationCreator::createAnnotationFor( QFileInfo file, int &index, QString kamera, QString targetDir, int nLen, QLabel *status, int &offset, int &limit, QProgressDialog &progress, int &kamId ) { if( limit == 0 ) return; if( file.isDir() ) { // Falls die Datei ein Ordner ist, erfolgt ein recursiver Aufruf für alle Dateien darin QString kamera = file.fileName(); QDir kamDir( file.absoluteFilePath() ); QFileInfoList kfInfos = kamDir.entryInfoList(); for( QFileInfo file : kfInfos ) { QString newKam = kamera; if( newKam != "" ) newKam += "/"; newKam += file.fileName(); createAnnotationFor( file, index, newKam, targetDir, nLen, status, offset, limit, progress, kamId ); } kamId++; } else { // Falls die Datei kein Ordner ist if( offset > 0 ) { offset--; return; } if( limit > 0 ) limit--; if( file.suffix().toLower() == "jpg" || file.suffix().toLower() == "png" || file.suffix().toLower() == "bmp" ) { // Falls die Datei ein Bild ist, wird eine Annotation für sie erstellt QString name = QString::number( index ); while( name.length() < nLen ) name = "0" + name; if( kamera == "" ) kamera = "Ohne Kamera"; QString sourceImagePath = targetDir + "/SourceImages/" + name + +"." + file.suffix().toLower(); QString jpegImagePath = targetDir + "/JPEGImages/" + name + +"." + file.suffix().toLower(); QString sourceMaskPath = targetDir + "/SourceMasks/" + QString::number( kamId ) + +".jpg"; QFile( file.absoluteFilePath() ).copy( sourceImagePath ); QFile( file.absoluteFilePath() ).copy( jpegImagePath ); if( !QFileInfo(sourceMaskPath).exists() ) { // Falls die Maske der Kamera noch nicht existiert wird sie erstellt QImage mask( file.absoluteFilePath() ); mask.fill( 0xFFFFFFFF ); mask.save( sourceMaskPath ); } Frame currentFrame( sourceImagePath, file.created().toString(Qt::SystemLocaleLongDate), 0, 0, 1 ); tinyxml2::XMLDocument doc; doc.LinkEndChild( doc.NewDeclaration( "xml version=\"1.0\" " ) ); tinyxml2::XMLElement *annotation = doc.NewElement( "annotation"); annotation->LinkEndChild( doc.NewElement( "folder" ) )->LinkEndChild( doc.NewText( "VOC2007" ) ); annotation->LinkEndChild( doc.NewElement( "filename" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) ); tinyxml2::XMLElement *source = doc.NewElement( "source" ); source->LinkEndChild( doc.NewElement( "database" ) )->LinkEndChild( doc.NewText( "The VOC2007 Database" ) ); source->LinkEndChild( doc.NewElement( "annotation" ) )->LinkEndChild( doc.NewText( "PASCAL VOC 2007" ) ); source->LinkEndChild( doc.NewElement( "image" ) )->LinkEndChild( doc.NewText( currentFrame.getName().toStdString().c_str() ) ); annotation->LinkEndChild( source ); annotation->LinkEndChild( doc.NewElement( "owner" ) )->LinkEndChild( doc.NewElement( "name" ) )->LinkEndChild( doc.NewText( "Kolja Strohm" ) ); annotation->LinkEndChild( doc.NewElement( "camera_id" ) )->LinkEndChild( doc.NewText( kamera.toStdString().c_str() ) ); tinyxml2::XMLElement *size = doc.NewElement( "size" ); QImage img = currentFrame.getImage(); size->LinkEndChild( doc.NewElement( "width" ) )->LinkEndChild( doc.NewText( std::to_string( img.width() ).c_str() ) ); size->LinkEndChild( doc.NewElement( "height" ) )->LinkEndChild( doc.NewText( std::to_string( img.height() ).c_str() ) ); size->LinkEndChild( doc.NewElement( "depth" ) )->LinkEndChild( doc.NewText( std::to_string( img.depth() ).c_str() ) ); annotation->LinkEndChild( size ); annotation->LinkEndChild( doc.NewElement( "segmented" ) )->LinkEndChild( doc.NewText( "0" ) ); annotation->LinkEndChild( doc.NewElement( "timestamp" ) )->LinkEndChild( doc.NewText( currentFrame.getTimestamp().toStdString().c_str() ) ); annotation->LinkEndChild( doc.NewElement( "noobject" ) )->LinkEndChild( doc.NewText( "need Annotation" ) ); doc.LinkEndChild( annotation ); doc.SaveFile( (targetDir + "/Annotations/" + name + ".xml").toStdString().c_str() ); status->setText( "create Annotation without csv ... " + QString::number((int)((numFrames - limit) / (float)numFrames * 100)) + "% complete" ); status->setGeometry( status->x(), status->y(), status->fontMetrics().width( status->text() ) + 10, status->height() ); status->repaint(); index++; progress.setValue( progress.value() + 1 ); } } } AnnotationCreator::~AnnotationCreator() { if( sequenz ) sequenz->refRelease(); } // Gibt die geladene neu erstellte Sequenz zurück // 0 falls beim Erstellen oder beim Laden ein Fehler aufgetreten ist Sequenz *AnnotationCreator::getSequenz() { if( sequenz ) sequenz->refNew(); return sequenz; }