【发布时间】:2014-03-25 05:40:43
【问题描述】:
我有一个 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 是增加 RAM 使用率的代码。我每十次调用(每秒一次)添加一个GC.Collect(),现在一切都很好。
我在这里做错了吗? UnlockBits 不应该自己清理吗?
【问题讨论】:
-
我正在做非常相似的计算,但我从未观察到这种行为。你的位图有多大?
-
你是在发布模式下调试器分离的情况下运行它吗?
-
消耗内存是 LockBits() 的意图。程序中真正的错误是位图上缺少 Dispose() 调用。是的, GC.Collect() 掩盖了你的错误。这是大锤解决方案。
-
@ScottChamberlain:不,在调试模式下运行。
-
@HansPassant:否定。我正在重用位图,并且同一个程序具有与具有相同“位图重用”方法的不同相机的接口,并且没有内存蠕变问题。