ImageUtils.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. using System;
  2. using System.Runtime.InteropServices;
  3. namespace YOLODetectProcessLib
  4. {
  5. /// <summary>
  6. /// cpp dll 中图像信息的数据结构
  7. /// </summary>
  8. public struct StructImageInfo
  9. {
  10. public int width;
  11. public int height;
  12. public int channel;
  13. public IntPtr pData;
  14. }
  15. /// <summary>
  16. /// cpp dll 中矩形框的数据结构
  17. /// </summary>
  18. public struct StructMyRect
  19. {
  20. public int left;
  21. public int top;
  22. public int width;
  23. public int height;
  24. }
  25. /// <summary>
  26. /// cpp dll 中提取前处理数据时所需信息的数据结构
  27. /// </summary>
  28. public struct StructExtractInfo
  29. {
  30. public int meanValueType;
  31. public float meanR;
  32. public float meanG;
  33. public float meanB;
  34. public int scaleValueType;
  35. public float scaleR;
  36. public float scaleG;
  37. public float scaleB;
  38. public int normalizationType;
  39. public bool reverseInputChannels;
  40. public int axisOrder;
  41. }
  42. /// <summary>
  43. /// 图像处理过程中的一些辅助的功能函数
  44. /// </summary>
  45. public class ImageUtils
  46. {
  47. #region dll import
  48. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ResizeBilinear", CallingConvention = CallingConvention.Cdecl)]
  49. public static extern int ResizeBilinear(StructImageInfo origImg, ref StructImageInfo dstImg);
  50. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "FillWithPadValue", CallingConvention = CallingConvention.Cdecl)]
  51. public static extern int FillWithPadValue(ref StructImageInfo origImg, IntPtr padValue);
  52. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "CropRect", CallingConvention = CallingConvention.Cdecl)]
  53. public static extern int CropRect(StructImageInfo origImg, StructMyRect cropRect, ref StructImageInfo dstImg);
  54. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "PasteRect", CallingConvention = CallingConvention.Cdecl)]
  55. public static extern int PasteRect(StructImageInfo srcImg, StructMyRect pasteRect, ref StructImageInfo dstImg);
  56. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsGray", CallingConvention = CallingConvention.Cdecl)]
  57. public static extern int ExtractRGB24AsGray(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo);
  58. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsColor", CallingConvention = CallingConvention.Cdecl)]
  59. public static extern int ExtractRGB24AsColor(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo);
  60. #endregion
  61. #region public
  62. public static IImage ResizeBilinearWithCpp(IImage image, int dstWidth, int dstHeight)
  63. {
  64. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  65. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  66. StructImageInfo origImgInfo = new StructImageInfo
  67. {
  68. width = image.Width,
  69. height = image.Height,
  70. channel = image.Channel,
  71. pData = pDataOrig,
  72. };
  73. RawImage resizedImg = new RawImage(new byte[dstWidth * dstHeight * image.Channel], dstWidth, dstHeight,
  74. image.Channel, dstWidth * image.Channel);
  75. GCHandle hObjectDst = GCHandle.Alloc(resizedImg.DataBuffer, GCHandleType.Pinned);
  76. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  77. StructImageInfo dstImgInfo = new StructImageInfo
  78. {
  79. width = resizedImg.Width,
  80. height = resizedImg.Height,
  81. channel = resizedImg.Channel,
  82. pData = pDataDst,
  83. };
  84. var ret = ResizeBilinear(origImgInfo, ref dstImgInfo);
  85. if (hObjectOrig.IsAllocated)
  86. {
  87. hObjectOrig.Free();
  88. }
  89. if (hObjectDst.IsAllocated)
  90. {
  91. hObjectDst.Free();
  92. }
  93. if (ret != 0)
  94. {
  95. throw new Exception("Failed at calling cpp func: ResizeBilinear.(error code: " + ret + ").");
  96. }
  97. return resizedImg;
  98. }
  99. public static void FillPadValWithCpp(IImage image, byte[] padValue)
  100. {
  101. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  102. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  103. StructImageInfo origImgInfo = new StructImageInfo
  104. {
  105. width = image.Width,
  106. height = image.Height,
  107. channel = image.Channel,
  108. pData = pDataOrig,
  109. };
  110. GCHandle hObjectPadValue = GCHandle.Alloc(padValue, GCHandleType.Pinned);
  111. IntPtr pDataPadValue = hObjectPadValue.AddrOfPinnedObject();
  112. var ret = FillWithPadValue(ref origImgInfo, pDataPadValue);
  113. if (hObjectOrig.IsAllocated)
  114. {
  115. hObjectOrig.Free();
  116. }
  117. if (hObjectPadValue.IsAllocated)
  118. {
  119. hObjectPadValue.Free();
  120. }
  121. if (ret != 0)
  122. {
  123. throw new Exception("Failed at calling cpp func: FillWithPadValue.(error code: " + ret + ").");
  124. }
  125. }
  126. public static IImage CropRectWithCpp(IImage image, Rect cropRect)
  127. {
  128. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  129. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  130. StructImageInfo origImgInfo = new StructImageInfo
  131. {
  132. width = image.Width,
  133. height = image.Height,
  134. channel = image.Channel,
  135. pData = pDataOrig,
  136. };
  137. RawImage dstImage = new RawImage(new byte[cropRect.Width * cropRect.Height * image.Channel],
  138. cropRect.Width, cropRect.Height, image.Channel, cropRect.Width * image.Channel);
  139. GCHandle hObjectDst = GCHandle.Alloc(dstImage.DataBuffer, GCHandleType.Pinned);
  140. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  141. StructImageInfo dstImgInfo = new StructImageInfo
  142. {
  143. width = dstImage.Width,
  144. height = dstImage.Height,
  145. channel = dstImage.Channel,
  146. pData = pDataDst,
  147. };
  148. StructMyRect structCropRect = new StructMyRect
  149. {
  150. left = cropRect.Left,
  151. top = cropRect.Top,
  152. width = cropRect.Width,
  153. height = cropRect.Height,
  154. };
  155. var ret = CropRect(origImgInfo, structCropRect, ref dstImgInfo);
  156. if (hObjectOrig.IsAllocated)
  157. {
  158. hObjectOrig.Free();
  159. }
  160. if (hObjectDst.IsAllocated)
  161. {
  162. hObjectDst.Free();
  163. }
  164. if (ret != 0)
  165. {
  166. throw new Exception("Failed at calling cpp func: CropRect.(error code: " + ret + ").");
  167. }
  168. return dstImage;
  169. }
  170. public static void CropRectWithCpp(IImage image, Rect cropRect, ref byte[] dstImgDataBuffer)
  171. {
  172. // 目标图像数据区尺寸是否需要改变?
  173. int dstChannel = image.Channel;
  174. int dstWidth = cropRect.Width;
  175. int dstHeight = cropRect.Height;
  176. int dstDataLen = dstWidth * dstHeight * dstChannel;
  177. if (dstImgDataBuffer.Length < dstDataLen)
  178. {
  179. Array.Resize(ref dstImgDataBuffer, dstDataLen);
  180. }
  181. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  182. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  183. StructImageInfo origImgInfo = new StructImageInfo
  184. {
  185. width = image.Width,
  186. height = image.Height,
  187. channel = image.Channel,
  188. pData = pDataOrig,
  189. };
  190. GCHandle hObjectDst = GCHandle.Alloc(dstImgDataBuffer, GCHandleType.Pinned);
  191. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  192. StructImageInfo dstImgInfo = new StructImageInfo
  193. {
  194. width = dstWidth,
  195. height = dstHeight,
  196. channel = dstChannel,
  197. pData = pDataDst,
  198. };
  199. StructMyRect structCropRect = new StructMyRect
  200. {
  201. left = cropRect.Left,
  202. top = cropRect.Top,
  203. width = cropRect.Width,
  204. height = cropRect.Height,
  205. };
  206. var ret = CropRect(origImgInfo, structCropRect, ref dstImgInfo);
  207. if (hObjectOrig.IsAllocated)
  208. {
  209. hObjectOrig.Free();
  210. }
  211. if (hObjectDst.IsAllocated)
  212. {
  213. hObjectDst.Free();
  214. }
  215. if (ret != 0)
  216. {
  217. throw new Exception("Failed at calling cpp func: CropRect.(error code: " + ret + ").");
  218. }
  219. return;
  220. }
  221. public static void PasteRectWithCpp(IImage dstImg, IImage srcImg, Rect pasteRect)
  222. {
  223. GCHandle hObjectSrc = GCHandle.Alloc(srcImg.DataBuffer, GCHandleType.Pinned);
  224. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  225. StructImageInfo srcImgInfo = new StructImageInfo
  226. {
  227. width = srcImg.Width,
  228. height = srcImg.Height,
  229. channel = srcImg.Channel,
  230. pData = pDataSrc,
  231. };
  232. GCHandle hObjectDst = GCHandle.Alloc(dstImg.DataBuffer, GCHandleType.Pinned);
  233. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  234. StructImageInfo dstImgInfo = new StructImageInfo
  235. {
  236. width = dstImg.Width,
  237. height = dstImg.Height,
  238. channel = dstImg.Channel,
  239. pData = pDataDst,
  240. };
  241. StructMyRect structPasteRect = new StructMyRect
  242. {
  243. left = pasteRect.Left,
  244. top = pasteRect.Top,
  245. width = pasteRect.Width,
  246. height = pasteRect.Height,
  247. };
  248. var ret = PasteRect(srcImgInfo, structPasteRect, ref dstImgInfo);
  249. if (hObjectSrc.IsAllocated)
  250. {
  251. hObjectSrc.Free();
  252. }
  253. if (hObjectDst.IsAllocated)
  254. {
  255. hObjectDst.Free();
  256. }
  257. if (ret != 0)
  258. {
  259. throw new Exception("Failed at calling cpp func: PasteRect.(error code: " + ret + ").");
  260. }
  261. }
  262. public static IImage ResizeBilinear(IImage image, int dstWidth, int dstHeight)
  263. {
  264. byte[] dstDataBuffer = new byte[dstWidth * dstHeight * image.Channel];
  265. ResizeBilinear(image, dstWidth, dstHeight, ref dstDataBuffer);
  266. return new RawImage(dstDataBuffer, dstWidth, dstHeight, image.Channel, dstWidth * image.Channel);
  267. }
  268. public static void FillPadVal(IImage image, byte[] padValue)
  269. {
  270. if (padValue == null)
  271. {
  272. throw new ArgumentNullException("padValue");
  273. }
  274. int channel = image.Channel;
  275. byte[] dataBuffer = image.DataBuffer;
  276. if (padValue.Length != channel)
  277. {
  278. throw new ArgumentException("padValue", "Length of the padValue should equal to Channel.");
  279. }
  280. if (dataBuffer == null)
  281. {
  282. throw new ArgumentNullException("image.DataBuffer");
  283. }
  284. int imageSize = image.Width * image.Height;
  285. for (int ni = 0; ni < imageSize; ni++)
  286. {
  287. Buffer.BlockCopy(padValue, 0, dataBuffer, ni * channel, channel);
  288. }
  289. }
  290. public static IImage CropRect(IImage image, Rect cropRect)
  291. {
  292. byte[] croppedDataBuffer = new byte[cropRect.Width * cropRect.Height * image.Channel];
  293. CropRect(image, cropRect, ref croppedDataBuffer);
  294. return new RawImage(croppedDataBuffer, cropRect.Width, cropRect.Height, image.Channel, cropRect.Width * image.Channel);
  295. }
  296. public static void PasteRect(IImage dstImg, IImage srcImg, Rect pasteRect)
  297. {
  298. int dstRectLeft = pasteRect.Left;
  299. int dstRectRight = pasteRect.Right;
  300. int dstRectTop = pasteRect.Top;
  301. int dstRectBottom = pasteRect.Bottom;
  302. int dstRectWidth = pasteRect.Width;
  303. int dstRectHeight = pasteRect.Height;
  304. int width = dstImg.Width;
  305. int height = dstImg.Height;
  306. int channel = dstImg.Channel;
  307. int stride = dstImg.Stride;
  308. byte[] dataBuffer = dstImg.DataBuffer;
  309. if (dstRectLeft < 0 || dstRectLeft > width - 1)
  310. {
  311. throw new ArgumentOutOfRangeException("dstRect.Left", "Left of the paste dstRect is out of the image.");
  312. }
  313. if (dstRectRight < 0 || dstRectRight > width)
  314. {
  315. throw new ArgumentOutOfRangeException("dstRect.Right", "Right of the paste dstRect is out of the image.");
  316. }
  317. if (dstRectTop < 0 || dstRectTop > height - 1)
  318. {
  319. throw new ArgumentOutOfRangeException("dstRect.Top", "Top of the paste dstRect is out of the image.");
  320. }
  321. if (dstRectBottom < 0 || dstRectBottom > height)
  322. {
  323. throw new ArgumentOutOfRangeException("dstRect.Bottom", "Right of the paste dstRect is out of the image.");
  324. }
  325. if (srcImg == null)
  326. {
  327. throw new ArgumentNullException("srcImg");
  328. }
  329. if (srcImg.Channel != channel)
  330. {
  331. throw new ArgumentException("srcImg.Channel", "The channel of srcImg should be equal to the channel of this image.");
  332. }
  333. if (srcImg.Width != dstRectWidth)
  334. {
  335. throw new ArgumentException("srcImg.Width", "The Width of srcImg should be equal to the width of the dstRect.");
  336. }
  337. if (srcImg.Height != dstRectHeight)
  338. {
  339. throw new ArgumentException("srcImg.Height", "The Height of srcImg should be equal to the height of the dstRect.");
  340. }
  341. if (dataBuffer == null)
  342. {
  343. throw new ArgumentNullException("dstImg.DataBuffer");
  344. }
  345. // 如果rect的尺寸就是整幅图的尺寸,就直接整幅图clone即可,不需要一行一行复制
  346. if (dstRectLeft == 0 && dstRectTop == 0 && dstRectWidth == width && dstRectHeight == height)
  347. {
  348. Buffer.BlockCopy(srcImg.DataBuffer, 0, dataBuffer, 0, width * height * channel);
  349. return;
  350. }
  351. byte[] srcImgDataBuffer = srcImg.DataBuffer;
  352. int rectRowCopyLen = dstRectWidth * channel;
  353. int rectCopyOffset = dstRectTop * width * channel + dstRectLeft * channel;
  354. int rowCopyOffset = width * channel;
  355. for (int nh = 0; nh < dstRectHeight; nh++)
  356. {
  357. Buffer.BlockCopy(srcImgDataBuffer, nh * rectRowCopyLen,
  358. dataBuffer, rectCopyOffset + nh * rowCopyOffset, rectRowCopyLen);
  359. }
  360. return;
  361. }
  362. public static void ResizeBilinear(IImage image, int dstWidth, int dstHeight, ref byte[] dstDataBuffer)
  363. {
  364. int width = image.Width;
  365. int height = image.Height;
  366. int channel = image.Channel;
  367. byte[] dataBuffer = image.DataBuffer;
  368. int dstDataLen = dstWidth * dstHeight * channel;
  369. if (dstDataBuffer.Length < dstDataLen)
  370. {
  371. Array.Resize(ref dstDataBuffer, dstDataLen);
  372. }
  373. float scale_x = (float)width / dstWidth;
  374. float scale_y = (float)height / dstHeight;
  375. int dstStep = dstWidth * channel;
  376. int srcStep = width * channel;
  377. float fy, fx;
  378. int sy, sx;
  379. short[] cbufy = new short[2];
  380. short[] cbufx = new short[2];
  381. int nh, nw, nc;
  382. for (nh = 0; nh < dstHeight; ++nh)
  383. {
  384. fy = (float)((nh + 0.5) * scale_y - 0.5);
  385. sy = Convert.ToInt32(Math.Floor(fy));
  386. fy -= sy;
  387. sy = Math.Min(sy, height - 2);
  388. sy = Math.Max(0, sy);
  389. cbufy[0] = Convert.ToInt16((1 - fy) * 2048);
  390. cbufy[1] = Convert.ToInt16(2048 - cbufy[0]);
  391. for (nw = 0; nw < dstWidth; ++nw)
  392. {
  393. fx = (float)((nw + 0.5) * scale_x - 0.5);
  394. sx = Convert.ToInt16(Math.Floor(fx));
  395. fx -= sx;
  396. if (sx < 0)
  397. {
  398. fx = 0;
  399. sx = 0;
  400. }
  401. if (sx >= width - 2)
  402. {
  403. fx = 0;
  404. sx = width - 2;
  405. }
  406. cbufx[0] = Convert.ToInt16((1 - fx) * 2048);
  407. cbufx[1] = Convert.ToInt16(2048 - cbufx[0]);
  408. for (nc = 0; nc < channel; ++nc)
  409. {
  410. dstDataBuffer[nh * dstStep + nw * channel + nc] = Convert.ToByte((
  411. dataBuffer[sy * srcStep + sx * channel + nc] * cbufx[0] * cbufy[0] +
  412. dataBuffer[(sy + 1) * srcStep + sx * channel + nc] * cbufx[0] * cbufy[1] +
  413. dataBuffer[sy * srcStep + (sx + 1) * channel + nc] * cbufx[1] * cbufy[0] +
  414. dataBuffer[(sy + 1) * srcStep + (sx + 1) * channel + nc] * cbufx[1] * cbufy[1]) >> 22);
  415. }
  416. }
  417. }
  418. }
  419. public static void ResizeBilinearWithCpp(IImage image, int dstWidth, int dstHeight, ref byte[] dstImgDataBuffer)
  420. {
  421. // 目标图像数据区尺寸是否需要改变?
  422. int dstChannel = image.Channel;
  423. int dstDataLen = dstWidth * dstHeight * dstChannel;
  424. if (dstImgDataBuffer.Length < dstDataLen)
  425. {
  426. Array.Resize(ref dstImgDataBuffer, dstDataLen);
  427. }
  428. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  429. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  430. StructImageInfo origImgInfo = new StructImageInfo
  431. {
  432. width = image.Width,
  433. height = image.Height,
  434. channel = image.Channel,
  435. pData = pDataOrig,
  436. };
  437. GCHandle hObjectDst = GCHandle.Alloc(dstImgDataBuffer, GCHandleType.Pinned);
  438. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  439. StructImageInfo dstImgInfo = new StructImageInfo
  440. {
  441. width = dstWidth,
  442. height = dstHeight,
  443. channel = dstChannel,
  444. pData = pDataDst,
  445. };
  446. var ret = ResizeBilinear(origImgInfo, ref dstImgInfo);
  447. if (hObjectOrig.IsAllocated)
  448. {
  449. hObjectOrig.Free();
  450. }
  451. if (hObjectDst.IsAllocated)
  452. {
  453. hObjectDst.Free();
  454. }
  455. if (ret != 0)
  456. {
  457. throw new Exception("Failed at calling cpp func: ResizeBilinear.(error code: " + ret + ").");
  458. }
  459. return;
  460. }
  461. public static void CropRect(IImage image, Rect cropRect, ref byte[] dstDataBuffer)
  462. {
  463. if (cropRect.IsEmpty())
  464. {
  465. return;
  466. }
  467. int rectLeft = cropRect.Left;
  468. int rectRight = cropRect.Right;
  469. int rectTop = cropRect.Top;
  470. int rectBottom = cropRect.Bottom;
  471. int rectWidth = cropRect.Width;
  472. int rectHeight = cropRect.Height;
  473. int width = image.Width;
  474. int height = image.Height;
  475. int channel = image.Channel;
  476. int stride = image.Stride;
  477. byte[] dataBuffer = image.DataBuffer;
  478. if (rectLeft < 0 || rectLeft > width - 1)
  479. {
  480. throw new ArgumentOutOfRangeException("rect.Left", "Left of the crop rect is out of the image.");
  481. }
  482. if (rectRight < 0 || rectRight > width)
  483. {
  484. throw new ArgumentOutOfRangeException("rect.Right", "Right of the crop rect is out of the image.");
  485. }
  486. if (rectTop < 0 || rectTop > height - 1)
  487. {
  488. throw new ArgumentOutOfRangeException("rect.Top", "Top of the crop rect is out of the image.");
  489. }
  490. if (rectBottom < 0 || rectBottom > height)
  491. {
  492. throw new ArgumentOutOfRangeException("rect.Bottom", "Right of the crop rect is out of the image.");
  493. }
  494. // 目标图像的尺寸是否需要改变
  495. int dstDataLen = rectWidth * rectHeight * channel;
  496. if (dstDataBuffer.Length < dstDataLen)
  497. {
  498. Array.Resize(ref dstDataBuffer, dstDataLen);
  499. }
  500. // 如果rect的尺寸就是整幅图的尺寸,就直接整幅图clone即可,不需要一行一行复制
  501. if (rectLeft == 0 && rectTop == 0 && rectWidth == width && rectHeight == height)
  502. {
  503. Buffer.BlockCopy(dataBuffer, 0, dstDataBuffer, 0, image.DataBufferSize);
  504. }
  505. int rectRowCopyLen = rectWidth * channel;
  506. int rectCopyOffset = rectTop * width * channel + rectLeft * channel;
  507. for (int nh = 0; nh < rectHeight; nh++)
  508. {
  509. Buffer.BlockCopy(dataBuffer, rectCopyOffset + nh * stride,
  510. dstDataBuffer, nh * rectRowCopyLen, rectRowCopyLen);
  511. }
  512. }
  513. #endregion
  514. }
  515. }