QT QImageReader setScaledSize и setAutoTransform взаимодействия - PullRequest
0 голосов
/ 02 января 2019

Я хотел бы прочитать и отобразить уменьшенные версии фотографий с цифровых камер. В настоящее время я использую QImageReader, который имеет 2 функции, которые мне нужны, но они, кажется, взаимодействуют неоптимально ...:

Я хочу загрузить и отобразить изображение с шириной 100 пикселей после поворота на основе свойств EXIF. Однако, что происходит, это:

код:

QImageReader imageReader(filepath);
auto origSize1 = imageReader.size();
imageReader.setAutoTransform(true);
auto origSize2 = imageReader.size();
auto scaledSize1 = origSize1.scaled(QSize(100, 1000), Qt::KeepAspectRatio);
auto scaledSize2 = origSize2.scaled(QSize(100, 1000), Qt::KeepAspectRatio);
imageReader.setScaledSize(scaledSize2);
auto qimage = imageReader.read();
auto imageSize = qimage.size();
auto qimageScaled = qimage.scaledToWidth(100, Qt::SmoothTransformation);
auto scaledSize3 = qimageScaled.size();

std::cout << "  origSize1 = (" << origSize1.width() << ", " << origSize1.height() << ")" << std::endl;
std::cout << "  origSize2 = (" << origSize2.width() << ", " << origSize2.height() << ")" << std::endl;
std::cout << "scaledSize1 = (" << scaledSize1.width() << ", " << scaledSize1.height() << ")" << std::endl;
std::cout << "scaledSize2 = (" << scaledSize2.width() << ", " << scaledSize2.height() << ")" << std::endl;
std::cout << "  imageSize = (" << imageSize.width() << ", " << imageSize.height() << ")" << std::endl;
std::cout << "scaledSize3 = (" << scaledSize3.width() << ", " << scaledSize3.height() << ")" << std::endl;

выход:

  origSize1 = (4896, 3672)
  origSize2 = (4896, 3672)
scaledSize1 = (100, 75)
scaledSize2 = (100, 75)
  imageSize = (75, 100)
scaledSize3 = (100, 134)

Итак, изображение читается с шириной 100 пикселей в ландшафтном режиме, затем применяется автоповорот, в результате чего изображение в портретном режиме имеет ширину всего 75 пикселей и высоту 100 пикселей. Дополнительный вызов scaledToWidth () заботится о том, чтобы сделать изображение правильного размера, но качество очень плохое из-за масштабирования x1.34.

Кажется, я мог бы вызвать setScaledSize с двойным (или тройным, или ...) разрешением, которое мне нужно, чтобы получить достаточное качество, а затем полагаться на дополнительный вызов scaledToWidth (), чтобы получить правильную конечную ширину.

Лучшим подходом, по-видимому, является использование информации QImageReader :: translation () и ее использование для обмена шириной / высотой в объекте размера, переданном в setScaledSize:

пересмотренный код:

QImageReader imageReader(filepath);
auto origSize1 = imageReader.size();
imageReader.setAutoTransform(true);
auto transformation = imageReader.transformation();
auto swapWH = transformation.testFlag(QImageIOHandler::TransformationRotate90);
auto swappedSize = swapWH ? origSize1.transposed() : origSize1;
auto scaledSwappedSize = swappedSize.scaled(QSize(100, 1000), Qt::KeepAspectRatio);
imageReader.setScaledSize(scaledSwappedSize);
auto qimage = imageReader.read();
auto imageSize = qimage.size();
auto qimageScaled = qimage.scaledToWidth(100, Qt::SmoothTransformation);
auto scaledSize3 = qimageScaled.size();

вывод с этим исправленным кодом:

  origSize1 = (4896, 3672)
transformation = 7
swap width/height? = 1
swappedSize = (3672, 4896)
scaledSwapp = (100, 133)
  imageSize = (133, 100)
scaledSize3 = (100, 76)

