QLabel doesn't display QPixmap - qt

I am trying to display the video frames in a QLabel with the below code but unfortunately, the Video is not displayed on the QLabel. I have inherited QAbstractVideoSurface into CameraFrameGrabber.
bool CameraFrameGrabber::present(const QVideoFrame &frame)
{
qDebug() << __FUNCTION__;
if (frame.isValid()) {
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
const QImage image(cloneFrame.bits(),
cloneFrame.width(),
cloneFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
if (MainWindow* child = dynamic_cast<MainWindow*>(this)) {
//QGraphicsScene *scene = new QGraphicsScene(this);
//scene->addPixmap(QPixmap::fromImage(image));
//scene->setSceneRect(image.rect());
child->ui->label->setPixmap(QPixmap::fromImage(image));
child->ui->label->update();
//child->ui->graphicsView->setScene(scene);
//child->ui->graphicsView->update();
}
//emit frameAvailable(image);
cloneFrame.unmap();
return true;
}
return false;
}

The Problem is with the VideoFrame format which doesn't match with the QLabel pixmap Formats and also removed dynamic Cast from mainwindow and added a Label in Cameraframegrabber.
QImage outImage = image.convertToFormat(QImage::Format_RGB888);
myLabel->setPixmap(QPixmap::fromImage(outImage));

Related

How to Zoom / Fit Image to GraphicsView

I read a lot of posts/threads but I can't get it to work.
I'd like to fit every Image to a GraphicsView regardless if it is smaller or bigger then the view.
What's wrong?
void frmMain::on_btLoadImage_clicked()
{
QGraphicsScene *scene;
QPixmap image;
QString imgPath = "O:/IMG_0001.JPG";
QRectF sceneRect = ui->imgMain->sceneRect();
image.load(imgPath);
image.scaled (sceneRect.width (),sceneRect.height (), Qt::KeepAspectRatio, Qt::SmoothTransformation);
scene = new QGraphicsScene(this);
scene->addPixmap(image);
scene->setSceneRect(sceneRect); //image.rect());
//ui->imgMain->fitInView (scene->itemsBoundingRect(), Qt::KeepAspectRatio); //ui->imgMain->width (), ui->imgMain->height ());
ui->imgMain->setScene(scene);
}
Here is a basic custom QGraphicsView implementation which displays one image and keeps it sized/scaled to fit the available viewport space. Note that the image needs to be rescaled every time the viewport size changes, which is why it is simplest to reimplement the QGraphicsView itself and change the scaling in resizeEvent(). Although it could be done inside a custom QGraphicsScene instead. (Or, really, a number of other ways depending on the exact needs.)
The same technique could be used to keep a QGraphicsWidget as the root item in the scene to always take up the full space. Then a layout could be used in the widget to keep children aligned/resized/positioned/etc.
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
class GrpahicsImageView : public QGraphicsView
{
Q_OBJECT
public:
using QGraphicsView::QGraphicsView;
public slots:
void setImage(const QString &imageFile)
{
if (m_imageFile != imageFile) {
m_imageFile = imageFile;
loadImage(viewport()->contentsRect().size());
}
}
void setImageScaleMode(int mode)
{
if (m_scaleMode != Qt::AspectRatioMode(mode)) {
m_scaleMode = Qt::AspectRatioMode(mode);
if (m_item)
loadImage(viewport()->contentsRect().size());
}
}
void loadImage(const QSize &size)
{
if (!scene())
return;
if (m_imageFile.isEmpty()) {
// remove existing image, if any
removeItem();
return;
}
// Load image at original size
QPixmap pm(m_imageFile);
if (pm.isNull()) {
// file not found/other error
removeItem();
return;
}
// Resize the image here.
pm = pm.scaled(size, m_scaleMode, Qt::SmoothTransformation);
if (createItem())
m_item->setPixmap(pm);
}
protected:
void resizeEvent(QResizeEvent *e) override
{
QGraphicsView::resizeEvent(e);
if (!scene())
return;
// Set scene size to fill the available viewport size;
const QRect sceneRect(viewport()->contentsRect());
scene()->setSceneRect(sceneRect);
// Keep the root item sized to fill the viewport and scene;
if (m_item)
loadImage(sceneRect.size());
}
private:
bool createItem() {
if (m_item)
return true;
if (!m_item && scene()) {
m_item = new QGraphicsPixmapItem();
scene()->addItem(m_item);
return true;
}
return false;
}
void removeItem()
{
if (m_item) {
if (scene())
scene()->removeItem(m_item);
delete m_item;
m_item = nullptr;
}
}
Qt::AspectRatioMode m_scaleMode = Qt::KeepAspectRatio;
QString m_imageFile;
QGraphicsPixmapItem *m_item = nullptr;
};
Usage example:
#include <QApplication>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QDialog d;
d.setLayout(new QVBoxLayout);
d.resize(350, 350);
GrpahicsImageView *view = new GrpahicsImageView(new QGraphicsScene, &d);
QComboBox *imgCb = new QComboBox(&d);
imgCb->addItems({
"./so-logo.png",
"./se-logo.png",
"./su-logo.png"
});
QComboBox *scaleCb = new QComboBox(&d);
scaleCb->addItems({
"IgnoreAspectRatio",
"KeepAspectRatio",
"KeepAspectRatioByExpanding"
});
QHBoxLayout *cbLayout = new QHBoxLayout;
cbLayout->setSpacing(9);
cbLayout->addWidget(imgCb);
cbLayout->addWidget(scaleCb);
d.layout()->addItem(cbLayout);
d.layout()->addWidget(view);
QObject::connect(imgCb, QOverload<const QString &>::of(&QComboBox::currentIndexChanged), view, &GrpahicsImageView::setImage);
QObject::connect(scaleCb, QOverload<int>::of(&QComboBox::currentIndexChanged), view, &GrpahicsImageView::setImageScaleMode);
view->setImageScaleMode(scaleCb->currentIndex());
view->setImage(imgCb->currentText());
return d.exec();
}
https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png
https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/se-logo.png
https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/su-logo.png

