AIDiagnosisService.cs 31 KB

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