使用来自intptr的可写位图创建位图时使用WritePixels。

时间:2011-04-20 13:24:06

标签: c# image-processing bitmap scanning

我目前正在尝试使用writeablebitmap对图像进行IntPtr扫描,并将每个图像转换为Bitmap。我想使用writeablebitmap因为我有标准gdi的问题 GDI+ System.Drawing.Bitmap gives error Parameter is not valid intermittently

WriteableBitmap上有一种调用WritePixels的方法 http://msdn.microsoft.com/en-us/library/aa346817.aspx

我不确定我为缓冲区和步幅设置的每个示例我发现它显示步幅为0虽然会引发错误。当我将步幅设置为5时,图像显示为黑色。我知道这可能不是最有效的代码,但任何帮助都会受到赞赏。

//create bitmap header
bmi = new BITMAPINFOHEADER();

//create initial rectangle
Int32Rect rect = new Int32Rect(0, 0, 0, 0);

//create duplicate intptr to use while in global lock
dibhand = dibhandp;
bmpptr = GlobalLock(dibhand);

//get the pixel sizes
pixptr = GetPixelInfo(bmpptr);

//create writeable bitmap
var wbitm = new WriteableBitmap(bmprect.Width, bmprect.Height, 96.0, 96.0, System.Windows.Media.PixelFormats.Bgr32, null);

//draw the image
wbitm.WritePixels(rect, dibhandp, 10, 0);

//convert the writeable bitmap to bitmap
var stream = new MemoryStream();

var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbitm));
encoder.Save(stream);

byte[] buffer = stream.GetBuffer();
var bitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));

GlobalUnlock(dibhand);
GlobalFree(dibhand);
GlobalFree(dibhandp);
GlobalFree(bmpptr);
dibhand = IntPtr.Zero;

return bitmap;

1 个答案:

答案 0 :(得分:2)

在C#中处理位图的一种有效方法是暂时以不安全模式传递(我知道我没有完全回答这个问题,但我认为OP没有设法使用Bitmap,所以这可能是一个解决方案) 。你只需要锁定位就完成了:

unsafe private void GaussianFilter()
{
    // Working images
    using (Bitmap newImage = new Bitmap(width, height))
    {
        // Lock bits for performance reason
        BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, newImage.Width,
            newImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        byte* pointer = (byte*)newImageData.Scan0;
        int offset = newImageData.Stride - newImageData.Width * 4;

        // Compute gaussian filter on temp image
        for (int j = 0; j < InputData.Height - 1; ++j)
        {
            for (int 0 = 1; i < InputData.Width - 1; ++i)
            {
                // You browse 4 bytes per 4 bytes
                // The 4 bytes are: B G R A
                byte blue = pointer[0];
                byte green = pointer[1];
                byte red = pointer[2];
                byte alpha = pointer[3];

                // Your business here by setting pointer[i] = ...
                // If you don't use alpha don't forget to set it to 255 else your whole image will be black !!

                // Go to next pixels
                pointer += 4;
            }
            // Go to next line: do not forget pixel at last and first column
            pointer += offset;
        }


        // Unlock image
        newImage.UnlockBits(newImageData);
        newImage.Save("D:\temp\OCR_gray_gaussian.tif");
    }
}

这比SetPixel(i, j)更有效率,您只需要注意指针限制(并且在完成后不要忘记解锁数据)。

现在回答你关于步幅的问题:步幅是一行的字节长度,它是4的倍数。在我的例子中,我使用格式Format32bppArgb,每个像素使用4个字节(R, G,B和alpha),因此newImageData.StridenewImageData.Width * 4始终相同。我只在循环中使用偏移来显示它的必要位置。

但是如果你使用另一种格式,例如Format24bppRgb每个像素使用3个字节(仅R,G和B),那么步幅和宽度之间可能存在偏移。对于此格式的10 * 10像素图像,您将获得10 * 3 = 30,+ 2的步幅,以达到4的最接近倍数,即32。