[solved]How to increase speed of MPO file converter
-
Hello,
I wrote a MPO file convert which returns a QPixmap pointer on a array of two QPixmap. For the ones who dont know MPO files: the format is developed for multi-image files like 3D images created with the Fuji Finepix Real 3D camera. In that case the files include two images (left and right eye). With 3D projectors or displays it is possible to view 3D images.
My problem is only the speed of the converter. The code above shows the function. The first part opens the file as a QDataStream, stores it in a QVector and searches for the beginning of the second image in the binary data. Normally the images have the same size. Therefore i start at 49% of the file.
The second part creates two QPixmaps from given data and returns the pointer on the two QPixmaps. The whole code works fine but it is slow!@QPixmap *MPO_Converter::pictureConvert(QString source)
{
// first part
int count = 0;
int laufIndex, fileLength;
int secondImageStart = 0;
// open MPO file as datastream
QFile imageFile(source);
// open if exist
if (imageFile.exists())
{
// open file in read only mode
imageFile.open(QIODevice::ReadOnly);
QDataStream dataInfilestream(&imageFile);
// get file length
fileLength = imageFile.size();
// define data vector
QVector<uchar> imageData(fileLength);
// read data from stream
while(!dataInfilestream.atEnd())
{
// copy file bytewise
dataInfilestream >> imageData[count++];
}
// close file
imageFile.close();
// search for beginning of second image (normaly at half of the full MPO file)
for (laufIndex = (int)(fileLength * 0.49f); laufIndex < (int)(fileLength * 0.55f); laufIndex++)
{
if ((imageData[laufIndex] == 0xFF) && (imageData[++laufIndex] == 0xD8) && (imageData[++laufIndex] == 0xFF) && (imageData[++laufIndex] == 0xE1))
{
secondImageStart = laufIndex - 3;
break;
}
}
// if no seperater is found
if (secondImageStart == 0)
{
return NULL;
}// Second part
// create QPixmap variables to store the images
QPixmap *leftView = new QPixmap;
QPixmap *rightView = new QPixmap;
// create QPixmap images from QVector(uchar)
leftView->loadFromData(&imageData[0], (secondImageStart - 1));
rightView->loadFromData(&imageData[secondImageStart], (fileLength - secondImageStart));
// store in pointer array
QPixmap *stereoImages = new QPixmap[2];
stereoImages[0] = *leftView;
stereoImages[1] = *rightView;
// return pointer array
return stereoImages;
}
}
@I hope someone has an idea how to increase the speed of my function.
greetz
Stephan -
The slow part is reading the data in lines 18 - 24. I would open the file and let Qt read the complete content with
@
QByteArray imageData = imageFile.readAll();
@If the images have the same size, you can simply divide the file size by two and have the start of the image. That should be reliable enough. In that case you can safe some memory like this:
@
qint64 fileSize = imageFile.size();
qint64 imageSize = fileSize / 2;
if(fileSize - imageSize * 2 != 0)
qWarning() << "Image file size is not an even byte count!";// read the first image
QByteArray imageData = imageFile.read(imageSize);
QPixmap *leftView = new QPixmap;
leftView->loadFromData(imageData);// the rest of the file is the second image
imageData = imageFile.readAll();
QPixmap *rightView = new QPixmap;
rightView->loadFromData(imageData);
@Your heuristic to detect the start of the second image is not safe! You cannot guarantee that somewhere before the actual end of the first image the byte sequence 0xFF 0xD8 0xFF will not be part of the image data itself. You might start at the wrong position!
Additionally, I strongly suggest to avoid the C-style array in line 50. You will have less problem if you use the "container classes":http://doc.qt.nokia.com/stable/containers.html provided by Qt. I would use a
@
QList<QPixmap> MPO_Converter::pictureConvert(QString source) {
// change
// QPixmap *leftView = new QPixmap;
// to
QPixmap leftView;
leftView.loadFromData(imageData);
// and so on// add it to the list QList<QPixmap> stereoImages; stereoImages << leftView;
}
@Qt uses "implicit sharing":http://doc.qt.nokia.com/stable/implicit-sharing.html, so the data will not be copied around in memory (thus neither consuming unnecessary space or CPU time).
And as last hint, it is better to change your method signature from
@
QList<QPixmap> MPO_Converter::pictureConvert(QString source)
@to
@
QList<QPixmap> MPO_Converter::pictureConvert(const QString &source)
@This way you avoid accidentally changing the argument within your function.
-
Hello,
I've tried your solutions. The second part ( QList<QPixmap> MPO_Converter::pictureConvert(const QString &source) ) works fine. It saves at least 200ms (measured with a QElapsedTimer).
The first part is tricky. As I said the images are not exactly 50:50 of the file size. It looks that the Fuji cam has some algorithm whereby the images are compressed by the factor 50:50. Problem if the files are made with different, maybe with a moving single sensor camera, the images are not 50:50. Therefore i still need the compare algorithm. I now that it is not 100% safe, but I tried it with several files and had no problem at all. Therefore the solution with writing the images directly from the string is not reliable.
THX for the help so far but my problem is not solved completly. If someone has any other solution, plz let me know!
greetz
Stephan -
Maybe Qt's search algorithm is faster. You can try it with "QByteArray::indexOf() ":http://doc.qt.nokia.com/latest/qbytearray.html#indexOf.
If you have the start index of your second image, you can use QByteArray's "left() ":http://doc.qt.nokia.com/latest/qbytearray.html#left and "right() ":http://doc.qt.nokia.com/latest/qbytearray.html#right
methods to get the respective parts of the data.@
QByteArray imageData = imageFile.readAll();
QByteArray toSearch;
toSearch.append( 0xFF );
toSearch.append( 0xD8 );
toSearch.append( 0xFF );int searchStart = (int)(fileLength * 0.49f);
int startOfSecondImage = imageData.indexOf(toSearch, searchStart);
if(startOfSecondImage < 0)
qDebug() << "2nd image not found";QByteArray leftData = imageData.left(startOfSecondImage-1);
QPixmap leftImage;
leftImage.loadFromData(leftData);QByteArray rightData = imageData.right(startOfSecondImage);
QPixmap rightImage;
rightImage.loadFromData(leftData);
@ -
Hello Volker,
your solution works fine! it saves more than 1 second with my file (about 10MB). With 2.2 seconds its still slow, but for the moment ok. Now the problem is the creating of the QPixmap files.
I will provide a open source library with functions to convert/use 3D files. I would like to mention you Volker, because you already helped me a lot. If you are interested send me your personal data.greetz
Stephan -
Hi Stephan,
2.2 seconds is not that bad! Keep in mind that QPixmap.loadFromData() must decode the bits to actual pixel data!
Regarding the credits, that would be a bit too much of fame ;) You might mention Qt DevNet, though. Alexandra would be happy, I'm sure :-)
PS: I had a quick look at some MPO documentation. It seems, it is pretty complex format - there is a 50+ pages "document":http://www.cipa.jp/english/hyoujunka/kikaku/pdf/DC-007_E.pdf describing it. Searching for the marker at about the middle of the file looks very optimistic to me.
-
Hi Volker,
you are right its not that bad, but with simple methods i got a better result in c# and i thought c++ must be even faster. Anyway, you are right: MPO files are pretty complex! The format is used as multipicture file format. Actually you can arange as many pictures as you would. Photographers use it to store large panorama phots which persist of many single shots. Therefore a header is include which holds the data about the position of each photo in the panorama.
My converter is not for such types of images. I only want a converter for stereoscopic photos like the ones the Fuji W1 or W3 shots. Therefore the middle of the file is more than sufficient.Once again thanks a lot! U helped me to understand C++/QT better!
greetz
Stephan -
Hm. Sounds freaky to me. If it works it does not necessarily mean that it's robust.
As you want to publish it as a public library I would really advice against this kind of hack. You will have support requests due to images that follow not your "standard", trust me.
You might add it later, but you should keep it on top of your TODO list.