How to store images on QTableView (Qt and openCV) - c++

I have a GUI composed of a QTableView with 18 columns and a QGraphicsView where I upload images using a button. I am able to draw a box on a region of interests (ROI) and as soon as I do that with a mouse right click I open up a small dialog. This dialog is composed of a TabWidget with two pages. The first page has a small QGraphicsView that carries the cropped image (say Image A) captured with the previous box drawn. The second page of the TabWidget has also another QGraphicsView that I use to upload a previously saved .jpg image (say Image B) that is on my Desktop. As soon as I hit ok the information on this dialog will be transferred to the first row of the QTableView. Image A will be stoted in one column (column 17 to be precise) and Image B in an additional column too (column 18 to be precise).
I was able to store the cropped Image A (which was the most difficult part because I had to work on understanding the conversion format between Qt and openCV and vice versa) in the QTableView but not Image B (which I handle as simple image that I saved on my Desktop).
I tried different options: option 1: I created a function with which I handle the image that I upload on the second page of the TabWidget (Image B), option 2: I tried to debug to narrow the problem and it seems that the compiler is not seeing the image I am trying to store.
I am attaching the most important part of the code that are carrying this bug:
In clipscene.h is how I declare the most important variables and functions I use:
class clipScene : public QDialog
{
Q_OBJECT
public:
explicit clipScene(QWidget *parent = 0);
~clipScene();
void setImage(QImage img);
void setClassifiedImage(QImage img);
SelectionData getData();
void setData(SelectionData newdata);
private:
SelectionData returnData;
Ui::clipScene *ui;
QImage simg;
QImage featureClassified;
};
In clipscene.cpp this is the function setClassifiedImage I use to handle Image B
void clipScene::setClassifiedImage(QImage img)
{
featureClassified = img;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(featureClassified));
workingImageScene->addItem(item);
}
In clipscene.cpp using setImage I successfully handle the Qt and openCV conversion of the cropped image
void clipScene::setImage(QImage img)
{
simg = img;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(simg));
scene->addItem(item);
ui->graphicsViewClipped->show();
cv::Mat input = cv::Mat(simg.height(), simg.width(), CV_16UC3, simg.bits(), simg.bytesPerLine());
// .....operations...
// .....operations...
}
In clipscene.cpp the following img1 represent the cropped Image A and the img2 represent Image B (that is currently nor stored neither passsed)
void clipScene::on_acceptBtn_clicked()
{
// .....operations...
// This will save the cropped Image A successfully
QPixmap img1;
img1.convertFromImage(simg);
QByteArray img1Array;
QBuffer buffer1(&img1Array);
buffer1.open(QIODevice::WriteOnly);
img1.save(&buffer1, "PNG");
returnData.mSave = img1Array;
// This will save a different Image B
// But here the compiler says that no arguments are being passed
QPixmap img2;
QByteArray img2Array;
QBuffer buffer2(&img2Array);
buffer2.open(QIODevice::WriteOnly);
img2.save(&buffer2, "PNG");
returnData.mClassImg = img2Array;
}
Finally when all information are passed and stored on the QTableView I am able to DoubleClick on one row of the QTableView and the same dialog I used before for manually storing the data will pop up with all information recorded and both images (Image A and Image B).
The part of the code that does this on MainWindow (and where the bug is connected) is below:
void MainWindow::onTableClick(const QModelIndex &index)
{
int row = index.row();
SelectionData currentData;
currentData.mName = index.sibling(row, 1).data().toString();
// ....additional data....
// ....additional data...
currentData.mSave = index.sibling(row, 17).data().toByteArray();
currentData.mClassImg = index.sibling(row, 18).data().toByteArray();
QPixmap iconPix;
if(!iconPix.loadFromData(index.sibling(row, 17).data().toByteArray())) {
}
QPixmap iconPix2;
if(!iconPix2.loadFromData(index.sibling(row, 18).data().toByteArray())) {
}
clipScene d(this);
d.setData(currentData);
d.setImage(iconPix.toImage());
d.setClassifiedImage(iconPix2.toImage());
// ....additional operations....
}
I have been struggling with this problem for some days now and anyone who can shed some light on this or provide a solution on how to solve this would be great.

I am not sure if this is still useful to you but since I had the same problem this might be helpful. I think you are never passing the image to the destination table. Are you ever loading the image? Are you ever doing something like this:
clipscene.h
private:
QList<QGraphicsPixmapItem*> leftClipPix;
clipscene.cpp
void clipScene::load_classifiedImageFromDb(QImage image)
{
clasImg = image;
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
leftClipPix.append(item);
workingImageScene->addItem(item);
ui->yourgraphicsView->show();
ui->yourgraphicsView->setSceneRect(QRectF(0, 0, image.width(), image.height()));
}
Also if, as you said, you are trying to upload an image from your Desktop or database, than it may be useful to do something like:
void clipScene::load_classifiedImage()
{
QString dir = QFileDialog::getOpenFileName(this, tr("Open image directory"), "", tr("Images (*.tif *.jpg *.png *.jpeg *.bmp *.gif *.tiff)"));
QImage image;
if(QString::compare(dir, QString()) != 0) {
image = QImage(dir);
QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
leftClipPix.append(item);
workingImageScene->addItem(item);
}
ui->yourgraphicsView->show();
ui->yourgraphicsView->setSceneRect(QRectF(0, 0, image.width(), image.height()));
clasImg = image;
}
and pass load_classifiedImage() function to a QPushButton

