QImage corrupts with OpenCV [duplicate] - c++

This question already has answers here:
cv::Mat to QImage and back
(2 answers)
Closed 1 year ago.
I have a webcam demo with Qt and OpenCV. Basically it will show the webcam's feed, and when a button gets clicked, it starts a thread with a long (three-five seconds) thread.
The problem is that the QImage gets immediately corrupted as you can see here when I click the button, and I don't see the video feed anymore. The signals & slots work (I see the output in the console), but I cannot spot the problem here.
Can anyone help?
window::window() : QMainWindow(NULL, 0)
{
std::cout << "constructor start" << std::endl;
setWindowTitle("Video");
button = new QPushButton("Long Job");
connect(button, SIGNAL(clicked()), this, SLOT(longjob()));
image = new QLabel();
image->setAlignment(Qt::AlignCenter);
layout = new QVBoxLayout();
layout->addWidget(image);
layout->addWidget(button);
mainwidget = new QWidget();
mainwidget->setLayout(layout);
resize(800, 600);
setCentralWidget(mainwidget);
cap = cv::VideoCapture(0);
timer = new QTimer();
timer->setInterval(100);
timer->start();
connect(timer, SIGNAL(timeout()), this, SLOT(newframe()));
std::cout << "constructor end" << std::endl;
}
void window::newframe()
{
std::cout << "FRAME " << count++ << std::endl;
cv::Mat frame;
cap >> frame;
image->setPixmap(QPixmap::fromImage(showImage(frame))); // converts perfectly
}
void window::longjob()
{
std::cout << "START THREAD" << std::endl;
w = new worker();
connect(w, SIGNAL(resultReady(double)), this, SLOT(detected(double)));
w->start();
return;
}
void window::detected(double d)
{
disconnect(w, SIGNAL(resultReady(double)), this, SLOT(detected(double)));
std::cout << "DETECTED " << d << std::endl;
delete w;
frames.clear();
}
class worker : public QThread
{
Q_OBJECT
public:
worker();
~worker();
void run() Q_DECL_OVERRIDE;
signals:
void resultReady(double d);
};
worker::worker() : QThread()
{
}
worker::~worker()
{
std::cout << "THREAD EXIT" << std::endl;
}
void worker::run()
{
std::cout << "THREAD RUN" << std::endl;
double d = longOpenCVJob();
emit resultReady(d);
}

I would use this code for showImage():
QImage window::showImage(cv::Mat mat)
{
cv::Mat tmp;
mat.convertTo(tmp, CV_8U);
cvtColor(tmp, tmp, CV_BGR2RGB);
QImage img = QImage((const unsigned char *)(tmp.data), tmp.cols, tmp.rows, tmp.step, QImage::Format_RGB888);
return img;
}

Related

capture QVideoFrame and display it in QVideoWidget

