ResampleModel.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. using Emgu.CV;
  2. using Emgu.CV.CvEnum;
  3. using Emgu.CV.Structure;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Threading.Tasks;
  10. using System.IO.Compression;
  11. using System.Text;
  12. using WingAIDiagnosisService.Carotid.Utilities;
  13. using WingAIDiagnosisService.Carotid.Utilities.DetectPlaque;
  14. using WingAIDiagnosisService.Manage;
  15. using Vinno.vCloud.Common.Vid2;
  16. using WingAIDiagnosisService.Carotid.CarotidInterFacesData;
  17. using WingServerCommon.Log;
  18. using Vinno.vCloud.Common.Vid2.Visuals;
  19. using System.Runtime.InteropServices;
  20. using AI.DiagSystem;
  21. namespace WingAIDiagnosisService.Carotid
  22. {
  23. /// <summary>
  24. /// 模型重采样错误码
  25. /// </summary>
  26. public enum ResampleErrorCode
  27. {
  28. Success = 1000,
  29. InvalidVidFile,
  30. TooFewSourceImages,
  31. SaveMdlFileFailed,
  32. SaveSurfaceFileFailed,
  33. Fail
  34. }
  35. public class ResampleResult
  36. {
  37. public ResampleErrorCode ResampleErrorCode { get; }
  38. public CarotidAIMeasureResult CarotidAIMeasureResult { get; }
  39. public ResampleResult(ResampleErrorCode resampleErrorCode, CarotidAIMeasureResult carotidAIMeasureResult)
  40. {
  41. ResampleErrorCode = resampleErrorCode;
  42. CarotidAIMeasureResult = carotidAIMeasureResult;
  43. }
  44. public ResampleResult(ResampleErrorCode resampleErrorCode)
  45. {
  46. ResampleErrorCode = resampleErrorCode;
  47. CarotidAIMeasureResult = new CarotidAIMeasureResult(new DetectIntimaResult(false), new DetectPlaqueResult(false), false);
  48. }
  49. }
  50. struct ResampleInputData
  51. {
  52. public const string MdlZipFileVersion = "V1";
  53. public string MdlFilePath { get; set; }
  54. public string MdlZipFilePath { get; set; }
  55. public string SurfaceFilePath { get; set; }
  56. public string BaseAIImagePath { get; set; }
  57. public string YShapeAIImagePath { get; set; }
  58. public string PlaqueAIImagePath { get; set; }
  59. public CarotidScanType ScanType { get; set; }
  60. public float ScanDistance { get; set; }
  61. public CarotidScanDirection CarotidScanDirection { get; set; }
  62. }
  63. class ResampleModel : IDisposable
  64. {
  65. //新建模型的推荐宽度
  66. private int RecommendResizeX = 600;
  67. //从vid文件中读到的模型数据
  68. private byte[][] _modelDataSrc;
  69. //重采样生成的模型数据
  70. private byte[][] _modelDataDst;
  71. private byte[][] _surfacePicList;
  72. //最多线程数
  73. private readonly ParallelOptions _parallelOption = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount / 2 };
  74. //图像个数
  75. private int _srcLengthZ;
  76. //重采样后图像的宽
  77. //此宽度为剪掉两侧黑色区域的宽
  78. private int _dstLengthX;
  79. //重采样后图像的高
  80. private int _dstLengthY;
  81. //重采样后图像个数
  82. private int _dstLengthZ;
  83. //病人相关信息
  84. private byte[] _patientInformation;
  85. //扫描深度
  86. private float _scanDepth;
  87. private bool _isDoingResample = false;
  88. private static ResampleModel _instance;
  89. /// <summary>
  90. /// Instance of CntkDetect detector.
  91. /// </summary>
  92. public static ResampleModel Instance
  93. {
  94. get => _instance ?? (_instance = new ResampleModel());
  95. }
  96. /// <summary>
  97. /// 执行模型重采样的入口函数
  98. /// </summary>
  99. /// <returns></returns>
  100. public ResampleResult Resample(VinnoImageData vinnoImageData, ResampleInputData inputData, EnumPerformance performance)
  101. {
  102. try
  103. {
  104. //This is a walkaround : vid's first frame seldom have no depth info.
  105. var middleCount = vinnoImageData.ImageCount / 2;
  106. var vinnoImage = vinnoImageData.GetImage(middleCount);
  107. var resampleResult = PreCheckData(vinnoImageData, vinnoImage);
  108. if (resampleResult.ResampleErrorCode != ResampleErrorCode.Success)
  109. {
  110. return resampleResult;
  111. }
  112. var initRes = Initialize(vinnoImage, vinnoImageData, inputData.ScanType);
  113. if (initRes.ErrorCode == ResampleErrorCode.Success)
  114. {
  115. var result = ResampleWorkflow(vinnoImageData, inputData, initRes);
  116. //保存模型数据
  117. if (ResampleErrorCode.Success == result)
  118. {
  119. result = SaveResampleModel(inputData.MdlFilePath, inputData.MdlZipFilePath);
  120. }
  121. if (result != ResampleErrorCode.Success)
  122. {
  123. Logger.WriteLineWarn($"Resample Model SaveResampleModel failed.");
  124. return new ResampleResult(result);
  125. }
  126. //保存表面数据
  127. if (ResampleErrorCode.Success == result)
  128. {
  129. result = SaveSurface(inputData.SurfaceFilePath);
  130. }
  131. var aiResult = new CarotidAIMeasureResult(new DetectIntimaResult(false), new DetectPlaqueResult(false), false);
  132. bool isReadedModel = false;
  133. isReadedModel = GetModelVesselAndPlaque.Instance.InitializationStatus();
  134. if (isReadedModel == false)
  135. {
  136. GetModelVesselAndPlaque.Instance.Initialization(performance, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks"));
  137. isReadedModel = true;
  138. // //Ai检测血管和斑块
  139. // //读取模型文件
  140. // string plaqueModelName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks", "Carotid",
  141. // Environment.OSVersion.Platform == PlatformID.Unix ? "PlaqueModel" : "PlaqueModel.emd");
  142. // string arteryModelName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks", "Carotid",
  143. // Environment.OSVersion.Platform == PlatformID.Unix ? "ArteryModel" : "ArteryModel.emd");
  144. // isReadedModel = GetModelVesselAndPlaque.Instance.Initialization(EnumPerformance.Medium, plaqueModelName);
  145. }
  146. List<Vector3D> points = new List<Vector3D>();
  147. if (isReadedModel)
  148. {
  149. //初始胡模型------------------------------------------------
  150. SliceHelper.Instance.SetModelSource(inputData.MdlFilePath, _dstLengthX, _dstLengthY, _dstLengthZ);
  151. var physicalPerPixel = _scanDepth / SliceHelper.Instance.GetModeSzie().ModelLengthY;
  152. GetModelVesselAndPlaque.Instance.DoDetect(SliceHelper.Instance.GetModelData(), SliceHelper.Instance.GetModeSzie());
  153. //获得内膜切面
  154. DetectIntimaResult detectIntimaResult = new DetectIntimaResult();
  155. detectIntimaResult = GetIntimaImage.Instance.GetImage(inputData.BaseAIImagePath, physicalPerPixel);
  156. //内膜平面上的点
  157. if (detectIntimaResult.IsSuccess)
  158. {
  159. var aiPlanePoint = GetIntimaImage.Instance.GetIntimaPlanePoint();
  160. if (aiPlanePoint.Count > 2)
  161. {
  162. points = new List<Vector3D>(aiPlanePoint);
  163. WriteIntimaAiPoint(inputData.SurfaceFilePath, points);
  164. }
  165. //获得Y型切面
  166. //var isGetedYImage = GetPlaneOfVesselY.Instance.GetPlane(SliceHelper.Instance.GetModelData(), SliceHelper.Instance.GetModeSzie(), inputData.YShapeAIImagePath);
  167. var isGetedYImage = false;
  168. //获得最大斑块切面
  169. DetectPlaqueResult detectPlaqueResult = CarotidPlaqueAutoDetection.Instance.GetPlaqueImage(SliceHelper.Instance.GetModelData(), SliceHelper.Instance.GetModeSzie(), inputData.PlaqueAIImagePath, physicalPerPixel * 10);
  170. aiResult = new CarotidAIMeasureResult(detectIntimaResult, detectPlaqueResult, isGetedYImage);
  171. }
  172. }
  173. else
  174. {
  175. Logger.WriteLineWarn($"Read cntk Model failed!");
  176. }
  177. //保存模型数据
  178. if (ResampleErrorCode.Success == result)
  179. {
  180. Logger.WriteLineInfo($"Resample Model success.");
  181. return new ResampleResult(result, aiResult);
  182. }
  183. }
  184. Logger.WriteLineInfo($"Resample Model InitializeData failed!");
  185. return new ResampleResult(ResampleErrorCode.Fail);
  186. }
  187. catch (Exception e)
  188. {
  189. Logger.WriteLineInfo($"Resample Model failed.");
  190. Logger.WriteLineError($"Resample Model have an error{e}");
  191. return new ResampleResult(ResampleErrorCode.Fail);
  192. }
  193. finally
  194. {
  195. Instance.Dispose();
  196. //因为每个模型大小不一样,slicehelp的复用性不大,所有释放掉
  197. SliceHelper.Instance.Dispose();
  198. GC.Collect();
  199. Instance._isDoingResample = false;
  200. }
  201. }
  202. private static ResampleResult PreCheckData(VinnoImageData vinnoImageData, VinnoImage vinnoImage)
  203. {
  204. if (Instance._isDoingResample == true)
  205. {
  206. Logger.WriteLineWarn("Resample was called multiple times.");
  207. return new ResampleResult(ResampleErrorCode.Fail);
  208. }
  209. Instance._isDoingResample = true;
  210. Logger.WriteLineInfo($"Begin Resample Model.");
  211. if (vinnoImageData.ImageCount < 3)
  212. {
  213. Logger.WriteLineWarn($"Resample Model too few SourceImages");
  214. return new ResampleResult(ResampleErrorCode.TooFewSourceImages);
  215. }
  216. if (vinnoImage == null || !vinnoImage.Visuals.Any())
  217. {
  218. Logger.WriteLineWarn($"Resample Model vinnoImage is null.");
  219. return new ResampleResult(ResampleErrorCode.InvalidVidFile);
  220. }
  221. return new ResampleResult(ResampleErrorCode.Success);
  222. }
  223. private bool WriteIntimaAiPoint(string surfaceFilePath, List<Vector3D> points)
  224. {
  225. using var stream = File.OpenWrite(surfaceFilePath);
  226. stream.Position = stream.Length;
  227. //写人切割面上的点
  228. if (points.Count > 2)
  229. {
  230. var aiClip = "AIClip";
  231. var data = System.Text.Encoding.Unicode.GetBytes(aiClip);
  232. stream.Write(BitConverter.GetBytes(data.Length), 0, sizeof(int));
  233. stream.Write(data, 0, data.Length);
  234. for (var i = 0; i < 3; ++i)
  235. {
  236. stream.Write(BitConverter.GetBytes(ChangePrecision(points[i].X)), 0, sizeof(float));
  237. stream.Write(BitConverter.GetBytes(ChangePrecision(points[i].Y)), 0, sizeof(float));
  238. stream.Write(BitConverter.GetBytes(ChangePrecision(points[i].Z)), 0, sizeof(float));
  239. }
  240. }
  241. return true;
  242. }
  243. private float GetScanDepth(VinnoImage vinnoImage)
  244. {
  245. if (vinnoImage.Visuals.First() is Vinno2DVisual visual)
  246. {
  247. if (visual.PhysicalCoordinates.ContainsKey(VinnoVisualAreaType.Tissue))
  248. {
  249. var physicalCoordinate = visual.PhysicalCoordinates[VinnoVisualAreaType.Tissue];
  250. if (physicalCoordinate is VinnoTissuePhysicalCoordinate coordinate)
  251. return (float)(coordinate.DepthEnd - coordinate.DepthStart);
  252. }
  253. }
  254. return 0;
  255. }
  256. private ResampleErrorCode ResampleWorkflow(VinnoImageData vinnoImageData, ResampleInputData inputData, ResampleInitResult result)
  257. {
  258. ResampleErrorCode errorCode = PreProcessSrcImages(vinnoImageData, result);
  259. if (ResampleErrorCode.Success == errorCode)
  260. {
  261. errorCode = ModelResample(inputData.ScanDistance);
  262. if (ResampleErrorCode.Success == errorCode)
  263. {
  264. errorCode = GetSurfacePic();
  265. }
  266. }
  267. return errorCode;
  268. }
  269. /// <summary>
  270. /// 释放资源
  271. /// </summary>
  272. public void Dispose()
  273. {
  274. _srcLengthZ = 0;
  275. _dstLengthX = 0;
  276. _dstLengthY = 0;
  277. _dstLengthZ = 0;
  278. _scanDepth = 0;
  279. _patientInformation = null;
  280. _surfacePicList = null;
  281. _modelDataSrc = null;
  282. _modelDataDst = null;
  283. }
  284. private ResampleInitResult Initialize(VinnoImage img, VinnoImageData imageData, CarotidScanType scanType)
  285. {
  286. ResampleInitResult result = new ResampleInitResult
  287. {
  288. ErrorCode = ResampleErrorCode.Fail
  289. };
  290. try
  291. {
  292. result.ResizeSize = GetResizeSize(img);
  293. using (var mat = new Mat())
  294. {
  295. using (var resizeImage = new Image<Gray, byte>(result.ResizeSize))
  296. {
  297. CvInvoke.Imdecode(img.ImageData, ImreadModes.Grayscale, mat);
  298. CvInvoke.Resize(mat, resizeImage, result.ResizeSize);
  299. //result.Edge = ImageEdgeDetector.DetectImageEdge(resizeImage.Mat);
  300. result.Edge = ImageCrop.CropImageByInfo(resizeImage.Mat, img, _scanDepth);
  301. }
  302. }
  303. if (result.Edge != null)
  304. {
  305. _dstLengthX = result.Edge.Width;
  306. _dstLengthY = result.Edge.Height;
  307. _modelDataSrc = new byte[imageData.ImageCount][];
  308. _scanDepth = GetScanDepth(img);
  309. _patientInformation = new VinnoCarotid3DPhysicalData(scanType == CarotidScanType.CarotidLeft ? CarotidType.Left : CarotidType.Right,
  310. imageData.Probe, img.Visuals).ToBytes();
  311. result.ErrorCode = ResampleErrorCode.Success;
  312. }
  313. }
  314. catch (Exception ex)
  315. {
  316. Logger.WriteLineError($"Resample initialize failed:{ex}");
  317. }
  318. return result;
  319. }
  320. /// <summary>
  321. /// Reisze bitmaps and convert to gray8 and crop by image edge.
  322. /// </summary>
  323. /// <param name="vinnoImageData">vinno image data</param>
  324. /// <returns>Resample error code</returns>
  325. private ResampleErrorCode PreProcessSrcImages(VinnoImageData vinnoImageData, ResampleInitResult initRes)
  326. {
  327. var roiRect = new Rectangle(initRes.Edge.Left, 0, initRes.Edge.Width, initRes.Edge.Height);
  328. try
  329. {
  330. using (var mat = new Mat())
  331. {
  332. using (var resizeMat = new Mat(initRes.ResizeSize, DepthType.Cv8U, 1))
  333. {
  334. _srcLengthZ = 0;
  335. for (int i = 0; i < vinnoImageData.ImageCount; ++i)
  336. {
  337. var imageBuffer = vinnoImageData.GetImage(i).ImageData;
  338. if (imageBuffer.Length > 0)
  339. {
  340. CvInvoke.Imdecode(imageBuffer, ImreadModes.Grayscale, mat);
  341. CvInvoke.Resize(mat, resizeMat, initRes.ResizeSize);
  342. using var cropMat = new Mat(resizeMat, roiRect);
  343. _modelDataSrc[_srcLengthZ] = cropMat.ToImage<Gray, byte>().Bytes;
  344. _srcLengthZ++;
  345. }
  346. }
  347. }
  348. }
  349. }
  350. catch (Exception ex)
  351. {
  352. Logger.WriteLineError($"Resize bitmap error when resample vid: {ex}");
  353. return ResampleErrorCode.Fail;
  354. }
  355. if (_srcLengthZ < DiagnosisHelper.CarotidMinImageCounts)
  356. {
  357. Logger.WriteLineWarn($"Too few images,total iamge num " + vinnoImageData.ImageCount + ", actual number of images " + _srcLengthZ + ".");
  358. return ResampleErrorCode.TooFewSourceImages;
  359. }
  360. return ResampleErrorCode.Success;
  361. }
  362. private Size GetResizeSize(VinnoImage img)
  363. {
  364. if (img.Width > RecommendResizeX)
  365. {
  366. double ratio = (double)img.Width / RecommendResizeX;
  367. return new Size(RecommendResizeX, (int)Math.Ceiling(img.Height / ratio));
  368. }
  369. else
  370. {
  371. var offset = img.Width % 4;
  372. var x = offset == 0 ? img.Width : img.Width - offset;
  373. return new Size(x, img.Height);
  374. }
  375. }
  376. /// <summary>
  377. /// 模型重采样
  378. /// </summary>
  379. /// <returns></returns>
  380. private ResampleErrorCode ModelResample(float scanDistance)
  381. {
  382. try
  383. {
  384. //resize后----------------------------------------------------------------------------------------
  385. int widthHeightDst = _dstLengthX * _dstLengthY;
  386. _dstLengthZ = (int)Math.Ceiling(scanDistance * _dstLengthY / _scanDepth);
  387. _modelDataDst = new byte[_dstLengthZ][];
  388. for (int i = 0; i < _dstLengthZ; i++)
  389. {
  390. _modelDataDst[i] = new byte[widthHeightDst];
  391. }
  392. int zSrc = _srcLengthZ - 1;
  393. int zDst = _dstLengthZ - 1;
  394. double ratioZ = (double)zSrc / zDst;
  395. double zSrcDistancePerImage = scanDistance / zSrc;
  396. double zDstDistancePerImage = scanDistance / zDst;
  397. Parallel.For(0, _dstLengthZ - 1, _parallelOption, z =>
  398. {
  399. double dstDistance = zDstDistancePerImage * z;
  400. //求取值区间
  401. var m1 = (int)Math.Floor(ratioZ * z);
  402. var m2 = m1 + 1;
  403. var d1 = dstDistance - m1 * zSrcDistancePerImage;
  404. var d2 = m2 * zSrcDistancePerImage - dstDistance;
  405. for (int cursor = 0; cursor < widthHeightDst; cursor++)
  406. {
  407. var grayM1 = _modelDataSrc[m1][cursor];
  408. var grayM2 = _modelDataSrc[m2][cursor];
  409. _modelDataDst[z][cursor] = (byte)((d2 * grayM1 + d1 * grayM2) / (d1 + d2));
  410. }
  411. });
  412. //拷贝最后一张图像
  413. Array.Copy(_modelDataSrc[zSrc], _modelDataDst[zDst], widthHeightDst);
  414. return ResampleErrorCode.Success;
  415. }
  416. catch (Exception e)
  417. {
  418. Logger.WriteLineError($"An error occurred during the model resampling process{e}" + "_dstLengthZ=" + _dstLengthZ + ",_srcLengthZ=" + _srcLengthZ + "," +
  419. "dstCount=" + _modelDataDst[_dstLengthZ - 1].Length + ",srcCount=" + _modelDataSrc[_srcLengthZ - 1].Length);
  420. return ResampleErrorCode.SaveMdlFileFailed;
  421. }
  422. }
  423. /// <summary>
  424. /// 保存重采样后数据
  425. /// </summary>
  426. /// <returns></returns>
  427. private ResampleErrorCode SaveResampleModel(string mdlFilePath, string mdlZipFilePath)
  428. {
  429. try
  430. {
  431. using (var stream = File.OpenWrite(mdlFilePath))
  432. {
  433. //写入图像张数
  434. stream.Write(BitConverter.GetBytes(_dstLengthZ), 0, sizeof(int));
  435. using var imageTemp = new Image<Gray, byte>(_dstLengthX, _dstLengthY);
  436. //写入图像
  437. for (var i = 0; i < _dstLengthZ; ++i)
  438. {
  439. WritePicFor(imageTemp, stream, _modelDataDst[i]);
  440. }
  441. }
  442. using (var fileStream = new FileStream(mdlZipFilePath, FileMode.Create))
  443. {
  444. var data = Encoding.Unicode.GetBytes(ResampleInputData.MdlZipFileVersion);
  445. fileStream.Write(BitConverter.GetBytes(data.Length), 0, sizeof(int));
  446. fileStream.Write(data, 0, data.Length);
  447. var bytes = Compress(mdlFilePath);
  448. fileStream.Write(BitConverter.GetBytes(bytes.Length), 0, sizeof(int));
  449. fileStream.Write(bytes, 0, bytes.Length);
  450. }
  451. return ResampleErrorCode.Success;
  452. }
  453. catch (Exception e)
  454. {
  455. Logger.WriteLineError($"Save the rsampled model error{e}");
  456. return ResampleErrorCode.SaveMdlFileFailed;
  457. }
  458. }
  459. private static byte[] Compress(string mdlFile)
  460. {
  461. using (var memoryStream = new MemoryStream())
  462. {
  463. using (var compressionStream = new GZipStream(memoryStream, CompressionMode.Compress))
  464. {
  465. var bytes = File.ReadAllBytes(mdlFile);
  466. compressionStream.Write(bytes, 0, bytes.Length);
  467. compressionStream.Close();
  468. return memoryStream.ToArray();
  469. }
  470. }
  471. }
  472. /// <summary>
  473. /// 获得模型的表面图
  474. /// </summary>
  475. /// <returns></returns>
  476. private ResampleErrorCode GetSurfacePic()
  477. {
  478. try
  479. {
  480. _surfacePicList = new byte[6][];
  481. _surfacePicList[0] = new byte[_dstLengthZ * _dstLengthY];
  482. _surfacePicList[1] = new byte[_dstLengthZ * _dstLengthY];
  483. _surfacePicList[2] = new byte[_dstLengthX * _dstLengthY];
  484. _surfacePicList[3] = new byte[_dstLengthX * _dstLengthY];
  485. _surfacePicList[4] = new byte[_dstLengthX * _dstLengthZ];
  486. _surfacePicList[5] = new byte[_dstLengthX * _dstLengthZ];
  487. Parallel.Invoke(
  488. () => CreateRightPic(_surfacePicList[0]),//pic1
  489. () => CreateLeftPic(_surfacePicList[1]),//pic2
  490. () => CreateBehindPic(_surfacePicList[2]),//pic3
  491. () => CreateFrontPic(_surfacePicList[3]),//第一张图,//pic4
  492. () => CreateTopPic(_surfacePicList[4]),//pic5
  493. () => CreateBottomPic(_surfacePicList[5]));//pic6
  494. return ResampleErrorCode.Success;
  495. }
  496. catch (Exception e)
  497. {
  498. Logger.WriteLineError($"Get the model surface picture error!{e}");
  499. return ResampleErrorCode.SaveSurfaceFileFailed;
  500. }
  501. }
  502. private void CreateBottomPic(byte[] pic)
  503. {
  504. //6
  505. int stride = 0;
  506. var temp = _dstLengthX * (_dstLengthY - 1);
  507. for (int j = 0; j != _dstLengthZ; ++j)
  508. {
  509. for (int i = 0; i < _dstLengthX; ++i)
  510. {
  511. pic[stride + i] = _modelDataDst[j][temp + i];
  512. }
  513. stride += _dstLengthX;
  514. }
  515. }
  516. private void CreateTopPic(byte[] pic)
  517. {
  518. //5
  519. int stride = 0;
  520. for (int j = 0; j != _dstLengthZ; ++j)
  521. {
  522. var j1 = _dstLengthZ - 1 - j;
  523. for (int i = 0; i < _dstLengthX; ++i)
  524. {
  525. pic[stride + i] = _modelDataDst[j1][i];
  526. }
  527. stride += _dstLengthX;
  528. }
  529. }
  530. private void CreateFrontPic(byte[] pic)
  531. {
  532. _modelDataDst[0].CopyTo(pic, 0);
  533. }
  534. private void CreateBehindPic(byte[] pic)
  535. {
  536. int stride = 0;
  537. int zNum = _dstLengthZ - 1;
  538. //最后一帧图像,围绕x轴反转
  539. for (int j = _dstLengthY - 1; j != -1; --j)
  540. {
  541. var count = j * _dstLengthX;
  542. for (int i = 0; i != _dstLengthX; ++i)
  543. {
  544. pic[stride + i] = _modelDataDst[zNum][count];
  545. ++count;
  546. }
  547. stride += _dstLengthX;
  548. }
  549. }
  550. private void CreateLeftPic(byte[] pic)
  551. {
  552. //贴图2
  553. for (int i = 0; i < _dstLengthY; ++i)
  554. {
  555. var stride = i * _dstLengthZ;
  556. for (int j = 0; j < _dstLengthZ; ++j)
  557. {
  558. var stride1 = stride + _dstLengthZ - j - 1;
  559. pic[stride1] = _modelDataDst[j][_dstLengthX * i];
  560. }
  561. }
  562. }
  563. private void CreateRightPic(byte[] pic)
  564. {
  565. //贴图1
  566. for (int j = 0; j < _dstLengthZ; ++j)
  567. {
  568. for (int i = 0; i < _dstLengthY; ++i)
  569. {
  570. var stride = i * _dstLengthZ + j;
  571. pic[stride] = _modelDataDst[j][_dstLengthX * i + _dstLengthX - 1];
  572. }
  573. }
  574. }
  575. private bool WritePicFor(Image<Gray, byte> imageTemp, FileStream stream, byte[] picData)
  576. {
  577. try
  578. {
  579. Marshal.Copy(picData, 0, imageTemp.Mat.DataPointer, picData.Length);
  580. using var buf = new Emgu.CV.Util.VectorOfByte();
  581. CvInvoke.Imencode(".jpg", imageTemp, buf,
  582. new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  583. var jpgByte = buf.ToArray();
  584. stream.Write(BitConverter.GetBytes(jpgByte.Length), 0, sizeof(int));
  585. stream.Write(jpgByte, 0, jpgByte.Length);
  586. return true;
  587. }
  588. catch (Exception e)
  589. {
  590. Logger.WriteLineError($"save one surface pic failed {e}");
  591. return false;
  592. }
  593. }
  594. private bool WritePic(FileStream stream, byte[] picData, int width, int height)
  595. {
  596. try
  597. {
  598. using var imageTemp = new Mat(height, width, DepthType.Cv8U, 1);
  599. Marshal.Copy(picData, 0, imageTemp.DataPointer, picData.Length);
  600. var buf = new Emgu.CV.Util.VectorOfByte();
  601. using var image = imageTemp.ToImage<Gray, byte>();
  602. CvInvoke.Imencode(".jpg", image, buf,
  603. new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, 80));
  604. var jpgByte = buf.ToArray();
  605. stream.Write(BitConverter.GetBytes(jpgByte.Length), 0, sizeof(int));
  606. stream.Write(jpgByte, 0, jpgByte.Length);
  607. return true;
  608. }
  609. catch (Exception e)
  610. {
  611. Logger.WriteLineError($"save one surface pic failed {e}");
  612. return false;
  613. }
  614. }
  615. /// <summary>
  616. /// 保存表面图
  617. /// </summary>
  618. /// <returns></returns>
  619. private ResampleErrorCode SaveSurface(string surfaceFilePath)
  620. {
  621. try
  622. {
  623. using (var stream = File.OpenWrite(surfaceFilePath))
  624. {
  625. //模型的长宽高
  626. stream.Write(BitConverter.GetBytes(_dstLengthX), 0, sizeof(int));
  627. stream.Write(BitConverter.GetBytes(_dstLengthY), 0, sizeof(int));
  628. stream.Write(BitConverter.GetBytes(_dstLengthZ), 0, sizeof(int));
  629. //写入病人信息
  630. stream.Write(BitConverter.GetBytes(_patientInformation.Length), 0, sizeof(int));
  631. stream.Write(_patientInformation, 0, _patientInformation.Length);
  632. //写入图像张数
  633. stream.Write(BitConverter.GetBytes(6), 0, sizeof(int));
  634. var dic = new Dictionary<int, Size>
  635. {
  636. {0, new Size(_dstLengthZ, _dstLengthY)},
  637. {1, new Size(_dstLengthZ, _dstLengthY)},
  638. {2, new Size(_dstLengthX, _dstLengthY)},
  639. {3, new Size(_dstLengthX, _dstLengthY)},
  640. {4, new Size(_dstLengthX, _dstLengthZ)},
  641. {5, new Size(_dstLengthX, _dstLengthZ)}
  642. };
  643. //写入图片
  644. foreach (var item in dic)
  645. {
  646. if (!WritePic(stream, _surfacePicList[item.Key], item.Value.Width, item.Value.Height))
  647. {
  648. return ResampleErrorCode.SaveSurfaceFileFailed;
  649. }
  650. }
  651. }
  652. return ResampleErrorCode.Success;
  653. }
  654. catch (Exception e)
  655. {
  656. Logger.WriteLineError($"save surface pic failed {e}");
  657. return ResampleErrorCode.SaveSurfaceFileFailed;
  658. }
  659. }
  660. private float ChangePrecision(double value)
  661. {
  662. return (float)Math.Round(value, 3, MidpointRounding.AwayFromZero);
  663. }
  664. }
  665. }