【问题标题】:Identical images have different pixel data when loaded as a QImage in Release在 Release 中作为 QImage 加载时,相同的图像具有不同的像素数据
【发布时间】:2018-11-08 08:05:54
【问题描述】:

比较两个以 QImage::Format_Indexed8 格式加载的相同图像只有在发布模式下运行时具有不同的像素数据。

以下代码显示了在 Release 中运行时的差异,但在 Debug 中运行时却没有:

int main()
{
  QImage _img1("C:\\tmp\\diff\\identicals\\file1.png");
  QImage _img2("C:\\tmp\\diff\\identicals\\file2.png");

  std::cout << QString("Format 1: %1").arg(_img1.format()).toStdString().c_str() << std::endl;
  std::cout << QString("Format 2: %2").arg(_img2.format()).toStdString().c_str() << std::endl;

  const unsigned char * _bits1 = _img1.bits();
  const unsigned char * _bits2 = _img2.bits();

  std::cout << QString("Byte count 1: %1 | Byte count 2: %2").arg(_img1.byteCount()).
    arg(_img2.byteCount()).toStdString().c_str() << std::endl;

  for (int _i = 0; _i < _img1.byteCount(); _i++)
  {
    if (_bits1[_i] != _bits2[_i])
    {
      std::cout << "--DIFFERENCE--" << std::endl;
      std::cout << QString("i --> %1").arg(_i).toStdString().c_str() << std::endl;
      std::cout << QString("Bit1: %1 | Bit2:  %2").arg(_bits1[_i]).arg(_bits2[_i]).toStdString().c_str() << std::endl << std::endl;
    }
  }

  std::cout << "BREAK" << std::endl;
}

输出:

Format 1: 3
Format 2: 3
Byte count 1: 23424 | Byte count 2: 23424
--DIFFERENCE--
i --> 1535
Bit1: 0 | Bit2:  217

--DIFFERENCE--
i --> 1663
Bit1: 0 | Bit2:  35

--DIFFERENCE--
i --> 1791
Bit1: 0 | Bit2:  94

--DIFFERENCE--
i --> 1919
Bit1: 0 | Bit2:  166

--DIFFERENCE--
i --> 2047
Bit1: 0 | Bit2:  143

--DIFFERENCE--
i --> 2175
Bit1: 0 | Bit2:  104

--DIFFERENCE--
i --> 2303
Bit1: 0 | Bit2:  240

--DIFFERENCE--
i --> 2431
Bit1: 0 | Bit2:  190

--DIFFERENCE--
i --> 2559
Bit1: 0 | Bit2:  129

--DIFFERENCE--
i --> 2687
Bit1: 0 | Bit2:  11

--DIFFERENCE--
i --> 2815
Bit1: 0 | Bit2:  30

--DIFFERENCE--
i --> 2943
Bit1: 0 | Bit2:  163

--DIFFERENCE--
i --> 3071
Bit1: 0 | Bit2:  206

--DIFFERENCE--
i --> 3199
Bit1: 0 | Bit2:  232

--DIFFERENCE--
i --> 3327
Bit1: 0 | Bit2:  124

--DIFFERENCE--
i --> 3455
Bit1: 0 | Bit2:  225

--DIFFERENCE--
i --> 12287
Bit1: 0 | Bit2:  240

--DIFFERENCE--
i --> 12415
Bit1: 0 | Bit2:  224

--DIFFERENCE--
i --> 12543
Bit1: 0 | Bit2:  240

几点说明:

  • 当使用convertToFormat 将图像格式更改为例如 QImage::Format_ARGB32 时,这不再可重现
  • 当使用pixelIndex 比较每个像素时,这不再可重现
  • 失败的总是相同的索引
  • 在不同机器上运行时失败的索引会发生变化。

我目前的猜测是 Qt 在以这种格式加载图像时会进行一些优化。但是,我无法解释为什么这不会导致两张相同图像的数据相同。

如果您想重现该问题,这是我的输入图像:

【问题讨论】:

  • 格式2的打印需要使用%1,顺便说一句

标签: c++ qt qimage


【解决方案1】:

我想我可以看到这里发生了什么。

您的图像是 8 位灰度,宽度为 127 像素。所有出现差异的索引都是 128 的倍数(减 1, 128 字节行中的最后一列)。由于您已经获得了原始图像位,因此很可能图像中的行数据是对齐的(通常是 2 或 4 个字节)。

Qt 可能不会在这些填充字节中写入任何内容,因为它们不被视为图像的一部分。因此,您确实看到了未定义的行为,因为您的程序无法保证可重复的结果(从未初始化的内存位置加载数据)。

要正确比较您的图像数据,您需要跳过任何填充字节。这意味着您需要知道填充量。鉴于 Qt 库的丰富性,我相信会有一些方法可以访问或推断这些信息。

[编辑] 我快速查找了 QImage 的参考资料,确实可以看到扫描线是 32 位对齐的。最简单的实现比较的方法是调用QImage::bytesPerLine()来确定每条扫描线要比较多少字节,然后通过QImage::scanLine(int)单独获取每条扫描线

【讨论】:

  • 不幸的是,bytesPerLine 似乎不适合填充并返回 8,这意味着比较每个 scanLine 也会失败。但是,我很确定您是正确的,因为像素数据的大小(以字节为单位)正好是 image heightimage width x image height 大。似乎每行有一个字节的填充。然而,比较总是在同一个索引上失败仍然很奇怪。如果它是由未初始化的内存引起的,我希望它会更加随机......
【解决方案2】:

您的图像比较实现不正确。你为什么不使用QImage::operator==?您也可以参考它的source code 以了解应该如何完成它,但这有点毫无意义,因为 Qt 已经为您提供了此代码 - 除非您真正的问题是如何正确迭代图像中的所有像素,因为这就是错误所在。根据QImageC++ 语义,您的代码具有未定义的行为,因为它可以读取可能从未写入过的位置。

【讨论】:

  • 此代码用于我们的端到端测试框架中,用于计算比较两个图像时的峰值信噪比。因为在我们的架构中没有两个图像完全相同,所以我们允许 PSNR 阈值。我的问题的解决方案之一是使用pixelIndex,这就是我所做的。我的问题的目的是更好地了解 Qt 在这里所做的事情。 @paddy 对此有很大帮助,但我仍然不明白为什么总是相同的索引不同(例如,不是图像的每个第 128 位,而是图像的一个子集)。
猜你喜欢
  • 2017-12-14
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
  • 2021-05-11
  • 1970-01-01
  • 2011-06-04
  • 2021-10-30
  • 2018-11-25
相关资源
最近更新 更多