Dropping images in Qt

I am looking to be able to take a QImage and drop it into an editing program such as Paint, for example. I've tried setting the mime data to the current image with the following code, but the data doesn't move correctly. The pixmap is displayed correctly, but the drop never happens. If anyone has any advice, that would be much appreciated! Thanks!
void LCDWidget::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
QImage image = renderFramebuffer(lcdState);
QPixmap mymap = QPixmap::fromImage(image);
mimeData->setImageData(image);
drag->setMimeData(mimeData);
drag->setHotSpot(e->pos());
drag->setPixmap(mymap);
drag->exec(Qt::CopyAction | Qt::MoveAction);
e->accept();
} else {
e->ignore();
}
}
I eventually figured this out with the following code. The solution is to copy the image to a local file, and then use the path to the file as the copy data.
void LCDWidget::mousePressEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) {
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
QImage image = getImage();
QPixmap mymap = QPixmap::fromImage(image);
QString path = QDir::tempPath() + randomString(5) + QStringLiteral(".png");
image.save(path, "PNG", 0);
mimeData->setImageData(image);
mimeData->setUrls(QList<QUrl>() << QUrl::fromLocalFile(path));
drag->setMimeData(mimeData);
drag->setHotSpot(e->pos());
drag->setPixmap(mymap);
drag->exec(Qt::CopyAction | Qt::MoveAction);
e->accept();
} else {
e->ignore();
}
}

How to QVideoFrame to QImage

