From 2c3fdf0f79363b9754f9ef62376f99d8ae38dfc6 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 11 Apr 2017 08:55:48 -0400 Subject: [PATCH] Initial commit --- .gitignore | 35 ++ CMakeLists.txt | 59 +++ Info.plist | 34 ++ about.cpp | 32 ++ about.h | 23 + about.ui | 193 +++++++++ debian/changelog | 5 + debian/compat | 1 + debian/control | 18 + debian/copyright | 35 ++ debian/extract-icons | 30 ++ debian/rules | 26 ++ debian/source/format | 1 + directoryloader.cpp | 63 +++ directoryloader.h | 51 +++ directoryloader.ui | 80 ++++ dot.cpp | 84 ++++ dot.h | 83 ++++ imagefile.cpp | 51 +++ imagefile.h | 91 ++++ images/plates.icns | Bin 0 -> 184756 bytes images/plates.ico | Bin 0 -> 370070 bytes imageview.cpp | 86 ++++ imageview.h | 60 +++ imageview.ui | 109 +++++ imageviewerbase.cpp | 88 ++++ imageviewerbase.h | 102 +++++ imageviewerobserver.cpp | 262 +++++++++++ imageviewerobserver.h | 62 +++ imageviewerplateselector.cpp | 212 +++++++++ imageviewerplateselector.h | 73 ++++ main.cpp | 117 +++++ mainwindow.cpp | 362 ++++++++++++++++ mainwindow.h | 73 ++++ mainwindow.ui | 501 ++++++++++++++++++++++ man/plates.1 | 21 + options.cpp | 29 ++ options.h | 26 ++ options.ui | 101 +++++ platefile.cpp | 204 +++++++++ platefile.h | 164 +++++++ plates.desktop | 9 + plates.iss | 105 +++++ plates.pro | 93 ++++ plates.qrc | 5 + plates.rc | 42 ++ plateselector.cpp | 171 ++++++++ plateselector.h | 75 ++++ plateselector.ui | 213 +++++++++ polygons.cpp | 48 +++ polygons.h | 57 +++ qprogressindicator/QProgressIndicator.cpp | 134 ++++++ qprogressindicator/QProgressIndicator.h | 88 ++++ selection.cpp | 56 +++ selection.h | 92 ++++ settings.cpp | 111 +++++ settings.h | 119 +++++ squeezedlabel.cpp | 40 ++ squeezedlabel.h | 33 ++ threadedimageloader.cpp | 20 + threadedimageloader.h | 32 ++ utils.cpp | 108 +++++ utils.h | 59 +++ 63 files changed, 5357 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Info.plist create mode 100644 about.cpp create mode 100644 about.h create mode 100644 about.ui create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/extract-icons create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 directoryloader.cpp create mode 100644 directoryloader.h create mode 100644 directoryloader.ui create mode 100644 dot.cpp create mode 100644 dot.h create mode 100644 imagefile.cpp create mode 100644 imagefile.h create mode 100644 images/plates.icns create mode 100644 images/plates.ico create mode 100644 imageview.cpp create mode 100644 imageview.h create mode 100644 imageview.ui create mode 100644 imageviewerbase.cpp create mode 100644 imageviewerbase.h create mode 100644 imageviewerobserver.cpp create mode 100644 imageviewerobserver.h create mode 100644 imageviewerplateselector.cpp create mode 100644 imageviewerplateselector.h create mode 100644 main.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 mainwindow.ui create mode 100644 man/plates.1 create mode 100644 options.cpp create mode 100644 options.h create mode 100644 options.ui create mode 100644 platefile.cpp create mode 100644 platefile.h create mode 100644 plates.desktop create mode 100644 plates.iss create mode 100644 plates.pro create mode 100644 plates.qrc create mode 100644 plates.rc create mode 100644 plateselector.cpp create mode 100644 plateselector.h create mode 100644 plateselector.ui create mode 100644 polygons.cpp create mode 100644 polygons.h create mode 100644 qprogressindicator/QProgressIndicator.cpp create mode 100644 qprogressindicator/QProgressIndicator.h create mode 100644 selection.cpp create mode 100644 selection.h create mode 100644 settings.cpp create mode 100644 settings.h create mode 100644 squeezedlabel.cpp create mode 100644 squeezedlabel.h create mode 100644 threadedimageloader.cpp create mode 100644 threadedimageloader.h create mode 100644 utils.cpp create mode 100644 utils.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13ad5df --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +plates.exe +*.app +obj +*.pro.user* +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +/.qmake.cache +/.qmake.stash +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3208e8b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 2.8.11) + +project(openalpr_tagger) + +SET(VERSION_MAJOR 1) +SET(VERSION_MINOR 0) +SET(VERSION_PATCH 0) + +SET (VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +add_definitions(-DVERSION_MAJOR=${VERSION_MAJOR}) +add_definitions(-DVERSION_MINOR=${VERSION_MINOR}) +add_definitions(-DVERSION_PATCH=${VERSION_PATCH}) +add_definitions(-DVERSION_STRING="${VERSION}") + + +add_definitions(-DTARGET="openalpr_tagger") +add_definitions(-DTARGET_STRING="openalpr_tagger") +add_definitions(-DTARGET_UPPER_STRING="OPENALPR_TAGGER") +add_definitions(-DTARGET_HUMAN_STRING="openalpr_tagger") + +ADD_DEFINITIONS( + -std=c++11 +) + + +# Find the QtWidgets library +find_package(Qt5Widgets) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Tell CMake to create the helloworld executable +add_executable(openalpr_tagger +about.cpp +directoryloader.cpp +dot.cpp +imagefile.cpp +imageview.cpp +imageviewerbase.cpp +imageviewerobserver.cpp +imageviewerplateselector.cpp +main.cpp +mainwindow.cpp +options.cpp +platefile.cpp +plateselector.cpp +polygons.cpp +selection.cpp +settings.cpp +squeezedlabel.cpp +threadedimageloader.cpp +utils.cpp +qprogressindicator/QProgressIndicator.cpp +) + +include_directories(${CMAKE_SOURCE_DIR}/qprogressindicator) +# Use the Widgets module from Qt 5. +target_link_libraries(openalpr_tagger Qt5::Widgets) diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..e1401d9 --- /dev/null +++ b/Info.plist @@ -0,0 +1,34 @@ + + + + + NSPrincipalClass + NSApplication + NSHumanReadableCopyright + (C) 2017, Dmitry Baryshev + CFBundleDevelopmentRegion + en + CFBundleInfoDictionaryVersion + 6.0 + CFBundleIconFile + plates.icns + CFBundlePackageType + APPL + CFBundleGetInfoString + OpenALPR Training Utility + CFBundleSignature + ???? + CFBundleExecutable + plates + CFBundleIdentifier + net.plates + CFBundleName + OpenALPR Training Utility + CFBundleDisplayName + OpenALPR Training Utility + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1.0.0 + + diff --git a/about.cpp b/about.cpp new file mode 100644 index 0000000..2c69297 --- /dev/null +++ b/about.cpp @@ -0,0 +1,32 @@ +#include + +#include "about.h" +#include "ui_about.h" + +About::About(QWidget *parent) + : QDialog(parent) + , ui(new Ui::About) +{ + ui->setupUi(this); + + ui->labelProduct->setText(TARGET_HUMAN_STRING); + ui->labelVersion->setText(tr("Version %1").arg(VERSION_STRING)); + ui->labelIcon->setPixmap(QIcon(":/images/" TARGET_STRING ".ico").pixmap(48, 48)); + + // %1 will be replaced with a product name + ui->labelWarranty->setText(tr("%1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.") + .arg(TARGET_HUMAN_STRING)); + +#ifdef Q_OS_MAC + ui->pushOK->hide(); +#else + setWindowTitle(tr("About")); +#endif + + adjustSize(); +} + +About::~About() +{ + delete ui; +} diff --git a/about.h b/about.h new file mode 100644 index 0000000..632cc91 --- /dev/null +++ b/about.h @@ -0,0 +1,23 @@ +#ifndef ABOUT_H +#define ABOUT_H + +#include + +namespace Ui +{ + class About; +} + +class About : public QDialog +{ + Q_OBJECT + +public: + explicit About(QWidget *parent = 0); + ~About(); + +private: + Ui::About *ui; +}; + +#endif // ABOUT_H diff --git a/about.ui b/about.ui new file mode 100644 index 0000000..710b03a --- /dev/null +++ b/about.ui @@ -0,0 +1,193 @@ + + + About + + + + 0 + 0 + 280 + 124 + + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + OK + + + true + + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + Qt::AlignJustify|Qt::AlignVCenter + + + true + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + 8 + + + + + + 75 + true + + + + Qt::AlignCenter + + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 6 + + + + + + + + + + pushOK + clicked() + About + accept() + + + 175 + 131 + + + 423 + 230 + + + + + + slotShowIPs() + + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..07373e6 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +plates (1.0.0-1) unstable; urgency=medium + + * Initial release. + + -- Dmitry Baryshev Fri, 07 Apr 2017 14:51:50 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..894e9d9 --- /dev/null +++ b/debian/control @@ -0,0 +1,18 @@ +Source: plates +Section: education +Priority: extra +Maintainer: Dmitry Baryshev +XSBC-Original-Maintainer: Dmitry Baryshev +Uploaders: Dmitry Baryshev +Build-Depends: debhelper (>= 9), qtbase5-dev, qttools5-dev-tools, imagemagick +Standards-Version: 3.9.4 +Homepage: https://github.com/openalpr/openalpr +#Vcs-Git: +#Vcs-Browser: + +Package: plates +Architecture: any +Multi-Arch: foreign +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: Plates is an OpenALPR training utility + to manually find and mark license plates on photos diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..a0189d0 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,35 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: plates +Source: https://github.com/openalpr/openalpr + +Files: * +Copyright: 2017 Dmitry Baryshev +License: + +Files: debian/* +Copyright: 2016 Dmitry Baryshev +License: LGPL-2.1 + +License: GPL-3 + GNU General Public License Usage + . + Alternatively, this file may be used under the terms of the GNU + General Public License version 3.0 as published by the Free Software + Foundation and appearing in the file LICENSE.GPL included in the + packaging of this file. Please review the following information to + ensure the GNU General Public License version 3.0 requirements will be + met: http://www.gnu.org/copyleft/gpl.html. + . + On Debian systems, the complete text of the license can be found in + `/usr/share/common-licenses/GPL-3`. + +License: LGPL-2.1 + This file may be used under the terms of the GNU Lesser + General Public License version 2.1 as published by the Free Software + Foundation and appearing in the file LICENSE.LGPL included in the + packaging of this file. Please review the following information to + ensure the GNU Lesser General Public License version 2.1 requirements + will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + . + On Debian systems, the complete text of the LGPL-2.1 license can be found in + `/usr/share/common-licenses/LGPL-2.1`, diff --git a/debian/extract-icons b/debian/extract-icons new file mode 100755 index 0000000..3fd34fc --- /dev/null +++ b/debian/extract-icons @@ -0,0 +1,30 @@ +#!/bin/sh -e + +if ! which identify >/dev/null 2>&1; then + echo "Fatal: 'identify' is not installed" >&2 + exit 1 +fi + +if ! which convert >/dev/null 2>&1; then + echo "Fatal: 'convert' is not installed" >&2 + exit 1 +fi + +ico="$1" +template="$2" + +###################################### + +identify "$ico" | while read str; do + name="$(echo "$str" | awk '{ print $1 }')" + res="$(echo "$str" | awk '{ print $3 }')" + + if [ -z "$name" -o -z "$res" ]; then + echo "Cannot extract icons information" >&2 + exit 1 + fi + + convert "$name" "$template-$res.png" + + echo "$res" +done diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..1302d86 --- /dev/null +++ b/debian/rules @@ -0,0 +1,26 @@ +#!/usr/bin/make -f + +DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) + +# Qt5 +export QT_SELECT := qt5 + +%: + dh $@ --parallel + +override_dh_auto_configure: + qmake CONFIG+=release + +override_dh_auto_install: + install -D -m 755 plates $(CURDIR)/debian/plates/usr/bin/plates + strip $(CURDIR)/debian/plates/usr/bin/plates + # extract and install icons + $(CURDIR)/debian/extract-icons images/plates.ico plates | while read res; do \ + install -D -m 644 plates-$${res}.png $(CURDIR)/debian/plates/usr/share/icons/hicolor/$${res}/apps/plates.png; \ + rm -f plates-$${res}.png; \ + done +# # install icons +# for i in 16 32 48 64 128 256; do install -D -m 644 plates-$${i}x$${i}.png $(CURDIR)/debian/plates/usr/share/icons/hicolor/$${i}x$${i}/apps/plates.png; done +# for i in 16 32 48 64 128 256; do rm -f plates-$${i}x$${i}.png; done + install -D -m 644 man/plates.1 $(CURDIR)/debian/plates/usr/share/man/man1/plates.1 + install -D -m 644 plates.desktop $(CURDIR)/debian/plates/usr/share/applications/plates.desktop diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/directoryloader.cpp b/directoryloader.cpp new file mode 100644 index 0000000..96346cf --- /dev/null +++ b/directoryloader.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "directoryloader.h" +#include "utils.h" +#include "ui_directoryloader.h" + +DirectoryLoader::DirectoryLoader(const QString &dirPath, QWidget *parent) + : QDialog(parent) + , ui(new Ui::DirectoryLoader) +{ + ui->setupUi(this); + setWindowFlags(windowFlags() | Qt::FramelessWindowHint); + + // directory iterator + m_iterator = new QDirIterator(dirPath, + Utils::imageMatchingWildcard(), + QDir::Files | QDir::Readable); + + ui->progress->startAnimation(); + + // start iterating immediately + QTimer::singleShot(0, this, SLOT(slotLoadNext())); +} + +DirectoryLoader::~DirectoryLoader() +{ + delete m_iterator; + delete ui; +} + +void DirectoryLoader::keyPressEvent(QKeyEvent *e) +{ + if(e->key() == Qt::Key_Escape) + { + e->accept(); + return; + } + + QDialog::keyPressEvent(e); +} + +void DirectoryLoader::slotLoadNext() +{ + if(m_iterator->hasNext()) + { + m_iterator->next(); + + // append file & update status label + m_imageFiles.append(m_iterator->fileInfo()); + ui->labelFound->setNum(m_imageFiles.size()); + + // next iteration + QTimer::singleShot(0, this, SLOT(slotLoadNext())); + } + else + { + if(m_imageFiles.isEmpty()) + reject(); + else + accept(); + } +} diff --git a/directoryloader.h b/directoryloader.h new file mode 100644 index 0000000..2f783a2 --- /dev/null +++ b/directoryloader.h @@ -0,0 +1,51 @@ +#ifndef DIRECTORYLOADER_H +#define DIRECTORYLOADER_H + +#include +#include + +#include "imagefile.h" + +class QDirIterator; + +namespace Ui +{ + class DirectoryLoader; +} + +/* + * Directory loader with a spinning progress indicator. Calls accept() + * when some files has been found + */ +class DirectoryLoader : public QDialog +{ + Q_OBJECT + +public: + explicit DirectoryLoader(const QString &dirPath, QWidget *parent = 0); + ~DirectoryLoader(); + + /* + * Found image files + */ + ImageFileList imageFiles() const; + +protected: + virtual void keyPressEvent(QKeyEvent *e) override; + +private slots: + void slotLoadNext(); + +private: + Ui::DirectoryLoader *ui; + QDirIterator *m_iterator; + ImageFileList m_imageFiles; +}; + +inline +ImageFileList DirectoryLoader::imageFiles() const +{ + return m_imageFiles; +} + +#endif // DIRECTORYLOADER_H diff --git a/directoryloader.ui b/directoryloader.ui new file mode 100644 index 0000000..8ebefe2 --- /dev/null +++ b/directoryloader.ui @@ -0,0 +1,80 @@ + + + DirectoryLoader + + + + 0 + 0 + 167 + 40 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Box + + + + + + Found images: + + + + + + + + 1 + 0 + + + + + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + + + + + QProgressIndicator + QWidget +
QProgressIndicator.h
+ 1 +
+
+ + +
diff --git a/dot.cpp b/dot.cpp new file mode 100644 index 0000000..64bf7e7 --- /dev/null +++ b/dot.cpp @@ -0,0 +1,84 @@ +#include +#include +#include + +#include "imageviewerplateselector.h" +#include "utils.h" +#include "dot.h" + +constexpr int DOT_WIDTH = 14; +constexpr int DOT_HEIGHT = 14; + +Dot::Dot(QWidget *parent) + : QWidget(parent) + , inMove(false) +{ + setMouseTracking(true); + setFixedSize(DOT_WIDTH, DOT_HEIGHT); + show(); +} + +void Dot::mousePressEvent(QMouseEvent *e) +{ + e->accept(); + + m_lastMousePoint = e->pos(); + inMove = true; +} + +void Dot::mouseMoveEvent(QMouseEvent *e) +{ + e->accept(); + + if(!inMove) + return; + + dragTo(e->pos()); +} + +void Dot::mouseReleaseEvent(QMouseEvent *e) +{ + e->accept(); + + dragTo(e->pos()); + + emit moved(); + + inMove = false; +} + +void Dot::dragTo(const QPoint &pos) +{ + ImageViewerPlateSelector *plateSelectorParent = qobject_cast(parent()); + + if(!plateSelectorParent) + { + qWarning("Parent must be of class ImageViewerPlateSelector"); + return; + } + + const QPoint &targetPos = this->pos() + (pos - m_lastMousePoint); + + if(plateSelectorParent->acceptableDotPosition(QRect(targetPos, size()))) + { + move(targetPos); + emit moving(); + } +} + +void Dot::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + + painter.setClipRegion(e->region()); + painter.setRenderHint(QPainter::Antialiasing); + + painter.setPen(Utils::polygonPaintingPen()); + painter.setBrush(painter.pen().brush()); + + // squeeze the ellipse by the pen width + painter.drawEllipse(painter.pen().width(), + painter.pen().width(), + width() - painter.pen().width()*2, + height() - painter.pen().width()*2); +} diff --git a/dot.h b/dot.h new file mode 100644 index 0000000..a133c57 --- /dev/null +++ b/dot.h @@ -0,0 +1,83 @@ +#ifndef DOT_H +#define DOT_H + +#include +#include + +/* + * Represents a single polygon dot in a plate selector + */ +class Dot : public QWidget +{ + Q_OBJECT + +public: + Dot(QWidget *parent = nullptr); + + /* + * Center point of the dot in parent's coordinates + */ + void setCenterPoint(const QPoint ¢er); + + /* + * Center point + */ + QPoint centerPoint() const; + + /* + * Appropriate not scaled point in the original image in pixels + */ + void setPointInImage(const QPoint &point); + QPoint pointInImage() const; + +protected: + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + virtual void paintEvent(QPaintEvent *e) override; + +private: + QPoint relativeCenter() const; + void dragTo(const QPoint &pos); + +signals: + void moving(); + void moved(); + +private: + QPoint m_lastMousePoint; + QPoint m_pointInImage; + bool inMove; +}; + +inline +void Dot::setCenterPoint(const QPoint ¢er) +{ + move(center - relativeCenter()); +} + +inline +QPoint Dot::centerPoint() const +{ + return pos() + relativeCenter(); +} + +inline +void Dot::setPointInImage(const QPoint &point) +{ + m_pointInImage = point; +} + +inline +QPoint Dot::pointInImage() const +{ + return m_pointInImage; +} + +inline +QPoint Dot::relativeCenter() const +{ + return QPoint(width()/2, height()/2); +} + +#endif // DOT_H diff --git a/imagefile.cpp b/imagefile.cpp new file mode 100644 index 0000000..9a86f5e --- /dev/null +++ b/imagefile.cpp @@ -0,0 +1,51 @@ +#include + +#include "imagefile.h" +#include "utils.h" + +ImageFile::ImageFile() +{} + +ImageFile::ImageFile(const QFileInfo &fileInfo) + : m_fileInfo(fileInfo) + , m_moved(false) +{ + // extract "/data/car" from "/data/car.jpg" + m_plateNameTemplate = m_fileInfo.canonicalPath() + QDir::separator() + m_fileInfo.completeBaseName(); + m_plates = PlateFile::fromImageFile(m_plateNameTemplate); +} + +void ImageFile::setPlates(const PlateFileList &plateFileList) +{ + m_plates = plateFileList; + + // sync to disk + for(int i = 0;i < m_plates.size();i++) + { + QString error; + PlateFile plateFile = m_plates.at(i); + + plateFile.setImageFile(m_fileInfo.fileName()); + + if(!plateFile.writeToFile(i, &error)) + { + Utils::error(QObject::tr("Cannot save YAML file with index %1. %2") + .arg(i) + .arg(error.isEmpty() + ? QObject::tr("Unknown error") + : QObject::tr("Error: %1").arg(error))); + } + } + + int index = m_plates.size(); + + while(true) + { + const QString &file = m_plateNameTemplate + QString("-%1.yaml").arg(index); + + if(!QFile::exists(file)) + break; + + QFile::remove(file); + } +} diff --git a/imagefile.h b/imagefile.h new file mode 100644 index 0000000..c4d3e27 --- /dev/null +++ b/imagefile.h @@ -0,0 +1,91 @@ +#ifndef IMAGEFILE_H +#define IMAGEFILE_H + +#include +#include + +#include "platefile.h" + +/* + * Represents a single image with associated YAML files + * + * If an image file is "car.jpg", associated YAML files will be named + * + * car-0.yaml + * car-1.yaml + * etc. + */ +class ImageFile +{ +public: + ImageFile(); + ImageFile(const QFileInfo &fileInfo); + + /* + * Image file info + */ + const QFileInfo &fileInfo() const; + + /* + * Plate name template + */ + + QString plateNameTemplate() const; + + /* + * Associated plates + */ + PlateFileList plates() const; + + /* + * Update plates + */ + void setPlates(const PlateFileList &plateFileList); + + /* + * Image has been moved and its methods and properties + * should not be used anymore + */ + bool moved() const; + void setMoved(bool moved); + +private: + QFileInfo m_fileInfo; + QString m_plateNameTemplate; + PlateFileList m_plates; + bool m_moved; +}; + +inline +const QFileInfo &ImageFile::fileInfo() const +{ + return m_fileInfo; +} + +inline +QString ImageFile::plateNameTemplate() const +{ + return m_plateNameTemplate; +} + +inline +PlateFileList ImageFile::plates() const +{ + return m_plates; +} + +inline +bool ImageFile::moved() const +{ + return m_moved; +} + +inline +void ImageFile::setMoved(bool moved) +{ + m_moved = moved; +} + +using ImageFileList = QList; + +#endif // IMAGEFILE_H diff --git a/images/plates.icns b/images/plates.icns new file mode 100644 index 0000000000000000000000000000000000000000..8ea3ba0acaba4484fec9ae71cc8542126e4dc17f GIT binary patch literal 184756 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 literal 0 HcmV?d00001 diff --git a/images/plates.ico b/images/plates.ico new file mode 100644 index 0000000000000000000000000000000000000000..2893c8bb01d2d9c741260f393a0749337e5db722 GIT binary patch literal 370070 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 +#include +#include + +#include "threadedimageloader.h" +#include "QProgressIndicator.h" +#include "imagefile.h" +#include "imageview.h" +#include "utils.h" +#include "ui_imageview.h" + +ImageView::ImageView(QWidget *parent) + : QStackedWidget(parent) + , ui(new Ui::ImageView) + , m_imageFile(nullptr) + , m_threadedImageLoader(new ThreadedImageLoader(this)) +{ + ui->setupUi(this); + + // the image has been loaded + connect(m_threadedImageLoader, SIGNAL(loaded(QImage)), this, SLOT(slotImageFileLoaded(QImage))); + + // proxying signals + connect(ui->imageViewer, SIGNAL(zoomChanged(double)), this, SIGNAL(zoomChanged(double))); + connect(ui->imageViewer, SIGNAL(platesChanged()), this, SIGNAL(platesChanged())); +} + +ImageView::~ImageView() +{ + delete ui; +} + +void ImageView::setImageFile(ImageFile *imageFile) +{ + qDebug("Loading image '%s'", qPrintable(imageFile->fileInfo().canonicalFilePath())); + + if(busy()) + return; + + m_imageFile = imageFile; + + setCurrentIndex(0); + ui->progress->startAnimation(); + + m_threadedImageLoader->loadImageFromFile(imageFile->fileInfo().canonicalFilePath()); +} + +bool ImageView::busy() const +{ + return m_threadedImageLoader->isRunning(); +} + +void ImageView::reset() +{ + if(busy()) + return; + + setCurrentIndex(0); + + m_imageFile = nullptr; + ui->imageViewer->reset(); +} + +void ImageView::setImage(const QImage &image) +{ + if(!m_imageFile) + return; + + setCurrentIndex(1); + ui->progress->stopAnimation(); + + ui->imageViewer->setImageFile(m_imageFile); + ui->imageViewer->setImage(image); +} + +void ImageView::slotImageFileLoaded(const QImage &image) +{ + if(image.isNull()) + { + setCurrentIndex(1); + Utils::error(tr("Cannot load the specified image. Possibly it's corrupted"), this); + return; + } + + setImage(image); +} diff --git a/imageview.h b/imageview.h new file mode 100644 index 0000000..4ad578a --- /dev/null +++ b/imageview.h @@ -0,0 +1,60 @@ +#ifndef IMAGEVIEW_H +#define IMAGEVIEW_H + +#include +#include + +class QImage; + +class ImageFile; + +namespace Ui +{ + class ImageView; +} + +class ThreadedImageLoader; + +/* + * Image viewer preserving aspect ratio and supporting scaling. + * + * Displays a spinning progress when loading a file. Utilizes ImageViewer class + * to display an actual image + */ +class ImageView : public QStackedWidget +{ + Q_OBJECT + +public: + explicit ImageView(QWidget *parent = 0); + ~ImageView(); + + /* + * Load the specified image + */ + void setImageFile(ImageFile *imageFile); + + /* + * Is loading the specified image + */ + bool busy() const; + + void reset(); + +private: + void setImage(const QImage &image); + +signals: + void zoomChanged(double); + void platesChanged(); + +private slots: + void slotImageFileLoaded(const QImage &image); + +private: + Ui::ImageView *ui; + ImageFile *m_imageFile; + ThreadedImageLoader *m_threadedImageLoader; +}; + +#endif // IMAGEVIEW_H diff --git a/imageview.ui b/imageview.ui new file mode 100644 index 0000000..88764ef --- /dev/null +++ b/imageview.ui @@ -0,0 +1,109 @@ + + + ImageView + + + + 0 + 0 + 389 + 300 + + + + QFrame::StyledPanel + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 120 + + + + + + + + Qt::Horizontal + + + + 165 + 20 + + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + + 164 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 120 + + + + + + + + + + + ImageViewerObserver + QWidget +
imageviewerobserver.h
+ 1 +
+ + QProgressIndicator + QWidget +
QProgressIndicator.h
+ 1 +
+
+ + +
diff --git a/imageviewerbase.cpp b/imageviewerbase.cpp new file mode 100644 index 0000000..4f8ef95 --- /dev/null +++ b/imageviewerbase.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +#include "imageviewerbase.h" + +ImageViewerBase::ImageViewerBase(QWidget *parent) + : QWidget(parent) + , m_zoom(1) +{ + m_timerDelayedUpdate = new QTimer(this); + m_timerDelayedUpdate->setSingleShot(true); + m_timerDelayedUpdate->setInterval(10); + connect(m_timerDelayedUpdate, SIGNAL(timeout()), this, SLOT(updatePixmap())); + + setMinimumSize(200, 120); +} + +void ImageViewerBase::setImage(const QImage &image) +{ + m_timerDelayedUpdate->stop(); + m_image = image; + + updatePixmap(); +} + +void ImageViewerBase::reset() +{ + m_image = QImage(); + m_pixmap = QPixmap(); + m_pixmapBoundingRect = QRect(); + m_zoom = 1; + + update(); +} + +void ImageViewerBase::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + + if(!m_timerDelayedUpdate->isActive()) + m_timerDelayedUpdate->start(); +} + +void ImageViewerBase::paintEvent(QPaintEvent *event) +{ + if(m_pixmap.isNull()) + return; + + QPainter painter(this); + + painter.setClipRegion(event->region()); + painter.setRenderHint(QPainter::Antialiasing); + + // car image + painter.drawPixmap(m_pixmapBoundingRect.x(), m_pixmapBoundingRect.y(), m_pixmap); + + // custom paint in subclasses + draw(&painter); +} + +void ImageViewerBase::updatePixmap() +{ + pixmapAboutToBeChanged(); + + m_pixmap = QPixmap(); + + if(m_image.isNull()) + return; + + // scale with a higher quality + m_pixmap = QPixmap::fromImage(m_image.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + + // calculate scaling + m_zoom = static_cast(m_pixmap.width()) / m_image.width(); + + // pixmap bounding rectangle + m_pixmapBoundingRect = QRect((width() - m_pixmap.width()) / 2, + (height() - m_pixmap.height()) / 2, + m_pixmap.width(), + m_pixmap.height()); + + pixmapChanged(); + + emit zoomChanged(m_zoom); + + update(); +} diff --git a/imageviewerbase.h b/imageviewerbase.h new file mode 100644 index 0000000..9cc5bae --- /dev/null +++ b/imageviewerbase.h @@ -0,0 +1,102 @@ +#ifndef IMAGEVIEWERBASE_H +#define IMAGEVIEWERBASE_H + +#include +#include +#include +#include + +class QPainter; +class QTimer; + +/* + * Base image viewer widget with auto scaling + */ +class ImageViewerBase : public QWidget +{ + Q_OBJECT + +public: + explicit ImageViewerBase(QWidget *parent = 0); + virtual ~ImageViewerBase() = default; + + /* + * Display an image in the view + */ + void setImage(const QImage &image); + + /* + * Current zoom + */ + double zoom() const; + + /* + * Clear and reset view to initial state with no actual image set + */ + virtual void reset(); + +protected: + virtual void resizeEvent(QResizeEvent *event) override; + virtual void paintEvent(QPaintEvent *event) override; + + /* + * Getters for derived classes + */ + QPixmap pixmap() const; + QImage image() const; + QRect pixmapBoundingRect() const; + + /* + * Pixmap will be scaled right now! + */ + virtual void pixmapAboutToBeChanged() = 0; + + /* + * Pixmap has been scaled and all the internal data has been updated + */ + virtual void pixmapChanged() = 0; + + /* + * Draw viewer-specific stuff + */ + virtual void draw(QPainter *painter) = 0; + +signals: + void zoomChanged(double); + +private slots: + void updatePixmap(); + +private: + QTimer *m_timerDelayedUpdate; + QImage m_image; + QPixmap m_pixmap; + double m_zoom; + QRect m_pixmapBoundingRect; +}; + +inline +double ImageViewerBase::zoom() const +{ + return m_zoom; +} + +inline +QPixmap ImageViewerBase::pixmap() const +{ + return m_pixmap; +} + +inline +QImage ImageViewerBase::image() const +{ + return m_image; +} + +inline +QRect ImageViewerBase::pixmapBoundingRect() const +{ + return m_pixmapBoundingRect; +} + +#endif // IMAGEVIEWERBASE_H diff --git a/imageviewerobserver.cpp b/imageviewerobserver.cpp new file mode 100644 index 0000000..1ed94f5 --- /dev/null +++ b/imageviewerobserver.cpp @@ -0,0 +1,262 @@ +#include +#include + +#include "imageviewerobserver.h" +#include "plateselector.h" +#include "imagefile.h" +#include "settings.h" + +constexpr double BOUNDING_ENLARGE_WIDTH_FACTOR = 0.15; +constexpr double BOUNDING_ENLARGE_HEIGHT_FACTOR = 0.25; + +ImageViewerObserver::ImageViewerObserver(QWidget *parent) + : ImageViewerBase(parent) + , m_imageFile(nullptr) + , m_plateFileIndexForEditing(-1) +{} + +void ImageViewerObserver::setImageFile(ImageFile *imageFile) +{ + // save image info + m_imageFile = imageFile; + + // saved license plates + m_polygons = Polygons::fromPlates(m_imageFile->plates()); +} + +void ImageViewerObserver::reset() +{ + m_imageFile = nullptr; + m_polygons.clear(); + + ImageViewerBase::reset(); +} + +void ImageViewerObserver::mousePressEvent(QMouseEvent *e) +{ + e->accept(); + + // start to paint a selection rectangle + m_selection.reset(); + + // the point is outside the image or nothing to select + if(pixmap().isNull()) + return; + + m_selection.start(e->pos()); + update(); +} + +void ImageViewerObserver::mouseMoveEvent(QMouseEvent *e) +{ + e->accept(); + + // no selection or the point is outside the image + if(!m_selection.hasSelection()) + return; + + m_selection.move(e->pos()); + update(); +} + +void ImageViewerObserver::mouseReleaseEvent(QMouseEvent *e) +{ + e->accept(); + + // no selection/selection is too small, or the point is outside the image + if(!m_selection.selectionIsFine()) + { + QPoint pos = (e->pos() - pixmapBoundingRect().topLeft()) / zoom(); + + // handle click + for(int i = 0;i < m_imageFile->plates().size();i++) + { + const PlateFile &plateFile = m_imageFile->plates().at(i); + + if(plateFile.plateCorners().containsPoint(pos, Qt::OddEvenFill)) + { + const QRect &boundingRect = plateFile.plateCorners().boundingRect(); + + // enlarge + m_fixedSelection = boundingRect.adjusted(-BOUNDING_ENLARGE_WIDTH_FACTOR * boundingRect.width(), + -BOUNDING_ENLARGE_HEIGHT_FACTOR * boundingRect.height(), + BOUNDING_ENLARGE_WIDTH_FACTOR * boundingRect.width(), + BOUNDING_ENLARGE_HEIGHT_FACTOR * boundingRect.height()); + + m_fixedSelection &= image().rect(); + + m_plateFileIndexForEditing = i; + + QTimer::singleShot(0, this, SLOT(slotShowPlateSelector())); + break; + } + } + + return; + } + + m_selection.stop(); + + m_fixedSelection = m_selection.selection(); + + // relative to the image itself + m_fixedSelection.moveTopLeft(m_fixedSelection.topLeft() - pixmapBoundingRect().topLeft()); + + // take zoom into account + m_fixedSelection.moveTopLeft(m_fixedSelection.topLeft() * (1 / zoom())); + m_fixedSelection.setSize(m_fixedSelection.size() * (1 / zoom())); + + // fix possible division errors: + // - negative coordinates + // - too large size + m_fixedSelection &= image().rect(); + + if(!m_fixedSelection.isValid()) + { + qWarning() << "Selection rectangle" << m_fixedSelection << "is invalid for image size" << image().size(); + return; + } + + qDebug() << "Actual selection: " << m_fixedSelection; + + m_plateFileIndexForEditing = -1; + + QTimer::singleShot(0, this, SLOT(slotShowPlateSelector())); + + update(); +} + +bool ImageViewerObserver::event(QEvent *event) +{ + if(event->type() == QEvent::ToolTip && SETTINGS_GET_BOOL(SETTING_TOOLTIPS)) + { + const QPoint &pos = (mapFromGlobal(QCursor::pos()) - pixmapBoundingRect().topLeft()) / zoom(); + + // handle click + foreach(const PlateFile &plateFile, m_imageFile->plates()) + { + if(plateFile.plateCorners().containsPoint(pos, Qt::OddEvenFill)) + { + QToolTip::showText(QCursor::pos(), + QString( + // "" + //"" + //"" + //"" + // "
%1%2
%3%4
%5%6
" + "%1  %2
" + "%3  %4
" + "%5  %6" + ) + .arg(tr("Plate number:")) + .arg(plateFile.plateNumber()) + .arg(tr("Plate region:")) + .arg(plateFile.regionCode()) + .arg(tr("Light-on-Dark:")) + .arg(plateFile.plateInverted() ? tr("yes") : tr("no")), + nullptr, + QRect()); + break; + } + } + + event->accept(); + return true; + } + + return QWidget::event(event); +} + +void ImageViewerObserver::pixmapAboutToBeChanged() +{ + m_selection.reset(); +} + +void ImageViewerObserver::pixmapChanged() +{ + m_selection.setReferenceRect(pixmapBoundingRect()); + m_polygons.setZoom(zoom()); +} + +void ImageViewerObserver::draw(QPainter *painter) +{ + if(!painter) + return; + + painter->translate(pixmapBoundingRect().topLeft()); + m_polygons.draw(painter); + + painter->resetTransform(); + m_selection.draw(painter); +} + +void ImageViewerObserver::slotShowPlateSelector() +{ + qDebug() << "Image selection:" << m_fixedSelection; + + const QImage &frame = image().copy(m_fixedSelection); + + if(frame.isNull()) + { + qWarning() << "Cannot edit a null image from region" << m_fixedSelection; + return; + } + + PlateSelector selector(frame, this); + + selector.setSelectionRect(m_fixedSelection); + + if(m_plateFileIndexForEditing >= 0) + selector.setPlateFile(m_imageFile->plates().at(m_plateFileIndexForEditing)); + + if(selector.exec() != PlateSelector::Accepted) + return; + + PlateFileList plateFileList = m_imageFile->plates(); + PlateFile *plateFile = nullptr; + + // existing plate + if(m_plateFileIndexForEditing >= 0) + { + qDebug("Editing existing plate index %d", m_plateFileIndexForEditing); + + // empty polygon -> delete it + if(selector.selectedPolygon().isEmpty()) + plateFileList.removeAt(m_plateFileIndexForEditing); + else + plateFile = &plateFileList[m_plateFileIndexForEditing]; + } + // new non-empty plate + else if(!selector.selectedPolygon().isEmpty()) + { + qDebug("Adding new plate with index %d", plateFileList.size()); + + PlateFile newPlateFile(m_imageFile->plateNameTemplate(), plateFileList.size()); + + newPlateFile.setImageFile(m_imageFile->fileInfo().fileName()); + newPlateFile.setImageWidth(image().width()); + newPlateFile.setImageHeight(image().height()); + + plateFileList.append(newPlateFile); + plateFile = &plateFileList.last(); + } + else + return; + + // update with entered data + if(plateFile) + { + plateFile->setPlateCorners(selector.selectedPolygon()); + plateFile->setRegionCode(selector.plateRegion()); + plateFile->setPlateNumber(selector.plateNumber()); + plateFile->setPlateInverted(selector.lightOnDark()); + } + + m_imageFile->setPlates(plateFileList); + + emit platesChanged(); + + m_polygons = Polygons::fromPlates(m_imageFile->plates()); + m_polygons.setZoom(zoom()); + update(); +} diff --git a/imageviewerobserver.h b/imageviewerobserver.h new file mode 100644 index 0000000..05880fa --- /dev/null +++ b/imageviewerobserver.h @@ -0,0 +1,62 @@ +#ifndef IMAGEVIEWEROBSERVER_H +#define IMAGEVIEWEROBSERVER_H + +#include "imageviewerbase.h" +#include "selection.h" +#include "polygons.h" + +class ImageFile; + +/* + * Image viewer to display a car with static license plates + */ +class ImageViewerObserver : public ImageViewerBase +{ + Q_OBJECT + +public: + explicit ImageViewerObserver(QWidget *parent = nullptr); + + /* + * Add information with static license plates + */ + void setImageFile(ImageFile *imageFile); + + /* + * Reset override + */ + virtual void reset() override; + +protected: + /* + * For handling selection + */ + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + + /* + * Custom tooltips + */ + virtual bool event(QEvent *event) override; + + virtual void pixmapAboutToBeChanged() override; + virtual void pixmapChanged() override; + + virtual void draw(QPainter *painter) override; + +signals: + void platesChanged(); + +private slots: + void slotShowPlateSelector(); + +private: + ImageFile *m_imageFile; + Polygons m_polygons; + Selection m_selection; + QRect m_fixedSelection; + int m_plateFileIndexForEditing; +}; + +#endif // IMAGEVIEWEROBSERVER_H diff --git a/imageviewerplateselector.cpp b/imageviewerplateselector.cpp new file mode 100644 index 0000000..605a936 --- /dev/null +++ b/imageviewerplateselector.cpp @@ -0,0 +1,212 @@ +#include +#include +#include + +#include + +#include "imageviewerplateselector.h" +#include "plateselector.h" +#include "utils.h" +#include "dot.h" + +ImageViewerPlateSelector::ImageViewerPlateSelector(QWidget *parent) + : ImageViewerBase(parent) +{} + +bool ImageViewerPlateSelector::acceptableDotPosition(const QRect &rect) const +{ + return rect.intersected(pixmapBoundingRect()) == rect; +} + +bool ImageViewerPlateSelector::addDotInViewCoordinates(const QPoint &point) +{ + // only 4 dots are allowed + if(m_dots.size() == 4) + return false; + + // add new selection dot + Dot *dot = new Dot(this); + + connect(dot, SIGNAL(moving()), this, SLOT(slotDotMoving())); + connect(dot, SIGNAL(moved()), this, SLOT(slotDotMoved())); + + dot->setCenterPoint(point); + updatePointInImage(dot); + + m_dots.append(dot); + + if(!reorderDots()) + update(); + + emitDotsChanged(); + + return true; +} + +bool ImageViewerPlateSelector::addDotInImageCoordinates(const QPoint &point) +{ + qDebug() << "Add dot" << point; + return addDotInViewCoordinates(pixmapBoundingRect().topLeft() + point * zoom()); +} + +void ImageViewerPlateSelector::clearDots() +{ + if(m_dots.isEmpty()) + return; + + qDeleteAll(m_dots); + m_dots.clear(); + update(); + + emitDotsChanged(); +} + +QPolygon ImageViewerPlateSelector::selectedPolygon() const +{ + QPolygon polygon; + + if(m_dots.size() != 4) + return polygon; + + polygon.reserve(m_dots.size()); + + foreach(Dot *dot, m_dots) + { + polygon.append(dot->pointInImage()); + } + + return polygon; +} + +void ImageViewerPlateSelector::mousePressEvent(QMouseEvent *e) +{ + e->accept(); +} + +void ImageViewerPlateSelector::mouseMoveEvent(QMouseEvent *e) +{ + e->accept(); +} + +void ImageViewerPlateSelector::mouseReleaseEvent(QMouseEvent *e) +{ + e->accept(); + + // not interesting click + if(!pixmapBoundingRect().contains(e->pos())) + return; + + addDotInViewCoordinates(e->pos()); +} + +void ImageViewerPlateSelector::pixmapAboutToBeChanged() +{} + +void ImageViewerPlateSelector::pixmapChanged() +{ + foreach(Dot *dot, m_dots) + { + dot->setCenterPoint(pixmapBoundingRect().topLeft() + dot->pointInImage() * zoom()); + } +} + +void ImageViewerPlateSelector::draw(QPainter *painter) +{ + if(!painter) + return; + + // draw only a full polygon + if(m_dots.size() == 4) + { + painter->setPen(Utils::polygonPaintingPen()); + + QPolygon polygon; + polygon.reserve(m_dots.size()); + + foreach(Dot *dot, m_dots) + { + polygon.append(dot->centerPoint()); + } + + painter->drawPolygon(polygon); + } +} + +void ImageViewerPlateSelector::updatePointInImage(Dot *dot) const +{ + if(!dot) + return; + + dot->setPointInImage((dot->centerPoint() - pixmapBoundingRect().topLeft()) / zoom()); +} + +void ImageViewerPlateSelector::emitDotsChanged() +{ + emit dotsChanged(m_dots.size()); +} + +bool ImageViewerPlateSelector::reorderDots() +{ + // sort dots to make a valid region + if(m_dots.size() != 4) + return false; + + QList newDots; + newDots.reserve(m_dots.size()); + + struct Point + { + int index; + QPointF point; + }; + + const QPoint ¢roid = Utils::centroid(selectedPolygon()); + const QPolygon &selectedPolygon = this->selectedPolygon(); + + QVector polarPolygon; + polarPolygon.reserve(selectedPolygon.size()); + + for(int i = 0;i < selectedPolygon.size();i++) + { + const QPoint &relativeToCentroid = selectedPolygon.at(i) - centroid; + + // convert to polar: + // x == theta (angle) + // y == r (length) + polarPolygon.append( { i, { qAtan2(relativeToCentroid.y(), relativeToCentroid.x()), + qSqrt(qPow(relativeToCentroid.x(),2) + qPow(relativeToCentroid.y(),2)) } } ); + } + + std::sort(polarPolygon.begin(), + polarPolygon.end(), + [](const Point &a, const Point &b) { + return (a.point.x() == b.point.x()) ? a.point.y() < b.point.y() : a.point.x() < b.point.x(); + }); + + foreach(const Point &point, polarPolygon) + { + newDots.append(m_dots.at(point.index)); + } + + m_dots = newDots; + + update(); + + return true; +} + +void ImageViewerPlateSelector::slotDotMoving() +{ + Dot *dot = qobject_cast(sender()); + + if(!dot) + return; + + updatePointInImage(dot); + update(); +} + +void ImageViewerPlateSelector::slotDotMoved() +{ + reorderDots(); +} diff --git a/imageviewerplateselector.h b/imageviewerplateselector.h new file mode 100644 index 0000000..cbb662a --- /dev/null +++ b/imageviewerplateselector.h @@ -0,0 +1,73 @@ +#ifndef IMAGEVIEWERPLATESELECTOR_H +#define IMAGEVIEWERPLATESELECTOR_H + +#include +#include + +#include "imageviewerbase.h" + +class Dot; + +/* + * Image viewer to display a car with static license plates + */ +class ImageViewerPlateSelector : public ImageViewerBase +{ + Q_OBJECT + +public: + explicit ImageViewerPlateSelector(QWidget *parent = nullptr); + + /* + * Check if the given dot geometry fits into the pixmap + */ + bool acceptableDotPosition(const QRect &rect) const; + + /* + * Add a new dot at the given point + */ + bool addDotInViewCoordinates(const QPoint &point); + bool addDotInImageCoordinates(const QPoint &point); + + /* + * Remove all dots + */ + void clearDots(); + + /* + * Selection result as a polygon with 4 points. Each point is + * in image coordinates + */ + QPolygon selectedPolygon() const; + +protected: + /* + * For handling selection + */ + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + + virtual void pixmapAboutToBeChanged() override; + virtual void pixmapChanged() override; + + virtual void draw(QPainter *painter) override; + +private: + void updatePointInImage(Dot *dot) const; + void emitDotsChanged(); + bool reorderDots(); + +signals: + void dotsChanged(int); + +private slots: + void slotDotMoving(); + void slotDotMoved(); + +private: + // selection dots + QList m_dots; +}; + +#endif // IMAGEVIEWERPLATESELECTOR_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..10e899c --- /dev/null +++ b/main.cpp @@ -0,0 +1,117 @@ +#include + +#include + +#include "mainwindow.h" + +static char messageTypeToChar(QtMsgType type) +{ + switch(type) + { + case QtDebugMsg: return 'D'; + case QtWarningMsg: return 'W'; + case QtCriticalMsg: return 'C'; + case QtFatalMsg: return 'F'; +#if QT_VERSION >= QT_VERSION_CHECK(5,5,0) + case QtInfoMsg: return 'I'; +#endif + + default: + return 'U'; + } +} + +static void platesOutput(QtMsgType type, const QMessageLogContext &context, const QString &message) +{ + Q_UNUSED(context) + + QByteArray msg = message.toLatin1(); + + const QString currentTime = QDateTime::currentDateTime().toString("hh:mm:ss.zzz"); + + const char messageChar = messageTypeToChar(type); + + fprintf(stderr, "%s %c %s\n", qPrintable(currentTime), messageChar, static_cast(msg)); + +#ifndef PLATES_NO_LOG + static bool noLog = qgetenv("PLATES_NO_LOG") == "1"; + + if(noLog) + return; + + static QFile log( + QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + QDir::separator() + + QCoreApplication::applicationName().toLower() + + ".log"); + + static bool failed = false; + + if(!log.isOpen() && !failed) + { + failed = !log.open(QIODevice::ReadWrite | QIODevice::Append) || !log.resize(0); + + if(failed) + fprintf(stderr, "Log file is unavailable\n"); + } + + if(!failed) + { + // truncate + if(log.size() > 1*1024*1024) // 1 Mb + { + fprintf(stderr, "Truncating log\n"); + + log.seek(0); + + char buf[1024]; + int lines = 0; + char c = 'x'; + + while(log.readLine(buf, sizeof(buf)) > 0 && lines++ < 30) + {} + + log.resize(log.pos()); + + // check if '\n' is last + if(log.seek(log.pos()-1)) + { + log.getChar(&c); + + if(c != '\n') + log.write("\n"); + } + + log.write("...\n\n...\n"); + } + + log.write(currentTime.toLatin1()); + log.write(" "); + log.write(&messageChar, sizeof(decltype(messageChar))); + log.write(" "); + log.write(static_cast(msg)); + log.write("\n"); + } +#endif // PLATES_NO_LOG + + if(type == QtFatalMsg) + abort(); +} + +int main(int argc, char *argv[]) +{ + setbuf(stderr, 0); + + QCoreApplication::setApplicationName(TARGET_HUMAN_STRING); + QCoreApplication::setOrganizationName("OpenALPR"); + QCoreApplication::setApplicationVersion(VERSION_STRING); + + QApplication app(argc, argv); + + qInstallMessageHandler(platesOutput); + + MainWindow w; + w.show(); + + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..836af20 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,362 @@ +#include +#include +#include + +#include "directoryloader.h" +#include "mainwindow.h" +#include "settings.h" +#include "options.h" +#include "about.h" +#include "utils.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , m_firstRun(true) +{ + ui->setupUi(this); + + setWindowTitle(TARGET_HUMAN_STRING); + + // shortcuts + ui->actionOpen_directory->setShortcut(QKeySequence::Open); + ui->actionQuit->setShortcut(QKeySequence::Quit); + ui->actionOptions->setShortcut(QKeySequence::Preferences); + ui->actionAbout->setShortcut(QKeySequence::HelpContents); + + // set tooltips + Utils::setShortcutTooltips(QList() + << ui->pushPrevious + << ui->pushJumpTo + << ui->pushNext); + + // zoom + connect(ui->imageView, SIGNAL(zoomChanged(double)), this, SLOT(slotZoomChanged(double))); + connect(ui->imageView, SIGNAL(platesChanged()), this, SLOT(slotPlatesChanged())); + + // open a directory at startup + QTimer::singleShot(0, this, SLOT(slotOpenDirectory())); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::reset() +{ + m_imageFiles.clear(); + m_currentIndex = -1; + + ui->imageView->reset(); + ui->labelImageNumber->clear(); + ui->labelImageFile->clear(); + ui->labelCurrentImageInfo->clear(); + ui->labelZoom->clear(); + + ui->labelImagesTagged->clear(); + ui->labelTotalPlates->clear(); +} + +void MainWindow::updateImageInformationFromIndex() +{ + if(m_currentIndex >= 0 && m_currentIndex < m_imageFiles.size()) + { + ImageFile &imageFile = m_imageFiles[m_currentIndex]; + + ui->imageView->setImageFile(&imageFile); + ui->labelImageFile->setText(imageFile.fileInfo().fileName()); + ui->labelImageNumber->setText(QString("%1 / %2").arg(m_currentIndex+1).arg(m_imageFiles.size())); + + updateImagePlatesInformationFromIndex(); + } + else + { + QStringList message = QStringList() << tr("No suitable images available"); + + if(SETTINGS_GET_BOOL(SETTING_SKIP_TAGGED)) + message.append(tr("Tagged images have been skipped")); + + if(SETTINGS_GET_BOOL(SETTING_MOVE_UNTAGGED)) + message.append(tr("Untagged images have been moved into \"negatives\" subdirectory")); + + Utils::message(message.join(". "), this); + + ui->imageView->reset(); + ui->labelImageNumber->setText(QString("- / %1").arg(m_imageFiles.size())); + ui->labelImageFile->clear(); + ui->labelCurrentImageInfo->clear(); + ui->labelZoom->clear(); + } +} + +void MainWindow::updateImagePlatesInformationFromIndex() +{ + if(m_currentIndex < 0 || m_currentIndex >= m_imageFiles.size()) + { + ui->labelCurrentImageInfo->clear(); + return; + } + + ui->labelCurrentImageInfo->setText(m_imageFiles[m_currentIndex].plates().isEmpty() + ? tr("Untagged: Drag a box around a license plate to tag it") + : tr("Number of tagged license plates: %1") + .arg(m_imageFiles[m_currentIndex].plates().size())); +} + +void MainWindow::updateTaggedAndPlatesInformation() +{ + int tagged = 0; + int plates = 0; + + foreach(const ImageFile &imageFile, m_imageFiles) + { + tagged += !imageFile.plates().isEmpty(); + plates += imageFile.plates().size(); + } + + ui->labelImagesTagged->setNum(tagged); + ui->labelTotalPlates->setNum(plates); +} + +void MainWindow::moveFileFromCurrentIndex() +{ + // return if: + // - moving disabled + // - invalid index + // - currently selected image has plates + if(!SETTINGS_GET_BOOL(SETTING_MOVE_UNTAGGED) + || m_currentIndex < 0 + || m_currentIndex >= m_imageFiles.size() + || !m_imageFiles.at(m_currentIndex).plates().isEmpty()) + return; + + const QFileInfo &imageFileInfo = m_imageFiles.at(m_currentIndex).fileInfo(); + + qDebug("Move '%s'", qPrintable(imageFileInfo.fileName())); + + QDir negatives(imageFileInfo.canonicalPath()); + + if(!negatives.mkpath("negatives")) + { + Utils::error(tr("Cannot create \"negatives\" subdirectory"), this); + return; + } + + negatives.cd("negatives"); + + const QString &target = negatives.absoluteFilePath(imageFileInfo.fileName()); + + if(QFile::exists(target) && !QFile::remove(target)) + { + Utils::error(tr("Cannot remove the file \"%1\" in \"negatives\" subdirectory").arg(imageFileInfo.fileName()), this); + return; + } + + if(!QFile::rename(imageFileInfo.canonicalFilePath(), target)) + { + Utils::error(tr("Cannot move the file \"%1\" into \"negatives\" subdirectory").arg(imageFileInfo.fileName()), this); + return; + } + + m_imageFiles[m_currentIndex].setMoved(true); +} + +void MainWindow::slotOpenDirectory() +{ + qDebug("Open directory"); + + ImageFileList list; + + while(true) + { + const QString &dir = QFileDialog::getExistingDirectory(this, + tr("Select a directory"), + SETTINGS_GET_STRING(SETTING_LAST_DIRECTORY)); + + if(dir.isEmpty()) + { + if(QMessageBox::question(this, + QString(), + tr("No directory has been selected. Whould you like to try again?")) == QMessageBox::Yes) + continue; + else + { + // in auto-open mode quit immediately + if(m_firstRun) + close(); + + return; + } + } + + QFileInfo dirInfo(dir); + + SETTINGS_SET_STRING(SETTING_LAST_DIRECTORY, dir); + + if(!dirInfo.isWritable()) + { + Utils::warning(tr("The selected directory is not writable. Please select another directory")); + continue; + } + + qDebug("Loading directory '%s'", qPrintable(dir)); + + DirectoryLoader loader(dir, this); + + if(loader.exec() != DirectoryLoader::Accepted) + { + Utils::warning(tr("The selected directory doesn't contain JPEG or PNG images. Please select another directory")); + continue; + } + + list = loader.imageFiles(); + m_firstRun = false; + + break; + } + + reset(); + + m_imageFiles = list; + + qDebug("Loaded files: %d", m_imageFiles.size()); + + updateTaggedAndPlatesInformation(); + + // load the next available image + slotNext(); +} + +void MainWindow::slotQuit() +{ + close(); +} + +void MainWindow::slotOptions() +{ + qDebug("Options"); + + Options options(this); + options.exec(); +} + +void MainWindow::slotUsage() +{ + qDebug("Usage"); + + Utils::help(tr("Drag a box around each plate in the image to tag it. " + "If the image has no plates, click Next to skip it. " + "If the option \"Move images without plates\" is selected, the image will be moved into \"negatives\" subdirectory.

" + "Hotkeys:" + "
    " + "
  • %1 - Previous" + "
  • %2 - Next" + "
  • %3 - Jump To..." + "
") + .arg(ui->pushPrevious->shortcut().toString()) + .arg(ui->pushNext->shortcut().toString()) + .arg(ui->pushJumpTo->shortcut().toString()) + , this); +} + +void MainWindow::slotAbout() +{ + About about(this); + about.exec(); +} + +void MainWindow::slotAboutQt() +{ + QMessageBox::aboutQt(this); +} + +void MainWindow::slotPrevious() +{ + qDebug("Previous"); + + // nothing to load + if(m_currentIndex <= 0 || ui->imageView->busy()) + return; + + do { + m_currentIndex--; + }while(m_currentIndex >= 0 && m_imageFiles.at(m_currentIndex).moved()); + + updateImageInformationFromIndex(); +} + +void MainWindow::slotJumpTo() +{ + qDebug("Jump to"); + + if(ui->imageView->busy()) + return; + + bool ok; + int index = QInputDialog::getInt(this, + tr("Jump To"), + tr("Image number:"), + m_currentIndex+1, // current value + 1, // min + m_imageFiles.size(), // max + 1, + &ok); + + if(ok) + { + int newIndex = index-1; + + if(m_imageFiles.at(newIndex).moved()) + { + Utils::message(tr("The specified image has been moved into \"negatives\" subdirectory and no longer available"), this); + return; + } + + m_currentIndex = newIndex; + updateImageInformationFromIndex(); + } +} + +void MainWindow::slotNext() +{ + qDebug("Next"); + + // nothing to load + if(m_currentIndex >= m_imageFiles.size() || ui->imageView->busy()) + { + qDebug("Nothing to load"); + return; + } + + // move if necessary + moveFileFromCurrentIndex(); + + // last image and it has not been moved + if(m_currentIndex == m_imageFiles.size()-1 && !m_imageFiles.at(m_currentIndex).moved()) + return; + + // not last or moved + do { + m_currentIndex++; + }while(m_currentIndex < m_imageFiles.size() + && (m_imageFiles.at(m_currentIndex).moved() + || (SETTINGS_GET_BOOL(SETTING_SKIP_TAGGED) && !m_imageFiles.at(m_currentIndex).plates().isEmpty())) + ); + + updateImageInformationFromIndex(); +} + +void MainWindow::slotZoomChanged(double zoom) +{ + ui->labelZoom->setText(QString("%1x (%2%)") + .arg(zoom, 0, 'f', 1) + .arg(zoom * 100, 0, 'f', 1)); +} + +void MainWindow::slotPlatesChanged() +{ + updateImagePlatesInformationFromIndex(); + updateTaggedAndPlatesInformation(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..c9eb0e5 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,73 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +#include "imagefile.h" + +class QImage; + +namespace Ui +{ + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + /* + * Reset all labels + */ + void reset(); + + /* + * Update labels based on the currently selected image + */ + void updateImageInformationFromIndex(); + void updateImagePlatesInformationFromIndex(); + + /* + * Update labels with a number of tagged images + */ + void updateTaggedAndPlatesInformation(); + + /* + * Move the currently selected image file to negatives + * if necessary + */ + void moveFileFromCurrentIndex(); + +private slots: + // File menu + void slotOpenDirectory(); + void slotQuit(); + + // Edit menu + void slotOptions(); + + // Help menu + void slotUsage(); + void slotAbout(); + void slotAboutQt(); + + // other slots + void slotPrevious(); + void slotJumpTo(); + void slotNext(); + void slotZoomChanged(double zoom); + void slotPlatesChanged(); + +private: + Ui::MainWindow *ui; + ImageFileList m_imageFiles; + int m_currentIndex; + bool m_firstRun; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..3ca93a2 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,501 @@ + + + MainWindow + + + + 0 + 0 + 800 + 550 + + + + + + 10 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + 1 + 0 + + + + + 75 + true + + + + Images tagged: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 75 + true + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 75 + true + + + + Total plates: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 75 + true + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Previous + + + P + + + + + + + Jump To... + + + J + + + + + + + Next + + + N + + + + + + + + + + + + 75 + true + + + + Image + + + + + + + + 1 + 0 + + + + + 75 + true + + + + + + + + + 75 + true + + + + + + + + + + + + + + 0 + 1 + + + + + + + + + + + 1 + 0 + + + + + 75 + true + + + + + + + + Zoom: + + + + + + + + + + + + + + 0 + 0 + 800 + 21 + + + + + File + + + + + + + + Edit + + + + + + Help + + + + + + + + + + + + + Open directory... + + + + + Preferences... + + + + + About... + + + + + Quit + + + + + Usage... + + + + + About Qt... + + + + + + + ImageView + QWidget +
imageview.h
+ 1 +
+ + SqueezedLabel + QLabel +
squeezedlabel.h
+
+
+ + + + actionOpen_directory + triggered() + MainWindow + slotOpenDirectory() + + + -1 + -1 + + + 199 + 149 + + + + + actionOptions + triggered() + MainWindow + slotOptions() + + + -1 + -1 + + + 199 + 149 + + + + + actionAbout + triggered() + MainWindow + slotAbout() + + + -1 + -1 + + + 320 + 207 + + + + + pushPrevious + clicked() + MainWindow + slotPrevious() + + + 356 + 68 + + + 223 + 466 + + + + + pushJumpTo + clicked() + MainWindow + slotJumpTo() + + + 437 + 68 + + + 306 + 437 + + + + + pushNext + clicked() + MainWindow + slotNext() + + + 518 + 68 + + + 384 + 490 + + + + + actionQuit + triggered() + MainWindow + slotQuit() + + + -1 + -1 + + + 338 + 210 + + + + + actionUsage + triggered() + MainWindow + slotUsage() + + + -1 + -1 + + + 338 + 210 + + + + + actionAbout_Qt + triggered() + MainWindow + slotAboutQt() + + + -1 + -1 + + + 399 + 274 + + + + + + slotOpenDirectory() + slotOptions() + slotAbout() + slotPrevious() + slotJumpTo() + slotNext() + slotQuit() + slotUsage() + slotAboutQt() + +
diff --git a/man/plates.1 b/man/plates.1 new file mode 100644 index 0000000..dc2b8fd --- /dev/null +++ b/man/plates.1 @@ -0,0 +1,21 @@ +.TH PLATES 1 "APRIL 7, 2017" +.\" Please adjust this date whenever revising the manpage. +.SH NAME +plates \- an OpenALPR training utility to manually find and mark license plates on photos. +.SH SYNOPSIS +.B plates +.br +.SH DESCRIPTION +OpenALPR is an open source Automatic License Plate Recognition library written +in C++ with bindings in C#, Java, Node.js, Go, and Python. The library analyzes +images and video streams to identify license plates. The output is the text +representation of any license plate characters. + +The purpose of this applications is to train OpenALPR to find license plates. + +See https://github.com/openalpr/openalpr for more. +.SH AUTHOR +plates was written by Dmitry Baryshev. +.PP +This manual page was written by Dmitry Baryshev , +for the Debian project (but may be used by others). diff --git a/options.cpp b/options.cpp new file mode 100644 index 0000000..3d5fcd8 --- /dev/null +++ b/options.cpp @@ -0,0 +1,29 @@ +#include "settings.h" +#include "options.h" +#include "ui_options.h" + +Options::Options(QWidget *parent) + : QDialog(parent) + , ui(new Ui::Options) +{ + ui->setupUi(this); + + // load settings + ui->checkSkipTagged->setChecked(SETTINGS_GET_BOOL(SETTING_SKIP_TAGGED)); + ui->checkMove->setChecked(SETTINGS_GET_BOOL(SETTING_MOVE_UNTAGGED)); + ui->checkTooltips->setChecked(SETTINGS_GET_BOOL(SETTING_TOOLTIPS)); +} + +Options::~Options() +{ + delete ui; +} + +void Options::slotAccept() +{ + SETTINGS_SET_BOOL(SETTING_SKIP_TAGGED, ui->checkSkipTagged->isChecked(), Settings::NoSync); + SETTINGS_SET_BOOL(SETTING_MOVE_UNTAGGED, ui->checkMove->isChecked(), Settings::NoSync); + SETTINGS_SET_BOOL(SETTING_TOOLTIPS, ui->checkTooltips->isChecked()); + + accept(); +} diff --git a/options.h b/options.h new file mode 100644 index 0000000..f84eac1 --- /dev/null +++ b/options.h @@ -0,0 +1,26 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +#include + +namespace Ui +{ + class Options; +} + +class Options : public QDialog +{ + Q_OBJECT + +public: + explicit Options(QWidget *parent = 0); + ~Options(); + +private slots: + void slotAccept(); + +private: + Ui::Options *ui; +}; + +#endif // OPTIONS_H diff --git a/options.ui b/options.ui new file mode 100644 index 0000000..fcab210 --- /dev/null +++ b/options.ui @@ -0,0 +1,101 @@ + + + Options + + + + 0 + 0 + 316 + 116 + + + + Preferences + + + + + + Qt::Vertical + + + + 20 + 4 + + + + + + + + Skip tagged images + + + + + + + Move images without plates into "negatives" subdirectory + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Show tooltips with a license plate information + + + + + + + + + buttonBox + accepted() + Options + slotAccept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Options + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + slotAccept() + + diff --git a/platefile.cpp b/platefile.cpp new file mode 100644 index 0000000..ce401ca --- /dev/null +++ b/platefile.cpp @@ -0,0 +1,204 @@ +#include + +#include "platefile.h" + +PlateFile::PlateFile(const QString &platePathTemplate, int index) + : m_plateFileTemplate(platePathTemplate) + , m_plateFile(platePathTemplate + QString("-%1.yaml").arg(index)) + , m_imageWidth(0) + , m_imageHeight(0) + , m_plateInverted(false) +{ + // very simple YAML parser + QFile file(m_plateFile); + + if(!file.open(QFile::ReadOnly)) + return; + + QByteArray line; + + while(!file.atEnd()) + { + line = file.readLine(); + + // nothing to parse + if(line.isEmpty()) + continue; + + QTextStream ts(&line, QIODevice::ReadOnly); + ts.setCodec("UTF-8"); + + QByteArray key; + + ts >> key; + + if(key == "image_file:") + {} // nothing, we set image_file explicitely when saving + else if(key == "image_width:") + ts >> m_imageWidth; + else if(key == "image_height:") + ts >> m_imageHeight; + else if(key == "region_code_gt:") + ts >> m_regionCode; + else if(key == "plate_number_gt:") + { + QString text; + ts >> text; + + qDebug() << text; + + // literal multiline + if(text == "|") + { + while(!file.atEnd()) + { + line = file.readLine().trimmed(); + + if(line.isEmpty()) + break; + + m_plateNumber.append(QString::fromUtf8(line) + '\n'); + } + + // cut the last \n + m_plateNumber.chop(1); + } + else + m_plateNumber = text; + } + else if(key == "plate_corners_gt:") + { + int x, y; + QPolygon polygon; + + while(true) + { + ts.skipWhiteSpace(); + + if(ts.atEnd()) + break; + + ts >> x >> y; + polygon.append(QPoint(x, y)); + } + + if(polygon.size() == 4) + m_plateCorners = polygon; + else + qWarning("Polygon in file '%s' has only %d point(s)", qPrintable(m_plateFile), polygon.size()); + } + else if(key == "plate_inverted_gt:") + { + QString boolString; + ts >> boolString; + m_plateInverted = (boolString == "true"); + } + } +} + +bool PlateFile::writeToFile(int index, QString *error) const +{ + // very simple YAML writer + QFile file(m_plateFileTemplate + QString("-%1.yaml").arg(index)); + + if(!file.open(QFile::WriteOnly | QFile::Truncate)) + { + if(error) + *error = file.errorString(); + + return false; + } + + QTextStream ts(&file); + + ts.setCodec("UTF-8"); + + QStringList cornersStringList; + + cornersStringList.reserve(m_plateCorners.size() * 2); + + foreach(const QPoint &point, m_plateCorners) + { + cornersStringList.append(QString::number(point.x())); + cornersStringList.append(QString::number(point.y())); + } + + ts + << "image_file: " << m_imageFile << endl + << "image_width: " << m_imageWidth << endl + << "image_height: " << m_imageHeight << endl; + + // don't save an empty region code + if(!m_regionCode.isEmpty()) + ts << "region_code_gt: " << m_regionCode << endl; + + // write with new lines or not? + if(m_plateNumber.contains('\n')) + { + /* + * literal style: + * + * plate_number_gt: | + * plate number + * with + * new lines + * + */ + const QStringList &lines = m_plateNumber.split('\n'); + + ts << "plate_number_gt: " << "|" << endl; + + // write line-by-line and ignore empty lines + foreach(const QString &line, lines) + { + ts << ' ' << line << endl; + } + + ts << endl; + } + else + ts << "plate_number_gt: " << m_plateNumber << endl; + + ts + << "plate_corners_gt: " << cornersStringList.join(" ") << endl + << "plate_inverted_gt: " << (m_plateInverted ? "true" : "false") << endl + ; + + ts.flush(); + + if(ts.status() != QTextStream::Ok) + { + if(error) + *error = QObject::tr("Cannot write into a file"); + + return false; + } + + return true; +} + +bool PlateFile::exists() +{ + return QFileInfo(m_plateFile).exists(); +} + +PlateFileList PlateFile::fromImageFile(const QString &fileTemplate) +{ + PlateFileList plateList; + + int index = 0; + + // find associated car-nnn.yaml files + while(true) + { + PlateFile plateFile(fileTemplate, index++); + + if(!plateFile.exists()) + break; + + if(plateFile.isValid()) + plateList.append(plateFile); + } + + return plateList; +} diff --git a/platefile.h b/platefile.h new file mode 100644 index 0000000..4bda381 --- /dev/null +++ b/platefile.h @@ -0,0 +1,164 @@ +#ifndef PLATEFILE_H +#define PLATEFILE_H + +#include +#include +#include + +class QTextStream; +class QFileInfo; + +class PlateFile; +using PlateFileList = QList; + +class PlateFile +{ +public: + PlateFile(const QString &platePathTemplate, int index); + + /* + * Overwrite a plate file with index 'index' + */ + bool writeToFile(int index, QString *error = nullptr) const; + + /* + * Plate file template like "/data/car" from "/data/car.jpg" + */ + QString plateFileTemplate() const; + + /* + * Check is the corresponding plate file exists + */ + bool exists(); + + /* + * PlateFile is valid when it has + * - an image path set + * - valid image dimensions + * - valid plate corners + */ + bool isValid() const; + + /* + * Some getters/setters + */ + void setImageFile(const QString &imageFile); + void setImageWidth(int imageWidth); + void setImageHeight(int imageHeight); + + QString regionCode() const; + void setRegionCode(const QString ®ionCode); + + QString plateNumber() const; + void setPlateNumber(const QString &plateNumber); + + QPolygon plateCorners() const; + void setPlateCorners(const QPolygon &plateCorners); + + bool plateInverted() const; + void setPlateInverted(bool plateInverted); + + /* + * Get a list of associated plates for the specified image file + * + * If an image file is "car.jpg", it will look for + * + * car-0.yaml + * car-1.yaml + * etc. + */ + static PlateFileList fromImageFile(const QString &fileTemplate); + +private: + QString m_plateFileTemplate; + QString m_plateFile; + QString m_imageFile; + int m_imageWidth; + int m_imageHeight; + QString m_regionCode; + QString m_plateNumber; + QPolygon m_plateCorners; + bool m_plateInverted; +}; + +inline +QString PlateFile::plateFileTemplate() const +{ + return m_plateFileTemplate; +} + +inline +bool PlateFile::isValid() const +{ + return m_imageWidth > 0 + && m_imageHeight > 0 + && !m_plateCorners.isEmpty(); +} + +inline +void PlateFile::setImageFile(const QString &imageFile) +{ + m_imageFile = imageFile; +} + +inline +void PlateFile::setImageWidth(int imageWidth) +{ + m_imageWidth = imageWidth; +} + +inline +void PlateFile::setImageHeight(int imageHeight) +{ + m_imageHeight = imageHeight; +} + +inline +QString PlateFile::regionCode() const +{ + return m_regionCode; +} + +inline +void PlateFile::setRegionCode(const QString ®ionCode) +{ + m_regionCode = regionCode; +} + +inline +QString PlateFile::plateNumber() const +{ + return m_plateNumber; +} + +inline +void PlateFile::setPlateNumber(const QString &plateNumber) +{ + m_plateNumber = plateNumber; +} + +inline +QPolygon PlateFile::plateCorners() const +{ + return m_plateCorners; +} + +inline +void PlateFile::setPlateCorners(const QPolygon &plateCorners) +{ + m_plateCorners = plateCorners; +} + +inline +bool PlateFile::plateInverted() const +{ + return m_plateInverted; +} + +inline +void PlateFile::setPlateInverted(bool plateInverted) +{ + m_plateInverted = plateInverted; +} + +#endif // PLATEFILE_H diff --git a/plates.desktop b/plates.desktop new file mode 100644 index 0000000..2548a63 --- /dev/null +++ b/plates.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Categories=Qt;Education; +Exec=plates +Icon=plates +Terminal=false +Name=OpenALPR training utility +Comment=OpenALPR training utility diff --git a/plates.iss b/plates.iss new file mode 100644 index 0000000..d8aaabc --- /dev/null +++ b/plates.iss @@ -0,0 +1,105 @@ +#define MyAppName "OpenALPR Training Utility" +#define MyAppPublisher "OpenALPR" +#define MyAppVersion "1.0.0" +#define MyAppTemplate "plates" +#define MyAppExecutable MyAppTemplate + ".exe" + +[Setup] +AppId={{2B5FE8FF-0856-4433-9FBD-57318237B5D4} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +AppPublisher={#MyAppPublisher} +DefaultDirName={pf}\{#MyAppName} +DefaultGroupName={#MyAppName} +OutputDir=. +OutputBaseFilename={#MyAppTemplate}-setup-{#MyAppVersion} +SetupIconFile={#MyAppTemplate}.ico +Compression=lzma +SolidCompression=yes +UninstallDisplayName={#MyAppName} +UninstallDisplayIcon={app}\{#MyAppExecutable} +MinVersion=0,5.1 + +[Languages] +Name: "AAAenglish"; MessagesFile: "compiler:Default.isl" +Name: "BrazilianPortuguesexisl"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl" +Name: "Catalanxisl"; MessagesFile: "compiler:Languages\Catalan.isl" +Name: "Corsicanxisl"; MessagesFile: "compiler:Languages\Corsican.isl" +Name: "Czechxisl"; MessagesFile: "compiler:Languages\Czech.isl" +Name: "Danishxisl"; MessagesFile: "compiler:Languages\Danish.isl" +Name: "Dutchxisl"; MessagesFile: "compiler:Languages\Dutch.isl" +Name: "Finnishxisl"; MessagesFile: "compiler:Languages\Finnish.isl" +Name: "Frenchxisl"; MessagesFile: "compiler:Languages\French.isl" +Name: "Germanxisl"; MessagesFile: "compiler:Languages\German.isl" +Name: "Greekxisl"; MessagesFile: "compiler:Languages\Greek.isl" +Name: "Hebrewxisl"; MessagesFile: "compiler:Languages\Hebrew.isl" +Name: "Hungarianxisl"; MessagesFile: "compiler:Languages\Hungarian.isl" +Name: "Italianxisl"; MessagesFile: "compiler:Languages\Italian.isl" +Name: "Japanesexisl"; MessagesFile: "compiler:Languages\Japanese.isl" +Name: "Norwegianxisl"; MessagesFile: "compiler:Languages\Norwegian.isl" +Name: "Polishxisl"; MessagesFile: "compiler:Languages\Polish.isl" +Name: "Portuguesexisl"; MessagesFile: "compiler:Languages\Portuguese.isl" +Name: "Russianxisl"; MessagesFile: "compiler:Languages\Russian.isl" +Name: "ScottishGaelicxisl"; MessagesFile: "compiler:Languages\ScottishGaelic.isl" +Name: "SerbianCyrillicxisl"; MessagesFile: "compiler:Languages\SerbianCyrillic.isl" +Name: "SerbianLatinxisl"; MessagesFile: "compiler:Languages\SerbianLatin.isl" +Name: "Slovenianxisl"; MessagesFile: "compiler:Languages\Slovenian.isl" +Name: "Spanishxisl"; MessagesFile: "compiler:Languages\Spanish.isl" +Name: "Turkishxisl"; MessagesFile: "compiler:Languages\Turkish.isl" +Name: "Ukrainianxisl"; MessagesFile: "compiler:Languages\Ukrainian.isl" + +[Files] +Source: "COPYING-runtime.txt"; DestDir: "{app}"; Flags: ignoreversion +Source: "COPYING-winpthreads.txt"; DestDir: "{app}"; Flags: ignoreversion +Source: "libgcc_s_dw2-1.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "libstdc++-6.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "libwinpthread-1.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "Qt5Core.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "Qt5Gui.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "Qt5Widgets.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "imageformats/qico.dll"; DestDir: "{app}/imageformats"; Flags: ignoreversion +Source: "imageformats/qjpeg.dll"; DestDir: "{app}/imageformats"; Flags: ignoreversion +Source: "platforms/qwindows.dll"; DestDir: "{app}/platforms"; Flags: ignoreversion +Source: "{#MyAppExecutable}"; DestDir: "{app}"; Flags: ignoreversion + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" +Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; OnlyBelowVersion: 0,6.1 + +[Run] +Filename: {app}\{#MyAppExecutable}; Description: {cm:LaunchProgram,{#MyAppName}}; Flags: nowait postinstall skipifsilent + +[Icons] +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExecutable}" +Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExecutable}"; Tasks: desktopicon +Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExecutable}"; Tasks: quicklaunchicon + +[Code] +procedure CurStepChanged(CurStep: TSetupStep); +var +ResultCode: Integer; +Uninstall: String; +UninstallQuery : String; +begin +UninstallQuery := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); + if (CurStep = ssInstall) then begin + if RegQueryStringValue(HKLM, UninstallQuery, 'UninstallString', Uninstall) + or RegQueryStringValue(HKCU, UninstallQuery, 'UninstallString', Uninstall) then begin + Uninstall := RemoveQuotes(Uninstall) + if (FileExists(Uninstall)) AND (not Exec(RemoveQuotes(Uninstall), '/VERYSILENT', '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode)) then begin + MsgBox(SysErrorMessage(ResultCode), mbCriticalError, MB_OK); + Abort(); + end; + end; + end; +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); + var ErrorCode: Integer; +begin + if (CurUninstallStep = usUninstall) then begin + ShellExec('open','taskkill.exe','/f /im {#MyAppExecutable}','',SW_HIDE,ewNoWait,ErrorCode); + ShellExec('open','tskill.exe',' {#MyAppName}','',SW_HIDE,ewNoWait,ErrorCode); + end; +end; diff --git a/plates.pro b/plates.pro new file mode 100644 index 0000000..2dc1c9a --- /dev/null +++ b/plates.pro @@ -0,0 +1,93 @@ +TEMPLATE = app +TARGET = plates +TARGET_HUMAN = OpenALPR Training Utility + +QT += core gui widgets +CONFIG += c++11 + +DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050000 + +VERSION_MAJOR=1 +VERSION_MINOR=0 +VERSION_PATCH=0 + +VERSION=$$sprintf("%1.%2.%3", $$VERSION_MAJOR, $$VERSION_MINOR, $$VERSION_PATCH) + +DEFINES += VERSION_MAJOR=$$VERSION_MAJOR +DEFINES += VERSION_MINOR=$$VERSION_MINOR +DEFINES += VERSION_PATCH=$$VERSION_PATCH +DEFINES += VERSION_STRING=$$sprintf("\"\\\"%1\\\"\"", $$VERSION) + +DEFINES += TARGET=$$TARGET +DEFINES += TARGET_STRING=$$sprintf("\"\\\"%1\\\"\"", $$TARGET) +UP=$$sprintf("%1", $$TARGET) +DEFINES += TARGET_UPPER_STRING=$$sprintf("\"\\\"%1\\\"\"", $$upper($$UP)) +DEFINES += TARGET_HUMAN_STRING=$$sprintf("\"\\\"%1\\\"\"", $$TARGET_HUMAN) + +win32 { + RC_FILE = $${TARGET}.rc +} else:macx { + QMAKE_INFO_PLIST = Info.plist + ICON = images/$${TARGET}.icns +} + +######################################## + +INCLUDEPATH += . qprogressindicator +DEPENDPATH += . qprogressindicator + +SOURCES += main.cpp \ + mainwindow.cpp \ + settings.cpp \ + utils.cpp \ + qprogressindicator/QProgressIndicator.cpp \ + directoryloader.cpp \ + about.cpp \ + imagefile.cpp \ + options.cpp \ + platefile.cpp \ + threadedimageloader.cpp \ + imageview.cpp \ + squeezedlabel.cpp \ + polygons.cpp \ + selection.cpp \ + imageviewerbase.cpp \ + imageviewerobserver.cpp \ + plateselector.cpp \ + imageviewerplateselector.cpp \ + dot.cpp + +HEADERS += mainwindow.h \ + settings.h \ + utils.h \ + qprogressindicator/QProgressIndicator.h \ + directoryloader.h \ + about.h \ + imagefile.h \ + options.h \ + platefile.h \ + threadedimageloader.h \ + imageview.h \ + squeezedlabel.h \ + polygons.h \ + selection.h \ + imageviewerbase.h \ + imageviewerobserver.h \ + plateselector.h \ + imageviewerplateselector.h \ + dot.h + +FORMS += mainwindow.ui \ + directoryloader.ui \ + about.ui \ + options.ui \ + imageview.ui \ + plateselector.ui + +RESOURCES += \ + $${TARGET}.qrc + +DISTFILES += \ + other/spec.txt \ + README.txt diff --git a/plates.qrc b/plates.qrc new file mode 100644 index 0000000..3691c7a --- /dev/null +++ b/plates.qrc @@ -0,0 +1,5 @@ + + + images/plates.ico + + diff --git a/plates.rc b/plates.rc new file mode 100644 index 0000000..f9eed95 --- /dev/null +++ b/plates.rc @@ -0,0 +1,42 @@ +#include +#include +#include + +#define stringify(v1) #v1 +#define quote(v1) stringify(v1) + +#define NVERSION VERSION_MAJOR.VERSION_MINOR.VERSION_PATCH + +STRINGTABLE LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +BEGIN + 1 quote(TARGET_UPPER)quote(TARGET)"\0" +END + +IDI_ICON1 ICON DISCARDABLE "images/plates.ico" + +1 VERSIONINFO + FILEVERSION VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, 0 + PRODUCTVERSION VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, 0 + FILEOS VOS_UNKNOWN + FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "Comments", quote(TARGET) "" + VALUE "CompanyName", "OpenALPR" + VALUE "FileDescription", quote(TARGET) + VALUE "FileVersion", quote(NVERSION) + VALUE "InternalName", quote(TARGET) + VALUE "LegalCopyright", "(C) 2017 OpenALPR" + VALUE "ProductName", quote(TARGET) + VALUE "ProductVersion", quote(NVERSION) + VALUE "Builder", "OpenALPR" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/plateselector.cpp b/plateselector.cpp new file mode 100644 index 0000000..bfab307 --- /dev/null +++ b/plateselector.cpp @@ -0,0 +1,171 @@ +#include +#include + +#include "plateselector.h" +#include "platefile.h" +#include "utils.h" +#include "ui_plateselector.h" + +PlateSelector::PlateSelector(const QImage &image, QWidget *parent) + : QDialog(parent) + , ui(new Ui::PlateSelector) +{ + ui->setupUi(this); + + setWindowFlags(windowFlags() | Qt::CustomizeWindowHint | Qt::WindowMaximizeButtonHint); + + ui->imageViewerPlateSelector->setImage(image); + + ui->plainTextPlateNumber->ensurePolished(); + ui->plainTextPlateNumber->setFixedHeight( + (ui->plainTextPlateNumber->document()->documentMargin() + + QFontMetrics(ui->plainTextPlateNumber->font()).lineSpacing()) * 2); + + ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); + ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Reset corners")); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Delete && Save")); + + // shortcuts + ui->buttonBox->button(QDialogButtonBox::Reset)->setShortcut(Qt::CTRL+Qt::Key_Q); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setShortcut(Qt::CTRL+Qt::Key_D); + ui->buttonBox->button(QDialogButtonBox::Save)->setShortcut(Qt::CTRL+Qt::Key_S); + ui->buttonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents); + + m_shortCutPlateNumber = new QShortcut(Qt::CTRL+Qt::Key_N, ui->plainTextPlateNumber, SLOT(setFocus())); + m_shortCutPlateRegion = new QShortcut(Qt::CTRL+Qt::Key_R, ui->linePlateRegion, SLOT(setFocus())); + m_shortCutLightOnDark = new QShortcut(Qt::CTRL+Qt::Key_L, ui->checkLightOnDark, SLOT(toggle())); + + // set tooltips + Utils::setShortcutTooltips(QList() + << ui->buttonBox->button(QDialogButtonBox::Reset) + << ui->buttonBox->button(QDialogButtonBox::RestoreDefaults) + << ui->buttonBox->button(QDialogButtonBox::Save) + << ui->buttonBox->button(QDialogButtonBox::Help)); + + connect(ui->imageViewerPlateSelector, SIGNAL(dotsChanged(int)), this, SLOT(slotCheckData())); +} + +PlateSelector::~PlateSelector() +{ + delete ui; +} + +void PlateSelector::setPlateFile(const PlateFile &plateFile) +{ + qDebug() << "Existing polygon:" << plateFile.plateCorners(); + + foreach(const QPoint &point, plateFile.plateCorners()) + { + ui->imageViewerPlateSelector->addDotInImageCoordinates(point - m_selection.topLeft()); + } + + ui->plainTextPlateNumber->setPlainText(plateFile.plateNumber()); + ui->linePlateRegion->setText(plateFile.regionCode()); + ui->checkLightOnDark->setChecked(plateFile.plateInverted()); +} + +QString PlateSelector::plateNumber() const +{ + QString result; + const QStringList plainLines = ui->plainTextPlateNumber->toPlainText().trimmed().split('\n', QString::SkipEmptyParts); + + foreach(QString plainLine, plainLines) + { + plainLine = plainLine.trimmed(); + + if(!plainLine.isEmpty()) + result += plainLine + '\n'; + } + + // cut the last '\n' + result.chop(1); + + return result; +} + +QString PlateSelector::plateRegion() const +{ + return ui->linePlateRegion->text().trimmed(); +} + +bool PlateSelector::lightOnDark() const +{ + return ui->checkLightOnDark->isChecked(); +} + +void PlateSelector::slotCheckData() +{ + ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled( + !plateNumber().isEmpty() + && !ui->imageViewerPlateSelector->selectedPolygon().isEmpty() + ); +} + +void PlateSelector::slotSave() +{ + m_selectedPolygon = ui->imageViewerPlateSelector->selectedPolygon(); + + for(int i = 0;i < m_selectedPolygon.size();i++) + { + m_selectedPolygon[i] += m_selection.topLeft(); + } + + qDebug() << "Selected polygon:" << m_selectedPolygon; + + accept(); +} + +void PlateSelector::slotHelp() +{ + qDebug("Help"); + + Utils::help(tr("Click the four corners of the license plate. " + "The lines should align with the edge of the license plate.

" + "Required fields:" + "
    " + "
  • Plate corners - the four corners of the license plate in the image" + "
  • Plate number - the plate number of the license plate" + "
" + "Optional:" + "
    " + "
  • Plate region - the country/state of issuance (e.g. California)" + "
  • Light-on-Dark letters - the plate background is darker than the letters (e.g. white characters on a dark blue background)" + "
" + "Hotkeys:" + "
    " + "
  • Tab - Move to the next field" + "
  • %1 - Jump to the plate number field" + "
  • %2 - Jump to the plate region field" + "
  • %3 - Toggle Light-on-Dark letters" + "
  • %4 - Reset corners" + "
  • %5 - Delete the corners and save" + "
  • %6 - Save" + "
  • Escape - Cancel" + "
  • %7 - Help" + "
") + .arg(m_shortCutPlateNumber->key().toString()) + .arg(m_shortCutPlateRegion->key().toString()) + .arg(m_shortCutLightOnDark->key().toString()) + .arg(ui->buttonBox->button(QDialogButtonBox::Reset)->shortcut().toString()) + .arg(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->shortcut().toString()) + .arg(ui->buttonBox->button(QDialogButtonBox::Save)->shortcut().toString()) + .arg(ui->buttonBox->button(QDialogButtonBox::Help)->shortcut().toString()) + , this); +} + +void PlateSelector::slotClicked(QAbstractButton *button) +{ + // reset + if(ui->buttonBox->button(QDialogButtonBox::Reset) == button) + { + qDebug("Reset corners"); + ui->imageViewerPlateSelector->clearDots(); + } + // delete + else if(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults) == button) + { + qDebug("Delete plate"); + ui->imageViewerPlateSelector->clearDots(); + accept(); + } +} diff --git a/plateselector.h b/plateselector.h new file mode 100644 index 0000000..412fc8e --- /dev/null +++ b/plateselector.h @@ -0,0 +1,75 @@ +#ifndef PLATESELECTOR_H +#define PLATESELECTOR_H + +#include +#include + +class QAbstractButton; +class QShortcut; + +class PlateFile; + +namespace Ui +{ + class PlateSelector; +} + +/* + * Dialog to select or edit a license plate with four points + */ +class PlateSelector : public QDialog +{ + Q_OBJECT + +public: + explicit PlateSelector(const QImage &image, QWidget *parent = 0); + ~PlateSelector(); + + /* + * Set an existing plate to edit + */ + void setPlateFile(const PlateFile &plateFile); + + /* + * Selection rectangle in the original (full) image. This rectangle is used to re-calculate + * correct relative dot coordinates and pass them into addDotInImageCoordinates() method + */ + void setSelectionRect(const QRect &rect); + + /* + * Getters + */ + QString plateNumber() const; + QString plateRegion() const; + bool lightOnDark() const; + + QPolygon selectedPolygon() const; + +private slots: + void slotCheckData(); + void slotSave(); + void slotHelp(); + void slotClicked(QAbstractButton *button); + +private: + Ui::PlateSelector *ui; + QRect m_selection; + QPolygon m_selectedPolygon; + QShortcut *m_shortCutPlateNumber; + QShortcut *m_shortCutPlateRegion; + QShortcut *m_shortCutLightOnDark; +}; + +inline +void PlateSelector::setSelectionRect(const QRect &rect) +{ + m_selection = rect; +} + +inline +QPolygon PlateSelector::selectedPolygon() const +{ + return m_selectedPolygon; +} + +#endif // PLATESELECTOR_H diff --git a/plateselector.ui b/plateselector.ui new file mode 100644 index 0000000..9b02b8a --- /dev/null +++ b/plateselector.ui @@ -0,0 +1,213 @@ + + + PlateSelector + + + + 0 + 0 + 700 + 500 + + + + License plate selection + + + + 10 + + + + + 6 + + + + + Plate number: + + + + + + + + + + Plate region: + + + + + + + 64 + + + + + + + Light-on-Dark letters + + + + + + + Qt::Horizontal + + + + 0 + 10 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save + + + + + + + QFrame::StyledPanel + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + + + + + + + + + ImageViewerPlateSelector + QWidget +
imageviewerplateselector.h
+ 1 +
+
+ + plainTextPlateNumber + linePlateRegion + checkLightOnDark + + + + + buttonBox + rejected() + PlateSelector + reject() + + + 549 + 490 + + + 541 + 440 + + + + + buttonBox + accepted() + PlateSelector + slotSave() + + + 521 + 490 + + + 503 + 431 + + + + + buttonBox + helpRequested() + PlateSelector + slotHelp() + + + 383 + 490 + + + 366 + 449 + + + + + buttonBox + clicked(QAbstractButton*) + PlateSelector + slotClicked(QAbstractButton*) + + + 202 + 490 + + + 198 + 440 + + + + + plainTextPlateNumber + textChanged() + PlateSelector + slotCheckData() + + + 243 + 387 + + + 252 + 519 + + + + + + slotSave() + slotCheckData() + slotHelp() + slotClicked(QAbstractButton*) + +
diff --git a/polygons.cpp b/polygons.cpp new file mode 100644 index 0000000..647cd3c --- /dev/null +++ b/polygons.cpp @@ -0,0 +1,48 @@ +#include + +#include "polygons.h" +#include "utils.h" + +Polygons::Polygons() +{} + +void Polygons::setZoom(double zoom) +{ + m_scaledPolygons.clear(); + m_scaledPolygons.reserve(m_polygons.size()); + + foreach(QPolygon polygon, m_polygons) + { + for(int i = 0;i < polygon.size();i++) + { + polygon.setPoint(i, polygon.point(i) * zoom); + } + + m_scaledPolygons.append(polygon); + } +} + +void Polygons::draw(QPainter *painter) +{ + if(!painter) + return; + + painter->setPen(Utils::polygonPaintingPen()); + + foreach(const QPolygon &polygon, m_scaledPolygons) + { + painter->drawPolygon(polygon); + } +} + +Polygons Polygons::fromPlates(const PlateFileList &plateFileList) +{ + Polygons polygons; + + foreach(const PlateFile &plateFile, plateFileList) + { + polygons.append(plateFile.plateCorners()); + } + + return polygons; +} diff --git a/polygons.h b/polygons.h new file mode 100644 index 0000000..09066da --- /dev/null +++ b/polygons.h @@ -0,0 +1,57 @@ +#ifndef POLYGONS_H +#define POLYGONS_H + +#include +#include + +#include "platefile.h" + +class QPainter; + +/* + * Class to hold static license plates polygons and draw them + */ +class Polygons +{ +public: + Polygons(); + + /* + * QList proxy methods + */ + void append(const QPolygon &polygon); + void clear(); + + /* + * Scale the currently saved polygons + */ + void setZoom(double zoom); + + /* + * Draw currently saved polygons with the given painter + */ + void draw(QPainter *painter); + + /* + * Constructs a 'Polygons' object from saved license plates + */ + static Polygons fromPlates(const PlateFileList &plateFileList); + +private: + QList m_polygons; + QList m_scaledPolygons; +}; + +inline +void Polygons::append(const QPolygon &polygon) +{ + m_polygons.append(polygon); +} + +inline +void Polygons::clear() +{ + m_polygons.clear(); +} + +#endif // POLYGONS_H diff --git a/qprogressindicator/QProgressIndicator.cpp b/qprogressindicator/QProgressIndicator.cpp new file mode 100644 index 0000000..6b71044 --- /dev/null +++ b/qprogressindicator/QProgressIndicator.cpp @@ -0,0 +1,134 @@ +#include "QProgressIndicator.h" + +#include + +QProgressIndicator::QProgressIndicator(QWidget* parent) + : QWidget(parent), + m_angle(0), + m_timerId(-1), + m_delay(40), + m_displayedWhenStopped(false), + m_color(Qt::black) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setFocusPolicy(Qt::NoFocus); +} + +bool QProgressIndicator::isAnimated () const +{ + return (m_timerId != -1); +} + +void QProgressIndicator::setDisplayedWhenStopped(bool state) +{ + m_displayedWhenStopped = state; + + update(); +} + +bool QProgressIndicator::isDisplayedWhenStopped() const +{ + return m_displayedWhenStopped; +} + +void QProgressIndicator::startAnimation() +{ + m_angle = 0; + + if (m_timerId == -1) + m_timerId = startTimer(m_delay); +} + +void QProgressIndicator::stopAnimation() +{ + if (m_timerId != -1) + killTimer(m_timerId); + + m_timerId = -1; + + update(); +} + +void QProgressIndicator::setAnimationDelay(int delay) +{ + if (m_timerId != -1) + killTimer(m_timerId); + + m_delay = delay; + + if (m_timerId != -1) + m_timerId = startTimer(m_delay); +} + +void QProgressIndicator::setColor(const QColor & color) +{ + m_color = color; + + update(); +} + +QSize QProgressIndicator::sizeHint() const +{ + return QSize(20,20); +} + +int QProgressIndicator::heightForWidth(int w) const +{ + return w; +} + +void QProgressIndicator::timerEvent(QTimerEvent * /*event*/) +{ + m_angle = (m_angle+30)%360; + + update(); +} + +void QProgressIndicator::paintEvent(QPaintEvent * /*event*/) +{ + if (!m_displayedWhenStopped && !isAnimated()) + return; + + int width = qMin(this->width(), this->height()); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + int outerRadius = (width-1)*0.5; + int innerRadius = (width-1)*0.5*0.38; + + int capsuleHeight = outerRadius - innerRadius; + int capsuleWidth = (width > 32 ) ? capsuleHeight *.23 : capsuleHeight *.35; + int capsuleRadius = capsuleWidth/2; + +/* CO-> */ + if (isAnimated()) + { + for (int i = 0; i < 12; ++i) + { + QColor color = m_color; + color.setAlphaF(1.0f - (i/12.0f)); + p.setPen(Qt::NoPen); + p.setBrush(color); + p.save(); + p.translate(rect().center()); + p.rotate(m_angle - i * 30.0f); + p.drawRoundedRect(-capsuleWidth * 0.5, -(innerRadius+capsuleHeight), capsuleWidth, capsuleHeight, capsuleRadius, capsuleRadius); + p.restore(); + } + } + else + for (int i = 0; i < 12; ++i) + { + QColor color = m_color; + color.setAlphaF(0.2F); + p.setPen(Qt::NoPen); + p.setBrush(color); + p.save(); + p.translate(rect().center()); + p.rotate(m_angle - i * 30.0f); + p.drawRoundedRect(-capsuleWidth * 0.5, -(innerRadius+capsuleHeight), capsuleWidth, capsuleHeight, capsuleRadius, capsuleRadius); + p.restore(); + } +/* <-CO */ +} diff --git a/qprogressindicator/QProgressIndicator.h b/qprogressindicator/QProgressIndicator.h new file mode 100644 index 0000000..700db81 --- /dev/null +++ b/qprogressindicator/QProgressIndicator.h @@ -0,0 +1,88 @@ +#ifndef QPROGRESSINDICATOR_H +#define QPROGRESSINDICATOR_H + +#include +#include + +/*! + \class QProgressIndicator + \brief The QProgressIndicator class lets an application display a progress indicator to show that a lengthy task is under way. + + Progress indicators are indeterminate and do nothing more than spin to show that the application is busy. + \sa QProgressBar +*/ +class QProgressIndicator : public QWidget +{ + Q_OBJECT + Q_PROPERTY(int delay READ animationDelay WRITE setAnimationDelay) + Q_PROPERTY(bool displayedWhenStopped READ isDisplayedWhenStopped WRITE setDisplayedWhenStopped) + Q_PROPERTY(QColor color READ color WRITE setColor) +public: + QProgressIndicator(QWidget* parent = 0); + + /*! Returns the delay between animation steps. + \return The number of milliseconds between animation steps. By default, the animation delay is set to 40 milliseconds. + \sa setAnimationDelay + */ + int animationDelay() const { return m_delay; } + + /*! Returns a Boolean value indicating whether the component is currently animated. + \return Animation state. + \sa startAnimation stopAnimation + */ + bool isAnimated () const; + + /*! Returns a Boolean value indicating whether the receiver shows itself even when it is not animating. + \return Return true if the progress indicator shows itself even when it is not animating. By default, it returns false. + \sa setDisplayedWhenStopped + */ + bool isDisplayedWhenStopped() const; + + /*! Returns the color of the component. + \sa setColor + */ + const QColor & color() const { return m_color; } + + virtual QSize sizeHint() const; + int heightForWidth(int w) const; +public slots: + /*! Starts the spin animation. + \sa stopAnimation isAnimated + */ + void startAnimation(); + + /*! Stops the spin animation. + \sa startAnimation isAnimated + */ + void stopAnimation(); + + /*! Sets the delay between animation steps. + Setting the \a delay to a value larger than 40 slows the animation, while setting the \a delay to a smaller value speeds it up. + \param delay The delay, in milliseconds. + \sa animationDelay + */ + void setAnimationDelay(int delay); + + /*! Sets whether the component hides itself when it is not animating. + \param state The animation state. Set false to hide the progress indicator when it is not animating; otherwise true. + \sa isDisplayedWhenStopped + */ + void setDisplayedWhenStopped(bool state); + + /*! Sets the color of the components to the given color. + \sa color + */ + void setColor(const QColor & color); + +protected: + virtual void timerEvent(QTimerEvent * event); + virtual void paintEvent(QPaintEvent * event); +private: + int m_angle; + int m_timerId; + int m_delay; + bool m_displayedWhenStopped; + QColor m_color; +}; + +#endif // QPROGRESSINDICATOR_H diff --git a/selection.cpp b/selection.cpp new file mode 100644 index 0000000..180644a --- /dev/null +++ b/selection.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include "selection.h" +#include "utils.h" + +constexpr int SELECTION_MIN_WIDTH = 6; +constexpr int SELECTION_MIN_HEIGHT = 6; + +Selection::Selection() + : m_hasSelection(false) +{} + +bool Selection::selectionIsFine() const +{ + const QRect &selection = m_selection.normalized(); + return m_hasSelection && selection.width() > SELECTION_MIN_WIDTH && selection.height() > SELECTION_MIN_HEIGHT; +} + +void Selection::reset() +{ + m_hasSelection = false; + m_selection = QRect(); +} + +void Selection::start(const QPoint &pos) +{ + if(!m_referenceRect.contains(pos)) + return; + + m_hasSelection = true; + m_selection = QRect(pos, pos); + + qDebug() << "Starting selection" << m_selection; +} + +void Selection::move(const QPoint &pos) +{ + m_selection.setBottomRight(Utils::fitPoint(m_referenceRect, pos)); +} + +void Selection::draw(QPainter *painter) +{ + if(!painter) + return; + + // selection must be normalized and checked against a minimal size + const QRect &selection = m_selection.normalized(); + + if(selectionIsFine()) + { + painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination); + painter->setPen(Qt::white); + painter->drawRect(selection); + } +} diff --git a/selection.h b/selection.h new file mode 100644 index 0000000..2b66e3a --- /dev/null +++ b/selection.h @@ -0,0 +1,92 @@ +#ifndef SELECTION_H +#define SELECTION_H + +#include + +class QPainter; + +/* + * Rectangular selection painter + */ +class Selection +{ +public: + Selection(); + + /* + * Actual selection rectangle + */ + QRect selection() const; + + /* + * In selection mode + */ + bool hasSelection() const; + + /* + * Has selection and it's enought large + */ + bool selectionIsFine() const; + + /* + * Reset to inital state + */ + void reset(); + + /* + * Relative rectangle + */ + void setReferenceRect(const QRect &rect); + + /* + * Start painting a selection rectangle from the given position + */ + void start(const QPoint &pos); + + /* + * Move the selection rectangle + */ + void move(const QPoint &pos); + + /* + * Stop painting selection + */ + void stop(); + + /* + * Paint selection using the specified painter + */ + void draw(QPainter *painter); + +private: + QRect m_referenceRect; + QRect m_selection; + bool m_hasSelection; +}; + +inline +QRect Selection::selection() const +{ + return m_selection; +} + +inline +bool Selection::hasSelection() const +{ + return m_hasSelection; +} + +inline +void Selection::setReferenceRect(const QRect &rect) +{ + m_referenceRect = rect; +} + +inline +void Selection::stop() +{ + m_hasSelection = false; + m_selection = m_selection.normalized(); +} + +#endif // SELECTION_H diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..688ce85 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,111 @@ +#include + +#include "settings.h" + +class SettingsPrivate +{ +public: + QSettings *settings; + QMap translations; + QHash defaultValues; +}; + +/*******************************************************/ + +Settings::Settings() +{ + d = new SettingsPrivate; + + addDefaultValues(); + + d->settings = new QSettings(QSettings::IniFormat, + QSettings::UserScope, + QCoreApplication::organizationName(), + QCoreApplication::applicationName()); + + qDebug("Configuration file: %s", qPrintable(d->settings->fileName())); + + d->settings->setFallbacksEnabled(false); +} + +void Settings::addDefaultValues() +{ + QHash defaultValues; + + defaultValues.insert(SETTING_SKIP_TAGGED, false); + defaultValues.insert(SETTING_MOVE_UNTAGGED, false); + defaultValues.insert(SETTING_TOOLTIPS, true); + + addDefaultValues(defaultValues); +} + +void Settings::addDefaultValues(const QHash &defaultValues) +{ + QHash::const_iterator itEnd = defaultValues.end(); + + for(QHash::const_iterator it = defaultValues.begin();it != itEnd;++it) + { + d->defaultValues.insert(it.key(), it.value()); + } +} + +void Settings::fillTranslations() +{ + d->translations.insert("en", "English"); + + // http://www.loc.gov/standards/iso639-2/php/code_list.php + // currently nothing to add +} + +Settings::~Settings() +{ + delete d->settings; + delete d; +} + +QVariant Settings::defaultValue(const QString &key) const +{ + return d->defaultValues.value(key); +} + +void Settings::remove(const QString &key, Settings::SyncType sync) +{ + QSettings *s = settings(); + + s->beginGroup("settings"); + s->remove(key); + s->endGroup(); + + if(sync == Sync) + s->sync(); +} + +void Settings::sync() +{ + d->settings->sync(); +} + +QMap Settings::translations() +{ + if(d->translations.isEmpty()) + fillTranslations(); + + return d->translations; +} + +Settings* Settings::instance() +{ + static Settings *inst = new Settings; + + return inst; +} + +QHash& Settings::defaultValues() +{ + return d->defaultValues; +} + +QSettings *Settings::settings() const +{ + return d->settings; +} diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..17dc23b --- /dev/null +++ b/settings.h @@ -0,0 +1,119 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + +#define SETTINGS_CONTAINS Settings::instance()->contains + +#define SETTINGS_GET_BOOL Settings::instance()->value +#define SETTINGS_SET_BOOL Settings::instance()->setValue + +#define SETTINGS_GET_INT Settings::instance()->value +#define SETTINGS_SET_INT Settings::instance()->setValue + +#define SETTINGS_GET_STRING Settings::instance()->value +#define SETTINGS_SET_STRING Settings::instance()->setValue + +#define SETTINGS_REMOVE Settings::instance()->remove + +#define SETTING_LAST_DIRECTORY "last-directory" +#define SETTING_SKIP_TAGGED "skip-tagged" +#define SETTING_MOVE_UNTAGGED "move-untagged" +#define SETTING_TOOLTIPS "tooltips" + +class SettingsPrivate; + +/* + * System settings + */ +class Settings +{ +public: + static Settings* instance(); + + ~Settings(); + + enum SyncType { NoSync, Sync }; + + template + T value(const QString &key); + + template + T value(const QString &key, const T &def); + + template + void setValue(const QString &key, const T &value, SyncType sync = Sync); + + QVariant defaultValue(const QString &key) const; + + /* + * Remove the specified key from the section "settings" + */ + void remove(const QString &key, SyncType sync = Sync); + + void sync(); + + /* + * Available translations, hardcoded + */ + QMap translations(); + +private: + Settings(); + + /* + * Install some default values + */ + void addDefaultValues(); + void addDefaultValues(const QHash &defaultValues); + + void fillTranslations(); + + QHash &defaultValues(); + + QSettings *settings() const; + +private: + SettingsPrivate *d; +}; + +/**********************************/ + +template +T Settings::value(const QString &key) +{ + T def = T(); + QHash::iterator it = defaultValues().find(key); + + if(it != defaultValues().end()) + def = it.value().value(); + + return value(key, def); +} + +template +T Settings::value(const QString &key, const T &def) +{ + QSettings *s = settings(); + + s->beginGroup("settings"); + QVariant value = s->value(key, QVariant::fromValue(def)); + s->endGroup(); + + return value.value(); +} + +template +void Settings::setValue(const QString &key, const T &value, Settings::SyncType sync) +{ + QSettings *s = settings(); + + s->beginGroup("settings"); + s->setValue(key, QVariant::fromValue(value)); + s->endGroup(); + + if(sync == Sync) + s->sync(); +} + +#endif // SETTINGS_H diff --git a/squeezedlabel.cpp b/squeezedlabel.cpp new file mode 100644 index 0000000..c742997 --- /dev/null +++ b/squeezedlabel.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "squeezedlabel.h" + +SqueezedLabel::SqueezedLabel(QWidget *parent) + : QLabel(parent) + , m_metrics(nullptr) +{ + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + setTextFormat(Qt::PlainText); + + ensurePolished(); +} + +SqueezedLabel::~SqueezedLabel() +{ + delete m_metrics; +} + +void SqueezedLabel::setText(const QString &text) +{ + m_text = text; + updateText(); +} + +void SqueezedLabel::updateText() +{ + if(!m_metrics) + m_metrics = new QFontMetrics(font()); + + QLabel::setText(m_metrics->elidedText(m_text, Qt::ElideMiddle, width())); +} + +void SqueezedLabel::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + + updateText(); +} diff --git a/squeezedlabel.h b/squeezedlabel.h new file mode 100644 index 0000000..8633278 --- /dev/null +++ b/squeezedlabel.h @@ -0,0 +1,33 @@ +#ifndef SQUEEZEDLABEL_H +#define SQUEEZEDLABEL_H + +#include + +class QFontMetrics; + +/* + * Label displaying dots if not enough space for all characters like + * long_image_...file.jpg + */ +class SqueezedLabel : public QLabel +{ + Q_OBJECT + +public: + explicit SqueezedLabel(QWidget *parent = 0); + ~SqueezedLabel(); + + void setText(const QString &text); + +private slots: + void updateText(); + +protected: + virtual void resizeEvent(QResizeEvent *event) override; + +private: + QFontMetrics *m_metrics; + QString m_text; +}; + +#endif // SQUEEZEDLABEL_H diff --git a/threadedimageloader.cpp b/threadedimageloader.cpp new file mode 100644 index 0000000..8f9fdfa --- /dev/null +++ b/threadedimageloader.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include "threadedimageloader.h" + +ThreadedImageLoader::ThreadedImageLoader(QObject *parent) + : QThread(parent) +{} + +void ThreadedImageLoader::loadImageFromFile(const QString &imagePath) +{ + m_imagePath = imagePath; + + start(); +} + +void ThreadedImageLoader::run() +{ + emit loaded(QImage(m_imagePath)); +} diff --git a/threadedimageloader.h b/threadedimageloader.h new file mode 100644 index 0000000..5b5fc8e --- /dev/null +++ b/threadedimageloader.h @@ -0,0 +1,32 @@ +#ifndef THREADEDIMAGELOADER_H +#define THREADEDIMAGELOADER_H + +#include +#include + +class QImage; + +/* + * Thread to asynchronously load images. Allocates an image on a heap. Signal receiver + * MUST delete it + */ +class ThreadedImageLoader : public QThread +{ + Q_OBJECT + +public: + ThreadedImageLoader(QObject *parent = nullptr); + + void loadImageFromFile(const QString &imagePath); + +protected: + virtual void run() override; + +signals: + void loaded(const QImage &); + +private: + QString m_imagePath; +}; + +#endif // THREADEDIMAGELOADER_H diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 0000000..09ed4db --- /dev/null +++ b/utils.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +#include "utils.h" + +QString Utils::helpString() +{ + return QObject::tr("Help"); +} + +QString Utils::informationString() +{ + return QObject::tr("Information"); +} + +QString Utils::warningString() +{ + return QObject::tr("Warning"); +} + +QString Utils::errorString() +{ + return QObject::tr("Error"); +} + +QString Utils::fatalErrorString() +{ + return QObject::tr("Fatal error"); +} + +QPoint Utils::centroid(const QPolygon &polygon) +{ + QPoint centroid{0,0}; + + foreach(const auto &point, polygon) + { + centroid.rx() += point.x(); + centroid.ry() += point.y(); + } + + return centroid / polygon.size(); +} + +void Utils::setShortcutTooltips(const QList &buttons) +{ + // set tooltips + foreach(auto *button, buttons) + { + button->setToolTip(QObject::tr("Shortcut: %1").arg(button->shortcut().toString())); + } +} + +QStringList Utils::imageMatchingWildcard() +{ + return QStringList() + << "*.jpg" + << "*.JPG" + << "*.jpeg" + << "*.JPEG" + << "*.png" + << "*.PNG" + ; +} + +QPoint Utils::fitPoint(const QRect &m_referenceRect, const QPoint &_pos) +{ + QPoint pos = _pos; + + if(pos.x() > m_referenceRect.topRight().x()) + pos.setX(m_referenceRect.topRight().x()); + else if(pos.x() < m_referenceRect.topLeft().x()) + pos.setX(m_referenceRect.topLeft().x()); + + if(pos.y() > m_referenceRect.bottomLeft().y()) + pos.setY(m_referenceRect.bottomLeft().y()); + else if(pos.y() < m_referenceRect.topLeft().y()) + pos.setY(m_referenceRect.topLeft().y()); + + return pos; +} + +void Utils::help(const QString &message, QWidget *parent) +{ + QMessageBox::information(parent, Utils::helpString(), message); +} + +void Utils::message(const QString &message, QWidget *parent) +{ + QMessageBox::information(parent, Utils::informationString(), message); +} + +void Utils::warning(const QString &message, QWidget *parent) +{ + QMessageBox::warning(parent, Utils::warningString(), message); +} + +void Utils::error(const QString &message, QWidget *parent) +{ + QMessageBox::critical(parent, Utils::errorString(), message); +} + +void Utils::fatalError(const QString &message, QWidget *parent) +{ + QMessageBox::critical(parent, Utils::fatalErrorString(), message); + ::exit(1); +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..8916ebd --- /dev/null +++ b/utils.h @@ -0,0 +1,59 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include + +class QPushButton; +class QPolygon; +class QWidget; +class QRect; + +class Utils +{ +public: + static QString helpString(); + static QString informationString(); + static QString warningString(); + static QString errorString(); + static QString fatalErrorString(); + + /* + * Pen to pain a polygon + */ + static QPen polygonPaintingPen(); + + /* + * Calculate a polygon centroid + */ + static QPoint centroid(const QPolygon &polygon); + + /* + * Set tooltips for buttons with their shortcuts + */ + static void setShortcutTooltips(const QList &buttons); + + /* + * Supported image formats as a UNIX wildcard + */ + static QStringList imageMatchingWildcard(); + + static QPoint fitPoint(const QRect &m_referenceRect, const QPoint &_pos); + + static void help(const QString &message, QWidget *parent = nullptr); + static void message(const QString &message, QWidget *parent = nullptr); + static void warning(const QString &message, QWidget *parent = nullptr); + static void error(const QString &message, QWidget *parent = nullptr); + static void fatalError(const QString &message, QWidget *parent = nullptr); +}; + +inline +QPen Utils::polygonPaintingPen() +{ + return QPen(Qt::magenta, 2); +} + +#endif // UTILS_H