AIDiagnosisService.cs 37 KB

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