Tools.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. using OpenCvSharp;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. using System.Linq;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace SmartCoalApplication.Core
  11. {
  12. /// <summary>
  13. /// Bitmap转化
  14. /// </summary>
  15. public static class Tools
  16. {
  17. static Bitmap currBitmap;
  18. #region ToMat
  19. /// <summary>
  20. /// Converts System.Drawing.Bitmap to Mat
  21. /// </summary>
  22. /// <param name="src">System.Drawing.Bitmap object to be converted</param>
  23. /// <returns>A Mat object which is converted from System.Drawing.Bitmap</returns>
  24. public static Mat ToMat(this Bitmap src)
  25. {
  26. if (src == null)
  27. throw new ArgumentNullException(nameof(src));
  28. int w = src.Width;
  29. int h = src.Height;
  30. int channels;
  31. switch (src.PixelFormat)
  32. {
  33. case PixelFormat.Format24bppRgb:
  34. case PixelFormat.Format32bppRgb:
  35. channels = 3; break;
  36. case PixelFormat.Format32bppArgb:
  37. case PixelFormat.Format32bppPArgb:
  38. channels = 4; break;
  39. case PixelFormat.Format8bppIndexed:
  40. case PixelFormat.Format1bppIndexed:
  41. channels = 1; break;
  42. default:
  43. throw new NotImplementedException();
  44. }
  45. Mat dst = new Mat(h, w, MatType.CV_8UC(channels));
  46. //return OpenCvSharp.Extensions.BitmapConverter.ToMat(src);
  47. ToMat(src, dst);
  48. return dst;
  49. }
  50. /// <summary>
  51. /// Converts System.Drawing.Bitmap to Mat
  52. /// </summary>
  53. /// <param name="src">System.Drawing.Bitmap object to be converted</param>
  54. /// <param name="dst">A Mat object which is converted from System.Drawing.Bitmap</param>
  55. public static unsafe void ToMat(this Bitmap src, Mat dst)
  56. {
  57. if (src == null)
  58. throw new ArgumentNullException(nameof(src));
  59. if (dst == null)
  60. throw new ArgumentNullException(nameof(dst));
  61. if (dst.IsDisposed)
  62. throw new ArgumentException("The specified dst is disposed.", nameof(dst));
  63. if (dst.Depth() != MatType.CV_8U)
  64. throw new NotSupportedException("Mat depth != CV_8U");
  65. //if (dst.Dims != 2)
  66. // throw new NotSupportedException("Mat dims != 2");
  67. if (src.Width != dst.Width || src.Height != dst.Height)
  68. throw new ArgumentException("src.Size != dst.Size");
  69. int w = src.Width;
  70. int h = src.Height;
  71. Rectangle rect = new Rectangle(0, 0, w, h);
  72. BitmapData bd = null;
  73. try
  74. {
  75. bd = src.LockBits(rect, ImageLockMode.ReadOnly, src.PixelFormat);
  76. switch (src.PixelFormat)
  77. {
  78. case PixelFormat.Format1bppIndexed:
  79. Format1bppIndexed();
  80. break;
  81. case PixelFormat.Format8bppIndexed:
  82. Format8bppIndexed();
  83. break;
  84. case PixelFormat.Format24bppRgb:
  85. Format24bppRgb();
  86. break;
  87. case PixelFormat.Format32bppRgb:
  88. case PixelFormat.Format32bppArgb:
  89. case PixelFormat.Format32bppPArgb:
  90. Format32bppRgb();
  91. break;
  92. }
  93. }
  94. finally
  95. {
  96. if (bd != null)
  97. src.UnlockBits(bd);
  98. }
  99. // ReSharper disable once InconsistentNaming
  100. void Format1bppIndexed()
  101. {
  102. if (dst.Channels() != 1)
  103. throw new ArgumentException("Invalid nChannels");
  104. if (dst.IsSubmatrix())
  105. throw new NotImplementedException("submatrix not supported");
  106. if (bd == null)
  107. throw new NotSupportedException("BitmapData == null (Format1bppIndexed)");
  108. byte* srcPtr = (byte*)bd.Scan0.ToPointer();
  109. byte* dstPtr = dst.DataPointer;
  110. int srcStep = bd.Stride;
  111. uint dstStep = (uint)dst.Step();
  112. int x = 0;
  113. for (int y = 0; y < h; y++)
  114. {
  115. // 横は必ず4byte幅に切り上げられる。
  116. // この行の各バイトを調べていく
  117. for (int bytePos = 0; bytePos < srcStep; bytePos++)
  118. {
  119. if (x < w)
  120. {
  121. // 現在の位置のバイトからそれぞれのビット8つを取り出す
  122. byte b = srcPtr[bytePos];
  123. for (int i = 0; i < 8; i++)
  124. {
  125. if (x >= w)
  126. {
  127. break;
  128. }
  129. // IplImageは8bit/pixel
  130. dstPtr[dstStep * y + x] = ((b & 0x80) == 0x80) ? (byte)255 : (byte)0;
  131. b <<= 1;
  132. x++;
  133. }
  134. }
  135. }
  136. // 次の行へ
  137. x = 0;
  138. srcPtr += srcStep;
  139. }
  140. }
  141. void Ch1(Mat dst1, int height, int srcStep, uint dstStep, IntPtr srcData, byte[] palette)
  142. {
  143. if (dstStep == srcStep && !dst1.IsSubmatrix() && dst1.IsContinuous())
  144. {
  145. // Read Bitmap pixel data to managed array
  146. long length = dst1.DataEnd.ToInt64() - dst1.Data.ToInt64();
  147. if (length > int.MaxValue)
  148. throw new NotSupportedException("Too big dst Mat");
  149. var buffer = new byte[length];
  150. Marshal.Copy(srcData, buffer, 0, buffer.Length);
  151. // Apply conversion by palette
  152. buffer = buffer.Select(b => palette[b]).ToArray();
  153. // Write to dst Mat
  154. Marshal.Copy(buffer, 0, dst1.Data, buffer.Length);
  155. }
  156. else
  157. {
  158. // Copy line bytes from src to dst for each line
  159. byte* sp = (byte*)srcData;
  160. byte* dp = (byte*)dst1.Data;
  161. var buffer = new byte[srcStep];
  162. for (int y = 0; y < height; y++)
  163. {
  164. // Read Bitmap pixel data to managed array
  165. Marshal.Copy(new IntPtr(sp), buffer, 0, buffer.Length);
  166. // Apply conversion by palette
  167. buffer = buffer.Select(b => palette[b]).ToArray();
  168. // Write to dst Mat
  169. Marshal.Copy(buffer, 0, new IntPtr(dp), buffer.Length);
  170. sp += srcStep;
  171. dp += dstStep;
  172. }
  173. }
  174. }
  175. // ReSharper disable once InconsistentNaming
  176. void Format8bppIndexed()
  177. {
  178. int srcStep = bd.Stride;
  179. uint dstStep = (uint)dst.Step();
  180. int channels = dst.Channels();
  181. if (channels == 1)
  182. {
  183. var palette = new byte[256];
  184. var paletteLength = Math.Min(256, src.Palette.Entries.Length);
  185. for (int i = 0; i < paletteLength; i++)
  186. {
  187. // TODO src.Palette.Flags & 2 == 2
  188. // https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.imaging.colorpalette.flags?view=netframework-4.8
  189. palette[i] = src.Palette.Entries[i].R;
  190. }
  191. Ch1(dst, h, srcStep, dstStep, bd.Scan0, palette);
  192. }
  193. else if (channels == 3)
  194. {
  195. // Palette
  196. var paletteR = new byte[256];
  197. var paletteG = new byte[256];
  198. var paletteB = new byte[256];
  199. var paletteLength = Math.Min(256, src.Palette.Entries.Length);
  200. for (int i = 0; i < paletteLength; i++)
  201. {
  202. var c = src.Palette.Entries[i];
  203. paletteR[i] = c.R;
  204. paletteG[i] = c.G;
  205. paletteB[i] = c.B;
  206. }
  207. using (var dstR = new Mat(h, w, MatType.CV_8UC1))
  208. using (var dstG = new Mat(h, w, MatType.CV_8UC1))
  209. using (var dstB = new Mat(h, w, MatType.CV_8UC1))
  210. {
  211. Ch1(dstR, h, srcStep, (uint)dstR.Step(), bd.Scan0, paletteR);
  212. Ch1(dstG, h, srcStep, (uint)dstG.Step(), bd.Scan0, paletteG);
  213. Ch1(dstB, h, srcStep, (uint)dstB.Step(), bd.Scan0, paletteB);
  214. Cv2.Merge(new[] { dstB, dstG, dstR }, dst);
  215. }
  216. }
  217. else
  218. {
  219. throw new ArgumentException($"Invalid channels of dst Mat ({channels})");
  220. }
  221. }
  222. // ReSharper disable once InconsistentNaming
  223. void Format24bppRgb()
  224. {
  225. if (dst.Channels() != 3)
  226. throw new ArgumentException("Invalid nChannels");
  227. if (dst.Depth() != MatType.CV_8U && dst.Depth() != MatType.CV_8S)
  228. throw new ArgumentException("Invalid depth of dst Mat");
  229. int srcStep = bd.Stride;
  230. long dstStep = dst.Step();
  231. if (dstStep == srcStep && !dst.IsSubmatrix() && dst.IsContinuous())
  232. {
  233. IntPtr dstData = dst.Data;
  234. long bytesToCopy = dst.DataEnd.ToInt64() - dstData.ToInt64();
  235. Buffer.MemoryCopy(bd.Scan0.ToPointer(), dstData.ToPointer(), bytesToCopy, bytesToCopy);
  236. }
  237. else
  238. {
  239. // Copy line bytes from src to dst for each line
  240. byte* sp = (byte*)bd.Scan0;
  241. byte* dp = (byte*)dst.Data;
  242. for (int y = 0; y < h; y++)
  243. {
  244. Buffer.MemoryCopy(sp, dp, dstStep, dstStep);
  245. sp += srcStep;
  246. dp += dstStep;
  247. }
  248. }
  249. }
  250. // ReSharper disable once InconsistentNaming
  251. void Format32bppRgb()
  252. {
  253. int srcStep = bd.Stride;
  254. long dstStep = dst.Step();
  255. switch (dst.Channels())
  256. {
  257. case 4:
  258. if (!dst.IsSubmatrix() && dst.IsContinuous())
  259. {
  260. IntPtr dstData = dst.Data;
  261. long bytesToCopy = dst.DataEnd.ToInt64() - dstData.ToInt64();
  262. Buffer.MemoryCopy(bd.Scan0.ToPointer(), dstData.ToPointer(), bytesToCopy, bytesToCopy);
  263. }
  264. else
  265. {
  266. byte* sp = (byte*)bd.Scan0;
  267. byte* dp = (byte*)dst.Data;
  268. for (int y = 0; y < h; y++)
  269. {
  270. Buffer.MemoryCopy(sp, dp, dstStep, dstStep);
  271. sp += srcStep;
  272. dp += dstStep;
  273. }
  274. }
  275. break;
  276. case 3:
  277. byte* srcPtr = (byte*)bd.Scan0.ToPointer();
  278. byte* dstPtr = (byte*)dst.Data.ToPointer();
  279. for (int y = 0; y < h; y++)
  280. {
  281. for (int x = 0; x < w; x++)
  282. {
  283. dstPtr[y * dstStep + x * 3 + 0] = srcPtr[y * srcStep + x * 4 + 0];
  284. dstPtr[y * dstStep + x * 3 + 1] = srcPtr[y * srcStep + x * 4 + 1];
  285. dstPtr[y * dstStep + x * 3 + 2] = srcPtr[y * srcStep + x * 4 + 2];
  286. }
  287. }
  288. break;
  289. default:
  290. throw new ArgumentException("Invalid nChannels");
  291. }
  292. }
  293. }
  294. #endregion
  295. #region ToBitmap
  296. /*
  297. /// <summary>
  298. /// Converts Mat to System.Drawing.Bitmap
  299. /// </summary>
  300. /// <param name="src">Mat</param>
  301. /// <returns></returns>
  302. public static Bitmap ToBitmap(this Mat src)
  303. {
  304. if (src == null)
  305. {
  306. throw new ArgumentNullException(nameof(src));
  307. }
  308. PixelFormat pf;
  309. switch (src.Channels())
  310. {
  311. case 1:
  312. pf = PixelFormat.Format8bppIndexed; break;
  313. case 3:
  314. pf = PixelFormat.Format24bppRgb; break;
  315. case 4:
  316. pf = PixelFormat.Format32bppArgb; break;
  317. default:
  318. throw new ArgumentException("Number of channels must be 1, 3 or 4.", nameof(src));
  319. }
  320. return ToBitmap(src, pf);
  321. }
  322. /// <summary>
  323. /// Converts Mat to System.Drawing.Bitmap
  324. /// </summary>
  325. /// <param name="src">Mat</param>
  326. /// <param name="pf">Pixel Depth</param>
  327. /// <returns></returns>
  328. public static Bitmap ToBitmap(this Mat src, PixelFormat pf)
  329. {
  330. if (src == null)
  331. throw new ArgumentNullException(nameof(src));
  332. src.ThrowIfDisposed();
  333. Bitmap bitmap = new Bitmap(src.Width, src.Height, pf);
  334. ToBitmap(src, bitmap);
  335. return bitmap;
  336. }
  337. /// <summary>
  338. /// Converts Mat to System.Drawing.Bitmap
  339. /// </summary>
  340. /// <param name="src">Mat</param>
  341. /// <param name="dst">Mat</param>
  342. /// <remarks>Author: shimat, Gummo (ROI support)</remarks>
  343. public static unsafe void ToBitmap(this Mat src, Bitmap dst)
  344. {
  345. if (src == null)
  346. throw new ArgumentNullException(nameof(src));
  347. if (dst == null)
  348. throw new ArgumentNullException(nameof(dst));
  349. if (src.IsDisposed)
  350. throw new ArgumentException("The image is disposed.", nameof(src));
  351. if (src.Depth() != MatType.CV_8U)
  352. throw new ArgumentException("Depth of the image must be CV_8U");
  353. //if (src.IsSubmatrix())
  354. // throw new ArgumentException("Submatrix is not supported");
  355. if (src.Width != dst.Width || src.Height != dst.Height)
  356. throw new ArgumentException("");
  357. PixelFormat pf = dst.PixelFormat;
  358. // 1プレーン用の場合、グレースケールのパレット情報を生成する
  359. if (pf == PixelFormat.Format8bppIndexed)
  360. {
  361. ColorPalette plt = dst.Palette;
  362. for (int x = 0; x < 256; x++)
  363. {
  364. plt.Entries[x] = Color.FromArgb(x, x, x);
  365. }
  366. dst.Palette = plt;
  367. }
  368. int w = src.Width;
  369. int h = src.Height;
  370. Rectangle rect = new Rectangle(0, 0, w, h);
  371. BitmapData bd = null;
  372. bool submat = src.IsSubmatrix();
  373. bool continuous = src.IsContinuous();
  374. try
  375. {
  376. bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf);
  377. IntPtr srcData = src.Data;
  378. byte* pSrc = (byte*)(srcData.ToPointer());
  379. byte* pDst = (byte*)(bd.Scan0.ToPointer());
  380. int ch = src.Channels();
  381. int srcStep = (int)src.Step();
  382. int dstStep = ((src.Width * ch) + 3) / 4 * 4; // 4の倍数に揃える
  383. int stride = bd.Stride;
  384. switch (pf)
  385. {
  386. case PixelFormat.Format1bppIndexed:
  387. {
  388. if (submat)
  389. throw new NotImplementedException("submatrix not supported");
  390. // BitmapDataは4byte幅だが、IplImageは1byte幅
  391. // 手作業で移し替える
  392. //int offset = stride - (w / 8);
  393. int x = 0;
  394. byte b = 0;
  395. for (int y = 0; y < h; y++)
  396. {
  397. for (int bytePos = 0; bytePos < stride; bytePos++)
  398. {
  399. if (x < w)
  400. {
  401. for (int i = 0; i < 8; i++)
  402. {
  403. var mask = (byte)(0x80 >> i);
  404. if (x < w && pSrc[srcStep * y + x] == 0)
  405. b &= (byte)(mask ^ 0xff);
  406. else
  407. b |= mask;
  408. x++;
  409. }
  410. pDst[bytePos] = b;
  411. }
  412. }
  413. x = 0;
  414. pDst += stride;
  415. }
  416. break;
  417. }
  418. case PixelFormat.Format8bppIndexed:
  419. case PixelFormat.Format24bppRgb:
  420. case PixelFormat.Format32bppArgb:
  421. if (srcStep == dstStep && !submat && continuous)
  422. {
  423. long bytesToCopy = src.DataEnd.ToInt64() - src.Data.ToInt64();
  424. Buffer.MemoryCopy(pSrc, pDst, bytesToCopy, bytesToCopy);
  425. }
  426. else
  427. {
  428. for (int y = 0; y < h; y++)
  429. {
  430. long offsetSrc = (y * srcStep);
  431. long offsetDst = (y * dstStep);
  432. long bytesToCopy = w * ch;
  433. // 一列ごとにコピー
  434. Buffer.MemoryCopy(pSrc + offsetSrc, pDst + offsetDst, bytesToCopy, bytesToCopy);
  435. }
  436. }
  437. break;
  438. default:
  439. throw new NotImplementedException();
  440. }
  441. }
  442. finally
  443. {
  444. if (bd != null)
  445. dst.UnlockBits(bd);
  446. }
  447. }*/
  448. #endregion
  449. }
  450. }