QT QImageReader setScaledSize和setAutoTransform交互

时间:2019-01-02 09:45:06

标签: c++ image qt exif

我想阅读和显示数码相机图片的缩略图版本。我目前使用的是QImageReader,它具有我需要的2个功能,但是它们似乎不是最佳地交互...:

我想基于EXIF属性旋转后 加载并显示宽度为100像素的图像。但是,会发生什么:

代码:

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 :: transformation()信息,并使用它来交换传递给setScaledSize的size对象中的宽度/高度:

修改后的代码:

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的分辨率可以,但是我需要为setScaledSize()提供133x100才能获得“正常”结果:

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 :(得分:0)

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来实际读取图像数据),您会看到:

  

支持此选项的处理程序应在应用任何Clip rect转换(ClipRect)之后将图像缩放到提供的大小(QSize)。如果处理程序不支持此选项,则QImageReader将在读取图像后执行缩放。

因此,缩放始终由处理程序或读取器在转换之前 应用。


结论

基于上述要点,您必须使用EXIF数据来提供正确的缩放大小,或者在读取图像后对图像进行缩放(更易于阅读)。因此,除非您正在读取数千张非常大的图像并且预缩放操作显着加快了转换速度,否则我建议您使用更可维护的代码。