最近用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.   

    如果灰度图像是用来显示的,就不用麻烦索引色(显示的时候内部还要转换回去)。还可能会更快一些。public static Bitmap ToGrayscale(Bitmap src)
    {
        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;
    }