AutoBlineCalculation.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using System.Runtime.InteropServices;
  5. using AI.Common;
  6. using AI.Common.Log;
  7. using AI.Common.Tools;
  8. using static System.Net.Mime.MediaTypeNames;
  9. namespace AutoBlineCalculationLib
  10. {
  11. public class AutoBlineCalculation
  12. {
  13. #region internal
  14. /// <summary>
  15. /// cpp dll中模型分割结果的结构体
  16. /// </summary>
  17. [StructLayout(LayoutKind.Sequential)]
  18. internal struct StructCppIDetectedObject
  19. {
  20. public int label;
  21. public float confidence;
  22. public Rect boundingBox;
  23. public int pointNum;
  24. }
  25. /// <summary>
  26. /// cpp dll中扇形成像区域的相关信息
  27. /// </summary>
  28. [StructLayout(LayoutKind.Sequential)]
  29. internal struct StructCppSectorImageRegion
  30. {
  31. public float startRadius;
  32. public float startAngle;
  33. public float radiusSpan;
  34. public float angleSpan;
  35. public Point2D centerOfArc;
  36. public Point2D startUp;
  37. public Point2D startDown;
  38. public Point2D endUp;
  39. public Point2D endDown;
  40. public int contourPointsNum;
  41. }
  42. /// <summary>
  43. /// cpp dll中平行四边形成像区域的相关信息
  44. /// </summary>
  45. [StructLayout(LayoutKind.Sequential)]
  46. internal struct StructCppParallelogramImageRegion
  47. {
  48. public float horizontalSideLength;
  49. public float slantSideLength;
  50. public float angle;
  51. public Point2D center;
  52. public Point2D leftUp;
  53. public Point2D leftDown;
  54. public Point2D rightUp;
  55. public Point2D rightDown;
  56. public int contourPointsNum;
  57. }
  58. /// <summary>
  59. /// cpp dll中参与B线得分计算的一些特征的相关信息(凸阵探头)
  60. /// </summary>
  61. [StructLayout(LayoutKind.Sequential)]
  62. internal struct StructCppSectorBLineScoreFeatures
  63. {
  64. public int label;
  65. public float startRadius;
  66. public float startAngle;
  67. public float radiusSpan;
  68. public float angleSpan;
  69. public Point2D centerOfArc;
  70. }
  71. /// <summary>
  72. /// cpp dll中参与B线得分计算的一些特征的相关信息(线阵探头)
  73. /// </summary>
  74. [StructLayout(LayoutKind.Sequential)]
  75. internal struct StructCppParallelogramBLineScoreFeatures
  76. {
  77. public int label;
  78. public float horizontalSideLength;
  79. public float slantSideLength;
  80. public float angle;
  81. public Point2D center;
  82. }
  83. /// <summary>
  84. /// cpp dll中感兴趣区域的相关信息(凸阵探头)
  85. /// </summary>
  86. [StructLayout(LayoutKind.Sequential)]
  87. internal struct StructCppSectorRegionOfInterestInfo
  88. {
  89. public float startRadius;
  90. public float startAngle;
  91. public float radiusSpan;
  92. public float angleSpan;
  93. public Point2D centerOfArc;
  94. public Point2D startUp;
  95. public Point2D startDown;
  96. public Point2D endUp;
  97. public Point2D endDown;
  98. public float regionOfInterestScore;
  99. }
  100. /// <summary>
  101. /// cpp dll中感兴趣区域的相关信息(线阵探头)
  102. /// </summary>
  103. [StructLayout(LayoutKind.Sequential)]
  104. internal struct StructCppParallelogramRegionOfInterestInfo
  105. {
  106. public float horizontalSideLength;
  107. public float slantSideLength;
  108. public float angle;
  109. public Point2D center;
  110. public Point2D leftUp;
  111. public Point2D leftDown;
  112. public Point2D rightUp;
  113. public Point2D rightDown;
  114. public float regionOfInterestScore;
  115. }
  116. #endregion
  117. #region dllimport
  118. [DllImport(@"AutoBLineCalculationPostProcess", CallingConvention = CallingConvention.Cdecl)]
  119. [return: MarshalAs(UnmanagedType.I1)]
  120. internal static extern bool CalculateBLineScoreSectorImageRegion(IntPtr img_data, int channels, int imgW, int imgH,
  121. float cmPerPixel, StructCppSectorImageRegion imageRegion, IntPtr imageRegionContourPoints,
  122. int objectNum, StructCppIDetectedObject[] detectedObjects, IntPtr detectedObjectContourPoints,
  123. IntPtr bLineScoreFeatures, ref int featuresNum, StructCppSectorRegionOfInterestInfo sectorRegionOfInterestInfo, ref float regionOfInterestScore);
  124. [DllImport(@"AutoBLineCalculationPostProcess", CallingConvention = CallingConvention.Cdecl)]
  125. [return: MarshalAs(UnmanagedType.I1)]
  126. internal static extern bool CalculateBLineScoreParallelogramImageRegion(IntPtr img_data, int channels, int imgW, int imgH,
  127. float cmPerPixel, StructCppParallelogramImageRegion imageRegion, IntPtr imageRegionContourPoints,
  128. int objectNum, StructCppIDetectedObject[] detectedObjects, IntPtr detectedObjectContourPoints,
  129. IntPtr bLineScoreFeatures, ref int featuresNum, StructCppParallelogramRegionOfInterestInfo ParallelogramRegionOfInterestInfo, ref float regionOfInterestScore);
  130. #endregion
  131. #region field
  132. private IInferenceNetwork _inferNet;
  133. private static InferenceCore _inferenceCore = new InferenceCore();
  134. #endregion
  135. #region event
  136. /// <summary>
  137. /// 通知订阅者,推理过程中有log要记录
  138. /// </summary>
  139. public event EventHandler<LogEventArgs> NotifyLog;
  140. /// <summary>
  141. /// 通知订阅者,推理过程中发生了错误
  142. /// </summary>
  143. public event EventHandler<ErrorEventArgs> NotifyError;
  144. #endregion
  145. #region private funcs
  146. /// <summary>
  147. /// 单幅图的B线分割
  148. /// </summary>
  149. /// <param name="rawImg">rawImg格式图像</param>
  150. /// <param name="cropRect">裁图框区域</param>
  151. /// <returns></returns>
  152. private DetectedObject[] BlineModelProcess(RawImage rawImg)
  153. {
  154. List<DetectedObject> detected = new List<DetectedObject>();
  155. try
  156. {
  157. Rect cropRect = new Rect(0, 0, rawImg.Width, rawImg.Height);
  158. //对一张图进行推理
  159. InferenceNetworkInputImage inferInput = new InferenceNetworkInputImage(rawImg, cropRect);
  160. IDetectedObject[] inferResults = _inferNet.Process(inferInput);
  161. if (inferResults.Length == 0)
  162. {
  163. detected.Add(new DetectedObject());
  164. return detected.ToArray();
  165. }
  166. else
  167. {
  168. foreach (var obj in inferResults)
  169. {
  170. detected.Add(new DetectedObject(obj.Label, obj.Confidence, obj.BoundingBox, obj.Contours));
  171. }
  172. return detected.ToArray();
  173. }
  174. }
  175. catch (Exception excep)
  176. {
  177. NotifyError?.Invoke(this, new ErrorEventArgs(excep));
  178. detected.Add(new DetectedObject());
  179. return detected.ToArray();
  180. }
  181. }
  182. /// <summary>
  183. /// 统计模型分割结果
  184. /// 统计各个结果在超声图像区域下的相关信息
  185. /// 统计B线评分
  186. /// </summary>
  187. /// <param name="rawImg"></param>
  188. /// <param name="segResults"></param>
  189. /// <returns></returns>
  190. private BlineScoringResults BlineCalculation(RawImage image, DetectedObject[] segResults, float cmPerPixel, IUltrasoundImageRegion imageRegion,
  191. IUltrasoundImageRegion roiSectorOrParallelogram, BlineScoringResults allResults)
  192. {
  193. try
  194. {
  195. int imgWidth = image.Width;
  196. int imgHeight = image.Height;
  197. int channel = image.BytesPerPixel;
  198. // 取出所有检测到的组织结构,及对应的轮廓
  199. int objectNum = segResults.Length;
  200. List<Point2D> allContourPoints = new List<Point2D>();
  201. StructCppIDetectedObject[] detectedObjectStructs = new StructCppIDetectedObject[objectNum];
  202. for (int ni = 0; ni < objectNum; ni++)
  203. {
  204. detectedObjectStructs[ni].label = segResults[ni].Label;
  205. detectedObjectStructs[ni].confidence = segResults[ni].Confidence;
  206. detectedObjectStructs[ni].boundingBox = segResults[ni].BoundingBox;
  207. detectedObjectStructs[ni].pointNum = segResults[ni].Contours[0].Length;
  208. allContourPoints.AddRange(segResults[ni].Contours[0]);
  209. }
  210. // 获取指针
  211. GCHandle hObjectContour = GCHandle.Alloc(allContourPoints.ToArray(), GCHandleType.Pinned);
  212. IntPtr pObjectContour = hObjectContour.AddrOfPinnedObject();
  213. GCHandle hObjectImg = GCHandle.Alloc(image.DataBuffer, GCHandleType.Pinned);
  214. IntPtr pObjectImg = hObjectImg.AddrOfPinnedObject();
  215. // 可做B线评分的特征数组的最大尺寸,暂定为30
  216. int featuresNum = 30;
  217. // 取出超声成像区域
  218. List<Point2D> imageRegionContourPoints = new List<Point2D>();
  219. var regionType = imageRegion.GetType();
  220. float regionOfInterestScore = 0; //暂时赋值为0
  221. // 凸阵探头
  222. if (regionType == typeof(ConvexArrayUltrasoundImageRegion))
  223. {
  224. StructCppSectorBLineScoreFeatures[] features = new StructCppSectorBLineScoreFeatures[featuresNum];
  225. var sector = (Sector)((ConvexArrayUltrasoundImageRegion)imageRegion).Geometry;
  226. StructCppSectorImageRegion sectorImageRegion = new StructCppSectorImageRegion
  227. {
  228. startAngle = sector.StartAngle,
  229. startRadius = sector.StartRadius,
  230. angleSpan = sector.AngleSpan,
  231. radiusSpan = sector.RadiusSpan,
  232. centerOfArc = sector.CenterOfArc,
  233. startUp = sector.StartUp,
  234. endUp = sector.EndUp,
  235. startDown = sector.StartDown,
  236. endDown = sector.EndDown,
  237. contourPointsNum = sector.Contour.Points.Length,
  238. };
  239. // 获取感兴趣区域结构体的相关参数
  240. StructCppSectorRegionOfInterestInfo sectorRegionOfInterestInfo = new StructCppSectorRegionOfInterestInfo();
  241. var sectorROI = (Sector)((ConvexArrayUltrasoundImageRegion)roiSectorOrParallelogram).Geometry;
  242. sectorRegionOfInterestInfo.startRadius = sectorROI.StartRadius;
  243. sectorRegionOfInterestInfo.startAngle = sectorROI.StartAngle;
  244. sectorRegionOfInterestInfo.radiusSpan = sectorROI.RadiusSpan;
  245. sectorRegionOfInterestInfo.angleSpan = sectorROI.AngleSpan;
  246. sectorRegionOfInterestInfo.centerOfArc = sectorROI.CenterOfArc;
  247. sectorRegionOfInterestInfo.startUp = sectorROI.StartUp;
  248. sectorRegionOfInterestInfo.startDown = sectorROI.StartDown;
  249. sectorRegionOfInterestInfo.endUp = sectorROI.EndUp;
  250. sectorRegionOfInterestInfo.endDown = sectorROI.EndDown;
  251. sectorRegionOfInterestInfo.regionOfInterestScore = 0; // 暂定感兴趣区域得分为0,记为红色框
  252. imageRegionContourPoints.AddRange(sector.Contour.Points);
  253. GCHandle hImageRegionContour = GCHandle.Alloc(imageRegionContourPoints.ToArray(), GCHandleType.Pinned);
  254. IntPtr pImageRegionContour = hImageRegionContour.AddrOfPinnedObject();
  255. GCHandle hFeatures = GCHandle.Alloc(features, GCHandleType.Pinned);
  256. IntPtr pFeatures = hFeatures.AddrOfPinnedObject();
  257. var ret = CalculateBLineScoreSectorImageRegion(pObjectImg, channel, imgWidth, imgHeight, (float)cmPerPixel,
  258. sectorImageRegion, pImageRegionContour, objectNum, detectedObjectStructs, pObjectContour, pFeatures, ref featuresNum, sectorRegionOfInterestInfo, ref regionOfInterestScore);
  259. // 释放
  260. if (hObjectContour.IsAllocated)
  261. {
  262. hObjectContour.Free();
  263. }
  264. if (hObjectImg.IsAllocated)
  265. {
  266. hObjectImg.Free();
  267. }
  268. if (hImageRegionContour.IsAllocated)
  269. {
  270. hImageRegionContour.Free();
  271. }
  272. if (hFeatures.IsAllocated)
  273. {
  274. hFeatures.Free();
  275. }
  276. // 统计并保存分割结果、凸阵探头下超声成像区域相关信息、B线评分
  277. // 1、将分割模型得到的目标结果放置在定义好的结构体中
  278. BlineRecognizedObjectInfo[] recognizedObjects = new BlineRecognizedObjectInfo[segResults.Length];
  279. for (int ni = 0; ni < segResults.Length; ni++)
  280. {
  281. recognizedObjects[ni] = new BlineRecognizedObjectInfo((EnumBlineRecognizedObjectType)segResults[ni].Label,
  282. segResults[ni].Confidence, segResults[ni].Contours[0]);
  283. }
  284. // 2、将凸阵探头下的每条B线轮廓换算成需要的扇形信息并放置至BLineScoringInfo函数中
  285. BLineScoringInfo[] calculatedObjects = new BLineScoringInfo[featuresNum];
  286. for (int ni = 0; ni < featuresNum; ni++)
  287. {
  288. IUltrasoundImageRegion eachgeometry;
  289. var depthSpanInPixel = (int)features[ni].radiusSpan;
  290. var centerDepthInPixel = (int)features[ni].startRadius + depthSpanInPixel / 2;
  291. var imageWidthInPixel = 2 * (int)features[ni].centerOfArc.X;
  292. var scanMaxWidth = features[ni].angleSpan;
  293. var originToProbeSurfaceInPixel = features[ni].centerOfArc.Y / -(Math.Cos(MathHelper.DegreeToRadian(scanMaxWidth / 2)));
  294. var startAngle = features[ni].startAngle;
  295. var centerOfAngle = startAngle + scanMaxWidth / 2;
  296. var beamPosition = centerOfAngle - 270;
  297. var widthSpan = features[ni].angleSpan;
  298. var geoTest = new ConvexArrayUltrasoundImageRegion((int)originToProbeSurfaceInPixel, (int)imageWidthInPixel, (int)centerDepthInPixel, (int)depthSpanInPixel,
  299. (double)scanMaxWidth, (double)widthSpan, (double)beamPosition);
  300. eachgeometry = geoTest;
  301. // 保存每条B线轮廓换算后的扇形信息
  302. calculatedObjects[ni] = new BLineScoringInfo((EnumBlineScoreType)features[ni].label, eachgeometry);
  303. }
  304. // 3、计算单帧图像中总的B线得分
  305. int finalScore = CalculateBLineScore(calculatedObjects);
  306. // 4、更新感兴趣区域及对应得分
  307. RegionOfInterestInfo regionOfInterestInfo = new RegionOfInterestInfo();
  308. regionOfInterestInfo.UltrasoundRegionOfInterest = roiSectorOrParallelogram;
  309. regionOfInterestInfo.RegionOfInterestScore = regionOfInterestScore;
  310. // 5、保存所有结果
  311. allResults.BlineScore = finalScore;
  312. allResults.BlineScoringInfo = calculatedObjects;
  313. allResults.RecognizedObjects = recognizedObjects;
  314. allResults.RegionOfInterestInfo = regionOfInterestInfo;
  315. return allResults;
  316. }
  317. // 线阵探头
  318. else if (regionType == typeof(LinearArrayUltrasoundImageRegion))
  319. {
  320. StructCppParallelogramBLineScoreFeatures[] features = new StructCppParallelogramBLineScoreFeatures[featuresNum];
  321. var region = (LinearArrayUltrasoundImageRegion)imageRegion;
  322. StructCppParallelogramImageRegion parallelogramImageRegion = new StructCppParallelogramImageRegion();
  323. StructCppParallelogramRegionOfInterestInfo ParallelogramRegionOfInterestInfo = new StructCppParallelogramRegionOfInterestInfo();
  324. if (MathHelper.AlmostEqual(region.Steer, 0))
  325. {
  326. var rectangle = (Rectangle)(region.Geometry);
  327. parallelogramImageRegion = new StructCppParallelogramImageRegion
  328. {
  329. horizontalSideLength = rectangle.Width,
  330. slantSideLength = rectangle.Height,
  331. angle = 0,
  332. center = rectangle.Center,
  333. leftUp = rectangle.LeftUp,
  334. leftDown = rectangle.LeftDown,
  335. rightUp = rectangle.RightUp,
  336. rightDown = rectangle.RightDown,
  337. contourPointsNum = rectangle.Contour.Points.Length,
  338. };
  339. imageRegionContourPoints.AddRange(rectangle.Contour.Points);
  340. // 获取感兴趣区域结构体的相关参数
  341. var rectangleROI = (Rectangle)((LinearArrayUltrasoundImageRegion)roiSectorOrParallelogram).Geometry;
  342. ParallelogramRegionOfInterestInfo.horizontalSideLength = rectangleROI.Width;
  343. ParallelogramRegionOfInterestInfo.slantSideLength = rectangleROI.Height;
  344. ParallelogramRegionOfInterestInfo.angle = 0;
  345. ParallelogramRegionOfInterestInfo.center = rectangleROI.Center;
  346. ParallelogramRegionOfInterestInfo.leftUp = rectangleROI.LeftUp;
  347. ParallelogramRegionOfInterestInfo.leftDown = rectangleROI.LeftDown;
  348. ParallelogramRegionOfInterestInfo.rightUp = rectangleROI.RightUp;
  349. ParallelogramRegionOfInterestInfo.rightDown = rectangleROI.RightDown;
  350. ParallelogramRegionOfInterestInfo.regionOfInterestScore = 0; // 暂定感兴趣区域得分为0,记为红色框
  351. }
  352. else
  353. {
  354. var parallelogram = (Parallelogram)(region.Geometry);
  355. parallelogramImageRegion = new StructCppParallelogramImageRegion
  356. {
  357. horizontalSideLength = parallelogram.HorizontalSideLength,
  358. slantSideLength = parallelogram.SlantSideLength,
  359. angle = parallelogram.Angle,
  360. center = parallelogram.Center,
  361. leftUp = parallelogram.LeftUp,
  362. leftDown = parallelogram.LeftDown,
  363. rightUp = parallelogram.RightUp,
  364. rightDown = parallelogram.RightDown,
  365. contourPointsNum = parallelogram.Contour.Points.Length,
  366. };
  367. imageRegionContourPoints.AddRange(parallelogram.Contour.Points);
  368. // 获取感兴趣区域结构体的相关参数
  369. var parallelogramROI = (Parallelogram)((LinearArrayUltrasoundImageRegion)roiSectorOrParallelogram).Geometry;
  370. ParallelogramRegionOfInterestInfo.horizontalSideLength = parallelogramROI.HorizontalSideLength;
  371. ParallelogramRegionOfInterestInfo.slantSideLength = parallelogramROI.SlantSideLength;
  372. ParallelogramRegionOfInterestInfo.angle = parallelogramROI.Angle;
  373. ParallelogramRegionOfInterestInfo.center = parallelogramROI.Center;
  374. ParallelogramRegionOfInterestInfo.leftUp = parallelogramROI.LeftUp;
  375. ParallelogramRegionOfInterestInfo.leftDown = parallelogramROI.LeftDown;
  376. ParallelogramRegionOfInterestInfo.rightUp = parallelogramROI.RightUp;
  377. ParallelogramRegionOfInterestInfo.rightDown = parallelogramROI.RightDown;
  378. ParallelogramRegionOfInterestInfo.regionOfInterestScore = 0; // 暂定感兴趣区域得分为0,记为红色框
  379. }
  380. GCHandle hImageRegionContour = GCHandle.Alloc(imageRegionContourPoints.ToArray(), GCHandleType.Pinned);
  381. IntPtr pImageRegionContour = hImageRegionContour.AddrOfPinnedObject();
  382. GCHandle hFeatures = GCHandle.Alloc(features, GCHandleType.Pinned);
  383. IntPtr pFeatures = hFeatures.AddrOfPinnedObject();
  384. var ret = CalculateBLineScoreParallelogramImageRegion(pObjectImg, channel, imgWidth, imgHeight, (float)cmPerPixel,
  385. parallelogramImageRegion, pImageRegionContour, objectNum, detectedObjectStructs, pObjectContour, pFeatures, ref featuresNum, ParallelogramRegionOfInterestInfo, ref regionOfInterestScore);
  386. // 释放
  387. if (hObjectContour.IsAllocated)
  388. {
  389. hObjectContour.Free();
  390. }
  391. if (hObjectImg.IsAllocated)
  392. {
  393. hObjectImg.Free();
  394. }
  395. if (hImageRegionContour.IsAllocated)
  396. {
  397. hImageRegionContour.Free();
  398. }
  399. if (hFeatures.IsAllocated)
  400. {
  401. hFeatures.Free();
  402. }
  403. // 统计并保存分割结果、线阵探头下超声成像区域相关信息、B线评分
  404. // 1、将分割模型得到的目标结果放置在定义好的结构体中
  405. BlineRecognizedObjectInfo[] recognizedObjects = new BlineRecognizedObjectInfo[segResults.Length];
  406. for (int ni = 0; ni < segResults.Length; ni++)
  407. {
  408. recognizedObjects[ni] = new BlineRecognizedObjectInfo((EnumBlineRecognizedObjectType)segResults[ni].Label,
  409. segResults[ni].Confidence, segResults[ni].Contours[0]);
  410. }
  411. // 2、将线阵探头下的每条B线轮廓换算成需要的矩形(或是平行四边行)信息并放置至BLineScoringInfo函数中
  412. BLineScoringInfo[] calculatedObjects = new BLineScoringInfo[featuresNum];
  413. if (MathHelper.AlmostEqual(region.Steer, 0))
  414. {
  415. for (int ni = 0; ni < featuresNum; ni++)
  416. {
  417. IUltrasoundImageRegion eachgeometry;
  418. var featureNi = features[ni];
  419. var beamPositionInPixelLinear = featureNi.center.X;
  420. var centerDepthInPixelLinear = featureNi.center.Y;
  421. var widthSpanInPixelLinear = featureNi.horizontalSideLength;
  422. var depthSpanInPixelLinear = featureNi.slantSideLength;
  423. var steer = featureNi.angle;
  424. var featureRectangle = new LinearArrayUltrasoundImageRegion(centerDepthInPixelLinear, (int)depthSpanInPixelLinear, (int)widthSpanInPixelLinear, (int)beamPositionInPixelLinear, steer);
  425. eachgeometry = featureRectangle;
  426. // 保存每条B线轮廓换算后的扇形信息
  427. calculatedObjects[ni] = new BLineScoringInfo((EnumBlineScoreType)features[ni].label, eachgeometry);
  428. }
  429. //3、计算单帧图像中总的B线得分
  430. int finalScore = CalculateBLineScore(calculatedObjects);
  431. // 4、更新感兴趣区域及对应得分
  432. RegionOfInterestInfo regionOfInterestInfo = new RegionOfInterestInfo();
  433. regionOfInterestInfo.UltrasoundRegionOfInterest = roiSectorOrParallelogram;
  434. regionOfInterestInfo.RegionOfInterestScore = regionOfInterestScore;
  435. // 5、保存所有结果
  436. allResults.BlineScore = finalScore;
  437. allResults.BlineScoringInfo = calculatedObjects;
  438. allResults.RecognizedObjects = recognizedObjects;
  439. allResults.RegionOfInterestInfo = regionOfInterestInfo;
  440. return allResults;
  441. }
  442. else
  443. {
  444. for (int ni = 0; ni < featuresNum; ni++)
  445. {
  446. IUltrasoundImageRegion eachgeometry;
  447. var featureNi = features[ni];
  448. var beamPositionInPixelLinear = featureNi.center.X;
  449. var centerDepthInPixelLinear = featureNi.center.Y;
  450. var widthSpanInPixelLinear = featureNi.horizontalSideLength;
  451. var depthSpanInPixelLinear = featureNi.slantSideLength;
  452. var steer = featureNi.angle;
  453. var featureRectangle = new LinearArrayUltrasoundImageRegion(centerDepthInPixelLinear, (int)depthSpanInPixelLinear, (int)widthSpanInPixelLinear, (int)beamPositionInPixelLinear, steer);
  454. eachgeometry = featureRectangle;
  455. // 保存每条B线轮廓换算后的扇形信息
  456. calculatedObjects[ni] = new BLineScoringInfo((EnumBlineScoreType)features[ni].label, eachgeometry);
  457. }
  458. // 3、计算总的B线得分
  459. int finalScore = CalculateBLineScore(calculatedObjects);
  460. // 4、更新感兴趣区域及对应得分
  461. RegionOfInterestInfo regionOfInterestInfo = new RegionOfInterestInfo();
  462. regionOfInterestInfo.UltrasoundRegionOfInterest = roiSectorOrParallelogram;
  463. regionOfInterestInfo.RegionOfInterestScore = regionOfInterestScore;
  464. // 5、保存所有结果
  465. allResults.BlineScore = finalScore;
  466. allResults.BlineScoringInfo = calculatedObjects;
  467. allResults.RecognizedObjects = recognizedObjects;
  468. allResults.RegionOfInterestInfo = regionOfInterestInfo;
  469. return allResults;
  470. }
  471. }
  472. }
  473. catch (Exception excep)
  474. {
  475. NotifyError?.Invoke(this, new ErrorEventArgs(excep));
  476. }
  477. return BlineScoringResults.Empty;
  478. }
  479. /// <summary>
  480. /// 计算B线评分
  481. /// </summary>
  482. /// <param name="calculatedObjectss"></param>
  483. /// <returns></returns>
  484. private int CalculateBLineScore(BLineScoringInfo[] calculatedObjectss)
  485. {
  486. // 没有检测到可以计算得分的特征,则B线得分为0
  487. if (calculatedObjectss.Length <= 0)
  488. {
  489. return 0;
  490. }
  491. // 根据融合B线、孤立B线和肺实变的个数,计算得分
  492. int finalScore = 0;
  493. int isolatedBLineNum = 0;
  494. int fusionBLineNum = 0;
  495. int lungConsolidationNum = 0;
  496. for (int ni = 0; ni < calculatedObjectss.Length; ni++)
  497. {
  498. if (calculatedObjectss[ni].Type == EnumBlineScoreType.IsolatedBLine)
  499. {
  500. isolatedBLineNum += 1;
  501. }
  502. else if (calculatedObjectss[ni].Type == EnumBlineScoreType.FusionBLine)
  503. {
  504. fusionBLineNum += 1;
  505. }
  506. else if (calculatedObjectss[ni].Type == EnumBlineScoreType.LungConsolidation)
  507. {
  508. lungConsolidationNum += 1;
  509. }
  510. }
  511. if (fusionBLineNum == 0 && lungConsolidationNum == 0)
  512. {
  513. // 孤立BLine小于等于两条视为正常通气
  514. if (isolatedBLineNum <= 2)
  515. {
  516. finalScore = 0;
  517. }
  518. // 孤立BLine大于两条视为中度通气受损
  519. else
  520. {
  521. finalScore = 1;
  522. }
  523. }
  524. // 如果存在融合BLine,暂定不管有没有孤立BLine,都判定为严重肺通气受损(依据需求中的评分规则,后续根据实际情况可能会做调整)
  525. else if (fusionBLineNum > 0 && lungConsolidationNum == 0)
  526. {
  527. finalScore = 2;
  528. }
  529. // 只要肺实变存在,无论是否存在孤立BLine或融合BLine,均视为肺实变
  530. else if (lungConsolidationNum > 0)
  531. {
  532. finalScore = 3;
  533. }
  534. return finalScore;
  535. }
  536. #endregion
  537. #region public funcs
  538. /// <summary>
  539. /// 构造函数
  540. /// </summary>
  541. /// <param name="numCpu"></param>
  542. /// <param name="netDir"></param>
  543. public AutoBlineCalculation(int numCpu, string netDir)
  544. {
  545. try
  546. {
  547. _inferNet = new InferNetOnnxAutoBlineSeg();
  548. if (!_inferNet.ModelLoaded)
  549. {
  550. byte[] trainedNetwork =
  551. InferenceNetworkUtils.ReadNetworkDataFromFile(netDir, _inferNet.NetworkName,
  552. _inferNet.HashCode);
  553. // 设置inferCore的参数
  554. _inferenceCore.SetConfig(EnumInferCoreConfigKey.CPU_THREADS_NUM, numCpu.ToString(),
  555. EnumDeviceType.CPU);
  556. _inferNet.LoadNetwork(_inferenceCore, EnumDeviceType.CPU, trainedNetwork);
  557. }
  558. }
  559. catch (Exception excep)
  560. {
  561. throw new Exception("Failed at Loading network:" + excep);
  562. }
  563. }
  564. /// <summary>
  565. /// 计算单张图像中的B线数量和评分
  566. /// </summary>
  567. /// <param name="rawImg"></param>
  568. /// <param name="cmPerPixel"></param>
  569. /// <param name="imgRegion"></param>
  570. /// <returns></returns>
  571. public BlineScoringResults EvaluateOneImage(RawImage rawImg, float cmPerPixel, IUltrasoundImageRegion imgRegion)
  572. {
  573. if (rawImg.ColorType == EnumColorType.Gray16)
  574. {
  575. throw new ArgumentOutOfRangeException("ColorType", "The colorType of Gray16 is not supported for AutoBlineCalculation by now.");
  576. }
  577. if (rawImg.ColorType == EnumColorType.GrayF32)
  578. {
  579. throw new ArgumentOutOfRangeException("ColorType", "The colorType of GrayF32 is not supported for AutoBlineCalculation by now.");
  580. }
  581. RawImage imgRGB = rawImg;
  582. if (rawImg.ColorType != EnumColorType.Bgr)
  583. {
  584. imgRGB = rawImg.Clone(EnumColorType.Bgr);
  585. }
  586. // 模型分割结果
  587. DetectedObject[] netResults = BlineModelProcess(imgRGB);
  588. // 如果没有B线或肺实变,则B线得分为0,可以不用再做后续的处理
  589. bool hasBline = Array.Find(netResults, a => a.Label == 2) != null;
  590. bool hasLungConsolidation = Array.Find(netResults, a => a.Label == 4) != null;
  591. //定义单帧图中的所有结果
  592. BlineScoringResults allResults = new BlineScoringResults();
  593. // 计算感兴趣区域框
  594. IUltrasoundImageRegion roiSectorOrParallelogram;
  595. var regionType = imgRegion.GetType();
  596. if (regionType != typeof(ConvexArrayUltrasoundImageRegion) && regionType != typeof(LinearArrayUltrasoundImageRegion))
  597. {
  598. throw new ArgumentOutOfRangeException("imageRegion", "the expected type of imageRegion is 'ConvexArrayUltrasoundImageRegion' or 'LinearArrayUltrasoundImageRegion'");
  599. }
  600. // B线计算必须要有超声成像区域
  601. if (imgRegion == null)
  602. {
  603. throw new ArgumentException("no 'UltrasoundImageRegion' found for task 'BLineScore'.");
  604. }
  605. // 凸阵探头
  606. if (regionType == typeof(ConvexArrayUltrasoundImageRegion))
  607. {
  608. // 计算感兴趣区域超声成像参数
  609. var sector = (Sector)((ConvexArrayUltrasoundImageRegion)imgRegion).Geometry;
  610. var depthSpanInPixelROI = sector.RadiusSpan;
  611. var centerDepthInPixelROI = sector.StartRadius + depthSpanInPixelROI / 2;
  612. var imageWidthInPixelROI = 2 * sector.CenterOfArc.X;
  613. var scanMaxWidthROI = (sector.AngleSpan / 8) * 6;
  614. var originToProbeSurfaceInPixelROI = sector.CenterOfArc.Y / -(Math.Cos(MathHelper.DegreeToRadian(scanMaxWidthROI / 2)));
  615. var startAngleROI = sector.StartAngle + sector.AngleSpan / 8;
  616. var centerOfAngleROI = startAngleROI + scanMaxWidthROI / 2;
  617. var beamPositionROI = centerOfAngleROI - 270;
  618. var widthSpanROI = (sector.AngleSpan / 8) * 6;
  619. var sectorROI = new ConvexArrayUltrasoundImageRegion((int)originToProbeSurfaceInPixelROI, (int)imageWidthInPixelROI, (int)centerDepthInPixelROI, (int)depthSpanInPixelROI,
  620. (double)scanMaxWidthROI, (double)widthSpanROI, (double)beamPositionROI);
  621. roiSectorOrParallelogram = sectorROI;
  622. RegionOfInterestInfo regionOfInterestInfo = new RegionOfInterestInfo(0, roiSectorOrParallelogram);
  623. allResults.RegionOfInterestInfo = regionOfInterestInfo;
  624. if (!(hasBline || hasLungConsolidation))
  625. {
  626. return allResults;
  627. }
  628. // 检测到上述四个目标中的任意一个,则进一步统计B线数量并计算评分
  629. var calResults = BlineCalculation(imgRGB, netResults, cmPerPixel, imgRegion, roiSectorOrParallelogram, allResults);
  630. // 更新allResults
  631. allResults.BlineScore = calResults.BlineScore;
  632. allResults.RecognizedObjects = calResults.RecognizedObjects;
  633. allResults.BlineScoringInfo= calResults.BlineScoringInfo;
  634. allResults.RegionOfInterestInfo= calResults.RegionOfInterestInfo;
  635. return allResults;
  636. }
  637. else if (regionType == typeof(LinearArrayUltrasoundImageRegion))
  638. {
  639. var linearRegion = (LinearArrayUltrasoundImageRegion)imgRegion;
  640. RegionOfInterestInfo regionOfInterestInfo = new RegionOfInterestInfo(0, null);
  641. if (MathHelper.AlmostEqual(linearRegion.Steer, 0))
  642. {
  643. var reRectangle = (Rectangle)(linearRegion).Geometry;
  644. var centerDepthInPixelROI = reRectangle.LeftUp.Y+ reRectangle.Height / 2;
  645. //var centerDepthInPixelROI = reRectangle.Height/2;
  646. var depthSpanInPixelROI = reRectangle.Height;
  647. var widthSpanInPixelROI = (reRectangle.Width / 8) * 6;
  648. var beamPositionInPixelROI = reRectangle.LeftUp.X+ reRectangle.Width/2;
  649. var steerROI = 0;
  650. var reRectangleROI = new LinearArrayUltrasoundImageRegion((int)centerDepthInPixelROI, (int)depthSpanInPixelROI, (int)widthSpanInPixelROI, (int)beamPositionInPixelROI, steerROI);
  651. roiSectorOrParallelogram = reRectangleROI;
  652. RegionOfInterestInfo roiParallelogram = new RegionOfInterestInfo(0, roiSectorOrParallelogram);
  653. allResults.RegionOfInterestInfo = roiParallelogram;
  654. if (!(hasBline || hasLungConsolidation))
  655. {
  656. return allResults;
  657. }
  658. // 检测到上述四个目标中的任意一个,则进一步统计B线数量并计算评分
  659. var calResults = BlineCalculation(imgRGB, netResults, cmPerPixel, imgRegion, roiSectorOrParallelogram, allResults);
  660. // 更新allResults
  661. allResults.BlineScore = calResults.BlineScore;
  662. allResults.RecognizedObjects = calResults.RecognizedObjects;
  663. allResults.BlineScoringInfo = calResults.BlineScoringInfo;
  664. allResults.RegionOfInterestInfo = calResults.RegionOfInterestInfo;
  665. return allResults;
  666. }
  667. else
  668. {
  669. var reParallelogram = (Parallelogram)(linearRegion).Geometry;
  670. var beamPositionInPixelROI = ((reParallelogram.Center.X / 8) * 6) / 2;
  671. var centerDepthInPixelROI = reParallelogram.Center.Y;
  672. var widthSpanInPixelROI = (int)(reParallelogram.HorizontalSideLength / 8 * 6);
  673. var depthSpanInPixelROI = (int)reParallelogram.SlantSideLength;
  674. var steerROI = reParallelogram.Angle;
  675. var reRectangleROI = new LinearArrayUltrasoundImageRegion((int)centerDepthInPixelROI, (int)depthSpanInPixelROI, (int)widthSpanInPixelROI, (int)beamPositionInPixelROI, steerROI);
  676. roiSectorOrParallelogram = reRectangleROI;
  677. RegionOfInterestInfo roiParallelogram = new RegionOfInterestInfo(0, roiSectorOrParallelogram);
  678. allResults.RegionOfInterestInfo = regionOfInterestInfo;
  679. if (!(hasBline || hasLungConsolidation))
  680. {
  681. return allResults;
  682. }
  683. // 检测到上述四个目标中的任意一个,则进一步统计B线数量并计算评分
  684. var calResults = BlineCalculation(imgRGB, netResults, cmPerPixel, imgRegion, roiSectorOrParallelogram, allResults);
  685. // 更新allResults
  686. allResults.BlineScore = calResults.BlineScore;
  687. allResults.RecognizedObjects = calResults.RecognizedObjects;
  688. allResults.BlineScoringInfo = calResults.BlineScoringInfo;
  689. allResults.RegionOfInterestInfo = calResults.RegionOfInterestInfo;
  690. return allResults;
  691. }
  692. }
  693. else
  694. {
  695. return allResults;
  696. }
  697. }
  698. /// <summary>
  699. /// 销毁
  700. /// </summary>
  701. public void Dispose()
  702. {
  703. DoDispose();
  704. GC.SuppressFinalize(this);
  705. }
  706. #endregion
  707. #region private funcs
  708. /// <summary>
  709. /// 主动销毁
  710. /// </summary>
  711. private void DoDispose()
  712. {
  713. _inferNet?.Dispose();
  714. _inferNet = null;
  715. }
  716. #endregion
  717. }
  718. }