.NET Bitmap.Load方法在不同的计算机上产生不同的结果

我尝试加载JPEG文件并删除图像中的所有黑白像素

C#代码:

... 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)); } } ... 

之后,我尝试通过此代码在List中find独特的颜色

  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); 

这段代码在同一图像上的不同机器上产生不同的结果!

例如,在这个图像上它产生:

  • XP SP3与.NET版本4 43198独特的颜色
  • .Win7 Ultimate上的43168独特颜色.NEt版本4.5

最小testing项目可以在这里下载 。 它只是打开选定的图像,并产生独特的颜色txt文件。

还有一个事实。 一些像素在不同的机器上读取的方式不同 我用记事本+ +比较txt文件,它显示一些像素有不同的RGB组件。 每个组件的差别是1,例如

  • Win7像素:255 200 100
  • WinXP像素:254 199 99

我读过这篇文章

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

(对不起,我没有足够的正常链接)。

…但没有信息如何解决它。


项目是在VS 2015 Commumity Edition中使用操作系统Windows 7的机器上为.NET 4 Client Profile进行编译的。

维基百科有这样的说明JPEG解码器的准确性要求 :

JPEG标准的编码描述不能确定输出压缩图像所需的精度 。 然而,JPEG标准(以及类似的MPEG标准)包括解码的一些精度要求,包括解码过程的所有部分(可变长度解码,逆DCT,反量化,输出的重整化); 参考算法的输出不得超过:

  • 每个像素分量的最大差值为一位
  • 每个8×8像素块的均方误差较小
  • 每个8×8像素块的平均误差非常低
  • 整个图像的均方误差非常低
  • 整个图像的平均误差极低

(我的重点)

简而言之,在这里播放的只有两种不同的解码器实现,并且在精确度要求(1比特=分量值的+/- 1,如您所观察的)内产生不同的图像。

因此,使用相同的(非内置的)JPEG解码器,这是可以预料的。 如果您需要具有完全相同的输出,那么您可能需要切换到不同的解码器,无论您使用的是哪个.NET版本或Windows,都将是相同的解码器。 我猜GDI +是这里的罪魁祸首,自从Windows XP以来,它经历了更大的变化。

我解决了我的问题,通过添加Libjpeg.NET项目和编写此代码:

  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; } 

所以,这对我来说就够了。