├── .gitignore ├── Chapter-01 └── ImageViewer │ ├── ImageViewer.pro │ ├── main.cpp │ ├── mainwindow.cpp │ └── mainwindow.h ├── Chapter-02 ├── AffinePlugin │ ├── AffinePlugin.pro │ ├── affine_plugin.cpp │ └── affine_plugin.h ├── CartoonPlugin │ ├── CartoonPlugin.pro │ ├── cartoon_plugin.cpp │ └── cartoon_plugin.h ├── ErodePlugin │ ├── ErodePlugin.pro │ ├── erode_plugin.cpp │ └── erode_plugin.h ├── ImageEditor │ ├── ImageEditor.pro │ ├── editor_plugin_interface.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── plugins │ │ └── .gitkeep ├── RotatePlugin │ ├── RotatePlugin.pro │ ├── rotate_plugin.cpp │ └── rotate_plugin.h └── SharpenPlugin │ ├── SharpenPlugin.pro │ ├── sharpen_plugin.cpp │ └── sharpen_plugin.h ├── Chapter-03 └── Gazer │ ├── Gazer.pro │ ├── capture_thread.cpp │ ├── capture_thread.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── utilities.cpp │ └── utilities.h ├── Chapter-04 └── Facetious │ ├── Facetious.pro │ ├── capture_thread.cpp │ ├── capture_thread.h │ ├── data │ └── README.txt │ ├── images.qrc │ ├── images │ ├── glasses.jpg │ ├── mouse-nose.jpg │ └── mustache.jpg │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── utilities.cpp │ └── utilities.h ├── Chapter-05 └── Literacy │ ├── Literacy.pro │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── screencapturer.cpp │ └── screencapturer.h ├── Chapter-06 ├── Detective │ ├── Detective.pro │ ├── capture_thread.cpp │ ├── capture_thread.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── utilities.cpp │ └── utilities.h ├── boston-bull │ ├── .gitignore │ ├── bg.txt │ ├── boston-bull-predict.png │ ├── cascade.xml │ ├── info.txt │ ├── test-visualisation.png │ └── train.sh └── no-entry │ ├── .gitignore │ ├── background │ ├── traffic-sign-bg-0.png │ ├── traffic-sign-bg-1.png │ ├── traffic-sign-bg-2.png │ └── traffic-sign-bg-3.png │ ├── bg.txt │ ├── cascade.xml │ ├── no-entry-predict.png │ ├── no-entry.png │ └── train.sh ├── Chapter-07 └── DiGauge │ ├── DiGauge.pro │ ├── capture_thread.cpp │ ├── capture_thread.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── utilities.cpp │ └── utilities.h ├── Chapter-08 ├── CVGLContext │ ├── Makefile │ └── main.c ├── GLFilter │ ├── GLFilter.pro │ ├── glpanel.cpp │ ├── glpanel.h │ ├── images │ │ └── lizard.jpg │ ├── main.cpp │ ├── res.qrc │ └── shaders │ │ ├── fragment.shader │ │ └── vertex.shader ├── Hello-OpenGL │ ├── Makefile │ └── main.c └── QtGL │ ├── QtGL.pro │ ├── glpanel.cpp │ ├── glpanel.h │ ├── main.cpp │ ├── shaders.qrc │ └── shaders │ ├── fragment.shader │ └── vertex.shader ├── LICENSE ├── README.md └── WireFrames.epgz /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.out 3 | *.so 4 | *.so.1 5 | *.so.1.0 6 | *.so.1.0.0 7 | *.mp4 8 | *.exe 9 | moc_* 10 | qrc_* 11 | .qmake.stash 12 | 13 | # ch01 14 | Chapter-01/ImageViewer/ImageViewer 15 | Chapter-01/ImageViewer/Makefile 16 | # ch02 17 | Chapter-02/ImageEditor/ImageEditor 18 | Chapter-02/*/Makefile 19 | # ch03 20 | Chapter-03/Gazer/Gazer 21 | Chapter-03/*/Makefile 22 | # ch04 23 | Chapter-04/Facetious/Facetious 24 | Chapter-04/*/Makefile 25 | Chapter-04/Facetious/data/lbfmodel.yaml 26 | # ch05 27 | Chapter-05/Literacy/Literacy 28 | Chapter-05/*/Makefile 29 | frozen_east_text_detection.pb 30 | # ch06 31 | Chapter-06/Detective/Detective 32 | Chapter-06/Detective/data/ 33 | Chapter-06/*/Makefile 34 | # ch07 35 | Chapter-07/DiGauge/DiGauge 36 | Chapter-07/DiGauge/data/ 37 | Chapter-07/*/Makefile 38 | # ch08 39 | Chapter-08/QtGL/Makefile 40 | Chapter-08/QtGL/QtGL 41 | Chapter-08/GLFilter/Makefile 42 | Chapter-08/GLFilter/GLFilter 43 | Chapter-08/GLFilter/output.jpg 44 | -------------------------------------------------------------------------------- /Chapter-01/ImageViewer/ImageViewer.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Sun Nov 11 20:07:20 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = app 6 | TARGET = ImageViewer 7 | 8 | QT += core gui network 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | INCLUDEPATH += . 12 | 13 | # The following define makes your compiler warn you if you use any 14 | # feature of Qt which has been marked as deprecated (the exact warnings 15 | # depend on your compiler). Please consult the documentation of the 16 | # deprecated API in order to know how to port your code away from it. 17 | DEFINES += QT_DEPRECATED_WARNINGS 18 | 19 | # You can also make your code fail to compile if you use deprecated APIs. 20 | # In order to do so, uncomment the following line. 21 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 22 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 23 | 24 | # Input 25 | HEADERS += mainwindow.h 26 | SOURCES += main.cpp mainwindow.cpp 27 | -------------------------------------------------------------------------------- /Chapter-01/ImageViewer/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("ImageViewer"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-01/ImageViewer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "mainwindow.h" 8 | 9 | 10 | MainWindow::MainWindow(QWidget *parent) : 11 | QMainWindow(parent) 12 | , fileMenu(nullptr) 13 | , viewMenu(nullptr) 14 | , currentImage(nullptr) 15 | { 16 | initUI(); 17 | } 18 | 19 | MainWindow::~MainWindow() 20 | { 21 | } 22 | 23 | void MainWindow::initUI() 24 | { 25 | this->resize(800, 600); 26 | // setup menubar 27 | fileMenu = menuBar()->addMenu("&File"); 28 | viewMenu = menuBar()->addMenu("&View"); 29 | 30 | // setup toolbar 31 | fileToolBar = addToolBar("File"); 32 | viewToolBar = addToolBar("View"); 33 | 34 | 35 | // main area for image display 36 | imageScene = new QGraphicsScene(this); 37 | imageView = new QGraphicsView(imageScene); 38 | setCentralWidget(imageView); 39 | 40 | // setup status bar 41 | mainStatusBar = statusBar(); 42 | mainStatusLabel = new QLabel(mainStatusBar); 43 | mainStatusBar->addPermanentWidget(mainStatusLabel); 44 | mainStatusLabel->setText("Image Information will be here!"); 45 | 46 | createActions(); 47 | } 48 | 49 | void MainWindow::createActions() 50 | { 51 | 52 | // create actions, add them to menus 53 | openAction = new QAction("&Open", this); 54 | fileMenu->addAction(openAction); 55 | saveAsAction = new QAction("&Save as", this); 56 | fileMenu->addAction(saveAsAction); 57 | exitAction = new QAction("E&xit", this); 58 | fileMenu->addAction(exitAction); 59 | 60 | zoomInAction = new QAction("Zoom in", this); 61 | viewMenu->addAction(zoomInAction); 62 | zoomOutAction = new QAction("Zoom Out", this); 63 | viewMenu->addAction(zoomOutAction); 64 | prevAction = new QAction("&Previous Image", this); 65 | viewMenu->addAction(prevAction); 66 | nextAction = new QAction("&Next Image", this); 67 | viewMenu->addAction(nextAction); 68 | 69 | // add actions to toolbars 70 | fileToolBar->addAction(openAction); 71 | viewToolBar->addAction(zoomInAction); 72 | viewToolBar->addAction(zoomOutAction); 73 | viewToolBar->addAction(prevAction); 74 | viewToolBar->addAction(nextAction); 75 | 76 | // connect the signals and slots 77 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 78 | connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openImage())); 79 | connect(saveAsAction, SIGNAL(triggered(bool)), this, SLOT(saveAs())); 80 | connect(zoomInAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn())); 81 | connect(zoomOutAction, SIGNAL(triggered(bool)), this, SLOT(zoomOut())); 82 | connect(prevAction, SIGNAL(triggered(bool)), this, SLOT(prevImage())); 83 | connect(nextAction, SIGNAL(triggered(bool)), this, SLOT(nextImage())); 84 | 85 | setupShortcuts(); 86 | } 87 | 88 | void MainWindow::openImage() 89 | { 90 | QFileDialog dialog(this); 91 | dialog.setWindowTitle("Open Image"); 92 | dialog.setFileMode(QFileDialog::ExistingFile); 93 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 94 | QStringList filePaths; 95 | if (dialog.exec()) { 96 | filePaths = dialog.selectedFiles(); 97 | showImage(filePaths.at(0)); 98 | } 99 | } 100 | 101 | 102 | void MainWindow::showImage(QString path) 103 | { 104 | imageScene->clear(); 105 | imageView->resetMatrix(); 106 | QPixmap image(path); 107 | currentImage = imageScene->addPixmap(image); 108 | imageScene->update(); 109 | imageView->setSceneRect(image.rect()); 110 | QString status = QString("%1, %2x%3, %4 Bytes").arg(path).arg(image.width()) 111 | .arg(image.height()).arg(QFile(path).size()); 112 | mainStatusLabel->setText(status); 113 | currentImagePath = path; 114 | } 115 | 116 | void MainWindow::zoomIn() 117 | { 118 | imageView->scale(1.2, 1.2); 119 | } 120 | 121 | void MainWindow::zoomOut() 122 | { 123 | imageView->scale(1/1.2, 1/1.2); 124 | } 125 | 126 | void MainWindow::prevImage() 127 | { 128 | QFileInfo current(currentImagePath); 129 | QDir dir = current.absoluteDir(); 130 | QStringList nameFilters; 131 | nameFilters << "*.png" << "*.bmp" << "*.jpg"; 132 | QStringList fileNames = dir.entryList(nameFilters, QDir::Files, QDir::Name); 133 | int idx = fileNames.indexOf(QRegExp(QRegExp::escape(current.fileName()))); 134 | if(idx > 0) { 135 | showImage(dir.absoluteFilePath(fileNames.at(idx - 1))); 136 | } else { 137 | QMessageBox::information(this, "Information", "Current image is the first one."); 138 | } 139 | } 140 | 141 | void MainWindow::nextImage() 142 | { 143 | QFileInfo current(currentImagePath); 144 | QDir dir = current.absoluteDir(); 145 | QStringList nameFilters; 146 | nameFilters << "*.png" << "*.bmp" << "*.jpg"; 147 | QStringList fileNames = dir.entryList(nameFilters, QDir::Files, QDir::Name); 148 | int idx = fileNames.indexOf(QRegExp(QRegExp::escape(current.fileName()))); 149 | if(idx < fileNames.size() - 1) { 150 | showImage(dir.absoluteFilePath(fileNames.at(idx + 1))); 151 | } else { 152 | QMessageBox::information(this, "Information", "Current image is the last one."); 153 | } 154 | } 155 | 156 | void MainWindow::saveAs() 157 | { 158 | if (currentImage == nullptr) { 159 | QMessageBox::information(this, "Information", "Nothing to save."); 160 | return; 161 | } 162 | QFileDialog dialog(this); 163 | dialog.setWindowTitle("Save Image As ..."); 164 | dialog.setFileMode(QFileDialog::AnyFile); 165 | dialog.setAcceptMode(QFileDialog::AcceptSave); 166 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 167 | QStringList fileNames; 168 | if (dialog.exec()) { 169 | fileNames = dialog.selectedFiles(); 170 | if(QRegExp(".+\\.(png|bmp|jpg)").exactMatch(fileNames.at(0))) { 171 | currentImage->pixmap().save(fileNames.at(0)); 172 | } else { 173 | QMessageBox::information(this, "Information", "Save error: bad format or filename."); 174 | } 175 | } 176 | } 177 | 178 | 179 | void MainWindow::setupShortcuts() 180 | { 181 | QList shortcuts; 182 | shortcuts << Qt::Key_Plus << Qt::Key_Equal; 183 | zoomInAction->setShortcuts(shortcuts); 184 | 185 | shortcuts.clear(); 186 | shortcuts << Qt::Key_Minus << Qt::Key_Underscore; 187 | zoomOutAction->setShortcuts(shortcuts); 188 | 189 | shortcuts.clear(); 190 | shortcuts << Qt::Key_Up << Qt::Key_Left; 191 | prevAction->setShortcuts(shortcuts); 192 | 193 | shortcuts.clear(); 194 | shortcuts << Qt::Key_Down << Qt::Key_Right; 195 | nextAction->setShortcuts(shortcuts); 196 | } 197 | -------------------------------------------------------------------------------- /Chapter-01/ImageViewer/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class MainWindow : public QMainWindow 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit MainWindow(QWidget *parent=nullptr); 20 | ~MainWindow(); 21 | 22 | private: 23 | void initUI(); 24 | void createActions(); 25 | void showImage(QString); 26 | void setupShortcuts(); 27 | 28 | private slots: 29 | void openImage(); 30 | void zoomIn(); 31 | void zoomOut(); 32 | void prevImage(); 33 | void nextImage(); 34 | void saveAs(); 35 | 36 | private: 37 | QMenu *fileMenu; 38 | QMenu *viewMenu; 39 | 40 | QToolBar *fileToolBar; 41 | QToolBar *viewToolBar; 42 | 43 | QGraphicsScene *imageScene; 44 | QGraphicsView *imageView; 45 | 46 | QStatusBar *mainStatusBar; 47 | QLabel *mainStatusLabel; 48 | 49 | QAction *openAction; 50 | QAction *saveAsAction; 51 | QAction *exitAction; 52 | QAction *zoomInAction; 53 | QAction *zoomOutAction; 54 | QAction *prevAction; 55 | QAction *nextAction; 56 | 57 | QString currentImagePath; 58 | QGraphicsPixmapItem *currentImage; 59 | }; 60 | 61 | #endif // MAINWINDOW_H 62 | -------------------------------------------------------------------------------- /Chapter-02/AffinePlugin/AffinePlugin.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Fri Nov 30 20:56:01 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = lib 6 | TARGET = AffinePlugin 7 | COPNFIG += plugin 8 | INCLUDEPATH += . ../ImageEditor 9 | 10 | # use your own path in the following config 11 | unix: !mac { 12 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 13 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 14 | } 15 | 16 | unix: mac { 17 | INCLUDEPATH += /path/to/opencv/include/opencv4 18 | LIBS += -L/path/to/opencv/lib -lopencv_world 19 | } 20 | 21 | win32 { 22 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 23 | LIBS += -lc:/path/to/opencv/lib/opencv_world 24 | } 25 | 26 | # The following define makes your compiler warn you if you use any 27 | # feature of Qt which has been marked as deprecated (the exact warnings 28 | # depend on your compiler). Please consult the documentation of the 29 | # deprecated API in order to know how to port your code away from it. 30 | DEFINES += QT_DEPRECATED_WARNINGS 31 | 32 | # You can also make your code fail to compile if you use deprecated APIs. 33 | # In order to do so, uncomment the following line. 34 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 35 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 36 | 37 | # Input 38 | HEADERS += affine_plugin.h 39 | SOURCES += affine_plugin.cpp 40 | -------------------------------------------------------------------------------- /Chapter-02/AffinePlugin/affine_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "affine_plugin.h" 2 | 3 | QString AffinePlugin::name() 4 | { 5 | return "Affine"; 6 | } 7 | 8 | void AffinePlugin::edit(const cv::Mat &input, cv::Mat &output) 9 | { 10 | 11 | cv::Point2f triangleA[3]; 12 | cv::Point2f triangleB[3]; 13 | 14 | triangleA[0] = cv::Point2f(0 , 0); 15 | triangleA[1] = cv::Point2f(1 , 0); 16 | triangleA[2] = cv::Point2f(0 , 1); 17 | 18 | triangleB[0] = cv::Point2f(0, 0); 19 | triangleB[1] = cv::Point2f(1, 0); 20 | triangleB[2] = cv::Point2f(1, 1); 21 | 22 | cv::Mat affineMatrix = cv::getAffineTransform(triangleA, triangleB); 23 | cv::Mat result; 24 | cv::warpAffine( 25 | input, result, 26 | affineMatrix, input.size(), // output image size, same as input 27 | cv::INTER_CUBIC, // Interpolation method 28 | cv::BORDER_CONSTANT // Extrapolation method 29 | //BORDER_WRAP // Extrapolation method 30 | ); 31 | 32 | output = result; 33 | } 34 | -------------------------------------------------------------------------------- /Chapter-02/AffinePlugin/affine_plugin.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "editor_plugin_interface.h" 5 | 6 | class AffinePlugin: public QObject, public EditorPluginInterface 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID); 10 | Q_INTERFACES(EditorPluginInterface); 11 | public: 12 | QString name(); 13 | void edit(const cv::Mat &input, cv::Mat &output); 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter-02/CartoonPlugin/CartoonPlugin.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Fri Nov 30 20:56:01 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = lib 6 | TARGET = CartoonPlugin 7 | COPNFIG += plugin 8 | INCLUDEPATH += . ../ImageEditor 9 | 10 | # use your own path in the following config 11 | unix: !mac { 12 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 13 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 14 | } 15 | 16 | unix: mac { 17 | INCLUDEPATH += /path/to/opencv/include/opencv4 18 | LIBS += -L/path/to/opencv/lib -lopencv_world 19 | } 20 | 21 | win32 { 22 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 23 | LIBS += -lc:/path/to/opencv/lib/opencv_world 24 | } 25 | 26 | # The following define makes your compiler warn you if you use any 27 | # feature of Qt which has been marked as deprecated (the exact warnings 28 | # depend on your compiler). Please consult the documentation of the 29 | # deprecated API in order to know how to port your code away from it. 30 | DEFINES += QT_DEPRECATED_WARNINGS 31 | 32 | # You can also make your code fail to compile if you use deprecated APIs. 33 | # In order to do so, uncomment the following line. 34 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 35 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 36 | 37 | # Input 38 | HEADERS += cartoon_plugin.h 39 | SOURCES += cartoon_plugin.cpp 40 | -------------------------------------------------------------------------------- /Chapter-02/CartoonPlugin/cartoon_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "cartoon_plugin.h" 2 | 3 | QString CartoonPlugin::name() 4 | { 5 | return "Cartoon"; 6 | } 7 | 8 | void CartoonPlugin::edit(const cv::Mat &input, cv::Mat &output) 9 | { 10 | int num_down = 2; 11 | int num_bilateral = 7; 12 | 13 | cv::Mat copy1, copy2; 14 | cv::Mat image_gray, image_edge; 15 | 16 | copy1 = input.clone(); 17 | for(int i = 0; i < num_down; i++) { 18 | cv::pyrDown(copy1, copy2); 19 | copy1 = copy2.clone(); 20 | } 21 | 22 | for(int i = 0; i < num_bilateral; i++) { 23 | cv::bilateralFilter(copy1, copy2, 9, 9, 7); 24 | copy1 = copy2.clone(); 25 | } 26 | 27 | for(int i = 0; i < num_down; i++) { 28 | cv::pyrUp(copy1, copy2); 29 | copy1 = copy2.clone(); 30 | } 31 | 32 | if (input.cols != copy1.cols || input.rows != copy1.rows) { 33 | cv::Rect rect(0, 0, input.cols, input.rows); 34 | copy1(rect).copyTo(copy2); 35 | copy1 = copy2; 36 | } 37 | 38 | cv::cvtColor(input, image_gray, cv::COLOR_RGB2GRAY); 39 | cv::medianBlur(image_gray, image_gray, 5); 40 | 41 | cv::adaptiveThreshold(image_gray, image_gray, 255, 42 | cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 9, 2); 43 | 44 | cv::cvtColor(image_gray, image_edge, cv::COLOR_GRAY2RGB); 45 | 46 | output = copy1 & image_edge; 47 | 48 | /* 49 | cv::GaussianBlur(image_edge, image_edge, cv::Size(5, 5), 0); 50 | cv::Mat mask(input.rows, input.cols, CV_8UC3, cv::Scalar(90, 90, 90)); 51 | mask = mask & (~image_edge); 52 | output = (copy1 & image_edge) | mask; 53 | */ 54 | } 55 | -------------------------------------------------------------------------------- /Chapter-02/CartoonPlugin/cartoon_plugin.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "editor_plugin_interface.h" 5 | 6 | class CartoonPlugin: public QObject, public EditorPluginInterface 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID); 10 | Q_INTERFACES(EditorPluginInterface); 11 | public: 12 | QString name(); 13 | void edit(const cv::Mat &input, cv::Mat &output); 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter-02/ErodePlugin/ErodePlugin.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Fri Nov 30 20:56:01 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = lib 6 | TARGET = ErodePlugin 7 | COPNFIG += plugin 8 | INCLUDEPATH += . ../ImageEditor 9 | 10 | # use your own path in the following config 11 | unix: !mac { 12 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 13 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 14 | } 15 | 16 | unix: mac { 17 | INCLUDEPATH += /path/to/opencv/include/opencv4 18 | LIBS += -L/path/to/opencv/lib -lopencv_world 19 | } 20 | 21 | win32 { 22 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 23 | LIBS += -lc:/path/to/opencv/lib/opencv_world 24 | } 25 | 26 | # The following define makes your compiler warn you if you use any 27 | # feature of Qt which has been marked as deprecated (the exact warnings 28 | # depend on your compiler). Please consult the documentation of the 29 | # deprecated API in order to know how to port your code away from it. 30 | DEFINES += QT_DEPRECATED_WARNINGS 31 | 32 | # You can also make your code fail to compile if you use deprecated APIs. 33 | # In order to do so, uncomment the following line. 34 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 35 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 36 | 37 | # Input 38 | HEADERS += erode_plugin.h 39 | SOURCES += erode_plugin.cpp 40 | -------------------------------------------------------------------------------- /Chapter-02/ErodePlugin/erode_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "erode_plugin.h" 2 | 3 | QString ErodePlugin::name() 4 | { 5 | return "Erode"; 6 | } 7 | 8 | void ErodePlugin::edit(const cv::Mat &input, cv::Mat &output) 9 | { 10 | erode(input, output, cv::Mat()); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-02/ErodePlugin/erode_plugin.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "editor_plugin_interface.h" 5 | 6 | class ErodePlugin: public QObject, public EditorPluginInterface 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID); 10 | Q_INTERFACES(EditorPluginInterface); 11 | public: 12 | QString name(); 13 | void edit(const cv::Mat &input, cv::Mat &output); 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/ImageEditor.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Sun Nov 11 20:07:20 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = app 6 | TARGET = ImageEditor 7 | 8 | QT += core gui network 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | INCLUDEPATH += . 12 | 13 | # use your own path in the following config 14 | unix: !mac { 15 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 16 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 17 | } 18 | 19 | unix: mac { 20 | INCLUDEPATH += /path/to/opencv/include/opencv4 21 | LIBS += -L/path/to/opencv/lib -lopencv_world 22 | } 23 | 24 | win32 { 25 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 26 | LIBS += -lc:/path/to/opencv/lib/opencv_world 27 | } 28 | 29 | # The following define makes your compiler warn you if you use any 30 | # feature of Qt which has been marked as deprecated (the exact warnings 31 | # depend on your compiler). Please consult the documentation of the 32 | # deprecated API in order to know how to port your code away from it. 33 | DEFINES += QT_DEPRECATED_WARNINGS 34 | 35 | # You can also make your code fail to compile if you use deprecated APIs. 36 | # In order to do so, uncomment the following line. 37 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 38 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 39 | 40 | # Input 41 | HEADERS += mainwindow.h editor_plugin_interface.h 42 | SOURCES += main.cpp mainwindow.cpp 43 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/editor_plugin_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef EDITOR_PLUGIN_INTERFACE_H 2 | #define EDITOR_PLUGIN_INTERFACE_H 3 | 4 | #include 5 | #include 6 | #include "opencv2/opencv.hpp" 7 | 8 | class EditorPluginInterface 9 | { 10 | public: 11 | virtual ~EditorPluginInterface() {}; 12 | virtual QString name() = 0; 13 | virtual void edit(const cv::Mat &input, cv::Mat &output) = 0; 14 | }; 15 | 16 | 17 | #define EDIT_PLUGIN_INTERFACE_IID "com.kdr2.editorplugininterface" 18 | Q_DECLARE_INTERFACE(EditorPluginInterface, EDIT_PLUGIN_INTERFACE_IID); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("ImageEditor"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | 11 | #include "mainwindow.h" 12 | 13 | MainWindow::MainWindow(QWidget *parent) : 14 | QMainWindow(parent) 15 | , fileMenu(nullptr) 16 | , viewMenu(nullptr) 17 | , currentImage(nullptr) 18 | { 19 | initUI(); 20 | loadPlugins(); 21 | } 22 | 23 | MainWindow::~MainWindow() 24 | { 25 | } 26 | 27 | void MainWindow::initUI() 28 | { 29 | this->resize(800, 600); 30 | // setup menubar 31 | fileMenu = menuBar()->addMenu("&File"); 32 | viewMenu = menuBar()->addMenu("&View"); 33 | editMenu = menuBar()->addMenu("&Edit"); 34 | 35 | // setup toolbar 36 | fileToolBar = addToolBar("File"); 37 | viewToolBar = addToolBar("View"); 38 | editToolBar = addToolBar("Edit"); 39 | 40 | 41 | // main area for image display 42 | imageScene = new QGraphicsScene(this); 43 | imageView = new QGraphicsView(imageScene); 44 | setCentralWidget(imageView); 45 | 46 | // setup status bar 47 | mainStatusBar = statusBar(); 48 | mainStatusLabel = new QLabel(mainStatusBar); 49 | mainStatusBar->addPermanentWidget(mainStatusLabel); 50 | mainStatusLabel->setText("Image Information will be here!"); 51 | 52 | createActions(); 53 | } 54 | 55 | void MainWindow::createActions() 56 | { 57 | 58 | // create actions, add them to menus 59 | openAction = new QAction("&Open", this); 60 | fileMenu->addAction(openAction); 61 | saveAsAction = new QAction("&Save as", this); 62 | fileMenu->addAction(saveAsAction); 63 | exitAction = new QAction("E&xit", this); 64 | fileMenu->addAction(exitAction); 65 | 66 | zoomInAction = new QAction("Zoom in", this); 67 | viewMenu->addAction(zoomInAction); 68 | zoomOutAction = new QAction("Zoom Out", this); 69 | viewMenu->addAction(zoomOutAction); 70 | prevAction = new QAction("&Previous Image", this); 71 | viewMenu->addAction(prevAction); 72 | nextAction = new QAction("&Next Image", this); 73 | viewMenu->addAction(nextAction); 74 | 75 | // add actions to toolbars 76 | fileToolBar->addAction(openAction); 77 | viewToolBar->addAction(zoomInAction); 78 | viewToolBar->addAction(zoomOutAction); 79 | viewToolBar->addAction(prevAction); 80 | viewToolBar->addAction(nextAction); 81 | 82 | // connect the signals and slots 83 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 84 | connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openImage())); 85 | connect(saveAsAction, SIGNAL(triggered(bool)), this, SLOT(saveAs())); 86 | connect(zoomInAction, SIGNAL(triggered(bool)), this, SLOT(zoomIn())); 87 | connect(zoomOutAction, SIGNAL(triggered(bool)), this, SLOT(zoomOut())); 88 | connect(prevAction, SIGNAL(triggered(bool)), this, SLOT(prevImage())); 89 | connect(nextAction, SIGNAL(triggered(bool)), this, SLOT(nextImage())); 90 | 91 | setupShortcuts(); 92 | 93 | // the edit actions 94 | blurAction = new QAction("Blur", this); 95 | editMenu->addAction(blurAction); 96 | editToolBar->addAction(blurAction); 97 | connect(blurAction, SIGNAL(triggered(bool)), this, SLOT(blurImage())); 98 | } 99 | 100 | void MainWindow::openImage() 101 | { 102 | QFileDialog dialog(this); 103 | dialog.setWindowTitle("Open Image"); 104 | dialog.setFileMode(QFileDialog::ExistingFile); 105 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 106 | QStringList filePaths; 107 | if (dialog.exec()) { 108 | filePaths = dialog.selectedFiles(); 109 | showImage(filePaths.at(0)); 110 | } 111 | } 112 | 113 | 114 | void MainWindow::showImage(QString path) 115 | { 116 | imageScene->clear(); 117 | imageView->resetMatrix(); 118 | QPixmap image(path); 119 | currentImage = imageScene->addPixmap(image); 120 | imageScene->update(); 121 | imageView->setSceneRect(image.rect()); 122 | QString status = QString("%1, %2x%3, %4 Bytes").arg(path).arg(image.width()) 123 | .arg(image.height()).arg(QFile(path).size()); 124 | mainStatusLabel->setText(status); 125 | currentImagePath = path; 126 | } 127 | 128 | void MainWindow::zoomIn() 129 | { 130 | imageView->scale(1.2, 1.2); 131 | } 132 | 133 | void MainWindow::zoomOut() 134 | { 135 | imageView->scale(1/1.2, 1/1.2); 136 | } 137 | 138 | void MainWindow::prevImage() 139 | { 140 | QFileInfo current(currentImagePath); 141 | QDir dir = current.absoluteDir(); 142 | QStringList nameFilters; 143 | nameFilters << "*.png" << "*.bmp" << "*.jpg"; 144 | QStringList fileNames = dir.entryList(nameFilters, QDir::Files, QDir::Name); 145 | int idx = fileNames.indexOf(QRegExp(QRegExp::escape(current.fileName()))); 146 | if(idx > 0) { 147 | showImage(dir.absoluteFilePath(fileNames.at(idx - 1))); 148 | } else { 149 | QMessageBox::information(this, "Information", "Current image is the first one."); 150 | } 151 | } 152 | 153 | void MainWindow::nextImage() 154 | { 155 | QFileInfo current(currentImagePath); 156 | QDir dir = current.absoluteDir(); 157 | QStringList nameFilters; 158 | nameFilters << "*.png" << "*.bmp" << "*.jpg"; 159 | QStringList fileNames = dir.entryList(nameFilters, QDir::Files, QDir::Name); 160 | int idx = fileNames.indexOf(QRegExp(QRegExp::escape(current.fileName()))); 161 | if(idx < fileNames.size() - 1) { 162 | showImage(dir.absoluteFilePath(fileNames.at(idx + 1))); 163 | } else { 164 | QMessageBox::information(this, "Information", "Current image is the last one."); 165 | } 166 | } 167 | 168 | void MainWindow::saveAs() 169 | { 170 | if (currentImage == nullptr) { 171 | QMessageBox::information(this, "Information", "Noting to save."); 172 | return; 173 | } 174 | QFileDialog dialog(this); 175 | dialog.setWindowTitle("Save Image As ..."); 176 | dialog.setFileMode(QFileDialog::AnyFile); 177 | dialog.setAcceptMode(QFileDialog::AcceptSave); 178 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 179 | QStringList fileNames; 180 | if (dialog.exec()) { 181 | fileNames = dialog.selectedFiles(); 182 | if(QRegExp(".+\\.(png|bmp|jpg)").exactMatch(fileNames.at(0))) { 183 | currentImage->pixmap().save(fileNames.at(0)); 184 | } else { 185 | QMessageBox::information(this, "Information", "Save error: bad format or filename."); 186 | } 187 | } 188 | } 189 | 190 | 191 | void MainWindow::setupShortcuts() 192 | { 193 | QList shortcuts; 194 | shortcuts << Qt::Key_Plus << Qt::Key_Equal; 195 | zoomInAction->setShortcuts(shortcuts); 196 | 197 | shortcuts.clear(); 198 | shortcuts << Qt::Key_Minus << Qt::Key_Underscore; 199 | zoomOutAction->setShortcuts(shortcuts); 200 | 201 | shortcuts.clear(); 202 | shortcuts << Qt::Key_Up << Qt::Key_Left; 203 | prevAction->setShortcuts(shortcuts); 204 | 205 | shortcuts.clear(); 206 | shortcuts << Qt::Key_Down << Qt::Key_Right; 207 | nextAction->setShortcuts(shortcuts); 208 | } 209 | 210 | 211 | void MainWindow::blurImage() 212 | { 213 | if (currentImage == nullptr) { 214 | QMessageBox::information(this, "Information", "No image to edit."); 215 | return; 216 | } 217 | QPixmap pixmap = currentImage->pixmap(); 218 | QImage image = pixmap.toImage(); 219 | image = image.convertToFormat(QImage::Format_RGB888); 220 | cv::Mat mat = cv::Mat( 221 | image.height(), 222 | image.width(), 223 | CV_8UC3, 224 | image.bits(), 225 | image.bytesPerLine()); 226 | 227 | cv::Mat tmp; 228 | cv::blur(mat, tmp, cv::Size(8, 8)); 229 | mat = tmp; 230 | 231 | QImage image_blurred( 232 | mat.data, 233 | mat.cols, 234 | mat.rows, 235 | mat.step, 236 | QImage::Format_RGB888); 237 | pixmap = QPixmap::fromImage(image_blurred); 238 | imageScene->clear(); 239 | imageView->resetMatrix(); 240 | currentImage = imageScene->addPixmap(pixmap); 241 | imageScene->update(); 242 | imageView->setSceneRect(pixmap.rect()); 243 | QString status = QString("(editted image), %1x%2") 244 | .arg(pixmap.width()).arg(pixmap.height()); 245 | mainStatusLabel->setText(status); 246 | } 247 | 248 | 249 | void MainWindow::loadPlugins() 250 | { 251 | QDir pluginsDir(QApplication::instance()->applicationDirPath() + "/plugins"); 252 | QStringList nameFilters; 253 | nameFilters << "*.so" << "*.dylib" << "*.dll"; 254 | QFileInfoList plugins = pluginsDir.entryInfoList( 255 | nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name); 256 | foreach(QFileInfo plugin, plugins) { 257 | QPluginLoader pluginLoader(plugin.absoluteFilePath(), this); 258 | EditorPluginInterface *plugin_ptr = dynamic_cast(pluginLoader.instance()); 259 | if(plugin_ptr) { 260 | QAction *action = new QAction(plugin_ptr->name()); 261 | editMenu->addAction(action); 262 | editToolBar->addAction(action); 263 | editPlugins[plugin_ptr->name()] = plugin_ptr; 264 | connect(action, SIGNAL(triggered(bool)), this, SLOT(pluginPerform())); 265 | // pluginLoader.unload(); 266 | } else { 267 | qDebug() << "bad plugin: " << plugin.absoluteFilePath(); 268 | } 269 | } 270 | } 271 | 272 | 273 | void MainWindow::pluginPerform() 274 | { 275 | if (currentImage == nullptr) { 276 | QMessageBox::information(this, "Information", "No image to edit."); 277 | return; 278 | } 279 | 280 | QAction *active_action = qobject_cast(sender()); 281 | EditorPluginInterface *plugin_ptr = editPlugins[active_action->text()]; 282 | if(!plugin_ptr) { 283 | QMessageBox::information(this, "Information", "No plugin is found."); 284 | return; 285 | } 286 | 287 | QPixmap pixmap = currentImage->pixmap(); 288 | QImage image = pixmap.toImage(); 289 | image = image.convertToFormat(QImage::Format_RGB888); 290 | cv::Mat mat = cv::Mat( 291 | image.height(), 292 | image.width(), 293 | CV_8UC3, 294 | image.bits(), 295 | image.bytesPerLine()); 296 | 297 | plugin_ptr->edit(mat, mat); 298 | 299 | QImage image_edited( 300 | mat.data, 301 | mat.cols, 302 | mat.rows, 303 | mat.step, 304 | QImage::Format_RGB888); 305 | pixmap = QPixmap::fromImage(image_edited); 306 | imageScene->clear(); 307 | imageView->resetMatrix(); 308 | currentImage = imageScene->addPixmap(pixmap); 309 | imageScene->update(); 310 | imageView->setSceneRect(pixmap.rect()); 311 | QString status = QString("(editted image), %1x%2") 312 | .arg(pixmap.width()).arg(pixmap.height()); 313 | mainStatusLabel->setText(status); 314 | } 315 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "editor_plugin_interface.h" 16 | 17 | class MainWindow : public QMainWindow 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit MainWindow(QWidget *parent=nullptr); 23 | ~MainWindow(); 24 | 25 | private: 26 | void initUI(); 27 | void createActions(); 28 | void showImage(QString); 29 | void setupShortcuts(); 30 | // plugin 31 | void loadPlugins(); 32 | 33 | private slots: 34 | void openImage(); 35 | void zoomIn(); 36 | void zoomOut(); 37 | void prevImage(); 38 | void nextImage(); 39 | void saveAs(); 40 | // for editting 41 | void blurImage(); 42 | void pluginPerform(); 43 | 44 | private: 45 | QMenu *fileMenu; 46 | QMenu *viewMenu; 47 | QMenu *editMenu; 48 | 49 | QToolBar *fileToolBar; 50 | QToolBar *viewToolBar; 51 | QToolBar *editToolBar; 52 | 53 | QGraphicsScene *imageScene; 54 | QGraphicsView *imageView; 55 | 56 | QStatusBar *mainStatusBar; 57 | QLabel *mainStatusLabel; 58 | 59 | QAction *openAction; 60 | QAction *saveAsAction; 61 | QAction *exitAction; 62 | QAction *zoomInAction; 63 | QAction *zoomOutAction; 64 | QAction *prevAction; 65 | QAction *nextAction; 66 | 67 | QAction *blurAction; 68 | 69 | QString currentImagePath; 70 | QGraphicsPixmapItem *currentImage; 71 | 72 | QMap editPlugins; 73 | }; 74 | 75 | #endif // MAINWINDOW_H 76 | -------------------------------------------------------------------------------- /Chapter-02/ImageEditor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-02/ImageEditor/plugins/.gitkeep -------------------------------------------------------------------------------- /Chapter-02/RotatePlugin/RotatePlugin.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Fri Nov 30 20:56:01 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = lib 6 | TARGET = RotatePlugin 7 | COPNFIG += plugin 8 | INCLUDEPATH += . ../ImageEditor 9 | 10 | # use your own path in the following config 11 | unix: !mac { 12 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 13 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 14 | } 15 | 16 | unix: mac { 17 | INCLUDEPATH += /path/to/opencv/include/opencv4 18 | LIBS += -L/path/to/opencv/lib -lopencv_world 19 | } 20 | 21 | win32 { 22 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 23 | LIBS += -lc:/path/to/opencv/lib/opencv_world 24 | } 25 | 26 | # The following define makes your compiler warn you if you use any 27 | # feature of Qt which has been marked as deprecated (the exact warnings 28 | # depend on your compiler). Please consult the documentation of the 29 | # deprecated API in order to know how to port your code away from it. 30 | DEFINES += QT_DEPRECATED_WARNINGS 31 | 32 | # You can also make your code fail to compile if you use deprecated APIs. 33 | # In order to do so, uncomment the following line. 34 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 35 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 36 | 37 | # Input 38 | HEADERS += rotate_plugin.h 39 | SOURCES += rotate_plugin.cpp 40 | -------------------------------------------------------------------------------- /Chapter-02/RotatePlugin/rotate_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "rotate_plugin.h" 2 | 3 | QString RotatePlugin::name() 4 | { 5 | return "Rotate"; 6 | } 7 | 8 | void RotatePlugin::edit(const cv::Mat &input, cv::Mat &output) 9 | { 10 | double angle = 45.0; 11 | double scale = 1.0; 12 | cv::Point2f center = cv::Point(input.cols/2, input.rows/2); 13 | cv::Mat rotateMatrix = cv::getRotationMatrix2D(center, angle, scale); 14 | 15 | cv::Mat result; 16 | cv::warpAffine(input, result, 17 | rotateMatrix, input.size(), 18 | cv::INTER_LINEAR, cv::BORDER_CONSTANT); 19 | output = result; 20 | } 21 | -------------------------------------------------------------------------------- /Chapter-02/RotatePlugin/rotate_plugin.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "editor_plugin_interface.h" 5 | 6 | class RotatePlugin: public QObject, public EditorPluginInterface 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID); 10 | Q_INTERFACES(EditorPluginInterface); 11 | public: 12 | QString name(); 13 | void edit(const cv::Mat &input, cv::Mat &output); 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter-02/SharpenPlugin/SharpenPlugin.pro: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # Automatically generated by qmake (3.1) Fri Nov 30 20:56:01 2018 3 | ###################################################################### 4 | 5 | TEMPLATE = lib 6 | TARGET = SharpenPlugin 7 | COPNFIG += plugin 8 | INCLUDEPATH += . ../ImageEditor 9 | 10 | # use your own path in the following config 11 | unix: !mac { 12 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 13 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -l opencv_imgproc 14 | } 15 | 16 | unix: mac { 17 | INCLUDEPATH += /path/to/opencv/include/opencv4 18 | LIBS += -L/path/to/opencv/lib -lopencv_world 19 | } 20 | 21 | win32 { 22 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 23 | LIBS += -lc:/path/to/opencv/lib/opencv_world 24 | } 25 | 26 | # The following define makes your compiler warn you if you use any 27 | # feature of Qt which has been marked as deprecated (the exact warnings 28 | # depend on your compiler). Please consult the documentation of the 29 | # deprecated API in order to know how to port your code away from it. 30 | DEFINES += QT_DEPRECATED_WARNINGS 31 | 32 | # You can also make your code fail to compile if you use deprecated APIs. 33 | # In order to do so, uncomment the following line. 34 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 35 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 36 | 37 | # Input 38 | HEADERS += sharpen_plugin.h 39 | SOURCES += sharpen_plugin.cpp 40 | -------------------------------------------------------------------------------- /Chapter-02/SharpenPlugin/sharpen_plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "sharpen_plugin.h" 2 | 3 | QString SharpenPlugin::name() 4 | { 5 | return "Sharpen"; 6 | } 7 | 8 | void SharpenPlugin::edit(const cv::Mat &input, cv::Mat &output) 9 | { 10 | int intensity = 2; 11 | cv::Mat smoothed; 12 | cv::GaussianBlur(input, smoothed, cv::Size(9, 9), 0); 13 | output = input + (input - smoothed) * intensity; 14 | } 15 | -------------------------------------------------------------------------------- /Chapter-02/SharpenPlugin/sharpen_plugin.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "editor_plugin_interface.h" 5 | 6 | class SharpenPlugin: public QObject, public EditorPluginInterface 7 | { 8 | Q_OBJECT 9 | Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID); 10 | Q_INTERFACES(EditorPluginInterface); 11 | public: 12 | QString name(); 13 | void edit(const cv::Mat &input, cv::Mat &output); 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/Gazer.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = Gazer 3 | INCLUDEPATH += . 4 | 5 | QT += core gui multimedia network concurrent 6 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 7 | 8 | unix: !mac { 9 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 10 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_video -lopencv_videoio 11 | } 12 | 13 | unix: mac { 14 | INCLUDEPATH += /path/to/opencv/include/opencv4 15 | LIBS += -L/path/to/opencv/lib -lopencv_world 16 | } 17 | 18 | win32 { 19 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 20 | LIBS += -lc:/path/to/opencv/lib/opencv_world 21 | } 22 | 23 | 24 | # Input 25 | HEADERS += mainwindow.h capture_thread.h utilities.h 26 | SOURCES += main.cpp mainwindow.cpp capture_thread.cpp utilities.cpp 27 | 28 | # Using OpenCV or QCamera 29 | # DEFINES += GAZER_USE_QT_CAMERA=1 30 | # QT += multimediawidgets 31 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/capture_thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utilities.h" 6 | #include "capture_thread.h" 7 | 8 | CaptureThread::CaptureThread(int camera, QMutex *lock): 9 | running(false), cameraID(camera), videoPath(""), data_lock(lock) 10 | { 11 | fps_calculating = false; 12 | fps = 0.0; 13 | 14 | frame_width = frame_height = 0; 15 | video_saving_status = STOPPED; 16 | saved_video_name = ""; 17 | video_writer = nullptr; 18 | 19 | motion_detecting_status = false; 20 | } 21 | 22 | CaptureThread::CaptureThread(QString videoPath, QMutex *lock): 23 | running(false), cameraID(-1), videoPath(videoPath), data_lock(lock) 24 | { 25 | fps_calculating = false; 26 | fps = 0.0; 27 | 28 | frame_width = frame_height = 0; 29 | video_saving_status = STOPPED; 30 | saved_video_name = ""; 31 | video_writer = nullptr; 32 | 33 | motion_detecting_status = false; 34 | } 35 | 36 | CaptureThread::~CaptureThread() { 37 | } 38 | 39 | void CaptureThread::run() { 40 | running = true; 41 | cv::VideoCapture cap(cameraID); 42 | // cv::VideoCapture cap("/home/kdr2/Videos/WIN_20190123_20_14_56_Pro.mp4"); 43 | cv::Mat tmp_frame; 44 | 45 | frame_width = cap.get(cv::CAP_PROP_FRAME_WIDTH); 46 | frame_height = cap.get(cv::CAP_PROP_FRAME_HEIGHT); 47 | 48 | segmentor = cv::createBackgroundSubtractorMOG2(500, 16, true); 49 | 50 | while(running) { 51 | cap >> tmp_frame; 52 | if (tmp_frame.empty()) { 53 | break; 54 | } 55 | if(motion_detecting_status) { 56 | motionDetect(tmp_frame); 57 | } 58 | if(video_saving_status == STARTING) { 59 | startSavingVideo(tmp_frame); 60 | } 61 | if(video_saving_status == STARTED) { 62 | video_writer->write(tmp_frame); 63 | } 64 | if(video_saving_status == STOPPING) { 65 | stopSavingVideo(); 66 | } 67 | 68 | cvtColor(tmp_frame, tmp_frame, cv::COLOR_BGR2RGB); 69 | data_lock->lock(); 70 | frame = tmp_frame; 71 | data_lock->unlock(); 72 | emit frameCaptured(&frame); 73 | if(fps_calculating) { 74 | calculateFPS(cap); 75 | } 76 | } 77 | cap.release(); 78 | running = false; 79 | } 80 | 81 | void CaptureThread::calculateFPS(cv::VideoCapture &cap) 82 | { 83 | const int count_to_read = 100; 84 | cv::Mat tmp_frame; 85 | QTime timer; 86 | timer.start(); 87 | for(int i = 0; i < count_to_read; i++) { 88 | cap >> tmp_frame; 89 | } 90 | int elapsed_ms = timer.elapsed(); 91 | fps = count_to_read / (elapsed_ms / 1000.0); 92 | fps_calculating = false; 93 | emit fpsChanged(fps); 94 | } 95 | 96 | 97 | void CaptureThread::startSavingVideo(cv::Mat &firstFrame) 98 | { 99 | saved_video_name = Utilities::newSavedVideoName(); 100 | 101 | QString cover = Utilities::getSavedVideoPath(saved_video_name, "jpg"); 102 | cv::imwrite(cover.toStdString(), firstFrame); 103 | 104 | video_writer = new cv::VideoWriter( 105 | Utilities::getSavedVideoPath(saved_video_name, "avi").toStdString(), 106 | cv::VideoWriter::fourcc('M','J','P','G'), 107 | fps? fps: 30, 108 | cv::Size(frame_width,frame_height)); 109 | video_saving_status = STARTED; 110 | } 111 | 112 | 113 | void CaptureThread::stopSavingVideo() 114 | { 115 | video_saving_status = STOPPED; 116 | video_writer->release(); 117 | delete video_writer; 118 | video_writer = nullptr; 119 | emit videoSaved(saved_video_name); 120 | } 121 | 122 | void CaptureThread::motionDetect(cv::Mat &frame) 123 | { 124 | cv::Mat fgmask; 125 | segmentor->apply(frame, fgmask); 126 | if (fgmask.empty()) { 127 | return; 128 | } 129 | 130 | cv::threshold(fgmask, fgmask, 25, 255, cv::THRESH_BINARY); 131 | 132 | int noise_size = 9; 133 | cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(noise_size, noise_size)); 134 | cv::erode(fgmask, fgmask, kernel); 135 | kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(noise_size, noise_size)); 136 | cv::dilate(fgmask, fgmask, kernel, cv::Point(-1,-1), 3); 137 | 138 | vector > contours; 139 | cv::findContours(fgmask, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); 140 | 141 | bool has_motion = contours.size() > 0; 142 | if(!motion_detected && has_motion) { 143 | motion_detected = true; 144 | setVideoSavingStatus(STARTING); 145 | qDebug() << "new motion detected, should send a notification."; 146 | QtConcurrent::run(Utilities::notifyMobile, cameraID); 147 | } else if (motion_detected && !has_motion) { 148 | motion_detected = false; 149 | setVideoSavingStatus(STOPPING); 150 | qDebug() << "detected motion disappeared."; 151 | } 152 | 153 | cv::Scalar color = cv::Scalar(0, 0, 255); // red 154 | for(size_t i = 0; i < contours.size(); i++) { 155 | cv::Rect rect = cv::boundingRect(contours[i]); 156 | cv::rectangle(frame, rect, color, 1); 157 | //cv::drawContours(frame, contours, (int)i, color, 1); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/capture_thread.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef CAPTURE_THREAD_H 3 | #define CAPTURE_THREAD_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | #include "opencv2/videoio.hpp" 11 | #include "opencv2/video/background_segm.hpp" 12 | 13 | using namespace std; 14 | 15 | class CaptureThread : public QThread 16 | { 17 | Q_OBJECT 18 | public: 19 | CaptureThread(int camera, QMutex *lock); 20 | CaptureThread(QString videoPath, QMutex *lock); 21 | ~CaptureThread(); 22 | void setRunning(bool run) {running = run; }; 23 | void startCalcFPS() {fps_calculating = true; }; 24 | enum VideoSavingStatus { 25 | STARTING, 26 | STARTED, 27 | STOPPING, 28 | STOPPED 29 | }; 30 | 31 | void setVideoSavingStatus(VideoSavingStatus status) {video_saving_status = status; }; 32 | void setMotionDetectingStatus(bool status) { 33 | motion_detecting_status = status; 34 | motion_detected = false; 35 | if(video_saving_status != STOPPED) video_saving_status = STOPPING; 36 | }; 37 | 38 | protected: 39 | void run() override; 40 | 41 | signals: 42 | void frameCaptured(cv::Mat *data); 43 | void fpsChanged(float fps); 44 | void videoSaved(QString name); 45 | 46 | private: 47 | void calculateFPS(cv::VideoCapture &cap); 48 | void startSavingVideo(cv::Mat &firstFrame); 49 | void stopSavingVideo(); 50 | void motionDetect(cv::Mat &frame); 51 | 52 | private: 53 | bool running; 54 | int cameraID; 55 | QString videoPath; 56 | QMutex *data_lock; 57 | cv::Mat frame; 58 | 59 | // FPS calculating 60 | bool fps_calculating; 61 | float fps; 62 | 63 | // video saving 64 | int frame_width, frame_height; 65 | VideoSavingStatus video_saving_status; 66 | QString saved_video_name; 67 | cv::VideoWriter *video_writer; 68 | 69 | // motion analysis 70 | bool motion_detecting_status; 71 | bool motion_detected; 72 | cv::Ptr segmentor; 73 | }; 74 | 75 | #endif // CAPTURE_THREAD_H 76 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("Gazer"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "opencv2/videoio.hpp" 14 | 15 | #include "mainwindow.h" 16 | #include "utilities.h" 17 | 18 | MainWindow::MainWindow(QWidget *parent) : 19 | QMainWindow(parent) 20 | , fileMenu(nullptr) 21 | , capturer(nullptr) 22 | { 23 | initUI(); 24 | data_lock = new QMutex(); 25 | } 26 | 27 | MainWindow::~MainWindow() 28 | { 29 | } 30 | 31 | void MainWindow::initUI() 32 | { 33 | this->resize(1000, 800); 34 | // setup menubar 35 | fileMenu = menuBar()->addMenu("&File"); 36 | 37 | // main area 38 | QGridLayout *main_layout = new QGridLayout(); 39 | 40 | #ifdef GAZER_USE_QT_CAMERA 41 | QList cameras = QCameraInfo::availableCameras(); 42 | // I have two cemaras and use the second one here 43 | camera = new QCamera(cameras[1]); 44 | viewfinder = new QCameraViewfinder(this); 45 | QCameraViewfinderSettings settings; 46 | // the size must be compatible with the camera 47 | settings.setResolution(QSize(800, 600)); 48 | camera->setViewfinder(viewfinder); 49 | camera->setViewfinderSettings(settings); 50 | main_layout->addWidget(viewfinder, 0, 0, 12, 1); 51 | #else 52 | imageScene = new QGraphicsScene(this); 53 | imageView = new QGraphicsView(imageScene); 54 | main_layout->addWidget(imageView, 0, 0, 12, 1); 55 | #endif 56 | 57 | // tools 58 | QGridLayout *tools_layout = new QGridLayout(); 59 | main_layout->addLayout(tools_layout, 12, 0, 1, 1); 60 | 61 | monitorCheckBox = new QCheckBox(this); 62 | monitorCheckBox->setText("Monitor On/Off"); 63 | tools_layout->addWidget(monitorCheckBox, 0, 0); 64 | connect(monitorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateMonitorStatus(int))); 65 | 66 | recordButton = new QPushButton(this); 67 | recordButton->setText("Record"); 68 | tools_layout->addWidget(recordButton, 0, 1, Qt::AlignHCenter); 69 | tools_layout->addWidget(new QLabel(this), 0, 2); 70 | connect(recordButton, SIGNAL(clicked(bool)), this, SLOT(recordingStartStop())); 71 | 72 | // list of saved videos 73 | saved_list = new QListView(this); 74 | saved_list->setViewMode(QListView::IconMode); 75 | saved_list->setResizeMode(QListView::Adjust); 76 | saved_list->setSpacing(5); 77 | saved_list->setWrapping(false); 78 | list_model = new QStandardItemModel(this); 79 | saved_list->setModel(list_model); 80 | main_layout->addWidget(saved_list, 13, 0, 4, 1); 81 | 82 | QWidget *widget = new QWidget(); 83 | widget->setLayout(main_layout); 84 | setCentralWidget(widget); 85 | 86 | // setup status bar 87 | mainStatusBar = statusBar(); 88 | mainStatusLabel = new QLabel(mainStatusBar); 89 | mainStatusBar->addPermanentWidget(mainStatusLabel); 90 | mainStatusLabel->setText("Gazer is Ready"); 91 | 92 | createActions(); 93 | populateSavedList(); 94 | } 95 | 96 | void MainWindow::createActions() 97 | { 98 | // create actions, add them to menus 99 | cameraInfoAction = new QAction("Camera &Information", this); 100 | fileMenu->addAction(cameraInfoAction); 101 | openCameraAction = new QAction("&Open Camera", this); 102 | fileMenu->addAction(openCameraAction); 103 | calcFPSAction = new QAction("&Calculate FPS", this); 104 | fileMenu->addAction(calcFPSAction); 105 | exitAction = new QAction("E&xit", this); 106 | fileMenu->addAction(exitAction); 107 | 108 | // connect the signals and slots 109 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 110 | connect(cameraInfoAction, SIGNAL(triggered(bool)), this, SLOT(showCameraInfo())); 111 | connect(openCameraAction, SIGNAL(triggered(bool)), this, SLOT(openCamera())); 112 | connect(calcFPSAction, SIGNAL(triggered(bool)), this, SLOT(calculateFPS())); 113 | } 114 | 115 | void MainWindow::showCameraInfo() 116 | { 117 | QList cameras = QCameraInfo::availableCameras(); 118 | QString info = QString("Available Cameras: \n"); 119 | 120 | foreach (const QCameraInfo &cameraInfo, cameras) { 121 | info += " - " + cameraInfo.deviceName() + ": "; 122 | info += cameraInfo.description() + "\n"; 123 | } 124 | QMessageBox::information(this, "Cameras", info); 125 | } 126 | 127 | 128 | #ifdef GAZER_USE_QT_CAMERA 129 | void MainWindow::openCamera() 130 | { 131 | camera->setCaptureMode(QCamera::CaptureVideo); 132 | camera->start(); 133 | } 134 | #else 135 | void MainWindow::openCamera() 136 | { 137 | if(capturer != nullptr) { 138 | // if a thread is already running, stop it 139 | capturer->setRunning(false); 140 | disconnect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 141 | disconnect(capturer, &CaptureThread::fpsChanged, this, &MainWindow::updateFPS); 142 | disconnect(capturer, &CaptureThread::videoSaved, this, &MainWindow::appendSavedVideo); 143 | connect(capturer, &CaptureThread::finished, capturer, &CaptureThread::deleteLater); 144 | } 145 | // I am using my second camera whose Index is 2. Usually, the 146 | // Index of the first camera is 0. 147 | int camID = 2; 148 | capturer = new CaptureThread(camID, data_lock); 149 | connect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 150 | connect(capturer, &CaptureThread::fpsChanged, this, &MainWindow::updateFPS); 151 | connect(capturer, &CaptureThread::videoSaved, this, &MainWindow::appendSavedVideo); 152 | capturer->start(); 153 | mainStatusLabel->setText(QString("Capturing Camera %1").arg(camID)); 154 | monitorCheckBox->setCheckState(Qt::Unchecked); 155 | recordButton->setText("Record"); 156 | recordButton->setEnabled(true); 157 | } 158 | #endif 159 | 160 | void MainWindow::calculateFPS() 161 | { 162 | if(capturer != nullptr) { 163 | capturer->startCalcFPS(); 164 | } 165 | } 166 | 167 | void MainWindow::updateFrame(cv::Mat *mat) 168 | { 169 | data_lock->lock(); 170 | currentFrame = *mat; 171 | data_lock->unlock(); 172 | 173 | QImage frame( 174 | currentFrame.data, 175 | currentFrame.cols, 176 | currentFrame.rows, 177 | currentFrame.step, 178 | QImage::Format_RGB888); 179 | QPixmap image = QPixmap::fromImage(frame); 180 | 181 | imageScene->clear(); 182 | imageView->resetMatrix(); 183 | imageScene->addPixmap(image); 184 | imageScene->update(); 185 | imageView->setSceneRect(image.rect()); 186 | } 187 | 188 | void MainWindow::updateFPS(float fps) 189 | { 190 | mainStatusLabel->setText(QString("FPS of current camera is %1").arg(fps)); 191 | } 192 | 193 | 194 | void MainWindow::recordingStartStop() { 195 | QString text = recordButton->text(); 196 | if(text == "Record" && capturer != nullptr) { 197 | capturer->setVideoSavingStatus(CaptureThread::STARTING); 198 | recordButton->setText("Stop Recording"); 199 | monitorCheckBox->setCheckState(Qt::Unchecked); 200 | monitorCheckBox->setEnabled(false); 201 | } else if(text == "Stop Recording" && capturer != nullptr) { 202 | capturer->setVideoSavingStatus(CaptureThread::STOPPING); 203 | recordButton->setText("Record"); 204 | monitorCheckBox->setEnabled(true); 205 | } 206 | } 207 | 208 | 209 | void MainWindow::populateSavedList() 210 | { 211 | QDir dir(Utilities::getDataPath()); 212 | QStringList nameFilters; 213 | nameFilters << "*.jpg"; 214 | QFileInfoList files = dir.entryInfoList( 215 | nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name); 216 | 217 | foreach(QFileInfo cover, files) { 218 | QString name = cover.baseName(); 219 | QStandardItem *item = new QStandardItem(); 220 | list_model->appendRow(item); 221 | QModelIndex index = list_model->indexFromItem(item); 222 | list_model->setData(index, QPixmap(cover.absoluteFilePath()).scaledToHeight(145), Qt::DecorationRole); 223 | list_model->setData(index, name, Qt::DisplayRole); 224 | } 225 | } 226 | 227 | void MainWindow::appendSavedVideo(QString name) 228 | { 229 | QString cover = Utilities::getSavedVideoPath(name, "jpg"); 230 | QStandardItem *item = new QStandardItem(); 231 | list_model->appendRow(item); 232 | QModelIndex index = list_model->indexFromItem(item); 233 | list_model->setData(index, QPixmap(cover).scaledToHeight(145), Qt::DecorationRole); 234 | list_model->setData(index, name, Qt::DisplayRole); 235 | saved_list->scrollTo(index); 236 | } 237 | 238 | void MainWindow::updateMonitorStatus(int status) 239 | { 240 | if(capturer == nullptr) { 241 | return; 242 | } 243 | if(status) { 244 | capturer->setMotionDetectingStatus(true); 245 | recordButton->setEnabled(false); 246 | } else { 247 | capturer->setMotionDetectingStatus(false); 248 | recordButton->setEnabled(true); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef GAZER_USE_QT_CAMERA 19 | #include 20 | #include 21 | #endif 22 | 23 | #include "opencv2/opencv.hpp" 24 | 25 | #include "capture_thread.h" 26 | 27 | class MainWindow : public QMainWindow 28 | { 29 | Q_OBJECT 30 | 31 | public: 32 | explicit MainWindow(QWidget *parent=nullptr); 33 | ~MainWindow(); 34 | 35 | private: 36 | void initUI(); 37 | void createActions(); 38 | void populateSavedList(); 39 | 40 | private slots: 41 | void showCameraInfo(); 42 | void openCamera(); 43 | void updateFrame(cv::Mat*); 44 | void calculateFPS(); 45 | void updateFPS(float); 46 | void recordingStartStop(); 47 | void appendSavedVideo(QString name); 48 | void updateMonitorStatus(int status); 49 | 50 | private: 51 | QMenu *fileMenu; 52 | 53 | QAction *cameraInfoAction; 54 | QAction *openCameraAction; 55 | QAction *calcFPSAction; 56 | QAction *exitAction; 57 | 58 | QGraphicsScene *imageScene; 59 | QGraphicsView *imageView; 60 | 61 | #ifdef GAZER_USE_QT_CAMERA 62 | QCamera *camera; 63 | QCameraViewfinder *viewfinder; 64 | #endif 65 | 66 | QCheckBox *monitorCheckBox; 67 | QPushButton *recordButton; 68 | 69 | QListView *saved_list; 70 | QStandardItemModel *list_model; 71 | 72 | QStatusBar *mainStatusBar; 73 | QLabel *mainStatusLabel; 74 | 75 | cv::Mat currentFrame; 76 | 77 | // for capture thread 78 | QMutex *data_lock; 79 | CaptureThread *capturer; 80 | }; 81 | 82 | #endif // MAINWINDOW_H 83 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "utilities.h" 16 | 17 | QString Utilities::getDataPath() 18 | { 19 | QString user_movie_path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation)[0]; 20 | QDir movie_dir(user_movie_path); 21 | movie_dir.mkpath("Gazer"); 22 | return movie_dir.absoluteFilePath("Gazer"); 23 | } 24 | 25 | QString Utilities::newSavedVideoName() 26 | { 27 | QDateTime time = QDateTime::currentDateTime(); 28 | return time.toString("yyyy-MM-dd+HH:mm:ss"); 29 | } 30 | 31 | QString Utilities::getSavedVideoPath(QString name, QString postfix) 32 | { 33 | return QString("%1/%2.%3").arg(Utilities::getDataPath(), name, postfix); 34 | } 35 | 36 | void Utilities::notifyMobile(int cameraID) 37 | { 38 | QString endpoint = "https://maker.ifttt.com/trigger/..."; 39 | // CHANGE endpoint TO YOURS HERE: 40 | // https://maker.ifttt.com/trigger/Motion-Detected-by-Gazer/with/key/-YOUR_KEY 41 | // QString endpoint = QUrl("https://maker.ifttt.com/YOUR_END PIOIN"); 42 | QNetworkRequest request = QNetworkRequest(QUrl(endpoint)); 43 | request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); 44 | QJsonObject json; 45 | json.insert("value1", QString("%1").arg(cameraID)); 46 | json.insert("value2", QHostInfo::localHostName()); 47 | QNetworkAccessManager nam; 48 | QNetworkReply *rep = nam.post(request, QJsonDocument(json).toJson()); 49 | while(!rep->isFinished()) { 50 | QApplication::processEvents(); 51 | } 52 | // QString strReply = (QString)rep->readAll(); 53 | // qDebug()<<"Test: "<deleteLater(); 55 | } 56 | -------------------------------------------------------------------------------- /Chapter-03/Gazer/utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H_ 2 | #define UTILITIES_H_ 3 | 4 | #include 5 | 6 | class Utilities 7 | { 8 | public: 9 | static QString getDataPath(); 10 | static QString newSavedVideoName(); 11 | static QString getSavedVideoPath(QString name, QString postfix); 12 | static void notifyMobile(int cameraID); 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/Facetious.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = Facetious 3 | INCLUDEPATH += . 4 | 5 | QT += core gui multimedia 6 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 7 | 8 | unix: !mac { 9 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 10 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_video -lopencv_videoio -lopencv_objdetect -lopencv_face 11 | } 12 | 13 | unix: mac { 14 | INCLUDEPATH += /path/to/opencv/include/opencv4 15 | LIBS += -L/path/to/opencv/lib -lopencv_world 16 | } 17 | 18 | win32 { 19 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 20 | LIBS += -lc:/path/to/opencv/lib/opencv_world 21 | } 22 | 23 | 24 | # Input 25 | HEADERS += mainwindow.h capture_thread.h utilities.h 26 | SOURCES += main.cpp mainwindow.cpp capture_thread.cpp utilities.cpp 27 | 28 | # DEFINES += OPENCV_DATA_DIR=\\\"/usr/share/opencv/\\\" 29 | DEFINES += OPENCV_DATA_DIR=\\\"/home/kdr2/programs/opencv/share/opencv4/\\\" 30 | 31 | RESOURCES = images.qrc 32 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/capture_thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "utilities.h" 7 | #include "capture_thread.h" 8 | 9 | CaptureThread::CaptureThread(int camera, QMutex *lock): 10 | running(false), cameraID(camera), videoPath(""), data_lock(lock) 11 | { 12 | frame_width = frame_height = 0; 13 | taking_photo = false; 14 | 15 | loadOrnaments(); 16 | masks_flag = 0; 17 | } 18 | 19 | CaptureThread::CaptureThread(QString videoPath, QMutex *lock): 20 | running(false), cameraID(-1), videoPath(videoPath), data_lock(lock) 21 | { 22 | frame_width = frame_height = 0; 23 | taking_photo = false; 24 | 25 | loadOrnaments(); 26 | masks_flag = 0; 27 | } 28 | 29 | CaptureThread::~CaptureThread() { 30 | } 31 | 32 | void CaptureThread::run() { 33 | running = true; 34 | cv::VideoCapture cap(cameraID); 35 | // cv::VideoCapture cap("/home/kdr2/Videos/WIN_20190123_20_14_56_Pro.mp4"); 36 | cv::Mat tmp_frame; 37 | 38 | frame_width = cap.get(cv::CAP_PROP_FRAME_WIDTH); 39 | frame_height = cap.get(cv::CAP_PROP_FRAME_HEIGHT); 40 | 41 | classifier = new cv::CascadeClassifier(OPENCV_DATA_DIR "haarcascades/haarcascade_frontalface_default.xml"); 42 | mark_detector = cv::face::createFacemarkLBF(); 43 | QString model_data = QApplication::instance()->applicationDirPath() + "/data/lbfmodel.yaml"; 44 | mark_detector->loadModel(model_data.toStdString()); 45 | while(running) { 46 | cap >> tmp_frame; 47 | if (tmp_frame.empty()) { 48 | break; 49 | } 50 | 51 | if(masks_flag > 0) 52 | detectFaces(tmp_frame); 53 | 54 | if(taking_photo) { 55 | takePhoto(tmp_frame); 56 | } 57 | 58 | cvtColor(tmp_frame, tmp_frame, cv::COLOR_BGR2RGB); 59 | data_lock->lock(); 60 | frame = tmp_frame; 61 | data_lock->unlock(); 62 | emit frameCaptured(&frame); 63 | } 64 | cap.release(); 65 | delete classifier; 66 | classifier = nullptr; 67 | running = false; 68 | } 69 | 70 | 71 | void CaptureThread::takePhoto(cv::Mat &frame) 72 | { 73 | QString photo_name = Utilities::newPhotoName(); 74 | QString photo_path = Utilities::getPhotoPath(photo_name, "jpg"); 75 | cv::imwrite(photo_path.toStdString(), frame); 76 | emit photoTaken(photo_name); 77 | taking_photo = false; 78 | } 79 | 80 | void CaptureThread::detectFaces(cv::Mat &frame) 81 | { 82 | vector faces; 83 | cv::Mat gray_frame; 84 | cv::cvtColor(frame, gray_frame, cv::COLOR_BGR2GRAY); 85 | classifier->detectMultiScale(gray_frame, faces, 1.3, 5); 86 | 87 | cv::Scalar color = cv::Scalar(0, 0, 255); // red 88 | 89 | // draw the circumscribe rectangles 90 | if (isMaskOn(RECTANGLE)) { 91 | for(size_t i = 0; i < faces.size(); i++) { 92 | cv::rectangle(frame, faces[i], color, 1); 93 | } 94 | } 95 | 96 | vector< vector > shapes; 97 | if (mark_detector->fit(frame, faces, shapes)) { 98 | // draw facial land marks 99 | for (unsigned long i=0; i &marks) 140 | { 141 | // resize 142 | cv::Mat ornament; 143 | double distance = cv::norm(marks[45] - marks[36]) * 1.5; 144 | cv::resize(glasses, ornament, cv::Size(0, 0), distance / glasses.cols, distance / glasses.cols, cv::INTER_NEAREST); 145 | 146 | // rotate 147 | double angle = -atan((marks[45].y - marks[36].y) / (marks[45].x - marks[36].x)); 148 | cv::Point2f center = cv::Point(ornament.cols/2, ornament.rows/2); 149 | cv::Mat rotateMatrix = cv::getRotationMatrix2D(center, angle * 180 / 3.14, 1.0); 150 | 151 | cv::Mat rotated; 152 | cv::warpAffine( 153 | ornament, rotated, rotateMatrix, ornament.size(), 154 | cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255)); 155 | 156 | // paint 157 | center = cv::Point((marks[45].x + marks[36].x) / 2, (marks[45].y + marks[36].y) / 2); 158 | cv::Rect rec(center.x - rotated.cols / 2, center.y - rotated.rows / 2, rotated.cols, rotated.rows); 159 | frame(rec) &= rotated; 160 | } 161 | 162 | void CaptureThread::drawMustache(cv::Mat &frame, vector &marks) 163 | { 164 | // resize 165 | cv::Mat ornament; 166 | double distance = cv::norm(marks[54] - marks[48]) * 1.5; 167 | cv::resize(mustache, ornament, cv::Size(0, 0), distance / mustache.cols, distance / mustache.cols, cv::INTER_NEAREST); 168 | 169 | // rotate 170 | double angle = -atan((marks[54].y - marks[48].y) / (marks[54].x - marks[48].x)); 171 | cv::Point2f center = cv::Point(ornament.cols/2, ornament.rows/2); 172 | cv::Mat rotateMatrix = cv::getRotationMatrix2D(center, angle * 180 / 3.14, 1.0); 173 | 174 | cv::Mat rotated; 175 | cv::warpAffine( 176 | ornament, rotated, rotateMatrix, ornament.size(), 177 | cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255)); 178 | 179 | // paint 180 | center = cv::Point((marks[33].x + marks[51].x) / 2, (marks[33].y + marks[51].y) / 2); 181 | cv::Rect rec(center.x - rotated.cols / 2, center.y - rotated.rows / 2, rotated.cols, rotated.rows); 182 | frame(rec) &= rotated; 183 | } 184 | 185 | void CaptureThread::drawMouseNose(cv::Mat &frame, vector &marks) 186 | { 187 | // resize 188 | cv::Mat ornament; 189 | double distance = cv::norm(marks[13] - marks[3]); 190 | cv::resize(mouse_nose, ornament, cv::Size(0, 0), distance / mouse_nose.cols, distance / mouse_nose.cols, cv::INTER_NEAREST); 191 | 192 | // rotate 193 | double angle = -atan((marks[16].y - marks[0].y) / (marks[16].x - marks[0].x)); 194 | cv::Point2f center = cv::Point(ornament.cols/2, ornament.rows/2); 195 | cv::Mat rotateMatrix = cv::getRotationMatrix2D(center, angle * 180 / 3.14, 1.0); 196 | 197 | cv::Mat rotated; 198 | cv::warpAffine( 199 | ornament, rotated, rotateMatrix, ornament.size(), 200 | cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(255, 255, 255)); 201 | 202 | // paint 203 | center = marks[30]; 204 | cv::Rect rec(center.x - rotated.cols / 2, center.y - rotated.rows / 2, rotated.cols, rotated.rows); 205 | frame(rec) &= rotated; 206 | } 207 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/capture_thread.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef CAPTURE_THREAD_H 3 | #define CAPTURE_THREAD_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | #include "opencv2/objdetect.hpp" 11 | #include "opencv2/face/facemark.hpp" 12 | 13 | using namespace std; 14 | 15 | class CaptureThread : public QThread 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit CaptureThread(int camera, QMutex *lock); 20 | explicit CaptureThread(QString videoPath, QMutex *lock); 21 | ~CaptureThread(); 22 | void setRunning(bool run) {running = run; }; 23 | void takePhoto() {taking_photo = true; } 24 | 25 | enum MASK_TYPE{ 26 | RECTANGLE = 0, 27 | LANDMARKS, 28 | GLASSES, 29 | MUSTACHE, 30 | MOUSE_NOSE, 31 | MASK_COUNT, 32 | }; 33 | 34 | void updateMasksFlag(MASK_TYPE type, bool on_or_off) { 35 | uint8_t bit = 1 << type; 36 | if(on_or_off) { 37 | masks_flag |= bit; 38 | } else { 39 | masks_flag &= ~bit; 40 | } 41 | }; 42 | 43 | protected: 44 | void run() override; 45 | 46 | signals: 47 | void frameCaptured(cv::Mat *data); 48 | void photoTaken(QString name); 49 | 50 | private: 51 | void takePhoto(cv::Mat &frame); 52 | void detectFaces(cv::Mat &frame); 53 | void loadOrnaments(); 54 | void drawGlasses(cv::Mat &frame, vector &marks); 55 | void drawMustache(cv::Mat &frame, vector &marks); 56 | void drawMouseNose(cv::Mat &frame, vector &marks); 57 | bool isMaskOn(MASK_TYPE type) {return (masks_flag & (1 << type)) != 0; }; 58 | private: 59 | bool running; 60 | int cameraID; 61 | QString videoPath; 62 | QMutex *data_lock; 63 | cv::Mat frame; 64 | 65 | int frame_width, frame_height; 66 | 67 | // take photos 68 | bool taking_photo; 69 | 70 | // face detection 71 | cv::CascadeClassifier *classifier; 72 | cv::Ptr mark_detector; 73 | 74 | // mask ornaments 75 | cv::Mat glasses; 76 | cv::Mat mustache; 77 | cv::Mat mouse_nose; 78 | 79 | uint8_t masks_flag; 80 | }; 81 | 82 | #endif // CAPTURE_THREAD_H 83 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/data/README.txt: -------------------------------------------------------------------------------- 1 | Please download lbfmodel.yaml from 2 | https://raw.githubusercontent.com/kurnianggoro/GSOC2017/master/data/lbfmodel.yaml 3 | and place it in this directory. 4 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | images/glasses.jpg 5 | images/mustache.jpg 6 | images/mouse-nose.jpg 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/images/glasses.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-04/Facetious/images/glasses.jpg -------------------------------------------------------------------------------- /Chapter-04/Facetious/images/mouse-nose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-04/Facetious/images/mouse-nose.jpg -------------------------------------------------------------------------------- /Chapter-04/Facetious/images/mustache.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-04/Facetious/images/mustache.jpg -------------------------------------------------------------------------------- /Chapter-04/Facetious/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("Facetious"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "mainwindow.h" 14 | #include "utilities.h" 15 | 16 | MainWindow::MainWindow(QWidget *parent) : 17 | QMainWindow(parent) 18 | , fileMenu(nullptr) 19 | , capturer(nullptr) 20 | { 21 | initUI(); 22 | data_lock = new QMutex(); 23 | } 24 | 25 | MainWindow::~MainWindow() 26 | { 27 | } 28 | 29 | void MainWindow::initUI() 30 | { 31 | this->resize(1000, 800); 32 | // setup menubar 33 | fileMenu = menuBar()->addMenu("&File"); 34 | 35 | // main area 36 | QGridLayout *main_layout = new QGridLayout(); 37 | 38 | imageScene = new QGraphicsScene(this); 39 | imageView = new QGraphicsView(imageScene); 40 | main_layout->addWidget(imageView, 0, 0, 12, 1); 41 | 42 | // tools 43 | QGridLayout *tools_layout = new QGridLayout(); 44 | main_layout->addLayout(tools_layout, 12, 0, 1, 1); 45 | 46 | shutterButton = new QPushButton(this); 47 | shutterButton->setText("Take a Photo"); 48 | tools_layout->addWidget(shutterButton, 0, 0, Qt::AlignHCenter); 49 | connect(shutterButton, SIGNAL(clicked(bool)), this, SLOT(takePhoto())); 50 | 51 | // masks 52 | QGridLayout *masks_layout = new QGridLayout(); 53 | main_layout->addLayout(masks_layout, 13, 0, 1, 1); 54 | masks_layout->addWidget(new QLabel("Select Masks:", this)); 55 | for (int i = 0; i < CaptureThread::MASK_COUNT; i++){ 56 | mask_checkboxes[i] = new QCheckBox(this); 57 | masks_layout->addWidget(mask_checkboxes[i], 0, i + 1); 58 | connect(mask_checkboxes[i], SIGNAL(stateChanged(int)), this, SLOT(updateMasks(int))); 59 | } 60 | mask_checkboxes[0]->setText("Rectangle"); 61 | mask_checkboxes[1]->setText("Landmarks"); 62 | mask_checkboxes[2]->setText("Glasses"); 63 | mask_checkboxes[3]->setText("Mustache"); 64 | mask_checkboxes[4]->setText("Mouse Nose"); 65 | 66 | // list of saved photos 67 | saved_list = new QListView(this); 68 | saved_list->setViewMode(QListView::IconMode); 69 | saved_list->setResizeMode(QListView::Adjust); 70 | saved_list->setSpacing(5); 71 | saved_list->setWrapping(false); 72 | list_model = new QStandardItemModel(this); 73 | saved_list->setModel(list_model); 74 | main_layout->addWidget(saved_list, 14, 0, 4, 1); 75 | 76 | QWidget *widget = new QWidget(); 77 | widget->setLayout(main_layout); 78 | setCentralWidget(widget); 79 | 80 | // setup status bar 81 | mainStatusBar = statusBar(); 82 | mainStatusLabel = new QLabel(mainStatusBar); 83 | mainStatusBar->addPermanentWidget(mainStatusLabel); 84 | mainStatusLabel->setText("Facetious is Ready"); 85 | 86 | createActions(); 87 | populateSavedList(); 88 | } 89 | 90 | void MainWindow::createActions() 91 | { 92 | // create actions, add them to menus 93 | cameraInfoAction = new QAction("Camera &Information", this); 94 | fileMenu->addAction(cameraInfoAction); 95 | openCameraAction = new QAction("&Open Camera", this); 96 | fileMenu->addAction(openCameraAction); 97 | exitAction = new QAction("E&xit", this); 98 | fileMenu->addAction(exitAction); 99 | 100 | // connect the signals and slots 101 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 102 | connect(cameraInfoAction, SIGNAL(triggered(bool)), this, SLOT(showCameraInfo())); 103 | connect(openCameraAction, SIGNAL(triggered(bool)), this, SLOT(openCamera())); 104 | } 105 | 106 | void MainWindow::showCameraInfo() 107 | { 108 | QList cameras = QCameraInfo::availableCameras(); 109 | QString info = QString("Available Cameras: \n"); 110 | 111 | foreach (const QCameraInfo &cameraInfo, cameras) 112 | info += " - " + cameraInfo.deviceName() + "\n"; 113 | 114 | QMessageBox::information(this, "Cameras", info); 115 | } 116 | 117 | 118 | void MainWindow::openCamera() 119 | { 120 | if(capturer != nullptr) { 121 | // if a thread is already running, stop it 122 | capturer->setRunning(false); 123 | disconnect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 124 | disconnect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 125 | connect(capturer, &CaptureThread::finished, capturer, &CaptureThread::deleteLater); 126 | } 127 | 128 | for (int i = 0; i < CaptureThread::MASK_COUNT; i++){ 129 | mask_checkboxes[i]->setCheckState(Qt::Unchecked); 130 | } 131 | 132 | // I am using my second camera whose Index is 2. Usually, the 133 | // Index of the first camera is 0. 134 | int camID = 2; 135 | capturer = new CaptureThread(camID, data_lock); 136 | connect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 137 | connect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 138 | capturer->start(); 139 | mainStatusLabel->setText(QString("Capturing Camera %1").arg(camID)); 140 | } 141 | 142 | 143 | void MainWindow::updateFrame(cv::Mat *mat) 144 | { 145 | data_lock->lock(); 146 | currentFrame = *mat; 147 | data_lock->unlock(); 148 | 149 | QImage frame( 150 | currentFrame.data, 151 | currentFrame.cols, 152 | currentFrame.rows, 153 | currentFrame.step, 154 | QImage::Format_RGB888); 155 | QPixmap image = QPixmap::fromImage(frame); 156 | 157 | imageScene->clear(); 158 | imageView->resetMatrix(); 159 | imageScene->addPixmap(image); 160 | imageScene->update(); 161 | imageView->setSceneRect(image.rect()); 162 | } 163 | 164 | 165 | void MainWindow::takePhoto() 166 | { 167 | if(capturer != nullptr) { 168 | capturer->takePhoto(); 169 | } 170 | } 171 | 172 | 173 | void MainWindow::populateSavedList() 174 | { 175 | QDir dir(Utilities::getDataPath()); 176 | QStringList nameFilters; 177 | nameFilters << "*.jpg"; 178 | QFileInfoList files = dir.entryInfoList( 179 | nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name); 180 | 181 | foreach(QFileInfo photo, files) { 182 | QString name = photo.baseName(); 183 | QStandardItem *item = new QStandardItem(); 184 | list_model->appendRow(item); 185 | QModelIndex index = list_model->indexFromItem(item); 186 | list_model->setData(index, QPixmap(photo.absoluteFilePath()).scaledToHeight(145), Qt::DecorationRole); 187 | list_model->setData(index, name, Qt::DisplayRole); 188 | } 189 | } 190 | 191 | void MainWindow::appendSavedPhoto(QString name) 192 | { 193 | QString photo_path = Utilities::getPhotoPath(name, "jpg"); 194 | QStandardItem *item = new QStandardItem(); 195 | list_model->appendRow(item); 196 | QModelIndex index = list_model->indexFromItem(item); 197 | list_model->setData(index, QPixmap(photo_path).scaledToHeight(145), Qt::DecorationRole); 198 | list_model->setData(index, name, Qt::DisplayRole); 199 | saved_list->scrollTo(index); 200 | } 201 | 202 | 203 | void MainWindow::updateMasks(int status) 204 | { 205 | if(capturer == nullptr) { 206 | return; 207 | } 208 | 209 | QCheckBox *box = qobject_cast(sender()); 210 | for (int i = 0; i < CaptureThread::MASK_COUNT; i++){ 211 | if (mask_checkboxes[i] == box) { 212 | capturer->updateMasksFlag(static_cast(i), status != 0); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "opencv2/opencv.hpp" 19 | 20 | #include "capture_thread.h" 21 | 22 | class MainWindow : public QMainWindow 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit MainWindow(QWidget *parent=nullptr); 28 | ~MainWindow(); 29 | 30 | private: 31 | void initUI(); 32 | void createActions(); 33 | void populateSavedList(); 34 | 35 | private slots: 36 | void showCameraInfo(); 37 | void openCamera(); 38 | void updateFrame(cv::Mat*); 39 | void takePhoto(); 40 | void appendSavedPhoto(QString name); 41 | void updateMasks(int status); 42 | 43 | private: 44 | QMenu *fileMenu; 45 | 46 | QAction *cameraInfoAction; 47 | QAction *openCameraAction; 48 | QAction *exitAction; 49 | 50 | QCheckBox *mask_checkboxes[CaptureThread::MASK_COUNT]; 51 | 52 | QGraphicsScene *imageScene; 53 | QGraphicsView *imageView; 54 | 55 | QPushButton *shutterButton; 56 | 57 | QListView *saved_list; 58 | QStandardItemModel *list_model; 59 | 60 | QStatusBar *mainStatusBar; 61 | QLabel *mainStatusLabel; 62 | 63 | cv::Mat currentFrame; 64 | 65 | // for capture thread 66 | QMutex *data_lock; 67 | CaptureThread *capturer; 68 | }; 69 | 70 | #endif // MAINWINDOW_H 71 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utilities.h" 9 | 10 | QString Utilities::getDataPath() 11 | { 12 | QString user_pictures_path = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)[0]; 13 | QDir pictures_dir(user_pictures_path); 14 | pictures_dir.mkpath("Facetious"); 15 | return pictures_dir.absoluteFilePath("Facetious"); 16 | } 17 | 18 | QString Utilities::newPhotoName() 19 | { 20 | QDateTime time = QDateTime::currentDateTime(); 21 | return time.toString("yyyy-MM-dd+HH:mm:ss"); 22 | } 23 | 24 | QString Utilities::getPhotoPath(QString name, QString postfix) 25 | { 26 | return QString("%1/%2.%3").arg(Utilities::getDataPath(), name, postfix); 27 | } 28 | -------------------------------------------------------------------------------- /Chapter-04/Facetious/utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H_ 2 | #define UTILITIES_H_ 3 | 4 | #include 5 | 6 | class Utilities 7 | { 8 | public: 9 | static QString getDataPath(); 10 | static QString newPhotoName(); 11 | static QString getPhotoPath(QString name, QString postfix); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/Literacy.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = Literacy 3 | 4 | QT += core gui 5 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 6 | 7 | INCLUDEPATH += . 8 | 9 | # use your own path in the following config 10 | unix: { 11 | INCLUDEPATH += /home/kdr2/programs/tesseract/include 12 | LIBS += -L/home/kdr2/programs/tesseract/lib -ltesseract 13 | } 14 | 15 | win32 { 16 | INCLUDEPATH += c:/path/to/tesseract/include 17 | LIBS += -lc:/path/to/opencv/lib/tesseract 18 | } 19 | 20 | # opencv config 21 | unix: !mac { 22 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 23 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_dnn 24 | } 25 | 26 | unix: mac { 27 | INCLUDEPATH += /path/to/opencv/include/opencv4 28 | LIBS += -L/path/to/opencv/lib -lopencv_world 29 | } 30 | 31 | win32 { 32 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 33 | LIBS += -lc:/path/to/opencv/lib/opencv_world 34 | } 35 | 36 | 37 | DEFINES += QT_DEPRECATED_WARNINGS 38 | DEFINES += TESSDATA_PREFIX=\\\"/home/kdr2/programs/tesseract/share/tessdata/\\\" 39 | 40 | # Input 41 | HEADERS += mainwindow.h screencapturer.h 42 | SOURCES += main.cpp mainwindow.cpp screencapturer.cpp 43 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("Literacy"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "mainwindow.h" 9 | #include "screencapturer.h" 10 | 11 | MainWindow::MainWindow(QWidget *parent) : 12 | QMainWindow(parent) 13 | , currentImage(nullptr) 14 | , tesseractAPI(nullptr) 15 | { 16 | initUI(); 17 | } 18 | 19 | MainWindow::~MainWindow() 20 | { 21 | // Destroy used object and release memory 22 | if(tesseractAPI != nullptr) { 23 | tesseractAPI->End(); 24 | delete tesseractAPI; 25 | } 26 | } 27 | 28 | void MainWindow::initUI() 29 | { 30 | this->resize(800, 600); 31 | // setup menubar 32 | fileMenu = menuBar()->addMenu("&File"); 33 | 34 | // setup toolbar 35 | fileToolBar = addToolBar("File"); 36 | 37 | // main area 38 | QSplitter *splitter = new QSplitter(Qt::Horizontal, this); 39 | 40 | imageScene = new QGraphicsScene(this); 41 | imageView = new QGraphicsView(imageScene); 42 | splitter->addWidget(imageView); 43 | 44 | editor = new QTextEdit(this); 45 | splitter->addWidget(editor); 46 | 47 | QList sizes = {400, 400}; 48 | splitter->setSizes(sizes); 49 | 50 | setCentralWidget(splitter); 51 | 52 | // setup status bar 53 | mainStatusBar = statusBar(); 54 | mainStatusLabel = new QLabel(mainStatusBar); 55 | mainStatusBar->addPermanentWidget(mainStatusLabel); 56 | mainStatusLabel->setText("Application Information will be here!"); 57 | 58 | createActions(); 59 | } 60 | 61 | void MainWindow::createActions() 62 | { 63 | // create actions, add them to menus 64 | openAction = new QAction("&Open", this); 65 | fileMenu->addAction(openAction); 66 | saveImageAsAction = new QAction("Save &Image as", this); 67 | fileMenu->addAction(saveImageAsAction); 68 | saveTextAsAction = new QAction("Save &Text as", this); 69 | fileMenu->addAction(saveTextAsAction); 70 | exitAction = new QAction("E&xit", this); 71 | fileMenu->addAction(exitAction); 72 | 73 | // add actions to toolbars 74 | fileToolBar->addAction(openAction); 75 | captureAction = new QAction("Capture Screen", this); 76 | fileToolBar->addAction(captureAction); 77 | ocrAction = new QAction("OCR", this); 78 | fileToolBar->addAction(ocrAction); 79 | detectAreaCheckBox = new QCheckBox("Detect Text Areas", this); 80 | fileToolBar->addWidget(detectAreaCheckBox); 81 | 82 | // connect the signals and slots 83 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 84 | connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openImage())); 85 | connect(saveImageAsAction, SIGNAL(triggered(bool)), this, SLOT(saveImageAs())); 86 | connect(saveTextAsAction, SIGNAL(triggered(bool)), this, SLOT(saveTextAs())); 87 | connect(ocrAction, SIGNAL(triggered(bool)), this, SLOT(extractText())); 88 | connect(captureAction, SIGNAL(triggered(bool)), this, SLOT(captureScreen())); 89 | 90 | setupShortcuts(); 91 | } 92 | 93 | void MainWindow::openImage() 94 | { 95 | QFileDialog dialog(this); 96 | dialog.setWindowTitle("Open Image"); 97 | dialog.setFileMode(QFileDialog::ExistingFile); 98 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 99 | QStringList filePaths; 100 | if (dialog.exec()) { 101 | filePaths = dialog.selectedFiles(); 102 | showImage(filePaths.at(0)); 103 | } 104 | } 105 | 106 | 107 | void MainWindow::showImage(QPixmap image) 108 | { 109 | imageScene->clear(); 110 | imageView->resetMatrix(); 111 | currentImage = imageScene->addPixmap(image); 112 | imageScene->update(); 113 | imageView->setSceneRect(image.rect()); 114 | } 115 | 116 | void MainWindow::showImage(QString path) 117 | { 118 | QPixmap image(path); 119 | showImage(image); 120 | currentImagePath = path; 121 | QString status = QString("%1, %2x%3, %4 Bytes").arg(path).arg(image.width()) 122 | .arg(image.height()).arg(QFile(path).size()); 123 | mainStatusLabel->setText(status); 124 | } 125 | 126 | void MainWindow::showImage(cv::Mat mat) 127 | { 128 | QImage image( 129 | mat.data, 130 | mat.cols, 131 | mat.rows, 132 | mat.step, 133 | QImage::Format_RGB888); 134 | 135 | QPixmap pixmap = QPixmap::fromImage(image); 136 | imageScene->clear(); 137 | imageView->resetMatrix(); 138 | currentImage = imageScene->addPixmap(pixmap); 139 | imageScene->update(); 140 | imageView->setSceneRect(pixmap.rect()); 141 | } 142 | 143 | void MainWindow::saveImageAs() 144 | { 145 | if (currentImage == nullptr) { 146 | QMessageBox::information(this, "Information", "Noting to save."); 147 | return; 148 | } 149 | QFileDialog dialog(this); 150 | dialog.setWindowTitle("Save Image As ..."); 151 | dialog.setFileMode(QFileDialog::AnyFile); 152 | dialog.setAcceptMode(QFileDialog::AcceptSave); 153 | dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)")); 154 | QStringList fileNames; 155 | if (dialog.exec()) { 156 | fileNames = dialog.selectedFiles(); 157 | if(QRegExp(".+\\.(png|bmp|jpg)").exactMatch(fileNames.at(0))) { 158 | currentImage->pixmap().save(fileNames.at(0)); 159 | } else { 160 | QMessageBox::information(this, "Error", "Save error: bad format or filename."); 161 | } 162 | } 163 | } 164 | 165 | void MainWindow::saveTextAs() 166 | { 167 | QFileDialog dialog(this); 168 | dialog.setWindowTitle("Save Text As ..."); 169 | dialog.setFileMode(QFileDialog::AnyFile); 170 | dialog.setAcceptMode(QFileDialog::AcceptSave); 171 | dialog.setNameFilter(tr("Text files (*.txt)")); 172 | QStringList fileNames; 173 | if (dialog.exec()) { 174 | fileNames = dialog.selectedFiles(); 175 | if(QRegExp(".+\\.(txt)").exactMatch(fileNames.at(0))) { 176 | QFile file(fileNames.at(0)); 177 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { 178 | QMessageBox::information(this, "Error", "Can't save text."); 179 | return; 180 | } 181 | QTextStream out(&file); 182 | out << editor->toPlainText() << "\n"; 183 | } else { 184 | QMessageBox::information(this, "Error", "Save error: bad format or filename."); 185 | } 186 | } 187 | } 188 | 189 | void MainWindow::setupShortcuts() 190 | { 191 | QList shortcuts; 192 | shortcuts << (Qt::CTRL + Qt::Key_O); 193 | openAction->setShortcuts(shortcuts); 194 | 195 | shortcuts.clear(); 196 | shortcuts << (Qt::CTRL + Qt::Key_Q); 197 | exitAction->setShortcuts(shortcuts); 198 | } 199 | 200 | void MainWindow::extractText() 201 | { 202 | if (currentImage == nullptr) { 203 | QMessageBox::information(this, "Information", "No opened image."); 204 | return; 205 | } 206 | 207 | char *old_ctype = strdup(setlocale(LC_ALL, NULL)); 208 | setlocale(LC_ALL, "C"); 209 | if (tesseractAPI == nullptr) { 210 | tesseractAPI = new tesseract::TessBaseAPI(); 211 | // Initialize tesseract-ocr with English, with specifying tessdata path 212 | if (tesseractAPI->Init(TESSDATA_PREFIX, "eng")) { 213 | QMessageBox::information(this, "Error", "Could not initialize tesseract."); 214 | return; 215 | } 216 | } 217 | 218 | QPixmap pixmap = currentImage->pixmap(); 219 | QImage image = pixmap.toImage(); 220 | image = image.convertToFormat(QImage::Format_RGB888); 221 | 222 | tesseractAPI->SetImage(image.bits(), image.width(), image.height(), 223 | 3, image.bytesPerLine()); 224 | 225 | if (detectAreaCheckBox->checkState() == Qt::Checked) { 226 | std::vector areas; 227 | cv::Mat newImage = detectTextAreas(image, areas); 228 | showImage(newImage); 229 | editor->setPlainText(""); 230 | for(cv::Rect &rect : areas) { 231 | tesseractAPI->SetRectangle(rect.x, rect.y, rect.width, rect.height); 232 | char *outText = tesseractAPI->GetUTF8Text(); 233 | editor->setPlainText(editor->toPlainText() + outText); 234 | delete [] outText; 235 | } 236 | } else { 237 | char *outText = tesseractAPI->GetUTF8Text(); 238 | editor->setPlainText(outText); 239 | delete [] outText; 240 | } 241 | 242 | setlocale(LC_ALL, old_ctype); 243 | free(old_ctype); 244 | } 245 | 246 | cv::Mat MainWindow::detectTextAreas(QImage &image, std::vector &areas) 247 | { 248 | float confThreshold = 0.5; 249 | float nmsThreshold = 0.4; 250 | int inputWidth = 320; 251 | int inputHeight = 320; 252 | std::string model = "./frozen_east_text_detection.pb"; 253 | // Load DNN network. 254 | if (net.empty()) { 255 | net = cv::dnn::readNet(model); 256 | } 257 | 258 | std::vector outs; 259 | std::vector layerNames(2); 260 | layerNames[0] = "feature_fusion/Conv_7/Sigmoid"; 261 | layerNames[1] = "feature_fusion/concat_3"; 262 | 263 | cv::Mat frame = cv::Mat( 264 | image.height(), 265 | image.width(), 266 | CV_8UC3, 267 | image.bits(), 268 | image.bytesPerLine()).clone(); 269 | cv::Mat blob; 270 | 271 | cv::dnn::blobFromImage( 272 | frame, blob, 273 | 1.0, cv::Size(inputWidth, inputHeight), 274 | cv::Scalar(123.68, 116.78, 103.94), true, false 275 | ); 276 | net.setInput(blob); 277 | net.forward(outs, layerNames); 278 | 279 | cv::Mat scores = outs[0]; 280 | cv::Mat geometry = outs[1]; 281 | 282 | std::vector boxes; 283 | std::vector confidences; 284 | decode(scores, geometry, confThreshold, boxes, confidences); 285 | 286 | std::vector indices; 287 | cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); 288 | 289 | // Render detections. 290 | cv::Point2f ratio((float)frame.cols / inputWidth, (float)frame.rows / inputHeight); 291 | cv::Scalar green = cv::Scalar(0, 255, 0); 292 | 293 | for (size_t i = 0; i < indices.size(); ++i) { 294 | cv::RotatedRect& box = boxes[indices[i]]; 295 | cv::Rect area = box.boundingRect(); 296 | area.x *= ratio.x; 297 | area.width *= ratio.x; 298 | area.y *= ratio.y; 299 | area.height *= ratio.y; 300 | areas.push_back(area); 301 | cv::rectangle(frame, area, green, 1); 302 | QString index = QString("%1").arg(i); 303 | cv::putText( 304 | frame, index.toStdString(), cv::Point2f(area.x, area.y - 2), 305 | cv::FONT_HERSHEY_SIMPLEX, 0.5, green, 1 306 | ); 307 | } 308 | return frame; 309 | } 310 | 311 | void MainWindow::decode(const cv::Mat& scores, const cv::Mat& geometry, float scoreThresh, 312 | std::vector& detections, std::vector& confidences) 313 | { 314 | CV_Assert(scores.dims == 4); CV_Assert(geometry.dims == 4); 315 | CV_Assert(scores.size[0] == 1); CV_Assert(scores.size[1] == 1); 316 | CV_Assert(geometry.size[0] == 1); CV_Assert(geometry.size[1] == 5); 317 | CV_Assert(scores.size[2] == geometry.size[2]); 318 | CV_Assert(scores.size[3] == geometry.size[3]); 319 | 320 | detections.clear(); 321 | const int height = scores.size[2]; 322 | const int width = scores.size[3]; 323 | for (int y = 0; y < height; ++y) { 324 | const float* scoresData = scores.ptr(0, 0, y); 325 | const float* x0_data = geometry.ptr(0, 0, y); 326 | const float* x1_data = geometry.ptr(0, 1, y); 327 | const float* x2_data = geometry.ptr(0, 2, y); 328 | const float* x3_data = geometry.ptr(0, 3, y); 329 | const float* anglesData = geometry.ptr(0, 4, y); 330 | for (int x = 0; x < width; ++x) { 331 | float score = scoresData[x]; 332 | if (score < scoreThresh) 333 | continue; 334 | 335 | // Decode a prediction. 336 | // Multiple by 4 because feature maps are 4 time less than input image. 337 | float offsetX = x * 4.0f, offsetY = y * 4.0f; 338 | float angle = anglesData[x]; 339 | float cosA = std::cos(angle); 340 | float sinA = std::sin(angle); 341 | float h = x0_data[x] + x2_data[x]; 342 | float w = x1_data[x] + x3_data[x]; 343 | 344 | cv::Point2f offset(offsetX + cosA * x1_data[x] + sinA * x2_data[x], 345 | offsetY - sinA * x1_data[x] + cosA * x2_data[x]); 346 | cv::Point2f p1 = cv::Point2f(-sinA * h, -cosA * h) + offset; 347 | cv::Point2f p3 = cv::Point2f(-cosA * w, sinA * w) + offset; 348 | cv::RotatedRect r(0.5f * (p1 + p3), cv::Size2f(w, h), -angle * 180.0f / (float)CV_PI); 349 | detections.push_back(r); 350 | confidences.push_back(score); 351 | } 352 | } 353 | } 354 | 355 | void MainWindow::captureScreen() 356 | { 357 | this->setWindowState(this->windowState() | Qt::WindowMinimized); 358 | QTimer::singleShot(500, this, SLOT(startCapture())); 359 | } 360 | 361 | void MainWindow::startCapture() 362 | { 363 | ScreenCapturer *cap = new ScreenCapturer(this); 364 | cap->show(); 365 | cap->activateWindow(); 366 | } 367 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "tesseract/baseapi.h" 19 | 20 | #include "opencv2/opencv.hpp" 21 | #include "opencv2/dnn.hpp" 22 | 23 | class MainWindow : public QMainWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit MainWindow(QWidget *parent=nullptr); 29 | ~MainWindow(); 30 | void showImage(QPixmap); 31 | 32 | private: 33 | void initUI(); 34 | void createActions(); 35 | void showImage(QString); 36 | void showImage(cv::Mat); 37 | void setupShortcuts(); 38 | 39 | void decode(const cv::Mat& scores, const cv::Mat& geometry, float scoreThresh, 40 | std::vector& detections, std::vector& confidences); 41 | cv::Mat detectTextAreas(QImage &image, std::vector&); 42 | 43 | private slots: 44 | void openImage(); 45 | void saveImageAs(); 46 | void saveTextAs(); 47 | void extractText(); 48 | void captureScreen(); 49 | void startCapture(); 50 | 51 | private: 52 | QMenu *fileMenu; 53 | 54 | QToolBar *fileToolBar; 55 | 56 | QGraphicsScene *imageScene; 57 | QGraphicsView *imageView; 58 | 59 | QTextEdit *editor; 60 | 61 | QStatusBar *mainStatusBar; 62 | QLabel *mainStatusLabel; 63 | 64 | QAction *openAction; 65 | QAction *saveImageAsAction; 66 | QAction *saveTextAsAction; 67 | QAction *exitAction; 68 | QAction *captureAction; 69 | QAction *ocrAction; 70 | QCheckBox *detectAreaCheckBox; 71 | 72 | QString currentImagePath; 73 | QGraphicsPixmapItem *currentImage; 74 | 75 | tesseract::TessBaseAPI *tesseractAPI; 76 | cv::dnn::Net net; 77 | }; 78 | 79 | #endif // MAINWINDOW_H 80 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/screencapturer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "screencapturer.h" 13 | 14 | ScreenCapturer::ScreenCapturer(MainWindow *w): 15 | QWidget(nullptr), window(w) 16 | { 17 | setWindowFlags( 18 | Qt::BypassWindowManagerHint 19 | | Qt::WindowStaysOnTopHint 20 | | Qt::FramelessWindowHint 21 | | Qt::Tool 22 | ); 23 | 24 | setAttribute(Qt::WA_DeleteOnClose); 25 | // setMouseTracking(true); 26 | 27 | screen = captureDesktop(); 28 | resize(screen.size()); 29 | initShortcuts(); 30 | } 31 | 32 | ScreenCapturer::~ScreenCapturer() { 33 | } 34 | 35 | QPixmap ScreenCapturer::captureDesktop() { 36 | QRect geometry; 37 | for (QScreen *const screen : QGuiApplication::screens()) { 38 | geometry = geometry.united(screen->geometry()); 39 | } 40 | 41 | QPixmap pixmap(QApplication::primaryScreen()->grabWindow( 42 | QApplication::desktop()->winId(), 43 | geometry.x(), 44 | geometry.y(), 45 | geometry.width(), 46 | geometry.height() 47 | )); 48 | pixmap.setDevicePixelRatio(QApplication::desktop()->devicePixelRatio()); 49 | return pixmap; 50 | } 51 | 52 | void ScreenCapturer::paintEvent(QPaintEvent*) { 53 | QPainter painter(this); 54 | painter.drawPixmap(0, 0, screen); 55 | 56 | QRegion grey(rect()); 57 | if(p1.x() != p2.x() && p1.y() != p2.y()) { 58 | painter.setPen(QColor(200, 100, 50, 255)); 59 | painter.drawRect(QRect(p1, p2)); 60 | grey = grey.subtracted(QRect(p1, p2)); 61 | } 62 | painter.setClipRegion(grey); 63 | QColor overlayColor(20, 20, 20, 50); 64 | painter.fillRect(rect(), overlayColor); 65 | painter.setClipRect(rect()); 66 | } 67 | 68 | void ScreenCapturer::mousePressEvent(QMouseEvent *event) 69 | { 70 | mouseDown = true; 71 | p1 = event->pos(); 72 | p2 = event->pos(); 73 | update(); 74 | } 75 | 76 | void ScreenCapturer::mouseMoveEvent(QMouseEvent *event) 77 | { 78 | if(!mouseDown) return; 79 | p2 = event->pos(); 80 | update(); 81 | } 82 | 83 | void ScreenCapturer::mouseReleaseEvent(QMouseEvent *event) 84 | { 85 | mouseDown = false; 86 | p2 = event->pos(); 87 | update(); 88 | } 89 | 90 | void ScreenCapturer::closeMe() 91 | { 92 | this->close(); 93 | window->showNormal(); 94 | window->activateWindow(); 95 | } 96 | 97 | void ScreenCapturer::confirmCapture() 98 | { 99 | QPixmap image = screen.copy(QRect(p1, p2)); 100 | window->showImage(image); 101 | closeMe(); 102 | } 103 | 104 | void ScreenCapturer::initShortcuts() { 105 | new QShortcut(Qt::Key_Escape, this, SLOT(closeMe())); 106 | new QShortcut(Qt::Key_Return, this, SLOT(confirmCapture())); 107 | } 108 | -------------------------------------------------------------------------------- /Chapter-05/Literacy/screencapturer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mainwindow.h" 8 | 9 | class ScreenCapturer : public QWidget { 10 | Q_OBJECT 11 | 12 | public: 13 | explicit ScreenCapturer(MainWindow *w); 14 | ~ScreenCapturer(); 15 | 16 | protected: 17 | void paintEvent(QPaintEvent *event) override; 18 | void mouseMoveEvent(QMouseEvent *event) override; 19 | void mousePressEvent(QMouseEvent *event) override; 20 | void mouseReleaseEvent(QMouseEvent *event) override; 21 | 22 | private slots: 23 | void closeMe(); 24 | void confirmCapture(); 25 | 26 | private: 27 | void initShortcuts(); 28 | QPixmap captureDesktop(); 29 | 30 | private: 31 | MainWindow *window; 32 | QPixmap screen; 33 | QPoint p1, p2; 34 | bool mouseDown; 35 | }; 36 | -------------------------------------------------------------------------------- /Chapter-06/Detective/Detective.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = Detective 3 | INCLUDEPATH += . 4 | 5 | QT += core gui multimedia 6 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 7 | 8 | unix: !mac { 9 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 10 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_video -lopencv_videoio -lopencv_objdetect -lopencv_dnn 11 | } 12 | 13 | unix: mac { 14 | INCLUDEPATH += /path/to/opencv/include/opencv4 15 | LIBS += -L/path/to/opencv/lib -lopencv_world 16 | } 17 | 18 | win32 { 19 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 20 | LIBS += -lc:/path/to/opencv/lib/opencv_world 21 | } 22 | 23 | DEFINES += OPENCV_DATA_DIR=\\\"/home/kdr2/programs/opencv/share/opencv4/\\\" 24 | DEFINES += TIME_MEASURE=1 25 | 26 | # Input 27 | HEADERS += mainwindow.h capture_thread.h utilities.h 28 | SOURCES += main.cpp mainwindow.cpp capture_thread.cpp utilities.cpp 29 | -------------------------------------------------------------------------------- /Chapter-06/Detective/capture_thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "utilities.h" 5 | #include "capture_thread.h" 6 | 7 | CaptureThread::CaptureThread(int camera, QMutex *lock): 8 | running(false), cameraID(camera), videoPath(""), data_lock(lock) 9 | { 10 | frame_width = frame_height = 0; 11 | taking_photo = false; 12 | } 13 | 14 | CaptureThread::CaptureThread(QString videoPath, QMutex *lock): 15 | running(false), cameraID(-1), videoPath(videoPath), data_lock(lock) 16 | { 17 | frame_width = frame_height = 0; 18 | taking_photo = false; 19 | } 20 | 21 | CaptureThread::~CaptureThread() { 22 | } 23 | 24 | void CaptureThread::run() { 25 | running = true; 26 | cv::VideoCapture cap(cameraID); 27 | // cv::VideoCapture cap("/home/kdr2/Videos/WIN_20190123_20_14_56_Pro.mp4"); 28 | cv::Mat tmp_frame; 29 | 30 | frame_width = cap.get(cv::CAP_PROP_FRAME_WIDTH); 31 | frame_height = cap.get(cv::CAP_PROP_FRAME_HEIGHT); 32 | 33 | classifier = new cv::CascadeClassifier(OPENCV_DATA_DIR "haarcascades/haarcascade_frontalcatface_extended.xml"); 34 | // classifier = new cv::CascadeClassifier("../boston-bull/cascade.xml"); 35 | // classifier = new cv::CascadeClassifier("../no-entry/cascade.xml"); 36 | while(running) { 37 | cap >> tmp_frame; 38 | if (tmp_frame.empty()) { 39 | break; 40 | } 41 | // tmp_frame = cv::imread("../boston-bull/boston-bull-predict.png"); 42 | // tmp_frame = cv::imread("../no-entry/no-entry-predict.png"); 43 | if(taking_photo) { 44 | takePhoto(tmp_frame); 45 | } 46 | 47 | #ifdef TIME_MEASURE 48 | int64 t0 = cv::getTickCount(); 49 | #endif 50 | // detectObjects(tmp_frame); 51 | detectObjectsDNN(tmp_frame); 52 | 53 | #ifdef TIME_MEASURE 54 | int64 t1 = cv::getTickCount(); 55 | double t = (t1-t0) * 1000 /cv::getTickFrequency(); 56 | qDebug() << "Detecting time on a single frame: " << t <<"ms"; 57 | #endif 58 | 59 | cvtColor(tmp_frame, tmp_frame, cv::COLOR_BGR2RGB); 60 | data_lock->lock(); 61 | frame = tmp_frame; 62 | data_lock->unlock(); 63 | emit frameCaptured(&frame); 64 | } 65 | cap.release(); 66 | delete classifier; 67 | classifier = nullptr; 68 | running = false; 69 | } 70 | 71 | 72 | void CaptureThread::takePhoto(cv::Mat &frame) 73 | { 74 | QString photo_name = Utilities::newPhotoName(); 75 | QString photo_path = Utilities::getPhotoPath(photo_name, "jpg"); 76 | cv::imwrite(photo_path.toStdString(), frame); 77 | emit photoTaken(photo_name); 78 | taking_photo = false; 79 | } 80 | 81 | void CaptureThread::detectObjects(cv::Mat &frame) 82 | { 83 | vector objects; 84 | int minNeighbors = 5; // 3 for no-entry-sign; 5-for others. 85 | classifier->detectMultiScale(frame, objects, 1.3, minNeighbors); 86 | 87 | cv::Scalar color = cv::Scalar(0, 0, 255); // red 88 | 89 | // draw the circumscribe rectangles 90 | for(size_t i = 0; i < objects.size(); i++) { 91 | cv::rectangle(frame, objects[i], color, 2); 92 | } 93 | } 94 | 95 | static vector getOutputsNames(const cv::dnn::Net& net); 96 | static void decodeOutLayers( 97 | cv::Mat &frame, const vector &outs, 98 | vector &outClassIds, 99 | vector &outConfidences, 100 | vector &outBoxes 101 | ); 102 | 103 | void CaptureThread::detectObjectsDNN(cv::Mat &frame) 104 | { 105 | int inputWidth = 416; 106 | int inputHeight = 416; 107 | 108 | if (net.empty()) { 109 | // give the configuration and weight files for the model 110 | string modelConfig = "data/yolov3.cfg"; 111 | string modelWeights = "data/yolov3.weights"; 112 | net = cv::dnn::readNetFromDarknet(modelConfig, modelWeights); 113 | // net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); 114 | // net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); 115 | 116 | objectClasses.clear(); 117 | string name; 118 | string namesFile = "data/coco.names"; 119 | ifstream ifs(namesFile.c_str()); 120 | while(getline(ifs, name)) objectClasses.push_back(name); 121 | } 122 | 123 | cv::Mat blob; 124 | cv::dnn::blobFromImage(frame, blob, 1 / 255.0, cv::Size(inputWidth, inputHeight), cv::Scalar(0, 0, 0), true, false); 125 | 126 | net.setInput(blob); 127 | 128 | // forward 129 | vector outs; 130 | net.forward(outs, getOutputsNames(net)); 131 | 132 | #ifdef TIME_MEASURE 133 | vector layersTimes; 134 | double freq = cv::getTickFrequency() / 1000; 135 | double t = net.getPerfProfile(layersTimes) / freq; 136 | qDebug() << "YOLO: Inference time on a single frame: " << t <<"ms"; 137 | #endif 138 | 139 | // remove the bounding boxes with low confidence 140 | vector outClassIds; 141 | vector outConfidences; 142 | vector outBoxes; 143 | decodeOutLayers(frame, outs, outClassIds, outConfidences, outBoxes); 144 | 145 | for(size_t i = 0; i < outClassIds.size(); i ++) { 146 | cv::rectangle(frame, outBoxes[i], cv::Scalar(0, 0, 255)); 147 | 148 | // get the label for the class name and its confidence 149 | string label = objectClasses[outClassIds[i]]; 150 | label += cv::format(":%.2f", outConfidences[i]); 151 | 152 | // display the label at the top of the bounding box 153 | int baseLine; 154 | cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); 155 | int left = outBoxes[i].x, top = outBoxes[i].y; 156 | top = max(top, labelSize.height); 157 | cv::putText(frame, label, cv::Point(left, top), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255,255,255)); 158 | } 159 | } 160 | 161 | vector getOutputsNames(const cv::dnn::Net& net) 162 | { 163 | static vector names; 164 | vector outLayers = net.getUnconnectedOutLayers(); 165 | vector layersNames = net.getLayerNames(); 166 | names.resize(outLayers.size()); 167 | for (size_t i = 0; i < outLayers.size(); ++i) 168 | names[i] = layersNames[outLayers[i] - 1]; 169 | 170 | return names; 171 | } 172 | 173 | void decodeOutLayers( 174 | cv::Mat &frame, const vector &outs, 175 | vector &outClassIds, 176 | vector &outConfidences, 177 | vector &outBoxes 178 | ) 179 | { 180 | float confThreshold = 0.5; // confidence threshold 181 | float nmsThreshold = 0.4; // non-maximum suppression threshold 182 | 183 | vector classIds; 184 | vector confidences; 185 | vector boxes; 186 | 187 | for (size_t i = 0; i < outs.size(); ++i) { 188 | float* data = (float*)outs[i].data; 189 | for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) 190 | { 191 | cv::Mat scores = outs[i].row(j).colRange(5, outs[i].cols); 192 | cv::Point classIdPoint; 193 | double confidence; 194 | // get the value and location of the maximum score 195 | cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); 196 | if (confidence > confThreshold) 197 | { 198 | int centerX = (int)(data[0] * frame.cols); 199 | int centerY = (int)(data[1] * frame.rows); 200 | int width = (int)(data[2] * frame.cols); 201 | int height = (int)(data[3] * frame.rows); 202 | int left = centerX - width / 2; 203 | int top = centerY - height / 2; 204 | 205 | classIds.push_back(classIdPoint.x); 206 | confidences.push_back((float)confidence); 207 | boxes.push_back(cv::Rect(left, top, width, height)); 208 | } 209 | } 210 | } 211 | 212 | // non maximum suppression 213 | vector indices; 214 | cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); 215 | for (size_t i = 0; i < indices.size(); ++i) { 216 | int idx = indices[i]; 217 | outClassIds.push_back(classIds[idx]); 218 | outBoxes.push_back(boxes[idx]); 219 | outConfidences.push_back(confidences[idx]); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /Chapter-06/Detective/capture_thread.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef CAPTURE_THREAD_H 3 | #define CAPTURE_THREAD_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | #include "opencv2/videoio.hpp" 11 | #include "opencv2/dnn.hpp" 12 | 13 | using namespace std; 14 | 15 | class CaptureThread : public QThread 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit CaptureThread(int camera, QMutex *lock); 20 | explicit CaptureThread(QString videoPath, QMutex *lock); 21 | ~CaptureThread(); 22 | void setRunning(bool run) {running = run; }; 23 | void takePhoto() {taking_photo = true; } 24 | 25 | protected: 26 | void run() override; 27 | 28 | signals: 29 | void frameCaptured(cv::Mat *data); 30 | void photoTaken(QString name); 31 | 32 | private: 33 | void takePhoto(cv::Mat &frame); 34 | void detectObjects(cv::Mat &frame); 35 | void detectObjectsDNN(cv::Mat &frame); 36 | 37 | private: 38 | bool running; 39 | int cameraID; 40 | QString videoPath; 41 | QMutex *data_lock; 42 | cv::Mat frame; 43 | 44 | int frame_width, frame_height; 45 | 46 | // take photos 47 | bool taking_photo; 48 | 49 | // object detection 50 | cv::CascadeClassifier *classifier; 51 | 52 | cv::dnn::Net net; 53 | vector objectClasses; 54 | }; 55 | 56 | #endif // CAPTURE_THREAD_H 57 | -------------------------------------------------------------------------------- /Chapter-06/Detective/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("Detective"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-06/Detective/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "opencv2/videoio.hpp" 14 | 15 | #include "mainwindow.h" 16 | #include "utilities.h" 17 | 18 | MainWindow::MainWindow(QWidget *parent) : 19 | QMainWindow(parent) 20 | , fileMenu(nullptr) 21 | , capturer(nullptr) 22 | { 23 | initUI(); 24 | data_lock = new QMutex(); 25 | } 26 | 27 | MainWindow::~MainWindow() 28 | { 29 | } 30 | 31 | void MainWindow::initUI() 32 | { 33 | this->resize(1000, 800); 34 | // setup menubar 35 | fileMenu = menuBar()->addMenu("&File"); 36 | 37 | // main area 38 | QGridLayout *main_layout = new QGridLayout(); 39 | 40 | imageScene = new QGraphicsScene(this); 41 | imageView = new QGraphicsView(imageScene); 42 | main_layout->addWidget(imageView, 0, 0, 12, 1); 43 | 44 | // tools 45 | QGridLayout *tools_layout = new QGridLayout(); 46 | main_layout->addLayout(tools_layout, 12, 0, 1, 1); 47 | 48 | shutterButton = new QPushButton(this); 49 | shutterButton->setText("Take a Photo"); 50 | tools_layout->addWidget(shutterButton, 0, 0, Qt::AlignHCenter); 51 | connect(shutterButton, SIGNAL(clicked(bool)), this, SLOT(takePhoto())); 52 | 53 | // list of saved photos 54 | saved_list = new QListView(this); 55 | saved_list->setViewMode(QListView::IconMode); 56 | saved_list->setResizeMode(QListView::Adjust); 57 | saved_list->setSpacing(5); 58 | saved_list->setWrapping(false); 59 | list_model = new QStandardItemModel(this); 60 | saved_list->setModel(list_model); 61 | main_layout->addWidget(saved_list, 13, 0, 4, 1); 62 | 63 | QWidget *widget = new QWidget(); 64 | widget->setLayout(main_layout); 65 | setCentralWidget(widget); 66 | 67 | // setup status bar 68 | mainStatusBar = statusBar(); 69 | mainStatusLabel = new QLabel(mainStatusBar); 70 | mainStatusBar->addPermanentWidget(mainStatusLabel); 71 | mainStatusLabel->setText("Detective is Ready"); 72 | 73 | createActions(); 74 | populateSavedList(); 75 | } 76 | 77 | void MainWindow::createActions() 78 | { 79 | // create actions, add them to menus 80 | cameraInfoAction = new QAction("Camera &Information", this); 81 | fileMenu->addAction(cameraInfoAction); 82 | openCameraAction = new QAction("&Open Camera", this); 83 | fileMenu->addAction(openCameraAction); 84 | exitAction = new QAction("E&xit", this); 85 | fileMenu->addAction(exitAction); 86 | 87 | // connect the signals and slots 88 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 89 | connect(cameraInfoAction, SIGNAL(triggered(bool)), this, SLOT(showCameraInfo())); 90 | connect(openCameraAction, SIGNAL(triggered(bool)), this, SLOT(openCamera())); 91 | } 92 | 93 | void MainWindow::showCameraInfo() 94 | { 95 | QList cameras = QCameraInfo::availableCameras(); 96 | QString info = QString("Available Cameras: \n"); 97 | 98 | foreach (const QCameraInfo &cameraInfo, cameras) 99 | info += " - " + cameraInfo.deviceName() + "\n"; 100 | 101 | QMessageBox::information(this, "Cameras", info); 102 | } 103 | 104 | 105 | void MainWindow::openCamera() 106 | { 107 | if(capturer != nullptr) { 108 | // if a thread is already running, stop it 109 | capturer->setRunning(false); 110 | disconnect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 111 | disconnect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 112 | connect(capturer, &CaptureThread::finished, capturer, &CaptureThread::deleteLater); 113 | } 114 | // I am using my second camera whose Index is 2. Usually, the 115 | // Index of the first camera is 0. 116 | int camID = 2; 117 | capturer = new CaptureThread(camID, data_lock); 118 | connect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 119 | connect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 120 | capturer->start(); 121 | mainStatusLabel->setText(QString("Capturing Camera %1").arg(camID)); 122 | } 123 | 124 | 125 | void MainWindow::updateFrame(cv::Mat *mat) 126 | { 127 | data_lock->lock(); 128 | currentFrame = *mat; 129 | data_lock->unlock(); 130 | 131 | QImage frame( 132 | currentFrame.data, 133 | currentFrame.cols, 134 | currentFrame.rows, 135 | currentFrame.step, 136 | QImage::Format_RGB888); 137 | QPixmap image = QPixmap::fromImage(frame); 138 | 139 | imageScene->clear(); 140 | imageView->resetMatrix(); 141 | imageScene->addPixmap(image); 142 | imageScene->update(); 143 | imageView->setSceneRect(image.rect()); 144 | } 145 | 146 | 147 | void MainWindow::takePhoto() 148 | { 149 | if(capturer != nullptr) { 150 | capturer->takePhoto(); 151 | } 152 | } 153 | 154 | 155 | void MainWindow::populateSavedList() 156 | { 157 | QDir dir(Utilities::getDataPath()); 158 | QStringList nameFilters; 159 | nameFilters << "*.jpg"; 160 | QFileInfoList files = dir.entryInfoList( 161 | nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name); 162 | 163 | foreach(QFileInfo photo, files) { 164 | QString name = photo.baseName(); 165 | QStandardItem *item = new QStandardItem(); 166 | list_model->appendRow(item); 167 | QModelIndex index = list_model->indexFromItem(item); 168 | list_model->setData(index, QPixmap(photo.absoluteFilePath()).scaledToHeight(145), Qt::DecorationRole); 169 | list_model->setData(index, name, Qt::DisplayRole); 170 | } 171 | } 172 | 173 | void MainWindow::appendSavedPhoto(QString name) 174 | { 175 | QString photo_path = Utilities::getPhotoPath(name, "jpg"); 176 | QStandardItem *item = new QStandardItem(); 177 | list_model->appendRow(item); 178 | QModelIndex index = list_model->indexFromItem(item); 179 | list_model->setData(index, QPixmap(photo_path).scaledToHeight(145), Qt::DecorationRole); 180 | list_model->setData(index, name, Qt::DisplayRole); 181 | saved_list->scrollTo(index); 182 | } 183 | -------------------------------------------------------------------------------- /Chapter-06/Detective/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "opencv2/opencv.hpp" 19 | 20 | #include "capture_thread.h" 21 | 22 | class MainWindow : public QMainWindow 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit MainWindow(QWidget *parent=nullptr); 28 | ~MainWindow(); 29 | 30 | private: 31 | void initUI(); 32 | void createActions(); 33 | void populateSavedList(); 34 | 35 | private slots: 36 | void showCameraInfo(); 37 | void openCamera(); 38 | void updateFrame(cv::Mat*); 39 | void takePhoto(); 40 | void appendSavedPhoto(QString name); 41 | 42 | private: 43 | QMenu *fileMenu; 44 | 45 | QAction *cameraInfoAction; 46 | QAction *openCameraAction; 47 | QAction *exitAction; 48 | 49 | QGraphicsScene *imageScene; 50 | QGraphicsView *imageView; 51 | 52 | QPushButton *shutterButton; 53 | 54 | QListView *saved_list; 55 | QStandardItemModel *list_model; 56 | 57 | QStatusBar *mainStatusBar; 58 | QLabel *mainStatusLabel; 59 | 60 | cv::Mat currentFrame; 61 | 62 | // for capture thread 63 | QMutex *data_lock; 64 | CaptureThread *capturer; 65 | }; 66 | 67 | #endif // MAINWINDOW_H 68 | -------------------------------------------------------------------------------- /Chapter-06/Detective/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utilities.h" 9 | 10 | QString Utilities::getDataPath() 11 | { 12 | QString user_pictures_path = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)[0]; 13 | QDir pictures_dir(user_pictures_path); 14 | pictures_dir.mkpath("Detective"); 15 | return pictures_dir.absoluteFilePath("Detective"); 16 | } 17 | 18 | QString Utilities::newPhotoName() 19 | { 20 | QDateTime time = QDateTime::currentDateTime(); 21 | return time.toString("yyyy-MM-dd+HH:mm:ss"); 22 | } 23 | 24 | QString Utilities::getPhotoPath(QString name, QString postfix) 25 | { 26 | return QString("%1/%2.%3").arg(Utilities::getDataPath(), name, postfix); 27 | } 28 | -------------------------------------------------------------------------------- /Chapter-06/Detective/utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H_ 2 | #define UTILITIES_H_ 3 | 4 | #include 5 | 6 | class Utilities 7 | { 8 | public: 9 | static QString getDataPath(); 10 | static QString newPhotoName(); 11 | static QString getPhotoPath(QString name, QString postfix); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /Chapter-06/boston-bull/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar 2 | *.bak 3 | *.zip 4 | *.vec 5 | Images/ 6 | classifier/ 7 | negative/ 8 | positive/ 9 | visualisation/ 10 | -------------------------------------------------------------------------------- /Chapter-06/boston-bull/bg.txt: -------------------------------------------------------------------------------- 1 | negative/n02105251_1201.jpg 2 | negative/n02105251_1240.jpg 3 | negative/n02105251_12.jpg 4 | negative/n02105251_1382.jpg 5 | negative/n02105251_1588.jpg 6 | negative/n02105251_1676.jpg 7 | negative/n02105251_1725.jpg 8 | negative/n02105251_224.jpg 9 | negative/n02105251_2317.jpg 10 | negative/n02105251_2406.jpg 11 | negative/n02105251_2439.jpg 12 | negative/n02105251_2480.jpg 13 | negative/n02105251_252.jpg 14 | negative/n02105251_2591.jpg 15 | negative/n02105251_2626.jpg 16 | negative/n02105251_2656.jpg 17 | negative/n02105251_2983.jpg 18 | negative/n02105251_3265.jpg 19 | negative/n02105251_3389.jpg 20 | negative/n02105251_3407.jpg 21 | negative/n02105251_3436.jpg 22 | negative/n02105251_3551.jpg 23 | negative/n02105251_3718.jpg 24 | negative/n02105251_3729.jpg 25 | negative/n02105251_3746.jpg 26 | negative/n02105251_4027.jpg 27 | negative/n02105251_4223.jpg 28 | negative/n02105251_4569.jpg 29 | negative/n02105251_4659.jpg 30 | negative/n02105251_4802.jpg 31 | negative/n02105251_4893.jpg 32 | negative/n02105251_4996.jpg 33 | negative/n02105251_5065.jpg 34 | negative/n02105251_5074.jpg 35 | negative/n02105251_5156.jpg 36 | negative/n02105251_5175.jpg 37 | negative/n02105251_5303.jpg 38 | negative/n02105251_5304.jpg 39 | negative/n02105251_5309.jpg 40 | negative/n02105251_5337.jpg 41 | negative/n02105251_5384.jpg 42 | negative/n02105251_5408.jpg 43 | negative/n02105251_5471.jpg 44 | negative/n02105251_5556.jpg 45 | negative/n02105251_5570.jpg 46 | negative/n02105251_5630.jpg 47 | negative/n02105251_5641.jpg 48 | negative/n02105251_5649.jpg 49 | negative/n02105251_5677.jpg 50 | negative/n02105251_5706.jpg 51 | negative/n02105251_5775.jpg 52 | negative/n02105251_5864.jpg 53 | negative/n02105251_5985.jpg 54 | negative/n02105251_6138.jpg 55 | negative/n02105251_6146.jpg 56 | negative/n02105251_6161.jpg 57 | negative/n02105251_6206.jpg 58 | negative/n02105251_6300.jpg 59 | negative/n02105251_6301.jpg 60 | negative/n02105251_6335.jpg 61 | negative/n02105251_6366.jpg 62 | negative/n02105251_6376.jpg 63 | negative/n02105251_6387.jpg 64 | negative/n02105251_6394.jpg 65 | negative/n02105251_6572.jpg 66 | negative/n02105251_6580.jpg 67 | negative/n02105251_6667.jpg 68 | negative/n02105251_6705.jpg 69 | negative/n02105251_673.jpg 70 | negative/n02105251_6805.jpg 71 | negative/n02105251_6840.jpg 72 | negative/n02105251_685.jpg 73 | negative/n02105251_6867.jpg 74 | negative/n02105251_6883.jpg 75 | negative/n02105251_6907.jpg 76 | negative/n02105251_6942.jpg 77 | negative/n02105251_6945.jpg 78 | negative/n02105251_6984.jpg 79 | negative/n02105251_6995.jpg 80 | negative/n02105251_7044.jpg 81 | negative/n02105251_7056.jpg 82 | negative/n02105251_7058.jpg 83 | negative/n02105251_7086.jpg 84 | negative/n02105251_7090.jpg 85 | negative/n02105251_7115.jpg 86 | negative/n02105251_7127.jpg 87 | negative/n02105251_7170.jpg 88 | negative/n02105251_7171.jpg 89 | negative/n02105251_7178.jpg 90 | negative/n02105251_7207.jpg 91 | negative/n02105251_7223.jpg 92 | negative/n02105251_7233.jpg 93 | negative/n02105251_7294.jpg 94 | negative/n02105251_7348.jpg 95 | negative/n02105251_7349.jpg 96 | negative/n02105251_7356.jpg 97 | negative/n02105251_7430.jpg 98 | negative/n02105251_7434.jpg 99 | negative/n02105251_7579.jpg 100 | negative/n02105251_7593.jpg 101 | negative/n02105251_7600.jpg 102 | negative/n02105251_7627.jpg 103 | negative/n02105251_7647.jpg 104 | negative/n02105251_7654.jpg 105 | negative/n02105251_7666.jpg 106 | negative/n02105251_7723.jpg 107 | negative/n02105251_7738.jpg 108 | negative/n02105251_7745.jpg 109 | negative/n02105251_7750.jpg 110 | negative/n02105251_7772.jpg 111 | negative/n02105251_7797.jpg 112 | negative/n02105251_7805.jpg 113 | negative/n02105251_7816.jpg 114 | negative/n02105251_7950.jpg 115 | negative/n02105251_7977.jpg 116 | negative/n02105251_7996.jpg 117 | negative/n02105251_8027.jpg 118 | negative/n02105251_8075.jpg 119 | negative/n02105251_8078.jpg 120 | negative/n02105251_8082.jpg 121 | negative/n02105251_8094.jpg 122 | negative/n02105251_8161.jpg 123 | negative/n02105251_8194.jpg 124 | negative/n02105251_8210.jpg 125 | negative/n02105251_8229.jpg 126 | negative/n02105251_8240.jpg 127 | negative/n02105251_8296.jpg 128 | negative/n02105251_8345.jpg 129 | negative/n02105251_8384.jpg 130 | negative/n02105251_853.jpg 131 | negative/n02105251_8540.jpg 132 | negative/n02105251_8543.jpg 133 | negative/n02105251_8552.jpg 134 | negative/n02105251_8560.jpg 135 | negative/n02105251_8570.jpg 136 | negative/n02105251_8587.jpg 137 | negative/n02105251_8629.jpg 138 | negative/n02105251_8643.jpg 139 | negative/n02105251_866.jpg 140 | negative/n02105251_8725.jpg 141 | negative/n02105251_8743.jpg 142 | negative/n02105251_8848.jpg 143 | negative/n02105251_8854.jpg 144 | negative/n02105251_8857.jpg 145 | negative/n02105251_8859.jpg 146 | negative/n02105251_8870.jpg 147 | negative/n02105251_8896.jpg 148 | negative/n02105251_8911.jpg 149 | negative/n02105251_8930.jpg 150 | negative/n02105251_8951.jpg 151 | negative/n02105251_9020.jpg 152 | negative/n02105251_917.jpg 153 | -------------------------------------------------------------------------------- /Chapter-06/boston-bull/boston-bull-predict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/boston-bull/boston-bull-predict.png -------------------------------------------------------------------------------- /Chapter-06/boston-bull/info.txt: -------------------------------------------------------------------------------- 1 | positive/n02096585_10380.jpg 1 7 4 342 326 2 | positive/n02096585_10452.jpg 1 189 63 203 209 3 | positive/n02096585_10596.jpg 1 263 126 122 150 4 | positive/n02096585_10604.jpg 1 301 201 94 107 5 | positive/n02096585_1069.jpg 1 121 83 171 188 6 | positive/n02096585_10734.jpg 1 150 145 210 227 7 | positive/n02096585_10768.jpg 1 167 92 157 174 8 | positive/n02096585_10823.jpg 1 81 105 218 212 9 | positive/n02096585_10846.jpg 1 199 130 149 153 10 | positive/n02096585_11180.jpg 1 226 64 95 112 11 | positive/n02096585_11417.jpg 1 209 34 232 221 12 | positive/n02096585_11427.jpg 1 132 115 110 121 13 | positive/n02096585_1143.jpg 1 221 121 57 64 14 | positive/n02096585_11476.jpg 1 115 44 68 69 15 | positive/n02096585_11614.jpg 1 71 107 162 187 16 | positive/n02096585_1172.jpg 1 212 77 42 32 17 | positive/n02096585_11731.jpg 1 158 218 93 83 18 | positive/n02096585_11776.jpg 2 47 196 104 120 377 76 93 98 19 | positive/n02096585_1179.jpg 1 259 26 170 165 20 | positive/n02096585_11808.jpg 1 301 93 142 174 21 | positive/n02096585_11817.jpg 1 115 70 131 127 22 | positive/n02096585_11836.jpg 1 76 51 171 134 23 | positive/n02096585_1207.jpg 1 82 113 123 111 24 | positive/n02096585_12168.jpg 1 85 87 193 243 25 | positive/n02096585_12352.jpg 1 101 311 158 146 26 | positive/n02096585_12362.jpg 1 112 13 195 226 27 | positive/n02096585_12372.jpg 1 241 56 146 170 28 | positive/n02096585_12374.jpg 1 58 92 70 64 29 | positive/n02096585_12464.jpg 1 83 94 163 158 30 | positive/n02096585_12485.jpg 1 82 82 105 98 31 | positive/n02096585_1260.jpg 1 306 73 63 74 32 | positive/n02096585_12716.jpg 1 144 77 93 108 33 | positive/n02096585_12776.jpg 1 136 94 201 192 34 | positive/n02096585_1281.jpg 1 93 103 254 213 35 | positive/n02096585_12825.jpg 0 36 | positive/n02096585_12844.jpg 1 73 108 101 105 37 | positive/n02096585_12880.jpg 1 171 80 105 117 38 | positive/n02096585_1295.jpg 1 119 95 206 168 39 | positive/n02096585_1307.jpg 1 165 219 45 47 40 | positive/n02096585_1311.jpg 1 229 116 55 53 41 | positive/n02096585_1313.jpg 1 197 104 115 122 42 | positive/n02096585_1347.jpg 1 142 133 271 310 43 | positive/n02096585_1403.jpg 1 137 124 118 118 44 | positive/n02096585_1439.jpg 1 206 111 88 118 45 | positive/n02096585_1449.jpg 1 154 130 148 121 46 | positive/n02096585_145.jpg 1 56 72 212 237 47 | positive/n02096585_1450.jpg 2 305 52 98 96 6 138 111 98 48 | positive/n02096585_1525.jpg 0 49 | positive/n02096585_1532.jpg 1 31 100 110 115 50 | positive/n02096585_1559.jpg 1 185 91 57 56 51 | positive/n02096585_1562.jpg 1 71 48 197 182 52 | positive/n02096585_1568.jpg 1 182 83 144 135 53 | positive/n02096585_1571.jpg 1 87 54 95 92 54 | positive/n02096585_1572.jpg 1 132 99 152 190 55 | positive/n02096585_1583.jpg 1 167 65 58 57 56 | positive/n02096585_1586.jpg 1 147 145 127 128 57 | positive/n02096585_1624.jpg 1 133 189 132 162 58 | positive/n02096585_1645.jpg 1 170 49 143 172 59 | positive/n02096585_1668.jpg 1 263 87 122 146 60 | positive/n02096585_169.jpg 1 130 177 104 125 61 | positive/n02096585_1692.jpg 1 62 47 235 255 62 | positive/n02096585_1737.jpg 1 114 76 216 257 63 | positive/n02096585_1753.jpg 1 195 92 93 120 64 | positive/n02096585_1756.jpg 1 82 122 203 209 65 | positive/n02096585_1761.jpg 1 74 106 159 165 66 | positive/n02096585_1775.jpg 1 181 40 118 114 67 | positive/n02096585_1856.jpg 1 371 135 53 69 68 | positive/n02096585_19.jpg 1 157 86 135 150 69 | positive/n02096585_1924.jpg 1 147 107 143 161 70 | positive/n02096585_1947.jpg 1 218 219 78 98 71 | positive/n02096585_1952.jpg 1 144 90 116 128 72 | positive/n02096585_1963.jpg 1 283 135 82 90 73 | positive/n02096585_1970.jpg 1 122 103 226 199 74 | positive/n02096585_1976.jpg 1 156 124 203 239 75 | positive/n02096585_1992.jpg 1 151 189 156 160 76 | positive/n02096585_2008.jpg 1 260 56 116 139 77 | positive/n02096585_2042.jpg 1 234 146 54 61 78 | positive/n02096585_2082.jpg 1 62 68 108 121 79 | positive/n02096585_2098.jpg 1 58 61 248 299 80 | positive/n02096585_210.jpg 1 86 132 253 278 81 | positive/n02096585_2108.jpg 1 147 67 50 42 82 | positive/n02096585_2177.jpg 1 183 33 252 236 83 | positive/n02096585_2180.jpg 1 186 77 181 188 84 | positive/n02096585_2260.jpg 1 88 67 213 227 85 | positive/n02096585_2354.jpg 1 177 88 79 74 86 | positive/n02096585_237.jpg 2 155 173 46 41 247 232 57 46 87 | positive/n02096585_2374.jpg 1 56 53 158 151 88 | positive/n02096585_2379.jpg 1 37 214 106 98 89 | positive/n02096585_2394.jpg 1 129 10 258 266 90 | positive/n02096585_2419.jpg 1 94 55 175 200 91 | positive/n02096585_2431.jpg 1 170 66 171 203 92 | positive/n02096585_2450.jpg 1 281 58 115 123 93 | positive/n02096585_2493.jpg 1 223 50 86 102 94 | positive/n02096585_2513.jpg 1 133 33 96 102 95 | positive/n02096585_2560.jpg 1 179 101 156 188 96 | positive/n02096585_2583.jpg 1 214 47 166 204 97 | positive/n02096585_2671.jpg 1 196 43 114 94 98 | positive/n02096585_2696.jpg 1 147 50 241 223 99 | positive/n02096585_2725.jpg 1 147 107 108 146 100 | positive/n02096585_2727.jpg 1 40 25 167 185 101 | positive/n02096585_2800.jpg 1 89 112 224 186 102 | positive/n02096585_2809.jpg 1 158 126 181 204 103 | positive/n02096585_2821.jpg 1 174 12 134 159 104 | positive/n02096585_2828.jpg 1 277 70 144 169 105 | positive/n02096585_2900.jpg 1 39 5 284 253 106 | positive/n02096585_2947.jpg 1 98 104 282 222 107 | positive/n02096585_296.jpg 1 149 114 211 181 108 | positive/n02096585_3105.jpg 1 67 147 125 104 109 | positive/n02096585_311.jpg 1 148 89 269 230 110 | positive/n02096585_318.jpg 1 118 84 210 221 111 | positive/n02096585_32.jpg 1 84 40 125 159 112 | positive/n02096585_341.jpg 1 323 83 138 146 113 | positive/n02096585_342.jpg 1 340 119 73 71 114 | positive/n02096585_353.jpg 1 44 65 86 92 115 | positive/n02096585_3681.jpg 1 138 49 82 109 116 | positive/n02096585_3697.jpg 1 106 55 127 142 117 | positive/n02096585_3738.jpg 1 153 199 198 151 118 | positive/n02096585_3861.jpg 1 71 82 280 267 119 | positive/n02096585_4103.jpg 1 120 189 77 75 120 | positive/n02096585_4136.jpg 1 123 59 149 171 121 | positive/n02096585_414.jpg 2 91 156 114 119 224 50 85 101 122 | positive/n02096585_4321.jpg 1 242 93 65 54 123 | positive/n02096585_4418.jpg 1 142 116 189 160 124 | positive/n02096585_4451.jpg 1 51 30 102 112 125 | positive/n02096585_4534.jpg 1 182 60 104 100 126 | positive/n02096585_454.jpg 1 167 62 69 79 127 | positive/n02096585_4575.jpg 1 171 11 90 127 128 | positive/n02096585_4662.jpg 1 373 38 74 89 129 | positive/n02096585_4774.jpg 0 130 | positive/n02096585_4983.jpg 1 138 13 183 235 131 | positive/n02096585_5043.jpg 1 62 9 86 97 132 | positive/n02096585_507.jpg 1 88 41 253 237 133 | positive/n02096585_5104.jpg 1 134 167 144 162 134 | positive/n02096585_5171.jpg 1 300 33 79 97 135 | positive/n02096585_5260.jpg 2 99 67 67 65 271 62 65 69 136 | positive/n02096585_554.jpg 1 127 100 243 243 137 | positive/n02096585_5599.jpg 1 230 98 85 68 138 | positive/n02096585_5855.jpg 1 287 82 50 58 139 | positive/n02096585_5943.jpg 1 126 92 136 160 140 | positive/n02096585_5980.jpg 1 342 96 67 88 141 | positive/n02096585_6028.jpg 1 147 39 158 212 142 | positive/n02096585_6238.jpg 1 219 168 66 64 143 | positive/n02096585_6367.jpg 1 196 95 103 109 144 | positive/n02096585_6567.jpg 1 162 30 125 157 145 | positive/n02096585_657.jpg 1 168 14 223 286 146 | positive/n02096585_664.jpg 1 115 241 189 187 147 | positive/n02096585_6653.jpg 1 217 37 72 73 148 | positive/n02096585_670.jpg 1 98 48 117 132 149 | positive/n02096585_6766.jpg 1 118 145 164 173 150 | positive/n02096585_6855.jpg 1 211 30 63 66 151 | positive/n02096585_6874.jpg 1 39 156 110 87 152 | positive/n02096585_6940.jpg 1 30 70 151 171 153 | positive/n02096585_7101.jpg 1 109 117 106 96 154 | positive/n02096585_7314.jpg 1 124 117 172 252 155 | positive/n02096585_7542.jpg 1 127 120 142 146 156 | positive/n02096585_772.jpg 1 91 90 151 155 157 | positive/n02096585_7859.jpg 1 184 120 65 68 158 | positive/n02096585_7887.jpg 1 75 24 91 97 159 | positive/n02096585_7933.jpg 1 56 61 147 124 160 | positive/n02096585_8065.jpg 1 103 86 251 193 161 | positive/n02096585_8120.jpg 1 251 135 62 73 162 | positive/n02096585_8148.jpg 2 208 63 85 96 303 128 92 90 163 | positive/n02096585_8203.jpg 1 77 165 233 237 164 | positive/n02096585_8323.jpg 1 42 70 187 196 165 | positive/n02096585_8396.jpg 1 153 25 81 85 166 | positive/n02096585_865.jpg 1 200 30 188 193 167 | positive/n02096585_8971.jpg 1 113 54 221 234 168 | positive/n02096585_904.jpg 1 127 161 158 160 169 | positive/n02096585_9122.jpg 1 150 53 100 108 170 | positive/n02096585_9153.jpg 1 91 109 154 150 171 | positive/n02096585_9343.jpg 1 75 101 165 179 172 | positive/n02096585_942.jpg 1 135 233 241 182 173 | positive/n02096585_9511.jpg 1 150 151 177 130 174 | positive/n02096585_9534.jpg 1 164 99 148 127 175 | positive/n02096585_956.jpg 1 79 220 129 139 176 | positive/n02096585_9681.jpg 1 191 101 98 107 177 | positive/n02096585_9725.jpg 1 266 152 48 52 178 | positive/n02096585_976.jpg 0 179 | positive/n02096585_9834.jpg 0 180 | positive/n02096585_9909.jpg 2 215 72 68 64 311 140 51 54 181 | positive/n02096585_9912.jpg 1 48 193 204 154 182 | positive/n02096585_994.jpg 0 183 | -------------------------------------------------------------------------------- /Chapter-06/boston-bull/test-visualisation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/boston-bull/test-visualisation.png -------------------------------------------------------------------------------- /Chapter-06/boston-bull/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # download data 4 | if [ ! -f images.tar ]; then 5 | curl -O http://vision.stanford.edu/aditya86/ImageNetDogs/images.tar 6 | tar xvf images.tar 7 | fi 8 | 9 | # prepare negative smaples 10 | 11 | NEG_SRC_DIR=Images/n02105251-briard 12 | rm negative -fr 13 | cp -r $NEG_SRC_DIR negative 14 | ls negative/* >bg.txt 15 | 16 | # prepare positive smaples 17 | POS_SRC_DIR=Images/n02096585-Boston_bull 18 | rm positive -fr 19 | cp -r $POS_SRC_DIR positive 20 | 21 | ## annotate the dog-face, this command will produce the "info.txt" file, 22 | ## the annotating process is tedious, so I prepare a "info.txt" for you, 23 | ## with that "info.txt", this command can be omitted. 24 | # opencv_annotation --annotations=info.txt --images=positive 25 | 26 | # create samples: 183 samples will be created 27 | opencv_createsamples -info info.txt -vec samples.vec -w 32 -h 32 28 | 29 | # train 30 | mkdir -p classifier 31 | opencv_traincascade -data classifier -vec samples.vec -bg bg.txt -numPos 180 -numNeg 180 -h 32 -w 32 32 | 33 | 34 | # mkdir -p visualisation 35 | # opencv_visualisation --image=./test-visualisation.png --model=./cascade.xml \ 36 | # --data=./visualisation/ 37 | -------------------------------------------------------------------------------- /Chapter-06/no-entry/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar 2 | *.bak 3 | *.zip 4 | *.vec 5 | *.log 6 | log* 7 | Images/ 8 | classifier/ 9 | negative/ 10 | positive/ 11 | -------------------------------------------------------------------------------- /Chapter-06/no-entry/background/traffic-sign-bg-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/background/traffic-sign-bg-0.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/background/traffic-sign-bg-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/background/traffic-sign-bg-1.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/background/traffic-sign-bg-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/background/traffic-sign-bg-2.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/background/traffic-sign-bg-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/background/traffic-sign-bg-3.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/bg.txt: -------------------------------------------------------------------------------- 1 | background/traffic-sign-bg-0.png 2 | background/traffic-sign-bg-1.png 3 | background/traffic-sign-bg-2.png 4 | background/traffic-sign-bg-3.png 5 | -------------------------------------------------------------------------------- /Chapter-06/no-entry/cascade.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BOOST 5 | LBP 6 | 32 7 | 32 8 | 9 | GAB 10 | 9.9500000476837158e-01 11 | 5.0000000000000000e-01 12 | 9.4999999999999996e-01 13 | 1 14 | 100 15 | 16 | 256 17 | 1 18 | 6 19 | 20 | 21 | <_> 22 | 1 23 | 1. 24 | 25 | <_> 26 | 27 | 0 -1 0 -1828257293 -1828126703 86097 1360597001 -2147385201 28 | -1894907648 73731 -74719089 29 | 30 | -1. 1. 31 | 32 | <_> 33 | 1 34 | 1. 35 | 36 | <_> 37 | 38 | 0 -1 3 -95169285 -788299583 33809 -746504191 537034889 39 | -2004344832 -1602207573 -611728767 40 | 41 | -1. 1. 42 | 43 | <_> 44 | 1 45 | 9.9004977941513062e-01 46 | 47 | <_> 48 | 49 | 0 -1 5 1205598943 -652541886 16826641 -1610513662 335937716 50 | -2146942976 -2113896319 -96493437 51 | 52 | -1. 9.9004977941513062e-01 53 | 54 | <_> 55 | 1 56 | 9.9004977941513062e-01 57 | 58 | <_> 59 | 60 | 0 -1 4 -1063923465 -1035927037 32771 -1056341117 -536674229 61 | -2080374781 -2105171829 -23098741 62 | 63 | -1. 9.9004977941513062e-01 64 | 65 | <_> 66 | 1 67 | 9.9004977941513062e-01 68 | 69 | <_> 70 | 71 | 0 -1 2 -786702353 -1879047936 805339407 -2063334629 72 | -1753054549 -1971126270 -2109691893 -77494109 73 | 74 | -1. 9.9004977941513062e-01 75 | 76 | <_> 77 | 1 78 | 9.9004977941513062e-01 79 | 80 | <_> 81 | 82 | 0 -1 1 352602399 -606924263 82193 -619638654 167772582 83 | -75431535 136883102 -8388477 84 | 85 | -1. 9.9004977941513062e-01 86 | 87 | <_> 88 | 89 | 1 5 8 5 90 | <_> 91 | 92 | 3 14 6 2 93 | <_> 94 | 95 | 5 12 7 4 96 | <_> 97 | 98 | 10 14 6 2 99 | <_> 100 | 101 | 12 11 2 3 102 | <_> 103 | 104 | 14 12 2 2 105 | 106 | -------------------------------------------------------------------------------- /Chapter-06/no-entry/no-entry-predict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/no-entry-predict.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/no-entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-06/no-entry/no-entry.png -------------------------------------------------------------------------------- /Chapter-06/no-entry/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # prepare smaples 4 | ls background/* >bg.txt 5 | opencv_createsamples -vec samples.vec -img no-entry.png -bg bg.txt \ 6 | -num 200 -bgcolor 0 -bgthresh 20 -maxidev 30 \ 7 | -maxxangle 0.3 -maxyangle 0.3 -maxzangle 0.3 \ 8 | -w 32 -h 32 9 | 10 | # train 11 | rm -fr classifier 12 | mkdir -p classifier 13 | opencv_traincascade -data classifier -numStages 10 -featureType HAAR -vec samples.vec -bg bg.txt -numPos 200 -numNeg 200 -h 32 -w 32 14 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/DiGauge.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = DiGauge 3 | INCLUDEPATH += . 4 | 5 | QT += core gui multimedia 6 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 7 | 8 | unix: !mac { 9 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 10 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_video -lopencv_videoio -lopencv_dnn 11 | } 12 | 13 | unix: mac { 14 | INCLUDEPATH += /path/to/opencv/include/opencv4 15 | LIBS += -L/path/to/opencv/lib -lopencv_world 16 | } 17 | 18 | win32 { 19 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 20 | LIBS += -lc:/path/to/opencv/lib/opencv_world 21 | } 22 | 23 | # DEFINES += OPENCV_DATA_DIR=\\\"/home/kdr2/programs/opencv/share/opencv4/\\\" 24 | # DEFINES += TIME_MEASURE=1 25 | 26 | # Input 27 | HEADERS += mainwindow.h capture_thread.h utilities.h 28 | SOURCES += main.cpp mainwindow.cpp capture_thread.cpp utilities.cpp 29 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/capture_thread.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "utilities.h" 8 | #include "capture_thread.h" 9 | 10 | CaptureThread::CaptureThread(int camera, QMutex *lock): 11 | running(false), cameraID(camera), videoPath(""), data_lock(lock) 12 | { 13 | frame_width = frame_height = 0; 14 | taking_photo = false; 15 | viewMode = BIRDEYE; 16 | } 17 | 18 | CaptureThread::CaptureThread(QString videoPath, QMutex *lock): 19 | running(false), cameraID(-1), videoPath(videoPath), data_lock(lock) 20 | { 21 | frame_width = frame_height = 0; 22 | taking_photo = false; 23 | viewMode = BIRDEYE; 24 | } 25 | 26 | CaptureThread::~CaptureThread() { 27 | } 28 | 29 | void CaptureThread::run() { 30 | running = true; 31 | cv::VideoCapture cap(cameraID); 32 | // cv::VideoCapture cap("/home/kdr2/Videos/WIN_20190123_20_14_56_Pro.mp4"); 33 | cv::Mat tmp_frame; 34 | 35 | frame_width = cap.get(cv::CAP_PROP_FRAME_WIDTH); 36 | frame_height = cap.get(cv::CAP_PROP_FRAME_HEIGHT); 37 | 38 | while(running) { 39 | cap >> tmp_frame; 40 | if (tmp_frame.empty()) { 41 | break; 42 | } 43 | if(taking_photo) { 44 | takePhoto(tmp_frame); 45 | } 46 | 47 | #ifdef TIME_MEASURE 48 | int64 t0 = cv::getTickCount(); 49 | #endif 50 | detectObjectsDNN(tmp_frame); 51 | 52 | #ifdef TIME_MEASURE 53 | int64 t1 = cv::getTickCount(); 54 | double t = (t1-t0) * 1000 /cv::getTickFrequency(); 55 | qDebug() << "Decteing time on a single frame: " << t <<"ms"; 56 | #endif 57 | 58 | cvtColor(tmp_frame, tmp_frame, cv::COLOR_BGR2RGB); 59 | data_lock->lock(); 60 | frame = tmp_frame; 61 | data_lock->unlock(); 62 | emit frameCaptured(&frame); 63 | } 64 | cap.release(); 65 | running = false; 66 | } 67 | 68 | 69 | void CaptureThread::takePhoto(cv::Mat &frame) 70 | { 71 | QString photo_name = Utilities::newPhotoName(); 72 | QString photo_path = Utilities::getPhotoPath(photo_name, "jpg"); 73 | cv::imwrite(photo_path.toStdString(), frame); 74 | emit photoTaken(photo_name); 75 | taking_photo = false; 76 | } 77 | 78 | static void decodeOutLayers( 79 | cv::Mat &frame, const vector &outs, 80 | vector &outBoxes 81 | ); 82 | void distanceBirdEye(cv::Mat &frame, vector &cars); 83 | void distanceEyeLevel(cv::Mat &frame, vector &cars); 84 | 85 | void CaptureThread::detectObjectsDNN(cv::Mat &frame) 86 | { 87 | int inputWidth = 416; 88 | int inputHeight = 416; 89 | 90 | if (net.empty()) { 91 | // give the configuration and weight files for the model 92 | string modelConfig = "data/yolov3.cfg"; 93 | string modelWeights = "data/yolov3.weights"; 94 | net = cv::dnn::readNetFromDarknet(modelConfig, modelWeights); 95 | // net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); 96 | // net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); 97 | 98 | objectClasses.clear(); 99 | string name; 100 | string namesFile = "data/coco.names"; 101 | ifstream ifs(namesFile.c_str()); 102 | while(getline(ifs, name)) objectClasses.push_back(name); 103 | } 104 | 105 | cv::Mat blob; 106 | cv::dnn::blobFromImage(frame, blob, 1 / 255.0, cv::Size(inputWidth, inputHeight), cv::Scalar(0, 0, 0), true, false); 107 | 108 | net.setInput(blob); 109 | 110 | // forward 111 | vector outs; 112 | net.forward(outs, net.getUnconnectedOutLayersNames()); 113 | 114 | #ifdef TIME_MEASURE 115 | vector layersTimes; 116 | double freq = cv::getTickFrequency() / 1000; 117 | double t = net.getPerfProfile(layersTimes) / freq; 118 | qDebug() << "YOLO: Inference time on a single frame: " << t <<"ms"; 119 | #endif 120 | 121 | // remove the bounding boxes with low confidence 122 | vector outBoxes; 123 | decodeOutLayers(frame, outs, outBoxes); 124 | 125 | for(size_t i = 0; i < outBoxes.size(); i ++) { 126 | cv::rectangle(frame, outBoxes[i], cv::Scalar(0, 0, 255)); 127 | } 128 | if (viewMode == BIRDEYE) { 129 | distanceBirdEye(frame, outBoxes); 130 | } else { 131 | distanceEyeLevel(frame, outBoxes); 132 | } 133 | } 134 | 135 | void decodeOutLayers( 136 | cv::Mat &frame, const vector &outs, 137 | vector &outBoxes 138 | ) 139 | { 140 | float confThreshold = 0.65; // confidence threshold 141 | float nmsThreshold = 0.4; // non-maximum suppression threshold 142 | 143 | vector confidences; 144 | vector boxes; 145 | 146 | for (size_t i = 0; i < outs.size(); ++i) { 147 | float* data = (float*)outs[i].data; 148 | for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) 149 | { 150 | cv::Mat scores = outs[i].row(j).colRange(5, outs[i].cols); 151 | cv::Point classIdPoint; 152 | double confidence; 153 | // get the value and location of the maximum score 154 | cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); 155 | if (classIdPoint.x != 2) // not a car! 156 | continue; 157 | if (confidence > confThreshold) 158 | { 159 | int centerX = (int)(data[0] * frame.cols); 160 | int centerY = (int)(data[1] * frame.rows); 161 | int width = (int)(data[2] * frame.cols); 162 | int height = (int)(data[3] * frame.rows); 163 | int left = centerX - width / 2; 164 | int top = centerY - height / 2; 165 | 166 | confidences.push_back((float)confidence); 167 | boxes.push_back(cv::Rect(left, top, width, height)); 168 | } 169 | } 170 | } 171 | 172 | // non maximum suppression 173 | vector indices; 174 | cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); 175 | for (size_t i = 0; i < indices.size(); ++i) { 176 | int idx = indices[i]; 177 | outBoxes.push_back(boxes[idx]); 178 | } 179 | } 180 | 181 | void distanceBirdEye(cv::Mat &frame, vector &cars) 182 | { 183 | if(cars.empty()) 184 | return; 185 | 186 | vector length_of_cars; 187 | vector> endpoints; 188 | vector> cars_merged; 189 | 190 | for (auto car: cars) { 191 | length_of_cars.push_back(car.width); 192 | endpoints.push_back(make_pair(car.x, 1)); 193 | endpoints.push_back(make_pair(car.x + car.width, -1)); 194 | } 195 | 196 | sort(length_of_cars.begin(), length_of_cars.end()); 197 | int length = length_of_cars[cars.size() / 2]; 198 | sort( 199 | endpoints.begin(), endpoints.end(), 200 | [](pair a, pair b) { 201 | return a.first < b.first; 202 | } 203 | ); 204 | 205 | int flag = 0, start = 0; 206 | for (auto ep: endpoints) { 207 | flag += ep.second; 208 | if (flag == 1 && start == 0) { // a start 209 | start = ep.first; 210 | } else if (flag == 0) { // an end 211 | cars_merged.push_back(make_pair(start, ep.first)); 212 | start = 0; 213 | } 214 | } 215 | 216 | for (size_t i = 1; i < cars_merged.size(); i++) { 217 | int x1 = cars_merged[i - 1].second; // head of car, start of spacing 218 | int x2 = cars_merged[i].first; // end of another car, end of spacing 219 | cv::line(frame, cv::Point(x1, 0), cv::Point(x1, frame.rows), cv::Scalar(0, 255, 0), 2); 220 | cv::line(frame, cv::Point(x2, 0), cv::Point(x2, frame.rows), cv::Scalar(0, 0, 255), 2); 221 | float distance = (x2 - x1) * (5.0 / length); 222 | 223 | // display the label at the top of the bounding box 224 | string label = cv::format("%.2f m", distance); 225 | int baseLine; 226 | cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); 227 | int label_x = (x1 + x2) / 2 - (labelSize.width / 2); 228 | cv::putText(frame, label, cv::Point(label_x, 20), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255)); 229 | } 230 | } 231 | 232 | void distanceEyeLevel(cv::Mat &frame, vector &cars) 233 | { 234 | const float d0 = 1000.0f; // cm 235 | const float w0 = 150.0f; // px 236 | 237 | // find the target car: the most middle and biggest one 238 | vector cars_in_middle; 239 | vector cars_area; 240 | size_t target_idx = 0; 241 | 242 | for (auto car: cars) { 243 | if(car.x < frame.cols / 2 && (car.x + car.width) > frame.cols / 2) { 244 | cars_in_middle.push_back(car); 245 | int area = car.width * car.height; 246 | cars_area.push_back(area); 247 | if (area > cars_area[target_idx]) { 248 | target_idx = cars_area.size() - 1; 249 | } 250 | } 251 | } 252 | 253 | if(cars_in_middle.size() <= target_idx) return; 254 | 255 | cv::Rect car = cars_in_middle[target_idx]; 256 | float distance = (w0 / car.width) * d0; // (w0 / w1) * d0 257 | // display the label at the top-left corner of the bounding box 258 | string label = cv::format("%.2f m", distance / 100); 259 | int baseLine; 260 | cv::Size labelSize = cv::getTextSize( 261 | label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); 262 | cv::putText(frame, label, cv::Point(car.x, car.y + labelSize.height), 263 | cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 255)); 264 | } 265 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/capture_thread.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef CAPTURE_THREAD_H 3 | #define CAPTURE_THREAD_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opencv2/opencv.hpp" 10 | #include "opencv2/videoio.hpp" 11 | #include "opencv2/dnn.hpp" 12 | 13 | using namespace std; 14 | 15 | class CaptureThread : public QThread 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit CaptureThread(int camera, QMutex *lock); 20 | explicit CaptureThread(QString videoPath, QMutex *lock); 21 | ~CaptureThread(); 22 | void setRunning(bool run) {running = run; }; 23 | void takePhoto() {taking_photo = true; } 24 | 25 | enum ViewMode { BIRDEYE, EYELEVEL, }; 26 | void setViewMode(ViewMode m) {viewMode = m; }; 27 | 28 | protected: 29 | void run() override; 30 | 31 | signals: 32 | void frameCaptured(cv::Mat *data); 33 | void photoTaken(QString name); 34 | 35 | private: 36 | void takePhoto(cv::Mat &frame); 37 | void detectObjectsDNN(cv::Mat &frame); 38 | 39 | private: 40 | bool running; 41 | int cameraID; 42 | QString videoPath; 43 | QMutex *data_lock; 44 | cv::Mat frame; 45 | 46 | int frame_width, frame_height; 47 | 48 | // take photos 49 | bool taking_photo; 50 | 51 | cv::dnn::Net net; 52 | vector objectClasses; 53 | 54 | ViewMode viewMode; 55 | }; 56 | 57 | #endif // CAPTURE_THREAD_H 58 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication app(argc, argv); 7 | MainWindow window; 8 | window.setWindowTitle("DiGauge"); 9 | window.show(); 10 | return app.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "opencv2/videoio.hpp" 14 | 15 | #include "mainwindow.h" 16 | #include "utilities.h" 17 | 18 | MainWindow::MainWindow(QWidget *parent) : 19 | QMainWindow(parent) 20 | , fileMenu(nullptr) 21 | , capturer(nullptr) 22 | { 23 | initUI(); 24 | data_lock = new QMutex(); 25 | } 26 | 27 | MainWindow::~MainWindow() 28 | { 29 | } 30 | 31 | void MainWindow::initUI() 32 | { 33 | this->resize(1000, 800); 34 | // setup menubar 35 | fileMenu = menuBar()->addMenu("&File"); 36 | viewMenu = menuBar()->addMenu("&View"); 37 | 38 | // main area 39 | QGridLayout *main_layout = new QGridLayout(); 40 | 41 | imageScene = new QGraphicsScene(this); 42 | imageView = new QGraphicsView(imageScene); 43 | main_layout->addWidget(imageView, 0, 0, 12, 1); 44 | 45 | // tools 46 | QGridLayout *tools_layout = new QGridLayout(); 47 | main_layout->addLayout(tools_layout, 12, 0, 1, 1); 48 | 49 | shutterButton = new QPushButton(this); 50 | shutterButton->setText("Take a Photo"); 51 | tools_layout->addWidget(shutterButton, 0, 0, Qt::AlignHCenter); 52 | connect(shutterButton, SIGNAL(clicked(bool)), this, SLOT(takePhoto())); 53 | 54 | // list of saved photos 55 | saved_list = new QListView(this); 56 | saved_list->setViewMode(QListView::IconMode); 57 | saved_list->setResizeMode(QListView::Adjust); 58 | saved_list->setSpacing(5); 59 | saved_list->setWrapping(false); 60 | list_model = new QStandardItemModel(this); 61 | saved_list->setModel(list_model); 62 | main_layout->addWidget(saved_list, 13, 0, 4, 1); 63 | 64 | QWidget *widget = new QWidget(); 65 | widget->setLayout(main_layout); 66 | setCentralWidget(widget); 67 | 68 | // setup status bar 69 | mainStatusBar = statusBar(); 70 | mainStatusLabel = new QLabel(mainStatusBar); 71 | mainStatusBar->addPermanentWidget(mainStatusLabel); 72 | mainStatusLabel->setText("DiGauge is Ready"); 73 | 74 | createActions(); 75 | populateSavedList(); 76 | } 77 | 78 | void MainWindow::createActions() 79 | { 80 | // create actions, add them to menus 81 | cameraInfoAction = new QAction("Camera &Information", this); 82 | fileMenu->addAction(cameraInfoAction); 83 | openCameraAction = new QAction("&Open Camera", this); 84 | fileMenu->addAction(openCameraAction); 85 | exitAction = new QAction("E&xit", this); 86 | fileMenu->addAction(exitAction); 87 | 88 | birdEyeAction = new QAction("Bird Eye View"); 89 | birdEyeAction->setCheckable(true); 90 | viewMenu->addAction(birdEyeAction); 91 | eyeLevelAction = new QAction("Eye Level View"); 92 | eyeLevelAction->setCheckable(true); 93 | viewMenu->addAction(eyeLevelAction); 94 | 95 | birdEyeAction->setChecked(true); 96 | 97 | // connect the signals and slots 98 | connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit())); 99 | connect(cameraInfoAction, SIGNAL(triggered(bool)), this, SLOT(showCameraInfo())); 100 | connect(openCameraAction, SIGNAL(triggered(bool)), this, SLOT(openCamera())); 101 | 102 | connect(birdEyeAction, SIGNAL(triggered(bool)), this, SLOT(changeViewMode())); 103 | connect(eyeLevelAction, SIGNAL(triggered(bool)), this, SLOT(changeViewMode())); 104 | } 105 | 106 | void MainWindow::showCameraInfo() 107 | { 108 | QList cameras = QCameraInfo::availableCameras(); 109 | QString info = QString("Available Cameras: \n"); 110 | 111 | foreach (const QCameraInfo &cameraInfo, cameras) 112 | info += " - " + cameraInfo.deviceName() + "\n"; 113 | 114 | QMessageBox::information(this, "Cameras", info); 115 | } 116 | 117 | 118 | void MainWindow::openCamera() 119 | { 120 | if(capturer != nullptr) { 121 | // if a thread is already running, stop it 122 | capturer->setRunning(false); 123 | disconnect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 124 | disconnect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 125 | connect(capturer, &CaptureThread::finished, capturer, &CaptureThread::deleteLater); 126 | } 127 | // I am using my second camera whose Index is 2. Usually, the 128 | // Index of the first camera is 0. 129 | int camID = 2; 130 | capturer = new CaptureThread(camID, data_lock); 131 | connect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame); 132 | connect(capturer, &CaptureThread::photoTaken, this, &MainWindow::appendSavedPhoto); 133 | capturer->start(); 134 | mainStatusLabel->setText(QString("Capturing Camera %1").arg(camID)); 135 | 136 | birdEyeAction->setChecked(true); 137 | eyeLevelAction->setChecked(false); 138 | } 139 | 140 | 141 | void MainWindow::updateFrame(cv::Mat *mat) 142 | { 143 | data_lock->lock(); 144 | currentFrame = *mat; 145 | data_lock->unlock(); 146 | 147 | QImage frame( 148 | currentFrame.data, 149 | currentFrame.cols, 150 | currentFrame.rows, 151 | currentFrame.step, 152 | QImage::Format_RGB888); 153 | QPixmap image = QPixmap::fromImage(frame); 154 | 155 | imageScene->clear(); 156 | imageView->resetMatrix(); 157 | imageScene->addPixmap(image); 158 | imageScene->update(); 159 | imageView->setSceneRect(image.rect()); 160 | } 161 | 162 | 163 | void MainWindow::takePhoto() 164 | { 165 | if(capturer != nullptr) { 166 | capturer->takePhoto(); 167 | } 168 | } 169 | 170 | 171 | void MainWindow::populateSavedList() 172 | { 173 | QDir dir(Utilities::getDataPath()); 174 | QStringList nameFilters; 175 | nameFilters << "*.jpg"; 176 | QFileInfoList files = dir.entryInfoList( 177 | nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name); 178 | 179 | foreach(QFileInfo photo, files) { 180 | QString name = photo.baseName(); 181 | QStandardItem *item = new QStandardItem(); 182 | list_model->appendRow(item); 183 | QModelIndex index = list_model->indexFromItem(item); 184 | list_model->setData(index, QPixmap(photo.absoluteFilePath()).scaledToHeight(145), Qt::DecorationRole); 185 | list_model->setData(index, name, Qt::DisplayRole); 186 | } 187 | } 188 | 189 | void MainWindow::appendSavedPhoto(QString name) 190 | { 191 | QString photo_path = Utilities::getPhotoPath(name, "jpg"); 192 | QStandardItem *item = new QStandardItem(); 193 | list_model->appendRow(item); 194 | QModelIndex index = list_model->indexFromItem(item); 195 | list_model->setData(index, QPixmap(photo_path).scaledToHeight(145), Qt::DecorationRole); 196 | list_model->setData(index, name, Qt::DisplayRole); 197 | saved_list->scrollTo(index); 198 | } 199 | 200 | void MainWindow::changeViewMode() 201 | { 202 | CaptureThread::ViewMode mode = CaptureThread::BIRDEYE; 203 | QAction *active_action = qobject_cast(sender()); 204 | if(active_action == birdEyeAction) { 205 | birdEyeAction->setChecked(true); 206 | eyeLevelAction->setChecked(false); 207 | mode = CaptureThread::BIRDEYE; 208 | } else if (active_action == eyeLevelAction) { 209 | eyeLevelAction->setChecked(true); 210 | birdEyeAction->setChecked(false); 211 | mode = CaptureThread::EYELEVEL; 212 | } 213 | if(capturer != nullptr) { 214 | capturer->setViewMode(mode); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "opencv2/opencv.hpp" 19 | 20 | #include "capture_thread.h" 21 | 22 | class MainWindow : public QMainWindow 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit MainWindow(QWidget *parent=nullptr); 28 | ~MainWindow(); 29 | 30 | private: 31 | void initUI(); 32 | void createActions(); 33 | void populateSavedList(); 34 | 35 | private slots: 36 | void showCameraInfo(); 37 | void openCamera(); 38 | void updateFrame(cv::Mat*); 39 | void takePhoto(); 40 | void appendSavedPhoto(QString name); 41 | void changeViewMode(); 42 | 43 | private: 44 | QMenu *fileMenu; 45 | 46 | QAction *cameraInfoAction; 47 | QAction *openCameraAction; 48 | QAction *exitAction; 49 | 50 | QMenu *viewMenu; 51 | 52 | QAction *birdEyeAction; 53 | QAction *eyeLevelAction; 54 | 55 | QGraphicsScene *imageScene; 56 | QGraphicsView *imageView; 57 | 58 | QPushButton *shutterButton; 59 | 60 | QListView *saved_list; 61 | QStandardItemModel *list_model; 62 | 63 | QStatusBar *mainStatusBar; 64 | QLabel *mainStatusLabel; 65 | 66 | cv::Mat currentFrame; 67 | 68 | // for capture thread 69 | QMutex *data_lock; 70 | CaptureThread *capturer; 71 | }; 72 | 73 | #endif // MAINWINDOW_H 74 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utilities.h" 9 | 10 | QString Utilities::getDataPath() 11 | { 12 | QString user_pictures_path = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation)[0]; 13 | QDir pictures_dir(user_pictures_path); 14 | pictures_dir.mkpath("DiGauge"); 15 | return pictures_dir.absoluteFilePath("DiGauge"); 16 | } 17 | 18 | QString Utilities::newPhotoName() 19 | { 20 | QDateTime time = QDateTime::currentDateTime(); 21 | return time.toString("yyyy-MM-dd+HH:mm:ss"); 22 | } 23 | 24 | QString Utilities::getPhotoPath(QString name, QString postfix) 25 | { 26 | return QString("%1/%2.%3").arg(Utilities::getDataPath(), name, postfix); 27 | } 28 | -------------------------------------------------------------------------------- /Chapter-07/DiGauge/utilities.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITIES_H_ 2 | #define UTILITIES_H_ 3 | 4 | #include 5 | 6 | class Utilities 7 | { 8 | public: 9 | static QString getDataPath(); 10 | static QString newPhotoName(); 11 | static QString getPhotoPath(QString name, QString postfix); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /Chapter-08/CVGLContext/Makefile: -------------------------------------------------------------------------------- 1 | EXE = main.exe 2 | CXX = g++ 3 | FLAGS = -Wall -I/home/kdr2/programs/opencv/include/opencv4 4 | LIBS = -lGLEW -lglfw -lGL -lm 5 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_highgui 6 | SRC = main.c 7 | 8 | all: 9 | ${CXX} ${FLAGS} -o ${EXE} ${SRC} ${LIBS} 10 | 11 | clean: 12 | rm *.o -fr 13 | rm ${EXE} 14 | -------------------------------------------------------------------------------- /Chapter-08/CVGLContext/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "opencv2/core.hpp" 4 | #include "opencv2/highgui.hpp" 5 | 6 | #include 7 | 8 | struct DrawData 9 | { 10 | GLuint vao; 11 | GLuint shader_prog; 12 | }; 13 | 14 | void draw(void* userdata) 15 | { 16 | DrawData* data = static_cast(userdata); 17 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 18 | glUseProgram(data->shader_prog); 19 | glBindVertexArray(data->vao); 20 | glDrawArrays(GL_TRIANGLES, 0, 3); 21 | } 22 | 23 | int main() { 24 | cv::namedWindow("OpenGL", cv::WINDOW_OPENGL); 25 | cv::resizeWindow("OpenGL", 640, 480); 26 | 27 | // start GLEW extension handler 28 | glewExperimental = GL_TRUE; 29 | GLenum ret = glewInit(); 30 | if (ret != GLEW_OK) { 31 | fprintf(stderr, "Error: %s\n", glewGetErrorString(ret)); 32 | } 33 | 34 | // opencv don't always choose the latest version of OpenGL, 35 | // and we have no chance to specify one, 36 | // so here it doesn't always use the core profile. 37 | printf("Using OpenGL: %s.\n", (const char*)glGetString(GL_VERSION)); 38 | 39 | // vao and vbo 40 | GLuint vao, vbo; 41 | GLfloat points[] = {+0.0f, +0.5f, +0.0f, 42 | +0.5f, -0.5f, +0.0f, 43 | -0.5f, -0.5f, +0.0f }; 44 | glGenBuffers(1, &vbo); 45 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 46 | glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(GLfloat), points, GL_STATIC_DRAW); 47 | 48 | glGenVertexArrays(1, &vao); 49 | glBindVertexArray(vao); 50 | glEnableVertexAttribArray(0); 51 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); 52 | 53 | // shader and shader program 54 | GLuint vert_shader, frag_shader; 55 | GLuint shader_prog; 56 | const char *vertex_shader_code = "#version 330\n" 57 | "in vec3 vp;" 58 | "void main () {" 59 | " gl_Position = vec4(vp, 1.0);" 60 | "}"; 61 | 62 | const char *fragment_shader_code = "#version 330\n" 63 | "out vec4 frag_colour;" 64 | "void main () {" 65 | " frag_colour = vec4(0.5, 1.0, 0.5, 1.0);" 66 | "}"; 67 | 68 | vert_shader = glCreateShader(GL_VERTEX_SHADER); 69 | glShaderSource(vert_shader, 1, &vertex_shader_code, NULL); 70 | glCompileShader(vert_shader); 71 | 72 | frag_shader = glCreateShader(GL_FRAGMENT_SHADER); 73 | glShaderSource(frag_shader, 1, &fragment_shader_code, NULL); 74 | glCompileShader(frag_shader); 75 | 76 | shader_prog = glCreateProgram(); 77 | glAttachShader(shader_prog, frag_shader); 78 | glAttachShader(shader_prog, vert_shader); 79 | glLinkProgram(shader_prog); 80 | 81 | DrawData data; 82 | data.vao = vao; 83 | data.shader_prog =shader_prog; 84 | 85 | cv::setOpenGlDrawCallback("OpenGL", draw, &data); 86 | 87 | while (true) { 88 | cv::updateWindow("OpenGL"); 89 | char key = (char)cv::waitKey(50); 90 | if (key == 27) 91 | break; 92 | } 93 | 94 | cv::setOpenGlDrawCallback("OpenGL", 0, 0); 95 | cv::destroyAllWindows(); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/GLFilter.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = GLFilter 3 | 4 | QT += core gui widgets 5 | 6 | INCLUDEPATH += . 7 | 8 | 9 | # use your own path in the following config 10 | unix: !mac { 11 | INCLUDEPATH += /home/kdr2/programs/opencv/include/opencv4 12 | LIBS += -L/home/kdr2/programs/opencv/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc 13 | } 14 | 15 | unix: mac { 16 | INCLUDEPATH += /path/to/opencv/include/opencv4 17 | LIBS += -L/path/to/opencv/lib -lopencv_world 18 | } 19 | 20 | win32 { 21 | INCLUDEPATH += c:/path/to/opencv/include/opencv4 22 | LIBS += -lc:/path/to/opencv/lib/opencv_world 23 | } 24 | 25 | 26 | DEFINES += QT_DEPRECATED_WARNINGS 27 | 28 | # Input 29 | HEADERS += glpanel.h 30 | SOURCES += main.cpp glpanel.cpp 31 | 32 | RESOURCES = res.qrc 33 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/glpanel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "glpanel.h" 9 | 10 | std::string textContent(QString path) { 11 | QFile file(path); 12 | file.open(QFile::ReadOnly | QFile::Text); 13 | QTextStream in(&file); 14 | return in.readAll().toStdString(); 15 | } 16 | 17 | GLPanel::GLPanel(QWidget *parent) : 18 | QOpenGLWidget(parent) 19 | { 20 | } 21 | 22 | GLPanel::~GLPanel() 23 | { 24 | } 25 | 26 | 27 | void GLPanel::initializeGL() 28 | { 29 | initializeOpenGLFunctions(); 30 | 31 | GLfloat points[] = { 32 | // first triangle 33 | +1.0f, +1.0f, +0.0f, +1.0f, +1.0f, // top-right 34 | +1.0f, -1.0f, +0.0f, +1.0f, +0.0f, // bottom-right 35 | -1.0f, -1.0f, +0.0f, +0.0f, +0.0f, // bottom-left 36 | // second triangle 37 | -1.0f, -1.0f, +0.0f, +0.0f, +0.0f, // bottom-left 38 | -1.0f, +1.0f, +0.0f, +0.0f, +1.0f, // top-left 39 | +1.0f, +1.0f, +0.0f, +1.0f, +1.0f // top-right 40 | }; 41 | 42 | // VBA & VAO 43 | glGenBuffers(1, &vbo); 44 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 45 | glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW); 46 | 47 | glGenVertexArrays(1, &vao); 48 | glBindVertexArray(vao); 49 | glEnableVertexAttribArray(0); 50 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL); 51 | glEnableVertexAttribArray(1); 52 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); 53 | 54 | // texture 55 | glEnable(GL_TEXTURE_2D); 56 | 57 | // 1. read the image data 58 | #ifdef USE_OPENCV 59 | img = cv::imread("./images/lizard.jpg"); 60 | cv::Mat tmp; 61 | cv::flip(img, tmp, 0); 62 | cvtColor(tmp, img, cv::COLOR_BGR2RGB); 63 | #else 64 | img = QImage(":/images/lizard.jpg"); 65 | img = img.convertToFormat(QImage::Format_RGB888).mirrored(false, true); 66 | #endif 67 | 68 | // 2. generate texture 69 | glGenTextures(1, &texture); 70 | glBindTexture(GL_TEXTURE_2D, texture); 71 | 72 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); 73 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); 74 | 75 | #ifdef USE_OPENCV 76 | glTexImage2D( 77 | GL_TEXTURE_2D, 0, GL_RGB, 78 | img.cols, img.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, img.data); 79 | #else 80 | glTexImage2D( 81 | GL_TEXTURE_2D, 0, GL_RGB, 82 | img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, img.bits()); 83 | #endif 84 | glGenerateMipmap(GL_TEXTURE_2D); 85 | 86 | 87 | // shader and shader program 88 | GLuint vert_shader, frag_shader; 89 | 90 | std::string vertex_shader_str = textContent(":/shaders/vertex.shader"); 91 | const char *vertex_shader_code = vertex_shader_str.data(); 92 | 93 | std::string fragment_shader_str = textContent(":/shaders/fragment.shader"); 94 | const char *fragment_shader_code = fragment_shader_str.data(); 95 | 96 | vert_shader = glCreateShader(GL_VERTEX_SHADER); 97 | glShaderSource(vert_shader, 1, &vertex_shader_code, NULL); 98 | glCompileShader(vert_shader); 99 | 100 | frag_shader = glCreateShader(GL_FRAGMENT_SHADER); 101 | glShaderSource(frag_shader, 1, &fragment_shader_code, NULL); 102 | glCompileShader(frag_shader); 103 | 104 | shaderProg = glCreateProgram(); 105 | glAttachShader(shaderProg, frag_shader); 106 | glAttachShader(shaderProg, vert_shader); 107 | glLinkProgram(shaderProg); 108 | 109 | // scale ration 110 | glUseProgram(shaderProg); 111 | int pixel_scale_loc = glGetUniformLocation(shaderProg, "pixelScale"); 112 | #ifdef USE_OPENCV 113 | glUniform2f(pixel_scale_loc, 1.0f / img.cols, 1.0f / img.rows); 114 | #else 115 | glUniform2f(pixel_scale_loc, 1.0f / img.width(), 1.0f / img.height()); 116 | #endif 117 | 118 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 119 | glClearColor(0.1, 0.1, 0.2, 1.0); 120 | 121 | #ifdef USE_OPENCV 122 | ((QMainWindow*)this->parent())->resize(img.cols, img.rows); 123 | #else 124 | ((QMainWindow*)this->parent())->resize(img.width(), img.height()); 125 | #endif 126 | } 127 | 128 | void GLPanel::paintGL() 129 | { 130 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 131 | 132 | glUseProgram(shaderProg); 133 | glBindVertexArray(vao); 134 | glBindTexture(GL_TEXTURE_2D, texture); 135 | glDrawArrays(GL_TRIANGLES, 0, 6); 136 | glFlush(); 137 | saveOutputImage("./output.jpg"); 138 | } 139 | 140 | void GLPanel::resizeGL(int w, int h) 141 | { 142 | glViewport(0, 0, (GLsizei)w, (GLsizei)h); 143 | } 144 | 145 | void GLPanel::saveOutputImage(QString path) 146 | { 147 | #ifdef USE_OPENCV 148 | cv::Mat output(img.rows, img.cols, CV_8UC3); 149 | glReadPixels( 150 | 0, 0, img.cols, img.rows, GL_RGB, GL_UNSIGNED_BYTE, output.data); 151 | cv::Mat tmp; 152 | cv::flip(output, tmp, 0); 153 | cvtColor(tmp, output, cv::COLOR_RGB2BGR); 154 | cv::imwrite(path.toStdString(), output); 155 | #else 156 | QImage output(img.width(), img.height(), QImage::Format_RGB888); 157 | glReadPixels( 158 | 0, 0, img.width(), img.height(), GL_RGB, GL_UNSIGNED_BYTE, output.bits()); 159 | output = output.mirrored(false, true); 160 | output.save(path); 161 | #endif 162 | } 163 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/glpanel.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef GL_PANEL_H 3 | #define GL_PANEL_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #define USE_OPENCV 17 | 18 | #ifdef USE_OPENCV 19 | #include "opencv2/opencv.hpp" 20 | #endif 21 | 22 | class GLPanel : public QOpenGLWidget, protected QOpenGLFunctions_4_2_Core 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | GLPanel(QWidget *parent = nullptr); 28 | ~GLPanel(); 29 | 30 | protected: 31 | void initializeGL() override; 32 | void paintGL() override; 33 | void resizeGL(int w, int h) override; 34 | 35 | private: 36 | void saveOutputImage(QString path); 37 | 38 | private: 39 | GLuint vbo; 40 | GLuint vao; 41 | GLuint texture; 42 | GLuint shaderProg; 43 | 44 | #ifdef USE_OPENCV 45 | cv::Mat img; 46 | #else 47 | QImage img; 48 | #endif 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/images/lizard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/Chapter-08/GLFilter/images/lizard.jpg -------------------------------------------------------------------------------- /Chapter-08/GLFilter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "glpanel.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QApplication app(argc, argv); 10 | 11 | QSurfaceFormat format = QSurfaceFormat::defaultFormat(); 12 | format.setProfile(QSurfaceFormat::CoreProfile); 13 | format.setVersion(4, 2); 14 | format.setDepthBufferSize(24); 15 | format.setAlphaBufferSize(8); 16 | format.setStencilBufferSize(8); 17 | QSurfaceFormat::setDefaultFormat(format); 18 | 19 | QMainWindow window; 20 | window.setWindowTitle("GLFilter"); 21 | window.resize(800, 600); 22 | GLPanel *panel = new GLPanel(&window); 23 | window.setCentralWidget(panel); 24 | window.show(); 25 | return app.exec(); 26 | } 27 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | shaders/vertex.shader 5 | shaders/fragment.shader 6 | images/lizard.jpg 7 | 8 | 9 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/shaders/fragment.shader: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | in vec2 texCoord; 4 | 5 | out vec4 frag_color; 6 | 7 | uniform sampler2D theTexture; 8 | 9 | uniform vec2 pixelScale; 10 | 11 | void main() 12 | { 13 | int kernel_size = 7; 14 | vec4 color = vec4(0.0, 0.0, 0.0, 0.0); 15 | // if(texCoord.x > 0.5) { 16 | for(int i = -(kernel_size / 2); i <= kernel_size / 2; i++) { 17 | for(int j = -(kernel_size / 2); j <= kernel_size / 2; j++) { 18 | if(i == 0 && j == 0) continue; 19 | vec2 coord = vec2(texCoord.x + i * pixelScale.x, texCoord.y + i * pixelScale.y); 20 | color = color + texture(theTexture, coord); 21 | } 22 | } 23 | frag_color = color / (kernel_size * kernel_size - 1); 24 | // } else { 25 | // frag_color = texture(theTexture, texCoord); 26 | // } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter-08/GLFilter/shaders/vertex.shader: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | layout (location = 0) in vec3 vertex; 4 | layout (location = 1) in vec2 inTexCoord; 5 | 6 | out vec2 texCoord; 7 | 8 | void main() 9 | { 10 | gl_Position = vec4(vertex, 1.0); 11 | texCoord = inTexCoord; 12 | } -------------------------------------------------------------------------------- /Chapter-08/Hello-OpenGL/Makefile: -------------------------------------------------------------------------------- 1 | EXE = main.exe 2 | CC = gcc 3 | FLAGS = -Wall -pedantic -std=c99 4 | LIBS = -lGLEW -lglfw -lGL -lm 5 | SRC = main.c 6 | 7 | all: 8 | ${CC} ${FLAGS} -o ${EXE} ${SRC} ${LIBS} 9 | 10 | clean: 11 | rm *.o -fr 12 | rm ${EXE} 13 | -------------------------------------------------------------------------------- /Chapter-08/Hello-OpenGL/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | int main() { 7 | 8 | // init glfw and GL context 9 | if (!glfwInit()) { 10 | fprintf(stderr, "ERROR: could not start GLFW3\n"); 11 | return 1; 12 | } 13 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 3.3 or 4.x 14 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 15 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 16 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 17 | 18 | 19 | GLFWwindow *window = NULL; 20 | window = glfwCreateWindow(640, 480, "Hello OpenGL", NULL, NULL); 21 | if (!window) { 22 | fprintf(stderr, "ERROR: could not open window with GLFW3\n"); 23 | glfwTerminate(); 24 | return 1; 25 | } 26 | glfwMakeContextCurrent(window); 27 | 28 | // start GLEW extension handler 29 | glewExperimental = GL_TRUE; 30 | GLenum ret = glewInit(); 31 | if ( ret != GLEW_OK) { 32 | fprintf(stderr, "Error: %s\n", glewGetErrorString(ret)); 33 | } 34 | 35 | // vao and vbo 36 | GLuint vao, vbo; 37 | 38 | GLfloat points[] = {+0.0f, +0.5f, +0.0f, 39 | +0.5f, -0.5f, +0.0f, 40 | -0.5f, -0.5f, +0.0f }; 41 | glGenBuffers(1, &vbo); 42 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 43 | glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(GLfloat), points, GL_STATIC_DRAW); 44 | 45 | glGenVertexArrays(1, &vao); 46 | glBindVertexArray(vao); 47 | glEnableVertexAttribArray(0); 48 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); 49 | 50 | // shader and shader program 51 | GLuint vert_shader, frag_shader; 52 | GLuint shader_prog; 53 | const char *vertex_shader_code = "#version 330\n" 54 | "in vec3 vp;" 55 | "void main () {" 56 | " gl_Position = vec4(vp, 1.0);" 57 | "}"; 58 | 59 | const char *fragment_shader_code = "#version 330\n" 60 | "out vec4 frag_colour;" 61 | "void main () {" 62 | " frag_colour = vec4(0.5, 1.0, 0.5, 1.0);" 63 | "}"; 64 | 65 | vert_shader = glCreateShader(GL_VERTEX_SHADER); 66 | glShaderSource(vert_shader, 1, &vertex_shader_code, NULL); 67 | glCompileShader(vert_shader); 68 | 69 | frag_shader = glCreateShader(GL_FRAGMENT_SHADER); 70 | glShaderSource(frag_shader, 1, &fragment_shader_code, NULL); 71 | glCompileShader(frag_shader); 72 | 73 | shader_prog = glCreateProgram(); 74 | glAttachShader(shader_prog, frag_shader); 75 | glAttachShader(shader_prog, vert_shader); 76 | glLinkProgram(shader_prog); 77 | 78 | while (!glfwWindowShouldClose(window)) { 79 | // wipe the drawing surface clear 80 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 81 | glUseProgram(shader_prog); 82 | glBindVertexArray(vao); 83 | // draw points 0-3 from the currently bound VAO with current in-use shader 84 | glDrawArrays(GL_TRIANGLES, 0, 3); 85 | // update other events like input handling 86 | glfwPollEvents(); 87 | // put the stuff we've been drawing onto the display 88 | glfwSwapBuffers(window); 89 | } 90 | 91 | glfwTerminate(); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/QtGL.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = QtGL 3 | 4 | QT += core gui widgets 5 | 6 | INCLUDEPATH += . 7 | 8 | DEFINES += QT_DEPRECATED_WARNINGS 9 | 10 | # Input 11 | HEADERS += glpanel.h 12 | SOURCES += main.cpp glpanel.cpp 13 | 14 | RESOURCES = shaders.qrc 15 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/glpanel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "glpanel.h" 8 | 9 | std::string textContent(QString path) { 10 | QFile file(path); 11 | file.open(QFile::ReadOnly | QFile::Text); 12 | QTextStream in(&file); 13 | return in.readAll().toStdString(); 14 | } 15 | 16 | GLPanel::GLPanel(QWidget *parent) : 17 | QOpenGLWidget(parent) 18 | { 19 | } 20 | 21 | GLPanel::~GLPanel() 22 | { 23 | } 24 | 25 | 26 | void GLPanel::initializeGL() 27 | { 28 | initializeOpenGLFunctions(); 29 | 30 | qDebug() << "GL Version:" << QString((const char*)glGetString(GL_VERSION)); 31 | 32 | GLfloat points[] = {+0.0f, +0.5f, +0.0f, 33 | +0.5f, -0.5f, +0.0f, 34 | -0.5f, -0.5f, +0.0f }; 35 | 36 | #ifndef USE_QTGL_API 37 | glGenBuffers(1, &vbo); 38 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 39 | // sizeof(points) = 9 * sizeof(GLfloat) 40 | glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW); 41 | 42 | glGenVertexArrays(1, &vao); 43 | glBindVertexArray(vao); 44 | glEnableVertexAttribArray(0); 45 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); 46 | 47 | // shader and shader program 48 | GLuint vert_shader, frag_shader; 49 | 50 | std::string vertex_shader_str = textContent(":/shaders/vertex.shader"); 51 | const char *vertex_shader_code = vertex_shader_str.data(); 52 | 53 | std::string fragment_shader_str = textContent(":/shaders/fragment.shader"); 54 | const char *fragment_shader_code = fragment_shader_str.data(); 55 | 56 | vert_shader = glCreateShader(GL_VERTEX_SHADER); 57 | glShaderSource(vert_shader, 1, &vertex_shader_code, NULL); 58 | glCompileShader(vert_shader); 59 | 60 | frag_shader = glCreateShader(GL_FRAGMENT_SHADER); 61 | glShaderSource(frag_shader, 1, &fragment_shader_code, NULL); 62 | glCompileShader(frag_shader); 63 | 64 | shaderProg = glCreateProgram(); 65 | glAttachShader(shaderProg, frag_shader); 66 | glAttachShader(shaderProg, vert_shader); 67 | glLinkProgram(shaderProg); 68 | 69 | #else 70 | 71 | vbo = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); 72 | vbo.setUsagePattern(QOpenGLBuffer::StaticDraw); 73 | Q_ASSERT(vbo.create()); 74 | Q_ASSERT(vbo.bind()); 75 | vbo.allocate(sizeof(points)); 76 | vbo.write(0, points, sizeof(points)); 77 | 78 | const int vPosition = 0; 79 | vao.create(); 80 | vao.bind(); 81 | glEnableVertexAttribArray(vPosition); 82 | glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 0, NULL); 83 | 84 | shaderProg.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vertex.shader"); 85 | shaderProg.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fragment.shader"); 86 | Q_ASSERT(shaderProg.link()); 87 | Q_ASSERT(shaderProg.bind()); 88 | 89 | #endif 90 | 91 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 92 | glClearColor(0.1, 0.1, 0.2, 1.0); 93 | } 94 | 95 | void GLPanel::paintGL() 96 | { 97 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 98 | 99 | #ifndef USE_QTGL_API 100 | glUseProgram(shaderProg); 101 | glBindVertexArray(vao); 102 | #else 103 | shaderProg.bind(); 104 | vao.bind(); 105 | #endif 106 | glDrawArrays(GL_TRIANGLES, 0, 3); 107 | glFlush(); 108 | } 109 | 110 | void GLPanel::resizeGL(int w, int h) 111 | { 112 | glViewport(0, 0, (GLsizei)w, (GLsizei)h); 113 | } 114 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/glpanel.h: -------------------------------------------------------------------------------- 1 | // mode: c++ 2 | #ifndef GL_PANEL_H 3 | #define GL_PANEL_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // #define USE_QTGL_API 14 | 15 | class GLPanel : public QOpenGLWidget, protected QOpenGLFunctions_4_2_Core 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | GLPanel(QWidget *parent = nullptr); 21 | ~GLPanel(); 22 | 23 | protected: 24 | void initializeGL() override; 25 | void paintGL() override; 26 | void resizeGL(int w, int h) override; 27 | private: 28 | 29 | #ifndef USE_QTGL_API 30 | GLuint vbo; 31 | GLuint vao; 32 | GLuint shaderProg; 33 | #else 34 | QOpenGLBuffer vbo; 35 | QOpenGLVertexArrayObject vao; 36 | QOpenGLShaderProgram shaderProg; 37 | #endif 38 | 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "glpanel.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QApplication app(argc, argv); 10 | 11 | QSurfaceFormat format = QSurfaceFormat::defaultFormat(); 12 | format.setProfile(QSurfaceFormat::CoreProfile); 13 | format.setVersion(4, 2); 14 | QSurfaceFormat::setDefaultFormat(format); 15 | 16 | QMainWindow window; 17 | window.setWindowTitle("QtGL"); 18 | window.resize(800, 600); 19 | GLPanel *panel = new GLPanel(&window); 20 | window.setCentralWidget(panel); 21 | window.show(); 22 | return app.exec(); 23 | } 24 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/shaders.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | shaders/vertex.shader 5 | shaders/fragment.shader 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter-08/QtGL/shaders/fragment.shader: -------------------------------------------------------------------------------- 1 | #version 420 2 | out vec4 frag_color; 3 | void main() 4 | { 5 | frag_color = vec4(0.5, 1.0, 0.5, 1.0); 6 | } -------------------------------------------------------------------------------- /Chapter-08/QtGL/shaders/vertex.shader: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | layout(location=0) in vec3 vertex; 4 | 5 | void main() 6 | { 7 | gl_Position = vec4(vertex, 1.0); 8 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## $5 Tech Unlocked 2021! 5 | [Buy and download this Book for only $5 on PacktPub.com](https://www.packtpub.com/product/qt-5-and-opencv-4-computer-vision-projects/9781789532586) 6 | ----- 7 | *If you have read this book, please leave a review on [Amazon.com](https://www.amazon.com/gp/product/1789532582). Potential readers can then use your unbiased opinion to help them make purchase decisions. Thank you. The $5 campaign runs from __December 15th 2020__ to __January 13th 2021.__* 8 | 9 | # Qt 5 and OpenCV 4 Computer Vision Projects 10 | 11 | Qt 5 and OpenCV 4 Computer Vision Projects 12 | 13 | This is the code repository for [Qt 5 and OpenCV 4 Computer Vision Projects](link), published by Packt. 14 | 15 | **Get up to speed with cross-platform computer vision app development by building seven practical projects** 16 | 17 | ## What is this book about? 18 | We are entering the age of artificial intelligence, and Computer Vision plays an important role in the AI field. This book combines OpenCV 4 and Qt 5 as well as many deep learning models to develop many complete, practical, and functional applications through which the readers can learn a lot in CV, GUI, and AI domains. 19 | 20 | This book covers the following exciting features: 21 | Create an image viewer with all the basic requirements 22 | Construct an image editor to filter or transform images 23 | Develop a security app to detect movement and secure homes 24 | Build an app to detect facial landmarks and apply masks to faces 25 | Create an app to extract text from scanned documents and photos 26 | Train and use cascade classifiers and DL models for object detection 27 | Build an app to measure the distance between detected objects 28 | Implement high-speed image filters on GPU with Open Graphics Library (OpenGL) 29 | 30 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789532582) today! 31 | 32 | https://www.packtpub.com/ 34 | 35 | ## Instructions and Navigations 36 | All of the code is organized into folders. For example, Chapter02. 37 | 38 | The code will look like the following: 39 | ``` 40 | QMenu *editMenu; 41 | QToolBar *editToolBar; 42 | QAction *blurAction; 43 | ``` 44 | 45 | **Following is what you need for this book:** 46 | This book is for engineers and developers who are familiar with both Qt and OpenCV frameworks and are capable of creating simple projects using them, but want to build their skills to create professional-level projects using them. Familiarity with the C++ language is a must to follow the example source codes in this book. 47 | 48 | With the following software and hardware list you can run all code files present in the book (Chapter 1-8). 49 | ### Software and Hardware List 50 | | Chapter | Software required | OS required | 51 | | -------- | ------------------------------------ | ----------------------------------- | 52 | | 1 | Qt 5.x | Windows, Mac OS X, and Linux (Any) | 53 | | 2, 3 | Qt 5.x, OpenCV 4.x | Windows, Mac OS X, and Linux (Any) | 54 | | 4 | Qt 5.x, OpenCV 4.x, OpenCV Extra Modules 4.x | Windows, Mac OS X, and Linux (Any) | 55 | | 5 | Qt 5.x, OpenCV 4.x, Tesseract 4.x | Windows, Mac OS X, and Linux (Any) | 56 | | 6 | Qt 5.x, OpenCV 3.4.5, OpenCV 4.x | Windows, Mac OS X, and Linux (Any) | 57 | | 7 | Qt 5.x, OpenCV 4.x | Windows, Mac OS X, and Linux (Any) | 58 | | 8 | Qt 5.x, Mesa 18.x (only on Linux), GLFW 3.x, GLEW 2.x | Windows, Mac OS X, and Linux (Any) | 59 | 60 | 61 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](http://www.packtpub.com/sites/default/files/downloads/9781789532586_ColorImages.pdf). 62 | 63 | ## Code in Action 64 | 65 | Click on the following link to see the Code in Action: 66 | 67 | [Click here to view the videos](http://bit.ly/2FfYSDS) 68 | 69 | ### Related products 70 | * Computer Vision with OpenCV 3 and Qt5 [[Packt]](https://www.packtpub.com/application-development/computer-vision-opencv-3-and-qt5?utm_source=github&utm_medium=repository&utm_campaign=9781788472395) [[Amazon]](https://www.amazon.com/dp/178847239X) 71 | 72 | * [[Packt]](Mastering OpenCV 4 - Third Edition?utm_source=github&utm_medium=repository&utm_campaign=) [[Amazon]](https://www.amazon.com/dp/1789533570) 73 | 74 | 75 | ## Get to Know the Author 76 | **Zhuo Qingliang** 77 | (a.k.a. @KDr2 online) is presently working at Beijing Paoding Technology Co. LTD., a start-up Fintech company in China that is dedicated to improving the financial industry by using artificial intelligence technologies. He has over 10 years’ experience in Linux, C, C++, Python, Perl, and Java development. He is interested in programming, doing consulting work, participating in and contributing to the open source community (of course, includes the Julia community). 78 | 79 | 80 | ## Other books by the authors 81 | []() 82 | 83 | 84 | 85 | ### Suggestions and Feedback 86 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 87 | ### Download a free PDF 88 | 89 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
90 |

https://packt.link/free-ebook/9781789532586

-------------------------------------------------------------------------------- /WireFrames.epgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects/eaf077670c0a873d0c181dc584d53067715ceaf5/WireFrames.epgz --------------------------------------------------------------------------------