1
0

MoldedImage.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. using System;
  2. using System.Linq;
  3. using System.Runtime.InteropServices;
  4. namespace YOLODetectProcessLib
  5. {
  6. /// <summary>
  7. /// 模型中图像数据的轴排列顺序
  8. /// </summary>
  9. public enum EnumAxisOrder
  10. {
  11. CHW,
  12. HWC,
  13. }
  14. /// <summary>
  15. /// 将原始图像转换为模型输入时所采用的resize方式
  16. /// </summary>
  17. public enum EnumResizeMode
  18. {
  19. /// <summary>
  20. /// 以长边为准,将原始图像等比例缩放,短边用固定灰度值填充
  21. /// </summary>
  22. FitLargeSizeAndPad,
  23. /// <summary>
  24. /// 以短边为准,将原始图像等比例缩放,长边直接裁切掉不要
  25. /// </summary>
  26. FitSmallSizeAndCrop,
  27. /// <summary>
  28. /// 非等比例缩放,长短边各自伸缩到模型所需的尺寸
  29. /// </summary>
  30. Warp,
  31. }
  32. /// <summary>
  33. /// 减均值的时候,均值的计算方式
  34. /// </summary>
  35. public enum EnumMeanValueType
  36. {
  37. /// <summary>
  38. /// 不用减均值
  39. /// </summary>
  40. None,
  41. /// <summary>
  42. /// 减均值(固定值)
  43. /// </summary>
  44. ConstantMean,
  45. /// <summary>
  46. /// 减均值(用当前图像的所有灰度计算一个均值)
  47. /// </summary>
  48. CalcMeanImage,
  49. /// <summary>
  50. /// 减均值(用当前图像的每个通道单独计算均值,减的时候也是每通道分别减)
  51. /// </summary>
  52. CalcMeanPerChannel,
  53. }
  54. /// <summary>
  55. /// 除以scale的时候,scale的计算方式
  56. /// </summary>
  57. public enum EnumScaleValueType
  58. {
  59. /// <summary>
  60. /// 不用除以scale
  61. /// </summary>
  62. None,
  63. /// <summary>
  64. /// 除以scale(固定值)
  65. /// </summary>
  66. ConstantScale,
  67. /// <summary>
  68. /// 除以scale(用当前图像的所有灰度计算一个标准差,作为scale)
  69. /// </summary>
  70. CalcStdImage,
  71. /// <summary>
  72. /// 除以scale(用当前图像的每个通道单独计算标准差,除的时候也是每个通道分别除)
  73. /// </summary>
  74. CalcStdPerChannel,
  75. }
  76. /// <summary>
  77. /// 归一化的方法
  78. /// </summary>
  79. public enum EnumNormalizationType
  80. {
  81. /// <summary>
  82. /// 不用做归一化
  83. /// </summary>
  84. None,
  85. /// <summary>
  86. /// 用当前图像的最大最小值做归一化(val'=(val-min)/(max-min))
  87. /// </summary>
  88. MinMaxScalingImage,
  89. /// <summary>
  90. /// 用当前图像每个通道的最大最小值做归一化
  91. /// </summary>
  92. MinMaxScalingPerChannel,
  93. }
  94. /// <summary>
  95. /// 颜色通道的顺序
  96. /// </summary>
  97. public enum EnumChannelOrder
  98. {
  99. None,
  100. RGB,
  101. BGR,
  102. }
  103. /// <summary>
  104. /// 根据模型需要的尺寸和resize参数,处理后的图像
  105. /// 可直接导入模型
  106. /// </summary>
  107. public class MoldedImage : IDisposable
  108. {
  109. #region private
  110. private readonly int _netImgH;
  111. private readonly int _netImgW;
  112. private readonly int _netImgC;
  113. private readonly EnumResizeMode _resizeMode;
  114. private readonly EnumMeanValueType _meanValueType;
  115. private readonly float _meanR;
  116. private readonly float _meanG;
  117. private readonly float _meanB;
  118. private readonly EnumScaleValueType _scaleValueType;
  119. private readonly float _scaleR;
  120. private readonly float _scaleG;
  121. private readonly float _scaleB;
  122. private readonly EnumNormalizationType _normalizationType;
  123. private readonly bool _reverseInputChannels;
  124. private readonly EnumAxisOrder _axisOrder;
  125. private readonly bool _useContoursAsMask;
  126. private IImage _resizedImage = null;
  127. private IImage _croppedROIImage = null;
  128. private IImage _moldedImage = null;
  129. private byte[] _padValue = null;
  130. private float[] _dataBuffer = null;
  131. #endregion
  132. #region property
  133. /// <summary>
  134. /// 图像数据
  135. /// </summary>
  136. public float[] DataBuffer => _dataBuffer;
  137. #endregion
  138. #region public funcs
  139. /// <summary>
  140. /// 释放
  141. /// </summary>
  142. public void Dispose()
  143. {
  144. DoDispose();
  145. GC.SuppressFinalize(this);
  146. LogHelper.InfoLog("MoldedImage.Disposed manually.");
  147. }
  148. /// <summary>
  149. /// 析构函数
  150. /// </summary>
  151. ~MoldedImage()
  152. {
  153. DoDispose();
  154. LogHelper.InfoLog("MoldedImage.Disposed by destructor.");
  155. }
  156. /// <summary>
  157. /// 对输入图像进行处理
  158. /// </summary>
  159. /// <param name="image"></param>
  160. public MoldedImageMetas Process(InferenceNetworkInputImage image)
  161. {
  162. // _dataBuffer的尺寸是否需要改变
  163. int dataBufferLength = _netImgH * _netImgW * _netImgC;
  164. if (dataBufferLength != _dataBuffer.Length)
  165. {
  166. Array.Resize(ref _dataBuffer, dataBufferLength);
  167. }
  168. // 传入原始图像相关信息
  169. Rect roiRect;
  170. roiRect = image.ROI;
  171. MoldedImageMetas imageMeta = new MoldedImageMetas
  172. {
  173. NetImgInputHeight = _netImgH,
  174. NetImgInputWidth = _netImgW,
  175. NetImgInputChannels = _netImgC,
  176. OrigImgHeight = image.Image.Height,
  177. OrigImgWidth = image.Image.Width,
  178. ROIRectHeight = roiRect.Height,
  179. ROIRectWidth = roiRect.Width,
  180. ROIRectTop = roiRect.Top,
  181. ROIRectLeft = roiRect.Left,
  182. };
  183. // 是否需要用contour作mask
  184. IImage imageToResize;
  185. imageToResize = image.Image;
  186. // 对原始图像进行缩放
  187. CalcResizeImageMetas(ref imageMeta);
  188. DoResizeCppFix(imageToResize, imageMeta);
  189. // 将Resize过后的图像像素转成所需的格式
  190. if (imageMeta.NetImgInputChannels == 1)
  191. {
  192. ExtractRGB24AsGrayCpp(0);
  193. }
  194. else
  195. {
  196. ExtractRGB24AsColorCpp(0);
  197. }
  198. return imageMeta;
  199. }
  200. #endregion
  201. #region constructor
  202. /// <summary>
  203. /// 构造函数
  204. /// </summary>
  205. /// <param name="netImgH"></param>
  206. /// <param name="netImgW"></param>
  207. /// <param name="netImgC"></param>
  208. /// <param name="resizeMode"></param>
  209. /// <param name="meanR"></param>
  210. /// <param name="meanG"></param>
  211. /// <param name="meanB"></param>
  212. /// <param name="substractMean"></param>
  213. /// <param name="scaleR"></param>
  214. /// <param name="scaleG"></param>
  215. /// <param name="scaleB"></param>
  216. /// <param name="devideScale"></param>
  217. /// <param name="channelOrder"></param>
  218. /// <param name="axisOrder"></param>
  219. public MoldedImage(int netImgH, int netImgW, int netImgC, EnumResizeMode resizeMode,
  220. EnumMeanValueType meanType, float meanR, float meanG, float meanB,
  221. EnumScaleValueType scaleType, float scaleR, float scaleG, float scaleB,
  222. EnumNormalizationType normType, EnumChannelOrder channelOrder, EnumAxisOrder axisOrder)
  223. {
  224. if (netImgC != 3 && netImgC != 1)
  225. {
  226. throw new ArgumentOutOfRangeException("netImgC", "The expected channel number is 1 or 3, but got " + netImgC + ".");
  227. }
  228. if (netImgH <= 0)
  229. {
  230. throw new ArgumentOutOfRangeException("netImgH", "The expected network height is greater than 0, but got " + netImgH + ".");
  231. }
  232. if (netImgW <= 0)
  233. {
  234. throw new ArgumentOutOfRangeException("netImgW", "The expected network width is greater than 0, but got " + netImgW + ".");
  235. }
  236. if (scaleType == EnumScaleValueType.ConstantScale && (Math.Abs(scaleR) < 1e-10))
  237. {
  238. throw new ArgumentOutOfRangeException("scaleR", "The expected scaleR is greater than 1e-10 or less than -1e10 when devideScale is true," +
  239. " but got " + scaleR + ".");
  240. }
  241. if (scaleType == EnumScaleValueType.ConstantScale && (Math.Abs(scaleG) < 1e-10))
  242. {
  243. throw new ArgumentOutOfRangeException("scaleG", "The expected scaleG is greater than 1e-10 or less than -1e10 when devideScale is true," +
  244. " but got " + scaleR + ".");
  245. }
  246. if (scaleType == EnumScaleValueType.ConstantScale && (Math.Abs(scaleB) < 1e-10))
  247. {
  248. throw new ArgumentOutOfRangeException("scaleG", "The expected scaleB is greater than 1e-10 or less than -1e10 when devideScale is true," +
  249. " but got " + scaleR + ".");
  250. }
  251. _netImgH = netImgH;
  252. _netImgW = netImgW;
  253. _netImgC = netImgC;
  254. _resizeMode = resizeMode;
  255. _meanValueType = meanType;
  256. _meanR = meanR;
  257. _meanG = meanG;
  258. _meanB = meanB;
  259. _scaleValueType = scaleType;
  260. _scaleR = scaleR;
  261. _scaleG = scaleG;
  262. _scaleB = scaleB;
  263. _normalizationType = normType;
  264. _reverseInputChannels = (channelOrder == EnumChannelOrder.BGR);
  265. _axisOrder = axisOrder;
  266. _resizedImage = new RawImage(new byte[netImgH * netImgH * 3], netImgW, netImgH, 3, netImgW * 3);
  267. _croppedROIImage = new RawImage(new byte[netImgH * netImgW * 3], netImgW, netImgH, 3, netImgW * 3);
  268. _moldedImage = new RawImage(new byte[netImgH * netImgW * 3], netImgW, netImgH, 3, netImgW * 3);
  269. _padValue = new byte[3];
  270. _padValue[0] = (byte)_meanB;
  271. _padValue[1] = (byte)_meanG;
  272. _padValue[2] = (byte)_meanR;
  273. _dataBuffer = new float[netImgH * netImgW * netImgC];
  274. }
  275. #endregion
  276. #region private funcs
  277. /// <summary>
  278. /// 计算Resize所需的imageMetas
  279. /// </summary>
  280. /// <param name="imageMetas"></param>
  281. private void CalcResizeImageMetas(ref MoldedImageMetas imageMetas)
  282. {
  283. // 先求出缩放所需的参数
  284. double scaleW = (double)imageMetas.ROIRectWidth / imageMetas.NetImgInputWidth;
  285. double scaleH = (double)imageMetas.ROIRectHeight / imageMetas.NetImgInputHeight;
  286. int width;
  287. int height;
  288. int startind;
  289. switch (_resizeMode)
  290. {
  291. case EnumResizeMode.FitLargeSizeAndPad:
  292. // 保持原来的长宽比,以长边为准,短边填充
  293. if (scaleW > scaleH)
  294. {
  295. scaleH = scaleW;
  296. width = imageMetas.NetImgInputWidth;
  297. height = (int)Math.Round(imageMetas.ROIRectHeight / scaleH);
  298. }
  299. else
  300. {
  301. scaleW = scaleH;
  302. width = (int)Math.Round(imageMetas.ROIRectWidth / scaleW);
  303. height = imageMetas.NetImgInputHeight;
  304. }
  305. imageMetas.MoldLeft = (imageMetas.NetImgInputWidth - width) / 2;
  306. imageMetas.MoldTop = (imageMetas.NetImgInputHeight - height) / 2;
  307. imageMetas.MoldWidth = width;
  308. imageMetas.MoldHeight = height;
  309. break;
  310. case EnumResizeMode.FitSmallSizeAndCrop:
  311. // 保持原来的长宽比,以短边为准,长边居中裁切
  312. if (scaleW > scaleH)
  313. {
  314. scaleW = scaleH;
  315. int correctedRoIWidth = (int)Math.Round(imageMetas.NetImgInputWidth * scaleW);
  316. startind = (imageMetas.ROIRectWidth - correctedRoIWidth) / 2;
  317. imageMetas.ROIRectLeft = imageMetas.ROIRectLeft + startind;
  318. imageMetas.ROIRectWidth = correctedRoIWidth;
  319. }
  320. else
  321. {
  322. scaleH = scaleW;
  323. int correctedRoIHeight = (int)Math.Round(imageMetas.NetImgInputHeight * scaleH);
  324. startind = (imageMetas.ROIRectHeight - correctedRoIHeight) / 2;
  325. imageMetas.ROIRectTop = imageMetas.ROIRectTop + startind;
  326. imageMetas.ROIRectHeight = correctedRoIHeight;
  327. }
  328. imageMetas.MoldLeft = 0;
  329. imageMetas.MoldTop = 0;
  330. imageMetas.MoldWidth = imageMetas.NetImgInputWidth;
  331. imageMetas.MoldHeight = imageMetas.NetImgInputHeight;
  332. break;
  333. case EnumResizeMode.Warp:
  334. // 不管长宽比,直接长短边各自resize成所需的尺寸
  335. imageMetas.MoldLeft = 0;
  336. imageMetas.MoldTop = 0;
  337. imageMetas.MoldWidth = imageMetas.NetImgInputWidth;
  338. imageMetas.MoldHeight = imageMetas.NetImgInputHeight;
  339. break;
  340. }
  341. }
  342. /// <summary>
  343. /// 对原始图像进行缩放
  344. /// </summary>
  345. private void DoResize(IImage image, MoldedImageMetas imageMetas)
  346. {
  347. // 先在resizedImage上填充背景色
  348. ImageUtils.FillPadVal(_resizedImage, _padValue);
  349. // 取出原始图像种的指定范围
  350. var roiRectInSrc = ImageUtils.CropRect(image, new Rect(imageMetas.ROIRectLeft, imageMetas.ROIRectTop,
  351. imageMetas.ROIRectWidth, imageMetas.ROIRectHeight));
  352. // 再将其resize成指定尺寸
  353. var molded = ImageUtils.ResizeBilinear(roiRectInSrc, imageMetas.MoldWidth, imageMetas.MoldHeight);
  354. // 再将其贴到resizedImage上指定位置
  355. ImageUtils.PasteRect(_resizedImage, molded, new Rect(imageMetas.MoldLeft, imageMetas.MoldTop,
  356. imageMetas.MoldWidth, imageMetas.MoldHeight));
  357. // 销毁
  358. roiRectInSrc.Dispose();
  359. molded.Dispose();
  360. }
  361. /// <summary>
  362. /// 对原始图像进行缩放
  363. /// </summary>
  364. private void DoResizeFix(RawImage image, MoldedImageMetas imageMetas)
  365. {
  366. // 先在resizedImage上填充背景色
  367. ImageUtils.FillPadVal(_resizedImage, _padValue);
  368. // 取出原始图像种的指定范围
  369. int croppedROIImageDataBufferSize = imageMetas.ROIRectWidth * imageMetas.ROIRectHeight * 3;
  370. if (_croppedROIImage.DataBuffer.Length < croppedROIImageDataBufferSize)
  371. {
  372. _croppedROIImage.SetDataBufferSize(croppedROIImageDataBufferSize);
  373. }
  374. var croppedROIImageData = _croppedROIImage.DataBuffer;
  375. ImageUtils.CropRect(image, new Rect(imageMetas.ROIRectLeft, imageMetas.ROIRectTop,
  376. imageMetas.ROIRectWidth, imageMetas.ROIRectHeight), ref croppedROIImageData);
  377. _croppedROIImage.Width = imageMetas.ROIRectWidth;
  378. _croppedROIImage.Height = imageMetas.ROIRectHeight;
  379. _croppedROIImage.Stride = imageMetas.ROIRectWidth * 3;
  380. _croppedROIImage.DataBufferSize = croppedROIImageDataBufferSize;
  381. // 再将其resize成指定尺寸
  382. int moldedImageDataBufferSize = imageMetas.MoldWidth * imageMetas.MoldHeight * 3;
  383. if (_moldedImage.DataBuffer.Length < moldedImageDataBufferSize)
  384. {
  385. _moldedImage.SetDataBufferSize(moldedImageDataBufferSize);
  386. }
  387. var moldedImageData = _moldedImage.DataBuffer;
  388. ImageUtils.ResizeBilinear(_croppedROIImage, imageMetas.MoldWidth, imageMetas.MoldHeight, ref moldedImageData);
  389. _moldedImage.Width = imageMetas.MoldWidth;
  390. _moldedImage.Height = imageMetas.MoldHeight;
  391. _moldedImage.Stride = imageMetas.MoldWidth * 3;
  392. _moldedImage.DataBufferSize = moldedImageDataBufferSize;
  393. // 再将其贴到resizedImage上指定位置
  394. ImageUtils.PasteRect(_resizedImage, _moldedImage, new Rect(imageMetas.MoldLeft, imageMetas.MoldTop,
  395. imageMetas.MoldWidth, imageMetas.MoldHeight));
  396. }
  397. /// <summary>
  398. /// 对原始图像进行缩放(用cpp dll中提供的函数)
  399. /// </summary>
  400. /// <param name="image"></param>
  401. /// <param name="imageMetas"></param>
  402. private void DoResizeCpp(IImage image, MoldedImageMetas imageMetas)
  403. {
  404. // 先在resizedImage上填充背景色
  405. ImageUtils.FillPadValWithCpp(_resizedImage, _padValue);
  406. // 取出原始图像种的指定范围
  407. var roiRectInSrc = ImageUtils.CropRectWithCpp(image, new Rect(imageMetas.ROIRectLeft, imageMetas.ROIRectTop,
  408. imageMetas.ROIRectWidth, imageMetas.ROIRectHeight));
  409. // 再将其resize成指定尺寸
  410. var molded = ImageUtils.ResizeBilinearWithCpp(roiRectInSrc, imageMetas.MoldWidth, imageMetas.MoldHeight);
  411. // 再将其贴到resizedImage上指定位置
  412. ImageUtils.PasteRectWithCpp(_resizedImage, molded, new Rect(imageMetas.MoldLeft, imageMetas.MoldTop,
  413. imageMetas.MoldWidth, imageMetas.MoldHeight));
  414. // 销毁
  415. roiRectInSrc.Dispose();
  416. molded.Dispose();
  417. }
  418. /// <summary>
  419. /// 对原始图像进行缩放(用cpp dll中提供的函数,且固定使用某些内存区域)
  420. /// </summary>
  421. /// <param name="image"></param>
  422. /// <param name="imageMetas"></param>
  423. private void DoResizeCppFix(IImage image, MoldedImageMetas imageMetas)
  424. {
  425. // 先在resizedImage上填充背景色
  426. ImageUtils.FillPadValWithCpp(_resizedImage, _padValue);
  427. // 取出原始图像种的指定范围
  428. // 注意:由于CalcResizeParams中,当ResizeMode为FitSmallSizeAndCrop时,ROIRect的位置可能会发生改变
  429. // 所以g.DrawImage时srcRect不要用InputImage里的ROIRect,而是现在的
  430. byte[] croppedROIImageData = _croppedROIImage.DataBuffer;
  431. int newSizeROI = imageMetas.ROIRectWidth * imageMetas.ROIRectHeight * 3;
  432. if (croppedROIImageData == null || croppedROIImageData.Length < newSizeROI)
  433. {
  434. _croppedROIImage.SetDataBufferSize(newSizeROI);
  435. croppedROIImageData = _croppedROIImage.DataBuffer;
  436. }
  437. ImageUtils.CropRectWithCpp(image, new Rect(imageMetas.ROIRectLeft, imageMetas.ROIRectTop,
  438. imageMetas.ROIRectWidth, imageMetas.ROIRectHeight), ref croppedROIImageData);
  439. _croppedROIImage.Width = imageMetas.ROIRectWidth;
  440. _croppedROIImage.Height = imageMetas.ROIRectHeight;
  441. // 再将其resize成指定尺寸
  442. byte[] moldedImageData = _moldedImage.DataBuffer;
  443. int newSizeMolded = imageMetas.MoldWidth * imageMetas.MoldHeight * 3;
  444. if (moldedImageData == null || moldedImageData.Length < newSizeMolded)
  445. {
  446. _moldedImage.SetDataBufferSize(newSizeMolded);
  447. moldedImageData = _moldedImage.DataBuffer;
  448. }
  449. ImageUtils.ResizeBilinearWithCpp(_croppedROIImage, imageMetas.MoldWidth, imageMetas.MoldHeight, ref moldedImageData);
  450. _moldedImage.Width = imageMetas.MoldWidth;
  451. _moldedImage.Height = imageMetas.MoldHeight;
  452. // 再将其贴到resizedImage上指定位置
  453. ImageUtils.PasteRectWithCpp(_resizedImage, _moldedImage, new Rect(imageMetas.MoldLeft, imageMetas.MoldTop,
  454. imageMetas.MoldWidth, imageMetas.MoldHeight));
  455. }
  456. /// <summary>
  457. /// 将原始图像转成单通道的数组(用cpp dll中提供的函数)
  458. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  459. /// </summary>
  460. private void ExtractRGB24AsGrayCpp(int startInd)
  461. {
  462. GCHandle hObjectSrc = GCHandle.Alloc(_resizedImage.DataBuffer, GCHandleType.Pinned);
  463. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  464. StructImageInfo srcImgInfo = new StructImageInfo
  465. {
  466. width = _resizedImage.Width,
  467. height = _resizedImage.Height,
  468. channel = _resizedImage.Channel,
  469. pData = pDataSrc,
  470. };
  471. GCHandle hObjectDst = GCHandle.Alloc(_dataBuffer, GCHandleType.Pinned);
  472. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  473. pDataDst = IntPtr.Add(pDataDst, startInd * sizeof(float));
  474. StructExtractInfo extractInfo = new StructExtractInfo
  475. {
  476. meanValueType = (int)_meanValueType,
  477. meanR = _meanR,
  478. meanG = _meanG,
  479. meanB = _meanB,
  480. scaleValueType = (int)_scaleValueType,
  481. scaleR = _scaleR,
  482. scaleG = _scaleG,
  483. scaleB = _scaleB,
  484. normalizationType = (int)_normalizationType,
  485. reverseInputChannels = _reverseInputChannels,
  486. axisOrder = (int)_axisOrder,
  487. };
  488. var ret = ImageUtils.ExtractRGB24AsGray(srcImgInfo, pDataDst, extractInfo);
  489. if (hObjectSrc.IsAllocated)
  490. {
  491. hObjectSrc.Free();
  492. }
  493. if (hObjectDst.IsAllocated)
  494. {
  495. hObjectDst.Free();
  496. }
  497. if (ret != 0)
  498. {
  499. throw new Exception("Failed at calling cpp func: ExtractRGB24AsGray.(error code: " + ret + ").");
  500. }
  501. }
  502. /// <summary>
  503. /// 将原始图像转成三维数组(用cpp dll中提供的函数)
  504. /// 根据axisOrder决定到底是CHW还是HWC的数据排列顺序
  505. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  506. /// </summary>
  507. private void ExtractRGB24AsColorCpp(int startInd)
  508. {
  509. GCHandle hObjectSrc = GCHandle.Alloc(_resizedImage.DataBuffer, GCHandleType.Pinned);
  510. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  511. StructImageInfo srcImgInfo = new StructImageInfo
  512. {
  513. width = _resizedImage.Width,
  514. height = _resizedImage.Height,
  515. channel = _resizedImage.Channel,
  516. pData = pDataSrc,
  517. };
  518. GCHandle hObjectDst = GCHandle.Alloc(_dataBuffer, GCHandleType.Pinned);
  519. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  520. pDataDst = IntPtr.Add(pDataDst, startInd * sizeof(float));
  521. StructExtractInfo extractInfo = new StructExtractInfo
  522. {
  523. meanValueType = (int)_meanValueType,
  524. meanR = _meanR,
  525. meanG = _meanG,
  526. meanB = _meanB,
  527. scaleValueType = (int)_scaleValueType,
  528. scaleR = _scaleR,
  529. scaleG = _scaleG,
  530. scaleB = _scaleB,
  531. normalizationType = (int)_normalizationType,
  532. reverseInputChannels = _reverseInputChannels,
  533. axisOrder = (int)_axisOrder,
  534. };
  535. var ret = ImageUtils.ExtractRGB24AsColor(srcImgInfo, pDataDst, extractInfo);
  536. if (hObjectSrc.IsAllocated)
  537. {
  538. hObjectSrc.Free();
  539. }
  540. if (hObjectDst.IsAllocated)
  541. {
  542. hObjectDst.Free();
  543. }
  544. if (ret != 0)
  545. {
  546. throw new Exception("Failed at calling cpp func: ExtractRGB24AsColor.(error code: " + ret + ").");
  547. }
  548. }
  549. /// <summary>
  550. /// 销毁
  551. /// </summary>
  552. private void DoDispose()
  553. {
  554. _resizedImage?.Dispose();
  555. _resizedImage = null;
  556. _croppedROIImage?.Dispose();
  557. _croppedROIImage = null;
  558. _moldedImage?.Dispose();
  559. _moldedImage = null;
  560. }
  561. /// <summary>
  562. /// 计算均值
  563. /// </summary>
  564. /// <param name="meanR"></param>
  565. /// <param name="meanG"></param>
  566. /// <param name="meanB"></param>
  567. /// <param name="meanImg"></param>
  568. private void CalcMean(out float meanR, out float meanG, out float meanB, out float meanImg)
  569. {
  570. meanR = 0;
  571. meanG = 0;
  572. meanB = 0;
  573. int imgSize = _resizedImage.Width * _resizedImage.Height;
  574. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  575. for (int ni = 0; ni < imgSize; ni++)
  576. {
  577. int offset = 3 * ni;
  578. meanB += resizedImageDataBuffer[offset];
  579. meanG += resizedImageDataBuffer[offset + 1];
  580. meanR += resizedImageDataBuffer[offset + 2];
  581. }
  582. meanR /= imgSize;
  583. meanG /= imgSize;
  584. meanB /= imgSize;
  585. meanImg = PixelRGBToGray(meanR, meanG, meanB);
  586. }
  587. /// <summary>
  588. /// 计算方差
  589. /// </summary>
  590. /// <param name="stdR"></param>
  591. /// <param name="stdG"></param>
  592. /// <param name="stdB"></param>
  593. /// <param name="stdImg"></param>
  594. private void CalcStd(float meanR, float meanG, float meanB, float meanImg,
  595. out float stdR, out float stdG, out float stdB, out float stdImg)
  596. {
  597. double sumOfSquareR = 0;
  598. double sumOfSquareG = 0;
  599. double sumOfSquareB = 0;
  600. double sumOfSquareImg = 0;
  601. int imgSize = _resizedImage.Width * _resizedImage.Height;
  602. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  603. for (int ni = 0; ni < imgSize; ni++)
  604. {
  605. int offset = 3 * ni;
  606. byte b = resizedImageDataBuffer[offset];
  607. byte g = resizedImageDataBuffer[offset + 1];
  608. byte r = resizedImageDataBuffer[offset + 2];
  609. sumOfSquareB += Math.Pow(b - meanB, 2);
  610. sumOfSquareG += Math.Pow(g - meanG, 2);
  611. sumOfSquareR += Math.Pow(r - meanR, 2);
  612. sumOfSquareImg += (Math.Pow(r - meanImg, 2) + Math.Pow(g - meanImg, 2) + Math.Pow(b - meanImg, 2));
  613. }
  614. stdR = (float)Math.Sqrt(sumOfSquareR / imgSize);
  615. stdG = (float)Math.Sqrt(sumOfSquareG / imgSize);
  616. stdB = (float)Math.Sqrt(sumOfSquareB / imgSize);
  617. stdImg = (float)Math.Sqrt(sumOfSquareImg / (imgSize * 3));
  618. }
  619. /// <summary>
  620. /// 计算最大最小值
  621. /// </summary>
  622. /// <param name="minR"></param>
  623. /// <param name="maxR"></param>
  624. /// <param name="minG"></param>
  625. /// <param name="maxG"></param>
  626. /// <param name="minB"></param>
  627. /// <param name="maxB"></param>
  628. /// <param name="minImg"></param>
  629. /// <param name="maxImg"></param>
  630. private void CalcMinMax(out float minR, out float maxR, out float minG, out float maxG,
  631. out float minB, out float maxB, out float minImg, out float maxImg)
  632. {
  633. minR = float.MaxValue;
  634. minG = float.MaxValue;
  635. minB = float.MaxValue;
  636. maxR = float.MinValue;
  637. maxG = float.MinValue;
  638. maxB = float.MinValue;
  639. int imgSize = _resizedImage.Width * _resizedImage.Height;
  640. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  641. for (int ni = 0; ni < imgSize; ni++)
  642. {
  643. int offset = 3 * ni;
  644. byte b = resizedImageDataBuffer[offset];
  645. byte g = resizedImageDataBuffer[offset + 1];
  646. byte r = resizedImageDataBuffer[offset + 2];
  647. if (r < minR)
  648. {
  649. minR = r;
  650. }
  651. if (r > maxR)
  652. {
  653. maxR = r;
  654. }
  655. if (g < minG)
  656. {
  657. minG = g;
  658. }
  659. if (g > maxG)
  660. {
  661. maxG = g;
  662. }
  663. if (b < minB)
  664. {
  665. minB = b;
  666. }
  667. if (b > maxB)
  668. {
  669. maxB = b;
  670. }
  671. }
  672. minImg = resizedImageDataBuffer.Min();
  673. maxImg = resizedImageDataBuffer.Max();
  674. }
  675. /// <summary>
  676. /// 将单个像素的RGB值转换为灰度值
  677. /// </summary>
  678. /// <param name="Rvalue"></param>
  679. /// <param name="Gvalue"></param>
  680. /// <param name="Bvalue"></param>
  681. /// <returns></returns>
  682. private float PixelRGBToGray(float Rvalue, float Gvalue, float Bvalue)
  683. {
  684. return (float)0.299 * Rvalue + (float)0.587 * Gvalue + (float)0.114 * Bvalue;
  685. }
  686. #endregion
  687. }
  688. }