Jpeg Images Have Different Pixel Values Across Multiple Devices

JPEG images have different pixel values across multiple devices

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.

JPEG images have different pixel values across multiple devices

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.

Is it possible read pixels from a jpeg file determenistically across all platforms?

In practice, no. Pixels may differ between different versions of the same library, or even the same version of the same library between platforms.

In theory, you will get the same pixels values if you use the exact same algorithm/software on all platforms you need to support. But I think this means you'll either have to write the decoder yourself, or at least read a lot of source code and configure and compile a known library yourself.

Why does tensorflow decode jpeg images differently from scipy imread?

The discrepancy arises because of inaccurate, but fast, default Discrete Cosine Tranform used by Tensorflow

According to the Source code

// The TensorFlow-chosen default for jpeg decoding is IFAST, sacrificing

// image quality for speed.

flags_.dct_method = JDCT_IFAST;

In order to get accurate decoding one can set the attribute dct_method = 'INTEGER_ACCURATE' as show in example below

def minimal_example():
#image_source = 'https://upload.wikimedia.org/wikipedia/commons/8/88/Astronaut-EVA.jpg'
image_path = 'astronaut.jpg'
image_file = open(image_path,'rb')
image_raw = image_file.read()
image_scipy = scipy.misc.imread(image_path)
image_tf = tf.image.decode_jpeg(image_raw).eval(session=tf.Session())
image_tf_accurate = tf.image.decode_jpeg(image_raw,dct_method="INTEGER_ACCURATE").eval(session=tf.Session())
print('Error For Default: ', np.sum(np.abs(image_tf - image_scipy)))
print('Error For Accurate: ', np.sum(np.abs(image_tf_accurate - image_scipy)))
#Error For Default: 3420883624
#Error For Accurate: 0

Images opened in Pillow and OpenCV are not equivelant

The original image is a JPEG.

JPEG decoding can produce different results depending on the libjpeg version, compiler optimization, platform, etc.

Check which version of libjpeg Pillow and OpenCV are using.

See this answer for more information: JPEG images have different pixel values across multiple devices or here.

BTW, (im-imP) produces uint8 overflow (there is no way to have such a high amount of large pixel differences without seeing it in your frequency chart). Try to cast to int type before doing your frequency computation.

the pixel values changed while using imwrite to jpg files in c++

If you want non-lossy compression, you can't use jpg's and have to use a .png (there's .bmp as well but its uncompressed)

jpg = cv.imread("../resources/fisheye/1_1.jpg")
cv.imwrite("1_1.png", jpg)
png = cv.imread("1_1.png")
np.sum(np.where(jpg != png, 1, 0)) # number of differing pixels between images

Output: 0

Fast accessing pixel values of jpeg images

Use CBitmap::GetBits() to get a raw pointer to the pixel data. You can now directly party on the pixels without going through the expensive GetPixel() method. There are a number of things you need to be careful with when you do this:

  • You have to use CBitmap::GetPitch() to calculate the offset to the start of a line. The pitch is not the same as the width.
  • Lines in the bitmap are stored upside-down
  • You have to deal with the pixel format yourself. A 24bpp image stores 3 bytes per pixel. An indexed format like 8bpp requires looking up the color in the color table. 32bpp is the easy one, 4 bytes per pixel and the pitch is always the same as the width.

Copied image saved with different pixels to original with PIL

*.jpg is a compressed image format. By saving the jpg again you use a different default quality for the jpg writer
so the resulting pixel-values differ.

See image file format params for jpg for the quality parameter that you can pass to image.save()

quality
The image quality, on a scale from 1 (worst) to 95 (best). The default is 75. Values above 95 should be avoided; 100 disables portions of the JPEG compression algorithm, and results in large files with hardly any gain in image quality.

Either

  • move to a non-compressive format (png f.e.) or
  • copy the file using file operations: see f.e. How do I copy a file in Python?

Related:

  • Why is the quality of JPEG images produced by PIL so poor?


Related Topics



Leave a reply



Submit