C#(WPF)-图片相关数据类型之间转换
常见的图片数据类型:
- **byte[ ]:**字节数组,可能有两种情况:1.将图片文件读取到内存后得到的字节数组,包括图片文件的文件头等 。2.图片的像素数据,比如 RGB 数据。
- **Stream:**数据流,比如 MemoryStream、FileStream等,一般和字节数组可以轻易地转换。
- **Bitmap:**WinForm 中的图片数据类型(基于 GDI +),命名空间是 System.Drawing。
- **BitmapImage:**WPF中的图片数据类型,命名空间是 System.Windows.Media.Imaging,常用于 Image 控件的 Source 属性 (是 ImageSource 类型)
- **BitmapSoure:**WPF中的图片数据类型,命名空间是 System.Windows.Media,是 BitmapImage 的基类。
- 其它一些来自第三方库的图片类型。
将图片文件路径转为 BitmapImage
如果知道图片的链接(可以是本地链接或网址),并且想让 Image 控件显示这个图片:
var image = new Image();
image.Source = new BitmapImage(new Uri(@"path\to\image.jpg",UriKind.RelativeOrAbsolute));不需要指定图片的格式,因为 BitmapImage 和 BitmapDecoder 都会自动进行处理。
对于大多数常见的图片格式(如JPG,PNG,BMP,GIF,TIFF,WebP,HEIC,AVIF等),这几种工作方式都能正常工作。
如果没有图片的路径,只有它被读进内存后的数据类型,那么就需要下面的几种方式。
Bitmap 转为 BitmapImage
System.Drawing.Bitmap 和 System.Windows.Media.Imageing.BitmapImage 是两个常见的图片数据类型。
System.Drawing.Bitmap 是 WinDForms 中的类型(GDI+)。
System.Windows.Media.Imageing.BitmapImage 是 WPF 的类型。
转换方法:
using System.Drawing;
using System.Windows.Media.Imaging;
static BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
{
using var stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapIamge.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze(); // (可选)冻结图片,提高性能和线程安全性
return bitmapImage;
}字节数组转为 ImageSource
1.如果字节数组只是读进内存的图片文件数据,比如一个本地的JPG、PNG、BMP等格式的文件:
using System.IO;
using System.Windows.Media.Imaging;
static ImageSource ConvertByteArrayToImageSource(byte[] bytes)
{
using var stream = new MemoryStream(bytes);
var bitmapImage = new BitmapImage();
bitmapIamge.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage();
}还有一种更简单的方式:直接使用 BitmapDecoder 类:
using System.Windows.Media.Imaging;
static ImageSource ConvertByteArrayToIamgeSource(byte[] bytes)
{
using var stream = new MemoryStream(bytes);
return BitmapDecoder
.Create(stream, BitmapCreateOptions.PreservePixelFormat,BitmapCacheOption.OnLoad)
.Frames[0];
}2.如果字节数组是图片的像素数据,比如从左上到右下的逐行 RGB 数据,那么会麻烦一些,而且需要有办法知道图片的宽高等信息:
using System.Windows.Media;
using System.Windows.Media.Imaging;
static ImageSource BgrByteArrayToImageSource(byte[] array, int width, int height, int channel = 3, int? stride = null)
{
var bmp = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, null);
stride ??= ((width * channel + 3) / 4) * 4;
bmp.WritePixels(new Int32Rect(0, 0, width, height), array, stride.Value, 0);
bmp.Freeze();
return bmp;
}BitmapSource 转为 BitmapImage
这两个类有继承关系,BitmapImage 继承自 BitmapSource。
但一般我们仍然需要进行一个"转换",因为通常的使用场景是,我们从 WPF 提供的剪贴板 API 中获取一个 BitmapSource,但经过简单的处理,将它转为 BitmapImage 从而添加给 Image 控件。
using System.Windows.Media.Imaging;
static BitmapImage ConvertBitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
var bitmapImage = new BitmapImage();
using var stream = new MemoryStream();
BitmapEncoder encoder = new BmpBitmapEncoder(); // 一般情况下,剪贴板中的图片数据是 BMP 格式的,而非 PNG 格式
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(stream);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}Emgu.CV.Image 转为 BitmapImage
BitmapImage 支持绝大多数常见的图片格式。但如果现在我们有一个不常见的格式,比如 JP2(JPEG 2000)格式,那么 BitmapImage 就无法直接处理了。这时候我们可以使用 Emgu.CV 库,它是 OpenCV 的 .NET 封装,支持更多的图片格式。下面给出一种方式:
var filename = @"path\to\image.jp2";
var mat = new Image<Bgr, Byte>(filename);
var bytes = mat.ToJpegData();
using var stream = new MemoryStream(bytes);
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
var control = new Image();
control.Source = bitmap;
control.Dump();总结
对于大多数情况,我们都要先将数据转为持有常见图像类型的 Stream,然后再创建 BitmapImage,最后将其赋给 Image 控件。