AIDiagnosisService.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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. namespace WingAIDiagnosisService.Service
  36. {
  37. /// <summary>
  38. /// AI诊断服务
  39. /// </summary>
  40. public partial class AIDiagnosisService : JsonRpcService, IAIDiagnosisService
  41. {
  42. private EnumPerformance _workerLevel = EnumPerformance.Medium;
  43. private int _batchImageSize = 16;
  44. private int _sleepTime = 400;
  45. private readonly string _tempFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DiagnosisTemp");
  46. private AIDiagSystem _diagSystem;
  47. private AILesionCompareUtils _lesionCompareUtil;
  48. private RecognizeCarotidType _recognizeCarotidType = new RecognizeCarotidType();
  49. //识别左右颈是否初始化成功
  50. private static bool _recognizeCarotidTypeCanBeUse = false;
  51. static HttpClient _httpClient = new HttpClient();
  52. private IAuthenticationService _authenticationService;
  53. private IStorageService _storageService;
  54. private readonly string _carotidName = "CarotidPlaqueDetect";
  55. /// <summary>
  56. /// Init service
  57. /// </summary>
  58. public override void Load(JsonRpcClientPool jsonRpcClientPool)
  59. {
  60. base.Load(jsonRpcClientPool);
  61. _authenticationService = GetProxy<IAuthenticationService>();
  62. _storageService = GetProxy<IStorageService>();
  63. _workerLevel = (EnumPerformance)ConfigurationManager.GetParammeter<IntParameter>("AI", "WorkerLevel").Value;
  64. _batchImageSize = ConfigurationManager.GetParammeter<IntParameter>("AI", "BatchImageSize").Value;
  65. _httpClient.Timeout = TimeSpan.FromMinutes(15);
  66. InitAISystem();
  67. //初始化识别左右颈
  68. _recognizeCarotidTypeCanBeUse = _recognizeCarotidType.Initialization();
  69. }
  70. private void InitAISystem()
  71. {
  72. var modeFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks");
  73. _diagSystem = new AIDiagSystem(_workerLevel, modeFilePath, EnumInferWorkName.Default, false, true, true);
  74. if (_diagSystem != null)
  75. {
  76. _diagSystem.NotifyError += AIdiagSystem_NotifyError;
  77. _diagSystem.NotifyLog += AIdiagSystem_NotifyLog;
  78. }
  79. _lesionCompareUtil = new AILesionCompareUtils();
  80. }
  81. /// <summary>
  82. /// 查询杏聆荟支持的AI模块
  83. /// </summary>
  84. /// <param name="request">查询杏聆荟支持的AI模块请求实体</param>
  85. /// <returns></returns>
  86. /// <show>false</show>
  87. public async Task<IList<string>> FindDiagnosisModulesAsync(TokenRequest request)
  88. {
  89. try
  90. {
  91. var moduleNames = AIDiagSystem.GetValidModuleNamesForVclound() ?? new List<EnumAIModuleNames>();
  92. var resultData = moduleNames.Select(x => x.ToString()).ToList();
  93. if (!resultData.Contains(_carotidName))
  94. {
  95. resultData.Remove("CarotidArtery");
  96. resultData.Add(_carotidName);
  97. }
  98. return await Task.FromResult(resultData.Distinct().ToList());
  99. }
  100. catch (Exception)
  101. {
  102. return new List<string>();
  103. }
  104. }
  105. /// <summary>
  106. /// 图像诊断
  107. /// </summary>
  108. /// <param name="request">图像诊断请求实体</param>
  109. /// <returns>图像诊断结果</returns>
  110. /// <show>false</show>
  111. public async Task<DiagnosisImageResult> DiagnosisImageAsync(DiagnosisImageRequest request)
  112. {
  113. try
  114. {
  115. var localFile = await DownloadAsync(request.FileToken);
  116. if (File.Exists(localFile))
  117. {
  118. if (_diagSystem == null)
  119. {
  120. InitAISystem();
  121. }
  122. using (var imageData = new VinnoImageData(localFile, OperationMode.Open))
  123. {
  124. if (DiagnosisHelper.IsCarotid(imageData))
  125. {
  126. if (imageData.ImageCount > DiagnosisHelper.CarotidMinImageCounts)
  127. {
  128. //颈动脉
  129. var result = await CarotidDiagnosis(imageData);
  130. var resultData = new DiagnosisImageResult()
  131. {
  132. DiagnosisOrgans = new List<DiagnosisOrganEnum>() { DiagnosisOrganEnum.CarotidArtery },
  133. CarotidResult = result
  134. };
  135. return resultData;
  136. }
  137. }
  138. else
  139. {
  140. //乳腺、肝脏
  141. var results = NormalDiagnosis(imageData);
  142. var diagnosisResults = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value)).ToList();
  143. var diagnosisResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisResults));
  144. var resultData = new DiagnosisImageResult()
  145. {
  146. DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisResults),
  147. DiagnosisResult = diagnosisResult,
  148. DiagnosisOrgans = GetDiagnosisOrgans(diagnosisResult),
  149. };
  150. return resultData;
  151. }
  152. }
  153. }
  154. }
  155. catch (Exception ex)
  156. {
  157. Logger.WriteLineWarn($"DiagnosisImageAsync err {ex}");
  158. }
  159. return new DiagnosisImageResult();
  160. }
  161. /// <summary>
  162. /// 生成AI报告
  163. /// </summary>
  164. /// <param name="request">生成AI报告请求实体</param>
  165. /// <returns></returns>
  166. /// <show>false</show>
  167. public async Task<DiagnosisReportResult> DiagnosisReportAsync(DiagnosisReportRequest request)
  168. {
  169. try
  170. {
  171. if (request.Organ == DiagnosisOrganEnum.CarotidArtery)
  172. {
  173. var result = await GetCarotidAIMeasureResult(request);
  174. return result;
  175. }
  176. else
  177. {
  178. var manager = await CreateDiagnosisManagerAsync(request);
  179. var reportPerImages = manager.GetReportResults();
  180. var diagnosisConclusion = GetDiagnosisConclusion(reportPerImages);
  181. var diagnosisResult = new List<DiagnosisPerImageDTO>();
  182. foreach (var item in reportPerImages)
  183. {
  184. var aiFileToken = await UploadFileAsync(item.AILocalImagePath, Path.GetFileName(item.AILocalImagePath));
  185. var perImageResult = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageDTO>(Newtonsoft.Json.JsonConvert.SerializeObject(item));
  186. perImageResult.RemedicalCode = item.RemedicalCode;
  187. perImageResult.DataType = (RemedicalFileDataTypeEnum)item.DataType;
  188. perImageResult.Pixel = item.Pixel;
  189. perImageResult.AIFileToken = aiFileToken;
  190. diagnosisResult.Add(perImageResult);
  191. }
  192. return new DiagnosisReportResult
  193. {
  194. DiagnosisConclusion = (DiagnosisConclusionEnum)diagnosisConclusion,
  195. DiagnosisResult = diagnosisResult,
  196. };
  197. }
  198. }
  199. catch (Exception ex)
  200. {
  201. Logger.WriteLineWarn($"AIService DiagnosisReport err, {ex}");
  202. }
  203. return new DiagnosisReportResult();
  204. }
  205. /// <summary>
  206. /// 获取颈动脉ai报告数据
  207. /// </summary>
  208. /// <param name="request">生成AI报告请求实体</param>
  209. /// <returns></returns>
  210. private async Task<DiagnosisReportResult> GetCarotidAIMeasureResult(DiagnosisReportRequest request)
  211. {
  212. var result = new DiagnosisReportResult()
  213. {
  214. DiagnosisConclusion = DiagnosisConclusionEnum.NoObviousLesion
  215. };
  216. var orderedExamDatas = request.RemedicalList.OrderByDescending(m => m.CarotidResult?.CarotidScanType).Where(m => m.CarotidResult?.MeasureImageFiles != null && m.CarotidResult.MeasureImageFiles.Count > 0);
  217. if (orderedExamDatas?.Count() > 0)
  218. {
  219. DiagnosisRemicalDTO leftData = null;
  220. //left
  221. var leftExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidLeft);
  222. if (leftExamDatas.Any())
  223. {
  224. foreach (var leftItem in leftExamDatas)
  225. {
  226. if (string.IsNullOrEmpty(leftItem.CarotidResult.MeasureResult))
  227. {
  228. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  229. continue;
  230. }
  231. //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}}";
  232. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(leftItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  233. if (measureResult.PlaqueResult?.PlaqueCountType != Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  234. {
  235. leftData = leftItem;
  236. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  237. break;
  238. }
  239. }
  240. if (leftData == null)
  241. {
  242. leftData = leftExamDatas.First();
  243. }
  244. }
  245. if (leftData != null)
  246. {
  247. result.CarotidResult.Add(leftData);
  248. }
  249. //right
  250. float intimaThickStandard = 1.0f;
  251. DiagnosisRemicalDTO rightData = null;
  252. var rightExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidRight);
  253. if (rightExamDatas.Any())
  254. {
  255. foreach (var rightItem in rightExamDatas)
  256. {
  257. if (string.IsNullOrEmpty(rightItem.CarotidResult.MeasureResult))
  258. {
  259. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  260. continue;
  261. }
  262. //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}}";
  263. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(rightItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  264. if (measureResult.IntimaResult?.PostIntima?.IntimaThick > intimaThickStandard)
  265. {
  266. rightData = rightItem;
  267. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  268. break;
  269. }
  270. }
  271. if (rightData == null)
  272. {
  273. rightData = rightExamDatas.First();
  274. }
  275. }
  276. if (rightData != null)
  277. {
  278. result.CarotidResult.Add(rightData);
  279. }
  280. Logger.WriteLineInfo($"AIDiagnosisService package carotidAIMeasureResult finished, CarotidLeftRemedicalCode:{leftData?.RemedicalCode}, CarotidLeft:{leftData?.CarotidResult?.MeasureResult}, CarotidRightRemedicalCode:{rightData?.RemedicalCode}, CarotidRight:" + rightData?.CarotidResult?.MeasureResult);
  281. }
  282. return result;
  283. }
  284. private async Task<BaseDiagnosis> CreateDiagnosisManagerAsync(DiagnosisReportRequest request)
  285. {
  286. var recordResults = new List<DiagnosisPerImageModel>();
  287. foreach (var remedical in request.RemedicalList)
  288. {
  289. foreach (var perImage in remedical.DiagnosisResult)
  290. {
  291. var localFile = await DownloadAsync(remedical.FileToken);
  292. if (File.Exists((localFile)))
  293. {
  294. var diagnosisPerImage = new DiagnosisPerImageModel();
  295. diagnosisPerImage.RemedicalCode = remedical.RemedicalCode;
  296. diagnosisPerImage.DataType = (WingAIDiagnosisService.Manage.DataType)remedical.DataType;
  297. diagnosisPerImage.LocalVidPath = localFile;
  298. diagnosisPerImage.Index = perImage.Index;
  299. diagnosisPerImage.PriorityScore = perImage.PriorityScore;
  300. var diagnosisResults = Newtonsoft.Json.JsonConvert.DeserializeObject<List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>>(Newtonsoft.Json.JsonConvert.SerializeObject(perImage.DiagResultsForEachOrgan));
  301. diagnosisPerImage.DiagResultsForEachOrgan = diagnosisResults;
  302. recordResults.Add(diagnosisPerImage);
  303. }
  304. }
  305. }
  306. switch (request.Organ)
  307. {
  308. case DiagnosisOrganEnum.Breast:
  309. return new BreastDiagnosis(recordResults, request.Organ);
  310. case DiagnosisOrganEnum.Liver:
  311. return new LiverDiagnosis(recordResults, request.Organ);
  312. default:
  313. throw new Exception($"not support organ type:{request.Organ.ToString()}");
  314. }
  315. }
  316. private async Task<CarotidResultDTO> CarotidDiagnosis(VinnoImageData imageData)
  317. {
  318. var resampleInputData = new ResampleInputData();
  319. resampleInputData.SurfaceFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.SurfaceFileSuffix);
  320. resampleInputData.MdlFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ModelFileSuffix);
  321. resampleInputData.MdlZipFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ZipFileSuffix);
  322. resampleInputData.BaseAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  323. resampleInputData.PlaqueAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  324. resampleInputData.YShapeAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  325. var resampleResult = TryResample(imageData, resampleInputData);
  326. if (resampleResult.ResampleErrorCode == ResampleErrorCode.Success)
  327. {
  328. var carotidAIImageTokens = new List<CarotidAIImage>();
  329. if (resampleResult.CarotidAIMeasureResult.IsYImageSuccess)
  330. {
  331. var fileUrl = await UploadFileAsync(resampleInputData.YShapeAIImagePath, Path.GetFileName(resampleInputData.YShapeAIImagePath));
  332. if (!string.IsNullOrWhiteSpace(fileUrl))
  333. {
  334. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.YShape });
  335. }
  336. File.Delete(resampleInputData.YShapeAIImagePath);
  337. }
  338. if (resampleResult.CarotidAIMeasureResult.IntimaResult.IsSuccess)
  339. {
  340. var fileUrl = await UploadFileAsync(resampleInputData.BaseAIImagePath, Path.GetFileName(resampleInputData.BaseAIImagePath));
  341. if (!string.IsNullOrWhiteSpace(fileUrl))
  342. {
  343. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Base });
  344. }
  345. File.Delete(resampleInputData.BaseAIImagePath);
  346. }
  347. if (resampleResult.CarotidAIMeasureResult.PlaqueResult.IsSuccess && resampleResult.CarotidAIMeasureResult.PlaqueResult.PlaqueCountType != WingAIDiagnosisService.Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  348. {
  349. var fileUrl = await UploadFileAsync(resampleInputData.PlaqueAIImagePath, Path.GetFileName(resampleInputData.PlaqueAIImagePath));
  350. if (!string.IsNullOrWhiteSpace(fileUrl))
  351. {
  352. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Plaque });
  353. }
  354. File.Delete(resampleInputData.PlaqueAIImagePath);
  355. }
  356. var surfaceImageList = new List<string>();
  357. long surfaceFileSize=0;
  358. var cdnSurfaceFile="";
  359. if (!string.IsNullOrWhiteSpace(resampleInputData.SurfaceFilePath) && File.Exists(resampleInputData.SurfaceFilePath))
  360. {
  361. surfaceImageList = await SurfaceFile(resampleInputData.SurfaceFilePath);
  362. var surfaceFileTuple = await UploadFileTupleAsync(resampleInputData.SurfaceFilePath, Path.GetFileName(resampleInputData.SurfaceFilePath));
  363. surfaceFileSize=surfaceFileTuple.Item2;
  364. File.Delete(resampleInputData.SurfaceFilePath);
  365. resampleInputData.SurfaceFilePath = surfaceFileTuple.Item1;
  366. cdnSurfaceFile= surfaceFileTuple.Item1.ToUrlToken();//ToCDN
  367. }
  368. long mdlFileFileSize=0;
  369. var cdnMdlFile="";
  370. if (!string.IsNullOrWhiteSpace(resampleInputData.MdlFilePath) && File.Exists(resampleInputData.MdlFilePath))
  371. {
  372. var mdlFileTuple = await UploadFileTupleAsync(resampleInputData.MdlFilePath, Path.GetFileName(resampleInputData.MdlFilePath));
  373. mdlFileFileSize=mdlFileTuple.Item2;
  374. File.Delete(resampleInputData.MdlFilePath);
  375. resampleInputData.MdlFilePath = mdlFileTuple.Item1;
  376. cdnMdlFile= mdlFileTuple.Item1.ToUrlToken();//ToCDN
  377. }
  378. var result = new CarotidResultDTO
  379. {
  380. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  381. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  382. SurfaceFile = resampleInputData.SurfaceFilePath,
  383. SurfaceFileSize=surfaceFileSize,
  384. CDNSurfaceFile=cdnSurfaceFile,
  385. MdlFileSize=mdlFileFileSize,
  386. MdlFile = resampleInputData.MdlFilePath,
  387. CDNMdlFile=cdnMdlFile,
  388. MeasureImageFiles = carotidAIImageTokens.Select(x => new MeasureImageFileDTO { ImageType = (CarotidAIImageTypeEnum)x.AIImageType, ImageFile = x.AIImageToken }).ToList(),
  389. MeasureResult = JsonConvert.SerializeObject(resampleResult.CarotidAIMeasureResult),
  390. SurfaceImageList = surfaceImageList,
  391. };
  392. return result;
  393. }
  394. return null;
  395. }
  396. private ResampleResult TryResample(VinnoImageData vinnoImageData, ResampleInputData resampleInputData)
  397. {
  398. float scanDistance = 7;
  399. ResampleResult resampleResult = new ResampleResult(ResampleErrorCode.Fail);
  400. var vinnoExtendedData = VinnoCarotidExtendedData.FromBytes(vinnoImageData.ExtendedData);
  401. CarotidScanType scanType = CarotidScanType.CarotidLeft;
  402. CarotidScanDirection direction = CarotidScanDirection.TopToBottom;
  403. if (vinnoExtendedData != null)
  404. {
  405. // Should be 6~9cm normally.
  406. //scanDistance = vinnoExtendedData.ScanDistance;
  407. scanType = vinnoExtendedData.CarotidType == CarotidType.Left ? CarotidScanType.CarotidLeft : CarotidScanType.CarotidRight;
  408. direction = vinnoExtendedData.CarotidDirection == CarotidDirection.BottomToTop ? CarotidScanDirection.BottomToTop
  409. : CarotidScanDirection.TopToBottom;
  410. }
  411. else
  412. {
  413. //This is a walkaround : vid's first frame is black image or depth coordinate is not in the right place.
  414. var middleCount = vinnoImageData.ImageCount / 2;
  415. var vinnoImage = vinnoImageData.GetImage(middleCount);
  416. var imageBuffer = vinnoImage.ImageData;
  417. if (_recognizeCarotidTypeCanBeUse)
  418. {
  419. //转成彩色图像
  420. using var img = new Mat();
  421. CvInvoke.Imdecode(imageBuffer, ImreadModes.Color, img);
  422. using var image = img.ToImage<Bgr, byte>();
  423. //识别左右颈信息
  424. scanType = _recognizeCarotidType.RecognizeType(image);
  425. }
  426. }
  427. resampleInputData.ScanDistance = scanDistance;
  428. resampleInputData.ScanType = scanType;
  429. resampleInputData.CarotidScanDirection = direction;
  430. resampleResult = ResampleModel.Instance.Resample(vinnoImageData, resampleInputData, _workerLevel);
  431. return resampleResult;
  432. }
  433. /// <summary>
  434. ///
  435. /// </summary>
  436. /// <param name="filePath"></param>
  437. private async Task<List<string>> SurfaceFile(string filePath)
  438. {
  439. var fileUrls = new List<string>();
  440. using (var stream = new FileStream(filePath, FileMode.Open))
  441. {
  442. var reader = new Carotid3DStreamReader(stream);
  443. var width = reader.ReadInt();
  444. var height = reader.ReadInt();
  445. var depth = reader.ReadInt();
  446. var physicalData = VinnoCarotid3DPhysicalData.FromBytes(reader.ReadBytes());
  447. var imageCount = reader.ReadInt();
  448. var fileName = $"{Guid.NewGuid():N}";
  449. for (var i = 0; i < imageCount; i++)
  450. {
  451. var readBytes = reader.ReadBytes();
  452. if (readBytes.Any())
  453. {
  454. using var img = new Mat();
  455. CvInvoke.Imdecode(readBytes, ImreadModes.Color, img);
  456. var buf = new Emgu.CV.Util.VectorOfByte();
  457. using var image = img.ToImage<Gray, byte>();
  458. CvInvoke.Imencode(".jpg", image, buf, new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  459. var jpgByte = buf.ToArray();
  460. var localPath = Path.Combine(_tempFolder, $"{fileName}_{i + 1}.jpg");
  461. File.WriteAllBytes(localPath, jpgByte);
  462. var fileUrl = await UploadFileAsync(localPath, Path.GetFileName(localPath));
  463. fileUrls.Add(fileUrl);
  464. File.Delete(localPath);
  465. }
  466. }
  467. }
  468. return fileUrls;
  469. }
  470. private SKBitmap CreateBitmap(VinnoImage vinnoImage)
  471. {
  472. try
  473. {
  474. return SKBitmap.Decode(vinnoImage.ImageData);
  475. }
  476. catch (Exception ex)
  477. {
  478. Logger.WriteLineError($"Create skbitmap by VinnoImage error:{ex}");
  479. }
  480. return null;
  481. }
  482. private EnumColorType MapTo(SKColorType sKColor)
  483. {
  484. switch (sKColor)
  485. {
  486. case SKColorType.Rgba8888:
  487. return EnumColorType.Rgba;
  488. case SKColorType.Rgb888x:
  489. return EnumColorType.Rgba;
  490. case SKColorType.Bgra8888:
  491. return EnumColorType.Bgra;
  492. case SKColorType.Gray8:
  493. return EnumColorType.Gray8;
  494. }
  495. throw new Exception($"AIService not support color type:{sKColor}");
  496. }
  497. private void AIdiagSystem_NotifyError(object sender, ErrorEventArgs e)
  498. {
  499. Logger.WriteLineError("AIdiagSystem_NotifyError:" + e.GetException());
  500. }
  501. private void AIdiagSystem_NotifyLog(object sender, LogEventArgs e)
  502. {
  503. if (e != null && !string.IsNullOrEmpty(e.Msg))
  504. {
  505. switch (e.LogType)
  506. {
  507. case EnumLogType.InfoLog:
  508. Logger.WriteLineInfo($"AIdiagSystem_NotifyLog:{e.Msg}");
  509. break;
  510. case EnumLogType.ErrorLog:
  511. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  512. break;
  513. case EnumLogType.WarnLog:
  514. Logger.WriteLineWarn($"AIdiagSystem_NotifyLog:{e.Msg}");
  515. break;
  516. case EnumLogType.FatalLog:
  517. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  518. break;
  519. default:
  520. Logger.WriteLineInfo(e.Msg);
  521. break;
  522. }
  523. }
  524. }
  525. private DiagnosisConclusion GetDiagnosisConclusion<T>(List<T> results) where T : AIDiagnosisPerImageModel
  526. {
  527. var diagnosisConclusions = new List<DiagnosisConclusion>();
  528. foreach (var imageResult in results)
  529. {
  530. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  531. {
  532. var benignLabels = new List<int>();
  533. var malignantLabels = new List<int>();
  534. if (diagnosisResult.Organ == DiagnosisOrganEnum.Breast)
  535. {
  536. benignLabels = new List<int> { 1, 2, 3 };
  537. malignantLabels = new List<int> { 4, 5, 6, 7 };
  538. }
  539. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Liver)
  540. {
  541. benignLabels = new List<int> { 1, 2, 3, 5, 6, 7, 8 };
  542. malignantLabels = new List<int> { 4 };
  543. }
  544. var labels = diagnosisResult.DetectedObjects.Select(x => x.Label);
  545. if (labels.Contains(0))
  546. {
  547. diagnosisConclusions.Add(DiagnosisConclusion.NoObviousLesion);
  548. }
  549. if (labels.Intersect(benignLabels).Any())
  550. {
  551. diagnosisConclusions.Add(DiagnosisConclusion.Benign);
  552. }
  553. if (labels.Intersect(malignantLabels).Any())
  554. {
  555. diagnosisConclusions.Add(DiagnosisConclusion.Malignant);
  556. }
  557. }
  558. }
  559. var containsBenign = diagnosisConclusions.Contains(DiagnosisConclusion.Benign);
  560. var containsMalignant = diagnosisConclusions.Contains(DiagnosisConclusion.Malignant);
  561. var containsNoObviousLesion = diagnosisConclusions.Contains(DiagnosisConclusion.NoObviousLesion);
  562. if (containsBenign && containsMalignant)
  563. {
  564. return DiagnosisConclusion.BenignAndMalignant;
  565. }
  566. else if (containsBenign)
  567. {
  568. return DiagnosisConclusion.Benign;
  569. }
  570. else if (containsMalignant)
  571. {
  572. return DiagnosisConclusion.Malignant;
  573. }
  574. else if (containsNoObviousLesion)
  575. {
  576. return DiagnosisConclusion.NoObviousLesion;
  577. }
  578. else
  579. {
  580. return DiagnosisConclusion.Unrecognized;
  581. }
  582. }
  583. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<AIDiagnosisPerImageDTO> results)
  584. {
  585. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  586. foreach (var imageResult in results)
  587. {
  588. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  589. {
  590. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  591. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null && diagnosisResult.DetectedObjects?.Any() == true)
  592. {
  593. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  594. }
  595. }
  596. }
  597. return diagnosisOrgans.Distinct().ToList();
  598. }
  599. private Dictionary<int, AIDiagResultPerImg> NormalDiagnosis(VinnoImageData imageData)
  600. {
  601. var images = new List<RawImage>();
  602. var results = new Dictionary<int, AIDiagResultPerImg>();
  603. try
  604. {
  605. var totalCount = imageData.ImageCount;
  606. for (var i = 0; i < totalCount; i++)
  607. {
  608. var image = imageData.GetImage(i);
  609. var bitmap = CreateBitmap(image);
  610. if (bitmap != null)
  611. {
  612. images.Add(new RawImage(bitmap.Bytes, bitmap.Width, bitmap.Height, MapTo(bitmap.ColorType)));
  613. }
  614. }
  615. if (images.Count > 0)
  616. {
  617. var excuteCount = images.Count / _batchImageSize;
  618. var remainderCount = images.Count % _batchImageSize;
  619. var diagId = _diagSystem.StartEvalutationOfMultipleImageBatches(images.Count);
  620. for (var j = 0; j < excuteCount; j++)
  621. {
  622. var excuteImages = images.GetRange(j * _batchImageSize, _batchImageSize);
  623. _diagSystem.PushOneBatchOfImagesAsync(diagId, excuteImages);
  624. Thread.Sleep(_sleepTime);
  625. }
  626. if (remainderCount > 0)
  627. {
  628. var excuteImages = images.GetRange(excuteCount * _batchImageSize, remainderCount);
  629. _diagSystem.PushOneBatchOfImagesAsync(diagId, excuteImages);
  630. }
  631. results = _diagSystem.GetEvaluationsOfPushedMultipleImageBatches(diagId);
  632. }
  633. }
  634. catch (Exception ex)
  635. {
  636. Logger.WriteLineWarn($"AIService NormalDiagnosis err, {ex}");
  637. }
  638. finally
  639. {
  640. foreach (var image in images)
  641. {
  642. image?.Dispose();
  643. }
  644. }
  645. return results;
  646. }
  647. /// <summary>下载文件</summary>
  648. /// <param name="fileUrl"></param>
  649. /// <returns></returns>
  650. private async Task<string> DownloadAsync(string fileUrl)
  651. {
  652. try
  653. {
  654. if (string.IsNullOrEmpty(fileUrl))
  655. {
  656. return string.Empty;
  657. }
  658. if (!Directory.Exists(_tempFolder))
  659. {
  660. Directory.CreateDirectory(_tempFolder);
  661. }
  662. var fileName = Path.GetFileName(fileUrl);
  663. var tempFile = Path.Combine(_tempFolder, fileName);
  664. if (File.Exists(tempFile))
  665. {
  666. return tempFile;
  667. }
  668. long fileSize = 0;
  669. using (var request = new HttpRequestMessage())
  670. {
  671. request.RequestUri = new Uri(fileUrl);
  672. request.Method = HttpMethod.Get;
  673. var response = await _httpClient.SendAsync(request);
  674. if (response != null && response.StatusCode == HttpStatusCode.OK)
  675. {
  676. var contentLength = response.Content.Headers.ContentLength;
  677. fileSize = contentLength == null ? 0 : contentLength.Value;
  678. }
  679. }
  680. if (fileSize <= 0)
  681. {
  682. throw new NotSupportedException($"fileSize is {fileSize}");
  683. }
  684. byte[] bytes = await _httpClient.GetByteArrayAsync(fileUrl);
  685. File.WriteAllBytes(tempFile, bytes);
  686. return tempFile;
  687. }
  688. catch (Exception ex)
  689. {
  690. Logger.WriteLineWarn($"DiagnosisService download file err, url: {fileUrl}, {ex}");
  691. }
  692. finally
  693. {
  694. //Logger.WriteLineInfo($"download file:{fileUrl}");
  695. }
  696. return string.Empty;
  697. }
  698. /// <summary>
  699. /// 上传文件
  700. /// </summary>
  701. /// <param name="filePath"></param>
  702. /// <param name="fileName"></param>
  703. /// <returns></returns>
  704. private async Task<string> UploadFileAsync(string filePath, string fileName)
  705. {
  706. var fileToken = "";
  707. using (var fileStream = new FileStream(filePath, FileMode.Open))
  708. {
  709. var size = fileStream.Length;
  710. byte[] buffer = new byte[fileStream.Length];
  711. fileStream.Read(buffer, 0, buffer.Length);
  712. fileToken = await DoUploadFile(fileName, buffer);
  713. }
  714. return fileToken;
  715. }
  716. /// <summary>
  717. /// 上传文件
  718. /// </summary>
  719. /// <param name="filePath"></param>
  720. /// <param name="fileName"></param>
  721. /// <returns></returns>
  722. private async Task<Tuple<string, long>> UploadFileTupleAsync(string filePath, string fileName)
  723. {
  724. var fileToken = "";
  725. long size = 0;
  726. using (var fileStream = new FileStream(filePath, FileMode.Open))
  727. {
  728. size = fileStream.Length;
  729. byte[] buffer = new byte[fileStream.Length];
  730. fileStream.Read(buffer, 0, buffer.Length);
  731. fileToken = await DoUploadFile(fileName, buffer);
  732. }
  733. return new Tuple<string, long>(fileToken, size);
  734. }
  735. /// <summary>
  736. /// 上传文件
  737. /// </summary>
  738. /// <param name="fileName"></param>
  739. /// <param name="fileData"></param>
  740. /// <returns></returns>
  741. async Task<string> DoUploadFile(string fileName, byte[] fileData)
  742. {
  743. var requestHeads = new Dictionary<string, string>();
  744. var defaultToken = await _authenticationService.GetServerDefaultTokenAsync();
  745. var authorizationRet = await _storageService.GetAuthorizationAsync(
  746. new FileServiceRequest
  747. {
  748. Token = defaultToken,
  749. FileName = fileName,
  750. });
  751. requestHeads.Add("Authorization", authorizationRet.Authorization);
  752. var fileUrl = authorizationRet.StorageUrl;
  753. Logger.WriteLineInfo($"DoUploadFile fileUrl:{fileUrl}");
  754. using (var request = new HttpRequestMessage())
  755. {
  756. var fileExtension = Path.GetExtension(fileName);
  757. var mimeType = FileHelper.GetMimeType(fileExtension);
  758. var contentType = MediaTypeHeaderValue.Parse(mimeType);
  759. using (UploadContent content = new UploadContent(fileData, contentType))
  760. {
  761. request.RequestUri = new Uri(fileUrl);
  762. request.Method = HttpMethod.Put;
  763. request.Content = content;
  764. foreach (var head in requestHeads)
  765. {
  766. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  767. }
  768. var result = await ExecuteRequest(request);
  769. if (!result)
  770. {
  771. throw new Exception("Upload file failed!");
  772. }
  773. }
  774. }
  775. return fileUrl;
  776. }
  777. private UploadFileTypeEnum GetUploadFileType(string fileName)
  778. {
  779. if (fileName.EndsWith(".vid"))
  780. {
  781. return UploadFileTypeEnum.VID;
  782. }
  783. else if (fileName.EndsWith(".jpg"))
  784. {
  785. return UploadFileTypeEnum.JPG;
  786. }
  787. else
  788. {
  789. return UploadFileTypeEnum.MP4;
  790. }
  791. }
  792. /// <summary>
  793. /// 执行请求
  794. /// </summary>
  795. /// <param name="httpRequestMessage"></param>
  796. /// <typeparam name="T"></typeparam>
  797. /// <returns></returns>
  798. public async Task<bool> ExecuteRequest(HttpRequestMessage httpRequestMessage)
  799. {
  800. try
  801. {
  802. var response = await _httpClient.SendAsync(httpRequestMessage);
  803. if (response != null && response.StatusCode == HttpStatusCode.OK)
  804. {
  805. return true;
  806. }
  807. return false;
  808. }
  809. catch (Exception ex)
  810. {
  811. throw ex;
  812. }
  813. }
  814. }
  815. }