The task is to copy a frame from a QVideoFrame, and possibly do something to that Image and displaying the manipulated Image in QML.
...
m_lastFrame = QImage(videoFrame.width(), videoFrame.height(), QImage::Format_ARGB32);
memcpy(m_lastFrame.bits(), videoFrame.bits(),videoFrame.mappedBytes());
...
The above code causes a crash, since m_lastFrame is short of 32 bytes(3686400 vs 3686432)
videoFrame.mappedBytes() reports 3686432 bytes. What am I doing wrong here? Or how should I calculate the size of m_lastFrame().
The code is running on Mac OSx 10.9.5 Qt 5.1.1.
Some additional code:
...
if( videoFrame.map(QAbstractVideoBuffer::ReadOnly) ){
m_lastFrame = QImage(videoFrame.width(),videoFrame.height(),QImage::Format_ARGB32);
memcpy(m_lastFrame.bits(), videoFrame.bits(),videoFrame.mappedBytes() - 32);
...
}
...
Since that doesn't always work, see also comment at convert QVideoFrame to QImage , i.e.
QImage Camera::imageFromVideoFrame(const QVideoFrame& buffer) const
{
QImage img;
QVideoFrame frame(buffer); // make a copy we can call map (non-const) on
frame.map(QAbstractVideoBuffer::ReadOnly);
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(
frame.pixelFormat());
// BUT the frame.pixelFormat() is QVideoFrame::Format_Jpeg, and this is
// mapped to QImage::Format_Invalid by
// QVideoFrame::imageFormatFromPixelFormat
if (imageFormat != QImage::Format_Invalid) {
img = QImage(frame.bits(),
frame.width(),
frame.height(),
// frame.bytesPerLine(),
imageFormat);
} else {
// e.g. JPEG
int nbytes = frame.mappedBytes();
img = QImage::fromData(frame.bits(), nbytes);
}
frame.unmap();
return img;
}
You can try creating a QImage by first mapping the QVideoFrame onto a QAbstractVideoBuffer in the following way :
bool CameraFrameGrabber::present(const QVideoFrame &frame)
{
Q_UNUSED(frame);
if (frame.isValid()) {
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
const QImage image(cloneFrame.bits(),
cloneFrame.width(),
cloneFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(cloneFrame .pixelFormat()));
emit frameAvailable(image);
qDebug()<<cloneFrame.mappedBytes();
cloneFrame.unmap();
return true;
}
If you want QImage in any other format just change the last parameter during creating the image, to the whichever format you like :
QImage::Format_xxx ;
instead of
QVideoFrame::imageFormatFromPixelFormat(cloneFrame .pixelFormat()));

How to save an image in a QGraphicsView into a bmp/jpg

I am a newbie for Qt.
The question is that: After turning an image into the QGraphicsView, I use a qrubberband to select the cropping area of the image. It is successful to select the croppping region at the moment.But I don't know how to save that cropping region into a jpg/bmp afterwards. Note that I made an ui component for the GraphicsView called CGraphicsView.
void CGraphicsView::mousePressEvent
( QMouseEvent* event)
{
mypoint = event->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);//new rectangle band
rubberBand->setGeometry(QRect(mypoint, QSize()));
rubberBand->show();
}
void CGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
if (rubberBand)
{
rubberBand->setGeometry(QRect(mypoint, event->pos()).normalized());//Area Bounding
}
}
void CGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
if (rubberBand)
{
QRect myRect(mypoint, event->pos());
rubberBand->hide();// hide on mouse Release
QImage copyImage; //<= this Qimage hold nothing
copyImage = copyImage.copy(myRect);
}
}
There is special method in Qt. It allows get screenshot of view.
QString fileName = "path";
QPixmap pixMap = QPixmap::grabWidget(graphicsView, rectRegion);
pixMap.save(fileName);
Save() method can save picture in different formats and with compression.
Also with grabWidget() method you can grab another widgets too. Moreover, this method take QRect as argument so you can create screenshot of region what you need.
You can save a part of your scene to an image like :
QPixmap pixmap=QPixmap(myRect.size());
QString filename = QFileDialog::getSaveFileName( this->parentWidget(), tr("Save As"), tr("image.png"));
if( !filename.isEmpty() )
{
QPainter painter( &pixmap );
painter.setRenderHint(QPainter::Antialiasing);
scene->render( &painter, pixmap.rect(),myRect, Qt::KeepAspectRatio );
painter.end();
pixmap.save(filename,"PNG");
}

Video not playing continuously

