AIDiagnosisService.cs 32 KB

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