Identical images have different pixel data when loaded as a QImage in Release

Comparing two identical images which are loaded in the format QImage::Format_Indexed8 have different pixel data only when running in Release mode.

The following code shows differences when running in Release, but not when running in 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;
}

Output:

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

A few notes:

  • This is no longer reproducible When changing the format of the images to, for example QImage::Format_ARGB32, using convertToFormat
  • This is no longer reproducible when using pixelIndex to compare each pixel
  • It is always the same indices that fail
  • The indices that fail change when running on a different machine.

My current guess would be that Qt does some optimization when loading images in this format. I can't explain why this doesn't result in the same data for two identical images however.

If you care to reproduce the issue, here is my input image:

Sample image

1 answer

  • answered 2018-11-08 08:38 paddy

    I think I can see what's going on here.

    Your image is 8-bit grayscale with a width of 127 pixels. All those indices where the differences occur are multiples of 128 (minus 1, i.e the last column in a 128-byte row). Since you have obtained the raw image bits, it's most likely that row data in the image is aligned (commonly 2 or 4 bytes).

    Qt is probably not writing anything into those padding bytes, since they're not considered part of the image. So you're really seeing undefined behavior, as your program cannot guarantee repeatable results (loading data from an uninitialized memory location).

    To properly compare your image data, you will need to skip over any padding bytes. That means you'll need to know the padding amount. Given the richness of the Qt library, I'm sure that there will be some way to access or infer that information.

    [Edit] I quickly looked up the reference for QImage, and indeed I can see that scanlines are 32-bit aligned. The simplest way to achieve to compare is to call QImage::bytesPerLine() to determine how many bytes to compare for each scanline, and then get each scanline individually via QImage::scanLine(int)