2011-02-04 38 views
3

我得到下面的C#(試驗 - )有問題的代碼後,沒有釋放:內存轉換的BitmapImage

public static void TestBitmap2ByteArray(BitmapImage bitmap) 
    { 
     JpegBitmapEncoder encoder = new JpegBitmapEncoder(); 
     MemoryStream memstream = new MemoryStream(); 
     encoder.Frames.Add(BitmapFrame.Create(bitmap)); 
     encoder.Save(memstream); 

     memstream.Close(); 
    } 

每次我打電話的功能,內存分配,而不是再次釋放。在真實的項目中,該方法經常被調用,應用程序內存不足。

這是一個精簡版的代碼,沒有返回任何東西。

我正在使用Visual Studio2010和.net 3.5 SP1。

幫助表示讚賞。 謝謝。

+0

你通常從這種方法返回什麼?也許問題不在於這種方法,而是你返回的對象永遠不會被拋棄。 – 2011-02-04 11:19:41

+2

向我們展示更多代碼。這還不夠。 – Euphoric 2011-02-04 11:29:49

+0

通常我返回一個流(byte [])。該功能稱爲不同位圖的多次。 – shepard1 2011-02-04 12:18:43

回答

4

正如我的其他答案中所述,解決此問題的更好方法是直接訪問位圖數據。 BitmapImage繼承自BitmapSource。 BitmapSource非常適合這一點,也適用於WPF綁定。

我自己使用BitmapSource來操作直接綁定到WPF(MVVM樣式)的圖像。基本上我在內存中創建一個區域並將BitmapSource指向它。這允許我直接讀/寫像素到內存並使BitmapSource無效,以便WPF重新繪製圖像。我有一個標準的「位圖」對象,我用這個。直接的數據訪問使其超快速。 (嚴重的是,沒有任何問題在30fps下修改4張圖像中的所有位...並沒有以更高的速度嘗試,因爲它沒有被要求。)

樣例用法可在my blog上找到。但基本上你這樣做:

unsafe { 
    byte* imgBytePtr = (byte*)myBitmap.ImageData; 
    Int32* imgInt32Ptr = (Int32*)myBitmap.ImageData; 
    int height = (int)myBitmap.BitmapSource.Height; 
    int width = (int)myBitmap.BitmapSource.Width; 
    int bpp = myBitmap.BytesPerPixel; 

    // Note: No need to iterate just for copy. A Marshal.Copy() at this point can copy all the bytes into a byte-array if you want. 
    // But the best would be if your application could do its work directly in the imgBytePtr[]-array. 

    for (int x = 0; x < height; x++) 
    { 
     for (int y = 0; y < width; y++) 
     { 
     // Get bytes into RGBA values 
     int bytePos = x * (width * bpp) + (y * bpp); 
     byte R = imgBytePtr[bytePos + 0]; 
     byte B = imgBytePtr[bytePos + 1]; 
     byte G = imgBytePtr[bytePos + 2]; 
     byte A = imgBytePtr[bytePos + 3]; 

     // Alternatively get Int32 value of color 
     int intPos = x * width + y; 
     int intColor = imgIntPtr[intPos]; 


     // Examples of manipulating data   

     // Remove blue 
     imgBytePtr[bytePos + 1] = 0; 
     // Alternative remove blue by bitmask 
     imgIntPtr[intPos] = imgIntPtr[intPos] & 0xFF00FFFF; 

     } 
    } 
} 
// Now execute Invalidate() and WPF will automagically update bound picture object :) 

這使得一個的BitmapSource,如果你需要的BitmapImage相反,你可以看到,如果你可以改變它的工作。

/// 
    /// This object holds a byte array of the picture as well as a BitmapSource for WPF objects to bind to. Simply call .Invalidate() to update GUI. 
    /// 
    public class Bitmap : IDisposable 
    { 
     // some ideas/code borowed from CL NUI sample CLNUIImage.cs 
     [DllImport("kernel32.dll", SetLastError = true)] 
     static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName); 
     [DllImport("kernel32.dll", SetLastError = true)] 
     static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); 
     [DllImport("kernel32.dll", SetLastError = true)] 
     static extern bool UnmapViewOfFile(IntPtr hMap); 
     [DllImport("kernel32.dll", SetLastError = true)] 
     static extern bool CloseHandle(IntPtr hHandle); 

     private IntPtr _section = IntPtr.Zero; 
     public IntPtr ImageData { get; private set; } 
     public InteropBitmap BitmapSource { get; private set; } 
     public int BytesPerPixel = 3; 

     /// 
     /// Initializes an empty Bitmap 
     /// 
     /// Image width 
     /// Image height 
     /// Image format 
     public Bitmap(int width, int height, PixelFormat pixelFormat) 
     { 
      BytesPerPixel = pixelFormat.BitsPerPixel/8; 
      uint imageSize = (uint)width * (uint)height * (uint)BytesPerPixel; 
      // create memory section and map 
      _section = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, imageSize, null); 
      ImageData = MapViewOfFile(_section, 0xF001F, 0, 0, imageSize); 
      BitmapSource = Imaging.CreateBitmapSourceFromMemorySection(_section, width, height, pixelFormat, width * BytesPerPixel, 0) as InteropBitmap; 
     } 

     /// 
     /// Invalidates the bitmap causing a redraw 
     /// 
     public void Invalidate() 
     { 
      BitmapSource.Invalidate(); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

     protected void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       // free managed resources 
      } 
      // free native resources if there are any. 
      if (ImageData != IntPtr.Zero) 
      { 
       UnmapViewOfFile(ImageData); 
       ImageData = IntPtr.Zero; 
      } 
      if (_section != IntPtr.Zero) 
      { 
       CloseHandle(_section); 
       _section = IntPtr.Zero; 
      } 
     } 
    } 

1

您正在通過將位圖數據保存到流中來訪問位圖數據。這會導致開銷。 更好的方法是使用LockBits。這將使您可以直接訪問圖像中的字節,並且可以輕鬆地以* byte []和* UInt32 [](注:需要不安全{})的形式訪問它。

如果您仍然需要將字節複製到字節數組中,則可以使用Marshal.Copy,但是如果您的意圖是直接對圖像進行修改,則可以自由使用。 LockBits鏈接有一個示例顯示了Marshal.Copy的用法。

如果您需要一些如何處理圖片的示例,我已經發布了關於Kinect圖像處理的some sample code