2014-10-02 53 views
2

我正在使用第三方DLL,它具有作爲參數的RGB緩衝區。C# - 與C++不同的位圖的RGB緩衝區

我已經使用下面的代碼來從Bitmap讀取RGB緩衝液:

private byte[] GetBGRValues(Bitmap bmp) 
    { 

     // Lock the bitmap's bits. 
     Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
     System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

     // Get the address of the first line. 
     IntPtr ptr = bmpData.Scan0; 
     // Declare an array to hold the bytes of the bitmap. 
     int bytes = Math.Abs(bmpData.Stride) * bmp.Height; 
     byte[] rgbValues = new byte[bytes]; 


     // Copy the RGB values into the array. 
     System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); 
     bmp.UnlockBits(bmpData); 

     return rgbValues; 
    } 

的問題是,所生成的RGB緩衝器是不正確的。如果我在IrfanView中打開這個緩衝區,提供正確的參數,生成的圖像不正確(看起來像它被移動)。

如果得到一個我用C++代碼讀取的緩衝區,它就可以工作。

我注意到bmpData.Stride比我預期的要大1個單位(width * channels)。 (我知道.NET使用4個字節對齊)。

問題是:爲什麼RGB緩衝區不正確?

+0

* ...爲什麼RGB緩衝區不正確?*。 rgb緩衝區是正確的。 *(我知道 。NET使用4字節對齊方式)*。它不是使用4字節對齊的.net。它自己使用它的圖像。 C++或.net提供了相同的緩衝區。不要急於下結論。 – 2014-10-02 22:21:13

+0

我給這個方法提供的位圖是24bpp,並且width = 897,height = 1281.爲什麼stride的值是2692而不是2691?如果我將生成的緩衝區保存到文件並在IrfanView上打開,則圖像不正確。爲什麼? – user1862876 2014-10-02 23:32:59

+0

您沒有訪問僅包含像素的緩衝區,您正在訪問針對快速訪問進行了優化的圖像的內存中表示,但兩者不同。正如你所觀察到的,內存中的表示將會優化alighment和類似的,這意味着跨度可以大於圖像寬度。換句話說,如果之後嘗試將該字節數組解釋爲897 * 1281的3字節像素集,則會遇到問題。 – 2016-11-01 08:52:09

回答

0

請確保您看到訂單是B-G-R而不是R-G-B 您可以嘗試將這些值轉換爲uint的不安全代碼。所以你有RGB轉換爲uint

/// <summary> 
///  Locks a Bitmap into system memory. 
/// </summary> 
public unsafe void LockBits() 
{ 
    var width = this.Bitmap.Width; 
    var height = this.Bitmap.Height; 

    var imageLockMode = ImageLockMode.UserInputBuffer; 

    // Setting imageLockMode 
    imageLockMode = imageLockMode | ImageLockMode.ReadOnly; 
    imageLockMode = imageLockMode | ImageLockMode.WriteOnly; 

    // Save the bouunds 
    this._bounds = new Rectangle(0, 0, width, height); 

    // Create Pointer 
    var someBuffer = new uint[width*height]; 
    // Pin someBuffer 
    fixed (uint* buffer = someBuffer) //pin 
    { 
     // Create new bitmap data. 
     var temporaryData = new BitmapData 
     { 
      Width = width, 
      Height = height, 
      PixelFormat = PixelFormat.Format32bppArgb, 
      Stride = width*4, 
      Scan0 = (IntPtr) buffer 
     }; 

     // Get the data 
     this.BitmapData = this.Bitmap.LockBits(this._bounds, imageLockMode,  PixelFormat.Format32bppArgb, 
     temporaryData); 
     // Set values 
     this.Buffer = someBuffer; 
    } 
} 
0

我記得我在多年前工作的圖書館 - 顏色轉移奇怪。底層的微軟圖書館有一個(功能),因爲圖書館裏面的RGB已經被逆轉 - 我們不知道,我們嘗試了一個轉換,並發現了那個小寶石。

另外不要忘記你的C#緩衝區中的Alpha通道。

內存中的位圖顏色通道以藍色,綠色,紅色和Alpha的順序表示,儘管通常用縮寫ARGB! http://softwarebydefault.com/2013/03/22/bitmap-swap-argb/

4

您注意了正確 - 您需要考慮Stride。一般來說,你不能簡單地在一個Copy呼叫中複製圖像。 Stride包括行長和填充,並且可以大於行長。所以你只需要從每一行復制你需要的字節,忽略填充字節並通過加入Stride前進到下一行。

我想這是你與你的代碼中看到:

original and valid result - 原始圖像和預期的結果

invalid result - 無效的結果,而不步幅

這裏是工作代碼:

public static byte[] GetBGRValues(Bitmap bmp) 
{ 
    var rect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
    var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat); 

    var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat)/8; 
    var imgBytes = bmp.Height * rowBytes; 
    byte[] rgbValues = new byte[imgBytes]; 

    var ptr = bmpData.Scan0; 
    for (var i = 0; i < bmp.Height; i++) 
    { 
     Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes); 
     ptr += bmpData.Stride; // next row 
    } 

    bmp.UnlockBits(bmpData); 

    return rgbValues; 
} 

更多的細節可以參考這個答案:Byte Array to Image Conversion

而且這也許圖像將幫助你瞭解Stride目的:

row length vs stride

你必須在正確的跳過空白區域,當你從Bitmap獲得字節。

+0

有時候,一張圖片說1000k以上:-)最後,我能理解整個「跨步」 - 是關於什麼的! P.S .:我現在使用AForge,它會處理大部分這些事情。 – 2017-05-11 12:35:15