I am playing video in Qt using opencv. I am having 6 tiled view cameras from which I am playing video. The problem is if one of the videos is not playing i.e finishes then the GUI freezes and exits. The error I get is you must reimplement QApplication::notify() and catch the exceptions there. How to do this?
The code I am using is as follows.
Somewhere in a function
void MainWindow::ActivateWindow()
{
//Some part of code to set Index for stacked widget
if(stackWidget->currentIndex()==9)
{
const int imagePeriod == 1000/25;
imageTimer->setInterval(imagePeriod);
connect(imageTimer,SIGNAL(timeout()),this,SLOT(demoSlot());
imageTimer->start();
}
}
In slot demoSlot
void MainWindow::demoSlot()
{
captureCamera1 cvCaptureFromFile("/root/mp.mp4");
captureCamera2 cvCaptureFromFile("/root/mp.mp4");
captureCamera3 cvCaptureFromFile("/root/mp.mp4");
while(imageTimer->isActive())
{
frameCamera1 = cvQueryFrame(captureCamera1);
frameCamera2 = cvQueryFrame(captureCamera2);
frameCamera3 = cvQueryFrame(captureCamera2);
sourceImageCam1 = frameCamera1;
sourceImageCam2 = frameCamera2;
sourceImageCam3 = frameCamera3;
cv::resize(sourceImageCam1,sourceImageCam1,cv::size(400,100),0,0);
cv::resize(sourceImageCam1,sourceImageCam1,cv::size(400,100),0,0);
cv::resize(sourceImageCam1,sourceImageCam1,cv::size(400,100),0,0);
cv::cvtColor(sourceImageCam1,sourceImageCam2,CV_BGR2RGB);
cv::cvtColor(sourceImageCam2,sourceImageCam2,CV_BGR2RGB);
cv::cvtColor(sourceImageCam2,sourceImageCam2,CV_BGR2RGB);
QImage tempImage1 = QImage((const unsigned char* sourceImageCam1.data,sourceImageCam1.cols,sourceImageCam2.rows,QImage::Format_RG888);
QImage tempImage2 = QImage((const unsigned char* sourceImageCam2.data,sourceImageCam2.cols,sourceImageCam2.rows,QImage::Format_RG888);
QImage tempImage3 = QImage((const unsigned char* sourceImageCam3.data,sourceImageCam3.cols,sourceImageCam3.rows,QImage::Format_RG888);
labelCameraCapture1->setPixmap(QPixmap::fromImage(tempImage1)); //label to display video
labelCameraCapture2->setPixmap(QPixmap::fromImage(tempImage2));
labelCameraCapture3->setPixmap(QPixmap::fromImage(tempImage3));
lblCameraCapture1->resize(lblCameraCapture1->Pixmap->size());
lblCameraCapture1->resize(lblCameraCapture1->Pixmap->size());
lblCameraCapture1->resize(lblCameraCapture1->Pixmap->size());
cvWaitkey(20);
qApp->processEvents();
}
if(imageTimer->isActive())
{
imageTimer->stop();
}
else
{
imageTimer->start();
}
}
In header file
cvCapture *captureCamera1;
cvCapture *captureCamera1;
cvCapture *captureCamera1;
IplImage frameCamera1;
IplImage frameCamera2;
IplImage frameCamera3;
cv::Mat sourceImageCam1;
cv::Mat sourceImageCam2;
cv::Mat sourceImageCam3;
This will do the trick changing that to 3 movies is simple.
class MainWindow : public QMainWindow {
Q_OBJECT
explicit QMainWindow(QWidget *parent) ....
// prepare timer and so on
public slots:
void startVideo() {
vid1.close();
vid1.open("/root/mp.mp4");
imageTimer->start();
}
void demoSlot() {
cv::Mat frame;
vid1 >> frame;
cv::cvtColor(frame,frame,CV_BGR2RGB);
QImage img((uchar*) frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
label1->setPixmap(QPixmap::fromImage(img));
}
private:
...
QTimer *imageTimer;
cv::VideoCapture vid1;
};
Check if frame captured from camera is NULL. Then simply skip processing steps for this camera.
And it'll be better to not mix C++ and C interfaces (I mean cv::Mat and IplImage).

Resources