#include "frame.h" #include "kamera.h" #include #include #include // Erstellt das Bild // imgPath: Der Pfad zu dem Quellbild // timestamp: Der Zeitpunkt der Aufnahme des Bildes // index: Der Index des Bildes in der Liste aller Bilder der Kamera // kam: Die Kamera, die das Bild aufgenommen hat // needAnnotation: true, falls das Bild noch nicht annotiert wurde Frame::Frame( QString imgPath, QString timestamp, int index, Kamera *kam, bool needAnnotation ) : FrameTreeNode( index, kam, 1 ), path( imgPath ), timestamp( timestamp ), needAnnotation( needAnnotation ), size( QSize( 0, 0 ) ), needSave( 0 ) {} Frame::~Frame() {} // Legt fest, dass das Bild seit dem letzten speichern verändert wurde void Frame::setNeedSave() { needSave = 1; } // Gibt this zurück void *Frame::getNodeObject() const { return (void*)this; } // Gibt die Anzahl aller Objekte auf dem Bild zurück int Frame::getChildCount() const { return objects.size(); } // Entfernt alle Eckpunkte in einem Gebit // area: Das Gebiet, in dem die Eckpunkte gelöscht werden sollen void Frame::removeSelectedVertices( QRect area ) { needSave = 1; int anz = objects.size(); for( int i = 0; i < anz; i++ ) { ObjectPolygon o = objects.at( i ); if( o->isSelected() ) { if( !o->removeVertices( area ) ) { objects.removeAt( i ); i--; anz--; } } } } // Löscht ein bestimmtes Objekt // o: Das Objekt, welches gelöscht werden soll void Frame::removeObject( ObjectPolygon o ) { needSave = 1; objects.removeOne( o ); } // Fügt ein Objekt zu dem Bild hinzu. Das Objekt wird automatisch am // Bildrand abgeschnitten // name: Die ID des Objektes // truncated: true, falls das Objekt abgeschnitten ist // po: Eine Liste mit Polygonen, welche die Umrisse des Objektes sind void Frame::addObject( QString id, bool truncated, QList< QPolygon > po ) { needSave = 1; if( po.size() < 1 ) return; if( size != QSize( 0, 0 ) ) { QPolygon cliped = clipPolygon( po.at( 0 ) ); ObjectPolygon newObject( id, cliped, truncated || cliped.boundingRect() != po.at( 0 ).boundingRect(), objects.size(), this ); int pAnz = po.count(); for( int i = 1; i < pAnz; i++ ) { cliped = clipPolygon( po.at( i ) ); if( cliped.boundingRect() != po.at( i ).boundingRect() ) newObject->setTruncated( true ); newObject->getPolygonList().append( cliped ); } objects.append( newObject ); } else { ObjectPolygon newObject( id, po.at( 0 ), truncated, objects.size(), this ); int pAnz = po.count(); for( int i = 1; i < pAnz; i++ ) newObject->getPolygonList().append( po.at( i ) ); objects.append( newObject ); } } // Wendet die Maske auf das Bild an und löscht alle Objekte, welche sich in // verbotenen Bereichen befinden // m: die Maske void Frame::applyMask( Mask &m ) { needSave = 1; int count = objects.count(); for( int i = 0; i < count; i++ ) { int pAnz = objects.at( i )->getPolygonList().count(); for( int j = 0; j < pAnz; j++ ) { if( !m.isPolygonInside( objects.at( i )->getPolygonList().at( j ) ) ) { pAnz--; objects.at( i )->getPolygonList().removeAt( j-- ); if( pAnz <= 0 ) { count--; objects.removeAt( i-- ); } } } } } // Spaltet ein Objekt in zwei Teile // object: Das Objekt welches zerteilt werden soll // beginn: Der Index des Start Eckpunktes // end: Der Index des End Eckpunktes // pIndex: Der Index des Polygons, welches zerteilt werden soll void Frame::splitObject( ObjectPolygon object, int begin, int end, int pIndex ) { needSave = 1; QPolygon newP = object->split( begin, end, pIndex ); QList< QPolygon > newPols; newPols.append( newP ); addObject( object->getId(), object->isTruncated(), newPols ); } // gibt den Namen des Bildes zurück QString Frame::getName() const { return path.mid( path.lastIndexOf( '/' ) + 1 ); } // Gibt den Aufnahmezeitpunkt des Bildes zurück QString Frame::getTimestamp() const { return timestamp; } // Gibt eine Liste mit Objekten zurück, welche sich auf dem Bild befinden QList< ObjectPolygon > &Frame::getObjects() { return objects; } // Setzt die Objekte, welche sich auf dem Bild befinden. Alle vorherigen // Objekte werden entfernt. // objects: die Liste mit den neuen Objekten void Frame::setObjects( std::vector< std::vector< cv::Point > > obs ) { needSave = 1; needAnnotation = false; objects.clear(); for( auto p = obs.begin(); p != obs.end(); p++ ) { std::vector< cv::Point > pol; cv::approxPolyDP( *p, pol, 15, 1 ); if( pol.size() > 2 ) { QPolygon polygon; int count = 0; for( auto point = pol.begin(); point != pol.end(); point++, count++ ) { polygon.append( QPoint( point->x, point->y ) ); } QList< QPolygon > newPols; newPols.append( polygon ); addObject( "-1", 0, newPols ); } } } // Gibt das geladene Bild als Objekt des Qt Frameworks zurück QImage Frame::getImage() { QImage img( path ); size = img.size(); return img; } // Gibt das geladene Bild als Objekt der OpenCV Bibliotek zurück cv::Mat Frame::getImageMatrix() const { return cv::imread( path.toStdString() ); } // Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist // objectId: die ID des Objektes, welches auf dem Bild sein soll QImage Frame::getObjectImage( QString objectId ) { for( ObjectPolygon o : objects ) { if( o->getId() == objectId ) { QImage img = getImage(); QRect bndBox = o->getBoundingBox(); bndBox.setLeft( MAX( bndBox.left() - 200, 0 ) ); bndBox.setTop( MAX( bndBox.top() - 200, 0 ) ); bndBox.setRight( MIN( bndBox.right() + 200, img.width() ) ); bndBox.setBottom( MIN( bndBox.bottom() + 200, img.height() ) ); QImage ret = img.copy( bndBox ); return ret; } } return QImage(); } // Gibt den Bildausschnitt zurück, auf dem ein Objekt komplett sichtbar ist // object: das Objekt, welches auf dem Bild sein soll QImage Frame::getObjectImage( ObjectPolygon o ) { QImage img = getImage(); QRect bndBox = o->getBoundingBox(); bndBox.setLeft( MAX( bndBox.left() - 200, 0 ) ); bndBox.setTop( MAX( bndBox.top() - 200, 0 ) ); bndBox.setRight( MIN( bndBox.right() + 200, img.width() ) ); bndBox.setBottom( MIN( bndBox.bottom() + 200, img.height() ) ); QImage ret = img.copy( bndBox ); return ret; } // Prüft ob ein Objekt auf dem Bild vorhanden ist // id: Die ID des Objektes bool Frame::hasObject( QString id ) { for( ObjectPolygon o : objects ) { if( o->getId() == id ) return true; } return false; } // Gibt true zurück, falls kein Fehler in der Annotation des Bildes gefunden // wurde bool Frame::isCorrectAnnotated() const { bool good = true; for( ObjectPolygon o1 : objects ) { for( ObjectPolygon o2 : objects) { if( o1 == o2 ) continue; good &= o2->getId() != o1->getId(); } good &= o1->getId() != "-1"; } return good; } // Gibt true zurück, falls das Bild noch nicht annotiert wurde bool Frame::isNotAnnotated() const { return needAnnotation; } // Gibt das Objekt zurück, welches an einer bestimmten Position im Bild ist. // 0 falls an der Stelle kein Objekt ist // pos: die Position, an der ein Objekt gesucht werden soll // pIndex: wird auf den Index des Polygons gesetzt, welches an der Stelle // ist, falls dort ein Objekt existiert ObjectPolygon Frame::getObjectAt( QPoint pos, int &pIndex ) const { for( ObjectPolygon o : objects ) { pIndex = 0; for( QPolygon pol : o->getPolygonList() ) { if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) ) return o; pIndex++; } } return ObjectPolygon(); } // Setzt das Objekt an einer bestimmten Position im Bild. // Fals an der Position bereits ein Objekt existiert, so wird nur die Objekt // ID gesetzt. // pos: die Position // object: Das neue Objekt // center: Der Mittelpunkt des neuen Objektes // rotation: Die Drehung des neuen Objektes um den Mittelpunkt void Frame::setObjectAt( QPoint pos, ObjectPolygon copy, QPoint center, float rotation ) { needSave = 1; for( ObjectPolygon o : objects ) { for( QPolygon pol : o->getPolygonList() ) { if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) ) { bool needQ = false; if( copy->getId() != "-1" ) { for( ObjectPolygon o2 : objects ) { if( o2 != o && o2->getId() == copy->getId() ) needQ = 1; } } o->setId( copy->getId() ); if( needQ ) { QMessageBox::StandardButton reply = QMessageBox::question(0, "Warnung", "Auf diesem Bild gibt es bereits ein Objekt mit der gleichen Id. Möchten sie beide Objekte vereinen?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) connectObjects( copy->getId() ); } return; } } } QMatrix m; m.QMatrix::translate( pos.x(), pos.y() ); m.rotate( rotation ); m.translate( -center.x(), -center.y() ); QList< QPolygon > newPols; for( QPolygon p : copy->getPolygonList() ) newPols.append( m.map( p ) ); bool needQ = false; if( copy->getId() != "-1" ) { for( ObjectPolygon o2 : objects ) { if( o2->getId() == copy->getId() ) needQ = 1; } } addObject( copy->getId(), copy->isTruncated(), newPols ); if( needQ ) { QMessageBox::StandardButton reply = QMessageBox::question(0, "Warnung", "Auf diesem Bild gibt es bereits ein Objekt mit der gleichen Id. Möchten sie beide Objekte vereinen?", QMessageBox::Yes|QMessageBox::No); if (reply == QMessageBox::Yes) connectObjects( copy->getId() ); } } // Macht ein Objekt an einer bestimmten Stelle sichtbar oder unsichtbar // pos: Die Position void Frame::selectObjectAt( QPoint pos ) { for( ObjectPolygon o : objects ) { for( QPolygon pol : o->getPolygonList() ) { if( pol.containsPoint( pos, Qt::FillRule::OddEvenFill ) ) o->setSelected( !o->isSelected() ); } } } // Schneidet ein Polygon am rand des Bildes ab und gibt das Ergebnis zurück // uncliped: Das nicht abgeschnittene Polygon QPolygon Frame::clipPolygon( QPolygon uncliped ) const { QPolygon cliped = uncliped.intersected( QPolygon( QRect( QPoint( 0, 0 ), size ), true ) ); if( !cliped.isEmpty() ) cliped.pop_back(); return cliped; } // Vereiniegt verschiedene Objekte mit der gleichen Objekt ID zu einem // id: Die ID, deren Objekte vereiniegt werden sollen void Frame::connectObjects( QString id ) { needSave = 1; ObjectPolygon o; int oAnz = objects.count(); for( int i = 0; i < oAnz; i++ ) { ObjectPolygon o2 = objects.at( i ); if( o2 != o && o2->getId() == id ) { if( o.isNull() ) o = o2; else { o->getPolygonList().append( o2->getPolygonList() ); o->setTruncated( o->isTruncated() || o2->isTruncated() ); o->setSelected( o->isSelected() || o2->isSelected() ); objects.removeAt( i ); i--; oAnz--; } } } } // Zerteilt ein Objekt, welches aus mehreren Polygonen besteht // o: Das Objekt welches zerteilt werden soll void Frame::disconnectObject( ObjectPolygon o ) { needSave = 1; for( QPolygon p : o->getPolygonList() ) { QList< QPolygon > plist; plist.append( p ); addObject( o->getId(), o->isTruncated(), plist ); } removeObject( o ); } // Gibt 1 zurück, falls die Annotation zu dem Bild gespeichert werden muss // und setzt den Flag auf 0, so dass erst nach einer änderung wieder 1 zurückgegeben wird bool Frame::wasChangedSinceLastSave() { bool ret = needSave; needSave = 0; return ret; }