![](/img/trans.png)
[英]Confused about LockBits, BitmapData and PixelFormat.Format48bppRgb
[英]BitmapData from LockBits consuming memory
我有一個C#例程來獲取YUV422位圖並轉換為RGB:
private unsafe void YUV422toRGB(byte[] YUV422, int YUVstride, ref Bitmap RGB)
{
//Found http://pastebin.com/MFsDnUCq after I wrote this.
int row, col, index;
byte y1, y2, u, v;
int r1, r2, g1, g2, b1, b2;
int c1, c2, d, e;
byte* RGBbytes;
int RGBindex;
//http://bobpowell.net/lockingbits.aspx
//It looks as though this bmp guy is consuming memory to the point
//where I must force the garbage collector or the program will crash.
//Why?
System.Drawing.Imaging.BitmapData bmp =
RGB.LockBits(
new Rectangle(0, 0, RGB.Width, RGB.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
RGB.PixelFormat);
RGBbytes = (byte*)bmp.Scan0;
RGBindex = 0;
index = 0;
for (row = 0; row < RGB.Height; row++)
{
for (col = 0; col < YUVstride; col += 4)
{
u = YUV422[index + 0];
y1 = YUV422[index + 1];
v = YUV422[index + 2];
y2 = YUV422[index + 3];
index += 4;
c1 = y1 - 16;
c2 = y2 - 16;
d = u - 128;
e = v - 128;
int c298 = 298 * c1;
r1 = (c298 + 409 * e + 128) >> 8;
g1 = (c298 - 100 * d - 208 * e + 128) >> 8;
b1 = (c298 + 516 * d + 128) >> 8;
c298 = 298 * c2;
r2 = (c298 + 409 * e + 128) >> 8;
g2 = (c298 - 100 * d - 208 * e + 128) >> 8;
b2 = (c298 + 516 * d + 128) >> 8;
//Now for clamping.
//From http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
//min(x, y) = y ^ ((x ^ y) & -(x < y))
//max(x, y) = x ^ ((x ^ y) & -(x < y))
//We want min(x, 255) followed by max(x, 0).
//The problem is that x < y in C# is a bool which cannot be converted to int.
//But effectively, -(x < y) is -1 if x < y and 0 otherwise.
//we can do this by looking at the first bit of x-y
//min(x, y) = y ^ ((x ^ y) & ((x - y) >> 31))
//max(x, y) = x ^ ((x ^ y) & ((x - y) >> 31))
//There appears to be 10% or so speed increase with the bithack.
//r1 = Math.Max(0, Math.Min(r1, 255));
//r2 = Math.Max(0, Math.Min(r2, 255));
//g1 = Math.Max(0, Math.Min(g1, 255));
//g2 = Math.Max(0, Math.Min(g2, 255));
//b1 = Math.Max(0, Math.Min(b1, 255));
//b2 = Math.Max(0, Math.Min(b2, 255));
r1 = 255 ^ ((r1 ^ 255) & ((r1 - 255) >> 31));
g1 = 255 ^ ((g1 ^ 255) & ((g1 - 255) >> 31));
b1 = 255 ^ ((b1 ^ 255) & ((b1 - 255) >> 31));
r2 = 255 ^ ((r2 ^ 255) & ((r2 - 255) >> 31));
g2 = 255 ^ ((g2 ^ 255) & ((g2 - 255) >> 31));
b2 = 255 ^ ((b2 ^ 255) & ((b2 - 255) >> 31));
r1 = r1 ^ ((r1 ^ 0) & ((r1 - 0) >> 31));
g1 = g1 ^ ((g1 ^ 0) & ((g1 - 0) >> 31));
b1 = b1 ^ ((b1 ^ 0) & ((b1 - 0) >> 31));
r2 = r2 ^ ((r2 ^ 0) & ((r2 - 0) >> 31));
g2 = g2 ^ ((g2 ^ 0) & ((g2 - 0) >> 31));
b2 = b2 ^ ((b2 ^ 0) & ((b2 - 0) >> 31));
RGBbytes[RGBindex + 0] = (byte)b1;
RGBbytes[RGBindex + 1] = (byte)g1;
RGBbytes[RGBindex + 2] = (byte)r1;
RGBbytes[RGBindex + 3] = (byte)b2;
RGBbytes[RGBindex + 4] = (byte)g2;
RGBbytes[RGBindex + 5] = (byte)r2;
RGBindex += 6;
}
}
RGB.UnlockBits(bmp);
}
在經典的“嘿,為什么我的程序為什么在30秒后總是崩潰”之后,我意識到垃圾收集器沒有跟上(我每秒調用此函數10次,並且位圖始終通過引用傳遞)。
RGB.LockBits
代碼,我看到RGB.LockBits
是增加RAM使用率的代碼。 我每十個調用(每秒一次GC.Collect()
添加一個GC.Collect()
),現在一切正常。
我在這里做錯什么了嗎? UnlockBits
不應該UnlockBits
清理嗎?
逐步查看代碼,我看到RGB.LockBits是增加RAM使用率的代碼。 我每十個調用(每秒一次)添加一個GC.Collect(),現在一切正常。
您的意思基本上是,正常的垃圾回收將回收所有內容,問題是,垃圾回收器沒有按預期執行。
您的機器內存不足嗎? 您是否存在實際的內存壓力,該壓力會迫使垃圾收集器觸發並回收空間? 還是您有幾GB的可用內存,以至於暫停所有線程來回收內存只是浪費cpu資源? 聽起來您的計算機只是聳了聳肩,說:“嗯,我的存儲空間更大了。一切照舊。”
漢斯·帕桑(Hans Passant)是正確的。 問題是一個位圖正在被復制,盡管我仍然不清楚為什么這導致垃圾收集器顯然無法完成其工作。
我在該程序中有一些后端類,這些類從相機拍攝圖像並將位圖轉換為適當的格式(例如,我發布的YUV422到RGB轉換)。 被傳遞的位圖被重用。
然后,我有一些UI類,其中一個是窗口,該窗口在以大約10 Hz或更低的頻率運行的計時器上顯示一個新幀。 在這里,您必須復制位圖,因為后端材料將用新框架替換它,等等。
更新顯示的例程很簡單:
public void SetImage(Bitmap Image)
{
if (this.IsDisposed) return;
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { SetImage(Image); });
else
picFrame.Image = (Bitmap)Image.Clone();
}
一台相機可提供640x480x8位(黑白)圖像; 在后端,我還使用鎖定位將從API調用獲得的像素數組復制到.NET位圖。 即使其中兩個相機(和顯示窗口)同時運行,我的內存也沒有問題。
1920x1080 YUV422圖像顯然是垃圾收集器“飽和”了(或者您可以稱呼它),因此此版本的顯示例程解決了該問題:
private Bitmap DisplayImage = null;
public void SetImage(Bitmap Image)
{
if (this.IsDisposed) return;
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { SetImage(Image); });
else
{
if (DisplayImage != null)
DisplayImage.Dispose();
DisplayImage = (Bitmap)Image.Clone();
picFrame.Image = DisplayImage;
}
}
讓我感到震驚的是,用黑白相機觀察內存使用情況時,只能發現它的數量很少,而僅彩色相機就已飆升至2 GB,沒有停止的跡象。 我已經看到了邊緣垃圾收集器的情況,在這種情況下,您看到內存逐漸增大,然后在垃圾收集器啟動時突然下降。
當然,這里的明顯區別是,即使是兩個黑白攝像機也只有614kB x 10 fps,而彩色攝像機大約是后者的10倍。 我什至不會開始猜測為什么前者對垃圾收集器沒有問題,而后者卻沒有。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.