AIDiagnosisService.cs 110 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147
  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. using System.Runtime.CompilerServices;
  43. using WingInterfaceLibrary.DTO.Common;
  44. using WingInterfaceLibrary.Request.User;
  45. using WingInterfaceLibrary.Internal.Interface;
  46. namespace WingAIDiagnosisService.Service
  47. {
  48. /// <summary>
  49. /// AI诊断服务
  50. /// </summary>
  51. public partial class AIDiagnosisService : JsonRpcService, IAIDiagnosisService
  52. {
  53. private EnumPerformance _workerLevel = EnumPerformance.Medium;
  54. private int _batchImageSize = 16;
  55. private int _sleepTime = 400;
  56. private readonly string _tempFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DiagnosisTemp");
  57. private AIDiagSystem _diagSystem;
  58. private AILesionCompareUtils _lesionCompareUtil;
  59. private RecognizeCarotidType _recognizeCarotidType;
  60. //识别左右颈是否初始化成功
  61. private static bool _recognizeCarotidTypeCanBeUse = false;
  62. static HttpClient _httpClient = new HttpClient();
  63. private IAuthenticationService _authenticationService;
  64. private IStorageService _storageService;
  65. private readonly string _carotidName = "CarotidPlaqueDetect";
  66. private int _contourInterval = 10;
  67. private IDiagnosisResultDBService _diagnosisResultDBService;
  68. private IRemedicalDBService _remedicalDBService;
  69. private bool _isCarotidOn = true;
  70. private string _storageDirectory;
  71. private INotificationService _notificationService;
  72. /// <summary>
  73. /// Init service
  74. /// </summary>
  75. public override void Load(JsonRpcClientPool jsonRpcClientPool)
  76. {
  77. base.Load(jsonRpcClientPool);
  78. _authenticationService = GetProxy<IAuthenticationService>();
  79. _storageService = GetProxy<IStorageService>();
  80. _workerLevel = (EnumPerformance)ConfigurationManager.GetParammeter<IntParameter>("AI", "WorkerLevel").Value;
  81. _batchImageSize = ConfigurationManager.GetParammeter<IntParameter>("AI", "BatchImageSize").Value;
  82. _contourInterval = ConfigurationManager.GetParammeter<IntParameter>("AI", "ContourInterval").Value;
  83. _httpClient.Timeout = TimeSpan.FromMinutes(15);
  84. _diagnosisResultDBService = GetProxy<IDiagnosisResultDBService>();
  85. _remedicalDBService = GetProxy<IRemedicalDBService>();
  86. _notificationService = GetProxy<INotificationService>();
  87. InitAISystem();
  88. //初始化识别左右颈
  89. _isCarotidOn = ConfigurationManager.GetParammeter<BoolParameter>("AI", "IsCarotidOn").Value;
  90. if (_isCarotidOn)
  91. {
  92. _recognizeCarotidType = new RecognizeCarotidType();
  93. _recognizeCarotidTypeCanBeUse = _recognizeCarotidType.Initialization();
  94. }
  95. var storageDisk = EnvironmentConfigManager.GetParammeter<StringParameter>("Storage", "VCSStorageDisk").Value;
  96. _storageDirectory = Path.Combine(storageDisk, "FlyinsonoStorage");
  97. var keyPointDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "KeyPointDistanceThreshold").Value;
  98. var dragStartPointCatchDistanceThreshold = ConfigurationManager.GetParammeter<IntParameter>("AI", "DragStartPointCatchDistanceThreshold").Value;
  99. ContourModifyHelper.KeyPointDistanceThreshold = keyPointDistanceThreshold;
  100. ContourModifyHelper.DragStartPointCatchDistanceThreshold = dragStartPointCatchDistanceThreshold;
  101. }
  102. private void InitAISystem()
  103. {
  104. var modeFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AIDiagnosis", "Networks");
  105. _diagSystem = new AIDiagSystem(_workerLevel, modeFilePath, EnumInferWorkName.Default, false, true, true);
  106. if (_diagSystem != null)
  107. {
  108. _diagSystem.NotifyError += AIdiagSystem_NotifyError;
  109. _diagSystem.NotifyLog += AIdiagSystem_NotifyLog;
  110. }
  111. _lesionCompareUtil = new AILesionCompareUtils();
  112. }
  113. /// <summary>
  114. /// 查询杏聆荟支持的AI模块
  115. /// </summary>
  116. /// <param name="request">查询杏聆荟支持的AI模块请求实体</param>
  117. /// <returns></returns>
  118. /// <show>false</show>
  119. public async Task<IList<string>> FindDiagnosisModulesAsync(TokenRequest request)
  120. {
  121. try
  122. {
  123. var moduleNames = AIDiagSystem.GetValidModuleNamesForVclound() ?? new List<EnumAIModuleNames>();
  124. var resultData = moduleNames.Select(x => x.ToString()).ToList();
  125. if (!resultData.Contains(_carotidName))
  126. {
  127. resultData.Remove("CarotidArtery");
  128. resultData.Add(_carotidName);
  129. }
  130. return await Task.FromResult(resultData.Distinct().ToList());
  131. }
  132. catch (Exception)
  133. {
  134. return new List<string>();
  135. }
  136. }
  137. /// <summary>
  138. /// 图像诊断
  139. /// </summary>
  140. /// <param name="request">图像诊断请求实体</param>
  141. /// <returns>图像诊断结果</returns>
  142. /// <show>false</show>
  143. public async Task<DiagnosisImageResult> DiagnosisImageAsync(DiagnosisImageRequest request)
  144. {
  145. try
  146. {
  147. var relationCode = request.RelationCode;
  148. var fileUrl = request.FileToken;
  149. var localFile = "";
  150. if (!string.IsNullOrWhiteSpace(request.DiskPath))
  151. {
  152. localFile = request.DiskPath;
  153. }
  154. else
  155. {
  156. localFile = await DownloadAsync(fileUrl);
  157. }
  158. if (File.Exists(localFile))
  159. {
  160. if (_diagSystem == null)
  161. {
  162. InitAISystem();
  163. }
  164. var resultData = new DiagnosisImageResult();
  165. var isCaroid = false;
  166. var diagnosisPerImages = new List<AIDiagnosisPerImageModel>();
  167. var results = new Dictionary<int, AIDiagResultPerImg>();
  168. var organs = new List<DiagnosisOrganEnum>();
  169. using (var imageData = new VinnoImageData(localFile, OperationMode.Open))
  170. {
  171. isCaroid = DiagnosisHelper.IsCarotid(imageData);
  172. if (isCaroid)
  173. {
  174. if (_isCarotidOn)
  175. {
  176. //颈动脉
  177. var result = await CarotidDiagnosis(imageData);
  178. resultData = new DiagnosisImageResult()
  179. {
  180. DiagnosisOrgans = new List<DiagnosisOrganEnum>() { DiagnosisOrganEnum.CarotidArtery },
  181. CarotidResult = result
  182. };
  183. }
  184. }
  185. //乳腺、肝脏
  186. results = NormalDiagnosis(relationCode, fileUrl, imageData);
  187. //AI实体有很多参数仅支持get,为了方便后续反序列化,db入库前先做个转换
  188. diagnosisPerImages = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value, 1)).ToList();
  189. diagnosisPerImages = GetAIDiagnosisPerImages(diagnosisPerImages, imageData, isCaroid);
  190. if (diagnosisPerImages == null || !diagnosisPerImages.Any())
  191. {
  192. return new DiagnosisImageResult
  193. {
  194. DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized,
  195. DiagnosisResult = null,
  196. DiagnosisOrgans = new List<DiagnosisOrganEnum>(),
  197. };
  198. }
  199. organs = GetDiagnosisOrgans(diagnosisPerImages);
  200. await AddDiagnosisResultInfosAsync(relationCode, fileUrl, diagnosisPerImages);
  201. //轮廓线分隔处理
  202. diagnosisPerImages = results.Select(x => new AIDiagnosisPerImageModel(x.Key, x.Value, _contourInterval)).ToList();
  203. diagnosisPerImages = GetAIDiagnosisPerImages(diagnosisPerImages, imageData, isCaroid);
  204. }
  205. var diagnosisResult = JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(JsonConvert.SerializeObject(diagnosisPerImages));
  206. resultData.DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisPerImages);
  207. resultData.DiagnosisResult = diagnosisResult;
  208. resultData.DiagnosisOrgans.AddRange(organs);
  209. return resultData;
  210. }
  211. }
  212. catch (Exception ex)
  213. {
  214. Logger.WriteLineWarn($"DiagnosisImageAsync err {ex}");
  215. }
  216. return new DiagnosisImageResult();
  217. }
  218. private List<AIDiagnosisPerImageModel> GetAIDiagnosisPerImages(List<AIDiagnosisPerImageModel> diagnosisPerImages, VinnoImageData imageData, bool isCaroid)
  219. {
  220. if (isCaroid && diagnosisPerImages.Count == 1)
  221. {
  222. foreach (var perImage in diagnosisPerImages)
  223. {
  224. if (perImage.DiagResultsForEachOrgan.All(x => x.Organ != DiagnosisOrganEnum.CarotidArtery))
  225. {
  226. var orgs = new List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>();
  227. foreach (var org in perImage.DiagResultsForEachOrgan)
  228. {
  229. org.Organ = DiagnosisOrganEnum.CarotidArtery;
  230. foreach (var detectedObject in org.DetectedObjects)
  231. {
  232. detectedObject.Label = 0;
  233. detectedObject.Contour = new List<Manage.AIDiagnosisPoint2D>();
  234. detectedObject.Contours = new List<Manage.AIDiagnosisPoint2D>();
  235. detectedObject.Descriptions = new List<Manage.AIDiagnosisDescription>();
  236. }
  237. orgs.Add(org);
  238. }
  239. perImage.DiagResultsForEachOrgan = orgs;
  240. }
  241. }
  242. }
  243. foreach (var perImage in diagnosisPerImages)
  244. {
  245. var orgs = new List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>();
  246. foreach (var org in perImage.DiagResultsForEachOrgan)
  247. {
  248. if (org.Organ == DiagnosisOrganEnum.CarotidArtery)
  249. {
  250. var physicalPerPixel = BaseDiagnosis.GetPhysicalPerPixel(imageData, perImage.Index, true);
  251. var info = GetReportDetectedObject(perImage.DiagResultsForEachOrgan, physicalPerPixel * 10);
  252. if (info.Item4 < 1.4)
  253. {
  254. foreach (var a in org.DetectedObjects)
  255. {
  256. a.Label = 0;
  257. a.Contour = new List<Manage.AIDiagnosisPoint2D>();
  258. a.Contours = new List<Manage.AIDiagnosisPoint2D>();
  259. a.Descriptions = new List<Manage.AIDiagnosisDescription>();
  260. }
  261. }
  262. orgs.Add(org);
  263. }
  264. else
  265. {
  266. orgs.Add(org);
  267. }
  268. }
  269. perImage.DiagResultsForEachOrgan = orgs;
  270. }
  271. var organs = GetDiagnosisOrgans(diagnosisPerImages);
  272. if (organs.Contains(DiagnosisOrganEnum.Breast))
  273. {
  274. var breastCount = diagnosisPerImages.Where(a => a.PriorityScore > 0 && a.DiagResultsForEachOrgan != null && a.DiagResultsForEachOrgan.Any(x => x.Organ == DiagnosisOrganEnum.Breast)).Count();
  275. var totalCount = diagnosisPerImages.Count;
  276. var breastRate = breastCount * 1.0 / totalCount * 1.0;
  277. if (organs.All(x => x == DiagnosisOrganEnum.Breast))
  278. {
  279. if (breastRate <= 1 / 3.0)
  280. {
  281. return new List<AIDiagnosisPerImageModel>();
  282. }
  283. }
  284. else
  285. {
  286. if (breastRate <= 1 / 3.0)
  287. {
  288. foreach (var item in diagnosisPerImages)
  289. {
  290. item.DiagResultsForEachOrgan = item.DiagResultsForEachOrgan?.Where(x => x.Organ != DiagnosisOrganEnum.Breast)?.ToList() ?? new List<Manage.AIDiagnosisResultPerOrgan>();
  291. }
  292. }
  293. }
  294. }
  295. if (isCaroid)
  296. {
  297. var carotidCount = diagnosisPerImages.Count(x => x.DiagResultsForEachOrgan.Any(x => x.Organ == DiagnosisOrganEnum.CarotidArtery));
  298. var throidCount = diagnosisPerImages.Count(x => x.DiagResultsForEachOrgan.Any(x => x.Organ == DiagnosisOrganEnum.Thyroid));
  299. if (carotidCount > throidCount)
  300. {
  301. foreach (var item in diagnosisPerImages)
  302. {
  303. item.DiagResultsForEachOrgan = item.DiagResultsForEachOrgan?.Where(x => x.Organ == DiagnosisOrganEnum.CarotidArtery)?.ToList() ?? new List<Manage.AIDiagnosisResultPerOrgan>();
  304. }
  305. }
  306. else
  307. {
  308. foreach (var item in diagnosisPerImages)
  309. {
  310. item.DiagResultsForEachOrgan = item.DiagResultsForEachOrgan?.Where(x => x.Organ == DiagnosisOrganEnum.Thyroid)?.ToList() ?? new List<Manage.AIDiagnosisResultPerOrgan>();
  311. }
  312. }
  313. }
  314. return diagnosisPerImages;
  315. }
  316. private class TempAIResult
  317. {
  318. public int Index { get; set; }
  319. public AIDiagnosisPerImageModel ImageDiagResult { get; set; }
  320. }
  321. private class Description : AI.Common.IDescription
  322. {
  323. public EnumDescriptionType Type { get; set; }
  324. // 其他必要的实现,根据您的接口定义
  325. }
  326. private class DescriptionConverter : JsonConverter
  327. {
  328. public override bool CanConvert(Type objectType)
  329. {
  330. return (objectType == typeof(AI.Common.IDescription));
  331. }
  332. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  333. {
  334. throw new NotImplementedException();
  335. }
  336. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  337. {
  338. var jsonObject = JObject.Load(reader);
  339. var description = new Description();
  340. serializer.Populate(jsonObject.CreateReader(), description);
  341. return description;
  342. }
  343. }
  344. public async Task<DiagnosisImageResult> GetDiagnosisImageResultByAIResultAsync(GetDiagnosisImageResultByAIResultRequest request)
  345. {
  346. try
  347. {
  348. var aiResult = request.AiResult;
  349. var relationCode = request.RelationCode;
  350. var fileUrl = request.FileUrl;
  351. var createTime = request.CreateTime;
  352. var upateTime = request.UpdateTime;
  353. //乳腺、肝脏
  354. // var settings = new JsonSerializerSettings
  355. // {
  356. // Converters = new List<JsonConverter> { new DescriptionConverter() }
  357. // };
  358. var aiDiagResultPerImgList = JsonConvert.DeserializeObject<List<TempAIResult>>(aiResult);
  359. foreach (var item in aiDiagResultPerImgList)
  360. {
  361. item.ImageDiagResult.Index = item.Index;
  362. }
  363. var diagnosisPerImages = aiDiagResultPerImgList.Select(x => x.ImageDiagResult)?.ToList() ?? new List<AIDiagnosisPerImageModel>();
  364. foreach (var perImage in diagnosisPerImages)
  365. {
  366. foreach (var perOrgan in perImage.DiagResultsForEachOrgan)
  367. {
  368. var detectedObjects = new List<WingAIDiagnosisService.Manage.AIDetectedObject>();
  369. foreach (var perDetectedObject in perOrgan.DetectedObjects)
  370. {
  371. if (perDetectedObject.Descriptions != null && perDetectedObject.Descriptions.Any())
  372. {
  373. foreach (var item in perDetectedObject.Descriptions)
  374. {
  375. item.Type = item.DescriptionType;
  376. item.Value = item.DescriptionValue;
  377. }
  378. }
  379. detectedObjects.Add(perDetectedObject);
  380. }
  381. perOrgan.DetectedObjects = detectedObjects;
  382. }
  383. }
  384. var organs = GetDiagnosisOrgans(diagnosisPerImages);
  385. await AddDiagnosisResultInfosAsync(relationCode, fileUrl, diagnosisPerImages, createTime, upateTime, false);
  386. //轮廓线分隔处理
  387. foreach (var perImage in diagnosisPerImages)
  388. {
  389. foreach (var perOrgan in perImage.DiagResultsForEachOrgan)
  390. {
  391. var organContours = new List<WingAIDiagnosisService.Manage.AIDiagnosisPoint2D>();
  392. if (perOrgan.OrganContour != null && perOrgan.OrganContour.Count > 0)
  393. {
  394. var coutours = perOrgan.OrganContour;
  395. if (coutours != null && coutours.Count > 0)
  396. {
  397. for (var i = 0; i < coutours.Count; i++)
  398. {
  399. if (i % _contourInterval == 0)
  400. {
  401. organContours.Add(coutours[i]);
  402. }
  403. }
  404. }
  405. }
  406. perOrgan.OrganContours = organContours;
  407. perOrgan.OrganContour = new List<Manage.AIDiagnosisPoint2D>();
  408. var detectedObjects = new List<WingAIDiagnosisService.Manage.AIDetectedObject>();
  409. foreach (var perDetectedObject in perOrgan.DetectedObjects)
  410. {
  411. var contours = new List<WingAIDiagnosisService.Manage.AIDiagnosisPoint2D>();
  412. if (perDetectedObject.Contour != null && perDetectedObject.Contour.Count > 0)
  413. {
  414. var coutours = perDetectedObject.Contour;
  415. if (coutours != null && coutours.Count > 0)
  416. {
  417. for (var i = 0; i < coutours.Count; i++)
  418. {
  419. if (i % _contourInterval == 0)
  420. {
  421. contours.Add(coutours[i]);
  422. }
  423. }
  424. }
  425. }
  426. perDetectedObject.Contours = contours;
  427. perDetectedObject.Contour = new List<Manage.AIDiagnosisPoint2D>();
  428. detectedObjects.Add(perDetectedObject);
  429. }
  430. perOrgan.DetectedObjects = detectedObjects;
  431. }
  432. }
  433. var diagnosisResult = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AIDiagnosisPerImageDTO>>(Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisPerImages));
  434. var resultData = new DiagnosisImageResult()
  435. {
  436. DiagnosisConclusion = (DiagnosisConclusionEnum)GetDiagnosisConclusion(diagnosisPerImages),
  437. DiagnosisResult = diagnosisResult,
  438. DiagnosisOrgans = organs,
  439. };
  440. return resultData;
  441. }
  442. catch (Exception ex)
  443. {
  444. Logger.WriteLineWarn("GetDiagnosisImageResultByAIResultAsync" + ex);
  445. return null;
  446. }
  447. }
  448. public async Task<CarotidResultDTO> GetCarotidResultAsync(GetCarotidResultRequest request)
  449. {
  450. try
  451. {
  452. var surfaceToken = request.SurfaceToken;
  453. var mdlToken = request.MdlToken;
  454. CarotidScanTypeEnum carotidScanType = request.CarotidScanType;
  455. CarotidScanDirectionEnum carotidScanDirection = request.CarotidScanDirection;
  456. var measureImageFiles = request.MeasureImageFiles;
  457. var measureResult = request.MeasureResult;
  458. var surfaceImageList = new List<string>();
  459. if (MatchCurrentStorage(surfaceToken))
  460. {
  461. surfaceImageList = await SurfaceFileByUrl(surfaceToken);
  462. }
  463. var result = new CarotidResultDTO
  464. {
  465. CarotidScanType = carotidScanType,
  466. CarotidScanDirection = carotidScanDirection,
  467. SurfaceFile = surfaceToken,
  468. SurfaceFileSize = 0,
  469. CDNSurfaceFile = surfaceToken,//源站地址
  470. MdlFileSize = 0,
  471. MdlFile = mdlToken,//源站地址
  472. CDNMdlFile = mdlToken,
  473. MeasureImageFiles = measureImageFiles,
  474. MeasureResult = measureResult,
  475. SurfaceImageList = surfaceImageList,
  476. };
  477. return result;
  478. }
  479. catch (Exception ex)
  480. {
  481. Logger.WriteLineWarn("GetCarotidResultAsync" + ex);
  482. return null;
  483. }
  484. }
  485. /// <summary>
  486. /// 根据颈动脉体数据生成3D 6面图像集合
  487. /// </summary>
  488. /// <param name="request"></param>
  489. /// <returns></returns>
  490. /// <show>false</show>
  491. public async Task<CreateCarotidSurfaceImagesResult> CreateCarotidSurfaceImagesAsync(CreateCarotidSurfaceImagesRequest request)
  492. {
  493. var surfaceImages = new List<string>();
  494. try
  495. {
  496. var surfaceFileUrl = request.SurfaceFileUrl;
  497. var result = await SurfaceFileByUrl(surfaceFileUrl);
  498. if (result != null && result.Any())
  499. {
  500. surfaceImages = result;
  501. }
  502. }
  503. catch (Exception ex)
  504. {
  505. Logger.WriteLineWarn($"AIDiagnosisService CreateCarotidSurfaceImagesAsync error, ex:{ex}");
  506. }
  507. return new CreateCarotidSurfaceImagesResult { SurfaceImages = surfaceImages };
  508. }
  509. /// <summary>
  510. ///
  511. /// </summary>
  512. /// <param name="baseUrl"></param>
  513. private async Task<List<string>> SurfaceFileByUrl(string baseUrl)
  514. {
  515. var fileUrls = new List<string>();
  516. var localPath = await DownloadAsync(baseUrl);
  517. if (!string.IsNullOrWhiteSpace(localPath))
  518. {
  519. fileUrls = await SurfaceFile(localPath);
  520. }
  521. return fileUrls;
  522. }
  523. /// <summary>
  524. /// 生成AI报告
  525. /// </summary>
  526. /// <param name="request">生成AI报告请求实体</param>
  527. /// <returns></returns>
  528. /// <show>false</show>
  529. public async Task<DiagnosisReportResult> DiagnosisReportAsync(DiagnosisReportRequest request)
  530. {
  531. var disgnosisReportResult = new DiagnosisReportResult();
  532. try
  533. {
  534. var is3DCarotid = false;
  535. if (request.Organ == DiagnosisOrganEnum.CarotidArtery && is3DCarotid)
  536. {
  537. var result = await GetCarotidAIMeasureResult(request);
  538. return result;
  539. }
  540. else
  541. {
  542. var reportPerImages = new List<DiagnosisPerImageModel>();
  543. if (request.Organ == DiagnosisOrganEnum.CarotidArtery)
  544. {
  545. var leftRemedicals = request.RemedicalList.Where(x => x.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidLeft)?.ToList() ?? new List<DiagnosisRemicalDTO>();
  546. var leftRemedicalCode = string.Empty;
  547. double leftPhysicalPerPixel = 0;
  548. if (leftRemedicals.Any())
  549. {
  550. var managerLeft = await CreateDiagnosisManagerAsync(new DiagnosisReportRequest { Organ = request.Organ, RemedicalList = leftRemedicals });
  551. var perImages = managerLeft.GetReportResults();
  552. leftRemedicalCode = perImages.FirstOrDefault()?.RemedicalCode;
  553. leftPhysicalPerPixel = perImages.FirstOrDefault()?.Pixel ?? 0;
  554. reportPerImages.AddRange(perImages);
  555. }
  556. disgnosisReportResult.CarotidItems.AddRange(GetCarotidLeftReportItems(leftRemedicals.FirstOrDefault(x => x.RemedicalCode == leftRemedicalCode), leftPhysicalPerPixel));
  557. var rightRemedicals = request.RemedicalList.Where(x => x.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidRight)?.ToList() ?? new List<DiagnosisRemicalDTO>();
  558. var rightRemedicalCode = string.Empty;
  559. double rightPhysicalPerPixel = 0;
  560. if (rightRemedicals.Any())
  561. {
  562. var managerRight = await CreateDiagnosisManagerAsync(new DiagnosisReportRequest { Organ = request.Organ, RemedicalList = rightRemedicals });
  563. var perImages = managerRight.GetReportResults();
  564. rightRemedicalCode = perImages.FirstOrDefault()?.RemedicalCode;
  565. rightPhysicalPerPixel = perImages.FirstOrDefault()?.Pixel ?? 0;
  566. reportPerImages.AddRange(perImages);
  567. }
  568. disgnosisReportResult.CarotidItems.AddRange(GetCarotidRightReportItems(rightRemedicals.FirstOrDefault(x => x.RemedicalCode == rightRemedicalCode), rightPhysicalPerPixel));
  569. }
  570. else
  571. {
  572. var manager = await CreateDiagnosisManagerAsync(request);
  573. reportPerImages = manager.GetReportResults();
  574. }
  575. var diagnosisConclusion = GetDiagnosisConclusion(reportPerImages);
  576. var diagnosisOrgans = GetDiagnosisOrgans(reportPerImages);
  577. var diagnosisResult = new List<DiagnosisPerImageDTO>();
  578. foreach (var item in reportPerImages)
  579. {
  580. var aiFileToken = await UploadFileAsync(item.AILocalImagePath, Path.GetFileName(item.AILocalImagePath));
  581. var previewPath = Path.Combine(_tempFolder, $"{Guid.NewGuid():N}.jpg");
  582. CreateThumbnailFile(item.AILocalImagePath, 100, 30, previewPath);
  583. var previewUrl = await UploadFileAsync(previewPath, Path.GetFileName(previewPath));
  584. var perImageJson = Newtonsoft.Json.JsonConvert.SerializeObject(item);
  585. var perImageResult = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageDTO>(perImageJson);
  586. perImageResult.RemedicalCode = item.RemedicalCode;
  587. perImageResult.DataType = (RemedicalFileDataTypeEnum)item.DataType;
  588. perImageResult.Pixel = item.Pixel;
  589. perImageResult.AIFileToken = aiFileToken;
  590. perImageResult.AIPreviewFileToken = previewUrl;
  591. perImageResult.PerImageJson = perImageJson;
  592. var conclusion = GetDiagnosisConclusion(new List<DiagnosisPerImageModel> { item });
  593. var organs = GetDiagnosisOrgans(new List<DiagnosisPerImageModel> { item });
  594. perImageResult.DiagnosisConclusion = (DiagnosisConclusionEnum)conclusion;
  595. perImageResult.DiagnosisOrgans = organs;
  596. diagnosisResult.Add(perImageResult);
  597. }
  598. disgnosisReportResult.DiagnosisConclusion = (DiagnosisConclusionEnum)diagnosisConclusion;
  599. disgnosisReportResult.DiagnosisResult = diagnosisResult;
  600. }
  601. }
  602. catch (Exception ex)
  603. {
  604. Logger.WriteLineWarn($"AIService DiagnosisReport err, {ex}");
  605. }
  606. return disgnosisReportResult;
  607. }
  608. private List<DataItemDTO> GetCarotidLeftReportItems(DiagnosisRemicalDTO leftData, double physicalPerPixel)
  609. {
  610. var reportItems = new List<DataItemDTO>();
  611. if (leftData == null)
  612. {
  613. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = "" });
  614. reportItems.Add(new DataItemDTO { Key = "d2449fa2-70e7-45d1-80e3-f69c62652186;98d1e829-b0ee-440d-ae3b-02ff74521357;LeftPlaqueThick", Value = "" });
  615. reportItems.Add(new DataItemDTO { Key = "6f57bb90-6492-421e-ad3e-6c684b7f7452;6e36fa47-edb2-4ed8-8521-ddb9e1fa5536;LeftPlaquePosition", Value = "" });
  616. reportItems.Add(new DataItemDTO { Key = "ccef71cd-1c52-4614-a54f-d6d58cb8c586;d440eb6e-2630-4787-8700-486d097ba9f1;LeftPlaqueEcho", Value = "" });
  617. reportItems.Add(new DataItemDTO { Key = "9a6f96bb-b81e-407d-bc84-9d9815e96696;fe948295-b065-46b9-ab25-fe80144d7bff;LeftCarotidAD", Value = "" });
  618. reportItems.Add(new DataItemDTO { Key = "e3610e58-79c4-4e18-94c9-8928583b9dab;e6da9399-b849-4017-a3b8-e3212f55c4a4;LeftStenosisRate", Value = "" });
  619. return reportItems;
  620. }
  621. var carotidResultInfos = GetCarotidResultInfos(leftData);
  622. if (carotidResultInfos.Any(x => x.DetectedObjects.Any(x => x.Label == 1)))
  623. {
  624. //有斑块
  625. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = FormatterPlaceholder2($"OnePlaque") });
  626. var info = GetReportDetectedObject(carotidResultInfos, physicalPerPixel * 10);
  627. var reportDetectedObject = info.Item1;
  628. reportItems.Add(new DataItemDTO { Key = "d2449fa2-70e7-45d1-80e3-f69c62652186;98d1e829-b0ee-440d-ae3b-02ff74521357;LeftPlaqueThick", Value = info.Item3.ToString() });
  629. var locationItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueLocation);
  630. if (locationItem != null)
  631. {
  632. reportItems.Add(new DataItemDTO { Key = "6f57bb90-6492-421e-ad3e-6c684b7f7452;6e36fa47-edb2-4ed8-8521-ddb9e1fa5536;LeftPlaquePosition", Value = FormatterPlaceholder2($"{locationItem.Value}") });
  633. }
  634. var echoItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueEchoPattern);
  635. if (echoItem != null)
  636. {
  637. reportItems.Add(new DataItemDTO { Key = "ccef71cd-1c52-4614-a54f-d6d58cb8c586;d440eb6e-2630-4787-8700-486d097ba9f1;LeftPlaqueEcho", Value = FormatterPlaceholder2($"{echoItem.Value}") });
  638. }
  639. var rateOfStenosis = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidRateOfStenosis);
  640. if (rateOfStenosis != null)
  641. {
  642. var rateOfStenosisValue = string.Empty;
  643. var rate = rateOfStenosis.Value.ToFloat() * 100;
  644. if (rate < 30)
  645. {
  646. rateOfStenosisValue = "Normal";
  647. }
  648. else if (rate >= 30 && rate < 50)
  649. {
  650. rateOfStenosisValue = "Mild";
  651. }
  652. else if (rate >= 50 && rate < 70)
  653. {
  654. rateOfStenosisValue = "Moderate";
  655. }
  656. else if (rate >= 70 && rate < 100)
  657. {
  658. rateOfStenosisValue = "Severe";
  659. }
  660. else
  661. {
  662. rateOfStenosisValue = "Blockage";
  663. }
  664. reportItems.Add(new DataItemDTO { Key = "e3610e58-79c4-4e18-94c9-8928583b9dab;e6da9399-b849-4017-a3b8-e3212f55c4a4;LeftStenosisRate", Value = FormatterPlaceholder2(rateOfStenosisValue) });
  665. }
  666. var innerDiameter = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidInnerDiameter);
  667. if (innerDiameter != null && !string.IsNullOrWhiteSpace(innerDiameter.Value))
  668. {
  669. var diagnosisMeasuringLine = JsonConvert.DeserializeObject<AIDiagnosisMeasuringLine>(innerDiameter.Value);
  670. var measuringLineLengthInPixel = Math.Round(diagnosisMeasuringLine.MeasuringLineLengthInPixel * physicalPerPixel * 10, 2);
  671. reportItems.Add(new DataItemDTO { Key = "9a6f96bb-b81e-407d-bc84-9d9815e96696;fe948295-b065-46b9-ab25-fe80144d7bff;LeftCarotidAD", Value = measuringLineLengthInPixel.ToString() });
  672. }
  673. }
  674. else
  675. {
  676. // reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  677. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = "" });
  678. reportItems.Add(new DataItemDTO { Key = "d2449fa2-70e7-45d1-80e3-f69c62652186;98d1e829-b0ee-440d-ae3b-02ff74521357;LeftPlaqueThick", Value = "" });
  679. reportItems.Add(new DataItemDTO { Key = "6f57bb90-6492-421e-ad3e-6c684b7f7452;6e36fa47-edb2-4ed8-8521-ddb9e1fa5536;LeftPlaquePosition", Value = "" });
  680. reportItems.Add(new DataItemDTO { Key = "ccef71cd-1c52-4614-a54f-d6d58cb8c586;d440eb6e-2630-4787-8700-486d097ba9f1;LeftPlaqueEcho", Value = "" });
  681. reportItems.Add(new DataItemDTO { Key = "9a6f96bb-b81e-407d-bc84-9d9815e96696;fe948295-b065-46b9-ab25-fe80144d7bff;LeftCarotidAD", Value = "" });
  682. reportItems.Add(new DataItemDTO { Key = "e3610e58-79c4-4e18-94c9-8928583b9dab;e6da9399-b849-4017-a3b8-e3212f55c4a4;LeftStenosisRate", Value = "" });
  683. }
  684. return reportItems;
  685. }
  686. private List<DataItemDTO> GetCarotidRightReportItems(DiagnosisRemicalDTO rightData, double physicalPerPixel)
  687. {
  688. var reportItems = new List<DataItemDTO>();
  689. if (rightData == null)
  690. {
  691. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = "" });
  692. reportItems.Add(new DataItemDTO { Key = "8e467ffb-da00-4d34-9aac-34e4e8f91844;46166cb6-ad3a-4ca2-821e-bd872137fa64;RightPlaqueThick", Value = "" });
  693. reportItems.Add(new DataItemDTO { Key = "1153870e-10c8-4674-99bf-108937db1060;053091d2-8ea0-4fcb-a222-00b46f60d12f;RightPlaquePosition", Value = "" });
  694. reportItems.Add(new DataItemDTO { Key = "c82e8655-169f-42be-ac99-5e7a56329924;e77f550d-37f3-492a-98dd-a981535383a5;RightPlaqueEcho", Value = "" });
  695. reportItems.Add(new DataItemDTO { Key = "b0092e8a-25ba-4690-b70f-e3525e6aaa39;2e742ddc-fa90-47bb-ac24-664238773b30;RightStenosisRate", Value = "" });
  696. reportItems.Add(new DataItemDTO { Key = "8c7543e9-95f2-4862-b2aa-4c4cb032948e;9f8e95ee-a262-41c2-9d4b-efbc7974b397;RightCarotidAD", Value = "" });
  697. return reportItems;
  698. }
  699. var carotidResultInfos = GetCarotidResultInfos(rightData);
  700. if (carotidResultInfos.Any(x => x.DetectedObjects.Any(x => x.Label == 1)))
  701. {
  702. //有斑块
  703. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = FormatterPlaceholder2($"OnePlaque") });
  704. var info = GetReportDetectedObject(carotidResultInfos, physicalPerPixel * 10);
  705. var reportDetectedObject = info.Item1;
  706. reportItems.Add(new DataItemDTO { Key = "8e467ffb-da00-4d34-9aac-34e4e8f91844;46166cb6-ad3a-4ca2-821e-bd872137fa64;RightPlaqueThick", Value = info.Item3.ToString() });
  707. var locationItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueLocation);
  708. if (locationItem != null)
  709. {
  710. reportItems.Add(new DataItemDTO { Key = "1153870e-10c8-4674-99bf-108937db1060;053091d2-8ea0-4fcb-a222-00b46f60d12f;RightPlaquePosition", Value = FormatterPlaceholder2($"{locationItem.Value}") });
  711. }
  712. var echoItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueEchoPattern);
  713. if (echoItem != null)
  714. {
  715. reportItems.Add(new DataItemDTO { Key = "c82e8655-169f-42be-ac99-5e7a56329924;e77f550d-37f3-492a-98dd-a981535383a5;RightPlaqueEcho", Value = FormatterPlaceholder2($"{echoItem.Value}") });
  716. }
  717. var rateOfStenosis = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidRateOfStenosis);
  718. if (rateOfStenosis != null)
  719. {
  720. var rateOfStenosisValue = string.Empty;
  721. var rate = rateOfStenosis.Value.ToFloat() * 100;
  722. if (rate < 30)
  723. {
  724. rateOfStenosisValue = "Normal";
  725. }
  726. else if (rate >= 30 && rate < 50)
  727. {
  728. rateOfStenosisValue = "Mild";
  729. }
  730. else if (rate >= 50 && rate < 70)
  731. {
  732. rateOfStenosisValue = "Moderate";
  733. }
  734. else if (rate >= 70 && rate < 100)
  735. {
  736. rateOfStenosisValue = "Severe";
  737. }
  738. else
  739. {
  740. rateOfStenosisValue = "Blockage";
  741. }
  742. reportItems.Add(new DataItemDTO { Key = "b0092e8a-25ba-4690-b70f-e3525e6aaa39;2e742ddc-fa90-47bb-ac24-664238773b30;RightStenosisRate", Value = FormatterPlaceholder2(rateOfStenosisValue) });
  743. }
  744. var innerDiameter = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidInnerDiameter);
  745. if (innerDiameter != null && !string.IsNullOrWhiteSpace(innerDiameter.Value))
  746. {
  747. var diagnosisMeasuringLine = JsonConvert.DeserializeObject<AIDiagnosisMeasuringLine>(innerDiameter.Value);
  748. var measuringLineLengthInPixel = Math.Round(diagnosisMeasuringLine.MeasuringLineLengthInPixel * physicalPerPixel * 10, 2);
  749. reportItems.Add(new DataItemDTO { Key = "8c7543e9-95f2-4862-b2aa-4c4cb032948e;9f8e95ee-a262-41c2-9d4b-efbc7974b397;RightCarotidAD", Value = measuringLineLengthInPixel.ToString() });
  750. }
  751. }
  752. else
  753. {
  754. // reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  755. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = "" });
  756. reportItems.Add(new DataItemDTO { Key = "8e467ffb-da00-4d34-9aac-34e4e8f91844;46166cb6-ad3a-4ca2-821e-bd872137fa64;RightPlaqueThick", Value = "" });
  757. reportItems.Add(new DataItemDTO { Key = "1153870e-10c8-4674-99bf-108937db1060;053091d2-8ea0-4fcb-a222-00b46f60d12f;RightPlaquePosition", Value = "" });
  758. reportItems.Add(new DataItemDTO { Key = "c82e8655-169f-42be-ac99-5e7a56329924;e77f550d-37f3-492a-98dd-a981535383a5;RightPlaqueEcho", Value = "" });
  759. reportItems.Add(new DataItemDTO { Key = "b0092e8a-25ba-4690-b70f-e3525e6aaa39;2e742ddc-fa90-47bb-ac24-664238773b30;RightStenosisRate", Value = "" });
  760. reportItems.Add(new DataItemDTO { Key = "8c7543e9-95f2-4862-b2aa-4c4cb032948e;9f8e95ee-a262-41c2-9d4b-efbc7974b397;RightCarotidAD", Value = "" });
  761. }
  762. return reportItems;
  763. }
  764. /// <summary>
  765. /// 查询AI诊断结论
  766. /// </summary>
  767. /// <param name="request">查询AI诊断结论请求实体</param>
  768. /// <returns></returns>
  769. /// <show>false</show>
  770. public async Task<GetDiagnosisConclusionResult> GetDiagnosisConclusionAsync(GetDiagnosisConclusionRequest request)
  771. {
  772. var preImage = Newtonsoft.Json.JsonConvert.DeserializeObject<DiagnosisPerImageModel>(request.PerImageJson);
  773. var conclusion = GetDiagnosisConclusion(new List<DiagnosisPerImageModel> { preImage });
  774. var organs = GetDiagnosisOrgans(new List<DiagnosisPerImageModel> { preImage });
  775. var resultData = new GetDiagnosisConclusionResult
  776. {
  777. DiagnosisConclusion = (DiagnosisConclusionEnum)conclusion,
  778. DiagnosisOrgans = organs,
  779. };
  780. return await Task.FromResult(resultData);
  781. }
  782. /// <summary>
  783. /// 查询AI相关枚举集合
  784. /// </summary>
  785. /// <param name="request">查询AI相关枚举集合请求实体</param>
  786. /// <returns>AI相关枚举集合</returns>
  787. /// <value></value>
  788. /// <errorCodes></errorCodes>
  789. public async Task<GetDiagnosisEnumItemsResult> GetDiagnosisEnumItemsAsync(GetDiagnosisEnumItemsRequest request)
  790. {
  791. var diagnosisItems = new List<EnumItemDTO>();
  792. //病灶
  793. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "Breast", new List<string> { "BIRads1" }));
  794. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "Liver", new List<string> { "BIRads1" }));
  795. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "Thyroid", new List<string> { "TIRADS0" }));
  796. //病灶特性描述
  797. diagnosisItems.Add(GetEnumItem(typeof(EnumDesShapeValue)));
  798. diagnosisItems.Add(GetEnumItem(typeof(EnumDesOrientationValue)));
  799. diagnosisItems.Add(GetEnumItem(typeof(EnumDesEchoPatternValue)));
  800. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLesionBoundaryValue)));
  801. diagnosisItems.Add(GetEnumItem(typeof(EnumDesMarginValue)));
  802. diagnosisItems.Add(GetEnumItem(typeof(EnumCalcificationsValue), "Calcification"));
  803. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverShapeValue)));
  804. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverBoundaryValue)));
  805. diagnosisItems.Add(GetEnumItem(typeof(EnumDesLiverEchoTextureValue)));
  806. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchoPatternValue)));
  807. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidShapeValue)));
  808. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidMarginValue)));
  809. diagnosisItems.Add(GetEnumItem(typeof(EnumDesThyroidEchogenicFociValue)));
  810. //局灶和弥漫区分
  811. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastLocalLesion", includeFields: new List<string> { "Lipomyoma", "BIRads2", "BIRads3" }));
  812. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisBreastLabelEnum), "BreastDiffuseLesion", includeFields: new List<string> { "BIRads4A", "BIRads4B", "BIRads4C", "BIRads5" }));
  813. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverLocalLesion", includeFields: new List<string> { "Hyperechoic", "HHE", "CYST", "PossibleCancer" }));
  814. diagnosisItems.Add(GetEnumItem(typeof(DiagnosisLiverLabelEnum), "LiverDiffuseLesion", includeFields: new List<string> { "FattyLiver", "DiffuseLesions", "Cirrhosis", "PCLD" }));
  815. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidLocalLesion", includeFields: new List<string> { "TIRADS2", "TIRADS3", "TIRADS4a", "TIRADS4b", "TIRADS4c", "TIRADS5" }));
  816. diagnosisItems.Add(GetEnumItem(typeof(AIThyroidLabelEnum), "ThyroidDiffuseLesion", includeFields: new List<string> { "DiffuseDisease" }));
  817. var resultData = new GetDiagnosisEnumItemsResult { Source = diagnosisItems };
  818. return await Task.FromResult(resultData);
  819. }
  820. private EnumItemDTO GetEnumItem(Type enumType, string keyCode = "", List<string> excludeFields = null, List<string> includeFields = null)
  821. {
  822. keyCode = !string.IsNullOrWhiteSpace(keyCode) ? keyCode : enumType.Name.ToString().Replace("EnumDes", "").Replace("Enum", "").Replace("Value", "");
  823. var enumNames = Enum.GetNames(enumType).ToList();
  824. if (excludeFields != null && excludeFields.Any())
  825. {
  826. enumNames = enumNames.Except(excludeFields).ToList();
  827. }
  828. if (includeFields != null && includeFields.Any())
  829. {
  830. enumNames = enumNames.Intersect(includeFields).ToList();
  831. }
  832. var children = new List<EnumFieldDTO>();
  833. foreach (var val in enumNames)
  834. {
  835. var id = (int)enumType.GetField(val).GetValue(val);
  836. var fieldValue = val.ToString();
  837. if (enumType == typeof(DiagnosisLiverLabelEnum) && fieldValue == "Hyperechoic")
  838. {
  839. fieldValue = "LiverHyperechoic";
  840. }
  841. children.Add(new EnumFieldDTO
  842. {
  843. Id = id,
  844. Value = fieldValue,
  845. });
  846. }
  847. return new EnumItemDTO
  848. {
  849. Code = keyCode,
  850. Children = children,
  851. };
  852. }
  853. /// <summary>
  854. /// 查询病灶轮廓的关键点
  855. /// </summary>
  856. /// <param name="request">查询病灶轮廓的关键点请求实体</param>
  857. /// <returns>关键点集合</returns>
  858. /// <value></value>
  859. /// <errorCodes></errorCodes>
  860. public async Task<List<DiagnosisKeyPointDTO>> GetKeyPointsOfContourAsync(GetKeyPointsOfContourRequest request)
  861. {
  862. var contourPoints = request.Contours.Select(c => new Point2D
  863. {
  864. X = c.X,
  865. Y = c.Y
  866. }).ToArray();
  867. var ls = request.LesionSize;
  868. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  869. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  870. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  871. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  872. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  873. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  874. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  875. var aiResult = ContourModifyHelper.KeyPointsOfContour(contourPoints, lesionSize);
  876. var resultData = KeyPointToDto(aiResult);
  877. return await Task.FromResult(resultData);
  878. }
  879. /// <summary>
  880. /// 移动光标,查询受影响关键点
  881. /// </summary>
  882. /// <param name="request">移动光标,查询受影响关键点请求实体</param>
  883. /// <returns>受影响关键点下标</returns>
  884. /// <value></value>
  885. /// <errorCodes></errorCodes>
  886. public async Task<List<int>> AffectedKeyPointsByDragActionAsync(AffectedKeyPointsByDragActionRequest request)
  887. {
  888. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  889. {
  890. Type = (EnumKeyPointType)c.Type,
  891. IndexInContour = c.IndexInContour,
  892. Point = new Point2D(c.Point.X, c.Point.Y)
  893. }).ToArray();
  894. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  895. var resultData = ContourModifyHelper.AffectedKeyPointsByDragAction(origKeyPoints, mousePoint).ToList();
  896. return await Task.FromResult(resultData);
  897. }
  898. /// <summary>
  899. /// 拖动光标,查询所有轮廓点和关键点
  900. /// </summary>
  901. /// <param name="request">拖动光标,查询所有轮廓点和关键点请求实体</param>
  902. /// <returns>所有轮廓点和关键点</returns>
  903. /// <value></value>
  904. /// <errorCodes></errorCodes>
  905. public async Task<ContourAndKeyPointsAfterDragResult> ContourAndKeyPointsAfterDragAsync(ContourAndKeyPointsAfterDragRequest request)
  906. {
  907. var origContourPoints = request.Contours.Select(c => new Point2D
  908. {
  909. X = c.X,
  910. Y = c.Y
  911. }).ToArray();
  912. var origKeyPoints = request.KeyPoints.Select(c => new KeyPointInfo
  913. {
  914. Type = (EnumKeyPointType)c.Type,
  915. IndexInContour = c.IndexInContour,
  916. Point = new Point2D(c.Point.X, c.Point.Y)
  917. }).ToArray();
  918. var dragStartPoint = new Point2D(request.StartPoint.X, request.StartPoint.Y);
  919. var dragEndPoint = new Point2D(request.EndPoint.X, request.EndPoint.Y);
  920. ContourModifyHelper.ContourAndKeyPointsAfterDrag(origContourPoints, origKeyPoints, dragStartPoint, dragEndPoint
  921. , out Point2D[] dstContourPoints, out KeyPointInfo[] dstKeyPoints, out int[] affectedKeyPointIndexes);
  922. var dstContours = PointToDto(dstContourPoints);
  923. var dstKeys = KeyPointToDto(dstKeyPoints);
  924. var resultData = new ContourAndKeyPointsAfterDragResult
  925. {
  926. DstContours = dstContours,
  927. DstKeyPoints = dstKeys,
  928. AffectedKeyPointIndexes = affectedKeyPointIndexes.ToList(),
  929. };
  930. return await Task.FromResult(resultData);
  931. }
  932. /// <summary>
  933. /// 画轮廓模式,查询鼠标位置到轮廓线的最短距离
  934. /// </summary>
  935. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  936. /// <returns>鼠标离轮廓线的距离,高亮点的下标</returns>
  937. /// <value></value>
  938. /// <errorCodes></errorCodes>
  939. public async Task<MinimumDistanceToContourPointsResult> MinimumDistanceToContourPointsAsync(MinimumDistanceToContourPointsRequest request)
  940. {
  941. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  942. {
  943. X = c.X,
  944. Y = c.Y
  945. }).ToArray();
  946. var mousePoint = new Point2D(request.MousePoint.X, request.MousePoint.Y);
  947. var distance = ContourModifyHelper.MinimumDistanceToContourPoints(origContourPoints, mousePoint, out int closestPointIndex);
  948. var resultData = new MinimumDistanceToContourPointsResult
  949. {
  950. DistanceCaught = distance,
  951. ClosestPointIndex = closestPointIndex,
  952. };
  953. return await Task.FromResult(resultData);
  954. }
  955. /// <summary>
  956. /// 画轮廓模式,合并新旧轮廓
  957. /// </summary>
  958. /// <param name="request">画轮廓模式,查询鼠标位置到轮廓线的最短距离请求实体</param>
  959. /// <returns>合并后的完整轮廓线,合并后的横纵径尺寸结果</returns>
  960. /// <value></value>
  961. /// <errorCodes></errorCodes>
  962. public async Task<ContourMergeResult> ContourMergeAsync(ContourMergeRequest request)
  963. {
  964. var origContourPoints = request.ContourPoints.Select(c => new Point2D
  965. {
  966. X = c.X,
  967. Y = c.Y
  968. }).ToArray();
  969. var ls = request.LesionSize;
  970. var horizontalP1 = new Point2D(ls.HorizontalPoint1.X, ls.HorizontalPoint1.Y);
  971. var horizontalP2 = new Point2D(ls.HorizontalPoint2.X, ls.HorizontalPoint2.Y);
  972. var verticalP1 = new Point2D(ls.VerticalPoint1.X, ls.VerticalPoint1.Y);
  973. var verticalP2 = new Point2D(ls.VerticalPoint2.X, ls.VerticalPoint2.Y);
  974. var lesionSize = new LesionSize(horizontalP1, horizontalP2, verticalP1, verticalP2);
  975. // lesionSize.HorizontalLengthInPixel = ls.HorizontalLengthInPixel;
  976. // lesionSize.VerticalLengthInPixel = ls.VerticalLengthInPixel;
  977. var drawingNewContourPoints = request.DrawingNewContourPoints.Select(c => new Point2D
  978. {
  979. X = c.X,
  980. Y = c.Y
  981. }).ToArray();
  982. var aiResult = ContourModifyHelper.ContourMerge(origContourPoints, lesionSize, drawingNewContourPoints, out Point2D[] dstContourPoints, out LesionSize dstLesionSize);
  983. var dstContours = PointToDto(dstContourPoints);
  984. var resultData = new ContourMergeResult
  985. {
  986. DstContours = dstContours,
  987. DstLesionSize = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisLesionSize
  988. {
  989. HorizontalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint1.X, Y = dstLesionSize.HorizontalPoint1.Y },
  990. HorizontalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.HorizontalPoint2.X, Y = dstLesionSize.HorizontalPoint2.Y },
  991. VerticalPoint1 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint1.X, Y = dstLesionSize.VerticalPoint1.Y },
  992. VerticalPoint2 = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D { X = dstLesionSize.VerticalPoint2.X, Y = dstLesionSize.VerticalPoint2.Y },
  993. HorizontalLengthInPixel = dstLesionSize.HorizontalLengthInPixel,
  994. VerticalLengthInPixel = dstLesionSize.VerticalLengthInPixel,
  995. },
  996. };
  997. return await Task.FromResult(resultData);
  998. }
  999. /// <summary>
  1000. /// 获取颈动脉ai报告数据
  1001. /// </summary>
  1002. /// <param name="request">生成AI报告请求实体</param>
  1003. /// <returns></returns>
  1004. private async Task<DiagnosisReportResult> GetCarotidAIMeasureResult(DiagnosisReportRequest request)
  1005. {
  1006. var result = new DiagnosisReportResult()
  1007. {
  1008. DiagnosisConclusion = DiagnosisConclusionEnum.NoObviousLesion
  1009. };
  1010. var reportItems = new List<DataItemDTO>();
  1011. var orderedExamDatas = request.RemedicalList.OrderByDescending(m => m.CarotidResult?.CarotidScanType).Where(m => m.CarotidResult?.MeasureImageFiles != null && m.CarotidResult.MeasureImageFiles.Count > 0);
  1012. if (orderedExamDatas?.Count() > 0)
  1013. {
  1014. DiagnosisRemicalDTO leftData = null;
  1015. //left
  1016. var leftExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidLeft);
  1017. if (leftExamDatas.Any())
  1018. {
  1019. foreach (var leftItem in leftExamDatas)
  1020. {
  1021. if (string.IsNullOrEmpty(leftItem.CarotidResult.MeasureResult))
  1022. {
  1023. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  1024. continue;
  1025. }
  1026. //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}}";
  1027. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(leftItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  1028. if (measureResult.PlaqueResult?.PlaqueCountType != Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  1029. {
  1030. leftData = leftItem;
  1031. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  1032. break;
  1033. }
  1034. }
  1035. if (leftData == null)
  1036. {
  1037. leftData = leftExamDatas.First();
  1038. }
  1039. }
  1040. if (leftData != null)
  1041. {
  1042. result.CarotidResult.Add(leftData);
  1043. var carotidResultInfos = GetCarotidResultInfos(leftData);
  1044. if (carotidResultInfos.Any(x => x.DetectedObjects.Any(x => x.Label == 1)))
  1045. {
  1046. //有斑块
  1047. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = FormatterPlaceholder2($"OnePlaque") });
  1048. var info = GetReportDetectedObject(carotidResultInfos, 0.1);
  1049. var reportDetectedObject = info.Item1;
  1050. var maxArea = Math.Round(info.Item2 * 0.1, 2);
  1051. reportItems.Add(new DataItemDTO { Key = "d2449fa2-70e7-45d1-80e3-f69c62652186;98d1e829-b0ee-440d-ae3b-02ff74521357;LeftPlaqueThick", Value = maxArea.ToString() });
  1052. var locationItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueLocation);
  1053. if (locationItem != null)
  1054. {
  1055. reportItems.Add(new DataItemDTO { Key = "6f57bb90-6492-421e-ad3e-6c684b7f7452;6e36fa47-edb2-4ed8-8521-ddb9e1fa5536;LeftPlaquePosition", Value = FormatterPlaceholder2($"{locationItem.Value}") });
  1056. }
  1057. var echoItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueEchoPattern);
  1058. if (echoItem != null)
  1059. {
  1060. reportItems.Add(new DataItemDTO { Key = "ccef71cd-1c52-4614-a54f-d6d58cb8c586;d440eb6e-2630-4787-8700-486d097ba9f1;LeftPlaqueEcho", Value = FormatterPlaceholder2($"{echoItem.Value}") });
  1061. }
  1062. var rateOfStenosis = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidRateOfStenosis);
  1063. if (rateOfStenosis != null)
  1064. {
  1065. var rateOfStenosisValue = string.Empty;
  1066. var rate = rateOfStenosis.Value.ToFloat() * 100;
  1067. if (rate < 30)
  1068. {
  1069. rateOfStenosisValue = "Normal";
  1070. }
  1071. else if (rate >= 30 && rate < 50)
  1072. {
  1073. rateOfStenosisValue = "Mild";
  1074. }
  1075. else if (rate >= 50 && rate < 70)
  1076. {
  1077. rateOfStenosisValue = "Moderate";
  1078. }
  1079. else if (rate >= 70 && rate < 100)
  1080. {
  1081. rateOfStenosisValue = "Severe";
  1082. }
  1083. else
  1084. {
  1085. rateOfStenosisValue = "Blockage";
  1086. }
  1087. reportItems.Add(new DataItemDTO { Key = "e3610e58-79c4-4e18-94c9-8928583b9dab;e6da9399-b849-4017-a3b8-e3212f55c4a4;LeftStenosisRate", Value = FormatterPlaceholder2(rateOfStenosisValue) });
  1088. }
  1089. var innerDiameter = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidInnerDiameter);
  1090. if (innerDiameter != null && !string.IsNullOrWhiteSpace(innerDiameter.Value))
  1091. {
  1092. var diagnosisMeasuringLine = JsonConvert.DeserializeObject<AIDiagnosisMeasuringLine>(innerDiameter.Value);
  1093. var measuringLineLengthInPixel = Math.Round(diagnosisMeasuringLine.MeasuringLineLengthInPixel * 0.1, 2);
  1094. reportItems.Add(new DataItemDTO { Key = "9a6f96bb-b81e-407d-bc84-9d9815e96696;fe948295-b065-46b9-ab25-fe80144d7bff;LeftCarotidAD", Value = measuringLineLengthInPixel.ToString() });
  1095. }
  1096. }
  1097. else
  1098. {
  1099. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  1100. }
  1101. }
  1102. else
  1103. {
  1104. reportItems.Add(new DataItemDTO { Key = "bd170b6a-e052-42f0-a778-4ec85138c1c6;33c0aaf8-e48e-4474-af68-b47d2378073f;LeftConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  1105. }
  1106. //right
  1107. float intimaThickStandard = 1.0f;
  1108. DiagnosisRemicalDTO rightData = null;
  1109. var rightExamDatas = orderedExamDatas.Where(m => m.CarotidResult.CarotidScanType == CarotidScanTypeEnum.CarotidRight);
  1110. if (rightExamDatas.Any())
  1111. {
  1112. foreach (var rightItem in rightExamDatas)
  1113. {
  1114. if (string.IsNullOrEmpty(rightItem.CarotidResult.MeasureResult))
  1115. {
  1116. result.DiagnosisConclusion = DiagnosisConclusionEnum.Unrecognized;
  1117. continue;
  1118. }
  1119. //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}}";
  1120. var measureResult = JsonConvert.DeserializeObject<CarotidAIMeasureResult>(rightItem.CarotidResult.MeasureResult) ?? new CarotidAIMeasureResult();
  1121. if (measureResult.IntimaResult?.PostIntima?.IntimaThick > intimaThickStandard)
  1122. {
  1123. rightData = rightItem;
  1124. result.DiagnosisConclusion = DiagnosisConclusionEnum.Other;
  1125. break;
  1126. }
  1127. }
  1128. if (rightData == null)
  1129. {
  1130. rightData = rightExamDatas.First();
  1131. }
  1132. }
  1133. if (rightData != null)
  1134. {
  1135. result.CarotidResult.Add(rightData);
  1136. var carotidResultInfos = GetCarotidResultInfos(rightData);
  1137. if (carotidResultInfos.Any(x => x.DetectedObjects.Any(x => x.Label == 1)))
  1138. {
  1139. //有斑块
  1140. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = FormatterPlaceholder2($"OnePlaque") });
  1141. var info = GetReportDetectedObject(carotidResultInfos, 0.1);
  1142. var reportDetectedObject = info.Item1;
  1143. var maxArea = Math.Round(info.Item2 * 0.1, 2);
  1144. reportItems.Add(new DataItemDTO { Key = "8e467ffb-da00-4d34-9aac-34e4e8f91844;46166cb6-ad3a-4ca2-821e-bd872137fa64;RightPlaqueThick", Value = maxArea.ToString() });
  1145. var locationItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueLocation);
  1146. if (locationItem != null)
  1147. {
  1148. reportItems.Add(new DataItemDTO { Key = "1153870e-10c8-4674-99bf-108937db1060;053091d2-8ea0-4fcb-a222-00b46f60d12f;RightPlaquePosition", Value = FormatterPlaceholder2($"{locationItem.Value}") });
  1149. }
  1150. var echoItem = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.QlaqueEchoPattern);
  1151. if (echoItem != null)
  1152. {
  1153. reportItems.Add(new DataItemDTO { Key = "c82e8655-169f-42be-ac99-5e7a56329924;e77f550d-37f3-492a-98dd-a981535383a5;RightPlaqueEcho", Value = FormatterPlaceholder2($"{echoItem.Value}") });
  1154. }
  1155. var rateOfStenosis = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidRateOfStenosis);
  1156. if (rateOfStenosis != null)
  1157. {
  1158. var rateOfStenosisValue = string.Empty;
  1159. var rate = rateOfStenosis.Value.ToFloat() * 100;
  1160. if (rate < 30)
  1161. {
  1162. rateOfStenosisValue = "Normal";
  1163. }
  1164. else if (rate >= 30 && rate < 50)
  1165. {
  1166. rateOfStenosisValue = "Mild";
  1167. }
  1168. else if (rate >= 50 && rate < 70)
  1169. {
  1170. rateOfStenosisValue = "Moderate";
  1171. }
  1172. else if (rate >= 70 && rate < 100)
  1173. {
  1174. rateOfStenosisValue = "Severe";
  1175. }
  1176. else
  1177. {
  1178. rateOfStenosisValue = "Blockage";
  1179. }
  1180. reportItems.Add(new DataItemDTO { Key = "b0092e8a-25ba-4690-b70f-e3525e6aaa39;2e742ddc-fa90-47bb-ac24-664238773b30;RightStenosisRate", Value = FormatterPlaceholder2(rateOfStenosisValue) });
  1181. }
  1182. var innerDiameter = GetDescription(reportDetectedObject, DiagnosisDescriptionEnum.CarotidInnerDiameter);
  1183. if (innerDiameter != null && !string.IsNullOrWhiteSpace(innerDiameter.Value))
  1184. {
  1185. var diagnosisMeasuringLine = JsonConvert.DeserializeObject<AIDiagnosisMeasuringLine>(innerDiameter.Value);
  1186. var measuringLineLengthInPixel = Math.Round(diagnosisMeasuringLine.MeasuringLineLengthInPixel * 0.1, 2);
  1187. reportItems.Add(new DataItemDTO { Key = "8c7543e9-95f2-4862-b2aa-4c4cb032948e;9f8e95ee-a262-41c2-9d4b-efbc7974b397;RightCarotidAD", Value = measuringLineLengthInPixel.ToString() });
  1188. }
  1189. }
  1190. else
  1191. {
  1192. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  1193. }
  1194. }
  1195. else
  1196. {
  1197. reportItems.Add(new DataItemDTO { Key = "ec4c775e-601c-48cc-a1a9-f39c8629e3f7;9918912e-b702-4a2b-8b4f-61dcfb846be3;RightConclusion", Value = FormatterPlaceholder2($"NoPlaque2") });
  1198. }
  1199. Logger.WriteLineInfo($"AIDiagnosisService package carotidAIMeasureResult finished, CarotidLeftRemedicalCode:{leftData?.RemedicalCode}, CarotidLeft:{leftData?.CarotidResult?.MeasureResult}, CarotidRightRemedicalCode:{rightData?.RemedicalCode}, CarotidRight:" + rightData?.CarotidResult?.MeasureResult);
  1200. }
  1201. result.CarotidItems = reportItems;
  1202. return result;
  1203. }
  1204. private string FormatterPlaceholder2(string val)
  1205. {
  1206. return string.Format("#@{0}@#", val);
  1207. }
  1208. private string FormatterPlaceholderArray(string format, string[] pars)
  1209. {
  1210. var parameters = string.Join(',', pars);
  1211. parameters = parameters.Replace("#@", "&@").Replace("@#", "@&");
  1212. return FormatterPlaceholder(format + $"[{parameters}]");
  1213. }
  1214. private string FormatterPlaceholder(string languageKey)
  1215. {
  1216. languageKey = languageKey?.Trim() ?? string.Empty;
  1217. if (!string.IsNullOrWhiteSpace(languageKey))
  1218. {
  1219. return string.Format("#@{0}@#", languageKey);
  1220. }
  1221. return string.Empty;
  1222. }
  1223. private List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisResultPerOrgan> GetCarotidResultInfos(DiagnosisRemicalDTO remedical)
  1224. {
  1225. var diagnosisResult = remedical.DiagnosisResult;
  1226. var carotidResultInfos = new List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisResultPerOrgan>();
  1227. if (diagnosisResult != null && diagnosisResult.Any())
  1228. {
  1229. foreach (var item in diagnosisResult)
  1230. {
  1231. var carotids = item.DiagResultsForEachOrgan?.Where(x => x.Organ == DiagnosisOrganEnum.CarotidArtery);
  1232. if (carotids != null && carotids.Any())
  1233. {
  1234. carotidResultInfos.AddRange(carotids);
  1235. }
  1236. }
  1237. }
  1238. return carotidResultInfos;
  1239. }
  1240. private Tuple<Manage.AIDetectedObject, int, string, double> GetReportDetectedObject(List<Manage.AIDiagnosisResultPerOrgan> carotidResultInfos, double physicalPerPixel)
  1241. {
  1242. Manage.AIDetectedObject detectedObject = null;
  1243. var maxArea = 0;
  1244. var maxAreaString = string.Empty;
  1245. double verticalLengthInPixel = 0;
  1246. double horizontalLengthInPixel = 0;
  1247. foreach (var organInfo in carotidResultInfos)
  1248. {
  1249. var plaqueDetectedObjects = organInfo.DetectedObjects.Where(x => x.Label == 1);
  1250. if (plaqueDetectedObjects == null || !plaqueDetectedObjects.Any())
  1251. {
  1252. continue;
  1253. }
  1254. foreach (var dob in plaqueDetectedObjects)
  1255. {
  1256. var lesionSizeDes = dob.Descriptions?.FirstOrDefault(x => x.Type == DiagnosisDescription.LesionSize);
  1257. if (lesionSizeDes != null && !string.IsNullOrWhiteSpace(lesionSizeDes.Value))
  1258. {
  1259. var diagnosisLesionSize = JsonConvert.DeserializeObject<Manage.AIDiagnosisLesionSize>(lesionSizeDes.Value);
  1260. var currArea = diagnosisLesionSize.VerticalLengthInPixel * diagnosisLesionSize.HorizontalLengthInPixel;
  1261. if (detectedObject == null || currArea > maxArea)
  1262. {
  1263. detectedObject = dob;
  1264. maxArea = currArea;
  1265. verticalLengthInPixel = Math.Round(diagnosisLesionSize.VerticalLengthInPixel * physicalPerPixel, 2);
  1266. horizontalLengthInPixel = Math.Round(diagnosisLesionSize.HorizontalLengthInPixel * physicalPerPixel, 2);
  1267. maxAreaString = $"{horizontalLengthInPixel} × {verticalLengthInPixel}";
  1268. }
  1269. }
  1270. }
  1271. }
  1272. return new Tuple<Manage.AIDetectedObject, int, string, double>(detectedObject, maxArea, maxAreaString, Math.Min(verticalLengthInPixel, horizontalLengthInPixel));
  1273. }
  1274. private Tuple<WingInterfaceLibrary.DTO.Comment.AIDetectedObject, int, string, double> GetReportDetectedObject(List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisResultPerOrgan> carotidResultInfos, double physicalPerPixel)
  1275. {
  1276. WingInterfaceLibrary.DTO.Comment.AIDetectedObject detectedObject = null;
  1277. var maxArea = 0;
  1278. var maxAreaString = string.Empty;
  1279. double verticalLengthInPixel = 0;
  1280. double horizontalLengthInPixel = 0;
  1281. foreach (var organInfo in carotidResultInfos)
  1282. {
  1283. var plaqueDetectedObjects = organInfo.DetectedObjects.Where(x => x.Label == 1);
  1284. if (plaqueDetectedObjects == null || !plaqueDetectedObjects.Any())
  1285. {
  1286. continue;
  1287. }
  1288. foreach (var dob in plaqueDetectedObjects)
  1289. {
  1290. var lesionSizeDes = dob.Descriptions?.FirstOrDefault(x => x.Type == DiagnosisDescriptionEnum.LesionSize);
  1291. if (lesionSizeDes != null && !string.IsNullOrWhiteSpace(lesionSizeDes.Value))
  1292. {
  1293. var diagnosisLesionSize = JsonConvert.DeserializeObject<Manage.AIDiagnosisLesionSize>(lesionSizeDes.Value);
  1294. var currArea = diagnosisLesionSize.VerticalLengthInPixel * diagnosisLesionSize.HorizontalLengthInPixel;
  1295. if (detectedObject == null || currArea > maxArea)
  1296. {
  1297. detectedObject = dob;
  1298. maxArea = currArea;
  1299. verticalLengthInPixel = Math.Round(diagnosisLesionSize.VerticalLengthInPixel * physicalPerPixel, 2);
  1300. horizontalLengthInPixel = Math.Round(diagnosisLesionSize.HorizontalLengthInPixel * physicalPerPixel, 2);
  1301. maxAreaString = $"{horizontalLengthInPixel} × {verticalLengthInPixel}";
  1302. }
  1303. }
  1304. }
  1305. }
  1306. return new Tuple<WingInterfaceLibrary.DTO.Comment.AIDetectedObject, int, string, double>(detectedObject, maxArea, maxAreaString, Math.Min(verticalLengthInPixel, horizontalLengthInPixel));
  1307. }
  1308. private WingInterfaceLibrary.DTO.Comment.AIDiagnosisDescription GetDescription(WingInterfaceLibrary.DTO.Comment.AIDetectedObject detectedObject, DiagnosisDescriptionEnum descriptionType)
  1309. {
  1310. var description = detectedObject?.Descriptions.FirstOrDefault(x => x.Type == descriptionType);
  1311. return description;
  1312. }
  1313. private async Task<BaseDiagnosis> CreateDiagnosisManagerAsync(DiagnosisReportRequest request)
  1314. {
  1315. var recordResults = new List<DiagnosisPerImageModel>();
  1316. foreach (var remedical in request.RemedicalList)
  1317. {
  1318. foreach (var perImage in remedical.DiagnosisResult)
  1319. {
  1320. var localFile = await DownloadAsync(remedical.FileToken);
  1321. if (File.Exists((localFile)))
  1322. {
  1323. var diagnosisPerImage = new DiagnosisPerImageModel();
  1324. diagnosisPerImage.RemedicalCode = remedical.RemedicalCode;
  1325. diagnosisPerImage.DataType = (WingAIDiagnosisService.Manage.DataType)remedical.DataType;
  1326. diagnosisPerImage.LocalVidPath = localFile;
  1327. diagnosisPerImage.Index = perImage.Index;
  1328. diagnosisPerImage.PriorityScore = perImage.PriorityScore;
  1329. var diagnosisResults = Newtonsoft.Json.JsonConvert.DeserializeObject<List<WingAIDiagnosisService.Manage.AIDiagnosisResultPerOrgan>>(Newtonsoft.Json.JsonConvert.SerializeObject(perImage.DiagResultsForEachOrgan));
  1330. diagnosisPerImage.DiagResultsForEachOrgan = diagnosisResults;
  1331. recordResults.Add(diagnosisPerImage);
  1332. }
  1333. }
  1334. }
  1335. switch (request.Organ)
  1336. {
  1337. case DiagnosisOrganEnum.Breast:
  1338. return new BreastDiagnosis(recordResults, request.Organ);
  1339. case DiagnosisOrganEnum.Liver:
  1340. return new LiverDiagnosis(recordResults, request.Organ);
  1341. case DiagnosisOrganEnum.Thyroid://甲状腺
  1342. return new ThyroidDiagnosis(recordResults, request.Organ);
  1343. case DiagnosisOrganEnum.CarotidArtery://颈动脉
  1344. return new CarotidDiagnosis(recordResults, request.Organ);
  1345. default:
  1346. throw new Exception($"not support organ type:{request.Organ.ToString()}");
  1347. }
  1348. }
  1349. private async Task<CarotidResultDTO> CarotidDiagnosis(VinnoImageData imageData)
  1350. {
  1351. var resampleInputData = new ResampleInputData();
  1352. resampleInputData.SurfaceFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.SurfaceFileSuffix);
  1353. resampleInputData.MdlFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ModelFileSuffix);
  1354. resampleInputData.MdlZipFilePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.ZipFileSuffix);
  1355. resampleInputData.BaseAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  1356. resampleInputData.PlaqueAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  1357. resampleInputData.YShapeAIImagePath = DiagnosisHelper.GetCacheFilePath(DiagnosisHelper.JPGFileSuffix);
  1358. var resampleResult = TryResample(imageData, resampleInputData);
  1359. if (imageData.ImageCount <= DiagnosisHelper.CarotidMinImageCounts)
  1360. {
  1361. return new CarotidResultDTO
  1362. {
  1363. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  1364. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  1365. };
  1366. }
  1367. return new CarotidResultDTO
  1368. {
  1369. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  1370. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  1371. };
  1372. if (resampleResult.ResampleErrorCode == ResampleErrorCode.Success)
  1373. {
  1374. var carotidAIImageTokens = new List<CarotidAIImage>();
  1375. if (resampleResult.CarotidAIMeasureResult.IsYImageSuccess)
  1376. {
  1377. var fileUrl = await UploadFileAsync(resampleInputData.YShapeAIImagePath, Path.GetFileName(resampleInputData.YShapeAIImagePath));
  1378. if (!string.IsNullOrWhiteSpace(fileUrl))
  1379. {
  1380. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.YShape });
  1381. }
  1382. File.Delete(resampleInputData.YShapeAIImagePath);
  1383. }
  1384. if (resampleResult.CarotidAIMeasureResult.IntimaResult.IsSuccess)
  1385. {
  1386. var fileUrl = await UploadFileAsync(resampleInputData.BaseAIImagePath, Path.GetFileName(resampleInputData.BaseAIImagePath));
  1387. if (!string.IsNullOrWhiteSpace(fileUrl))
  1388. {
  1389. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Base });
  1390. }
  1391. File.Delete(resampleInputData.BaseAIImagePath);
  1392. }
  1393. if (resampleResult.CarotidAIMeasureResult.PlaqueResult.IsSuccess && resampleResult.CarotidAIMeasureResult.PlaqueResult.PlaqueCountType != WingAIDiagnosisService.Carotid.Utilities.DetectPlaque.PlaqueCountType.NoPlaque)
  1394. {
  1395. var fileUrl = await UploadFileAsync(resampleInputData.PlaqueAIImagePath, Path.GetFileName(resampleInputData.PlaqueAIImagePath));
  1396. if (!string.IsNullOrWhiteSpace(fileUrl))
  1397. {
  1398. carotidAIImageTokens.Add(new CarotidAIImage() { AIImageToken = fileUrl, AIImageType = CarotidAIImageType.Plaque });
  1399. }
  1400. File.Delete(resampleInputData.PlaqueAIImagePath);
  1401. }
  1402. var surfaceImageList = new List<string>();
  1403. long surfaceFileSize = 0;
  1404. var cdnSurfaceFile = "";
  1405. if (!string.IsNullOrWhiteSpace(resampleInputData.SurfaceFilePath) && File.Exists(resampleInputData.SurfaceFilePath))
  1406. {
  1407. surfaceImageList = await SurfaceFile(resampleInputData.SurfaceFilePath);
  1408. var surfaceFileTuple = await UploadFileTupleAsync(resampleInputData.SurfaceFilePath, Path.GetFileName(resampleInputData.SurfaceFilePath));
  1409. surfaceFileSize = surfaceFileTuple.Item2;
  1410. File.Delete(resampleInputData.SurfaceFilePath);
  1411. resampleInputData.SurfaceFilePath = surfaceFileTuple.Item1;
  1412. cdnSurfaceFile = surfaceFileTuple.Item1.ToUrlToken();//ToCDN
  1413. }
  1414. long mdlFileFileSize = 0;
  1415. var cdnMdlFile = "";
  1416. if (!string.IsNullOrWhiteSpace(resampleInputData.MdlZipFilePath) && File.Exists(resampleInputData.MdlZipFilePath))
  1417. {
  1418. var mdlFileTuple = await UploadFileTupleAsync(resampleInputData.MdlZipFilePath, Path.GetFileName(resampleInputData.MdlZipFilePath));
  1419. mdlFileFileSize = mdlFileTuple.Item2;
  1420. File.Delete(resampleInputData.MdlZipFilePath);
  1421. resampleInputData.MdlZipFilePath = mdlFileTuple.Item1;
  1422. cdnMdlFile = mdlFileTuple.Item1.ToUrlToken();//ToCDN
  1423. }
  1424. var result = new CarotidResultDTO
  1425. {
  1426. CarotidScanType = (CarotidScanTypeEnum)resampleInputData.ScanType,
  1427. CarotidScanDirection = (CarotidScanDirectionEnum)resampleInputData.CarotidScanDirection,
  1428. SurfaceFile = resampleInputData.SurfaceFilePath,
  1429. SurfaceFileSize = surfaceFileSize,
  1430. CDNSurfaceFile = cdnSurfaceFile,
  1431. MdlFileSize = mdlFileFileSize,
  1432. MdlFile = resampleInputData.MdlZipFilePath,
  1433. CDNMdlFile = cdnMdlFile,
  1434. MeasureImageFiles = carotidAIImageTokens.Select(x => new MeasureImageFileDTO { ImageType = (CarotidAIImageTypeEnum)x.AIImageType, ImageFile = x.AIImageToken }).ToList(),
  1435. MeasureResult = JsonConvert.SerializeObject(resampleResult.CarotidAIMeasureResult),
  1436. SurfaceImageList = surfaceImageList,
  1437. };
  1438. return result;
  1439. }
  1440. return null;
  1441. }
  1442. private ResampleResult TryResample(VinnoImageData vinnoImageData, ResampleInputData resampleInputData)
  1443. {
  1444. float scanDistance = 7;
  1445. ResampleResult resampleResult = new ResampleResult(ResampleErrorCode.Fail);
  1446. var vinnoExtendedData = VinnoCarotidExtendedData.FromBytes(vinnoImageData.ExtendedData);
  1447. CarotidScanType scanType = CarotidScanType.CarotidLeft;
  1448. CarotidScanDirection direction = CarotidScanDirection.TopToBottom;
  1449. if (vinnoExtendedData != null)
  1450. {
  1451. // Should be 6~9cm normally.
  1452. //scanDistance = vinnoExtendedData.ScanDistance;
  1453. scanType = vinnoExtendedData.CarotidType == CarotidType.Left ? CarotidScanType.CarotidLeft : CarotidScanType.CarotidRight;
  1454. direction = vinnoExtendedData.CarotidDirection == CarotidDirection.BottomToTop ? CarotidScanDirection.BottomToTop
  1455. : CarotidScanDirection.TopToBottom;
  1456. }
  1457. else
  1458. {
  1459. //This is a walkaround : vid's first frame is black image or depth coordinate is not in the right place.
  1460. var middleCount = vinnoImageData.ImageCount / 2;
  1461. var vinnoImage = vinnoImageData.GetImage(middleCount);
  1462. var imageBuffer = vinnoImage.ImageData;
  1463. if (_recognizeCarotidTypeCanBeUse)
  1464. {
  1465. //转成彩色图像
  1466. using var img = new Mat();
  1467. CvInvoke.Imdecode(imageBuffer, ImreadModes.Color, img);
  1468. using var image = img.ToImage<Bgr, byte>();
  1469. //识别左右颈信息
  1470. scanType = _recognizeCarotidType.RecognizeType(image);
  1471. }
  1472. }
  1473. resampleInputData.ScanDistance = scanDistance;
  1474. resampleInputData.ScanType = scanType;
  1475. resampleInputData.CarotidScanDirection = direction;
  1476. resampleResult = ResampleModel.Instance.Resample(vinnoImageData, resampleInputData, _workerLevel);
  1477. return resampleResult;
  1478. }
  1479. /// <summary>
  1480. ///
  1481. /// </summary>
  1482. /// <param name="filePath"></param>
  1483. private async Task<List<string>> SurfaceFile(string filePath)
  1484. {
  1485. var fileUrls = new List<string>();
  1486. try
  1487. {
  1488. using (var stream = new FileStream(filePath, FileMode.Open))
  1489. {
  1490. var reader = new Carotid3DStreamReader(stream);
  1491. var width = reader.ReadInt();
  1492. var height = reader.ReadInt();
  1493. var depth = reader.ReadInt();
  1494. var physicalData = VinnoCarotid3DPhysicalData.FromBytes(reader.ReadBytes());
  1495. var imageCount = reader.ReadInt();
  1496. var fileName = $"{Guid.NewGuid():N}";
  1497. for (var i = 0; i < imageCount; i++)
  1498. {
  1499. var readBytes = reader.ReadBytes();
  1500. if (readBytes.Any())
  1501. {
  1502. using var img = new Mat();
  1503. CvInvoke.Imdecode(readBytes, ImreadModes.Color, img);
  1504. var buf = new Emgu.CV.Util.VectorOfByte();
  1505. using var image = img.ToImage<Gray, byte>();
  1506. CvInvoke.Imencode(".jpg", image, buf, new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  1507. var jpgByte = buf.ToArray();
  1508. var localPath = Path.Combine(_tempFolder, $"{fileName}_{i + 1}.jpg");
  1509. File.WriteAllBytes(localPath, jpgByte);
  1510. var fileUrl = await UploadFileAsync(localPath, Path.GetFileName(localPath));
  1511. fileUrls.Add(fileUrl);
  1512. File.Delete(localPath);
  1513. }
  1514. }
  1515. }
  1516. }
  1517. catch (Exception ex)
  1518. {
  1519. Logger.WriteLineWarn($"SurfaceFile err, ex:{ex}");
  1520. }
  1521. return fileUrls;
  1522. }
  1523. private SKBitmap CreateBitmap(VinnoImage vinnoImage)
  1524. {
  1525. try
  1526. {
  1527. return SKBitmap.Decode(vinnoImage.ImageData);
  1528. }
  1529. catch (Exception ex)
  1530. {
  1531. Logger.WriteLineError($"Create skbitmap by VinnoImage error:{ex}");
  1532. }
  1533. return null;
  1534. }
  1535. private SKBitmap CreateBitmap(byte[] imageData)
  1536. {
  1537. try
  1538. {
  1539. return SKBitmap.Decode(imageData);
  1540. }
  1541. catch (Exception ex)
  1542. {
  1543. Logger.WriteLineError($"Create skbitmap by VinnoImage error:{ex}");
  1544. }
  1545. return null;
  1546. }
  1547. private EnumColorType MapTo(SKColorType sKColor)
  1548. {
  1549. switch (sKColor)
  1550. {
  1551. case SKColorType.Rgba8888:
  1552. return EnumColorType.Rgba;
  1553. case SKColorType.Rgb888x:
  1554. return EnumColorType.Rgba;
  1555. case SKColorType.Bgra8888:
  1556. return EnumColorType.Bgra;
  1557. case SKColorType.Gray8:
  1558. return EnumColorType.Gray8;
  1559. }
  1560. throw new Exception($"AIService not support color type:{sKColor}");
  1561. }
  1562. private void AIdiagSystem_NotifyError(object sender, ErrorEventArgs e)
  1563. {
  1564. Logger.WriteLineError("AIdiagSystem_NotifyError:" + e.GetException());
  1565. }
  1566. private void AIdiagSystem_NotifyLog(object sender, LogEventArgs e)
  1567. {
  1568. if (e != null && !string.IsNullOrEmpty(e.Msg))
  1569. {
  1570. switch (e.LogType)
  1571. {
  1572. case EnumLogType.InfoLog:
  1573. Logger.WriteLineInfo($"AIdiagSystem_NotifyLog:{e.Msg}");
  1574. break;
  1575. case EnumLogType.ErrorLog:
  1576. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  1577. break;
  1578. case EnumLogType.WarnLog:
  1579. Logger.WriteLineWarn($"AIdiagSystem_NotifyLog:{e.Msg}");
  1580. break;
  1581. case EnumLogType.FatalLog:
  1582. Logger.WriteLineError($"AIdiagSystem_NotifyLog:{e.Msg}");
  1583. break;
  1584. default:
  1585. Logger.WriteLineInfo(e.Msg);
  1586. break;
  1587. }
  1588. }
  1589. }
  1590. private DiagnosisConclusion GetDiagnosisConclusion<T>(List<T> results) where T : AIDiagnosisPerImageModel
  1591. {
  1592. var diagnosisConclusions = new List<DiagnosisConclusion>();
  1593. foreach (var imageResult in results)
  1594. {
  1595. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1596. {
  1597. var benignLabels = new List<int>();
  1598. var malignantLabels = new List<int>();
  1599. if (diagnosisResult.Organ == DiagnosisOrganEnum.Breast)
  1600. {
  1601. benignLabels = new List<int> { 1, 2, 3 };
  1602. malignantLabels = new List<int> { 4, 5, 6, 7 };
  1603. }
  1604. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Liver)
  1605. {
  1606. benignLabels = new List<int> { 1, 2, 3, 5, 6, 7, 8 };
  1607. malignantLabels = new List<int> { 4 };
  1608. }
  1609. else if (diagnosisResult.Organ == DiagnosisOrganEnum.Thyroid)
  1610. {
  1611. benignLabels = new List<int> { 1, 2, 7 };
  1612. malignantLabels = new List<int> { 3, 4, 5, 6 };
  1613. }
  1614. else if (diagnosisResult.Organ == DiagnosisOrganEnum.CarotidArtery)
  1615. {
  1616. benignLabels = new List<int> { 1 };
  1617. malignantLabels = new List<int> { };
  1618. }
  1619. var labels = diagnosisResult.DetectedObjects.Select(x => x.Label);
  1620. if (labels.Contains(0))
  1621. {
  1622. diagnosisConclusions.Add(DiagnosisConclusion.NoObviousLesion);
  1623. }
  1624. if (labels.Intersect(benignLabels).Any())
  1625. {
  1626. diagnosisConclusions.Add(DiagnosisConclusion.Benign);
  1627. }
  1628. if (labels.Intersect(malignantLabels).Any())
  1629. {
  1630. diagnosisConclusions.Add(DiagnosisConclusion.Malignant);
  1631. }
  1632. }
  1633. }
  1634. var containsBenign = diagnosisConclusions.Contains(DiagnosisConclusion.Benign);
  1635. var containsMalignant = diagnosisConclusions.Contains(DiagnosisConclusion.Malignant);
  1636. var containsNoObviousLesion = diagnosisConclusions.Contains(DiagnosisConclusion.NoObviousLesion);
  1637. if (containsBenign && containsMalignant)
  1638. {
  1639. return DiagnosisConclusion.BenignAndMalignant;
  1640. }
  1641. else if (containsBenign)
  1642. {
  1643. return DiagnosisConclusion.Benign;
  1644. }
  1645. else if (containsMalignant)
  1646. {
  1647. return DiagnosisConclusion.Malignant;
  1648. }
  1649. else if (containsNoObviousLesion)
  1650. {
  1651. return DiagnosisConclusion.NoObviousLesion;
  1652. }
  1653. else
  1654. {
  1655. return DiagnosisConclusion.Unrecognized;
  1656. }
  1657. }
  1658. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<AIDiagnosisPerImageModel> results)
  1659. {
  1660. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  1661. foreach (var imageResult in results)
  1662. {
  1663. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1664. {
  1665. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  1666. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null)
  1667. {
  1668. if (diagnosisResult.Organ == DiagnosisOrganEnum.CarotidArtery || diagnosisResult.DetectedObjects?.Any() == true)
  1669. {
  1670. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  1671. }
  1672. }
  1673. }
  1674. }
  1675. return diagnosisOrgans.Distinct().ToList();
  1676. }
  1677. private List<DiagnosisOrganEnum> GetDiagnosisOrgans(List<DiagnosisPerImageModel> results)
  1678. {
  1679. var diagnosisOrgans = new List<DiagnosisOrganEnum>();
  1680. foreach (var imageResult in results)
  1681. {
  1682. foreach (var diagnosisResult in imageResult.DiagResultsForEachOrgan)
  1683. {
  1684. var organName = Enum.GetName(typeof(DiagnosisOrganEnum), (int)diagnosisResult.Organ);
  1685. if (!string.IsNullOrWhiteSpace(organName) && diagnosisResult.Organ != DiagnosisOrganEnum.Null && diagnosisResult.DetectedObjects?.Any() == true)
  1686. {
  1687. diagnosisOrgans.Add((DiagnosisOrganEnum)diagnosisResult.Organ);
  1688. }
  1689. }
  1690. }
  1691. return diagnosisOrgans.Distinct().ToList();
  1692. }
  1693. private Dictionary<int, AIDiagResultPerImg> NormalDiagnosis(string relationCode, string fileUrl, VinnoImageData imageData)
  1694. {
  1695. var results = new Dictionary<int, AIDiagResultPerImg>();
  1696. try
  1697. {
  1698. var imageCount = imageData.ImageCount;
  1699. Logger.WriteLineInfo($"AIDiagnosisService diagnosis start, relationCode:{relationCode}, fileUrl:{fileUrl}, imageCount:{imageCount}");
  1700. if (imageCount > 0)
  1701. {
  1702. var diagId = _diagSystem.StartEvalutationOfMultipleImageBatches(imageCount);
  1703. for (var i = 0; i < imageCount; i++)
  1704. {
  1705. var image = imageData.GetImage(i);
  1706. using (var bitmap = CreateBitmap(image))
  1707. {
  1708. using (var rawImage = new RawImage(bitmap.Bytes, bitmap.Width, bitmap.Height, MapTo(bitmap.ColorType)))
  1709. {
  1710. _diagSystem.PushOneBatchOfImagesAsync(diagId, new List<RawImage> { rawImage });
  1711. }
  1712. }
  1713. }
  1714. results = _diagSystem.GetEvaluationsOfPushedMultipleImageBatches(diagId);
  1715. Logger.WriteLineInfo($"AIDiagnosisService diagnosis successfully");
  1716. }
  1717. }
  1718. catch (Exception ex)
  1719. {
  1720. Logger.WriteLineWarn($"AIDiagnosisService NormalDiagnosis err, {ex}");
  1721. }
  1722. return results;
  1723. }
  1724. /// <summary>
  1725. /// 保存AI诊断结果
  1726. /// </summary>
  1727. /// <param name="relationCode"></param>
  1728. /// <param name="fileUrl"></param>
  1729. /// <param name="diagnosisResultInfos"></param>
  1730. /// <returns></returns>
  1731. private async Task<bool> AddDiagnosisResultInfosAsync(string relationCode, string fileUrl, List<AIDiagnosisPerImageModel> diagnosisResultInfos
  1732. , DateTime? createTime = null, DateTime? updateTime = null, bool syncOthers = true)
  1733. {
  1734. try
  1735. {
  1736. var resultInfos = new List<DiagnosisResultDTO>();
  1737. for (var i = 0; i < diagnosisResultInfos.Count; i++)
  1738. {
  1739. resultInfos.Add(new DiagnosisResultDTO
  1740. {
  1741. Index = i,
  1742. DiagnosisResult = Newtonsoft.Json.JsonConvert.SerializeObject(diagnosisResultInfos[i]),
  1743. });
  1744. }
  1745. var addRequest = new AddDiagnosisResultInfosDBRequest
  1746. {
  1747. RelationCode = relationCode,
  1748. FileUrl = fileUrl,
  1749. DiagnosisResultInfos = resultInfos,
  1750. CreateTime = createTime,
  1751. UpdateTime = updateTime,
  1752. SyncOthers = syncOthers,
  1753. };
  1754. if (syncOthers)
  1755. {
  1756. _diagnosisResultDBService.AddDiagnosisResultInfosAsync(addRequest);
  1757. }
  1758. else
  1759. {
  1760. await _diagnosisResultDBService.AddDiagnosisResultInfosAsync(addRequest);
  1761. }
  1762. return true;
  1763. }
  1764. catch (Exception ex)
  1765. {
  1766. Logger.WriteLineWarn($"AIDiagnosisService AddDiagnosisResultInfosAsync err, ex:{ex}");
  1767. return false;
  1768. }
  1769. }
  1770. /// <summary>下载文件</summary>
  1771. /// <param name="fileUrl"></param>
  1772. /// <returns></returns>
  1773. private async Task<string> DownloadAsync(string fileUrl)
  1774. {
  1775. try
  1776. {
  1777. if (string.IsNullOrEmpty(fileUrl))
  1778. {
  1779. return string.Empty;
  1780. }
  1781. if (!Directory.Exists(_tempFolder))
  1782. {
  1783. Directory.CreateDirectory(_tempFolder);
  1784. }
  1785. var fileName = Path.GetFileName(fileUrl);
  1786. var tempFile = Path.Combine(_tempFolder, fileName);
  1787. if (File.Exists(tempFile))
  1788. {
  1789. return tempFile;
  1790. }
  1791. long fileSize = 0;
  1792. using (var request = new HttpRequestMessage())
  1793. {
  1794. request.RequestUri = new Uri(fileUrl);
  1795. request.Method = HttpMethod.Get;
  1796. var response = await _httpClient.SendAsync(request);
  1797. if (response != null && response.StatusCode == HttpStatusCode.OK)
  1798. {
  1799. var contentLength = response.Content.Headers.ContentLength;
  1800. fileSize = contentLength == null ? 0 : contentLength.Value;
  1801. }
  1802. }
  1803. if (fileSize <= 0)
  1804. {
  1805. throw new NotSupportedException($"fileSize is {fileSize}");
  1806. }
  1807. byte[] bytes = await _httpClient.GetByteArrayAsync(fileUrl);
  1808. File.WriteAllBytes(tempFile, bytes);
  1809. return tempFile;
  1810. }
  1811. catch (Exception ex)
  1812. {
  1813. Logger.WriteLineWarn($"DiagnosisService download file err, url: {fileUrl}, {ex}");
  1814. }
  1815. finally
  1816. {
  1817. //Logger.WriteLineInfo($"download file:{fileUrl}");
  1818. }
  1819. return string.Empty;
  1820. }
  1821. /// <summary>
  1822. /// 上传文件
  1823. /// </summary>
  1824. /// <param name="filePath"></param>
  1825. /// <param name="fileName"></param>
  1826. /// <returns></returns>
  1827. private async Task<string> UploadFileAsync(string filePath, string fileName)
  1828. {
  1829. var fileToken = "";
  1830. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1831. {
  1832. var size = fileStream.Length;
  1833. byte[] buffer = new byte[fileStream.Length];
  1834. fileStream.Read(buffer, 0, buffer.Length);
  1835. fileToken = await DoUploadFile(fileName, buffer);
  1836. }
  1837. return fileToken;
  1838. }
  1839. private async Task<Tuple<string, long>> UploadFileWithSizeAsync(string filePath, string fileName)
  1840. {
  1841. var fileToken = "";
  1842. long size = 0;
  1843. using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
  1844. {
  1845. size = fileStream.Length;
  1846. byte[] buffer = new byte[fileStream.Length];
  1847. fileStream.Read(buffer, 0, buffer.Length);
  1848. fileToken = await DoUploadFile(fileName, buffer);
  1849. }
  1850. return new Tuple<string, long>(fileToken, size);
  1851. }
  1852. /// <summary>
  1853. /// 上传文件
  1854. /// </summary>
  1855. /// <param name="filePath"></param>
  1856. /// <param name="fileName"></param>
  1857. /// <returns></returns>
  1858. private async Task<Tuple<string, long>> UploadFileTupleAsync(string filePath, string fileName)
  1859. {
  1860. var fileToken = "";
  1861. long size = 0;
  1862. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1863. {
  1864. size = fileStream.Length;
  1865. byte[] buffer = new byte[fileStream.Length];
  1866. fileStream.Read(buffer, 0, buffer.Length);
  1867. fileToken = await DoUploadFile(fileName, buffer);
  1868. }
  1869. return new Tuple<string, long>(fileToken, size);
  1870. }
  1871. /// <summary>
  1872. /// 上传文件
  1873. /// </summary>
  1874. /// <param name="fileName"></param>
  1875. /// <param name="fileData"></param>
  1876. /// <returns></returns>
  1877. async Task<string> DoUploadFile(string fileName, byte[] fileData)
  1878. {
  1879. var requestHeads = new Dictionary<string, string>();
  1880. var defaultToken = await _authenticationService.GetServerDefaultTokenAsync();
  1881. var authorizationRet = await _storageService.GetAuthorizationAsync(
  1882. new FileServiceRequest
  1883. {
  1884. Token = defaultToken,
  1885. FileName = fileName,
  1886. });
  1887. requestHeads.Add("Authorization", authorizationRet.Authorization);
  1888. var fileUrl = authorizationRet.StorageUrl;
  1889. Logger.WriteLineInfo($"DoUploadFile fileUrl:{fileUrl}");
  1890. using (var request = new HttpRequestMessage())
  1891. {
  1892. var fileExtension = Path.GetExtension(fileName);
  1893. var mimeType = FileHelper.GetMimeType(fileExtension);
  1894. var contentType = MediaTypeHeaderValue.Parse(mimeType);
  1895. using (UploadContent content = new UploadContent(fileData, contentType))
  1896. {
  1897. request.RequestUri = new Uri(fileUrl);
  1898. request.Method = HttpMethod.Put;
  1899. request.Content = content;
  1900. foreach (var head in requestHeads)
  1901. {
  1902. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  1903. }
  1904. var result = await ExecuteRequest(request);
  1905. if (!result)
  1906. {
  1907. throw new Exception("Upload file failed!");
  1908. }
  1909. }
  1910. }
  1911. return fileUrl;
  1912. }
  1913. private UploadFileTypeEnum GetUploadFileType(string fileName)
  1914. {
  1915. if (fileName.EndsWith(".vid"))
  1916. {
  1917. return UploadFileTypeEnum.VID;
  1918. }
  1919. else if (fileName.EndsWith(".jpg"))
  1920. {
  1921. return UploadFileTypeEnum.JPG;
  1922. }
  1923. else
  1924. {
  1925. return UploadFileTypeEnum.MP4;
  1926. }
  1927. }
  1928. /// <summary>
  1929. /// 执行请求
  1930. /// </summary>
  1931. /// <param name="httpRequestMessage"></param>
  1932. /// <typeparam name="T"></typeparam>
  1933. /// <returns></returns>
  1934. public async Task<bool> ExecuteRequest(HttpRequestMessage httpRequestMessage)
  1935. {
  1936. try
  1937. {
  1938. var response = await _httpClient.SendAsync(httpRequestMessage);
  1939. if (response != null && response.StatusCode == HttpStatusCode.OK)
  1940. {
  1941. return true;
  1942. }
  1943. return false;
  1944. }
  1945. catch (Exception ex)
  1946. {
  1947. throw ex;
  1948. }
  1949. }
  1950. private List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D> PointToDto(Point2D[] points)
  1951. {
  1952. var dstContours = new List<WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D>();
  1953. foreach (var p in points)
  1954. {
  1955. dstContours.Add(new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1956. {
  1957. X = p.X,
  1958. Y = p.Y
  1959. });
  1960. }
  1961. return dstContours;
  1962. }
  1963. private List<DiagnosisKeyPointDTO> KeyPointToDto(KeyPointInfo[] points)
  1964. {
  1965. var dstKeys = new List<DiagnosisKeyPointDTO>();
  1966. foreach (var point in points)
  1967. {
  1968. dstKeys.Add(new DiagnosisKeyPointDTO
  1969. {
  1970. Type = (DiagnosisKeyPointType)point.Type,
  1971. IndexInContour = point.IndexInContour,
  1972. Point = new WingInterfaceLibrary.DTO.Comment.AIDiagnosisPoint2D
  1973. {
  1974. X = point.Point.X,
  1975. Y = point.Point.Y,
  1976. },
  1977. });
  1978. }
  1979. return dstKeys;
  1980. }
  1981. /// <summary>
  1982. /// 创建缩略图文件
  1983. /// </summary>
  1984. /// <param name="vinnoImage"></param>
  1985. /// <param name="requestHeight"></param>
  1986. /// <param name="quality"></param>
  1987. /// <param name="fullPath"></param>
  1988. private void CreateThumbnailFile(string filePath, int requestHeight, int quality, string fullPath)
  1989. {
  1990. using (var fileStream = new FileStream(filePath, FileMode.Open))
  1991. {
  1992. var size = fileStream.Length;
  1993. byte[] buffer = new byte[fileStream.Length];
  1994. fileStream.Read(buffer, 0, buffer.Length);
  1995. byte[] compressedData = buffer;
  1996. using (var ms = new MemoryStream(compressedData))
  1997. {
  1998. using (var image = SKBitmap.Decode(ms))
  1999. {
  2000. var ratio = (double)requestHeight / image.Height;
  2001. if (ratio < 1)
  2002. {
  2003. var width = (int)(image.Width * ratio);
  2004. var height = (int)(image.Height * ratio);
  2005. SKBitmap tempImage = image;
  2006. if (image.Width != width || image.Height != height)
  2007. {
  2008. tempImage = image.Resize(new SKImageInfo(width, height), SKFilterQuality.High);
  2009. }
  2010. try
  2011. {
  2012. using (var map = new SKPixmap(new SKImageInfo(tempImage.Width, tempImage.Height, tempImage.ColorType), tempImage.GetPixels()))
  2013. {
  2014. using (var stream = new SKDynamicMemoryWStream())
  2015. {
  2016. SKPixmap.Encode(stream, map, SKEncodedImageFormat.Jpeg, quality);
  2017. compressedData = stream.CopyToData().ToArray();
  2018. }
  2019. }
  2020. }
  2021. catch (Exception ex)
  2022. {
  2023. throw ex;
  2024. }
  2025. finally
  2026. {
  2027. tempImage.Dispose();
  2028. }
  2029. }
  2030. }
  2031. }
  2032. using (var fs = File.Create(fullPath))
  2033. {
  2034. fs.Write(compressedData, 0, compressedData.Length);
  2035. }
  2036. }
  2037. }
  2038. /// <summary>
  2039. /// 是否与当前存储匹配
  2040. /// </summary>
  2041. /// <param name="value"></param>
  2042. /// <returns></returns>
  2043. private bool MatchCurrentStorage(string value)
  2044. {
  2045. if (!string.IsNullOrWhiteSpace(value))
  2046. {
  2047. var cfsServerUrl = EnvironmentConfigs.Storage.CFSServerUrl.Replace("http://", "").Replace("https://", "");
  2048. var cdnServerUrl = EnvironmentConfigs.Storage.CDNServerUrl.Replace("http://", "").Replace("https://", "");
  2049. return value.Contains(cfsServerUrl) || value.Contains(cdnServerUrl);
  2050. }
  2051. return false;
  2052. }
  2053. }
  2054. }