将 BitmapImage 转换为 Bitmap,反之亦然

Posted

技术标签:

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

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

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

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

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

【问题讨论】:

如果您不想在内存中创建副本,那么您想要的就是 sharedbitmapsource。 ***.com/a/32841840/690656 【参考方案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更好。

以上是关于将 BitmapImage 转换为 Bitmap,反之亦然的主要内容,如果未能解决你的问题,请参考以下文章

C# Bitmap转化为BitmapImage方法

从 System.Drawing.Bitmap 加载 WPF BitmapImage

在UWP 将BitmapImage转换为 WriteableBitmap

c# image转bitmap出现错误

Windows 8 如何将 BitmapImage 作为流打开?

图片Bitmap在本地的存储与读取 File