2016-02-14 95 views
5

我嘗試加載JPEG文件,並從圖像中刪除所有的黑色和白色像素.NET Bitmap.Load方法產生不同的計算機上不同的結果

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

之後,我試圖找到此代碼列表中的唯一顏色

result.Sort((a, b) => 
    { 
     return a.R != b.R ? a.R - b.R : 
       a.G != b.G ? a.G - b.G : 
       a.B != b.B ? a.B - b.B : 
       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); 

而且此代碼在同一圖像上的不同機器上產生不同的結果!

例如,在this image它產生:

  • 43198獨特的顏色上XP SP3與.NET版本上的Win7終極4
  • 43168種獨特的顏色與.NET版本4.5

最小測試項目你可以download here。它只是打開選定的圖像,併產生獨特的顏色txt文件。

還有一個事實。一些像素在不同的機器上讀取不同。我將txt文件與記事本++進行了比較,它顯示了一些像素具有不同的RGB分量。每個組件的差別是1,例如,

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

我已經閱讀這個交

stackoverflow.com/questions/2419598/why-might-different- (對不起,我沒有足夠的正常鏈接)。

...但沒有信息如何解決它。


在VS 2015 Commumity Edition中針對OS Windows 7的計算機上的.NET 4客戶端配置文件進行了編譯。

+0

要上傳圖片,您應該選擇免費上傳服務,不需要我們登錄 – TaW

+0

@TaW將圖片移動到imgur.com並將測試項目移動到github –

+0

謝謝。不過我猜Lasse的答案是正確的:JPeg並不意味着以絕對準確的方式再現圖像。如果你真的需要這個,請轉到PNG! – TaW

回答

3

Wikipedia has this to say about the accuracy requirements for JPEG Decoders

在JPEG編碼標準描述不能解決所需壓縮圖像輸出的精度。然而,JPEG標準(以及類似的MPEG標準)包括解碼的一些精度要求,包括解碼過程的所有部分(可變長度解碼,反DCT,反量化,輸出的重整化);從參考算法的輸出必須不超過:

  • 針對每個像素分量的最大差的一個位
  • 低均方誤差對每個8×8像素塊
  • 非常低的平均誤差在每個8×8像素塊
  • 非常低的平均平方誤差在整個圖像
  • 極低平均誤差在整個圖像
在組件

(我的強調)

總之,根本就在播放兩種不同的解碼器實現在這裏,它們產生不同的圖像,精度要求內(1個比特= +/- 1值,如你所觀察到的)。

這麼短的使用相同的(非內置的)jpeg解碼器,這是可以預料的。如果您需要具有完全相同的輸出,那麼您可能需要切換到不同的解碼器,無論您使用的是哪個.NET版本或Windows,這個解碼器都是相同的。我猜GDI +是這裏的罪魁禍首,因爲自從Windows XP以來,它經歷了更大的變化。

+0

這是個可悲的消息...感謝您的回答。你知道可以使用PNG,BMP,JPEG和TIFF格式的圖書館嗎? –

+0

我試圖添加Bitmiracle Libjpeg.NET庫到項目和結果相同... –

+1

爲什麼你需要這樣跨平臺產生相同的像素呢?您使用的是有損壓縮圖像格式,您無法真正控制像素。 –

0

我加入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; 
     } 

所以,這足以讓我。

相關問題