Related

Can you play SEMI transparent WEBM videos in QT?

I am using QT to build a custom component for QML. I want this component to be able to play a WEBM video that contains an alpha channel. However, all of my attempts have resulted in the transparent pixels of the video getting replaced with black ones. This is my code currently:
MyClass::MyClass()
{
m_pixmap = new QPixmap(1920, 1080); // Create a canvas to draw on:
// Create something that can be drawn:
m_painter = new QPainter(m_pixmap);
m_rect = new QRect(0, 0, 1920, 1080);
// Create an area to present on:
m_label = new QLabel();
m_label->setPixmap(*m_pixmap);
m_label->show();
// Play video:
m_videoSink = new QVideoSink();
m_mediaPlayer = new QMediaPlayer();
m_mediaPlayer->setVideoSink(m_videoSink);
m_mediaPlayer->setSource(QUrl::fromLocalFile("path..."));
m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
m_mediaPlayer->play();
// Add a an event for when the video frame changes:
connect(m_videoSink, SIGNAL(videoFrameChanged(QVideoFrame)), this, SLOT(SetFrame(QVideoFrame)));
qDebug() << "Constructed";
}
void MyClass::SetFrame(QVideoFrame frame)
{
frame.paint(m_painter, *m_rect, m_options); //Stores the frame in the m_pixmap
m_label->setPixmap(*m_pixmap);
}
In this example I attempt to use a QMediaPlayer to play the video, then a QVideoSink to extract the currently playing QVideoFrame and paint that to a QPixmap that is finally being displayed in a QLabel.
I have also tried to have the QMediaPlayer directly hooked up to a QVideoWidget.
I know that my WEBM video works as it is displaying as expected when imported in to other programs.

How to postion a pixmap in QT Designer

void Editor::DisplayImage(QString(pathOfImage))
{
// QPixmap pix (pathOfImage);
// ui->lbl_DisplayImage->setPixmap(pix);
// ui->lbl_DisplayImage->lower();
// annotationPixmap = QPixmap(pix.size());
// annotationPixmap.fill(Qt::transparent);
ui->lbl_DisplayImage->setPixmap(annotationPixmap);
annotationPixmap = QPixmap(pathOfImage);
clickMouse = false;
}
I am trying to move this pixmap and change the size of the pixmap image. I have tried using labels but they prevent me from drawing over the images and the drawings are always placed behind the image.
Is there a way to manipulate the pixmap?

Saving images and then display them in a QLabel

