ImageUtils.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  1. using System;
  2. using System.Linq;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.Runtime.InteropServices;
  6. namespace ImageProcessUtilsTest
  7. {
  8. public enum EnumMeanValueType
  9. {
  10. None,
  11. ConstantMean,
  12. CalcMeanImage,
  13. CalcMeanPerChannel,
  14. }
  15. public enum EnumScaleValueType
  16. {
  17. None,
  18. ConstantScale,
  19. CalcStdImage,
  20. CalcStdPerChannel,
  21. }
  22. public enum EnumNormalizationType
  23. {
  24. None,
  25. MinMaxScalingImage,
  26. MinMaxScalingPerChannel,
  27. }
  28. public enum EnumAxisOrder
  29. {
  30. CHW,
  31. HWC,
  32. }
  33. public struct StructImageInfo
  34. {
  35. public int width;
  36. public int height;
  37. public int channel;
  38. public IntPtr pData;
  39. }
  40. public struct StructMyRect
  41. {
  42. public int left;
  43. public int top;
  44. public int width;
  45. public int height;
  46. }
  47. public struct StructExtractInfo
  48. {
  49. public int meanValueType;
  50. public float meanR;
  51. public float meanG;
  52. public float meanB;
  53. public int scaleValueType;
  54. public float scaleR;
  55. public float scaleG;
  56. public float scaleB;
  57. public int normalizationType;
  58. public bool reverseInputChannels;
  59. public int axisOrder;
  60. }
  61. public class ImageUtils
  62. {
  63. #region private variables
  64. private int _netImgH = 256;
  65. private int _netImgW = 256;
  66. private int _netImgC = 3;
  67. private readonly EnumMeanValueType _meanValueType = EnumMeanValueType.ConstantMean;
  68. private readonly float _meanR = 61;
  69. private readonly float _meanG = 63;
  70. private readonly float _meanB = 66;
  71. private readonly EnumScaleValueType _scaleValueType = EnumScaleValueType.ConstantScale;
  72. private readonly float _scaleR = 256;
  73. private readonly float _scaleG = 256;
  74. private readonly float _scaleB = 256;
  75. private readonly EnumNormalizationType _normalizationType = EnumNormalizationType.MinMaxScalingImage;
  76. private readonly bool _reverseInputChannels = false;
  77. private readonly EnumAxisOrder _axisOrder = EnumAxisOrder.CHW;
  78. private float[] _dataBuffer = null;
  79. private byte[] _padValue = null;
  80. private RawImage _resizedImage;
  81. #endregion
  82. #region properties
  83. public int NetImgH => _netImgH;
  84. public int NetImgW => _netImgW;
  85. public int NetImgC => _netImgC;
  86. public float[] DataBuffer => _dataBuffer;
  87. public byte[] ResizedImageDataBuffer => _resizedImage.DataBuffer;
  88. #endregion
  89. #region dll import
  90. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ResizeBilinear", CallingConvention = CallingConvention.Cdecl)]
  91. public static extern int ResizeBilinear(StructImageInfo origImg, ref StructImageInfo dstImg);
  92. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ResizeBilinearParallel", CallingConvention = CallingConvention.Cdecl)]
  93. public static extern int ResizeBilinearParallel(StructImageInfo origImg, ref StructImageInfo dstImg);
  94. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "FillWithPadValue", CallingConvention = CallingConvention.Cdecl)]
  95. public static extern int FillWithPadValue(ref StructImageInfo origImg, IntPtr padValue);
  96. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "CropRect", CallingConvention = CallingConvention.Cdecl)]
  97. public static extern int CropRect(StructImageInfo origImg, StructMyRect cropRect, ref StructImageInfo dstImg);
  98. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "PasteRect", CallingConvention = CallingConvention.Cdecl)]
  99. public static extern int PasteRect(StructImageInfo srcImg, StructMyRect pasteRect, ref StructImageInfo dstImg);
  100. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsGray", CallingConvention = CallingConvention.Cdecl)]
  101. public static extern int ExtractRGB24AsGray(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo);
  102. [DllImport(@"ImageProcessUtilsLib.dll", EntryPoint = "ExtractRGB24AsColor", CallingConvention = CallingConvention.Cdecl)]
  103. public static extern int ExtractRGB24AsColor(StructImageInfo srcImg, IntPtr dstDataPtr, StructExtractInfo extractInfo);
  104. #endregion
  105. #region constructor
  106. public ImageUtils()
  107. {
  108. _resizedImage = new RawImage(new byte[_netImgH * _netImgW * _netImgC], _netImgW, _netImgH, _netImgC);
  109. _dataBuffer = new float[_netImgH * _netImgW * _netImgC];
  110. _padValue = new byte[3];
  111. _padValue[0] = (byte)_meanB;
  112. _padValue[1] = (byte)_meanG;
  113. _padValue[2] = (byte)_meanR;
  114. }
  115. #endregion
  116. #region public funcs
  117. public RawImage BitmapToRawImage(Bitmap image)
  118. {
  119. int width = image.Width;
  120. int height = image.Height;
  121. PixelFormat pixelFormat = image.PixelFormat;
  122. if (pixelFormat != PixelFormat.Format24bppRgb && pixelFormat != PixelFormat.Format32bppArgb &&
  123. pixelFormat != PixelFormat.Format32bppPArgb && pixelFormat != PixelFormat.Format32bppRgb &&
  124. pixelFormat != PixelFormat.Format8bppIndexed)
  125. {
  126. throw new Exception("Unexpected image pixel format:" + pixelFormat);
  127. }
  128. int origChannel = 3;
  129. int dstChannel = 3;
  130. if (pixelFormat == PixelFormat.Format24bppRgb)
  131. {
  132. origChannel = 3;
  133. }
  134. else if (pixelFormat == PixelFormat.Format8bppIndexed)
  135. {
  136. origChannel = 1;
  137. }
  138. else
  139. {
  140. origChannel = 4;
  141. }
  142. var bmData = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, pixelFormat);
  143. byte[] dstDataArray = new byte[width * height * dstChannel];
  144. unsafe
  145. {
  146. int stride = bmData.Stride;
  147. int dstStride = width * dstChannel;
  148. IntPtr ptr = bmData.Scan0;
  149. IntPtr ptrH, ptrW;
  150. for (int nh = 0; nh < height; nh++)
  151. {
  152. ptrH = IntPtr.Add(ptr, nh * stride);
  153. if (origChannel == dstChannel)
  154. {
  155. Marshal.Copy(ptrH, dstDataArray, nh * dstStride, dstStride);
  156. }
  157. else if (origChannel > dstChannel)
  158. {
  159. for (int nw = 0; nw < width; nw++)
  160. {
  161. ptrW = IntPtr.Add(ptrH, nw * origChannel);
  162. Marshal.Copy(ptrW, dstDataArray, nw * dstChannel, dstChannel);
  163. }
  164. }
  165. else
  166. {
  167. for (int nw = 0; nw < width; nw++)
  168. {
  169. ptrW = IntPtr.Add(ptrH, nw * origChannel);
  170. for (int nc = 0; nc < dstChannel; nc++)
  171. {
  172. Marshal.Copy(ptrW, dstDataArray, nw * dstChannel + nc, 1);
  173. }
  174. }
  175. }
  176. }
  177. }
  178. image.UnlockBits(bmData);
  179. return new RawImage(dstDataArray, width, height, dstChannel);
  180. }
  181. public Bitmap RawImageToBitmap(RawImage image)
  182. {
  183. int width = image.Width;
  184. int height = image.Height;
  185. int channel = image.Channel;
  186. PixelFormat pixelFormat;
  187. switch (channel)
  188. {
  189. case 1:
  190. pixelFormat = PixelFormat.Format8bppIndexed;
  191. break;
  192. case 3:
  193. pixelFormat = PixelFormat.Format24bppRgb;
  194. break;
  195. case 4:
  196. pixelFormat = PixelFormat.Format32bppArgb;
  197. break;
  198. default:
  199. throw new ArgumentOutOfRangeException("The expected image.channel is 1,3,4 but got:" + channel + ".");
  200. }
  201. Bitmap dstBmp = new Bitmap(width, height, pixelFormat);
  202. var bmData = dstBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, pixelFormat);
  203. unsafe
  204. {
  205. int stride = bmData.Stride;
  206. IntPtr ptr = bmData.Scan0;
  207. byte[] dataBuffer = image.DataBuffer;
  208. if (stride == width * channel)
  209. {
  210. Marshal.Copy(dataBuffer, 0, ptr, width * height * channel);
  211. }
  212. else
  213. {
  214. IntPtr ptrDst;
  215. for (int ni = 0; ni < height; ni++)
  216. {
  217. ptrDst = IntPtr.Add(ptr, ni * stride);
  218. Marshal.Copy(dataBuffer, ni * width * channel, ptrDst, width * channel);
  219. }
  220. }
  221. }
  222. dstBmp.UnlockBits(bmData);
  223. return dstBmp;
  224. }
  225. public RawImage ResizeBilinearWithCpp(RawImage image, int dstWidth, int dstHeight)
  226. {
  227. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  228. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  229. StructImageInfo origImgInfo = new StructImageInfo
  230. {
  231. width = image.Width,
  232. height = image.Height,
  233. channel = image.Channel,
  234. pData = pDataOrig,
  235. };
  236. RawImage resizedImg = new RawImage(new byte[dstWidth * dstHeight * image.Channel], dstWidth, dstHeight, image.Channel);
  237. GCHandle hObjectDst = GCHandle.Alloc(resizedImg.DataBuffer, GCHandleType.Pinned);
  238. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  239. StructImageInfo dstImgInfo = new StructImageInfo
  240. {
  241. width = resizedImg.Width,
  242. height = resizedImg.Height,
  243. channel = resizedImg.Channel,
  244. pData = pDataDst,
  245. };
  246. var ret = ResizeBilinear(origImgInfo, ref dstImgInfo);
  247. if (hObjectOrig.IsAllocated)
  248. {
  249. hObjectOrig.Free();
  250. }
  251. if (hObjectDst.IsAllocated)
  252. {
  253. hObjectDst.Free();
  254. }
  255. if (ret != 0)
  256. {
  257. throw new Exception("Failed at calling cpp func: ResizeBilinear.(error code: " + ret + ").");
  258. }
  259. return resizedImg;
  260. }
  261. public RawImage ResizeBilinearParallelWithCpp(RawImage image)
  262. {
  263. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  264. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  265. StructImageInfo origImgInfo = new StructImageInfo
  266. {
  267. width = image.Width,
  268. height = image.Height,
  269. channel = image.Channel,
  270. pData = pDataOrig,
  271. };
  272. RawImage resizedImg = new RawImage(new byte[_netImgH * _netImgW * _netImgC], _netImgW, _netImgH, _netImgC);
  273. GCHandle hObjectDst = GCHandle.Alloc(resizedImg.DataBuffer, GCHandleType.Pinned);
  274. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  275. StructImageInfo dstImgInfo = new StructImageInfo
  276. {
  277. width = resizedImg.Width,
  278. height = resizedImg.Height,
  279. channel = resizedImg.Channel,
  280. pData = pDataDst,
  281. };
  282. var ret = ResizeBilinear(origImgInfo, ref dstImgInfo);
  283. if (hObjectOrig.IsAllocated)
  284. {
  285. hObjectOrig.Free();
  286. }
  287. if (hObjectDst.IsAllocated)
  288. {
  289. hObjectDst.Free();
  290. }
  291. if (ret!=0)
  292. {
  293. throw new Exception("Failed at calling cpp func: ResizeBilinearParallel.");
  294. }
  295. return resizedImg;
  296. }
  297. public void FillPadValWithCpp(RawImage image, byte[] padValue)
  298. {
  299. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  300. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  301. StructImageInfo origImgInfo = new StructImageInfo
  302. {
  303. width = image.Width,
  304. height = image.Height,
  305. channel = image.Channel,
  306. pData = pDataOrig,
  307. };
  308. GCHandle hObjectPadValue = GCHandle.Alloc(padValue, GCHandleType.Pinned);
  309. IntPtr pDataPadValue = hObjectPadValue.AddrOfPinnedObject();
  310. var ret = FillWithPadValue(ref origImgInfo, pDataPadValue);
  311. if (hObjectOrig.IsAllocated)
  312. {
  313. hObjectOrig.Free();
  314. }
  315. if (hObjectPadValue.IsAllocated)
  316. {
  317. hObjectPadValue.Free();
  318. }
  319. if (ret!=0)
  320. {
  321. throw new Exception("Failed at calling cpp func: FillWithPadValue.");
  322. }
  323. }
  324. public RawImage CropRectWithCpp(RawImage image, MyRect cropRect)
  325. {
  326. GCHandle hObjectOrig = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  327. IntPtr pDataOrig = hObjectOrig.AddrOfPinnedObject();
  328. StructImageInfo origImgInfo = new StructImageInfo
  329. {
  330. width = image.Width,
  331. height = image.Height,
  332. channel = image.Channel,
  333. pData = pDataOrig,
  334. };
  335. RawImage dstImage = new RawImage(new byte[cropRect.Width * cropRect.Height * image.Channel],
  336. cropRect.Width, cropRect.Height, image.Channel);
  337. GCHandle hObjectDst = GCHandle.Alloc(dstImage.DataBuffer, GCHandleType.Pinned);
  338. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  339. StructImageInfo dstImgInfo = new StructImageInfo
  340. {
  341. width = dstImage.Width,
  342. height = dstImage.Height,
  343. channel = dstImage.Channel,
  344. pData = pDataDst,
  345. };
  346. StructMyRect structCropRect = new StructMyRect
  347. {
  348. left = cropRect.Left,
  349. top = cropRect.Top,
  350. width = cropRect.Width,
  351. height = cropRect.Height,
  352. };
  353. var ret = CropRect(origImgInfo, structCropRect, ref dstImgInfo);
  354. if (hObjectOrig.IsAllocated)
  355. {
  356. hObjectOrig.Free();
  357. }
  358. if (hObjectDst.IsAllocated)
  359. {
  360. hObjectDst.Free();
  361. }
  362. if (ret!=0)
  363. {
  364. throw new Exception("Failed at calling cpp func: CropRect.");
  365. }
  366. return dstImage;
  367. }
  368. public void PasteRectWithCpp(RawImage dstImg, RawImage srcImg, MyRect pasteRect)
  369. {
  370. GCHandle hObjectSrc = GCHandle.Alloc(srcImg.DataBuffer, GCHandleType.Pinned);
  371. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  372. StructImageInfo srcImgInfo = new StructImageInfo
  373. {
  374. width = srcImg.Width,
  375. height = srcImg.Height,
  376. channel = srcImg.Channel,
  377. pData = pDataSrc,
  378. };
  379. GCHandle hObjectDst = GCHandle.Alloc(dstImg.DataBuffer, GCHandleType.Pinned);
  380. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  381. StructImageInfo dstImgInfo = new StructImageInfo
  382. {
  383. width = dstImg.Width,
  384. height = dstImg.Height,
  385. channel = dstImg.Channel,
  386. pData = pDataDst,
  387. };
  388. StructMyRect structPasteRect = new StructMyRect
  389. {
  390. left = pasteRect.Left,
  391. top = pasteRect.Top,
  392. width = pasteRect.Width,
  393. height = pasteRect.Height,
  394. };
  395. var ret = PasteRect(srcImgInfo, structPasteRect, ref dstImgInfo);
  396. if (hObjectSrc.IsAllocated)
  397. {
  398. hObjectSrc.Free();
  399. }
  400. if (hObjectDst.IsAllocated)
  401. {
  402. hObjectDst.Free();
  403. }
  404. if (ret!=0)
  405. {
  406. throw new Exception("Failed at calling cpp func: PasteRect.");
  407. }
  408. }
  409. /// <summary>
  410. /// 对原始图像进行缩放
  411. /// </summary>
  412. public void DoResize(RawImage image, MyRect roiRect, MyRect moldRect)
  413. {
  414. // 先在resizedImage上填充背景色
  415. _resizedImage.Fill(_padValue);
  416. // 取出原始图像种的指定范围
  417. // 注意:由于CalcResizeParams中,当ResizeMode为FitSmallSizeAndCrop时,ROIRect的位置可能会发生改变
  418. // 所以g.DrawImage时srcRect不要用InputImage里的ROIRect,而是现在的
  419. var roiRectInSrc = image.CropRect(new MyRect(roiRect.Left, roiRect.Top,roiRect.Width, roiRect.Height));
  420. // 再将其resize成指定尺寸
  421. var molded = roiRectInSrc.Resize(moldRect.Width, moldRect.Height, EnumResizeMethod.Bilinear);
  422. // 再将其贴到resizedImage上指定位置
  423. _resizedImage.PasteRect(molded, new MyRect(moldRect.Left, moldRect.Top, moldRect.Width, moldRect.Height));
  424. // 销毁
  425. roiRectInSrc.Dispose();
  426. molded.Dispose();
  427. }
  428. /// <summary>
  429. /// 对原始图像进行缩放(用cpp dll中提供的函数)
  430. /// </summary>
  431. /// <param name="image"></param>
  432. /// <param name="imageMetas"></param>
  433. public void DoResizeCpp(RawImage image, MyRect roiRect, MyRect moldRect)
  434. {
  435. // 先在resizedImage上填充背景色
  436. FillPadValWithCpp(_resizedImage, _padValue);
  437. // 取出原始图像种的指定范围
  438. // 注意:由于CalcResizeParams中,当ResizeMode为FitSmallSizeAndCrop时,ROIRect的位置可能会发生改变
  439. // 所以g.DrawImage时srcRect不要用InputImage里的ROIRect,而是现在的
  440. var roiRectInSrc = CropRectWithCpp(image, new MyRect(roiRect.Left, roiRect.Top, roiRect.Width, roiRect.Height));
  441. // 再将其resize成指定尺寸
  442. var molded = ResizeBilinearWithCpp(roiRectInSrc, moldRect.Width, moldRect.Height);
  443. // 再将其贴到resizedImage上指定位置
  444. PasteRectWithCpp(_resizedImage, molded, new MyRect(moldRect.Left, moldRect.Top, moldRect.Width, moldRect.Height));
  445. // 销毁
  446. roiRectInSrc.Dispose();
  447. molded.Dispose();
  448. }
  449. /// <summary>
  450. /// 将原始图像转成单通道的数组
  451. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  452. /// </summary>
  453. public void ExtractRGB24AsGray(int startInd)
  454. {
  455. // 计算均值
  456. float meanR, meanG, meanB, meanImg;
  457. if (_meanValueType == EnumMeanValueType.CalcMeanImage || _meanValueType == EnumMeanValueType.CalcMeanPerChannel)
  458. {
  459. CalcMean(out meanR, out meanG, out meanB, out meanImg);
  460. }
  461. else
  462. {
  463. meanR = _meanR;
  464. meanG = _meanG;
  465. meanB = _meanB;
  466. meanImg = PixelRGBToGray(meanR, meanG, meanB);
  467. }
  468. // 计算方差
  469. float stdR, stdG, stdB, stdImg;
  470. if (_scaleValueType == EnumScaleValueType.CalcStdImage || _meanValueType == EnumMeanValueType.CalcMeanPerChannel)
  471. {
  472. CalcStd(meanR, meanG, meanB, meanImg, out stdR, out stdG, out stdB, out stdImg);
  473. }
  474. else
  475. {
  476. stdR = _scaleR;
  477. stdG = _scaleG;
  478. stdB = _scaleB;
  479. stdImg = PixelRGBToGray(stdR, stdG, stdB);
  480. }
  481. // 计算最大最小值
  482. float minR = 0, maxR = 0, minG = 0, maxG = 0, minB = 0, maxB = 0, minImg = 0, maxImg = 0;
  483. if (_normalizationType != EnumNormalizationType.None)
  484. {
  485. CalcMinMax(out minR, out maxR, out minG, out maxG, out minB, out maxB, out minImg, out maxImg);
  486. if (_meanValueType != EnumMeanValueType.None)
  487. {
  488. minImg -= meanImg;
  489. maxImg -= meanImg;
  490. }
  491. if (_scaleValueType != EnumScaleValueType.None)
  492. {
  493. minImg /= stdImg;
  494. maxImg /= stdImg;
  495. }
  496. }
  497. // 进行处理
  498. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  499. int resizedImageHeight = _resizedImage.Height;
  500. int resizedImageWidth = _resizedImage.Width;
  501. for (int h = 0; h < resizedImageHeight; h++)
  502. {
  503. int hIndexOffset = resizedImageWidth * h;
  504. for (int w = 0; w < resizedImageWidth; w++)
  505. {
  506. // Bitmap中通道顺序是BGR,所以srcIndex+2是红色通道的值,srcIndex+1是绿色通道的值,srcIndex是蓝色通道的值
  507. var srcIndex = hIndexOffset * 3 + 3 * w;
  508. float bPixelVal = resizedImageDataBuffer[srcIndex];
  509. float gPixelVal = resizedImageDataBuffer[srcIndex + 1];
  510. float rPixelVal = resizedImageDataBuffer[srcIndex + 2];
  511. float gray = PixelRGBToGray(rPixelVal, gPixelVal, bPixelVal);
  512. if (_meanValueType != EnumMeanValueType.None)
  513. {
  514. gray -= meanImg;
  515. }
  516. if (_scaleValueType != EnumScaleValueType.None)
  517. {
  518. gray /= stdImg;
  519. }
  520. if (_normalizationType != EnumNormalizationType.None)
  521. {
  522. gray = (gray - minImg) / (maxImg - minImg);
  523. }
  524. var dstIndex = startInd + hIndexOffset + w;
  525. _dataBuffer[dstIndex] = gray;
  526. }
  527. }
  528. }
  529. /// <summary>
  530. /// 将原始图像转成单通道的数组(用cpp dll中提供的函数)
  531. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  532. /// </summary>
  533. public void ExtractRGB24AsGrayCpp(int startInd)
  534. {
  535. GCHandle hObjectSrc = GCHandle.Alloc(_resizedImage.DataBuffer, GCHandleType.Pinned);
  536. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  537. StructImageInfo srcImgInfo = new StructImageInfo
  538. {
  539. width = _resizedImage.Width,
  540. height = _resizedImage.Height,
  541. channel = _resizedImage.Channel,
  542. pData = pDataSrc,
  543. };
  544. GCHandle hObjectDst = GCHandle.Alloc(_dataBuffer, GCHandleType.Pinned);
  545. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  546. pDataDst = IntPtr.Add(pDataDst, startInd * sizeof(float));
  547. StructExtractInfo extractInfo = new StructExtractInfo
  548. {
  549. meanValueType = (int)_meanValueType,
  550. meanR = _meanR,
  551. meanG = _meanG,
  552. meanB = _meanB,
  553. scaleValueType = (int)_scaleValueType,
  554. scaleR = _scaleR,
  555. scaleG = _scaleG,
  556. scaleB = _scaleB,
  557. normalizationType = (int)_normalizationType,
  558. reverseInputChannels = _reverseInputChannels,
  559. axisOrder = (int)_axisOrder,
  560. };
  561. var ret = ExtractRGB24AsGray(srcImgInfo, pDataDst, extractInfo);
  562. if (hObjectSrc.IsAllocated)
  563. {
  564. hObjectSrc.Free();
  565. }
  566. if (hObjectDst.IsAllocated)
  567. {
  568. hObjectDst.Free();
  569. }
  570. if (ret != 0)
  571. {
  572. throw new Exception("Failed at calling cpp func: ExtractRGB24AsGray.(error code: " + ret + ").");
  573. }
  574. }
  575. /// <summary>
  576. /// 将原始图像转成三维数组
  577. /// 根据axisOrder决定到底是CHW还是HWC的数据排列顺序
  578. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  579. /// </summary>
  580. public void ExtractRGB24AsColor(int startInd)
  581. {
  582. // 计算均值
  583. float meanR, meanG, meanB, meanImg;
  584. if (_meanValueType == EnumMeanValueType.CalcMeanImage || _meanValueType == EnumMeanValueType.CalcMeanPerChannel)
  585. {
  586. CalcMean(out meanR, out meanG, out meanB, out meanImg);
  587. }
  588. else
  589. {
  590. meanR = _meanR;
  591. meanG = _meanG;
  592. meanB = _meanB;
  593. meanImg = PixelRGBToGray(meanR, meanG, meanB);
  594. }
  595. // 计算方差
  596. float stdR, stdG, stdB, stdImg;
  597. if (_scaleValueType == EnumScaleValueType.CalcStdImage || _scaleValueType == EnumScaleValueType.CalcStdPerChannel)
  598. {
  599. CalcStd(meanR, meanG, meanB, meanImg, out stdR, out stdG, out stdB, out stdImg);
  600. }
  601. else
  602. {
  603. stdR = _scaleR;
  604. stdG = _scaleG;
  605. stdB = _scaleB;
  606. stdImg = PixelRGBToGray(stdR, stdG, stdB);
  607. }
  608. // 计算最大最小值
  609. float minR = 0, maxR = 0, minG = 0, maxG = 0, minB = 0, maxB = 0, minImg = 0, maxImg = 0;
  610. if (_normalizationType != EnumNormalizationType.None)
  611. {
  612. CalcMinMax(out minR, out maxR, out minG, out maxG, out minB, out maxB, out minImg, out maxImg);
  613. if (_meanValueType != EnumMeanValueType.None)
  614. {
  615. minR -= meanR;
  616. maxR -= meanR;
  617. minG -= meanG;
  618. maxG -= meanG;
  619. minB -= meanB;
  620. maxB -= meanB;
  621. minImg -= meanImg;
  622. maxImg -= meanImg;
  623. }
  624. if (_scaleValueType != EnumScaleValueType.None)
  625. {
  626. minR /= stdR;
  627. maxR /= stdR;
  628. minG /= stdG;
  629. maxG /= stdG;
  630. minB /= stdB;
  631. maxB /= stdB;
  632. minImg /= stdImg;
  633. maxImg /= stdImg;
  634. }
  635. }
  636. // 准备
  637. float[] meanValues = new float[3];
  638. if (_meanValueType == EnumMeanValueType.CalcMeanImage)
  639. {
  640. meanValues[0] = meanImg;
  641. meanValues[1] = meanImg;
  642. meanValues[2] = meanImg;
  643. }
  644. else
  645. {
  646. meanValues[0] = meanB;
  647. meanValues[1] = meanG;
  648. meanValues[2] = meanR;
  649. }
  650. float[] scaleValues = new float[3];
  651. if (_scaleValueType == EnumScaleValueType.CalcStdImage)
  652. {
  653. scaleValues[0] = stdImg;
  654. scaleValues[1] = stdImg;
  655. scaleValues[2] = stdImg;
  656. }
  657. else
  658. {
  659. scaleValues[0] = stdB;
  660. scaleValues[1] = stdG;
  661. scaleValues[2] = stdR;
  662. }
  663. float[] minValues = new float[3];
  664. float[] maxValues = new float[3];
  665. if (_normalizationType == EnumNormalizationType.MinMaxScalingImage)
  666. {
  667. minValues[0] = minImg;
  668. minValues[1] = minImg;
  669. minValues[2] = minImg;
  670. maxValues[0] = maxImg;
  671. maxValues[1] = maxImg;
  672. maxValues[2] = maxImg;
  673. }
  674. else
  675. {
  676. minValues[0] = minB;
  677. minValues[1] = minG;
  678. minValues[2] = minR;
  679. maxValues[0] = maxB;
  680. maxValues[1] = maxG;
  681. maxValues[2] = maxR;
  682. }
  683. // 进行处理
  684. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  685. int resizedImageHeight = _resizedImage.Height;
  686. int resizedImageWidth = _resizedImage.Width;
  687. int resizedImageSize = resizedImageHeight * resizedImageWidth;
  688. for (int c = 0; c < 3; c++)
  689. {
  690. for (int h = 0; h < resizedImageHeight; h++)
  691. {
  692. int hIndexOffset = resizedImageWidth * h;
  693. for (int w = 0; w < resizedImageWidth; w++)
  694. {
  695. var srcIndex = resizedImageWidth * 3 * h + 3 * w + c;
  696. float gray = resizedImageDataBuffer[srcIndex];
  697. if (_meanValueType != EnumMeanValueType.None)
  698. {
  699. gray = gray - meanValues[c];
  700. }
  701. if (_scaleValueType != EnumScaleValueType.None)
  702. {
  703. gray = gray / scaleValues[c];
  704. }
  705. if (_normalizationType != EnumNormalizationType.None)
  706. {
  707. gray = (gray - minValues[c]) / (maxValues[c] - minValues[c]);
  708. }
  709. int dstIndex = 0;
  710. switch (_axisOrder)
  711. {
  712. case EnumAxisOrder.CHW:
  713. if (_reverseInputChannels)
  714. {
  715. // 为真,则输出需要是BGR, 正好和Bitmap中一致
  716. dstIndex = resizedImageSize * c + hIndexOffset + w;
  717. }
  718. else
  719. {
  720. // 为假,则输出需要是RGB, 则需要转换一下 c' = (2-c)
  721. dstIndex = resizedImageSize * (2 - c) + hIndexOffset + w;
  722. }
  723. break;
  724. case EnumAxisOrder.HWC:
  725. if (_reverseInputChannels)
  726. {
  727. // 为真,则输出需要时BGR, 正好和Bitmap中一致
  728. dstIndex = hIndexOffset * 3 + 3 * w + c;
  729. }
  730. else
  731. {
  732. // 为假,则输出需要是RGB,则需要转换一下,c'= 2-c
  733. dstIndex = hIndexOffset * 3 + 3 * w + (2 - c);
  734. }
  735. break;
  736. }
  737. dstIndex += startInd;
  738. _dataBuffer[dstIndex] = gray;
  739. }
  740. }
  741. }
  742. }
  743. /// <summary>
  744. /// 将原始图像转成三维数组(用cpp dll中提供的函数)
  745. /// 根据axisOrder决定到底是CHW还是HWC的数据排列顺序
  746. /// (如果需要做减均值和Scale等操作,也在这一步完成)
  747. /// </summary>
  748. public void ExtractRGB24AsColorCpp(int startInd)
  749. {
  750. GCHandle hObjectSrc = GCHandle.Alloc(_resizedImage.DataBuffer, GCHandleType.Pinned);
  751. IntPtr pDataSrc = hObjectSrc.AddrOfPinnedObject();
  752. StructImageInfo srcImgInfo = new StructImageInfo
  753. {
  754. width = _resizedImage.Width,
  755. height = _resizedImage.Height,
  756. channel = _resizedImage.Channel,
  757. pData = pDataSrc,
  758. };
  759. GCHandle hObjectDst = GCHandle.Alloc(_dataBuffer, GCHandleType.Pinned);
  760. IntPtr pDataDst = hObjectDst.AddrOfPinnedObject();
  761. pDataDst = IntPtr.Add(pDataDst, startInd * sizeof(float));
  762. StructExtractInfo extractInfo = new StructExtractInfo
  763. {
  764. meanValueType = (int)_meanValueType,
  765. meanR = _meanR,
  766. meanG = _meanG,
  767. meanB = _meanB,
  768. scaleValueType = (int)_scaleValueType,
  769. scaleR = _scaleR,
  770. scaleG = _scaleG,
  771. scaleB = _scaleB,
  772. normalizationType = (int)_normalizationType,
  773. reverseInputChannels = _reverseInputChannels,
  774. axisOrder = (int)_axisOrder,
  775. };
  776. var ret = ExtractRGB24AsColor(srcImgInfo, pDataDst, extractInfo);
  777. if (hObjectSrc.IsAllocated)
  778. {
  779. hObjectSrc.Free();
  780. }
  781. if (hObjectDst.IsAllocated)
  782. {
  783. hObjectDst.Free();
  784. }
  785. if (ret != 0)
  786. {
  787. throw new Exception("Failed at calling cpp func: ExtractRGB24AsColor.(error code: " + ret + ").");
  788. }
  789. }
  790. /// <summary>
  791. /// 计算均值
  792. /// </summary>
  793. /// <param name="meanR"></param>
  794. /// <param name="meanG"></param>
  795. /// <param name="meanB"></param>
  796. /// <param name="meanImg"></param>
  797. private void CalcMean(out float meanR, out float meanG, out float meanB, out float meanImg)
  798. {
  799. meanR = 0;
  800. meanG = 0;
  801. meanB = 0;
  802. int imgSize = _resizedImage.Width * _resizedImage.Height;
  803. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  804. for (int ni = 0; ni < imgSize; ni++)
  805. {
  806. int offset = 3 * ni;
  807. meanB += resizedImageDataBuffer[offset];
  808. meanG += resizedImageDataBuffer[offset + 1];
  809. meanR += resizedImageDataBuffer[offset + 2];
  810. }
  811. meanR /= imgSize;
  812. meanG /= imgSize;
  813. meanB /= imgSize;
  814. meanImg = PixelRGBToGray(meanR, meanG, meanB);
  815. }
  816. /// <summary>
  817. /// 计算方差
  818. /// </summary>
  819. /// <param name="stdR"></param>
  820. /// <param name="stdG"></param>
  821. /// <param name="stdB"></param>
  822. /// <param name="stdImg"></param>
  823. private void CalcStd(float meanR, float meanG, float meanB, float meanImg,
  824. out float stdR, out float stdG, out float stdB, out float stdImg)
  825. {
  826. double sumOfSquareR = 0;
  827. double sumOfSquareG = 0;
  828. double sumOfSquareB = 0;
  829. double sumOfSquareImg = 0;
  830. int imgSize = _resizedImage.Width * _resizedImage.Height;
  831. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  832. for (int ni = 0; ni < imgSize; ni++)
  833. {
  834. int offset = 3 * ni;
  835. byte b = resizedImageDataBuffer[offset];
  836. byte g = resizedImageDataBuffer[offset + 1];
  837. byte r = resizedImageDataBuffer[offset + 2];
  838. sumOfSquareB += Math.Pow(b - meanB, 2);
  839. sumOfSquareG += Math.Pow(g - meanG, 2);
  840. sumOfSquareR += Math.Pow(r - meanR, 2);
  841. sumOfSquareImg += (Math.Pow(r - meanImg, 2) + Math.Pow(g - meanImg, 2) + Math.Pow(b - meanImg, 2));
  842. }
  843. stdR = (float)Math.Sqrt(sumOfSquareR / imgSize);
  844. stdG = (float)Math.Sqrt(sumOfSquareG / imgSize);
  845. stdB = (float)Math.Sqrt(sumOfSquareB / imgSize);
  846. stdImg = (float)Math.Sqrt(sumOfSquareImg / (imgSize * 3));
  847. }
  848. /// <summary>
  849. /// 计算最大最小值
  850. /// </summary>
  851. /// <param name="minR"></param>
  852. /// <param name="maxR"></param>
  853. /// <param name="minG"></param>
  854. /// <param name="maxG"></param>
  855. /// <param name="minB"></param>
  856. /// <param name="maxB"></param>
  857. /// <param name="minImg"></param>
  858. /// <param name="maxImg"></param>
  859. private void CalcMinMax(out float minR, out float maxR, out float minG, out float maxG,
  860. out float minB, out float maxB, out float minImg, out float maxImg)
  861. {
  862. minR = float.MaxValue;
  863. minG = float.MaxValue;
  864. minB = float.MaxValue;
  865. maxR = float.MinValue;
  866. maxG = float.MinValue;
  867. maxB = float.MinValue;
  868. int imgSize = _resizedImage.Width * _resizedImage.Height;
  869. byte[] resizedImageDataBuffer = _resizedImage.DataBuffer;
  870. for (int ni = 0; ni < imgSize; ni++)
  871. {
  872. int offset = 3 * ni;
  873. byte b = resizedImageDataBuffer[offset];
  874. byte g = resizedImageDataBuffer[offset + 1];
  875. byte r = resizedImageDataBuffer[offset + 2];
  876. if (r < minR)
  877. {
  878. minR = r;
  879. }
  880. if (r > maxR)
  881. {
  882. maxR = r;
  883. }
  884. if (g < minG)
  885. {
  886. minG = g;
  887. }
  888. if (g > maxG)
  889. {
  890. maxG = g;
  891. }
  892. if (b < minB)
  893. {
  894. minB = b;
  895. }
  896. if (b > maxB)
  897. {
  898. maxB = b;
  899. }
  900. }
  901. minImg = resizedImageDataBuffer.Min();
  902. maxImg = resizedImageDataBuffer.Max();
  903. }
  904. /// <summary>
  905. /// 将单个像素的RGB值转换为灰度值
  906. /// </summary>
  907. /// <param name="Rvalue"></param>
  908. /// <param name="Gvalue"></param>
  909. /// <param name="Bvalue"></param>
  910. /// <returns></returns>
  911. private float PixelRGBToGray(float Rvalue, float Gvalue, float Bvalue)
  912. {
  913. return (float)0.299 * Rvalue + (float)0.587 * Gvalue + (float)0.114 * Bvalue;
  914. }
  915. #endregion
  916. }
  917. }