![](/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.