I want capture from camera frames.
I setup ui, camera, and surface.
QtVideoWidgetsIssueTrack::QtVideoWidgetsIssueTrack(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
QCamera* mCamera = new QCamera();
QMediaRecorder* recorder = new QMediaRecorder(mCamera);
QVideoEncoderSettings settings = recorder->videoSettings();
QMyAbstractVideoSurface* surface = new QMyAbstractVideoSurface();
settings.setResolution(640, 480);
settings.setQuality(QMultimedia::VeryHighQuality);
settings.setFrameRate(30.0);
settings.setCodec("video/mp4");
recorder->setVideoSettings(settings);
recorder->setContainerFormat("mp4");
mCamera->setCaptureMode(QCamera::CaptureViewfinder);
mCamera->setViewfinder(surface);
// Set layout
QGridLayout* layout = new QGridLayout();
QVideoWidget* videoWidget = new QVideoWidget;
videoWidget->show();
// Set layout in QWidget
QWidget* window = new QWidget();
layout->addWidget(videoWidget);
window->setLayout(layout);
// Set QWidget as the central layout of the main window
setCentralWidget(window);
bool o = recorder->setOutputLocation(QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + "/" + "test_video"));
recorder->record();
mCamera->start();
qDebug() << o;
qDebug() << recorder->supportedVideoCodecs();
qDebug() << recorder->state();
qDebug() << recorder->error();
qDebug() << recorder->outputLocation();
}
QtVideoWidgetsIssueTrack::~QtVideoWidgetsIssueTrack()
{}
I have created QMyAbstractVideoSurface instance and set surface in camera.
#pragma once
#include <QtMultimedia/QAbstractVideoSurface>
class QMyAbstractVideoSurface : public QAbstractVideoSurface
{
Q_OBJECT
public:
explicit QMyAbstractVideoSurface(QObject* parent = 0);
~QMyAbstractVideoSurface();
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
bool present(const QVideoFrame& frame);
bool start(const QVideoSurfaceFormat& format);
void stop();
};
Now the problem is capture QVideoFrame from present method and display it in QVideoWidget.
Then add QVideoWidget to grid layout.
#include "QMyAbstractVideoSurface.h"
#include <QDebug>
#include <QBuffer>
QMyAbstractVideoSurface::QMyAbstractVideoSurface(QObject* parent) {
}
bool QMyAbstractVideoSurface::start(const QVideoSurfaceFormat& format) {
return QAbstractVideoSurface::start(format);
}
void QMyAbstractVideoSurface::stop() {
QAbstractVideoSurface::stop();
}
bool QMyAbstractVideoSurface::present(const QVideoFrame& frame) {
if (frame.isValid()) {
QVideoFrame cloneFrame(frame);
QAbstractVideoBuffer::HandleType handleType;
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
qDebug() << cloneFrame;
const QImage image(cloneFrame.bits(),
cloneFrame.width(),
cloneFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
QByteArray ba;
QBuffer bu(&ba);
//bu.open(QBuffer::ReadWrite);
bu.open(QIODevice::WriteOnly);
image.save(&bu, "PNG");
//bu.close();
//QString imgBase64 = ba.toBase64();
QString imgBase64 = QString::fromLatin1(ba.toBase64().data());
qDebug() << "image base64: " << imgBase64;
cloneFrame.unmap();
return true;
}
return true;
}
QList<QVideoFrame::PixelFormat> QMyAbstractVideoSurface::
supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
Q_UNUSED(handleType);
return QList<QVideoFrame::PixelFormat>()
<< QVideoFrame::Format_ARGB32
<< QVideoFrame::Format_ARGB32_Premultiplied
<< QVideoFrame::Format_RGB32
<< QVideoFrame::Format_RGB24
<< QVideoFrame::Format_RGB565
<< QVideoFrame::Format_RGB555
<< QVideoFrame::Format_ARGB8565_Premultiplied
<< QVideoFrame::Format_BGRA32
<< QVideoFrame::Format_BGRA32_Premultiplied
<< QVideoFrame::Format_BGR32
<< QVideoFrame::Format_BGR24
<< QVideoFrame::Format_BGR565
<< QVideoFrame::Format_BGR555
<< QVideoFrame::Format_BGRA5658_Premultiplied
<< QVideoFrame::Format_AYUV444
<< QVideoFrame::Format_AYUV444_Premultiplied
<< QVideoFrame::Format_YUV444
<< QVideoFrame::Format_YUV420P
<< QVideoFrame::Format_YV12
<< QVideoFrame::Format_UYVY
<< QVideoFrame::Format_YUYV
<< QVideoFrame::Format_NV12
<< QVideoFrame::Format_NV21
<< QVideoFrame::Format_IMC1
<< QVideoFrame::Format_IMC2
<< QVideoFrame::Format_IMC3
<< QVideoFrame::Format_IMC4
<< QVideoFrame::Format_Y8
<< QVideoFrame::Format_Y16
<< QVideoFrame::Format_Jpeg
<< QVideoFrame::Format_CameraRaw
<< QVideoFrame::Format_AdobeDng;
}
QMyAbstractVideoSurface::~QMyAbstractVideoSurface()
{}
it display me this
QVideoFrame(QSize(640, 480), Format_YUYV, NoHandle, ReadOnly, [no timestamp])
image base64: ""

Problem with connecting to camera in Qt CPP

I'm running a simple program in Qt creator to connecting to a camera. When I run the program, the following issue is reported:
Graph failed to connect filters -2147467259
[0x0] Failed to connect graph (The operation completed successfully.)
Can somebody tell me what the problem is?! Any solution would be appreciated.
Thanks
I followed the link you mentioned. But i didn't understand the exact way to solve the problem.
Here is the code i used to connect to a camera:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connected = false;
camera = new QCamera();
qDebug() << "Number of cameras found:" << QCameraInfo::availableCameras().count();
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
foreach (const QCameraInfo &cameraInfo, cameras)
{
qDebug() << "Camera info:" << cameraInfo.deviceName() << cameraInfo.description() << cameraInfo.position();
ui->comboBox->addItem(cameraInfo.description());
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
if(!connected)
connectCamera();
else {
camera->stop();
viewfinder->deleteLater();
ui->pushButton->setText("Connect");
connected = false;
}
}
void MainWindow::connectCamera()
{
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
foreach (const QCameraInfo &cameraInfo, cameras)
{
qDebug() << cameraInfo.description() << ui->comboBox->currentText();
if(cameraInfo.description()==ui->comboBox->currentText()) {
camera = new QCamera(cameraInfo, this);
viewfinder = new QCameraViewfinder(this);
camera->setViewfinder(viewfinder);
ui->horizontalLayout_2->addWidget(viewfinder);
connected = true;
ui->pushButton->setText("Disconnect");
camera->start();
return;
}
}
}

Draw one by one image on screen from QList<QImage> in paintevent() method in Qt4.8

