IT-Talents: Hallo Jan, herzlichen Glückwunsch zu Deinem ersten Platz bei der Code Competition „Mustererkennung“! Erzähl den anderen IT-Talenten doch kurz etwas über Dich.
Jan: Ich bin 19 Jahre alt und jetzt in den letzten Wochen meiner Schulzeit, da ich dieses Jahr das Abitur abschließe. Ich habe vor ca. 6 Jahren mit dem Programmieren angefangen und bin immer auf der Suche nach spannenden Aufgaben und Herausforderungen.
IT-Talents: Was hat Dich motiviert, an der Competition teilzunehmen und wie bist Du auf den Wettbewerb aufmerksam geworden?
Jan: Ich habe vor einem halben Jahr von einem Schulkamerad von der it-talents Seite erzählt bekommen. Seitdem schaue ich immer mal wieder vorbei und gucke was es so Neues gibt, wobei mich diese Code Competition besonders angesprochen hat. Daraufhin habe ich relativ schnell mit dem Programmieren angefangen und mich immer weiter damit beschäftigt.
IT-Talents: Wie bist Du an die Lösung der Aufgabenstellung herangegangen? Hattest Du schon Erfahrung mit Algorithmen zur Mustererkennung?
Jan: Erfahrung mit dem Thema hatte ich noch nicht wirklich. Es gab dann zunächst einen “Crashkurs” in Bildverarbeitung mit YouTube-Videos und verschiedenen Internetseiten um mich mit gängigen Methoden und Algorithmen vertraut zu machen, die mir möglicherweise bei dem Problem helfen könnten. Dabei bin ich dann auch relativ schnell auf die Bildverarbeitungsbibliothek OpenCV gekommen, die fast alle nötigen Algorithmen zur Verfügung stellt und sich super zur Anschauung anbietet.
IT-Talents: Welche Probleme sind bei der Entwicklung der Software aufgekommen? Wie lange hat die Entwicklung gedauert?
Jan: Vor allem die Einarbeitung in das Thema und ein grobes Verständnis der Methoden zum Lösen waren sehr demotivierend, da diese oft deutlich tieferes mathematisches Verständnis fordern und ziemlich komplex sind. Außerdem waren Tests während dem Programmieren immer wieder fehlerhaft und haben Zweifel am Funktionieren der Software verursacht.
IT-Talents: Und was hast Du durch die Entwicklung gelernt?
Jan: Während der Entwicklung war vor allem die Dokumentation des Projekt für mich sehr wichtig, da ich diese meist vernachlässige. Dabei habe ich unter anderem Doxygen und Markdown als gängige Mittel zur Dokumentation kennengelernt. Außerdem waren natürlich die vielen Bildverarbeitungsalgorithmen sehr spannend und haben mein Wissen in diese Richtung deutlich erweitert.
IT-Talents: Zu guter Letzt: Was würdest Du Dir thematisch gerne einmal als Code Competition wünschen?
Jan: Ich glaube einen konkreten Wunsch habe ich nicht. Ich lasse mich dann lieber überraschen und schaue mal was noch so kommt.
IT-Talents: Vielen Dank für Deine Teilnahme, das Interview und viel Spaß mit Deinem Gewinn 😉
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); init(); initGui(); setConnections(); registerTemplateMatcher(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::init() { // Instanzen erzeugen, auf die die Pointer verweisen sollen // Memorymanagment wird durch parent (this) übernommen -> Deallokieren nicht nötig _matching_progressbar = new QProgressBar(this); _image_manager = new ImageIOManager(this); _template_matching_processor = new TemplateMatchingProcessor(this); // Pointer auf ImageManager auch an TemplateMatchingProcessor übergeben _template_matching_processor->setImageManager(_image_manager); } void MainWindow::initGui() { // Ladebalken in Statusbar hinzufügen (nicht möglichen im Designer) ui->statusBar->addPermanentWidget(_matching_progressbar,1); // ladebalken erstmal deaktiveren ui->statusBar->setVisible(false); // Vorschau zurücksetzten (->Scrollbar deaktiveren) updateUnfilteredImages(); // Vorschau zurücksetzten (->Scrollbar deaktiveren) updateFilteredImages(); } void MainWindow::setConnections() { // Alle Verbindungen der Objecte und Widgets untereinander herstellen QObject::connect(ui->filter_widget, &FilterWidget::templateMatcherChanged, _template_matching_processor, &TemplateMatchingProcessor::setTemplateMatcher); QObject::connect(ui->filter_widget, &FilterWidget::imagesShouldCotainTemplate, _template_matching_processor, &TemplateMatchingProcessor::imagesShouldContainTemplate); QObject::connect(ui->apply_filter_button, &QPushButton::clicked, this, &MainWindow::startMatching); QObject::connect(_template_matching_processor, &TemplateMatchingProcessor::matchingStatusChanged, this, &MainWindow::setMatchingStatus); QObject::connect(_template_matching_processor, &TemplateMatchingProcessor::matchingFinished, this, &MainWindow::matchingFinished); QObject::connect(ui->import_images_button, &QPushButton::clicked, this, &MainWindow::importImagesDialog); QObject::connect(ui->clear_all_button, &QPushButton::clicked, this, &MainWindow::clearImages); QObject::connect(ui->export_images_button, &QPushButton::clicked, this, &MainWindow::exportImagesDialog); QObject::connect(ui->unfiltered_scrollbar, &QScrollBar::valueChanged, this, &MainWindow::unfilteredImagesScroll); QObject::connect(ui->filtered_scrollbar, &QScrollBar::valueChanged, this, &MainWindow::filterImageScroll); QObject::connect(ui->stop_filtering_button, &QPushButton::clicked, _template_matching_processor, &TemplateMatchingProcessor::stopMatching); } void MainWindow::registerTemplateMatcher() { // Es wird ein neues Filter Objekt instanziert und der Pointer an das FilterWidget übergeben ui->filter_widget->registerTemplateMatcher(new CorrelationTemplateMatcher(this)); ui->filter_widget->registerTemplateMatcher(new FeatureTemplateMatcher(this)); } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { // Drop akzeptieren event->acceptProposedAction(); } void MainWindow::dropEvent(QDropEvent *event) { // Daten des DropEvents holen const QMimeData* mime_data = event->mimeData(); // nur wenn DropEvent Urls enthält diese weiter überprüfen // (Dateien, werden nur als Urls bei DropEvents übergeben) if (mime_data->hasUrls()) { // Überprüfen, in welches Widget die Dateien gedropped wurden if (ui->unfiltered_gbox->rect().contains( ui->unfiltered_gbox->mapFromParent(event->pos()) )) { // DropEvent annehmen event->acceptProposedAction(); // Urls holen QList urls = mime_data->urls(); QStringList filenames; // aus Url konkreten Dateipfad (als String) machen for (QUrl url : urls) { if (url.isLocalFile()) { filenames += url.toLocalFile(); } } // ImageManager Dateipfade zum Importieren übergeben _image_manager->importImages(filenames,ui->import_recursive_checkbox->isChecked()); // Vorschau im Programm aktualisieren updateUnfilteredImages(); } else if (ui->filtered_gbox->rect().contains( ui->filtered_gbox->mapFromParent(event->pos()) )) { // Daten holen QList urls = mime_data->urls(); // Überprüfen ob nur eine Url übergebn wurde if (urls.size()==1) { // weitere Infos zur übergebenen Url QFileInfo info(urls.first().toLocalFile()); // Überprüfen ob Url auf Ordner verweist if (info.isDir()) { event->acceptProposedAction(); // gefilterte Bilder in diesen Ordner exportieren _image_manager->exportImages(info.absoluteFilePath()); } } } } } void MainWindow::importImagesDialog() { // Urls per Dateidialog holen QList urls = QFileDialog::getOpenFileUrls(this,"Bild laden"); // Überprüfen ob min. 1 Datei ausgwählt wurde if (urls.size()) { QStringList filenames; // Urls zu Dateipfaden (Strings) konvertieren for (QUrl url : urls) { if (url.isLocalFile()) { filenames += url.toLocalFile(); } } // ImageManager Dateipfade zum Importieren übergeben _image_manager->importImages(filenames,ui->import_recursive_checkbox->isChecked()); // Vorschau aktualiesieren updateUnfilteredImages(); } } void MainWindow::clearImages() { // Bilder aus ImageManager löschen _image_manager->clearAll(); // Vorschau aktualisieren updateUnfilteredImages(); } void MainWindow::exportImagesDialog() { // Dateidialog zum Auswahlen eines Ordner öffnen QString dirname = QFileDialog::getExistingDirectory(this, "Bilder in Ordner speichern"); // Überprüfen ob Ordner ausgewählt wurde if (dirname.size()) { // ImageManager Ordner zum exportieren übergeben _image_manager->exportImages(dirname); } } void MainWindow::updateUnfilteredImages() { int unfiltered_count = _image_manager->unfilteredImagesCount(); // Wenn mehr als 1 Bild Scrollbar anzeigen ui->unfiltered_scrollbar->setVisible(unfiltered_count>1); // Überprüfen ob min. 1 Bild vorhanden ist if (unfiltered_count) { // Scrollbar-Grenzen anpassen ui->unfiltered_scrollbar->setMinimum(0); ui->unfiltered_scrollbar->setMaximum(unfiltered_count-1); // Erstes Bild in Vorschau anzeigen ui->unfiltered_image_label->setImage(_image_manager->unfilteredImage(0)); } else { // Vorschau aufräumen ui->unfiltered_image_label->clear(); } } void MainWindow::updateFilteredImages() { int filtered_count = _image_manager->filteredImagesCount(); // Wenn mehr als 1 Bild Scrollbar anzeigen ui->filtered_scrollbar->setVisible(filtered_count>1); // Überprüfen ob min. 1 Bild vorhanden ist if (filtered_count) { // Scrollbar-Grenzen anpassen ui->filtered_scrollbar->setMinimum(0); ui->filtered_scrollbar->setMaximum(filtered_count-1); // Erstes Bild in Vorschau anzeigen ui->filtered_image_label->setImage(_image_manager->filteredImage(0)); } else { // Vorschau aufräumen ui->filtered_image_label->clear(); } } void MainWindow::unfilteredImagesScroll(int index) { // Vorschaubild anhand der Scrollbar Position (-> index) anpassen ui->unfiltered_image_label->setImage(_image_manager->unfilteredImage(index)); } void MainWindow::filterImageScroll(int index) { // Vorschaubild anhand der Scrollbar Position (-> index) anpassen ui->filtered_image_label->setImage(_image_manager->filteredImage(index)); } void MainWindow::startMatching() { // Alte gefilterten Bilder löschen _image_manager->clearFilteredImages(); // Vorschau löschen updateFilteredImages(); // Ladebalken anzeigen ui->statusBar->setVisible(true); // Matching Vorgang starten _template_matching_processor->startMatching(); } void MainWindow::setMatchingStatus(int status) { // Matching Vorgang mit neuem status aktualisieren _matching_progressbar->setValue(status); } void MainWindow::matchingFinished() { // Ladebalken deaktivieren ui->statusBar->setVisible(false); // Vorschau aktualisieren updateFilteredImages(); }