Как вы можете видеть, я все равно получаю пейзажное изображение. Контент находится в портретном режиме, но растянут горизонтально (делая всех жирными). Итак, разрешение 100x133 в порядке, но мне нужно предоставить 133x100 для setScaledSize (), чтобы получить «нормальные» результаты:

QImageReader imageReader(filepath);
auto origSize1 = imageReader.size();
imageReader.setAutoTransform(true);
auto transformation = imageReader.transformation();
auto swapWH = transformation.testFlag(QImageIOHandler::TransformationRotate90);
auto swappedSize = swapWH ? origSize1.transposed() : origSize1;
auto scaledSwappedSize = swappedSize.scaled(QSize(100, 1000), Qt::KeepAspectRatio);
auto swappedScaledSwappedSize = swapWH ? scaledSwappedSize.transposed() : scaledSwappedSize;
imageReader.setScaledSize(swappedScaledSwappedSize);
auto qimage = imageReader.read();
auto imageSize = qimage.size();
auto qimageScaled = qimage.scaledToWidth(100, Qt::SmoothTransformation);
auto scaledSize3 = qimageScaled.size();

Теперь я получаю "правильные" результаты (обратите внимание, что imagesize == scaledSize3):

  origSize1 = (4896, 3672)
transformation = 7
swap width/height? = 1
swappedSize = (3672, 4896)
scaledSwapp = (100, 133)
swpSclSwapp = (133, 100)
  imageSize = (100, 133)
scaledSize3 = (100, 133)

Итак, я заставляю это работать, но я чувствую, что делаю слишком много циклического кода. Это ожидаемое поведение? Есть ли более простые способы получить этот результат?

1 Ответ

0 голосов
/ 03 января 2019

AFAIK нет встроенного способа сделать это (см. Ниже, почему).

Проблема, которую я вижу с вашим текущим подходом, состоит в том, что он слишком сложен при попытке использоватьвстроенная функция масштабирования.Вместо этого я бы предложил вам использовать что-то более простое для чтения, например:

QImageReader imageReader(filepath);
imageReader.setAutoTransform(true);
const auto qimage = imageReader.read();
const auto qscaledImage = qimage.scaledToWidth(100, Qt::SmoothTransformation);

(см. Внизу один случай)


Объяснение

Считывая исходный код QImageReader::read (<qt_src_dir>/src/gui/image/qimagereader.cpp), метод сначала считывает изображение, затем масштабирует его, а затем применяет преобразование:

bool QImageReader::read(QImage *image)
{
    // [...]

    // read the image
    if (!d->handler->read(image)) {
        d->imageReaderError = InvalidDataError;
        d->errorString = QImageReader::tr("Unable to read image data");
        return false;
    }

    // provide default implementations for any unsupported image
    // options
    if (d->handler->supportsOption(QImageIOHandler::ClipRect) && !d->clipRect.isNull()) {
        if (d->handler->supportsOption(QImageIOHandler::ScaledSize) && d->scaledSize.isValid()) {

        // [... more lines about scaling ...]

        }
    }

    // [...]
    if (autoTransform())
        qt_imageTransform(*image, transformation());

    return true;
}

Также, читая документацию QImageIOHandler::ImageOption::ScaledSize (QImageIOHandler используется QImageReader для фактического чтения данных изображения). Вы видите:

Ожидается, что обработчик, поддерживающий эту опцию, масштабирует изображение допредоставленный размер (QSize) после применения любого преобразования прямоугольника клипа (ClipRect).Если обработчик не поддерживает эту опцию, QImageReader выполнит масштабирование после того, как изображение будет прочитано.

Поэтому масштаб всегда применяется либо обработчиком, либо читателем до преобразование.


Выводы

Исходя из указанных выше моментов, вы должны либо использовать данные EXIF, чтобы обеспечить правильный масштабированный размер, либо масштабировать изображение после того, как оно было прочитано(который легче читать).Поэтому, если вы не читаете тысячи очень больших изображений, а операция предварительного масштабирования значительно ускоряет преобразование, я предлагаю вам использовать более понятный код.

...