Problem with QT save() function for QImage - qt

I have a problem which I have been trying to figure out for few days now. I am using OpenCV to get frames from a camera and then I am converting it to QImage and trying to save the frame on a disk. The problem I am facing is that if I compile/run the code in release/debug mode in visual studio then it works fine. If I run the executable created by visual studio for debug mode then it runs fine. But if I run the executable created by visual studio in release mode, the gui works well except that the QImage save() function doesn't work anymore. If I use OpenCV function for saving the frame then it works well in all modes. The QImage that I am trying to save is notNull because my comments text box displays the line I print inside but the save function returns false. I would really appreciate help.
Here is a little code:
Code for conversion between IplImage and QImage (I have also used another code, with the same result)
QImage A::IplImage2QImage(const IplImage *iplImage)
{
int height = iplImage->height;
int width = iplImage->width;
if (iplImage->depth == IPL_DEPTH_8U && iplImage->nChannels == 3) {
const uchar *qImageBuffer = (const uchar*)iplImage->imageData;
QImage img(qImageBuffer, width, height, QImage::Format_RGB888);
return img.rgbSwapped();
}
else {
// qWarning() << "Image cannot be converted.";
return QImage();
}
}
void A::getFrame()
{
commentsTextBox->setText("getFrame");
if (VI.isFrameNew(device1)) {
VI.getPixels(device1, (unsigned char *)frame->imageData, false, true);
}
QImage qimg2 = IplImage2QImage(frame);
m_Label->setPixmap(QPixmap::fromImage(qimg2));
if (!qimg2.isNull()) {
x = qimg2.save("C:\\Data\\test.jpg");
commentsTextBox->append("notEmpty33");
}
}

I had the same problem to QImage::load(). I will post a copy of the solution pointed out in the link of alexisdm, in order to make it clearer.
Under windows the qjpeg4.dll must in a imageformats directory under
the application's directory by default.
Thank you!

Try something like this,
// Prepare the url,
QUrl url("file:///home/ubuntu/Desktop/image.jpg");
// Get the path,
QString path = url.path();
// Save the image,
myQimage.save(path);

Related

QPixmap fromImage gives segment fault on image converted from cv::mat