I am creating QImage reading from shared memory and adding to QList and in paintevent I want to draw all Images I have in QList but ending up with crash as shown in below code snippet.
I have created user defined paintImage() to read one by one from QList and QPainter and tried drawing although there is no crash but ending up with no images on screen.
int fd = open("/run/user/1000/xpra.NanPLD.mmap", O_RDONLY);
int filesize = 256*1024*1024;
void* addr = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
createImage(addr,width, height, offset, colorFormat);
void Client::createImage(void* chunk,int width, int height, int offset,
QString colorFormat)
{
qDebug() << "offset Value " << offset;
image = QImage(((const unsigned char*)chunk ) + offset, width, height, QImage::Format_RGB888);
xpraImages.append(image);
}
void Client::paintEvent(QPaintEvent *event)
{
qDebug() <<"Inside paint event" ;
QPainter painter(this);
static int counter = 0;
qDebug() << "counter: " << counter;
qDebug() << "Image list length : " << xpraImages.count();
if(counter < xpraImages.count()){
qDebug() << "counter " << counter;
image = xpraImages.at(counter); // image is class member
qWarning() << image.isNull() << image.width()<< image.height();
if(!image.isNull()){
qDebug() << "Inside paintvent after nullcheck";
painter.drawImage(0,0,image); // here crashes
}
counter++;
QTimer::singleShot(50, this, SLOT(update()));
}
}

Qt slot is not activated

So i have 2 classes, one named ConsoleInput, which contains member function check4Flood and second named AntiFloodSys, in which connect function for signal-slot system is present, and also its signal (QTimer) and slot.
AntiFloodSys object is in check4Flood member function which scope never ends as inside there is infinite while loop. Thus the object is never destroyed. So when the object anti is created, the constuctor of AntiFloodSys class is called and therefore the connection between signal and slot.
My question at which point of the code the connection timeout signal and mySlot is separated, so the slot is never fired?
ConsoleInput cpp file looks like this:
void ConsoleInput::check4Flood(int& lineCounter)
{
AntiFloodSys anti;
while(1)
{
std::string chatLine[2];
std::cin >> chatLine[0] >> chatLine[1];
anti.input(chatLine[0], chatLine[1]);
}
}
and AntiFloodSys class like this:
AntiFloodSys::AntiFloodSys(QObject *parent) : QObject(parent)
{
timeFrame = 1000 ;
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(mySlot()));
timer->start(timeFrame);
std::cout << "AntiFloodSys constructor - timer starts " << "\n";
}
AntiFloodSys::~AntiFloodSys()
{
std::cout << "AntiFloodSys Destructor" << "\n";
}
void AntiFloodSys::input(std::string nick_, std::string line_)
{
nick = nick_;
line = line_;
std::cout << "nick: " << nick << " line: " << line << " " << "\n";
}
void AntiFloodSys::mySlot()
{
std::cout << "slot" << "\n";
}
The problem is your while(1): the Qt event loop is never processed because your program is blocked in this loop.
You can force the event loop processing calling QCoreApplication::processEvents() but the std::cin is a blocking function. So, it will not completly solve your problem.
You should move your loop in a dedicated thread that will send data to the main thread (e.g. signals/slots system).
You can also use the QSocketNotifier class to create a non blocking stdin access.
A quick example:
class Widget: public QWidget
{
Q_OBJECT
public:
Widget(): QWidget(), input(new QLabel("Edit", this))
{
connect(this, &Widget::displayText, input, &QLabel::setText);
}
private:
QLabel* input;
signals:
void displayText(QString const&);
};
class UserInput: public QObject
{
Q_OBJECT
public:
UserInput(): QObject()
{}
public slots:
void run()
{
while(1) // User Input in an infinite loop
{
std::string line;
std::cin >> line;
emit inputReceived(QString::fromStdString(line));
}
}
signals:
void inputReceived(QString const&);
};
int main(int argc, char** argv) {
QApplication app(argc, argv);
Widget* w = new Widget();
UserInput* input = new UserInput();
QThread* thread = new QThread();
input->moveToThread(thread); // Move the user input in another thread
QObject::connect(thread, &QThread::started, input, &UserInput::run);
QObject::connect(input, &UserInput::inputReceived, w, &Widget::displayText);
thread->start();
w->show();
return app.exec();
}

How to maintain specific height to width ratio of widget in qt5?

I tried overriding methods hasHeightToWidth() and heightToWidth() but it didn't work for some reason.
Is there some complete example that I can use?
Upd1:
class MyWidget : public QWidget {
Q_OBJECT
public:
MyWidget() {
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
sizePolicy.setHeightForWidth(true);
setSizePolicy(sizePolicy);
}
bool hasHeightForWidth() const override {
std::cout << __FUNCTION__ << std::endl;
return true;
}
int heightForWidth(int w) const override {
std::cout << __FUNCTION__ << " " << w << std::endl;
return w;
}
QSize sizeHint() const override {
return QSize(100, heightForWidth(100));
}
};
MyWidget instances are inserted in QHBoxLayout.
I use qt5.
Debug std::cout's show that hasHeightForWidth and heightForWidth are called many times