#if WINDOWS && (NET48 || NETCOREAPP3_1_OR_GREATER)
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using PixelFormat = System.Windows.Media.PixelFormat;
namespace OpenCvSharp.WpfExtensions;
///
/// Static class which provides conversion between System.Windows.Media.Imaging.BitmapSource and IplImage
///
public static class BitmapSourceConverter
{
///
/// Converts Mat to BitmapSource.
///
/// Input IplImage
/// BitmapSource
public static BitmapSource ToBitmapSource(
this Mat src) =>
src.ToWriteableBitmap();
///
/// Converts Mat to BitmapSource.
///
/// Input IplImage
///
///
///
///
/// BitmapSource
public static BitmapSource ToBitmapSource(
this Mat src,
int horizontalResolution,
int verticalResolution,
PixelFormat pixelFormat,
BitmapPalette palette) =>
src.ToWriteableBitmap(horizontalResolution, verticalResolution, pixelFormat, palette);
///
/// Converts System.Drawing.Bitmap to BitmapSource.
///
/// Input System.Drawing.Bitmap
/// http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource
/// BitmapSource
public static BitmapSource ToBitmapSource(this Bitmap src)
{
if (src is null)
throw new ArgumentNullException(nameof(src));
if (Application.Current?.Dispatcher is null)
{
using var memoryStream = new MemoryStream();
src.Save(memoryStream, ImageFormat.Png);
return CreateBitmapSourceFromBitmap(memoryStream);
}
using (var memoryStream = new MemoryStream())
{
// You need to specify the image format to fill the stream.
// I'm assuming it is PNG
src.Save(memoryStream, ImageFormat.Png);
memoryStream.Seek(0, SeekOrigin.Begin);
// Make sure to create the bitmap in the UI thread
if (IsInvokeRequired())
return (BitmapSource) Application.Current.Dispatcher.Invoke(
new Func(CreateBitmapSourceFromBitmap),
DispatcherPriority.Normal,
memoryStream);
return CreateBitmapSourceFromBitmap(memoryStream);
}
}
// http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource
private static bool IsInvokeRequired()
=> Dispatcher.CurrentDispatcher != Application.Current.Dispatcher;
// http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource
private static BitmapSource CreateBitmapSourceFromBitmap(Stream stream)
{
var bitmapDecoder = BitmapDecoder.Create(
stream,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.OnLoad);
// This will disconnect the stream from the image completely...
var writable = new WriteableBitmap(bitmapDecoder.Frames.Single());
writable.Freeze();
return writable;
}
#region ToMat
///
/// Converts BitmapSource to Mat
///
/// Input BitmapSource
/// IplImage
public static Mat ToMat(this BitmapSource src)
{
if (src is null)
{
throw new ArgumentNullException(nameof(src));
}
int w = src.PixelWidth;
int h = src.PixelHeight;
MatType type = WriteableBitmapConverter.GetOptimumType(src.Format);
Mat dst = new Mat(h, w, type);
ToMat(src, dst);
return dst;
}
///
/// Converts BitmapSource to Mat
///
/// Input BitmapSource
/// Output Mat
public static void ToMat(this BitmapSource src, Mat dst)
{
if (src is null)
throw new ArgumentNullException(nameof(src));
if (dst is null)
throw new ArgumentNullException(nameof(dst));
if (src.PixelWidth != dst.Width || src.PixelHeight != dst.Height)
throw new ArgumentException("size of src must be equal to size of dst");
if (dst.Dims > 2)
throw new ArgumentException("Mat dimensions must be 2");
int w = src.PixelWidth;
int h = src.PixelHeight;
int bpp = src.Format.BitsPerPixel;
int channels = WriteableBitmapConverter.GetOptimumChannels(src.Format);
if (dst.Channels() != channels)
{
throw new ArgumentException("nChannels of dst is invalid", nameof(dst));
}
bool submat = dst.IsSubmatrix();
bool continuous = dst.IsContinuous();
unsafe
{
byte* p = (byte*)(dst.Data);
long step = dst.Step();
// 1bppは手作業でコピー
if (bpp == 1)
{
if (submat)
throw new NotImplementedException("submatrix not supported");
// BitmapImageのデータを配列にコピー
// 要素1つに横8ピクセル分のデータが入っている。
int stride = (w / 8) + 1;
byte[] pixels = new byte[h * stride];
src.CopyPixels(pixels, stride, 0);
int x = 0;
for (int y = 0; y < h; y++)
{
int offset = y * stride;
// この行の各バイトを調べていく
for (int bytePos = 0; bytePos < stride; bytePos++)
{
if (x < w)
{
// 現在の位置のバイトからそれぞれのビット8つを取り出す
byte b = pixels[offset + bytePos];
for (int i = 0; i < 8; i++)
{
if (x >= w)
{
break;
}
p[step * y + x] = ((b & 0x80) == 0x80) ? (byte)255 : (byte)0;
b <<= 1;
x++;
}
}
}
// 次の行へ
x = 0;
}
}
// 8bpp
/*else if (bpp == 8)
{
int stride = w;
byte[] pixels = new byte[h * stride];
src.CopyPixels(pixels, stride, 0);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
p[step * y + x] = pixels[y * stride + x];
}
}
}*/
// 24bpp, 32bpp, ...
else
{
int stride = w * ((bpp + 7) / 8);
if (!submat && continuous)
{
long imageSize = dst.DataEnd.ToInt64() - dst.Data.ToInt64();
if (imageSize < 0)
throw new OpenCvSharpException("The mat has invalid data pointer");
if (imageSize > int.MaxValue)
throw new OpenCvSharpException("Too big mat data");
src.CopyPixels(Int32Rect.Empty, dst.Data, (int)imageSize, stride);
}
else
{
// 高さ1pxの矩形ごと(≒1行ごと)にコピー
var roi = new Int32Rect { X = 0, Y = 0, Width = w, Height = 1 };
IntPtr dstData = dst.Data;
for (int y = 0; y < h; y++)
{
roi.Y = y;
src.CopyPixels(roi, dstData, stride, stride);
dstData = new IntPtr(dstData.ToInt64() + stride);
}
}
}
}
}
///
/// Copies pixel data from System.Windows.Media.Imaging.BitmapSource to IplImage instance
///
///
///
///
public static void CopyFrom(this Mat mat, BitmapSource wb)
{
if (wb is null)
throw new ArgumentNullException(nameof(wb));
ToMat(wb, mat);
}
#endregion
}
#endif