AIDiagnosisService.cs 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. using WingServerCommon.Service;
  2. using WingInterfaceLibrary.Interface;
  3. using WingInterfaceLibrary.Request;
  4. using WingInterfaceLibrary.Enum;
  5. using WingServerCommon.Config;
  6. using WingServerCommon.Config.Parameters;
  7. using WingAIDiagnosisService.Carotid.Utilities;
  8. using Vinno.vCloud.Common.Vid2;
  9. using WingAIDiagnosisService.Manage;
  10. using WingAIDiagnosisService.Carotid;
  11. using Newtonsoft.Json;
  12. using AI.DiagSystem;
  13. using WingServerCommon.Log;
  14. using System.IO;
  15. using System;
  16. using System.Threading.Tasks;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using AI.Common;
  20. using System.Threading;
  21. using AI.Common.Log;
  22. using SkiaSharp;
  23. using System.Net.Http;
  24. using System.Net;
  25. using WingInterfaceLibrary.Request.Storage;
  26. using WingInterfaceLibrary.Result.AIDiagnosis;
  27. using WingInterfaceLibrary.Request.AIDiagnosis;
  28. using WingInterfaceLibrary.DTO.Record;
  29. using WingInterfaceLibrary.DTO.Comment;
  30. using Emgu.CV;
  31. using Emgu.CV.CvEnum;
  32. using Emgu.CV.Structure;
  33. using System.Net.Http.Headers;
  34. using WingAIDiagnosisService.Common;
  35. using WingInterfaceLibrary.Interface.DBInterface;
  36. using WingInterfaceLibrary.Request.DBRequest;
  37. using WingInterfaceLibrary.DTO.DiagnosisResult;
  38. using WingInterfaceLibrary.Request.RemedicalAISelected;
  39. using ContourModifyUtils;
  40. using WingInterfaceLibrary.Request.DBCopy;
  41. using Newtonsoft.Json.Linq;
  42. namespace WingAIDiagnosisService.Service
  43. {
  44. /// <summary>
  45. /// AI诊断服务
  46. /// </summary>
  47. public partial class AIDiagnosisService : JsonRpcService, IAIDiagnosisService
  48. {
  49. private EnumPerformance _workerLevel = EnumPerformance.Medium;
  50. private int _batchImageSize = 16;
  51. private int _sleepTime = 400;
  52. private readonly string _tempFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DiagnosisTemp");
  53. private AIDiagSystem _diagSystem;
  54. private AILesionCompareUtils _lesionCompareUtil;
  55. private RecognizeCarotidType _recognizeCarotidType = new RecognizeCarotidType();
  56. //识别左右颈是否初始化成功
  57. private static bool _recognizeCarotidTypeCanBeUse = false;
  58. static HttpClient _httpClient = new HttpClient();
  59. private IAuthenticationService _authenticationService;
  60. private IStorageService _storageService;
  61. private readonly string _carotidName = "CarotidPlaqueDetect";
  62. private int _contourInterval = 10;
  63. private IDiagnosisResultDBService _diagnosisResultDBService;
  64. /// <summary>
  65. /// Init service
  66. /// </summary>
  67. public override void Load(JsonRpcClientPool jsonRpcClientPool)
  68. {
  69. base.Load(jsonRpcClientPool);
  70. _authenticationService = GetProxy<IAuthenticationService>();
  71. _storageService = GetProxy<IStorageService>();
  72. _workerLevel = (EnumPerformance)ConfigurationManager.GetParammeter<IntParameter>("AI", "WorkerLevel").Value;
  73. _batchImageSize = ConfigurationManager.GetParammeter<IntParameter>("AI", "BatchImageSize").Value;
  74. _contourInterval = ConfigurationManager.GetParammeter<IntParameter>("AI", "ContourInterval").Value;
  75. _httpClient.Timeout = TimeSpan.FromMinutes(15);
  76. _diagnosisResultDBService = GetProxy<IDiagnosisResultDBService>();
  77. InitAISystem();
  78. //初始化识别左右颈
  79. _recognizeCarotidTypeCanBeUse = _recognizeCarotidType.Initialization();
  80. var keyPointDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "KeyPointDistanceThreshold").Value;
  81. var dragStartPointCatchDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "DragStartPointCatchDistanceThreshold").Value;
  82. ContourModifyHelper.KeyPointDistanceThreshold = keyPointDistanceThreshold;
  83. ContourModifyHelper.DragStartPointCatchDistanceThreshold = dragStartPointCatchDistanceThreshold;
  84. }
  85. private void InitAISystem()
  86. {
  87. var modeFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AIDiagnosis\\Networks");
  88. _diagSystem = new AIDiagSystem(_workerLevel, modeFilePath, EnumInferWorkName.Default, false, true, true);
  89. if (_diagSystem != null)
  90. {
  91. _diagSystem.NotifyError += AIdiagSystem_NotifyError;
  92. _diagSystem.NotifyLog += AIdiagSystem_NotifyLog;
  93. }
  94. _lesionCompareUtil = new AILesionCompareUtils();
  95. }
  96. /// <summary>
  97. /// 查询杏聆荟支持的AI模块
  98. /// </summary>
  99. /// <param name="request">查询杏聆荟支持的AI模块请求实体</param>
  100. /// <returns></returns>
  101. /// <show>false</show>
  102. public async Task<IList<string>> FindDiagnosisModulesAsync(TokenRequest request)
  103. {
  104. try
  105. {
  106. var moduleNames = AIDiagSystem.GetValidModuleNamesForVclound() ?? new List<EnumAIModuleNames>();
  107. var resultData = moduleNames.Select(x => x.ToString()).ToList();
  108. if (!resultData.Contains(_carotidName))
  109. {
  110. resultData.Remove("CarotidArtery");
  111. resultData.Add(_carotidName);
  112. }
  113. return await Task.FromResult(resultData.Distinct().ToList());
  114. }
  115. catch (Exception)
  116. {
  117. return new List<string>();
  118. }
  119. }
  120. /// <summary>
  121. /// 图像诊断
  122. /// </summary>
  123. /// <param name="request">图像诊断请求实体</param>
  124. /// <returns>图像诊断结果</returns>
  125. /// <show>false</show>
  126. public async Task<DiagnosisImageResult> DiagnosisImageAsync(DiagnosisImageRequest request)
  127. {
  128. try
  129. {
  130. var relationCode = request.RelationCode;
  131. var fileUrl = request.FileToken;
  132. var localFile = "";
  133. if (!string.IsNullOrWhiteSpace(request.DiskPath))
  134. {
  135. localFile = request.DiskPath;
  136. }
  137. else
  138. {
  139. localFile = await DownloadAsync(fileUrl);
  140. }
  141. if (File.Exists(localFile))
  142. {
  143. if (_diagSystem == null)
  144. {
  145. InitAISystem();
  146. }
  147. using (var imageData = new VinnoImageData(localFile, OperationMode.Open))
  148. {
  149. if (DiagnosisHelper.IsCarotid(imageData))
  150. {
  151. if (imageData.ImageCount > DiagnosisHelper.CarotidMinImageCounts)
  152. {
  153. //颈动脉
  154. var result = await CarotidDiagnosis(imageData);
  155. var resultData = new DiagnosisImageResult()
  156. {
  157. DiagnosisOrgans = new List<DiagnosisOrganEnum>() { DiagnosisOrganEnum.CarotidArtery },
  158. CarotidResult = result
  159. };
  160. return resultData;
  161. }
  162. }
  163. else
  164. {
  165. //乳腺、肝脏
  166. var results = NormalDiagnosis(relationCode, fileUrl, imageData);
  167. //AI实体有很多参数仅支持get,为了方便后续反序列化,db入库前先做个转换
  168. var diagnosisPerImages = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value, 1)).ToList();
  169. var organs = GetDiagnosisOrgans(diagnosisPerImages);
  170. if (organs.Contains(DiagnosisOrganEnum.Breast))
  171. {
  172. var breastCount = diagnosisPerImages.Where(a => a.PriorityScore > 0).Count();
  173. var totalCount = results.Count;
  174. var breastRate = breastCount * 1.0 / totalCount * 1.0;
  175. if (breastRate <= 1 / 3.0)
  176. {
  177. return new DiagnosisImageResult
  178. {
  179. DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized,
  180. DiagnosisResult = null,
  181. DiagnosisOrgans = new List<DiagnosisOrganEnum>(),
  182. };
  183. }
  184. }
  185. await AddDiagnosisResultInfosAsync(relationCode, fileUrl, diagnosisPerImages);
  186. //轮廓线分隔处理
  187. diagnosisPerImages = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value, _contourInterval)).ToList();
  188. var diagnosisResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisPerImages));
  189. var resultData = new DiagnosisImageResult()
  190. {
  191. DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisPerImages),
  192. DiagnosisResult = diagnosisResult,
  193. DiagnosisOrgans = organs,
  194. };
  195. return resultData;
  196. }
  197. }
  198. }
  199. }
  200. catch (Exception ex)
  201. {
  202. Logger.WriteLineWarn($"DiagnosisImageAsync err {ex}");
  203. }
  204. return new DiagnosisImageResult();
  205. }
  206. private class TempAIResult
  207. {
  208. public int Index { get; set; }
  209. public AIDiagnosisPerImageModel ImageDiagResult { get; set; }
  210. }
  211. private class Description : AI.Common.IDescription
  212. {
  213. public EnumDescriptionType Type { get; set; }
  214. // 其他必要的实现,根据您的接口定义
  215. }
  216. private class DescriptionConverter : JsonConverter
  217. {
  218. public override bool CanConvert(Type objectType)
  219. {
  220. return (objectType == typeof(AI.Common.IDescription));
  221. }
  222. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  223. {
  224. throw new NotImplementedException();
  225. }
  226. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  227. {
  228. var jsonObject = JObject.Load(reader);
  229. var description = new Description();
  230. serializer.Populate(jsonObject.CreateReader(), description);
  231. return description;
  232. }
  233. }
  234. public async Task<DiagnosisImageResult> GetDiagnosisImageResultByAIResultAsync(GetDiagnosisImageResultByAIResultRequest request)
  235. {
  236. try
  237. {
  238. var aiResult = request.AiResult;
  239. var relationCode = request.RelationCode;
  240. var fileUrl = request.FileUrl;
  241. var createTime = request.CreateTime;
  242. var upateTime = request.UpdateTime;
  243. //乳腺、肝脏
  244. // var settings = new JsonSerializerSettings
  245. // {
  246. // Converters = new List<JsonConverter> { new DescriptionConverter() }
  247. // };
  248. var aiDiagResultPerImgList = JsonConvert.DeserializeObject<List<TempAIResult>>(aiResult);
  249. foreach (var item in aiDiagResultPerImgList)
  250. {
  251. item.ImageDiagResult.Index = item.Index;
  252. }
  253. var diagnosisPerImages = aiDiagResultPerImgList.Select(x => x.ImageDiagResult)?.ToList() ?? new List<AIDiagnosisPerImageModel>();
  254. foreach (var perImage in diagnosisPerImages)
  255. {
  256. foreach (var perOrgan in perImage.DiagResultsForEachOrgan)
  257. {
  258. var detectedObjects = new List<WingAIDiagnosisService.Manage.AIDetectedObject>();
  259. foreach (var perDetectedObject in perOrgan.DetectedObjects)
  260. {
  261. if (perDetectedObject.Descriptions != null && perDetectedObject.Descriptions.Any())
  262. {
  263. foreach (var item in perDetectedObject.Descriptions)
  264. {
  265. item.Type = item.DescriptionType;
  266. item.Value = item.DescriptionValue;
  267. }
  268. }
  269. detectedObjects.Add(perDetectedObject);
  270. }
  271. perOrgan.DetectedObjects = detectedObjects;
  272. }
  273. }
  274. var organs = GetDiagnosisOrgans(diagnosisPerImages);
  275. await AddDiagnosisResultInfosAsync(relationCode, fileUrl, diagnosisPerImages, createTime, upateTime, false);
  276. //轮廓线分隔处理
  277. foreach (var perImage in diagnosisPerImages)
  278. {
  279. foreach (var perOrgan in perImage.DiagResultsForEachOrgan)
  280. {
  281. var organContours = new List<WingAIDiagnosisService.Manage.AIDiagnosisPoint2D>();
  282. if (perOrgan.OrganContour != null && perOrgan.OrganContour.Count > 0)
  283. {
  284. var coutours = perOrgan.OrganContour;
  285. if (coutours != null && coutours.Count > 0)
  286. {
  287. for (var i = 0; i < coutours.Count; i++)
  288. {
  289. if (i % _contourInterval == 0)
  290. {
  291. organContours.Add(coutours[i]);
  292. }
  293. }
  294. }
  295. }
  296. perOrgan.OrganContours = organContours;
  297. perOrgan.OrganContour = new List<Manage.AIDiagnosisPoint2D>();
  298. var detectedObjects = new List<WingAIDiagnosisService.Manage.AIDetectedObject>();
  299. foreach (var perDetectedObject in perOrgan.DetectedObjects)
  300. {
  301. var contours = new List<WingAIDiagnosisService.Manage.AIDiagnosisPoint2D>();
  302. if (perDetectedObject.Contour != null && perDetectedObject.Contour.Count > 0)
  303. {
  304. var coutours = perDetectedObject.Contour;
  305. if (coutours != null && coutours.Count > 0)
  306. {
  307. for (var i = 0; i < coutours.Count; i++)
  308. {
  309. if (i % _contourInterval == 0)
  310. {
  311. contours.Add(coutours[i]);
  312. }
  313. }
  314. }
  315. }
  316. perDetectedObject.Contours = contours;
  317. perDetectedObject.Contour = new List<Manage.AIDiagnosisPoint2D>();
  318. detectedObjects.Add(perDetectedObject);
  319. }
  320. perOrgan.DetectedObjects = detectedObjects;
  321. }
  322. }
  323. var diagnosisResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisPerImages));
  324. var resultData = new DiagnosisImageResult()
  325. {
  326. DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisPerImages),
  327. DiagnosisResult = diagnosisResult,
  328. DiagnosisOrgans = organs,
  329. };
  330. return resultData;
  331. }
  332. catch (Exception ex)
  333. {
  334. Logger.WriteLineWarn("GetDiagnosisImageResultByAIResultAsync" + ex);
  335. return null;
  336. }
  337. }
  338. public async Task<CarotidResultDTO> GetCarotidResultAsync(GetCarotidResultRequest request)
  339. {
  340. try
  341. {
  342. var surfaceToken = request.SurfaceToken;
  343. var mdlToken = request.MdlToken;
  344. CarotidScanTypeEnum carotidScanType = request.CarotidScanType;
  345. CarotidScanDirectionEnum carotidScanDirection = request.CarotidScanDirection;
  346. var measureImageFiles = request.MeasureImageFiles;
  347. var measureResult = request.MeasureResult;
  348. var surfaceImageList = await SurfaceFileByUrl(surfaceToken);
  349. var result = new CarotidResultDTO
  350. {
  351. CarotidScanType = carotidScanType,
  352. CarotidScanDirection = carotidScanDirection,
  353. SurfaceFile = surfaceToken,
  354. SurfaceFileSize = 0,
  355. CDNSurfaceFile = surfaceToken,//源站地址
  356. MdlFileSize = 0,
  357. MdlFile = mdlToken,//源站地址
  358. CDNMdlFile = mdlToken,
  359. MeasureImageFiles = measureImageFiles,
  360. MeasureResult = measureResult,
  361. SurfaceImageList = surfaceImageList,
  362. };
  363. return result;
  364. }
  365. catch (Exception ex)
  366. {
  367. Logger.WriteLineWarn("GetCarotidResultAsync" + ex);
  368. return null;
  369. }
  370. }
  371. /// <summary>
  372. ///
  373. /// </summary>
  374. /// <param name="baseUrl"></param>
  375. private async Task<List<string>> SurfaceFileByUrl(string baseUrl)
  376. {
  377. var fileUrls = new List<string>();
  378. //todo 自建存储
  379. try
  380. {
  381. using (var httpClient = new HttpClient())
  382. {
  383. using (var stream = await httpClient.GetStreamAsync(baseUrl))
  384. {
  385. var reader = new Carotid3DStreamReader(stream);
  386. var width = reader.ReadInt();
  387. var height = reader.ReadInt();
  388. var depth = reader.ReadInt();
  389. var physicalData = VinnoCarotid3DPhysicalData.FromBytes(reader.ReadBytes());
  390. var imageCount = reader.ReadInt();
  391. var fileName = $"{Guid.NewGuid():N}";
  392. for (var i = 0; i < imageCount; i++)
  393. {
  394. var readBytes = reader.ReadBytes();
  395. if (readBytes.Any())
  396. {
  397. using var img = new Mat();
  398. CvInvoke.Imdecode(readBytes, ImreadModes.Color, img);
  399. var buf = new Emgu.CV.Util.VectorOfByte();
  400. using var image = img.ToImage<Gray, byte>();
  401. CvInvoke.Imencode(".jpg", image, buf, new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  402. var jpgByte = buf.ToArray();
  403. var localPath = Path.Combine(_tempFolder, $"{fileName}_{i + 1}.jpg");
  404. File.WriteAllBytes(localPath, jpgByte);
  405. var fileUrl = await UploadFileAsync(localPath, Path.GetFileName(localPath));
  406. fileUrls.Add(fileUrl);
  407. File.Delete(localPath);
  408. }
  409. }
  410. }
  411. }
  412. }
  413. catch (Exception ex)
  414. {
  415. Logger.WriteLineWarn("SurfaceFileByUrl" + ex);
  416. fileUrls = new List<string>();
  417. }
  418. return fileUrls;
  419. }
  420. /// <summary>
  421. /// 生成AI报告
  422. /// </summary>
  423. /// <param name="request">生成AI报告请求实体</param>
  424. /// <returns></returns>
  425. /// <show>false</show>
  426. public async Task<DiagnosisReportResult> DiagnosisReportAsync(DiagnosisReportRequest request)
  427. {
  428. try
  429. {
  430. if (request.Organ == DiagnosisOrganEnum.CarotidArtery)
  431. {
  432. var result = await GetCarotidAIMeasureResult(request);
  433. return result;
  434. }
  435. else
  436. {
  437. var manager = await CreateDiagnosisManagerAsync(request);
  438. var reportPerImages = manager.GetReportResults();
  439. var diagnosisConclusion = GetDiagnosisConclusion(reportPerImages);
  440. var diagnosisOrgans = GetDiagnosisOrgans(reportPerImages);
  441. var diagnosisResult = new List<DiagnosisPerImageDTO>();
  442. foreach (var item in reportPerImages)
  443. {
  444. var aiFileToken = await UploadFileAsync(item.AILocalImagePath, Path.GetFileName(item.AILocalImagePath));
  445. var previewPath = Path.Combine(_tempFolder, $"{Guid.NewGuid():N}.jpg");
  446. CreateThumbnailFile(item.AILocalImagePath, 100, 30, previewPath);
  447. var previewUrl = await UploadFileAsync(previewPath, Path.GetFileName(previewPath));
  448. var perImageJson = Newtonsoft.Json.JsonConvert.SerializeObject(item);
  449. var perImageResult = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageDTO>(perImageJson);
  450. perImageResult.RemedicalCode = item.RemedicalCode;
  451. perImageResult.DataType = (RemedicalFileDataTypeEnum)item.DataType;
  452. perImageResult.Pixel = item.Pixel;
  453. perImageResult.AIFileToken = aiFileToken;
  454. perImageResult.AIPreviewFileToken = previewUrl;
  455. perImageResult.PerImageJson = perImageJson;
  456. var conclusion = GetDiagnosisConclusion(new List<DiagnosisPerImageModel> { item });
  457. var organs = GetDiagnosisOrgans(new List<DiagnosisPerImageModel> { item });
  458. perImageResult.DiagnosisConclusion = (DiagnosisConclusionEnum)conclusion;
  459. perImageResult.DiagnosisOrgans = organs;
  460. diagnosisResult.Add(perImageResult);
  461. }
  462. return new DiagnosisReportResult
  463. {
  464. DiagnosisConclusion = (DiagnosisConclusionEnum)diagnosisConclusion,
  465. DiagnosisResult = diagnosisResult,
  466. };
  467. }
  468. }
  469. catch (Exception ex)
  470. {
  471. Logger.WriteLineWarn($"AIService DiagnosisReport err, {ex}");
  472. }
  473. return new DiagnosisReportResult();
  474. }
  475. /// <summary>
  476. /// 查询AI诊断结论
  477. /// </summary>
  478. /// <param name="request">查询AI诊断结论请求实体</param>
  479. /// <returns></returns>
  480. /// <show>false</show>
  481. public async Task<GetDiagnosisConclusionResult> GetDiagnosisConclusionAsync(GetDiagnosisConclusionRequest request)
  482. {
  483. var preImage = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageModel>(request.PerImageJson);
  484. var conclusion = GetDiagnosisConclusion(new List<DiagnosisPerImageModel> { preImage });
  485. var organs = GetDiagnosisOrgans(new List<DiagnosisPerImageModel> { preImage });
  486. var resultData = new GetDiagnosisConclusionResult
  487. {
  488. DiagnosisConclusion = (DiagnosisConclusionEnum)conclusion,
  489. DiagnosisOrgans = organs,
  490. };
  491. return await Task.FromResult(resultData);
  492. }
  493. /// <summary>
  494. /// 查询AI相关枚举集合
  495. /// </summary>
  496. /// <param name="request">查询AI相关枚举集合请求实体</param>
  497. /// <returns>AI相关枚举集合</returns>
  498. /// <value></value>
  499. /// <errorCodes></errorCodes>
  500. public async Task<GetDiagnosisEnumItemsResult> GetDiagnosisEnumItemsAsync(GetDiagnosisEnumItemsRequest request)
  501. {
  502. var diagnosisItems = new List<EnumItemDTO>();
  503. //病灶
  504. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "Breast", new List<string> { "BIRads1" }));
  505. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "Liver", new List<string> { "BIRads1" }));
  506. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "Thyroid", new List<string> { "TIRADS0" }));
  507. //病灶特性描述
  508. diagnosisItems.Add(GetEnumItem(typeof(EnumDesShapeValue)));
  509. diagnosisItems.Add(GetEnumItem(typeof(EnumDesOrientationValue)));
  510. diagnosisItems.Add(GetEnumItem(typeof(EnumDesEchoPatternValue)));
  511. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLesionBoundaryValue)));
  512. diagnosisItems.Add(GetEnumItem(typeof(EnumDesMarginValue)));
  513. diagnosisItems.Add(GetEnumItem(typeof(EnumCalcificationsValue), "Calcification"));
  514. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverShapeValue)));
  515. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverBoundaryValue)));
  516. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverEchoTextureValue)));
  517. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchoPatternValue)));
  518. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidShapeValue)));
  519. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidMarginValue)));
  520. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchogenicFociValue)));
  521. //局灶和弥漫区分
  522. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastLocalLesion", includeFields: new List<string> { "Lipomyoma", "BIRads2", "BIRads3" }));
  523. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastDiffuseLesion", includeFields: new List<string> { "BIRads4A", "BIRads4B", "BIRads4C", "BIRads5" }));
  524. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverLocalLesion", includeFields: new List<string> { "Hyperechoic", "HHE", "CYST", "PossibleCancer" }));
  525. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverDiffuseLesion", includeFields: new List<string> { "FattyLiver", "DiffuseLesions", "Cirrhosis", "PCLD" }));
  526. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidLocalLesion", includeFields: new List<string> { "TIRADS2", "TIRADS3", "TIRADS4a", "TIRADS4b", "TIRADS4c", "TIRADS5" }));
  527. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidDiffuseLesion", includeFields: new List<string> { "DiffuseDisease" }));
  528. var resultData = new GetDiagnosisEnumItemsResult { Source = diagnosisItems };
  529. return await Task.FromResult(resultData);
  530. }
  531. private EnumItemDTO GetEnumItem(Type enumType, string keyCode = "", List<string> excludeFields = null, List<string> includeFields = null)
  532. {
  533. keyCode = !string.IsNullOrWhiteSpace(keyCode) ? keyCode : enumType.Name.ToString().Replace("EnumDes", "").Replace("Enum", "").Replace("Value", "");
  534. var enumNames = Enum.GetNames(enumType).ToList();
  535. if (excludeFields != null && excludeFields.Any())
  536. {
  537. enumNames = enumNames.Except(excludeFields).ToList();
  538. }
  539. if (includeFields != null && includeFields.Any())
  540. {
  541. enumNames = enumNames.Intersect(includeFields).ToList();
  542. }
  543. var children = new List<EnumFieldDTO>();
  544. foreach (var val in enumNames)
  545. {
  546. var id = (int)enumType.GetField(val).GetValue(val);
  547. var fieldValue = val.ToString();
  548. if (enumType == typeof(DiagnosisLiverLabelEnum) && fieldValue == "Hyperechoic")
  549. {
  550. fieldValue = "LiverHyperechoic";
  551. }
  552. children.Add(new EnumFieldDTO
  553. {
  554. Id = id,
  555. Value = fieldValue,
  556. });
  557. }
  558. return new EnumItemDTO
  559. {
  560. Code = keyCode,
  561. Children = children,
  562. };
  563. }
  564. /// <summary>
  565. /// 查询病灶轮廓的关键点
  566. /// </summary>
  567. /// <param name="request">查询病灶轮廓的关键点请求实体</param>
  568. /// <returns>关键点集合</returns>
  569. /// <value></value>
  570. /// <errorCodes></errorCodes>
  571. public async Task<List<DiagnosisKeyPointDTO>> GetKeyPointsOfContourAsync(GetKeyPointsOfContourRequest request)
  572. {
  573. var contourPoints = request.Contours.Select(c => new Point2D
  574. {
  575. X = c.X,
  576. Y = c.Y
  577. }).ToArray();
  578. var ls = request.LesionSize;
  579. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  580. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  581. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  582. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  583. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  584. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  585. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  586. var aiResult = ContourModifyHelper.KeyPointsOfContour(contourPoints, lesionSize);
  587. var resultData = KeyPointToDto(aiResult);
  588. return await Task.FromResult(resultData);
  589. }
  590. /// <summary>
  591. /// 移动光标,查询受影响关键点
  592. /// </summary>
  593. /// <param name="request">移动光标,查询受影响关键点请求实体</param>
  594. /// <returns>受影响关键点下标</returns>
  595. /// <value></value>
  596. /// <errorCodes></errorCodes>
  597. public async Task<List<int>> AffectedKeyPointsByDragActionAsync(AffectedKeyPointsByDragActionRequest request)
  598. {
  599. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  600. {
  601. Type = (EnumKeyPointType)c.Type,
  602. IndexInContour = c.IndexInContour,
  603. Point = new Point2D(c.Point.X, c.Point.Y)
  604. }).ToArray();
  605. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  606. var resultData = ContourModifyHelper.AffectedKeyPointsByDragAction(origKeyPoints, mousePoint).ToList();
  607. return await Task.FromResult(resultData);
  608. }
  609. /// <summary>
  610. /// 拖动光标,查询所有轮廓点和关键点
  611. /// </summary>
  612. /// <param name="request">拖动光标,查询所有轮廓点和关键点请求实体</param>
  613. /// <returns>所有轮廓点和关键点</returns>
  614. /// <value></value>
  615. /// <errorCodes></errorCodes>
  616. public async Task<ContourAndKeyPointsAfterDragResult> ContourAndKeyPointsAfterDragAsync(ContourAndKeyPointsAfterDragRequest request)
  617. {
  618. var origContourPoints = request.Contours.Select(c => new Point2D
  619. {
  620. X = c.X,
  621. Y = c.Y
  622. }).ToArray();
  623. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  624. {
  625. Type = (EnumKeyPointType)c.Type,
  626. IndexInContour = c.IndexInContour,
  627. Point = new Point2D(c.Point.X, c.Point.Y)
  628. }).ToArray();
  629. var dragStartPoint = new Point2D(request.StartPoint.X, request.StartPoint.Y);
  630. var dragEndPoint = new Point2D(request.EndPoint.X, request.EndPoint.Y);
  631. ContourModifyHelper.ContourAndKeyPointsAfterDrag(origContourPoints, origKeyPoints, dragStartPoint, dragEndPoint
  632. , out Point2D[] dstContourPoints, out KeyPointInfo[] dstKeyPoints, out int[] affectedKeyPointIndexes);
  633. var dstContours = PointToDto(dstContourPoints);
  634. var dstKeys = KeyPointToDto(dstKeyPoints);
  635. var resultData = new ContourAndKeyPointsAfterDragResult
  636. {
  637. DstContours = dstContours,
  638. DstKeyPoints = dstKeys,
  639. AffectedKeyPointIndexes = affectedKeyPointIndexes.ToList(),
  640. };
  641. return await Task.FromResult(resultData);
  642. }
  643. /// <summary>
  644. /// 画轮廓模式,查询鼠标位置到轮廓线的最短距离
  645. /// </summary>
  646. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  647. /// <returns>鼠标离轮廓线的距离,高亮点的下标</returns>
  648. /// <value></value>
  649. /// <errorCodes></errorCodes>
  650. public async Task<MinimumDistanceToContourPointsResult> MinimumDistanceToContourPointsAsync(MinimumDistanceToContourPointsRequest request)
  651. {
  652. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  653. {
  654. X = c.X,
  655. Y = c.Y
  656. }).ToArray();
  657. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  658. var distance = ContourModifyHelper.MinimumDistanceToContourPoints(origContourPoints, mousePoint, out int closestPointIndex);
  659. var resultData = new MinimumDistanceToContourPointsResult
  660. {
  661. DistanceCaught = distance,
  662. ClosestPointIndex = closestPointIndex,
  663. };
  664. return await Task.FromResult(resultData);
  665. }
  666. /// <summary>
  667. /// 画轮廓模式,合并新旧轮廓
  668. /// </summary>
  669. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  670. /// <returns>合并后的完整轮廓线,合并后的横纵径尺寸结果</returns>
  671. /// <value></value>
  672. /// <errorCodes></errorCodes>
  673. public async Task<ContourMergeResult> ContourMergeAsync(ContourMergeRequest request)
  674. {
  675. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  676. {
  677. X = c.X,
  678. Y = c.Y
  679. }).ToArray();
  680. var ls = request.LesionSize;
  681. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  682. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  683. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  684. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  685. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  686. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  687. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  688. var drawingNewContourPoints = request.DrawingNewContourPoints.Select(c => new Point2D
  689. {
  690. X = c.X,
  691. Y = c.Y
  692. }).ToArray();
  693. var aiResult = ContourModifyHelper.ContourMerge(origContourPoints, lesionSize, drawingNewContourPoints, out Point2D[] dstContourPoints, out LesionSize dstLesionSize);
  694. var dstContours = PointToDto(dstContourPoints);
  695. var resultData = new ContourMergeResult
  696. {
  697. DstContours = dstContours,
  698. DstLesionSize = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisLesionSize
  699. {
  700. HorizontalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint1.X, Y = dstLesionSize.HorizontalPoint1.Y },
  701. HorizontalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint2.X, Y = dstLesionSize.HorizontalPoint2.Y },
  702. VerticalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint1.X, Y = dstLesionSize.VerticalPoint1.Y },
  703. VerticalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint2.X, Y = dstLesionSize.VerticalPoint2.Y },
  704. HorizontalLengthInPixel = dstLesionSize.HorizontalLengthInPixel,
  705. VerticalLengthInPixel = dstLesionSize.VerticalLengthInPixel,
  706. },
  707. };
  708. return await Task.FromResult(resultData);
  709. }
  710. /// <summary>
  711. /// 获取颈动脉ai报告数据
  712. /// </summary>
  713. /// <param name="request">生成AI报告请求实体</param>
  714. /// <returns></returns>
  715. private async Task<DiagnosisReportResult> GetCarotidAIMeasureResult(DiagnosisReportRequest request)
  716. {
  717. var result = new DiagnosisReportResult()
  718. {
  719. DiagnosisConclusion = DiagnosisConclusionEnum.NoObviousLesion
  720. };
  721. var orderedExamDatas = request.RemedicalList.OrderByDescending(m => m.CarotidResult?.CarotidScanType).Where(m => m.CarotidResult?.MeasureImageFiles != null && m.CarotidResult.MeasureImageFiles.Count > 0);
  722. if (orderedExamDatas?.Count() > 0)
  723. {
  724. DiagnosisRemicalDTO leftData = null;
  725. //left
  726. var leftExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidLeft);
  727. if (leftExamDatas.Any())
  728. {
  729. foreach (var leftItem in leftExamDatas)
  730. {
  731. if (string.IsNullOrEmpty(leftItem.CarotidResult.MeasureResult))
  732. {
  733. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  734. continue;
  735. }
  736. //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}}";
  737. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(leftItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  738. if (measureResult.PlaqueResult?.PlaqueCountType != Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  739. {
  740. leftData = leftItem;
  741. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  742. break;
  743. }
  744. }
  745. if (leftData == null)
  746. {
  747. leftData = leftExamDatas.First();
  748. }
  749. }
  750. if (leftData != null)
  751. {
  752. result.CarotidResult.Add(leftData);
  753. }
  754. //right
  755. float intimaThickStandard = 1.0f;
  756. DiagnosisRemicalDTO rightData = null;
  757. var rightExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidRight);
  758. if (rightExamDatas.Any())
  759. {
  760. foreach (var rightItem in rightExamDatas)
  761. {
  762. if (string.IsNullOrEmpty(rightItem.CarotidResult.MeasureResult))
  763. {
  764. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  765. continue;
  766. }
  767. //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}}";
  768. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(rightItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  769. if (measureResult.IntimaResult?.PostIntima?.IntimaThick > intimaThickStandard)
  770. {
  771. rightData = rightItem;
  772. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  773. break;
  774. }
  775. }
  776. if (rightData == null)
  777. {
  778. rightData = rightExamDatas.First();
  779. }
  780. }
  781. if (rightData != null)
  782. {
  783. result.CarotidResult.Add(rightData);
  784. }
  785. Logger.WriteLineInfo($"AIDiagnosisService package carotidAIMeasureResult finished, CarotidLeftRemedicalCode:{leftData?.RemedicalCode}, CarotidLeft:{leftData?.CarotidResult?.MeasureResult}, CarotidRightRemedicalCode:{rightData?.RemedicalCode}, CarotidRight:" + rightData?.CarotidResult?.MeasureResult);
  786. }
  787. return result;
  788. }
  789. private async Task<BaseDiagnosis> CreateDiagnosisManagerAsync(DiagnosisReportRequest request)
  790. {
  791. var recordResults = new List<DiagnosisPerImageModel>();
  792. foreach (var remedical in request.RemedicalList)
  793. {
  794. foreach (var perImage in remedical.DiagnosisResult)
  795. {
  796. var localFile = await DownloadAsync(remedical.FileToken);
  797. if (File.Exists((localFile)))
  798. {
  799. var diagnosisPerImage = new DiagnosisPerImageModel();
  800. diagnosisPerImage.RemedicalCode = remedical.RemedicalCode;
  801. diagnosisPerImage.DataType = (WingAIDiagnosisService.Manage.DataType)remedical.DataType;
  802. diagnosisPerImage.LocalVidPath = localFile;
  803. diagnosisPerImage.Index = perImage.Index;
  804. diagnosisPerImage.PriorityScore = perImage.PriorityScore;
  805. var diagnosisResults = Newtonsoft.Json.JsonConvert.DeserializeObject<List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>>(Newtonsoft.Json.JsonConvert.SerializeObject(perImage.DiagResultsForEachOrgan));
  806. diagnosisPerImage.DiagResultsForEachOrgan = diagnosisResults;
  807. recordResults.Add(diagnosisPerImage);
  808. }
  809. }
  810. }
  811. switch (request.Organ)
  812. {
  813. case DiagnosisOrganEnum.Breast:
  814. return new BreastDiagnosis(recordResults, request.Organ);
  815. case DiagnosisOrganEnum.Liver:
  816. return new LiverDiagnosis(recordResults, request.Organ);
  817. case DiagnosisOrganEnum.Thyroid://甲状腺
  818. return new ThyroidDiagnosis(recordResults, request.Organ);
  819. default:
  820. throw new Exception($"not support organ type:{request.Organ.ToString()}");
  821. }
  822. }
  823. private async Task<CarotidResultDTO> CarotidDiagnosis(VinnoImageData imageData)
  824. {
  825. var resampleInputData = new ResampleInputData();
  826. resampleInputData.SurfaceFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.SurfaceFileSuffix);
  827. resampleInputData.MdlFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ModelFileSuffix);
  828. resampleInputData.MdlZipFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ZipFileSuffix);
  829. resampleInputData.BaseAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  830. resampleInputData.PlaqueAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  831. resampleInputData.YShapeAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  832. var resampleResult = TryResample(imageData, resampleInputData);
  833. if (resampleResult.ResampleErrorCode == ResampleErrorCode.Success)
  834. {
  835. var carotidAIImageTokens = new List<CarotidAIImage>();
  836. if (resampleResult.CarotidAIMeasureResult.IsYImageSuccess)
  837. {
  838. var fileUrl = await UploadFileAsync(resampleInputData.YShapeAIImagePath, Path.GetFileName(resampleInputData.YShapeAIImagePath));
  839. if (!string.IsNullOrWhiteSpace(fileUrl))
  840. {
  841. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.YShape });
  842. }
  843. File.Delete(resampleInputData.YShapeAIImagePath);
  844. }
  845. if (resampleResult.CarotidAIMeasureResult.IntimaResult.IsSuccess)
  846. {
  847. var fileUrl = await UploadFileAsync(resampleInputData.BaseAIImagePath, Path.GetFileName(resampleInputData.BaseAIImagePath));
  848. if (!string.IsNullOrWhiteSpace(fileUrl))
  849. {
  850. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Base });
  851. }
  852. File.Delete(resampleInputData.BaseAIImagePath);
  853. }
  854. if (resampleResult.CarotidAIMeasureResult.PlaqueResult.IsSuccess && resampleResult.CarotidAIMeasureResult.PlaqueResult.PlaqueCountType != WingAIDiagnosisService.Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  855. {
  856. var fileUrl = await UploadFileAsync(resampleInputData.PlaqueAIImagePath, Path.GetFileName(resampleInputData.PlaqueAIImagePath));
  857. if (!string.IsNullOrWhiteSpace(fileUrl))
  858. {
  859. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Plaque });
  860. }
  861. File.Delete(resampleInputData.PlaqueAIImagePath);
  862. }
  863. var surfaceImageList = new List<string>();
  864. long surfaceFileSize = 0;
  865. var cdnSurfaceFile = "";
  866. if (!string.IsNullOrWhiteSpace(resampleInputData.SurfaceFilePath) && File.Exists(resampleInputData.SurfaceFilePath))
  867. {
  868. surfaceImageList = await SurfaceFile(resampleInputData.SurfaceFilePath);
  869. var surfaceFileTuple = await UploadFileTupleAsync(resampleInputData.SurfaceFilePath, Path.GetFileName(resampleInputData.SurfaceFilePath));
  870. surfaceFileSize = surfaceFileTuple.Item2;
  871. File.Delete(resampleInputData.SurfaceFilePath);
  872. resampleInputData.SurfaceFilePath = surfaceFileTuple.Item1;
  873. cdnSurfaceFile = surfaceFileTuple.Item1.ToUrlToken();//ToCDN
  874. }
  875. long mdlFileFileSize = 0;
  876. var cdnMdlFile = "";
  877. if (!string.IsNullOrWhiteSpace(resampleInputData.MdlFilePath) && File.Exists(resampleInputData.MdlFilePath))
  878. {
  879. var mdlFileTuple = await UploadFileTupleAsync(resampleInputData.MdlFilePath, Path.GetFileName(resampleInputData.MdlFilePath));
  880. mdlFileFileSize = mdlFileTuple.Item2;
  881. File.Delete(resampleInputData.MdlFilePath);
  882. resampleInputData.MdlFilePath = mdlFileTuple.Item1;
  883. cdnMdlFile = mdlFileTuple.Item1.ToUrlToken();//ToCDN
  884. }
  885. var result = new CarotidResultDTO
  886. {
  887. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  888. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  889. SurfaceFile = resampleInputData.SurfaceFilePath,
  890. SurfaceFileSize = surfaceFileSize,
  891. CDNSurfaceFile = cdnSurfaceFile,
  892. MdlFileSize = mdlFileFileSize,
  893. MdlFile = resampleInputData.MdlFilePath,
  894. CDNMdlFile = cdnMdlFile,
  895. MeasureImageFiles = carotidAIImageTokens.Select(x => new MeasureImageFileDTO { ImageType = (CarotidAIImageTypeEnum)x.AIImageType, ImageFile = x.AIImageToken }).ToList(),
  896. MeasureResult = JsonConvert.SerializeObject(resampleResult.CarotidAIMeasureResult),
  897. SurfaceImageList = surfaceImageList,
  898. };
  899. return result;
  900. }
  901. return null;
  902. }
  903. private ResampleResult TryResample(VinnoImageData vinnoImageData, ResampleInputData resampleInputData)
  904. {
  905. float scanDistance = 7;
  906. ResampleResult resampleResult = new ResampleResult(ResampleErrorCode.Fail);
  907. var vinnoExtendedData = VinnoCarotidExtendedData.FromBytes(vinnoImageData.ExtendedData);
  908. CarotidScanType scanType = CarotidScanType.CarotidLeft;
  909. CarotidScanDirection direction = CarotidScanDirection.TopToBottom;
  910. if (vinnoExtendedData != null)
  911. {
  912. // Should be 6~9cm normally.
  913. //scanDistance = vinnoExtendedData.ScanDistance;
  914. scanType = vinnoExtendedData.CarotidType == CarotidType.Left ? CarotidScanType.CarotidLeft : CarotidScanType.CarotidRight;
  915. direction = vinnoExtendedData.CarotidDirection == CarotidDirection.BottomToTop ? CarotidScanDirection.BottomToTop
  916. : CarotidScanDirection.TopToBottom;
  917. }
  918. else
  919. {
  920. //This is a walkaround : vid's first frame is black image or depth coordinate is not in the right place.
  921. var middleCount = vinnoImageData.ImageCount / 2;
  922. var vinnoImage = vinnoImageData.GetImage(middleCount);
  923. var imageBuffer = vinnoImage.ImageData;
  924. if (_recognizeCarotidTypeCanBeUse)
  925. {
  926. //转成彩色图像
  927. using var img = new Mat();
  928. CvInvoke.Imdecode(imageBuffer, ImreadModes.Color, img);
  929. using var image = img.ToImage<Bgr, byte>();
  930. //识别左右颈信息
  931. scanType = _recognizeCarotidType.RecognizeType(image);
  932. }
  933. }
  934. resampleInputData.ScanDistance = scanDistance;
  935. resampleInputData.ScanType = scanType;
  936. resampleInputData.CarotidScanDirection = direction;
  937. resampleResult = ResampleModel.Instance.Resample(vinnoImageData, resampleInputData, _workerLevel);
  938. return resampleResult;
  939. }
  940. /// <summary>
  941. ///
  942. /// </summary>
  943. /// <param name="filePath"></param>
  944. private async Task<List<string>> SurfaceFile(string filePath)
  945. {
  946. var fileUrls = new List<string>();
  947. using (var stream = new FileStream(filePath, FileMode.Open))
  948. {
  949. var reader = new Carotid3DStreamReader(stream);
  950. var width = reader.ReadInt();
  951. var height = reader.ReadInt();
  952. var depth = reader.ReadInt();
  953. var physicalData = VinnoCarotid3DPhysicalData.FromBytes(reader.ReadBytes());
  954. var imageCount = reader.ReadInt();
  955. var fileName = $"{Guid.NewGuid():N}";
  956. for (var i = 0; i < imageCount; i++)
  957. {
  958. var readBytes = reader.ReadBytes();
  959. if (readBytes.Any())
  960. {
  961. using var img = new Mat();
  962. CvInvoke.Imdecode(readBytes, ImreadModes.Color, img);
  963. var buf = new Emgu.CV.Util.VectorOfByte();
  964. using var image = img.ToImage<Gray, byte>();
  965. CvInvoke.Imencode(".jpg", image, buf, new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  966. var jpgByte = buf.ToArray();
  967. var localPath = Path.Combine(_tempFolder, $"{fileName}_{i + 1}.jpg");
  968. File.WriteAllBytes(localPath, jpgByte);
  969. var fileUrl = await UploadFileAsync(localPath, Path.GetFileName(localPath));
  970. fileUrls.Add(fileUrl);
  971. File.Delete(localPath);
  972. }
  973. }
  974. }
  975. return fileUrls;
  976. }
  977. private SKBitmap CreateBitmap(VinnoImage vinnoImage)
  978. {
  979. try
  980. {
  981. return SKBitmap.Decode(vinnoImage.ImageData);
  982. }
  983. catch (Exception ex)
  984. {
  985. Logger.WriteLineError($"Create skbitmap by VinnoImage error:{ex}");
  986. }
  987. return null;
  988. }
  989. private EnumColorType MapTo(SKColorType sKColor)
  990. {
  991. switch (sKColor)
  992. {
  993. case SKColorType.Rgba8888:
  994. return EnumColorType.Rgba;
  995. case SKColorType.Rgb888x:
  996. return EnumColorType.Rgba;
  997. case SKColorType.Bgra8888:
  998. return EnumColorType.Bgra;
  999. case SKColorType.Gray8:
  1000. return EnumColorType.Gray8;
  1001. }
  1002. throw new Exception($"AIService not support color type:{sKColor}");
  1003. }
  1004. private void AIdiagSystem_NotifyError(object sender, ErrorEventArgs e)
  1005. {
  1006. Logger.WriteLineError("AIdiagSystem_NotifyError:" + e.GetException());
  1007. }
  1008. private void AIdiagSystem_NotifyLog(object sender, LogEventArgs e)
  1009. {
  1010. if (e != null && !string.IsNullOrEmpty(e.Msg))
  1011. {
  1012. switch (e.LogType)
  1013. {
  1014. case EnumLogType.InfoLog:
  1015. Logger.WriteLineInfo($"AIdiagSystem_NotifyLog:{e.Msg}");
  1016. break;
  1017. case EnumLogType.ErrorLog:
  1018. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  1019. break;
  1020. case EnumLogType.WarnLog:
  1021. Logger.WriteLineWarn($"AIdiagSystem_NotifyLog:{e.Msg}");
  1022. break;
  1023. case EnumLogType.FatalLog:
  1024. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  1025. break;
  1026. default:
  1027. Logger.WriteLineInfo(e.Msg);
  1028. break;
  1029. }
  1030. }
  1031. }
  1032. private DiagnosisConclusion GetDiagnosisConclusion<T>(List<T> results) where T : AIDiagnosisPerImageModel
  1033. {
  1034. var diagnosisConclusions = new List<DiagnosisConclusion>();
  1035. foreach (var imageResult in results)
  1036. {
  1037. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1038. {
  1039. var benignLabels = new List<int>();
  1040. var malignantLabels = new List<int>();
  1041. if (diagnosisResult.Organ == DiagnosisOrganEnum.Breast)
  1042. {
  1043. benignLabels = new List<int> { 1, 2, 3 };
  1044. malignantLabels = new List<int> { 4, 5, 6, 7 };
  1045. }
  1046. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Liver)
  1047. {
  1048. benignLabels = new List<int> { 1, 2, 3, 5, 6, 7, 8 };
  1049. malignantLabels = new List<int> { 4 };
  1050. }
  1051. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Thyroid)
  1052. {
  1053. benignLabels = new List<int> { 1, 2, 7 };
  1054. malignantLabels = new List<int> { 3, 4, 5, 6 };
  1055. }
  1056. var labels = diagnosisResult.DetectedObjects.Select(x => x.Label);
  1057. if (labels.Contains(0))
  1058. {
  1059. diagnosisConclusions.Add(DiagnosisConclusion.NoObviousLesion);
  1060. }
  1061. if (labels.Intersect(benignLabels).Any())
  1062. {
  1063. diagnosisConclusions.Add(DiagnosisConclusion.Benign);
  1064. }
  1065. if (labels.Intersect(malignantLabels).Any())
  1066. {
  1067. diagnosisConclusions.Add(DiagnosisConclusion.Malignant);
  1068. }
  1069. }
  1070. }
  1071. var containsBenign = diagnosisConclusions.Contains(DiagnosisConclusion.Benign);
  1072. var containsMalignant = diagnosisConclusions.Contains(DiagnosisConclusion.Malignant);
  1073. var containsNoObviousLesion = diagnosisConclusions.Contains(DiagnosisConclusion.NoObviousLesion);
  1074. if (containsBenign && containsMalignant)
  1075. {
  1076. return DiagnosisConclusion.BenignAndMalignant;
  1077. }
  1078. else if (containsBenign)
  1079. {
  1080. return DiagnosisConclusion.Benign;
  1081. }
  1082. else if (containsMalignant)
  1083. {
  1084. return DiagnosisConclusion.Malignant;
  1085. }
  1086. else if (containsNoObviousLesion)
  1087. {
  1088. return DiagnosisConclusion.NoObviousLesion;
  1089. }
  1090. else
  1091. {
  1092. return DiagnosisConclusion.Unrecognized;
  1093. }
  1094. }
  1095. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<AIDiagnosisPerImageModel> results)
  1096. {
  1097. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  1098. foreach (var imageResult in results)
  1099. {
  1100. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1101. {
  1102. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  1103. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null && diagnosisResult.DetectedObjects?.Any() == true)
  1104. {
  1105. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  1106. }
  1107. }
  1108. }
  1109. return diagnosisOrgans.Distinct().ToList();
  1110. }
  1111. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<DiagnosisPerImageModel> results)
  1112. {
  1113. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  1114. foreach (var imageResult in results)
  1115. {
  1116. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1117. {
  1118. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  1119. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null && diagnosisResult.DetectedObjects?.Any() == true)
  1120. {
  1121. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  1122. }
  1123. }
  1124. }
  1125. return diagnosisOrgans.Distinct().ToList();
  1126. }
  1127. private Dictionary<int, AIDiagResultPerImg> NormalDiagnosis(string relationCode, string fileUrl, VinnoImageData imageData)
  1128. {
  1129. var results = new Dictionary<int, AIDiagResultPerImg>();
  1130. try
  1131. {
  1132. var imageCount = imageData.ImageCount;
  1133. Logger.WriteLineInfo($"AIDiagnosisService diagnosis start, relationCode:{relationCode}, fileUrl:{fileUrl}, imageCount:{imageCount}");
  1134. if (imageCount > 0)
  1135. {
  1136. var diagId = _diagSystem.StartEvalutationOfMultipleImageBatches(imageCount);
  1137. for (var i = 0; i < imageCount; i++)
  1138. {
  1139. var image = imageData.GetImage(i);
  1140. using (var bitmap = CreateBitmap(image))
  1141. {
  1142. var rawImage = new RawImage(bitmap.Bytes, bitmap.Width, bitmap.Height, MapTo(bitmap.ColorType));
  1143. _diagSystem.PushOneBatchOfImagesAsync(diagId, new List<RawImage> { rawImage });
  1144. }
  1145. }
  1146. results = _diagSystem.GetEvaluationsOfPushedMultipleImageBatches(diagId);
  1147. Logger.WriteLineInfo($"AIDiagnosisService diagnosis successfully");
  1148. }
  1149. }
  1150. catch (Exception ex)
  1151. {
  1152. Logger.WriteLineWarn($"AIDiagnosisService NormalDiagnosis err, {ex}");
  1153. }
  1154. return results;
  1155. }
  1156. /// <summary>
  1157. /// 保存AI诊断结果
  1158. /// </summary>
  1159. /// <param name="relationCode"></param>
  1160. /// <param name="fileUrl"></param>
  1161. /// <param name="diagnosisResultInfos"></param>
  1162. /// <returns></returns>
  1163. private async Task<bool> AddDiagnosisResultInfosAsync(string relationCode, string fileUrl, List<AIDiagnosisPerImageModel> diagnosisResultInfos
  1164. , DateTime? createTime = null, DateTime? updateTime = null, bool syncOthers = true)
  1165. {
  1166. try
  1167. {
  1168. var resultInfos = new List<DiagnosisResultDTO>();
  1169. for (var i = 0; i < diagnosisResultInfos.Count; i++)
  1170. {
  1171. resultInfos.Add(new DiagnosisResultDTO
  1172. {
  1173. Index = i,
  1174. DiagnosisResult = Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisResultInfos[i]),
  1175. });
  1176. }
  1177. var addRequest = new AddDiagnosisResultInfosDBRequest
  1178. {
  1179. RelationCode = relationCode,
  1180. FileUrl = fileUrl,
  1181. DiagnosisResultInfos = resultInfos,
  1182. CreateTime = createTime,
  1183. UpdateTime = updateTime,
  1184. SyncOthers = syncOthers,
  1185. };
  1186. if (syncOthers)
  1187. {
  1188. _diagnosisResultDBService.AddDiagnosisResultInfosAsync(addRequest);
  1189. }
  1190. else
  1191. {
  1192. await _diagnosisResultDBService.AddDiagnosisResultInfosAsync(addRequest);
  1193. }
  1194. return true;
  1195. }
  1196. catch (Exception ex)
  1197. {
  1198. Logger.WriteLineWarn($"AIDiagnosisService AddDiagnosisResultInfosAsync err, ex:{ex}");
  1199. return false;
  1200. }
  1201. }
  1202. /// <summary>下载文件</summary>
  1203. /// <param name="fileUrl"></param>
  1204. /// <returns></returns>
  1205. private async Task<string> DownloadAsync(string fileUrl)
  1206. {
  1207. try
  1208. {
  1209. if (string.IsNullOrEmpty(fileUrl))
  1210. {
  1211. return string.Empty;
  1212. }
  1213. if (!Directory.Exists(_tempFolder))
  1214. {
  1215. Directory.CreateDirectory(_tempFolder);
  1216. }
  1217. var fileName = Path.GetFileName(fileUrl);
  1218. var tempFile = Path.Combine(_tempFolder, fileName);
  1219. if (File.Exists(tempFile))
  1220. {
  1221. return tempFile;
  1222. }
  1223. long fileSize = 0;
  1224. using (var request = new HttpRequestMessage())
  1225. {
  1226. request.RequestUri = new Uri(fileUrl);
  1227. request.Method = HttpMethod.Get;
  1228. var response = await _httpClient.SendAsync(request);
  1229. if (response != null && response.StatusCode == HttpStatusCode.OK)
  1230. {
  1231. var contentLength = response.Content.Headers.ContentLength;
  1232. fileSize = contentLength == null ? 0 : contentLength.Value;
  1233. }
  1234. }
  1235. if (fileSize <= 0)
  1236. {
  1237. throw new NotSupportedException($"fileSize is {fileSize}");
  1238. }
  1239. byte[] bytes = await _httpClient.GetByteArrayAsync(fileUrl);
  1240. File.WriteAllBytes(tempFile, bytes);
  1241. return tempFile;
  1242. }
  1243. catch (Exception ex)
  1244. {
  1245. Logger.WriteLineWarn($"DiagnosisService download file err, url: {fileUrl}, {ex}");
  1246. }
  1247. finally
  1248. {
  1249. //Logger.WriteLineInfo($"download file:{fileUrl}");
  1250. }
  1251. return string.Empty;
  1252. }
  1253. /// <summary>
  1254. /// 上传文件
  1255. /// </summary>
  1256. /// <param name="filePath"></param>
  1257. /// <param name="fileName"></param>
  1258. /// <returns></returns>
  1259. private async Task<string> UploadFileAsync(string filePath, string fileName)
  1260. {
  1261. var fileToken = "";
  1262. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1263. {
  1264. var size = fileStream.Length;
  1265. byte[] buffer = new byte[fileStream.Length];
  1266. fileStream.Read(buffer, 0, buffer.Length);
  1267. fileToken = await DoUploadFile(fileName, buffer);
  1268. }
  1269. return fileToken;
  1270. }
  1271. /// <summary>
  1272. /// 上传文件
  1273. /// </summary>
  1274. /// <param name="filePath"></param>
  1275. /// <param name="fileName"></param>
  1276. /// <returns></returns>
  1277. private async Task<Tuple<string, long>> UploadFileTupleAsync(string filePath, string fileName)
  1278. {
  1279. var fileToken = "";
  1280. long size = 0;
  1281. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1282. {
  1283. size = fileStream.Length;
  1284. byte[] buffer = new byte[fileStream.Length];
  1285. fileStream.Read(buffer, 0, buffer.Length);
  1286. fileToken = await DoUploadFile(fileName, buffer);
  1287. }
  1288. return new Tuple<string, long>(fileToken, size);
  1289. }
  1290. /// <summary>
  1291. /// 上传文件
  1292. /// </summary>
  1293. /// <param name="fileName"></param>
  1294. /// <param name="fileData"></param>
  1295. /// <returns></returns>
  1296. async Task<string> DoUploadFile(string fileName, byte[] fileData)
  1297. {
  1298. var requestHeads = new Dictionary<string, string>();
  1299. var defaultToken = await _authenticationService.GetServerDefaultTokenAsync();
  1300. var authorizationRet = await _storageService.GetAuthorizationAsync(
  1301. new FileServiceRequest
  1302. {
  1303. Token = defaultToken,
  1304. FileName = fileName,
  1305. });
  1306. requestHeads.Add("Authorization", authorizationRet.Authorization);
  1307. var fileUrl = authorizationRet.StorageUrl;
  1308. Logger.WriteLineInfo($"DoUploadFile fileUrl:{fileUrl}");
  1309. using (var request = new HttpRequestMessage())
  1310. {
  1311. var fileExtension = Path.GetExtension(fileName);
  1312. var mimeType = FileHelper.GetMimeType(fileExtension);
  1313. var contentType = MediaTypeHeaderValue.Parse(mimeType);
  1314. using (UploadContent content = new UploadContent(fileData, contentType))
  1315. {
  1316. request.RequestUri = new Uri(fileUrl);
  1317. request.Method = HttpMethod.Put;
  1318. request.Content = content;
  1319. foreach (var head in requestHeads)
  1320. {
  1321. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  1322. }
  1323. var result = await ExecuteRequest(request);
  1324. if (!result)
  1325. {
  1326. throw new Exception("Upload file failed!");
  1327. }
  1328. }
  1329. }
  1330. return fileUrl;
  1331. }
  1332. private UploadFileTypeEnum GetUploadFileType(string fileName)
  1333. {
  1334. if (fileName.EndsWith(".vid"))
  1335. {
  1336. return UploadFileTypeEnum.VID;
  1337. }
  1338. else if (fileName.EndsWith(".jpg"))
  1339. {
  1340. return UploadFileTypeEnum.JPG;
  1341. }
  1342. else
  1343. {
  1344. return UploadFileTypeEnum.MP4;
  1345. }
  1346. }
  1347. /// <summary>
  1348. /// 执行请求
  1349. /// </summary>
  1350. /// <param name="httpRequestMessage"></param>
  1351. /// <typeparam name="T"></typeparam>
  1352. /// <returns></returns>
  1353. public async Task<bool> ExecuteRequest(HttpRequestMessage httpRequestMessage)
  1354. {
  1355. try
  1356. {
  1357. var response = await _httpClient.SendAsync(httpRequestMessage);
  1358. if (response != null && response.StatusCode == HttpStatusCode.OK)
  1359. {
  1360. return true;
  1361. }
  1362. return false;
  1363. }
  1364. catch (Exception ex)
  1365. {
  1366. throw ex;
  1367. }
  1368. }
  1369. private List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D> PointToDto(Point2D[] points)
  1370. {
  1371. var dstContours = new List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D>();
  1372. foreach (var p in points)
  1373. {
  1374. dstContours.Add(new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1375. {
  1376. X = p.X,
  1377. Y = p.Y
  1378. });
  1379. }
  1380. return dstContours;
  1381. }
  1382. private List<DiagnosisKeyPointDTO> KeyPointToDto(KeyPointInfo[] points)
  1383. {
  1384. var dstKeys = new List<DiagnosisKeyPointDTO>();
  1385. foreach (var point in points)
  1386. {
  1387. dstKeys.Add(new DiagnosisKeyPointDTO
  1388. {
  1389. Type = (DiagnosisKeyPointType)point.Type,
  1390. IndexInContour = point.IndexInContour,
  1391. Point = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1392. {
  1393. X = point.Point.X,
  1394. Y = point.Point.Y,
  1395. },
  1396. });
  1397. }
  1398. return dstKeys;
  1399. }
  1400. /// <summary>
  1401. /// 创建缩略图文件
  1402. /// </summary>
  1403. /// <param name="vinnoImage"></param>
  1404. /// <param name="requestHeight"></param>
  1405. /// <param name="quality"></param>
  1406. /// <param name="fullPath"></param>
  1407. private void CreateThumbnailFile(string filePath, int requestHeight, int quality, string fullPath)
  1408. {
  1409. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1410. {
  1411. var size = fileStream.Length;
  1412. byte[] buffer = new byte[fileStream.Length];
  1413. fileStream.Read(buffer, 0, buffer.Length);
  1414. byte[] compressedData = buffer;
  1415. using (var ms = new MemoryStream(compressedData))
  1416. {
  1417. using (var image = SKBitmap.Decode(ms))
  1418. {
  1419. var ratio = (double)requestHeight / image.Height;
  1420. if (ratio < 1)
  1421. {
  1422. var width = (int)(image.Width * ratio);
  1423. var height = (int)(image.Height * ratio);
  1424. SKBitmap tempImage = image;
  1425. if (image.Width != width || image.Height != height)
  1426. {
  1427. tempImage = image.Resize(new SKImageInfo(width, height), SKFilterQuality.High);
  1428. }
  1429. try
  1430. {
  1431. using (var map = new SKPixmap(new SKImageInfo(tempImage.Width, tempImage.Height, tempImage.ColorType), tempImage.GetPixels()))
  1432. {
  1433. using (var stream = new SKDynamicMemoryWStream())
  1434. {
  1435. SKPixmap.Encode(stream, map, SKEncodedImageFormat.Jpeg, quality);
  1436. compressedData = stream.CopyToData().ToArray();
  1437. }
  1438. }
  1439. }
  1440. catch (Exception ex)
  1441. {
  1442. throw ex;
  1443. }
  1444. finally
  1445. {
  1446. tempImage.Dispose();
  1447. }
  1448. }
  1449. }
  1450. }
  1451. using (var fs = File.Create(fullPath))
  1452. {
  1453. fs.Write(compressedData, 0, compressedData.Length);
  1454. }
  1455. }
  1456. }
  1457. }
  1458. }