我有一个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增加了内存的使用。我每十次添加一个GC.Collect() (每秒一次),现在一切都好了。
我在这里做错什么了吗?UnlockBits不应该自己清理吗?
发布于 2014-03-28 15:01:17
汉斯·帕桑是对的。这个问题是一个正在被复制的位图,尽管我仍然不清楚为什么这会导致垃圾收集器显然没有完成它的工作。
在这个程序中,我有一些后端类,它们从摄像机中获取图像,并将位图转换成适当的格式(例如,我发布的YUV422到RGB转换)。传递的位图将被重用。
然后我有一些UI类,其中之一是一个窗口,它在计时器上显示一个新的帧,运行频率大约为10赫兹。在这里,您必须复制位图,因为后端的东西将用一个新的框架来替换它,等等。
更新显示的例程很简单:
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;
}
}让我感到惊讶的是,用黑白相机观察内存的使用情况只显示它被固定在一些低的数字上,而仅彩色相机就飞升到了2GB,没有停止的迹象。我已经看到了边缘垃圾收集器的情况,在这种情况下,您可以看到内存爬升,然后在垃圾收集器启动时突然恢复。
这里的明显区别当然是,即使是两个黑白相机也只有614 kBx10 fps,而彩色相机大约是这个的10倍。我甚至不会开始猜测为什么前者与垃圾收集器没有问题,而后者却有问题。
发布于 2014-03-25 09:26:26
在我看到的代码中,RGB.LockBits增加了内存的使用。我每10个调用添加一个GC.Collect() (每秒一次),现在一切都好了。
您的意思基本上是,一个普通的垃圾收集将回收所有的东西,问题是垃圾收集器不会在您期望的时候被执行。
你的机器内存不足吗?是否存在实际的内存压力,从而迫使垃圾收集器触发和回收空间?或者您有数千兆字节的空闲内存,以至于暂停所有线程来回收内存只是浪费cpu资源?听起来你的机器只是耸耸肩说:“呃,我这里有更多的记忆,一切如常。”
https://stackoverflow.com/questions/22626276
复制相似问题