AIDiagnosisService.cs 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  1. using WingServerCommon.Service;
  2. using WingInterfaceLibrary.Interface;
  3. using WingInterfaceLibrary.Request;
  4. using WingInterfaceLibrary.Enum;
  5. using WingServerCommon.Config;
  6. using WingServerCommon.Config.Parameters;
  7. using WingAIDiagnosisService.Carotid.Utilities;
  8. using Vinno.vCloud.Common.Vid2;
  9. using WingAIDiagnosisService.Manage;
  10. using WingAIDiagnosisService.Carotid;
  11. using Newtonsoft.Json;
  12. using AI.DiagSystem;
  13. using WingServerCommon.Log;
  14. using System.IO;
  15. using System;
  16. using System.Threading.Tasks;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using AI.Common;
  20. using System.Threading;
  21. using AI.Common.Log;
  22. using SkiaSharp;
  23. using System.Net.Http;
  24. using System.Net;
  25. using WingInterfaceLibrary.Request.Storage;
  26. using WingInterfaceLibrary.Result.AIDiagnosis;
  27. using WingInterfaceLibrary.Request.AIDiagnosis;
  28. using WingInterfaceLibrary.DTO.Record;
  29. using WingInterfaceLibrary.DTO.Comment;
  30. using Emgu.CV;
  31. using Emgu.CV.CvEnum;
  32. using Emgu.CV.Structure;
  33. using System.Net.Http.Headers;
  34. using WingAIDiagnosisService.Common;
  35. using WingInterfaceLibrary.Interface.DBInterface;
  36. using WingInterfaceLibrary.Request.DBRequest;
  37. using WingInterfaceLibrary.DTO.DiagnosisResult;
  38. using WingInterfaceLibrary.Request.RemedicalAISelected;
  39. using ContourModifyUtils;
  40. namespace WingAIDiagnosisService.Service
  41. {
  42. /// <summary>
  43. /// AI诊断服务
  44. /// </summary>
  45. public partial class AIDiagnosisService : JsonRpcService, IAIDiagnosisService
  46. {
  47. private EnumPerformance _workerLevel = EnumPerformance.Medium;
  48. private int _batchImageSize = 16;
  49. private int _sleepTime = 400;
  50. private readonly string _tempFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DiagnosisTemp");
  51. private AIDiagSystem _diagSystem;
  52. private AILesionCompareUtils _lesionCompareUtil;
  53. private RecognizeCarotidType _recognizeCarotidType = new RecognizeCarotidType();
  54. //识别左右颈是否初始化成功
  55. private static bool _recognizeCarotidTypeCanBeUse = false;
  56. static HttpClient _httpClient = new HttpClient();
  57. private IAuthenticationService _authenticationService;
  58. private IStorageService _storageService;
  59. private readonly string _carotidName = "CarotidPlaqueDetect";
  60. private int _contourInterval = 10;
  61. private IDiagnosisResultDBService _diagnosisResultDBService;
  62. /// <summary>
  63. /// Init service
  64. /// </summary>
  65. public override void Load(JsonRpcClientPool jsonRpcClientPool)
  66. {
  67. base.Load(jsonRpcClientPool);
  68. _authenticationService = GetProxy<IAuthenticationService>();
  69. _storageService = GetProxy<IStorageService>();
  70. _workerLevel = (EnumPerformance)ConfigurationManager.GetParammeter<IntParameter>("AI", "WorkerLevel").Value;
  71. _batchImageSize = ConfigurationManager.GetParammeter<IntParameter>("AI", "BatchImageSize").Value;
  72. _contourInterval = ConfigurationManager.GetParammeter<IntParameter>("AI", "ContourInterval").Value;
  73. _httpClient.Timeout = TimeSpan.FromMinutes(15);
  74. _diagnosisResultDBService = GetProxy<IDiagnosisResultDBService>();
  75. InitAISystem();
  76. //初始化识别左右颈
  77. _recognizeCarotidTypeCanBeUse = _recognizeCarotidType.Initialization();
  78. var keyPointDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "ContourInterval").Value;
  79. var dragStartPointCatchDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "ContourInterval").Value;
  80. ContourModifyHelper.KeyPointDistanceThreshold = keyPointDistanceThreshold;
  81. ContourModifyHelper.DragStartPointCatchDistanceThreshold = dragStartPointCatchDistanceThreshold;
  82. }
  83. private void InitAISystem()
  84. {
  85. var modeFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AIDiagnosis\\Networks");
  86. _diagSystem = new AIDiagSystem(_workerLevel, modeFilePath, EnumInferWorkName.Default, false, true, true);
  87. if (_diagSystem != null)
  88. {
  89. _diagSystem.NotifyError += AIdiagSystem_NotifyError;
  90. _diagSystem.NotifyLog += AIdiagSystem_NotifyLog;
  91. }
  92. _lesionCompareUtil = new AILesionCompareUtils();
  93. }
  94. /// <summary>
  95. /// 查询杏聆荟支持的AI模块
  96. /// </summary>
  97. /// <param name="request">查询杏聆荟支持的AI模块请求实体</param>
  98. /// <returns></returns>
  99. /// <show>false</show>
  100. public async Task<IList<string>> FindDiagnosisModulesAsync(TokenRequest request)
  101. {
  102. try
  103. {
  104. var moduleNames = AIDiagSystem.GetValidModuleNamesForVclound() ?? new List<EnumAIModuleNames>();
  105. var resultData = moduleNames.Select(x => x.ToString()).ToList();
  106. if (!resultData.Contains(_carotidName))
  107. {
  108. resultData.Remove("CarotidArtery");
  109. resultData.Add(_carotidName);
  110. }
  111. return await Task.FromResult(resultData.Distinct().ToList());
  112. }
  113. catch (Exception)
  114. {
  115. return new List<string>();
  116. }
  117. }
  118. /// <summary>
  119. /// 图像诊断
  120. /// </summary>
  121. /// <param name="request">图像诊断请求实体</param>
  122. /// <returns>图像诊断结果</returns>
  123. /// <show>false</show>
  124. public async Task<DiagnosisImageResult> DiagnosisImageAsync(DiagnosisImageRequest request)
  125. {
  126. try
  127. {
  128. var relationCode = request.RelationCode;
  129. var fileUrl = request.FileToken;
  130. var localFile = "";
  131. if (!string.IsNullOrWhiteSpace(request.DiskPath))
  132. {
  133. localFile = request.DiskPath;
  134. }
  135. else
  136. {
  137. localFile = await DownloadAsync(fileUrl);
  138. }
  139. if (File.Exists(localFile))
  140. {
  141. if (_diagSystem == null)
  142. {
  143. InitAISystem();
  144. }
  145. using (var imageData = new VinnoImageData(localFile, OperationMode.Open))
  146. {
  147. if (DiagnosisHelper.IsCarotid(imageData))
  148. {
  149. if (imageData.ImageCount > DiagnosisHelper.CarotidMinImageCounts)
  150. {
  151. //颈动脉
  152. var result = await CarotidDiagnosis(imageData);
  153. var resultData = new DiagnosisImageResult()
  154. {
  155. DiagnosisOrgans = new List<DiagnosisOrganEnum>() { DiagnosisOrganEnum.CarotidArtery },
  156. CarotidResult = result
  157. };
  158. return resultData;
  159. }
  160. }
  161. else
  162. {
  163. //乳腺、肝脏
  164. var results = NormalDiagnosis(imageData);
  165. await AddDiagnosisResultInfosAsync(relationCode, fileUrl, results);
  166. var diagnosisResults = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value, _contourInterval)).ToList();
  167. var diagnosisResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisResults));
  168. var resultData = new DiagnosisImageResult()
  169. {
  170. DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisResults),
  171. DiagnosisResult = diagnosisResult,
  172. DiagnosisOrgans = GetDiagnosisOrgans(diagnosisResult),
  173. };
  174. return resultData;
  175. }
  176. }
  177. }
  178. }
  179. catch (Exception ex)
  180. {
  181. Logger.WriteLineWarn($"DiagnosisImageAsync err {ex}");
  182. }
  183. return new DiagnosisImageResult();
  184. }
  185. /// <summary>
  186. /// 生成AI报告
  187. /// </summary>
  188. /// <param name="request">生成AI报告请求实体</param>
  189. /// <returns></returns>
  190. /// <show>false</show>
  191. public async Task<DiagnosisReportResult> DiagnosisReportAsync(DiagnosisReportRequest request)
  192. {
  193. try
  194. {
  195. if (request.Organ == DiagnosisOrganEnum.CarotidArtery)
  196. {
  197. var result = await GetCarotidAIMeasureResult(request);
  198. return result;
  199. }
  200. else
  201. {
  202. var manager = await CreateDiagnosisManagerAsync(request);
  203. var reportPerImages = manager.GetReportResults();
  204. var diagnosisConclusion = GetDiagnosisConclusion(reportPerImages);
  205. var diagnosisResult = new List<DiagnosisPerImageDTO>();
  206. foreach (var item in reportPerImages)
  207. {
  208. var aiFileToken = await UploadFileAsync(item.AILocalImagePath, Path.GetFileName(item.AILocalImagePath));
  209. var perImageResult = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageDTO>(Newtonsoft.Json.JsonConvert.SerializeObject(item));
  210. perImageResult.RemedicalCode = item.RemedicalCode;
  211. perImageResult.DataType = (RemedicalFileDataTypeEnum)item.DataType;
  212. perImageResult.Pixel = item.Pixel;
  213. perImageResult.AIFileToken = aiFileToken;
  214. diagnosisResult.Add(perImageResult);
  215. }
  216. return new DiagnosisReportResult
  217. {
  218. DiagnosisConclusion = (DiagnosisConclusionEnum)diagnosisConclusion,
  219. DiagnosisResult = diagnosisResult,
  220. };
  221. }
  222. }
  223. catch (Exception ex)
  224. {
  225. Logger.WriteLineWarn($"AIService DiagnosisReport err, {ex}");
  226. }
  227. return new DiagnosisReportResult();
  228. }
  229. /// <summary>
  230. /// 查询AI相关枚举集合
  231. /// </summary>
  232. /// <param name="request">查询AI相关枚举集合请求实体</param>
  233. /// <returns>AI相关枚举集合</returns>
  234. /// <value></value>
  235. /// <errorCodes></errorCodes>
  236. public async Task<GetDiagnosisEnumItemsResult> GetDiagnosisEnumItemsAsync(GetDiagnosisEnumItemsRequest request)
  237. {
  238. var diagnosisItems = new List<EnumItemDTO>();
  239. //病灶
  240. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "Breast", new List<string> { "BIRads1" }));
  241. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "Liver", new List<string> { "BIRads1" }));
  242. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "Thyroid", new List<string> { "TIRADS0" }));
  243. //病灶特性描述
  244. diagnosisItems.Add(GetEnumItem(typeof(EnumDesShapeValue)));
  245. diagnosisItems.Add(GetEnumItem(typeof(EnumDesOrientationValue)));
  246. diagnosisItems.Add(GetEnumItem(typeof(EnumDesEchoPatternValue)));
  247. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLesionBoundaryValue)));
  248. diagnosisItems.Add(GetEnumItem(typeof(EnumDesMarginValue)));
  249. diagnosisItems.Add(GetEnumItem(typeof(EnumCalcificationsValue), "Calcification"));
  250. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverShapeValue)));
  251. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverBoundaryValue)));
  252. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverEchoTextureValue)));
  253. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchoPatternValue)));
  254. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidShapeValue)));
  255. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidMarginValue)));
  256. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchogenicFociValue)));
  257. //局灶和弥漫区分
  258. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastLocalLesion", includeFields: new List<string> { "Lipomyoma", "BIRads2", "BIRads3" }));
  259. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastDiffuseLesion", includeFields: new List<string> { "BIRads4A", "BIRads4B", "BIRads4C", "BIRads5" }));
  260. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverLocalLesion", includeFields: new List<string> { "Hyperechoic", "HHE", "CYST", "PossibleCancer" }));
  261. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverDiffuseLesion", includeFields: new List<string> { "FattyLiver", "DiffuseLesions", "Cirrhosis", "PCLD" }));
  262. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidLocalLesion", includeFields: new List<string> { "TIRADS2", "TIRADS3", "TIRADS4a", "TIRADS4b", "TIRADS4c", "TIRADS5" }));
  263. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidDiffuseLesion", includeFields: new List<string> { "DiffuseDisease" }));
  264. var resultData = new GetDiagnosisEnumItemsResult { Source = diagnosisItems };
  265. return await Task.FromResult(resultData);
  266. }
  267. private EnumItemDTO GetEnumItem(Type enumType, string keyCode = "", List<string> excludeFields = null, List<string> includeFields = null)
  268. {
  269. keyCode = !string.IsNullOrWhiteSpace(keyCode) ? keyCode : enumType.Name.ToString().Replace("EnumDes", "").Replace("Enum", "").Replace("Value", "");
  270. var enumNames = Enum.GetNames(enumType).ToList();
  271. if (excludeFields != null && excludeFields.Any())
  272. {
  273. enumNames = enumNames.Except(excludeFields).ToList();
  274. }
  275. if (includeFields != null && includeFields.Any())
  276. {
  277. enumNames = enumNames.Intersect(includeFields).ToList();
  278. }
  279. var children = new List<EnumFieldDTO>();
  280. foreach (var val in enumNames)
  281. {
  282. var id = (int)enumType.GetField(val).GetValue(val);
  283. children.Add(new EnumFieldDTO
  284. {
  285. Id = id,
  286. Value = val,
  287. });
  288. }
  289. return new EnumItemDTO
  290. {
  291. Code = keyCode,
  292. Children = children,
  293. };
  294. }
  295. /// <summary>
  296. /// 查询病灶轮廓的关键点
  297. /// </summary>
  298. /// <param name="request">查询病灶轮廓的关键点请求实体</param>
  299. /// <returns>关键点集合</returns>
  300. /// <value></value>
  301. /// <errorCodes></errorCodes>
  302. public async Task<List<DiagnosisKeyPointDTO>> GetKeyPointsOfContourAsync(GetKeyPointsOfContourRequest request)
  303. {
  304. var contourPoints = request.Contours.Select(c => new Point2D
  305. {
  306. X = c.X,
  307. Y = c.Y
  308. }).ToArray();
  309. var ls = request.LesionSize;
  310. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  311. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  312. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  313. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  314. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  315. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  316. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  317. var aiResult = ContourModifyHelper.KeyPointsOfContour(contourPoints, lesionSize);
  318. var resultData = KeyPointToDto(aiResult);
  319. return await Task.FromResult(resultData);
  320. }
  321. /// <summary>
  322. /// 移动光标,查询受影响关键点
  323. /// </summary>
  324. /// <param name="request">移动光标,查询受影响关键点请求实体</param>
  325. /// <returns>受影响关键点下标</returns>
  326. /// <value></value>
  327. /// <errorCodes></errorCodes>
  328. public async Task<List<int>> AffectedKeyPointsByDragActionAsync(AffectedKeyPointsByDragActionRequest request)
  329. {
  330. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  331. {
  332. Type = (EnumKeyPointType)c.Type,
  333. IndexInContour = c.IndexInContour,
  334. Point = new Point2D(c.Point.X, c.Point.Y)
  335. }).ToArray();
  336. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  337. var resultData = ContourModifyHelper.AffectedKeyPointsByDragAction(origKeyPoints, mousePoint).ToList();
  338. return await Task.FromResult(resultData);
  339. }
  340. /// <summary>
  341. /// 拖动光标,查询所有轮廓点和关键点
  342. /// </summary>
  343. /// <param name="request">拖动光标,查询所有轮廓点和关键点请求实体</param>
  344. /// <returns>所有轮廓点和关键点</returns>
  345. /// <value></value>
  346. /// <errorCodes></errorCodes>
  347. public async Task<ContourAndKeyPointsAfterDragResult> ContourAndKeyPointsAfterDragAsync(ContourAndKeyPointsAfterDragRequest request)
  348. {
  349. var origContourPoints = request.Contours.Select(c => new Point2D
  350. {
  351. X = c.X,
  352. Y = c.Y
  353. }).ToArray();
  354. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  355. {
  356. Type = (EnumKeyPointType)c.Type,
  357. IndexInContour = c.IndexInContour,
  358. Point = new Point2D(c.Point.X, c.Point.Y)
  359. }).ToArray();
  360. var dragStartPoint = new Point2D(request.StartPoint.X, request.StartPoint.Y);
  361. var dragEndPoint = new Point2D(request.EndPoint.X, request.EndPoint.Y);
  362. ContourModifyHelper.ContourAndKeyPointsAfterDrag(origContourPoints, origKeyPoints, dragStartPoint, dragEndPoint
  363. , out Point2D[] dstContourPoints, out KeyPointInfo[] dstKeyPoints, out int[] affectedKeyPointIndexes);
  364. var dstContours = PointToDto(dstContourPoints);
  365. var dstKeys = KeyPointToDto(dstKeyPoints);
  366. var resultData = new ContourAndKeyPointsAfterDragResult
  367. {
  368. DstContours = dstContours,
  369. DstKeyPoints = dstKeys,
  370. AffectedKeyPointIndexes = affectedKeyPointIndexes.ToList(),
  371. };
  372. return await Task.FromResult(resultData);
  373. }
  374. /// <summary>
  375. /// 画轮廓模式,查询鼠标位置到轮廓线的最短距离
  376. /// </summary>
  377. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  378. /// <returns>鼠标离轮廓线的距离,高亮点的下标</returns>
  379. /// <value></value>
  380. /// <errorCodes></errorCodes>
  381. public async Task<MinimumDistanceToContourPointsResult> MinimumDistanceToContourPointsAsync(MinimumDistanceToContourPointsRequest request)
  382. {
  383. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  384. {
  385. X = c.X,
  386. Y = c.Y
  387. }).ToArray();
  388. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  389. var distance = ContourModifyHelper.MinimumDistanceToContourPoints(origContourPoints, mousePoint, out int closestPointIndex);
  390. var resultData = new MinimumDistanceToContourPointsResult
  391. {
  392. DistanceCaught = distance,
  393. ClosestPointIndex = closestPointIndex,
  394. };
  395. return await Task.FromResult(resultData);
  396. }
  397. /// <summary>
  398. /// 画轮廓模式,合并新旧轮廓
  399. /// </summary>
  400. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  401. /// <returns>合并后的完整轮廓线,合并后的横纵径尺寸结果</returns>
  402. /// <value></value>
  403. /// <errorCodes></errorCodes>
  404. public async Task<ContourMergeResult> ContourMergeAsync(ContourMergeRequest request)
  405. {
  406. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  407. {
  408. X = c.X,
  409. Y = c.Y
  410. }).ToArray();
  411. var ls = request.LesionSize;
  412. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  413. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  414. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  415. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  416. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  417. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  418. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  419. var drawingNewContourPoints = request.DrawingNewContourPoints.Select(c => new Point2D
  420. {
  421. X = c.X,
  422. Y = c.Y
  423. }).ToArray();
  424. var aiResult = ContourModifyHelper.ContourMerge(origContourPoints, lesionSize, drawingNewContourPoints, out Point2D[] dstContourPoints, out LesionSize dstLesionSize);
  425. var dstContours = PointToDto(dstContourPoints);
  426. var resultData = new ContourMergeResult
  427. {
  428. DstContours = dstContours,
  429. DstLesionSize = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisLesionSize
  430. {
  431. HorizontalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint1.X, Y = dstLesionSize.HorizontalPoint1.Y },
  432. HorizontalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint2.X, Y = dstLesionSize.HorizontalPoint2.Y },
  433. VerticalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint1.X, Y = dstLesionSize.VerticalPoint1.Y },
  434. VerticalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint2.X, Y = dstLesionSize.VerticalPoint2.Y },
  435. HorizontalLengthInPixel = dstLesionSize.HorizontalLengthInPixel,
  436. VerticalLengthInPixel = dstLesionSize.VerticalLengthInPixel,
  437. },
  438. };
  439. return await Task.FromResult(resultData);
  440. }
  441. /// <summary>
  442. /// 获取颈动脉ai报告数据
  443. /// </summary>
  444. /// <param name="request">生成AI报告请求实体</param>
  445. /// <returns></returns>
  446. private async Task<DiagnosisReportResult> GetCarotidAIMeasureResult(DiagnosisReportRequest request)
  447. {
  448. var result = new DiagnosisReportResult()
  449. {
  450. DiagnosisConclusion = DiagnosisConclusionEnum.NoObviousLesion
  451. };
  452. var orderedExamDatas = request.RemedicalList.OrderByDescending(m => m.CarotidResult?.CarotidScanType).Where(m => m.CarotidResult?.MeasureImageFiles != null && m.CarotidResult.MeasureImageFiles.Count > 0);
  453. if (orderedExamDatas?.Count() > 0)
  454. {
  455. DiagnosisRemicalDTO leftData = null;
  456. //left
  457. var leftExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidLeft);
  458. if (leftExamDatas.Any())
  459. {
  460. foreach (var leftItem in leftExamDatas)
  461. {
  462. if (string.IsNullOrEmpty(leftItem.CarotidResult.MeasureResult))
  463. {
  464. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  465. continue;
  466. }
  467. //leftItem.CarotidResult.MeasureResult = "{\"IntimaResult\":{\"IsSuccess\":true,\"AntIntima\":{\"IntimaThick\":0.0,\"IsSuccess\":false},\"PostIntima\":{\"IntimaThick\":0.75,\"IsSuccess\":true}},\"IsYImageSuccess\":false,\"PlaqueResult\":{\"PlaquePostion\":0,\"PlaqueCountType\":2,\"PlaqueType\":0,\"PlaqueWidth\":1.75,\"PlaqueHeight\":3.76,\"Stenosis\":10.37,\"IsSuccess\":true}}";
  468. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(leftItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  469. if (measureResult.PlaqueResult?.PlaqueCountType != Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  470. {
  471. leftData = leftItem;
  472. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  473. break;
  474. }
  475. }
  476. if (leftData == null)
  477. {
  478. leftData = leftExamDatas.First();
  479. }
  480. }
  481. if (leftData != null)
  482. {
  483. result.CarotidResult.Add(leftData);
  484. }
  485. //right
  486. float intimaThickStandard = 1.0f;
  487. DiagnosisRemicalDTO rightData = null;
  488. var rightExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidRight);
  489. if (rightExamDatas.Any())
  490. {
  491. foreach (var rightItem in rightExamDatas)
  492. {
  493. if (string.IsNullOrEmpty(rightItem.CarotidResult.MeasureResult))
  494. {
  495. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  496. continue;
  497. }
  498. //rightItem.CarotidResult.MeasureResult = "{\"IntimaResult\":{\"IsSuccess\":true,\"AntIntima\":{\"IntimaThick\":0.0,\"IsSuccess\":false},\"PostIntima\":{\"IntimaThick\":0.75,\"IsSuccess\":true}},\"IsYImageSuccess\":false,\"PlaqueResult\":{\"PlaquePostion\":0,\"PlaqueCountType\":2,\"PlaqueType\":0,\"PlaqueWidth\":1.75,\"PlaqueHeight\":3.76,\"Stenosis\":10.37,\"IsSuccess\":true}}";
  499. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(rightItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  500. if (measureResult.IntimaResult?.PostIntima?.IntimaThick > intimaThickStandard)
  501. {
  502. rightData = rightItem;
  503. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  504. break;
  505. }
  506. }
  507. if (rightData == null)
  508. {
  509. rightData = rightExamDatas.First();
  510. }
  511. }
  512. if (rightData != null)
  513. {
  514. result.CarotidResult.Add(rightData);
  515. }
  516. Logger.WriteLineInfo($"AIDiagnosisService package carotidAIMeasureResult finished, CarotidLeftRemedicalCode:{leftData?.RemedicalCode}, CarotidLeft:{leftData?.CarotidResult?.MeasureResult}, CarotidRightRemedicalCode:{rightData?.RemedicalCode}, CarotidRight:" + rightData?.CarotidResult?.MeasureResult);
  517. }
  518. return result;
  519. }
  520. private async Task<BaseDiagnosis> CreateDiagnosisManagerAsync(DiagnosisReportRequest request)
  521. {
  522. var recordResults = new List<DiagnosisPerImageModel>();
  523. foreach (var remedical in request.RemedicalList)
  524. {
  525. foreach (var perImage in remedical.DiagnosisResult)
  526. {
  527. var localFile = await DownloadAsync(remedical.FileToken);
  528. if (File.Exists((localFile)))
  529. {
  530. var diagnosisPerImage = new DiagnosisPerImageModel();
  531. diagnosisPerImage.RemedicalCode = remedical.RemedicalCode;
  532. diagnosisPerImage.DataType = (WingAIDiagnosisService.Manage.DataType)remedical.DataType;
  533. diagnosisPerImage.LocalVidPath = localFile;
  534. diagnosisPerImage.Index = perImage.Index;
  535. diagnosisPerImage.PriorityScore = perImage.PriorityScore;
  536. var diagnosisResults = Newtonsoft.Json.JsonConvert.DeserializeObject<List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>>(Newtonsoft.Json.JsonConvert.SerializeObject(perImage.DiagResultsForEachOrgan));
  537. diagnosisPerImage.DiagResultsForEachOrgan = diagnosisResults;
  538. recordResults.Add(diagnosisPerImage);
  539. }
  540. }
  541. }
  542. switch (request.Organ)
  543. {
  544. case DiagnosisOrganEnum.Breast:
  545. return new BreastDiagnosis(recordResults, request.Organ);
  546. case DiagnosisOrganEnum.Liver:
  547. return new LiverDiagnosis(recordResults, request.Organ);
  548. case DiagnosisOrganEnum.Thyroid://甲状腺
  549. return new ThyroidDiagnosis(recordResults, request.Organ);
  550. default:
  551. throw new Exception($"not support organ type:{request.Organ.ToString()}");
  552. }
  553. }
  554. private async Task<CarotidResultDTO> CarotidDiagnosis(VinnoImageData imageData)
  555. {
  556. var resampleInputData = new ResampleInputData();
  557. resampleInputData.SurfaceFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.SurfaceFileSuffix);
  558. resampleInputData.MdlFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ModelFileSuffix);
  559. resampleInputData.MdlZipFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ZipFileSuffix);
  560. resampleInputData.BaseAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  561. resampleInputData.PlaqueAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  562. resampleInputData.YShapeAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  563. var resampleResult = TryResample(imageData, resampleInputData);
  564. if (resampleResult.ResampleErrorCode == ResampleErrorCode.Success)
  565. {
  566. var carotidAIImageTokens = new List<CarotidAIImage>();
  567. if (resampleResult.CarotidAIMeasureResult.IsYImageSuccess)
  568. {
  569. var fileUrl = await UploadFileAsync(resampleInputData.YShapeAIImagePath, Path.GetFileName(resampleInputData.YShapeAIImagePath));
  570. if (!string.IsNullOrWhiteSpace(fileUrl))
  571. {
  572. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.YShape });
  573. }
  574. File.Delete(resampleInputData.YShapeAIImagePath);
  575. }
  576. if (resampleResult.CarotidAIMeasureResult.IntimaResult.IsSuccess)
  577. {
  578. var fileUrl = await UploadFileAsync(resampleInputData.BaseAIImagePath, Path.GetFileName(resampleInputData.BaseAIImagePath));
  579. if (!string.IsNullOrWhiteSpace(fileUrl))
  580. {
  581. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Base });
  582. }
  583. File.Delete(resampleInputData.BaseAIImagePath);
  584. }
  585. if (resampleResult.CarotidAIMeasureResult.PlaqueResult.IsSuccess && resampleResult.CarotidAIMeasureResult.PlaqueResult.PlaqueCountType != WingAIDiagnosisService.Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  586. {
  587. var fileUrl = await UploadFileAsync(resampleInputData.PlaqueAIImagePath, Path.GetFileName(resampleInputData.PlaqueAIImagePath));
  588. if (!string.IsNullOrWhiteSpace(fileUrl))
  589. {
  590. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Plaque });
  591. }
  592. File.Delete(resampleInputData.PlaqueAIImagePath);
  593. }
  594. var surfaceImageList = new List<string>();
  595. long surfaceFileSize = 0;
  596. var cdnSurfaceFile = "";
  597. if (!string.IsNullOrWhiteSpace(resampleInputData.SurfaceFilePath) && File.Exists(resampleInputData.SurfaceFilePath))
  598. {
  599. surfaceImageList = await SurfaceFile(resampleInputData.SurfaceFilePath);
  600. var surfaceFileTuple = await UploadFileTupleAsync(resampleInputData.SurfaceFilePath, Path.GetFileName(resampleInputData.SurfaceFilePath));
  601. surfaceFileSize = surfaceFileTuple.Item2;
  602. File.Delete(resampleInputData.SurfaceFilePath);
  603. resampleInputData.SurfaceFilePath = surfaceFileTuple.Item1;
  604. cdnSurfaceFile = surfaceFileTuple.Item1.ToUrlToken();//ToCDN
  605. }
  606. long mdlFileFileSize = 0;
  607. var cdnMdlFile = "";
  608. if (!string.IsNullOrWhiteSpace(resampleInputData.MdlFilePath) && File.Exists(resampleInputData.MdlFilePath))
  609. {
  610. var mdlFileTuple = await UploadFileTupleAsync(resampleInputData.MdlFilePath, Path.GetFileName(resampleInputData.MdlFilePath));
  611. mdlFileFileSize = mdlFileTuple.Item2;
  612. File.Delete(resampleInputData.MdlFilePath);
  613. resampleInputData.MdlFilePath = mdlFileTuple.Item1;
  614. cdnMdlFile = mdlFileTuple.Item1.ToUrlToken();//ToCDN
  615. }
  616. var result = new CarotidResultDTO
  617. {
  618. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  619. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  620. SurfaceFile = resampleInputData.SurfaceFilePath,
  621. SurfaceFileSize = surfaceFileSize,
  622. CDNSurfaceFile = cdnSurfaceFile,
  623. MdlFileSize = mdlFileFileSize,
  624. MdlFile = resampleInputData.MdlFilePath,
  625. CDNMdlFile = cdnMdlFile,
  626. MeasureImageFiles = carotidAIImageTokens.Select(x => new MeasureImageFileDTO { ImageType = (CarotidAIImageTypeEnum)x.AIImageType, ImageFile = x.AIImageToken }).ToList(),
  627. MeasureResult = JsonConvert.SerializeObject(resampleResult.CarotidAIMeasureResult),
  628. SurfaceImageList = surfaceImageList,
  629. };
  630. return result;
  631. }
  632. return null;
  633. }
  634. private ResampleResult TryResample(VinnoImageData vinnoImageData, ResampleInputData resampleInputData)
  635. {
  636. float scanDistance = 7;
  637. ResampleResult resampleResult = new ResampleResult(ResampleErrorCode.Fail);
  638. var vinnoExtendedData = VinnoCarotidExtendedData.FromBytes(vinnoImageData.ExtendedData);
  639. CarotidScanType scanType = CarotidScanType.CarotidLeft;
  640. CarotidScanDirection direction = CarotidScanDirection.TopToBottom;
  641. if (vinnoExtendedData != null)
  642. {
  643. // Should be 6~9cm normally.
  644. //scanDistance = vinnoExtendedData.ScanDistance;
  645. scanType = vinnoExtendedData.CarotidType == CarotidType.Left ? CarotidScanType.CarotidLeft : CarotidScanType.CarotidRight;
  646. direction = vinnoExtendedData.CarotidDirection == CarotidDirection.BottomToTop ? CarotidScanDirection.BottomToTop
  647. : CarotidScanDirection.TopToBottom;
  648. }
  649. else
  650. {
  651. //This is a walkaround : vid's first frame is black image or depth coordinate is not in the right place.
  652. var middleCount = vinnoImageData.ImageCount / 2;
  653. var vinnoImage = vinnoImageData.GetImage(middleCount);
  654. var imageBuffer = vinnoImage.ImageData;
  655. if (_recognizeCarotidTypeCanBeUse)
  656. {
  657. //转成彩色图像
  658. using var img = new Mat();
  659. CvInvoke.Imdecode(imageBuffer, ImreadModes.Color, img);
  660. using var image = img.ToImage<Bgr, byte>();
  661. //识别左右颈信息
  662. scanType = _recognizeCarotidType.RecognizeType(image);
  663. }
  664. }
  665. resampleInputData.ScanDistance = scanDistance;
  666. resampleInputData.ScanType = scanType;
  667. resampleInputData.CarotidScanDirection = direction;
  668. resampleResult = ResampleModel.Instance.Resample(vinnoImageData, resampleInputData, _workerLevel);
  669. return resampleResult;
  670. }
  671. /// <summary>
  672. ///
  673. /// </summary>
  674. /// <param name="filePath"></param>
  675. private async Task<List<string>> SurfaceFile(string filePath)
  676. {
  677. var fileUrls = new List<string>();
  678. using (var stream = new FileStream(filePath, FileMode.Open))
  679. {
  680. var reader = new Carotid3DStreamReader(stream);
  681. var width = reader.ReadInt();
  682. var height = reader.ReadInt();
  683. var depth = reader.ReadInt();
  684. var physicalData = VinnoCarotid3DPhysicalData.FromBytes(reader.ReadBytes());
  685. var imageCount = reader.ReadInt();
  686. var fileName = $"{Guid.NewGuid():N}";
  687. for (var i = 0; i < imageCount; i++)
  688. {
  689. var readBytes = reader.ReadBytes();
  690. if (readBytes.Any())
  691. {
  692. using var img = new Mat();
  693. CvInvoke.Imdecode(readBytes, ImreadModes.Color, img);
  694. var buf = new Emgu.CV.Util.VectorOfByte();
  695. using var image = img.ToImage<Gray, byte>();
  696. CvInvoke.Imencode(".jpg", image, buf, new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  697. var jpgByte = buf.ToArray();
  698. var localPath = Path.Combine(_tempFolder, $"{fileName}_{i + 1}.jpg");
  699. File.WriteAllBytes(localPath, jpgByte);
  700. var fileUrl = await UploadFileAsync(localPath, Path.GetFileName(localPath));
  701. fileUrls.Add(fileUrl);
  702. File.Delete(localPath);
  703. }
  704. }
  705. }
  706. return fileUrls;
  707. }
  708. private SKBitmap CreateBitmap(VinnoImage vinnoImage)
  709. {
  710. try
  711. {
  712. return SKBitmap.Decode(vinnoImage.ImageData);
  713. }
  714. catch (Exception ex)
  715. {
  716. Logger.WriteLineError($"Create skbitmap by VinnoImage error:{ex}");
  717. }
  718. return null;
  719. }
  720. private EnumColorType MapTo(SKColorType sKColor)
  721. {
  722. switch (sKColor)
  723. {
  724. case SKColorType.Rgba8888:
  725. return EnumColorType.Rgba;
  726. case SKColorType.Rgb888x:
  727. return EnumColorType.Rgba;
  728. case SKColorType.Bgra8888:
  729. return EnumColorType.Bgra;
  730. case SKColorType.Gray8:
  731. return EnumColorType.Gray8;
  732. }
  733. throw new Exception($"AIService not support color type:{sKColor}");
  734. }
  735. private void AIdiagSystem_NotifyError(object sender, ErrorEventArgs e)
  736. {
  737. Logger.WriteLineError("AIdiagSystem_NotifyError:" + e.GetException());
  738. }
  739. private void AIdiagSystem_NotifyLog(object sender, LogEventArgs e)
  740. {
  741. if (e != null && !string.IsNullOrEmpty(e.Msg))
  742. {
  743. switch (e.LogType)
  744. {
  745. case EnumLogType.InfoLog:
  746. Logger.WriteLineInfo($"AIdiagSystem_NotifyLog:{e.Msg}");
  747. break;
  748. case EnumLogType.ErrorLog:
  749. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  750. break;
  751. case EnumLogType.WarnLog:
  752. Logger.WriteLineWarn($"AIdiagSystem_NotifyLog:{e.Msg}");
  753. break;
  754. case EnumLogType.FatalLog:
  755. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  756. break;
  757. default:
  758. Logger.WriteLineInfo(e.Msg);
  759. break;
  760. }
  761. }
  762. }
  763. private DiagnosisConclusion GetDiagnosisConclusion<T>(List<T> results) where T : AIDiagnosisPerImageModel
  764. {
  765. var diagnosisConclusions = new List<DiagnosisConclusion>();
  766. foreach (var imageResult in results)
  767. {
  768. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  769. {
  770. var benignLabels = new List<int>();
  771. var malignantLabels = new List<int>();
  772. if (diagnosisResult.Organ == DiagnosisOrganEnum.Breast)
  773. {
  774. benignLabels = new List<int> { 1, 2, 3 };
  775. malignantLabels = new List<int> { 4, 5, 6, 7 };
  776. }
  777. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Liver)
  778. {
  779. benignLabels = new List<int> { 1, 2, 3, 5, 6, 7, 8 };
  780. malignantLabels = new List<int> { 4 };
  781. }
  782. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Thyroid)
  783. {
  784. benignLabels = new List<int> { 1, 2, 7 };
  785. malignantLabels = new List<int> { 3, 4, 5, 6 };
  786. }
  787. var labels = diagnosisResult.DetectedObjects.Select(x => x.Label);
  788. if (labels.Contains(0))
  789. {
  790. diagnosisConclusions.Add(DiagnosisConclusion.NoObviousLesion);
  791. }
  792. if (labels.Intersect(benignLabels).Any())
  793. {
  794. diagnosisConclusions.Add(DiagnosisConclusion.Benign);
  795. }
  796. if (labels.Intersect(malignantLabels).Any())
  797. {
  798. diagnosisConclusions.Add(DiagnosisConclusion.Malignant);
  799. }
  800. }
  801. }
  802. var containsBenign = diagnosisConclusions.Contains(DiagnosisConclusion.Benign);
  803. var containsMalignant = diagnosisConclusions.Contains(DiagnosisConclusion.Malignant);
  804. var containsNoObviousLesion = diagnosisConclusions.Contains(DiagnosisConclusion.NoObviousLesion);
  805. if (containsBenign && containsMalignant)
  806. {
  807. return DiagnosisConclusion.BenignAndMalignant;
  808. }
  809. else if (containsBenign)
  810. {
  811. return DiagnosisConclusion.Benign;
  812. }
  813. else if (containsMalignant)
  814. {
  815. return DiagnosisConclusion.Malignant;
  816. }
  817. else if (containsNoObviousLesion)
  818. {
  819. return DiagnosisConclusion.NoObviousLesion;
  820. }
  821. else
  822. {
  823. return DiagnosisConclusion.Unrecognized;
  824. }
  825. }
  826. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<AIDiagnosisPerImageDTO> results)
  827. {
  828. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  829. foreach (var imageResult in results)
  830. {
  831. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  832. {
  833. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  834. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null && diagnosisResult.DetectedObjects?.Any() == true)
  835. {
  836. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  837. }
  838. }
  839. }
  840. return diagnosisOrgans.Distinct().ToList();
  841. }
  842. private Dictionary<int, AIDiagResultPerImg> NormalDiagnosis(VinnoImageData imageData)
  843. {
  844. var images = new List<RawImage>();
  845. var results = new Dictionary<int, AIDiagResultPerImg>();
  846. try
  847. {
  848. var totalCount = imageData.ImageCount;
  849. for (var i = 0; i < totalCount; i++)
  850. {
  851. var image = imageData.GetImage(i);
  852. var bitmap = CreateBitmap(image);
  853. if (bitmap != null)
  854. {
  855. images.Add(new RawImage(bitmap.Bytes, bitmap.Width, bitmap.Height, MapTo(bitmap.ColorType)));
  856. }
  857. }
  858. if (images.Count > 0)
  859. {
  860. var excuteCount = images.Count / _batchImageSize;
  861. var remainderCount = images.Count % _batchImageSize;
  862. var diagId = _diagSystem.StartEvalutationOfMultipleImageBatches(images.Count);
  863. for (var j = 0; j < excuteCount; j++)
  864. {
  865. var excuteImages = images.GetRange(j * _batchImageSize, _batchImageSize);
  866. _diagSystem.PushOneBatchOfImagesAsync(diagId, excuteImages);
  867. Thread.Sleep(_sleepTime);
  868. }
  869. if (remainderCount > 0)
  870. {
  871. var excuteImages = images.GetRange(excuteCount * _batchImageSize, remainderCount);
  872. _diagSystem.PushOneBatchOfImagesAsync(diagId, excuteImages);
  873. }
  874. results = _diagSystem.GetEvaluationsOfPushedMultipleImageBatches(diagId);
  875. }
  876. }
  877. catch (Exception ex)
  878. {
  879. Logger.WriteLineWarn($"AIService NormalDiagnosis err, {ex}");
  880. }
  881. finally
  882. {
  883. foreach (var image in images)
  884. {
  885. image?.Dispose();
  886. }
  887. }
  888. return results;
  889. }
  890. /// <summary>
  891. /// 保存AI诊断结果
  892. /// </summary>
  893. /// <param name="relationCode"></param>
  894. /// <param name="fileUrl"></param>
  895. /// <param name="diagnosisResultInfos"></param>
  896. /// <returns></returns>
  897. private async Task<bool> AddDiagnosisResultInfosAsync(string relationCode, string fileUrl, Dictionary<int, AIDiagResultPerImg> diagnosisResultInfos)
  898. {
  899. try
  900. {
  901. var addRequest = new AddDiagnosisResultInfosDBRequest
  902. {
  903. RelationCode = relationCode,
  904. FileUrl = fileUrl,
  905. DiagnosisResultInfos = diagnosisResultInfos.Select(r => new DiagnosisResultDTO
  906. {
  907. Index = r.Key,
  908. DiagnosisResult = Newtonsoft.Json.JsonConvert.SerializeObject(r.Value)
  909. }).ToList()
  910. };
  911. _diagnosisResultDBService.AddDiagnosisResultInfosAsync(addRequest);
  912. return true;
  913. }
  914. catch (Exception ex)
  915. {
  916. Logger.WriteLineWarn($"AIDiagnosisService AddDiagnosisResultInfosAsync err, ex:{ex}");
  917. return false;
  918. }
  919. }
  920. /// <summary>下载文件</summary>
  921. /// <param name="fileUrl"></param>
  922. /// <returns></returns>
  923. private async Task<string> DownloadAsync(string fileUrl)
  924. {
  925. try
  926. {
  927. if (string.IsNullOrEmpty(fileUrl))
  928. {
  929. return string.Empty;
  930. }
  931. if (!Directory.Exists(_tempFolder))
  932. {
  933. Directory.CreateDirectory(_tempFolder);
  934. }
  935. var fileName = Path.GetFileName(fileUrl);
  936. var tempFile = Path.Combine(_tempFolder, fileName);
  937. if (File.Exists(tempFile))
  938. {
  939. return tempFile;
  940. }
  941. long fileSize = 0;
  942. using (var request = new HttpRequestMessage())
  943. {
  944. request.RequestUri = new Uri(fileUrl);
  945. request.Method = HttpMethod.Get;
  946. var response = await _httpClient.SendAsync(request);
  947. if (response != null && response.StatusCode == HttpStatusCode.OK)
  948. {
  949. var contentLength = response.Content.Headers.ContentLength;
  950. fileSize = contentLength == null ? 0 : contentLength.Value;
  951. }
  952. }
  953. if (fileSize <= 0)
  954. {
  955. throw new NotSupportedException($"fileSize is {fileSize}");
  956. }
  957. byte[] bytes = await _httpClient.GetByteArrayAsync(fileUrl);
  958. File.WriteAllBytes(tempFile, bytes);
  959. return tempFile;
  960. }
  961. catch (Exception ex)
  962. {
  963. Logger.WriteLineWarn($"DiagnosisService download file err, url: {fileUrl}, {ex}");
  964. }
  965. finally
  966. {
  967. //Logger.WriteLineInfo($"download file:{fileUrl}");
  968. }
  969. return string.Empty;
  970. }
  971. /// <summary>
  972. /// 上传文件
  973. /// </summary>
  974. /// <param name="filePath"></param>
  975. /// <param name="fileName"></param>
  976. /// <returns></returns>
  977. private async Task<string> UploadFileAsync(string filePath, string fileName)
  978. {
  979. var fileToken = "";
  980. using (var fileStream = new FileStream(filePath, FileMode.Open))
  981. {
  982. var size = fileStream.Length;
  983. byte[] buffer = new byte[fileStream.Length];
  984. fileStream.Read(buffer, 0, buffer.Length);
  985. fileToken = await DoUploadFile(fileName, buffer);
  986. }
  987. return fileToken;
  988. }
  989. /// <summary>
  990. /// 上传文件
  991. /// </summary>
  992. /// <param name="filePath"></param>
  993. /// <param name="fileName"></param>
  994. /// <returns></returns>
  995. private async Task<Tuple<string, long>> UploadFileTupleAsync(string filePath, string fileName)
  996. {
  997. var fileToken = "";
  998. long size = 0;
  999. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1000. {
  1001. size = fileStream.Length;
  1002. byte[] buffer = new byte[fileStream.Length];
  1003. fileStream.Read(buffer, 0, buffer.Length);
  1004. fileToken = await DoUploadFile(fileName, buffer);
  1005. }
  1006. return new Tuple<string, long>(fileToken, size);
  1007. }
  1008. /// <summary>
  1009. /// 上传文件
  1010. /// </summary>
  1011. /// <param name="fileName"></param>
  1012. /// <param name="fileData"></param>
  1013. /// <returns></returns>
  1014. async Task<string> DoUploadFile(string fileName, byte[] fileData)
  1015. {
  1016. var requestHeads = new Dictionary<string, string>();
  1017. var defaultToken = await _authenticationService.GetServerDefaultTokenAsync();
  1018. var authorizationRet = await _storageService.GetAuthorizationAsync(
  1019. new FileServiceRequest
  1020. {
  1021. Token = defaultToken,
  1022. FileName = fileName,
  1023. });
  1024. requestHeads.Add("Authorization", authorizationRet.Authorization);
  1025. var fileUrl = authorizationRet.StorageUrl;
  1026. Logger.WriteLineInfo($"DoUploadFile fileUrl:{fileUrl}");
  1027. using (var request = new HttpRequestMessage())
  1028. {
  1029. var fileExtension = Path.GetExtension(fileName);
  1030. var mimeType = FileHelper.GetMimeType(fileExtension);
  1031. var contentType = MediaTypeHeaderValue.Parse(mimeType);
  1032. using (UploadContent content = new UploadContent(fileData, contentType))
  1033. {
  1034. request.RequestUri = new Uri(fileUrl);
  1035. request.Method = HttpMethod.Put;
  1036. request.Content = content;
  1037. foreach (var head in requestHeads)
  1038. {
  1039. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  1040. }
  1041. var result = await ExecuteRequest(request);
  1042. if (!result)
  1043. {
  1044. throw new Exception("Upload file failed!");
  1045. }
  1046. }
  1047. }
  1048. return fileUrl;
  1049. }
  1050. private UploadFileTypeEnum GetUploadFileType(string fileName)
  1051. {
  1052. if (fileName.EndsWith(".vid"))
  1053. {
  1054. return UploadFileTypeEnum.VID;
  1055. }
  1056. else if (fileName.EndsWith(".jpg"))
  1057. {
  1058. return UploadFileTypeEnum.JPG;
  1059. }
  1060. else
  1061. {
  1062. return UploadFileTypeEnum.MP4;
  1063. }
  1064. }
  1065. /// <summary>
  1066. /// 执行请求
  1067. /// </summary>
  1068. /// <param name="httpRequestMessage"></param>
  1069. /// <typeparam name="T"></typeparam>
  1070. /// <returns></returns>
  1071. public async Task<bool> ExecuteRequest(HttpRequestMessage httpRequestMessage)
  1072. {
  1073. try
  1074. {
  1075. var response = await _httpClient.SendAsync(httpRequestMessage);
  1076. if (response != null && response.StatusCode == HttpStatusCode.OK)
  1077. {
  1078. return true;
  1079. }
  1080. return false;
  1081. }
  1082. catch (Exception ex)
  1083. {
  1084. throw ex;
  1085. }
  1086. }
  1087. private List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D> PointToDto(Point2D[] points)
  1088. {
  1089. var dstContours = new List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D>();
  1090. foreach (var p in points)
  1091. {
  1092. dstContours.Add(new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1093. {
  1094. X = p.X,
  1095. Y = p.Y
  1096. });
  1097. }
  1098. return dstContours;
  1099. }
  1100. private List<DiagnosisKeyPointDTO> KeyPointToDto(KeyPointInfo[] points)
  1101. {
  1102. var dstKeys = new List<DiagnosisKeyPointDTO>();
  1103. foreach (var point in points)
  1104. {
  1105. dstKeys.Add(new DiagnosisKeyPointDTO
  1106. {
  1107. Type = (DiagnosisKeyPointType)point.Type,
  1108. IndexInContour = point.IndexInContour,
  1109. Point = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1110. {
  1111. X = point.Point.X,
  1112. Y = point.Point.Y,
  1113. },
  1114. });
  1115. }
  1116. return dstKeys;
  1117. }
  1118. }
  1119. }