Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPEG images have different pixel values across multiple devices

Tags:

I had noticed that when reading in an identical photograph across devices in the JPEG format, the pixel values do not match up. They are close, but different. When converted to PNG files, the pixel values seem to match up.

This would seem that it would be due to the (un)compression algorithms across devices. That's what comes to mind anyways. Is there a way to read in JPEG files so that the same pixels are retrieved from the photograph across devices? I don't see an option within the BitmapFactory Options component.

Currently applying the following to maintain size when working on pixel values of an image across devices:

Options options = new Options();
options.inScaled = false;
options.inPreferQualityOverSpeed = true;

Currently comparing pixels with the following just to look at a few (close matches, but not equal):

int[] pixels = new int[bitmapF.getWidth() * bitmapF.getHeight()];
bitmapF.getPixels(pixels, 0, bitmapF.getWidth(), 0, 0, bitmapF.getWidth(), bitmapF.getHeight());
Log.d("pixel entries", "pixels = " + pixels[233] + " - " + pixels[4002] + " - " + pixels[11391]);

Note: If reading in a PNG version of that same file which is uncompressed, the values are identical as expected.

The Samsung Galaxy S4 for example and the Samsung Galaxy S5 even have different pixels from the same jpeg (running off of the same test activity) stored in the assets folder.

pixel[233] for instance would be -5205635 on s5 but -5336451 on the s4. The pixel[4002] is a little off as well. But pixel[11391] are equal across both devices on this jpeg picture.

like image 902
Jay Snayder Avatar asked May 09 '14 13:05

Jay Snayder


1 Answers

The JPEG standard does not require that decoder implementations produce bit-for-bit identical output images. Unfortunately the standards document specifying decoder requirements, ISO 10918-2, is apparently not freely available online but Wikipedia says:

...the JPEG standard (and the similar MPEG standards) includes some precision requirements for the decoding, including all parts of the decoding process (variable length decoding, inverse DCT, dequantization, renormalization of outputs); the output from the reference algorithm must not exceed:

  • a maximum 1 bit of difference for each pixel component
  • low mean square error over each 8×8-pixel block
  • [etc.]

Differences between different decoder outputs using the same input are generally due to differing levels of internal precision, particularly in performing the IDCT. Another possible source of differences is smoothing, which attempts to reduce "blockiness" artifacts.

Like you, I would expect that setting inPreferQualityOverSpeed would produce the same output but nothing actually guarantees that. I can think of at least a couple ways that you could get small variations on two different phones:

  1. The phones may run different versions of Android where the implementation of BitmapFactory changed (e.g. perhaps inPreferQualityOverSpeed was broken and then fixed, or vice versa), or
  2. The phones may provide different hardware features (e.g. vector instruction set, DSP co-processor, etc.) that BitmapFactory leverages. Even differences in scalar floating-point units can cause discrepancies, especially with JIT compilation producing the actual machine instructions.

Given the wiggle room in the standard plus your experimental observations, it appears the only way to guarantee bit-for-bit agreement is to perform decoding within your own application. Perhaps you can find some alternative Android-compatible library.

like image 195
rhashimoto Avatar answered Nov 10 '22 13:11

rhashimoto