将 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,反之亦然的主要内容,如果未能解决你的问题,请参考以下文章
从 System.Drawing.Bitmap 加载 WPF BitmapImage
在UWP 将BitmapImage转换为 WriteableBitmap