1

I have this Qt Project where I can capture an Image from WebCam and afterwards draw on it using OpenCV MouseCallbacks (I perform the drawing on the imshow, not the QGraphicsView). I can capture the Image and display it using my pushbutton, but I can't draw anything (it even crashes after I click on the Image).

Codes:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPixmap>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>

#include <vector>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

     static void DrawScanPoints(int event, int x, int y, int flag, void* param);
     void DrawScanPoints(int event, int x, int y);

private slots:
    void on_pbt_Capture_clicked();

    void on_pbt_Scan_clicked();

public slots:
    void UpdateFrame();

private:
    Ui::MainWindow *ui;
    cv::VideoCapture videoCap;
     cv::Mat liveImage, inputImage;
    bool camRun = true;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    videoCap.open(0);

    QTimer* timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(UpdateFrame()));
    timer->start(20);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::UpdateFrame()
{
    if(camRun)
    {
        videoCap.read(liveImage);

        QImage image = QImage(liveImage.data, liveImage.cols, liveImage.rows, QImage::Format_RGB888).rgbSwapped();
        QGraphicsScene* scene = new QGraphicsScene(this);
        scene->addPixmap(QPixmap::fromImage(image));

        ui->graphicsView->setScene(scene);
        ui->graphicsView->show();
    }
}

void MainWindow::DrawScanPoints(int event, int x, int y, int flag, void* param)
{
    MainWindow* mw = reinterpret_cast<MainWindow*>(param);
    mw->DrawScanPoints(event, x, y);
}

void MainWindow::DrawScanPoints(int event, int x, int y)
{
    if(event & cv::EVENT_LBUTTONDOWN)
    {
        cv::Point pt = cv::Point(x, y);
        cv::circle(inputImage, pt, 10, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
    }
}

void MainWindow::on_pbt_Capture_clicked()
{
    camRun = false;
    inputImage = liveImage;

    cv::namedWindow("Capture");
    cv::setMouseCallback("Capture", DrawScanPoints, this);

    while(1)
    {
        cv::imshow("Capture", inputImage);
        cv::waitKey(0);
        cv::destroyAllWindows();
    }
}

void MainWindow::on_pbt_Scan_clicked()
{

}


main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>608</width>
    <height>440</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QGraphicsView" name="graphicsView">
    <property name="geometry">
     <rect>
      <x>10</x>
      <y>10</y>
      <width>501</width>
      <height>391</height>
     </rect>
    </property>
   </widget>
   <widget class="QPushButton" name="pbt_Capture">
    <property name="geometry">
     <rect>
      <x>520</x>
      <y>10</y>
      <width>81</width>
      <height>20</height>
     </rect>
    </property>
    <property name="text">
     <string>Capture</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pbt_Scan">
    <property name="geometry">
     <rect>
      <x>520</x>
      <y>40</y>
      <width>80</width>
      <height>18</height>
     </rect>
    </property>
    <property name="text">
     <string>Scan</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>608</width>
     <height>17</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

nowsqwhat
  • 101
  • 7

1 Answers1

0

I'm not able to reproduce this locally (I'm missing some of the source files, e.g. main and the UI) but probably you should pass this in setMouseCallback as the last parameter. See How should I pass a pointer-to-member-function to setMouseCallback in OpenCV?

Edit after question was updated:

Remove cv::destroyAllWindows from on_pbt_Capture_clicked; replace cv::waitKey(0); with cv::waitKey(30);:

void MainWindow::on_pbt_Capture_clicked()
{
    camRun = false;
    inputImage = liveImage;

    cv::namedWindow("Capture");
    cv::setMouseCallback("Capture", DrawScanPoints, this);

    while(1)
    {
        cv::imshow("Capture", inputImage);
        cv::waitKey(30);
    }
}

Reason: you've never updated the "capture" window: waitKey(0) waits forever (for any key input) - so your window will be updated with circles only after you press any key on the keyboard. So if you change to waitKey(30) the updates will be happening every 30 [ms] automatically (timeout after which waitKey exits).

No need to destroy and recreate window every time hence removing destroyAllWindows. If you want to be smart about when to update the window in on_pbt_Capture_clicked, use conditional variable. WAit for it inside while(1) and notify_one it from inside DrawScanPoints.

Quarra
  • 2,527
  • 1
  • 19
  • 27
  • It now detects my clicks, however the circle are only shown after I try to close the imshow (I can't close it completely because of the while loop). I also edited the question above. – nowsqwhat May 17 '22 at 12:02