January 28, 2011

steph0815 steph0815
Lab Rat
10 posts

[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!

  1. QPixmap *MPO_Converter::pictureConvert(QString source)
  2. {  
  3.        // first part
  4.  int count = 0;
  5.     int laufIndex, fileLength;
  6.  int secondImageStart = 0;
  7.  // open MPO file as datastream
  8.  QFile imageFile(source);
  9.  // open if exist
  10.  if (imageFile.exists())
  11.  {
  12.   // open file in read only mode
  13.   imageFile.open(QIODevice::ReadOnly);
  14.   QDataStream dataInfilestream(&imageFile);
  15.   // get file length
  16.   fileLength = imageFile.size();
  17.   // define data vector
  18.   QVector<uchar> imageData(fileLength);
  19.   // read data from stream
  20.   while(!dataInfilestream.atEnd())
  21.   {  
  22.    // copy file bytewise
  23.    dataInfilestream >> imageData[count++];  
  24.   }  
  25.   // close file
  26.   imageFile.close();
  27.   // search for beginning of second image (normaly at half of the full MPO file)            
  28.         for (laufIndex = (int)(fileLength * 0.49f); laufIndex < (int)(fileLength * 0.55f); laufIndex++)
  29.         {  
  30.             if ((imageData[laufIndex] == 0xFF) && (imageData[++laufIndex] == 0xD8) && (imageData[++laufIndex] == 0xFF) && (imageData[++laufIndex] == 0xE1))
  31.             {
  32.                 secondImageStart = laufIndex - 3;
  33.                 break;
  34.             }
  35.         }
  36.   // if no seperater is found
  37.   if (secondImageStart == 0)
  38.   {
  39.    return NULL;
  40.   }
  41.  
  42.                 // Second part
  43.   // create QPixmap variables to store the images
  44.   QPixmap *leftView = new QPixmap;
  45.   QPixmap *rightView = new QPixmap;  
  46.   // create QPixmap images from QVector(uchar)
  47.   leftView->loadFromData(&imageData[0], (secondImageStart - 1));
  48.   rightView->loadFromData(&imageData[secondImageStart], (fileLength - secondImageStart));
  49.   // store in pointer array
  50.   QPixmap *stereoImages = new QPixmap[2];
  51.   stereoImages[0] = *leftView;
  52.   stereoImages[1] = *rightView;
  53.   // return pointer array
  54.   return stereoImages;  
  55.  }
  56. }

I hope someone has an idea how to increase the speed of my function.

greetz
Stephan

 Signature 

greetz
Stephan

7 replies

January 28, 2011

Volker Volker
Ant Farmer
5331 posts

The slow part is reading the data in lines 18 – 24. I would open the file and let Qt read the complete content with

  1. 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:

  1. qint64 fileSize = imageFile.size();
  2. qint64 imageSize = fileSize / 2;
  3. if(fileSize - imageSize * 2 != 0)
  4.     qWarning() << "Image file size is not an even byte count!";
  5.  
  6. // read the first image
  7. QByteArray imageData = imageFile.read(imageSize);
  8. QPixmap *leftView = new QPixmap;
  9. leftView->loadFromData(imageData);
  10.  
  11. // the rest of the file is the second image
  12. imageData = imageFile.readAll();
  13. QPixmap *rightView = new QPixmap;
  14. 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 [doc.qt.nokia.com] provided by Qt. I would use a

  1. QList<QPixmap> MPO_Converter::pictureConvert(QString source) {
  2.     // change
  3.     // QPixmap *leftView = new QPixmap;
  4.     // to
  5.     QPixmap leftView;
  6.     leftView.loadFromData(imageData);
  7.     // and so on
  8.  
  9.     // add it to the list
  10.     QList<QPixmap> stereoImages;
  11.     stereoImages << leftView;
  12. }

Qt uses implicit sharing [doc.qt.nokia.com], 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

  1. QList<QPixmap> MPO_Converter::pictureConvert(QString source)

to

  1. QList<QPixmap> MPO_Converter::pictureConvert(const QString &source)

This way you avoid accidentally changing the argument within your function.

January 28, 2011

steph0815 steph0815
Lab Rat
10 posts

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

 Signature 

greetz
Stephan

January 29, 2011

Volker Volker
Ant Farmer
5331 posts

Maybe Qt’s search algorithm is faster. You can try it with QByteArray::indexOf() [doc.qt.nokia.com].

If you have the start index of your second image, you can use QByteArray’s left() [doc.qt.nokia.com] and right() [doc.qt.nokia.com]
methods to get the respective parts of the data.

  1. QByteArray imageData = imageFile.readAll();
  2. QByteArray toSearch;
  3. toSearch.append( 0xFF );
  4. toSearch.append( 0xD8 );
  5. toSearch.append( 0xFF );
  6.  
  7. int searchStart = (int)(fileLength * 0.49f);
  8. int startOfSecondImage = imageData.indexOf(toSearch, searchStart);
  9. if(startOfSecondImage < 0)
  10.     qDebug() << "2nd image not found";
  11.  
  12. QByteArray leftData = imageData.left(startOfSecondImage-1);
  13. QPixmap leftImage;
  14. leftImage.loadFromData(leftData);
  15.  
  16. QByteArray rightData = imageData.right(startOfSecondImage);
  17. QPixmap rightImage;
  18. rightImage.loadFromData(leftData);

January 30, 2011

steph0815 steph0815
Lab Rat
10 posts

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

 Signature 

greetz
Stephan

January 31, 2011

Volker Volker
Ant Farmer
5331 posts

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 [cipa.jp] describing it. Searching for the marker at about the middle of the file looks very optimistic to me.

February 1, 2011

steph0815 steph0815
Lab Rat
10 posts

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

 Signature 

greetz
Stephan

February 1, 2011

Volker Volker
Ant Farmer
5331 posts

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.

 
  ‹‹ Cannot Obtain Correct Size for QMenuBar on Windows Before Showing QMainWindow      Signals and RAD GUI design ? ››

You must log in to post a reply. Not a member yet? Register here!