最近用c#做视觉的项目,需要将彩色图像转换为灰度图像,考虑到性能问题,尝试了下定义Intptr作为数据缓存中转,并用new Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr)的形式回归bitmap图像,虽然性能得到了大幅度的提升,但定义的Intptr无法释放,狂吃内存,不知该如何解决?
具体过程为:
1)通过System.Runtime.InteropServices.Marshal.AllocHGlobal(int cb)定义一个intptr
2)相关数据计算到intptr的内存空间中
3)用new Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr)将intptr转化为Bitmap图像此时产生一个问题,如果调用System.Runtime.InteropServices.Marshal.FreeHGlobal(System.IntPtr)释放intptr的内存,新的bitmap图像损坏,如果不释放intptr的内存,系统内存疯狂被吃掉代码如下:
public static void ColortoGray(ref Bitmap ImageScr,ref Bitmap ImageDst)
{
int height = ImageScr.Height;
int width = ImageScr.Width;
int offsetDst = width / 4 * 4 + 4 - width;
if (offsetDst == 4)
offsetDst = 0;
int strideDst = width + offsetDst;
int num = strideDst * height;
IntPtr intptrbuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(num);
BitmapData dataScr = ImageScr.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, ImageScr.PixelFormat);
int offsetScr = dataScr.Stride - width * 3;
unsafe
{
byte* ptrScr = (byte*)dataScr.Scan0.ToPointer();
byte* ptrDst = (byte*)intptrbuffer.ToPointer();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
*ptrDst = (byte)(0.114 * (*ptrScr) + 0.587 * (*(ptrScr + 1)) + 0.299 * (*(ptrScr + 2)));
ptrDst++;
ptrScr += 3;
}
ptrDst += offsetDst;
ptrScr += offsetScr;
}
}
ImageScr.UnlockBits(dataScr);
if (ImageDst != null)
{
ImageDst.Dispose();//释放图像资源
}
imageDst= new Bitmap(width, height, strideDst,PixelFormat.Format8bppIndexed, intptrbuffer);
//System.Runtime.InteropServices.Marshal.FreeHGlobal(intptrbuffer);//释放intptrbuffer内存,
//设置调色板
ColorPalette palette = ImageDst.Palette;
for (int i = 0; i < palette.Entries.Length; i++)
palette.Entries[i] = Color.FromArgb(255, i, i, i);
ImageDst.Palette = palette;
}个人理解上新的bitmap图像应该把intptr占用了,但是重复调用该段代码时,由于ImageDst是上次调用该函数通过intptr产生的图像,但通过 ImageDst.Dispose()释放图像资源时,intptr的内存并没有释放掉其实定义一个byte[] buffer=new byte[num]来代替intptr作为中转缓存也是没有问题的,但是性能上差了将近20%的样子,个人比较好奇的是,通过intptr的形式在c#中该如何实现这个功能,毕竟性能差的挺多的
具体过程为:
1)通过System.Runtime.InteropServices.Marshal.AllocHGlobal(int cb)定义一个intptr
2)相关数据计算到intptr的内存空间中
3)用new Bitmap(Int32, Int32, Int32, PixelFormat, IntPtr)将intptr转化为Bitmap图像此时产生一个问题,如果调用System.Runtime.InteropServices.Marshal.FreeHGlobal(System.IntPtr)释放intptr的内存,新的bitmap图像损坏,如果不释放intptr的内存,系统内存疯狂被吃掉代码如下:
public static void ColortoGray(ref Bitmap ImageScr,ref Bitmap ImageDst)
{
int height = ImageScr.Height;
int width = ImageScr.Width;
int offsetDst = width / 4 * 4 + 4 - width;
if (offsetDst == 4)
offsetDst = 0;
int strideDst = width + offsetDst;
int num = strideDst * height;
IntPtr intptrbuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(num);
BitmapData dataScr = ImageScr.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, ImageScr.PixelFormat);
int offsetScr = dataScr.Stride - width * 3;
unsafe
{
byte* ptrScr = (byte*)dataScr.Scan0.ToPointer();
byte* ptrDst = (byte*)intptrbuffer.ToPointer();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
*ptrDst = (byte)(0.114 * (*ptrScr) + 0.587 * (*(ptrScr + 1)) + 0.299 * (*(ptrScr + 2)));
ptrDst++;
ptrScr += 3;
}
ptrDst += offsetDst;
ptrScr += offsetScr;
}
}
ImageScr.UnlockBits(dataScr);
if (ImageDst != null)
{
ImageDst.Dispose();//释放图像资源
}
imageDst= new Bitmap(width, height, strideDst,PixelFormat.Format8bppIndexed, intptrbuffer);
//System.Runtime.InteropServices.Marshal.FreeHGlobal(intptrbuffer);//释放intptrbuffer内存,
//设置调色板
ColorPalette palette = ImageDst.Palette;
for (int i = 0; i < palette.Entries.Length; i++)
palette.Entries[i] = Color.FromArgb(255, i, i, i);
ImageDst.Palette = palette;
}个人理解上新的bitmap图像应该把intptr占用了,但是重复调用该段代码时,由于ImageDst是上次调用该函数通过intptr产生的图像,但通过 ImageDst.Dispose()释放图像资源时,intptr的内存并没有释放掉其实定义一个byte[] buffer=new byte[num]来代替intptr作为中转缓存也是没有问题的,但是性能上差了将近20%的样子,个人比较好奇的是,通过intptr的形式在c#中该如何实现这个功能,毕竟性能差的挺多的
{
var dst = new Bitmap(src.Width, src.Height);
using (var g = Graphics.FromImage(dst))
{
var ias = new ImageAttributes();
var m = new ColorMatrix();
m[0, 0] = m[0, 1] = m[0, 2] = 0.299f;
m[1, 0] = m[1, 1] = m[1, 2] = 0.587f;
m[2, 0] = m[2, 1] = m[2, 2] = 0.114f;
ias.SetColorMatrix(m);
g.DrawImage(src, new Rectangle(Point.Empty, src.Size), 0, 0, src.Width, src.Height, GraphicsUnit.Pixel, ias);
}
return dst;
}