AIDiagnosisService.cs 32 KB

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