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

发布于 2024-11-02 08:35:56 字数 1547 浏览 5 评论 0原文

我目前正在尝试使用 writeablebitmap 来获取图像扫描的 IntPtr 并将每个图像转换为 Bitmap。我想使用 writeablebitmap 因为我对标准 gdi 有问题 GDI+ System.Drawing.Bitmap 给出错误参数是间歇性无效

WriteableBitmap 上有一个名为 WritePixels 的方法 http://msdn.microsoft.com/en-us/library/aa346817。 我不确定我为缓冲区

和步幅设置了什么,我发现它显示步幅为 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;

Im currently trying to use writeablebitmap to take a IntPtr of a scan of images and turn each one into a Bitmap. Im wanting to use writeablebitmap because im having an issue with standard gdi
GDI+ System.Drawing.Bitmap gives error Parameter is not valid intermittently

There is a method on a WriteableBitmap that called WritePixels
http://msdn.microsoft.com/en-us/library/aa346817.aspx

Im not sure what I set for the buffer and the stride every example I find it shows the stride as 0 although that throws an error. When I set the stride to 5 the image appear black. I know this may not be the most efficient code but any help would be appreciated.

//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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柠北森屋 2024-11-09 08:35:56

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

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。

An efficient way to work on Bitmaps in C# is to pass temporarily in unsafe mode (I know I don't answer the question exactly but I think the OP did not manage to use Bitmap, so this could be a solution anyway). You just have to lock bits and you're done:

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");
    }
}

This is really much more efficient than SetPixel(i, j), you just have to be careful about pointer limits (and not forget to unlock data when you're done).

Now to answer your question about stride: the stride is the length in bytes of a line, it is a multiple of 4. In my exemple I use the format Format32bppArgb which uses 4 bytes per pixel (R, G, B and alpha), so newImageData.Stride and newImageData.Width * 4 are always the same. I use the offset in my loops only to show where it would be necessary.

But if you use another format, for instance Format24bppRgb which uses 3 bytes per pixel (R, G and B only), then there may be an offset between stride and width. For an image 10 * 10 pixels in this format, you will have a stride of 10 * 3 = 30, + 2 to reach nearest multiple of 4, i.e. 32.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文