QT QImageReader设置ScaledSize和设置AutoTransform交互

QT QImageReader setScaledSize and setAutoTransform interaction

本文关键字:设置 AutoTransform 交互 ScaledSize QImageReader QT      更新时间:2023-10-16

我想阅读并显示数码相机图片的缩略图版本。我目前使用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的分辨率还可以,但我需要提供133x100来设置ScaledSize()以获得"正常"结果:

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)

所以,我开始工作了,但我觉得我在代码方面做得太多了。这是预期的行为吗?有没有更简单的方法可以得到这个结果?

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

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

因此,小数位数总是由处理程序或读取器转换之前应用。


结论

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