【问题标题】:Converting BitmapImage to Bitmap and vice versa将 BitmapImage 转换为 Bitmap,反之亦然
【发布时间】:2011-09-22 23:37:09
【问题描述】:

我在 C# 中有 BitmapImage。我需要对图像进行操作。例如灰度化、在图像上添加文字等。

我在 stackoverflow 中找到了接受 Bitmap 并返回 Bitmap 的灰度化函数。

所以我需要将BitmapImage转换为Bitmap,进行操作再转换回来。

我该怎么做?这是最好的方法吗?

【问题讨论】:

  • 如果您不想在内存中创建副本,那么您想要的就是 sharedbitmapsource。 stackoverflow.com/a/32841840/690656

标签: c# .net bitmap


【解决方案1】:

无需使用国外库。

将 BitmapImage 转换为 Bitmap:

private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
    // BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));

    using(MemoryStream outStream = new MemoryStream())
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapImage));
        enc.Save(outStream);
        System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);

        return new Bitmap(bitmap);
    }
}

将位图转换回位图图像:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);

private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
    IntPtr hBitmap = bitmap.GetHbitmap();
    BitmapImage retval;

    try
    {
        retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap(
                     hBitmap,
                     IntPtr.Zero,
                     Int32Rect.Empty,
                     BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(hBitmap);
    }

    return retval;
}

【讨论】:

  • Bitmap2BitmapImage 更简洁的实现是:return Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
  • @BVB:确实需要释放句柄。我修好了它。它是我脑子里想出来的,所以我不保证任何事情:)
  • @Jeff:处理任何东西都没有问题。注释实际上是注释掉的“返回位图”;另外,为什么它什么都不做? Afaik,MemoryStream 实现了 IDisposable,因此应该被释放。
  • @SaschaHennig: 在BitmapImage2Bitmap: 在最后一行,为什么return new Bitmap(bitmap) 而不是return bitmap
  • Bitmap2BitmapImage 函数对我不起作用。我得到 System.InvalidCastException Unable to cast object of type 'System.Windows.Interop.InteropBitmap' to type 'System.Windows.Media.Imaging.BitmapImage'
【解决方案2】:

这是一个将 Bitmap 转换为 BitmapImage 的扩展方法。

    public static BitmapImage ToBitmapImage(this Bitmap bitmap)
    {
        using (var memory = new MemoryStream())
        {
            bitmap.Save(memory, ImageFormat.Png);
            memory.Position = 0;

            var bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.Freeze();

            return bitmapImage;
        }
    }

【讨论】:

  • 我更喜欢这个,因为它不涉及 PInvoke ...太棒了
  • bitmapImage.EndInit(); 之后,调用bitmapImage.Freeze(); 以避免在与GUI 不同的线程上执行此操作时出错。
  • MemoryStream 版本比 Imaging.CreateBitmapSourceFromHBitmap 版本慢 10 倍。
【解决方案3】:

如果您只需要从 BitmapImage 转到 Bitmap,这很容易,

private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
    {
        return new Bitmap(bitmapImage.StreamSource);
    }

【讨论】:

  • 也许不是?如果图像有 Uri 源,那么 StreamSource 肯定为空吗?
【解决方案4】:

使用 System.Windows.Interop; ...

 private BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
        {                
            BitmapSource i = Imaging.CreateBitmapSourceFromHBitmap(
                           bitmap.GetHbitmap(),
                           IntPtr.Zero,
                           Int32Rect.Empty,
                           BitmapSizeOptions.FromEmptyOptions());
            return (BitmapImage)i;
        }

【讨论】:

  • 也许我错过了一些东西,但您不能将“System.Windows.Interop.InteropBitmap”转换为“System.Windows.Media.Imaging.BitmapImage i>”基于弹出的InvalidCastException
  • @WiiMaxx 它确实会引发异常,但如果您想转换 System.Drawing.Image 以便能够在 WPF Image 控件中显示它,您可以返回 BitmapSource 而不是 BitmapImage 和移除演员表。这样就可以完美运行了。
  • 不返回图片只返回BitmapSource
  • 这个 CreateBitmapSourceFromHBitmap 版本比 MemoryStream 版本快 10 倍。
  • 虽然这确实有效,但它存在内存泄漏。使用上面列出的“Sascha Hennig”解决方案,因为它们会正确处理非托管内存。
【解决方案5】:

我刚刚尝试在我的代码中使用上述内容,我认为 Bitmap2BitmapImage 函数(可能还有另一个函数)存在问题。

using (MemoryStream ms = new MemoryStream())

上面的行会导致流被处理掉吗?这意味着返回的 BitmapImage 会丢失其内容。

由于我是 WPF 新手,我不确定这是否是正确的技术解释,但在我删除 using 指令之前,该代码无法在我的应用程序中运行。

【讨论】:

  • 使用“使用”块确实会释放对象。这就是 using 块的用途。一旦不再需要它们,您确实确实想处理这些流。因此,我的帖子中的编辑就像 2 年前一样。 BitmapImage2Bitmap-Fix 在关闭流之前会创建一个 Bitmap() 类型的新对象。对于 Bitmap2BitmapImage(),您想尝试作为评论发布的实现,以评论 Steven 发布的我的答案。基本上你想要做的是从流中的数据创建一个新对象,关闭流并返回创建的对象。
【解决方案6】:

这里是异步版本。

public static Task<BitmapSource> ToBitmapSourceAsync(this Bitmap bitmap)
{
    return Task.Run(() =>
    {
        using (System.IO.MemoryStream memory = new System.IO.MemoryStream())
        {
            bitmap.Save(memory, ImageFormat.Png);
            memory.Position = 0;
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = memory;
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.Freeze();
            return bitmapImage as BitmapSource;
        }
    });

}

【讨论】:

  • 这是一个很好的解决方案,非常适合我的情况。
【解决方案7】:

感谢 Guillermo Hernandez,我为您的代码创建了一个可以运行的变体。我在这段代码中添加了命名空间以供参考。

System.Reflection.Assembly theAsm = Assembly.LoadFrom("My.dll");
// Get a stream to the embedded resource
System.IO.Stream stream = theAsm.GetManifestResourceStream(@"picture.png");

// Here is the most important part:
System.Windows.Media.Imaging.BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.StreamSource = stream;
bmi.EndInit();

【讨论】:

    【解决方案8】:

    这会将 System.Drawing.Bitmap 转换为 BitmapImage:

    MemoryStream ms = new MemoryStream();
    YOURBITMAP.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
    BitmapImage image = new BitmapImage();
    image.BeginInit();
    ms.Seek(0, SeekOrigin.Begin);
    image.StreamSource = ms;
    image.EndInit();
    

    【讨论】:

    • BMP 格式不能正确处理透明度。 PNG更好。
    猜你喜欢
    • 1970-01-01
    • 2021-10-20
    • 2010-12-03
    • 2011-10-08
    • 2013-03-15
    • 2019-09-19
    • 2013-12-29
    • 2012-05-14
    • 2020-03-01
    相关资源
    最近更新 更多