Чтение файлов изображений с помощью QImageReader с использованием QtConcurrent

Я пытаюсь использовать QImageReader для чтения частей файла изображения за раз (на плитку), чтобы для очень больших изображений они не считывались в память с диска до тех пор, пока их не нужно отображать.

Похоже, я столкнулся с некоторыми проблемами безопасности потоков.

Это то, что у меня есть на данный момент:

#include "rastertile.h"

QMutex RasterTile::mutex;
RasterTile::RasterTile()
{
}

//RasterTile::RasterTile(QImageReader *reader, int nBlocksX, int nBlocksY, int xoffset, int yoffset, int nXBlockSize, int nYBlockSize)
RasterTile::RasterTile(QString filename, int nBlocksX, int nBlocksY, int xoffset, int yoffset, int nXBlockSize, int nYBlockSize)

    : Tile(nBlocksX, nBlocksY, xoffset, yoffset, nXBlockSize, nYBlockSize)
{
        this->reader = new QImageReader(filename);
        connect(&watcher,SIGNAL(finished()),this,SLOT(updateSceneSlot()));
}


void RasterTile::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
    if(image.isNull())
    {
        TilePainter=painter;
        TileOption=option;
        TileWidget=widget;
        future = QtConcurrent::run(this, &RasterTile::LoadTilePixmap);
        watcher.setFuture(future);

    }else
    {
        QRectF imageRect = image.rect();
        painter->drawImage(imageRect, image);
    }

}

QImage RasterTile::LoadTilePixmap()
{
    QMutexLocker locker(&mutex);

    QImage img(nBlockXSize, nBlockYSize, QImage::Format_RGB32);

    QRect rect(tilePosX*nBlockXSize, tilePosY*nBlockYSize, nBlockXSize, nBlockYSize);

    reader->setClipRect(rect);
    reader->read(&img);
    if(reader->error())
    {
        qDebug("Not null error");
        qDebug()<<"Error string is: "<<reader->errorString();
    }
    return img;

}

Таким образом, это в основном создание нового считывателя для каждой плитки и обновление переменной «изображение» суперкласса, которую я затем могу нарисовать.

Кажется, это дает мне много ошибок от читателя, которые просто говорят: «Невозможно прочитать данные изображения».

Я думаю, что это, вероятно, связано с тем, что многие плитки обращаются к одному и тому же файлу, но я не знаю, как это доказать или исправить.

Я думаю, что Qt использует libjpeg и libpng и что-то еще для чтения различных форматов изображений.


person Derek    schedule 23.06.2011    source источник
comment
Сколько плиток? Практический предел количества потоков обычно довольно мал.   -  person Jay    schedule 23.06.2011
comment
Убедитесь, что простая однопоточная программа может загружать тайлы через QImageReader. Я бы также создал QImageReader в стеке в LoadTilePixmap(). Если вы сделаете LoadPixmap бесплатной функцией и передадите все в качестве аргумента, вы сможете избежать проблем с синхронизацией. Почему мьютекс? Это статично? Это ограничило бы плитки, загружаемые за раз, до 1...   -  person Frank Osterfeld    schedule 24.06.2011
comment
общее количество плиток может быть более 1000, но за один раз загружается только несколько плиток. Те плитки, которые впервые отображаются в средстве просмотра, являются единственными, для которых это запускает новые потоки. Мьютекс был статическим, да. Я пытался не допустить, чтобы QImageReader имел более одного доступа к файлу на диске одновременно. Я попытался поместить QImageReader в стек, но получил те же результаты, что и в куче, и передал его.   -  person Derek    schedule 24.06.2011
comment
Не могли бы вы опубликовать определение RasterTile и Tile? Нам было бы легче проводить эксперименты.   -  person Luc Touraille    schedule 04.07.2011
comment
RasteTile - это то, что опубликовано. Я посмотрю, смогу ли я добавить Tile. По сути, это было просто нечто, расширяющее QGraphicsItem.   -  person Derek    schedule 09.07.2011


Ответы (4)


Ознакомьтесь с исходным кодом QImageReader.

Вы получите "Невозможно прочитать данные изображения", когда средство чтения вернет InvalidDataError.

Если вы также прочитали объяснение InvalidDataError, QT Doc говорит, что

Данные изображения были недопустимыми, и QImageReader не смог прочитать из них изображение. Это может произойти, если файл изображения поврежден.

Так что, вероятно, ваш файл поврежден.

person O.C.    schedule 24.06.2011
comment
Я задавался этим вопросом, но я могу читать файл из других мест, и если я уберу параллелизм в этом коде и просто попытаюсь прочитать каждую плитку последовательно. - person Derek; 24.06.2011

Я вижу здесь две потенциальные проблемы.

  1. Это не ваша настоящая проблема, но позже я буду проблемой с маленькими плитками. future устанавливается после запуска потока. Это может вызвать проблемы, если поток завершится до того, как future будет установлен правильно. (Не уверен на 100% в этом, но скажем... 85%, и я считаю, что это очень маловероятно)
  2. paint можно вызывать очень часто. Я полагаю, что ваша проблема в том, что он вызывается во второй раз, прежде чем ваша ветка чтения закончилась. Это приведет к тому, что другой поток попытается прочитать тайл, в то время как первый все еще читает его. Ты даже попытаешься использовать один и тот же экземпляр QImageReader одновременно...
person SteakOverflow    schedule 30.07.2012

Вы можете попробовать добавить:

if (reader->canRead())
    reader->read(&img);
else
    qDebug() << "Could not read from device";

Это может не сильно помочь, но согласно документации canRead: возвращает true, если образ может быть прочитан для устройства (т. е. формат изображения поддерживается, и устройство, кажется, содержит достоверные данные); в противном случае возвращает ложь.

person cdyer    schedule 03.07.2011

Вы можете попробовать другой способ создания потоков для чтения тайлов.

Создайте myReaderObject как подкласс QObject.

В конструкторе основного потока создайте объект-член QThread:

m_workerthread=new QThread();
m_workerthread->start();

Для чтения плитки сделайте

myReaderObject *reader=new myReaderObject();
reader->moveToThread(m_workerthread);
connect ( reader, SIGNAL(myFinishSignal() , ...
QMetaObject::invokeMethod(reader,"read", Qt::AutoConnection);

Тогда вашему myReaderObject, конечно, нужен метод чтения и сигнал myFinishSignal.

person dirkboye    schedule 19.08.2011