I simplified the code and it is like this:
Mat mat = imread("xxx.jpg"); //Successfully read the image, confirmed by cvShowImage.
if (mat.empty())
{
qDebug() << "Couldn't load image";
return;
}
Mat cpy = mat.clone();
cvtColor(mat,cpy,CV_BGR2RGB);
QImage image(cpy.data, cpy.cols, cpy.rows, cpy.step, QImage::Format_RGB888);
try {
pm = QPixmap::fromImage(image); //crash line
} catch(std::exception const &ex){
qDebug()<<ex.what();
}
however the program just crashed without any debug log.. I've tried many images and the result is same. I tried to find the "stack trace" and it seems give segfault on this..
Had similar crash in QPixmap.fromImage. Found workaround by resizing image to a size aligned to 4 bytes
aligned = cv2.resize(img, (img.shape[1]//4*4, img.shape[0]//4*4), fx=0, fy=0, interpolation=cv2.INTER_NEAREST)
rgb = cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB)
qimage = QImage(rgb.data, rgb.shape[1], rgb.shape[0], QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qimage)
okay.. just change the conversion code to
QImage image(cpy.data, cpy.cols, cpy.rows, cpy.step, QImage::Format_RGB888);
image = image.rgbSwapped();
solved it. Actually I don't know why this fixed it. Maybe because I should use COLOR_BGR2RGB rather than CV_BGR2RGB..

QPixmap::load() crash - trying to avoid it

I am working on a project, using Qt 4.8.3 for an ARM platform. In my project, I use QGraphicsItems... one of which is a subclass of QGraphicsPixmapItem.
The code was tested with a 32 bit bitmap image - and it crashes.
The crash occurs not just when running on the ARM, but also in QVFB.
QPixmap p;
if (!p.load(filename)) // crashes here
return false;
I have tried to surround this with a try...catch, but it did not help.
I seem unable to step in the Qt code for this version... but the crash occurs inside QImageReader::read(QImage*).
The stack trace:
QImageReader::read(QImage*)
QImageReader::read()
QPixmapData::fromFile(QString const&*, QFlags<QT::ImageConversionFlag>)
QPixmap::load(QString const&, char const*, QFlags<QT::ImageConversionFlag>)
QPixmapItem::loadItemFromFile // mine, the code above
Any other type of image works... and the same 32 bit bitmap loads properly in windows, same Qt version. It fails to load (returning false) in the same Qt version, for Desktop.
I would be happy to exclude this type of file - but I don't know how.
Is there any way to check for the image type without loading the image and avoiding the crash ?
Is there a way to perhaps load the image header only, and verify its type ?
Since you want to exclude 32-bit BMP images, you have to read a BMP header. First two bytes are the characters "BM" and bytes 28, 29 contain bits per pixel.
Here is a small example where we read a file into QByteArray, check its format and load it to QPixmap if OK.
#include <QtCore>
#include <QtGui>
int main(int argc,char** argv)
{
QApplication app(argc,argv);
QFile file("./plot.bmp");
if (!file.open(QIODevice::ReadOnly)) return 1;
QByteArray ba=file.readAll();
if(ba[0]=='B' && ba[1]=='M' && ba[28] == 32) {
qDebug() << "Wrong format!";
return 1;
}
QPixmap pixmap;
pixmap.loadFromData(ba);
qDebug()<<"OK!";
return 0;
}
Or if you don't want to read everything into memory, you can open a file using QFile, ifstream etc, check these bytes and then close it.

Qt - Cannot export QImage to 16bit bmp

I have created a simple application and I need export from pixmap to the 16-bit bmp image. I have several pixmap items so I have the for loop like this where I first create QImage and convert it to Format_RGB16:
for(QList<image_handler * >::iterator it=imageItems->begin(); it!=imageItems->end(); it++)
{
...
// image_handler inherits QPixmap
QFile export_image(path+"/img_"+code+".bmp");
QImage export_img = (*it)->toImage().convertToFormat(QImage::Format_RGB16);
export_img.save(&export_image, "BMP");
...
}
where image_handler is my custom QPixmap. Images are exported at path given, with correct filename. However when I look at properties of file (in windows) I can see that image depth is 24-bit. Unfortunately I need them to be 16-bit.
What I am doing wrong here? Or is this a bug in Qt? Then how can I export 16-bit bmps from pixmap?
Turns out, that Qt forcibly converts image, before saving it to bmp.
qt-src/src/gui/image/qbmphandler.cpp:777:
bool QBmpHandler::write(const QImage &img)
{
QImage image;
switch (img.format()) {
case QImage::Format_ARGB8565_Premultiplied:
case QImage::Format_ARGB8555_Premultiplied:
case QImage::Format_ARGB6666_Premultiplied:
case QImage::Format_ARGB4444_Premultiplied:
image = img.convertToFormat(QImage::Format_ARGB32);
break;
case QImage::Format_RGB16:
case QImage::Format_RGB888:
case QImage::Format_RGB666:
case QImage::Format_RGB555:
case QImage::Format_RGB444:
image = img.convertToFormat(QImage::Format_RGB32);
break;
default:
image = img;
}
...
So, if you need to save bmp 16bit, you'll have to do it manually, filling header and using QImage::bits() and QImage::byteCount().

Error passing pointer in QT

In QT have the following code that starts a thread to send out commands. The thread takes a char * and int as arguments. In the "run" I use the pointer that is given by the constuctor. The code is:
MyThread::MyThread(char * payld, int payld_size)
{
payload_size = payld_size;
payload_p = payld;
}
void MyThread::run()
{
while(...)
{
sendCommand(payload_p, payload_size);
}
}
Unfortunately this doesn´t work and my application crashes when I try to use thread.start(). But when I change it to:
MyThread::MyThread(char * payld, int payld_size)
{
payload_size = payld_size;
payload_p = payld;
for(int i=0; i<payload_size; i++)
{
payload[i] = payld[i];
}
}
void MyThread::run()
{
while(...)
{
sendCommand(payload, payload_size);
}
}
The code does run and only crashes sometimes (looks pretty random to me). Can anybody Explain me why version one doesnt work and version two does? And any ideas on why the second code sometimes crashes? Could it be because the size of payload is not predefined (in the header file I defined it as
char payload[];
When I define it as:
char payload[10];
it seems to work better, but it is annoying to test since the crashes are pretty random.
instead of fiddling with char*, I would switch to QString (since you're using Qt). It takes a bit of learning, but it's almost mandatory to get code working smoothly in this framework. Then declare
QString payload;
and depending on sendCommand implementation, use one of the member functions QString to get the char*, like payload.toLatin1()

cv::Mat to QImage conversion

I've found very similiar topic: how to convert an opencv cv::Mat to qimage , but it does not solve my problem.
I have function converting cv::Mat to QImage
QImage cvMatToQImg(cv::Mat& mat)
{
cv::Mat rgb;
if(mat.channels()==1)
{
cv::cvtColor(mat,rgb,CV_GRAY2BGR);
cv::cvtColor(rgb,rgb,CV_BGR2BGRA);
QImage temp = QImage((unsigned char*)(rgb.data), rgb.cols,
rgb.rows,QImage::Format_ARGB32 );
QImage returnImage = temp.copy();
return returnImage;
}
And it's works for my but I want to make it more efficient.
First: why changing 2 cvtColor functions with:
cv::cvtColor(mat,rgb,CV_GRAY2BGRA)
fails on
QImage returnImage = temp.copy()
with segfault.
Then how to eliminate copying of QImage. When I simply return temp image, I'm getting segfault.
Any other optimalizations can be done there? It's very often used function so I want to make it as fast as possible.
Your solution to the problem is not efficient, in particular it is less efficient then the code I posted on the other question you link to.
Your problem is that you have to convert from grayscale to color, or RGBA. As soon as you need this conversation, naturally a copy of the data is needed.
My solution does the conversion between grayscale and color, as well as between cv::Mat and QImage at the same time. That's why it is the most efficient you can get.
In your solution, you first try to convert and then want to build QImage around OpenCV data directly to spare a second copy. But, the data you point to is temporary. As soon as you leave the function, the cv::Mat free's its associated memory and that's why it is not valid anymore also within the QImage. You could manually increase the reference counter of the cv::Mat beforehand, but that opens the door for a memory leak afterwards.
In the end, you attempt a dirty solution to a problem better solved in a clean fashion.
It may be easiest to roll your own solution. Below is the current OpenCV implementation for going from gray to RGBA format:
template<typename _Tp>
struct Gray2RGB
{
typedef _Tp channel_type;
Gray2RGB(int _dstcn) : dstcn(_dstcn) {}
void operator()(const _Tp* src, _Tp* dst, int n) const
{
if( dstcn == 3 )
for( int i = 0; i < n; i++, dst += 3 )
{
dst[0] = dst[1] = dst[2] = src[i];
}
else
{
_Tp alpha = ColorChannel<_Tp>::max();
for( int i = 0; i < n; i++, dst += 4 )
{
dst[0] = dst[1] = dst[2] = src[i];
dst[3] = alpha;
}
}
}
int dstcn;
};
Here is where the actual cvtColor call occurs:
case CV_GRAY2BGR: case CV_GRAY2BGRA:
if( dcn <= 0 ) dcn = 3;
CV_Assert( scn == 1 && (dcn == 3 || dcn == 4));
_dst.create(sz, CV_MAKETYPE(depth, dcn));
dst = _dst.getMat();
if( depth == CV_8U )
CvtColorLoop(src, dst, Gray2RGB<uchar>(dcn));
This code is contained in the color.cpp file in the imgproc library.
As you can see, since you are not setting the dstCn parameter in your cvtColor calls, it defaults to dcn = 3. To go straight from gray to BGRA, set dstCn to 4. Since OpenCV's default color order is BGR, you'll still need to swap the color channels for it to look right (assuming you get your image data from an OpenCV function). So, it may be worth it to implement your own converter possibly following the above example, or using ypnos answer here.
Also, have a look at my other answer involving how to integrate OpenCV with Qt.
The problem is that both the cv::Mat and QImage data isn't necessarily contiguous.
New data rows in opencv start on a 32bit boundary (not sure about QImage - I think it's system dependant) so you can't copy a memeory block unless your rows happen to be exact multiples of 4bytes
See How to output this 24 bit image in Qt

Resources