#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