1

I am trying to pass multiple images (actually a video) between two processes using QSharedmemory. Currently I am serializing a QVector of QImages and copying this to the memory. This works, but the serialization steps takes about 99% of the time. How could I do this faster in a platform independent manner?

My code for sending:

int main(int argc, const char* argv[])
{
QObject *parent;

QString program = "python";
QStringList arguments;
QString programPath(PYSOURCE);
arguments << "-u" << programPath+QString("/test.py");
qDebug() << arguments;

QProcess *pyProcess = new QProcess();
pyProcess->start(program, arguments);

QVector<QImage> images;
//.. fill with some images

auto start = std::chrono::system_clock::now();
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);

// this takes extremely long ~44s for 80mb
out << images;

int size = buffer.size();

QSharedMemory sharedMemory("process_example");
if (!sharedMemory.create(size)) {
    qDebug() << "Unable to create shared memory segment.";
    return 0;
}

qDebug() << "sizeof mem: " << sharedMemory.size() << "bufsize:" << buffer.size();
sharedMemory.lock();


char *to = (char*)sharedMemory.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(sharedMemory.size(), size));

sharedMemory.unlock();  

qDebug() << "image copied to shared memory";
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
qDebug() << "duration:" << duration.count() << "ms";

while (pyProcess->waitForReadyRead(-1)){
QByteArray newData = pyProcess->readAllStandardOutput();
QString result = QString::fromLocal8Bit(newData);
qDebug(qPrintable(QString("py:")+result));

}
sharedMemory.detach();

return 0;
}
MaxR
  • 11
  • 3

3 Answers3

1

I met the same question a few days ago. I couldn't find the answers on the Internet, so I tried to write the codes by myself. The method is to copy the raw data of the images to and from the shared memory directly. This method is very very fast.

The function for sending the images:

//The image-file names are stored in a QStringList object
void SendImage(QStringList fileList)
{
    if (sharedMemory.isAttached())
        sharedMemory.detach();
    int count = fileList.count();
    int totalSize = 0;
    QList<QImage> images;
    
    //Load the images
    for (int i = 0; i < count; i++)
    {
        QImage image(fileList.at(i));
        images.append(image);
        totalSize += image.sizeInBytes();
    }
    //Total size needed for shared memory
    totalSize += (count * 5 + 1) * sizeof(int);
    if (!sharedMemory.create(totalSize)) {
        throw QString( "Shared memory cannot be allocated!");
        return;
    }
    sharedMemory.lock();
    
    //to points to the target space to write
    uchar *to = (uchar *)sharedMemory.data();
    //from points to the source to write
    uchar *from = (uchar *)&count;
    
    //Write the count of the images from the first byte of the shared memory
    memcpy(to, from, sizeof(int));
    //Move the pointer forward
    to += sizeof(int);
    
    //Write the images one by one
    for (int i = 0; i < count; i++) {
        //Write the total size of the image and move the pointer forward
        int sizeInBytes = images.at(i).sizeInBytes();
        from = (uchar *)&sizeInBytes;
        memcpy(to, from ,sizeof(int));
        to += sizeof(int);
        
        //Write the width of the image and move the pointer forward
        int width = images.at(i).width();
        from = (uchar *)&width;
        memcpy(to, from ,sizeof(int));
        to += sizeof(int);
        
        //Write the height of the image and move the pointer forward
        int height = images.at(i).height();
        from = (uchar *)&height;
        memcpy(to, from ,sizeof(int));
        to += sizeof(int);
        
        //Write the image format of the image and move the pointer forward
        int imageFormat =  (int)images.at(i).format() ;
        from = (uchar *)&imageFormat;
        memcpy(to, from ,sizeof(int));
        to += sizeof(int);
        
        //Write the bytes per line of the image and move the pointer forward
        int bytesPerLine = images.at(i).bytesPerLine();
        from = (uchar *)&bytesPerLine;
        memcpy(to, from ,sizeof(int));
        to += sizeof(int);
        
        //Write the raw data of the image and move the pointer forward
        from = (uchar *)images.at(i).bits();
        memcpy(to, from, sizeInBytes);
        to += sizeInBytes;
    }
    sharedMemory.unlock();
}

My code for receiving the images:

QList<QImage> * FetchImages()
{
    QList<QImage> * imageList = new QList<QImage>;
    
    if (!sharedMemory.attach())
    {
        throw QString("The shared memory cannot be attached!");
        return imageList;
    }
    
    sharedMemory.lock();
    //from points to the first byte of the shared memory
    uchar * from = (uchar *)sharedMemory.data();
    //Get the count of the images and move the pointer forward
    int count;
    count = *(int *)from;
    from += sizeof(int);
    //Get the images one by one
    for (int i = 0; i < count; i++)
    {
        //Get the size of the image and move the pointer forward
        int sizeInBytes = *(int *)from;
        from += sizeof(int);
        
        //Get the width of the image and move the pointer forward
        int width = *(int *)from;
        from += sizeof(int);
        
        //Get the height of the image and move the pointer forward
        int height  = *(int *)from;
        from += sizeof(int);
        
        //Get the image format of the image and move the pointer forward
        int imageFormat = *(int *)from;
        from += sizeof(int);
        
        ////Get the bytes per line of the image and move the pointer forward
        int bytesPerLine = *(int *)from;
        from += sizeof(int);

        //Generate an image using the raw data and move the pointer forward
        QImage image(from, width, height, bytesPerLine, (QImage::Format)imageFormat);
        from += sizeInBytes;
        
        //It is very important here and I spent much time.
        //A new image object "image1" must be deeply copied from "image" before appended to the imageList.
        //Because the raw data of "image" use the space of the shared memory, which will not be accessed after the shared memory is detached.
        //The raw data of the newly constructed "image1" use the memory of its own process.
        QImage image1 = image.copy(0, 0, width, height);
        imageList -> append(image1);
    }
    sharedMemory.unlock();
    sharedMemory.detach();

    return imageList;
}
Yu Lei
  • 11
  • 1
0

I think the main problem for your buffer is that it need resizing many times. Try set big size for underling byte array to bigger size then your data need.

buffer.buffer ().resize (100000000);

Call this just before serialization of images.

Evgeny
  • 3,910
  • 2
  • 20
  • 37
  • That does not make a significant difference in my tests. I suspect that the acutal serialization is inefficient and a simple memcpy would suffice, but Im not sure how to handle the Image headers etc. – MaxR Jan 20 '16 at 18:10
0

Try using QImage.save to save it to a QByteArray as "PNG", and then copy the data from that into the QSharedMemory with memcpy.

http://doc.qt.io/qt-4.8/qimage.html#save-2

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • Unfortunately, the png compression is too slow for my application ( its about as slow as datastream encoding). Im writing the raw bytes to memory now, which works, but uses up about 30x times more space than compressed data – MaxR Jan 22 '16 at 17:45