BitmapSourceConverter.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #if WINDOWS && (NET48 || NETCOREAPP3_1_OR_GREATER)
  2. using System.Drawing;
  3. using System.Drawing.Imaging;
  4. using System.IO;
  5. using System.Windows;
  6. using System.Windows.Media.Imaging;
  7. using System.Windows.Threading;
  8. using PixelFormat = System.Windows.Media.PixelFormat;
  9. namespace OpenCvSharp.WpfExtensions;
  10. /// <summary>
  11. /// Static class which provides conversion between System.Windows.Media.Imaging.BitmapSource and IplImage
  12. /// </summary>
  13. public static class BitmapSourceConverter
  14. {
  15. /// <summary>
  16. /// Converts Mat to BitmapSource.
  17. /// </summary>
  18. /// <param name="src">Input IplImage</param>
  19. /// <returns>BitmapSource</returns>
  20. public static BitmapSource ToBitmapSource(
  21. this Mat src) =>
  22. src.ToWriteableBitmap();
  23. /// <summary>
  24. /// Converts Mat to BitmapSource.
  25. /// </summary>
  26. /// <param name="src">Input IplImage</param>
  27. /// <param name="horizontalResolution"></param>
  28. /// <param name="verticalResolution"></param>
  29. /// <param name="pixelFormat"></param>
  30. /// <param name="palette"></param>
  31. /// <returns>BitmapSource</returns>
  32. public static BitmapSource ToBitmapSource(
  33. this Mat src,
  34. int horizontalResolution,
  35. int verticalResolution,
  36. PixelFormat pixelFormat,
  37. BitmapPalette palette) =>
  38. src.ToWriteableBitmap(horizontalResolution, verticalResolution, pixelFormat, palette);
  39. /// <summary>
  40. /// Converts System.Drawing.Bitmap to BitmapSource.
  41. /// </summary>
  42. /// <param name="src">Input System.Drawing.Bitmap</param>
  43. /// <remarks>http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource</remarks>
  44. /// <returns>BitmapSource</returns>
  45. public static BitmapSource ToBitmapSource(this Bitmap src)
  46. {
  47. if (src is null)
  48. throw new ArgumentNullException(nameof(src));
  49. if (Application.Current?.Dispatcher is null)
  50. {
  51. using var memoryStream = new MemoryStream();
  52. src.Save(memoryStream, ImageFormat.Png);
  53. return CreateBitmapSourceFromBitmap(memoryStream);
  54. }
  55. using (var memoryStream = new MemoryStream())
  56. {
  57. // You need to specify the image format to fill the stream.
  58. // I'm assuming it is PNG
  59. src.Save(memoryStream, ImageFormat.Png);
  60. memoryStream.Seek(0, SeekOrigin.Begin);
  61. // Make sure to create the bitmap in the UI thread
  62. if (IsInvokeRequired())
  63. return (BitmapSource) Application.Current.Dispatcher.Invoke(
  64. new Func<Stream, BitmapSource>(CreateBitmapSourceFromBitmap),
  65. DispatcherPriority.Normal,
  66. memoryStream);
  67. return CreateBitmapSourceFromBitmap(memoryStream);
  68. }
  69. }
  70. // http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource
  71. private static bool IsInvokeRequired()
  72. => Dispatcher.CurrentDispatcher != Application.Current.Dispatcher;
  73. // http://www.codeproject.com/Articles/104929/Bitmap-to-BitmapSource
  74. private static BitmapSource CreateBitmapSourceFromBitmap(Stream stream)
  75. {
  76. var bitmapDecoder = BitmapDecoder.Create(
  77. stream,
  78. BitmapCreateOptions.PreservePixelFormat,
  79. BitmapCacheOption.OnLoad);
  80. // This will disconnect the stream from the image completely...
  81. var writable = new WriteableBitmap(bitmapDecoder.Frames.Single());
  82. writable.Freeze();
  83. return writable;
  84. }
  85. #region ToMat
  86. /// <summary>
  87. /// Converts BitmapSource to Mat
  88. /// </summary>
  89. /// <param name="src">Input BitmapSource</param>
  90. /// <returns>IplImage</returns>
  91. public static Mat ToMat(this BitmapSource src)
  92. {
  93. if (src is null)
  94. {
  95. throw new ArgumentNullException(nameof(src));
  96. }
  97. int w = src.PixelWidth;
  98. int h = src.PixelHeight;
  99. MatType type = WriteableBitmapConverter.GetOptimumType(src.Format);
  100. Mat dst = new Mat(h, w, type);
  101. ToMat(src, dst);
  102. return dst;
  103. }
  104. /// <summary>
  105. /// Converts BitmapSource to Mat
  106. /// </summary>
  107. /// <param name="src">Input BitmapSource</param>
  108. /// <param name="dst">Output Mat</param>
  109. public static void ToMat(this BitmapSource src, Mat dst)
  110. {
  111. if (src is null)
  112. throw new ArgumentNullException(nameof(src));
  113. if (dst is null)
  114. throw new ArgumentNullException(nameof(dst));
  115. if (src.PixelWidth != dst.Width || src.PixelHeight != dst.Height)
  116. throw new ArgumentException("size of src must be equal to size of dst");
  117. if (dst.Dims > 2)
  118. throw new ArgumentException("Mat dimensions must be 2");
  119. int w = src.PixelWidth;
  120. int h = src.PixelHeight;
  121. int bpp = src.Format.BitsPerPixel;
  122. int channels = WriteableBitmapConverter.GetOptimumChannels(src.Format);
  123. if (dst.Channels() != channels)
  124. {
  125. throw new ArgumentException("nChannels of dst is invalid", nameof(dst));
  126. }
  127. bool submat = dst.IsSubmatrix();
  128. bool continuous = dst.IsContinuous();
  129. unsafe
  130. {
  131. byte* p = (byte*)(dst.Data);
  132. long step = dst.Step();
  133. // 1bppは手作業でコピー
  134. if (bpp == 1)
  135. {
  136. if (submat)
  137. throw new NotImplementedException("submatrix not supported");
  138. // BitmapImageのデータを配列にコピー
  139. // 要素1つに横8ピクセル分のデータが入っている。
  140. int stride = (w / 8) + 1;
  141. byte[] pixels = new byte[h * stride];
  142. src.CopyPixels(pixels, stride, 0);
  143. int x = 0;
  144. for (int y = 0; y < h; y++)
  145. {
  146. int offset = y * stride;
  147. // この行の各バイトを調べていく
  148. for (int bytePos = 0; bytePos < stride; bytePos++)
  149. {
  150. if (x < w)
  151. {
  152. // 現在の位置のバイトからそれぞれのビット8つを取り出す
  153. byte b = pixels[offset + bytePos];
  154. for (int i = 0; i < 8; i++)
  155. {
  156. if (x >= w)
  157. {
  158. break;
  159. }
  160. p[step * y + x] = ((b & 0x80) == 0x80) ? (byte)255 : (byte)0;
  161. b <<= 1;
  162. x++;
  163. }
  164. }
  165. }
  166. // 次の行へ
  167. x = 0;
  168. }
  169. }
  170. // 8bpp
  171. /*else if (bpp == 8)
  172. {
  173. int stride = w;
  174. byte[] pixels = new byte[h * stride];
  175. src.CopyPixels(pixels, stride, 0);
  176. for (int y = 0; y < h; y++)
  177. {
  178. for (int x = 0; x < w; x++)
  179. {
  180. p[step * y + x] = pixels[y * stride + x];
  181. }
  182. }
  183. }*/
  184. // 24bpp, 32bpp, ...
  185. else
  186. {
  187. int stride = w * ((bpp + 7) / 8);
  188. if (!submat && continuous)
  189. {
  190. long imageSize = dst.DataEnd.ToInt64() - dst.Data.ToInt64();
  191. if (imageSize < 0)
  192. throw new OpenCvSharpException("The mat has invalid data pointer");
  193. if (imageSize > int.MaxValue)
  194. throw new OpenCvSharpException("Too big mat data");
  195. src.CopyPixels(Int32Rect.Empty, dst.Data, (int)imageSize, stride);
  196. }
  197. else
  198. {
  199. // 高さ1pxの矩形ごと(≒1行ごと)にコピー
  200. var roi = new Int32Rect { X = 0, Y = 0, Width = w, Height = 1 };
  201. IntPtr dstData = dst.Data;
  202. for (int y = 0; y < h; y++)
  203. {
  204. roi.Y = y;
  205. src.CopyPixels(roi, dstData, stride, stride);
  206. dstData = new IntPtr(dstData.ToInt64() + stride);
  207. }
  208. }
  209. }
  210. }
  211. }
  212. /// <summary>
  213. /// Copies pixel data from System.Windows.Media.Imaging.BitmapSource to IplImage instance
  214. /// </summary>
  215. /// <param name="mat"></param>
  216. /// <param name="wb"></param>
  217. /// <returns></returns>
  218. public static void CopyFrom(this Mat mat, BitmapSource wb)
  219. {
  220. if (wb is null)
  221. throw new ArgumentNullException(nameof(wb));
  222. ToMat(wb, mat);
  223. }
  224. #endregion
  225. }
  226. #endif