I'm currently building a Qt application that using some camera.
In this application the uses capture images, and then they are automatically saved in a specific folder. Everything works great.
Now, when the "library" button is clicked, I want to read all the images (JPEG files) and display all the images that were taken one by one in a QLabel.
I couldn't find any tutorials for this, only found tutorials and uses the argv argument which is no good for me, because in my application the user may capture images and then display them in the same run.
How can read the files list and display it?
Thank you very much :)
If you have a single QLabel then you have to join the images together a single one. I find easier to display a list of QLabels:
auto layout = new QVBoxLayout();
Q_FOREACH (auto imageName, listOfImages) {
QPixmap pixmap(dirPath + "/" + imageName);
if (!pixmap.isNull()) {
auto label = new QLabel();
label->setPixmap(pixmap);
layout->addWidget(label);
}
}
a_wdiget_where_to_show_images->setLayout(layout);
The last line will depend on when do you want to place the labels. I suggest some widget with a scroll bar.
Now, you want to read all the images from a directory (the listOfImages variable above). If you don't have it:
const auto listOfImages = QDir(dirPath).entryList(QStringList("*.jpg"), QDir::Files);
You may have layout problems if your images are too big. In that case you should scale them if they are bigger than a given size. Take a look at QPixmap::scaled or QPixmap::scaledToWidth. Also, if image quality is important, specify Qt::SmoothTransformation as the transformation mode.
You can use opencv library to read all images in a directory.
vector<String> filenames; // notice here that we are using the Opencv's embedded "String" class
String folder = "Deri-45x45/"; // again we are using the Opencv's embedded "String" class
float sayi = 0;
glob(folder, filenames); // new function that does the job ;-)
float toplam = 0;
for (size_t i = 0; i < filenames.size(); ++i)
{
Mat img = imread(filenames[i],0);
//Display img in QLabel
QImage imgIn = putImage(img);
imgIn = imgIn.scaled(ui->label_15->width(), ui->label_15->height(),Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
ui->label_15->setPixmap(QPixmap::fromImage(imgIn));
}
In order to convert Mat type to QImage, we use putImage function:
QImage putImage(const Mat& mat)
{
// 8-bits unsigned, NO. OF CHANNELS=1
if (mat.type() == CV_8UC1)
{
// Set the color table (used to translate colour indexes to qRgb values)
QVector<QRgb> colorTable;
for (int i = 0; i < 256; i++)
colorTable.push_back(qRgb(i, i, i));
// Copy input Mat
const uchar *qImageBuffer = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_Indexed8);
img.setColorTable(colorTable);
return img;
}
// 8-bits unsigned, NO. OF CHANNELS=3
if (mat.type() == CV_8UC3)
{
// Copy input Mat
const uchar *qImageBuffer = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage img(qImageBuffer, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
return img.rgbSwapped();
}
else
{
qDebug() << "ERROR: Mat could not be converted to QImage.";
return QImage();
}
}

Passing cv::Mat img between forms

I'm having trouble passing cv::mat data between forms on a qt gui application, for the moment I simply want to pass the Image chosen by the user on the main window and display it on the results page
void MainWindow::on_pushButton_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this,
tr("Open Image"), ".", tr("Image Files (*.png *.jpg *.bmp)"));
Lateral= cv::imread(fileName.toAscii().data());
}
In the header file of the main window I definded:-
public:
cv::Mat get_Lateral(cv::Mat img);
cv::Mat get_Posteroanterior();
In the MainWindow.cpp file I have defined the folowing (i've tried a few variations of the method):-
cv::Mat MainWindow::get_Lateral(cv::Mat img ){
Lateral.copyTo(img);
return img;
}
cv::Mat MainWindow::get_Posteroanterior(){
return Posteroanterior;
}
Finally on the new form I have something like this:-
MainWindow Mw ;
cv::Mat op;
Mw.get_Lateral(op);
//(Mw.get_Posteroanterior()).copyTo(op);
cv::namedWindow("Lateral Image");
cv::imshow("Lateral Image",op);
When I run this I get a runtime error, so I added an if statment to check the contents of cv::mat op like this:-
MainWindow Mw ;
cv::Mat op;
Mw.get_Lateral(op);
//(Mw.get_Posteroanterior()).copyTo(op);
if (!op.data)
cv::namedWindow("dud Image");
else{
cv::namedWindow("Lateral Image");
cv::imshow("Lateral Image",op);
}
And I am given the dud image window implying that op is empty.
Any advice on how to do this process properly will be apppreciated, I am fairly new to opencv and c++ so I apologize for any blatant mistakes.
Cheers
Have a look at my answer involving integrating OpenCV with larger applications. I wouldn't recommend using the highgui imshow function inside of a Qt GUI application. It has done some weird things for me in the past.
Basically, you can convert the cv::Mat to a QImage and then either use QGLWidget, or just simply draw it onto a QPixmap if you don't particularly need high speed.
As for passing a Mat object between forms, you can either convert it to a QImage and then use signals/slots like my example shows, or if you need to manipulate the Mat object further, you can create a QMetaType. The QMetaType will allow you transmit it across forms just like you would any other native Qt object. Here is a Qt example to get you started.
Hope that is helpful!
The return value of get_Lateral is being discarded - you either need to modify it in place via a reference:
void MainWindow::get_Lateral(cv::Mat& img ){
Lateral.copyTo(img);
//return img;
}
or assign back to your local cv::Mat object in main:
MainWindow Mw ;
cv::Mat op;
op = Mw.get_Lateral(op);
//(Mw.get_Posteroanterior()).copyTo(op);
if (!op.data)
cv::namedWindow("dud Image");
else{
cv::namedWindow("Lateral Image");
cv::imshow("Lateral Image",op);
}

How to create image file from QGraphicsScene/QGraphicsView?

Given a QGraphicsScene, or QGraphicsView, is it possible to create an image file (preferably PNG or JPG)? If yes, how?
After just dealing with this problem, there's enough improvement here to warrant a new answer:
scene->clearSelection(); // Selections would also render to the file
scene->setSceneRect(scene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
QImage image(scene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
image.fill(Qt::transparent); // Start all pixels transparent
QPainter painter(&image);
scene->render(&painter);
image.save("file_name.png");
I have not tried this, but this is the idea of how to do it.
You can do this in several ways
One form is as follows:
QGraphicsView* view = new QGraphicsView(scene,this);
QString fileName = "file_name.png";
QPixmap pixMap = view->grab(view->sceneRect().toRect());
pixMap.save(fileName);
//Uses QWidget::grab function to create a pixmap and paints the QGraphicsView inside it.
The other is to use the render function QGraphicsScene::render():
QImage image(fn);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
image.save("file_name.png")
grabWidget is deprecated, use grab. And you can use a QFileDialog
QString fileName= QFileDialog::getSaveFileName(this, "Save image", QCoreApplication::applicationDirPath(), "BMP Files (*.bmp);;JPEG (*.JPEG);;PNG (*.png)" );
if (!fileName.isNull())
{
QPixmap pixMap = this->ui->graphicsView->grab();
pixMap.save(fileName);
}