The .NET Bitmap.Load method produces different results on different computers

I am trying to upload a JPEG file and remove all black and white pixels from the image

C # code:

... m_SrcImage = new Bitmap(imagePath); Rectangle r = new Rectangle(0, 0, m_SrcImage.Width, m_SrcImage.Height); BitmapData bd = m_SrcImage.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); //Load Colors int[] colours = new int[m_SrcImage.Width * m_SrcImage.Height]; Marshal.Copy(bd.Scan0, colours, 0, colours.Length); m_SrcImage.UnlockBits(bd); int len = colours.Length; List<Color> result = new List<Color>(len); for (int i = 0; i < len; ++i) { uint w = ((uint)colours[i]) & 0x00FFFFFF; //Delete alpha-channel if (w != 0x00000000 && w != 0x00FFFFFF) //Check pixel is not black or white { w |= 0xFF000000; //Return alpha channel result.Add(Color.FromArgb((int)w)); } } ... 

After that I try to find unique colors in the list with this code

  result.Sort((a, b) => { return aR != bR ? aR - bR : aG != bG ? aG - bG : aB != bB ? aB - bB : 0; }); List<Color> uniqueColors = new List<Color>( result.Count); Color rgbTemp = result[0]; for (int i = 0; i < len; ++i) { if (rgbTemp == result[i]) { continue; } uniqueColors.Add(rgbTemp); rgbTemp = result[i]; } uniqueColors.Add(rgbTemp); 

And this code produces different results on different machines on the same image!

For example, on this image it produces:

  • 43198 unique colors on XP SP3 with .NET version 4
  • 43,168 unique colors on Win7 Ultimate with .NEt version 4.5

You can download the minimum test project here . It simply opens the selected image and creates a txt file with unique colors.

Another fact. Some pixels are read differently on different machines. I am comparing txt files with notepad ++ and it shows that some pixels have different RGB components. The difference is 1 for each component, for example.

  • Win7 pixel: 255,200,100
  • WinXP pixel: 254 199 99

I read this post

stackoverflow.com/questions/2419598/why-might-different-computers-calculate-different-arithmetic-results-in-vb-net

(sorry, I don't have enough ratings for a normal link).

... but there was no information on how to fix it.


The project was compiled for the .NET 4 Client profile on a computer running Windows 7 in the VS 2015 Commumity Edition.

+5
source share
2 answers

Wikipedia has to say about the accuracy requirements for JPEG decoders :

The JPEG encoding description does not capture the accuracy required for the output compressed image . However, the JPEG standard (and similar MPEG standards) includes some requirements for decoding accuracy, including all parts of the decoding process (variable length decoding, reverse DCT, dequantization, renormalization of outputs); the output from the reference algorithm should not exceed:

  • maximum one bit of difference for each pixel component
  • low standard error for each block of 8 × 8 pixels.
  • very low average error for each block of 8 × 8 pixels.
  • very low standard error for the whole image
  • extremely low average error across the entire image

(my emphasis)

In short, there are simply two different decoder implementations, and they produce different images in accordance with the accuracy requirement (1 bit = +/- 1 in the component values, as you noticed).

In order not to use the same (not built-in) jpeg decoder, this should be expected. If you need the same output, you will probably have to switch to a different decoder, which will be the same regardless of which version of .NET or Windows you are using. I assume GDI + is the culprit here, as it has undergone great changes since Windows XP.

+3
source

I solve my problem by adding Libjpeg.NET to write and write this code:

  private Bitmap JpegToBitmap(JpegImage jpeg) { int width = jpeg.Width; int height = jpeg.Height; // Read the image into the memory buffer int[] raster = new int[height * width]; for(int i = 0; i < height; ++i) { byte[] temp = jpeg.GetRow(i).ToBytes(); for (int j = 0; j < temp.Length; j += 3) { int offset = i*width + j / 3; raster[offset] = 0; raster[offset] |= (((int)temp[j+2]) << 16); raster[offset] |= (((int)temp[j+1]) << 8); raster[offset] |= (int)temp[j]; } } Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb); Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] bits = new byte[bmpdata.Stride * bmpdata.Height]; for (int y = 0; y < bmp.Height; y++) { int rasterOffset = y * bmp.Width; int bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride; for (int x = 0; x < bmp.Width; x++) { int rgba = raster[rasterOffset++]; bits[bitsOffset++] = (byte)((rgba >> 16) & 0xff); bits[bitsOffset++] = (byte)((rgba >> 8) & 0xff); bits[bitsOffset++] = (byte)(rgba & 0xff); } } System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length); bmp.UnlockBits(bmpdata); return bmp; } 

So that is enough for me.

0
source

Source: https://habr.com/ru/post/1242976/


All Articles