AIReconstructor.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using AI.Common.Log;
  7. using AI.Common;
  8. using AI.Common.Tools;
  9. namespace AI.Reconstruction
  10. {
  11. /// <summary>
  12. /// 专门负责三维重建相关工作的一个类
  13. /// </summary>
  14. public class AIReconstructor
  15. {
  16. #region internal
  17. /// <summary>
  18. /// 图像读入时的模式
  19. /// </summary>
  20. public enum EnumImreadMode
  21. {
  22. Unchanged = -1,
  23. Grayscale = 0,
  24. Color = 1,
  25. AnyDepth = 2,
  26. AnyColor = 4,
  27. LoadGdal = 8,
  28. }
  29. /// <summary>
  30. /// 用来做体数据预处理的一些信息
  31. /// </summary>
  32. internal struct StructVolumeDataPreProcessorInfo
  33. {
  34. private int _origImgWidth;
  35. private int _origImgHeight;
  36. private int _origImgCount;
  37. private int _colorType;
  38. private Rect _cropRect;
  39. private int _desiredImgWidth;
  40. private int _desiredImgHeight;
  41. private int _desiredImgCount;
  42. private IntPtr _dataPointer;
  43. public int OrigImgWidth
  44. {
  45. get => _origImgWidth;
  46. set => _origImgWidth = value;
  47. }
  48. public int OrigImgHeight
  49. {
  50. get => _origImgHeight;
  51. set => _origImgHeight = value;
  52. }
  53. public int OrigImgCount
  54. {
  55. get => _origImgCount;
  56. set => _origImgCount = value;
  57. }
  58. public int ColorType
  59. {
  60. get => _colorType;
  61. set => _colorType = value;
  62. }
  63. public Rect CropRect
  64. {
  65. get => _cropRect;
  66. set => _cropRect = value;
  67. }
  68. public int DesiredImgWidth
  69. {
  70. get => _desiredImgWidth;
  71. set => _desiredImgWidth = value;
  72. }
  73. public int DesiredImgHeight
  74. {
  75. get => _desiredImgHeight;
  76. set => _desiredImgHeight = value;
  77. }
  78. public int DesiredImgCount
  79. {
  80. get => _desiredImgCount;
  81. set => _desiredImgCount = value;
  82. }
  83. public IntPtr DataPointer
  84. {
  85. get => _dataPointer;
  86. set => _dataPointer = value;
  87. }
  88. public StructVolumeDataPreProcessorInfo(int origImgWidth, int origImgHeight, int origImgCount, EnumColorType colorType,
  89. Rect cropRect, int desiredImgWidth, int desiredImgHeight, int desiredImgCount, IntPtr dataPointer)
  90. {
  91. _origImgWidth = origImgWidth;
  92. _origImgHeight = origImgHeight;
  93. _origImgCount = origImgCount;
  94. _colorType = (int)colorType;
  95. _cropRect = cropRect;
  96. _desiredImgWidth = desiredImgWidth;
  97. _desiredImgHeight = desiredImgHeight;
  98. _desiredImgCount = desiredImgCount;
  99. _dataPointer = dataPointer;
  100. }
  101. }
  102. /// <summary>
  103. /// 图像写入时可以设置的一些标志
  104. /// </summary>
  105. public enum EnumImwriteFlags
  106. {
  107. JpegQuality,
  108. JpegProgressive,
  109. JpegOptimize,
  110. JpegRstInterval,
  111. JpegLumaQuality,
  112. JpegChromaQuality,
  113. PngCompression,
  114. PngStrategy,
  115. PngBilevel,
  116. }
  117. /// <summary>
  118. /// 图像写入时参数传入的格式
  119. /// </summary>
  120. public struct StructImwriteParam
  121. {
  122. private EnumImwriteFlags _flagName;
  123. private int _flagValue;
  124. public EnumImwriteFlags FlagName
  125. {
  126. get => _flagName;
  127. set => _flagName = value;
  128. }
  129. public int FlagValue
  130. {
  131. get => _flagValue;
  132. set => _flagValue = value;
  133. }
  134. public StructImwriteParam(EnumImwriteFlags flagName, int flagValue)
  135. {
  136. _flagName = flagName;
  137. _flagValue = flagValue;
  138. }
  139. }
  140. /// <summary>
  141. /// 图像写入时的扩展名
  142. /// </summary>
  143. public enum EnumImwriteExtension
  144. {
  145. Png,
  146. Jpg,
  147. Bmp,
  148. }
  149. /// <summary>
  150. /// 表面类型
  151. /// </summary>
  152. internal enum EnumSurfacePicType
  153. {
  154. Right,
  155. Left,
  156. Behind,
  157. Front,
  158. Top,
  159. Bottom,
  160. }
  161. /// <summary>
  162. /// 均一立方体数据信息
  163. /// </summary>
  164. internal struct StructUniformVolumeDataInfo
  165. {
  166. private int _x;
  167. private int _y;
  168. private int _z;
  169. private float _spacing;
  170. private EnumColorType _colorType;
  171. private IntPtr _dataPointer;
  172. public int X
  173. {
  174. get => _x;
  175. set => _x = value;
  176. }
  177. public int Y
  178. {
  179. get => _y;
  180. set => _y = value;
  181. }
  182. public int Z
  183. {
  184. get => _z;
  185. set => _z = value;
  186. }
  187. public float Spacing
  188. {
  189. get => _spacing;
  190. set => _spacing = value;
  191. }
  192. public EnumColorType ColorType
  193. {
  194. get => _colorType;
  195. set => _colorType = value;
  196. }
  197. public IntPtr DataPointer
  198. {
  199. get => _dataPointer;
  200. set => _dataPointer = value;
  201. }
  202. public StructUniformVolumeDataInfo(int x, int y, int z, float spacing,
  203. EnumColorType colorType, IntPtr dataPointer)
  204. {
  205. _x = x;
  206. _y = y;
  207. _z = z;
  208. _spacing = spacing;
  209. _colorType = colorType;
  210. _dataPointer = dataPointer;
  211. }
  212. }
  213. /// <summary>
  214. /// 图像信息
  215. /// </summary>
  216. [StructLayout(LayoutKind.Sequential)]
  217. public struct StructImageInfo
  218. {
  219. private int _width;
  220. private int _height;
  221. private EnumColorType _colorType;
  222. private IntPtr _dataPointer;
  223. public int Width
  224. {
  225. get => _width;
  226. set => _width = value;
  227. }
  228. public int Height
  229. {
  230. get => _height;
  231. set => _height = value;
  232. }
  233. public EnumColorType ColorType
  234. {
  235. get => _colorType;
  236. set => _colorType = value;
  237. }
  238. public IntPtr DataPointer
  239. {
  240. get => _dataPointer;
  241. set => _dataPointer = value;
  242. }
  243. public StructImageInfo(int width, int height, EnumColorType colorType, IntPtr dataPointer)
  244. {
  245. _width = width;
  246. _height = height;
  247. _colorType = colorType;
  248. _dataPointer = dataPointer;
  249. }
  250. }
  251. /// <summary>
  252. /// 表面图像的信息
  253. /// </summary>
  254. internal struct StructSurfacePicInfo
  255. {
  256. private EnumSurfacePicType _surfaceType;
  257. private StructImageInfo _imageInfo;
  258. public EnumSurfacePicType SurfaceType
  259. {
  260. get => _surfaceType;
  261. set => _surfaceType = value;
  262. }
  263. public StructImageInfo ImageInfo
  264. {
  265. get => _imageInfo;
  266. set => _imageInfo = value;
  267. }
  268. public StructSurfacePicInfo(EnumSurfacePicType surfaceType, StructImageInfo imageInfo)
  269. {
  270. _surfaceType = surfaceType;
  271. _imageInfo = imageInfo;
  272. }
  273. }
  274. /// <summary>
  275. /// 错误代码
  276. /// </summary>
  277. public enum EnumCppCoreErrorCode
  278. {
  279. None = 0,
  280. EncodeError = 1,
  281. DecodeError = 2,
  282. StraightScanDataUniformError = 3,
  283. LinearInterpolationError = 4,
  284. GetSurfaceError = 5,
  285. FusionError = 6,
  286. }
  287. #endregion
  288. #region dllimport
  289. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  290. [return: MarshalAs(UnmanagedType.I1)]
  291. public static extern void GetErrorCodeAndMsg(ref EnumCppCoreErrorCode errorCode, StringBuilder error, int errorMaxLen);
  292. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  293. [return: MarshalAs(UnmanagedType.I1)]
  294. public static extern bool ImDataDecode(byte[] srcImData, int srcDataSize, byte[] dstImData, int dstDataSize,
  295. EnumImreadMode imreadMode = EnumImreadMode.Unchanged);
  296. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  297. [return: MarshalAs(UnmanagedType.I1)]
  298. public static extern bool ImDataEncode(StructImageInfo imageInfo, EnumImwriteExtension extension,
  299. StructImwriteParam[] imwriteParams, int paramCount, byte[] dstImgData, ref int dstDataSize);
  300. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  301. [return: MarshalAs(UnmanagedType.I1)]
  302. public static extern bool ImDataSave(StructImageInfo imageInfo, EnumImwriteExtension extension,
  303. StructImwriteParam[] imwriteParams, int paramCount, string savePath);
  304. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  305. [return: MarshalAs(UnmanagedType.I1)]
  306. internal static extern bool StraightScanDataToUniformCube(StructVolumeDataPreProcessorInfo dataInfo,
  307. byte[] dstDataBuffer);
  308. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  309. [return: MarshalAs(UnmanagedType.I1)]
  310. internal static extern bool GetSurfacePicsFromUniformCube(StructUniformVolumeDataInfo volumeDataInfo,
  311. int surfaceNum, [In, Out] StructSurfacePicInfo[] surfaceInfos);
  312. [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)]
  313. [return: MarshalAs(UnmanagedType.I1)]
  314. internal static extern bool ITKBasedFusionAlongX(StructUniformVolumeDataInfo[] volumeDataInfos,
  315. int volumeDataCount, ref StructUniformVolumeDataInfo fusedDataInfo);
  316. #endregion
  317. #region private
  318. private byte[] _fusedDataBuffer = null;
  319. private float _expectedSpacing = 0.01f;
  320. #endregion
  321. #region events
  322. /// <summary>
  323. /// 通知订阅者,重建过程中发生了错误
  324. /// </summary>
  325. public event EventHandler<ErrorEventArgs> NotifyError;
  326. /// <summary>
  327. /// 通知订阅者,有log要记
  328. /// </summary>
  329. public event EventHandler<LogEventArgs> NotifyLog;
  330. #endregion
  331. /// <summary>
  332. /// 甲状腺3D融合
  333. /// 目前主要负责将左右两次扫查的甲状腺数据融合成一个完整的甲状腺三维体数据
  334. /// </summary>
  335. public bool ThyroidThreeDFusion(List<RawVolumeData> rawVolumeDatas, ref RawVolumeData fusedVolumeData)
  336. {
  337. try
  338. {
  339. int volumeDataCount = rawVolumeDatas.Count;
  340. if (volumeDataCount < 2)
  341. {
  342. throw new ArgumentException("too few ( " + volumeDataCount + " ) volume datas for fusion");
  343. }
  344. // 由于C++代码里是直接按照Volume的Index从小到大决定融合的顺序,因此这里需要整理一下每个Volume被输入的顺序
  345. // 如果有左甲状腺直扫或右甲状腺直扫,就一定要有且仅有这两个(目前暂不支持甲状腺峡部的扫查)
  346. // 这时,输入到C++里时的顺序为左甲状腺、右甲状腺(如果后续支持甲状腺峡部的扫查,输入顺序应为左甲状腺、峡部、右甲状腺)
  347. // 如果没有指定是左侧还是右侧的直扫(包括 NotSpecified ComputerReconstructed 和 FreeScan 三种情况)
  348. // 都应该根据每个体数据扫查的坐标,按X方向从小到大依次融合
  349. // 如果每个体数据的OriginX都一样,则直接按照其在rawVolumeDatas里的Index序号依次融合
  350. List<int> fusionOrder = new List<int>();
  351. int findIndexLeft = rawVolumeDatas.FindIndex(x => x.ScanType == EnumVolumeDataScanType.ThyroidLeftStraightScan);
  352. int findIndexRight = rawVolumeDatas.FindIndex(x => x.ScanType == EnumVolumeDataScanType.ThyroidRightStraightScan);
  353. // 如果有左甲状腺扫查或右甲状腺扫查
  354. if (findIndexLeft != -1 || findIndexRight != -1)
  355. {
  356. // 确定有且只有左甲状腺或右甲状腺
  357. if (findIndexLeft != -1 && findIndexRight != -1 && volumeDataCount==2)
  358. {
  359. fusionOrder.Add(findIndexLeft);
  360. fusionOrder.Add(findIndexRight);
  361. }
  362. else
  363. {
  364. throw new ArgumentException("left thyroid and right thyroid should appear at the same time.");
  365. }
  366. }
  367. else
  368. {
  369. // 按照OriginX从小到大的顺序排列
  370. List<KeyValuePair<int,float>> originX = new List<KeyValuePair<int, float>>();
  371. for (int ni = 0; ni < volumeDataCount; ni++)
  372. {
  373. originX.Add(new KeyValuePair<int, float>(ni, rawVolumeDatas[ni].TransducerPoses[0].TransX));
  374. }
  375. originX.Sort((one,another)=>one.Value.CompareTo(another.Value));
  376. for (int ni = 0; ni < volumeDataCount; ni++)
  377. {
  378. fusionOrder.Add(originX[ni].Key);
  379. }
  380. }
  381. // 由于融合需要均一立方体,因此,先把不是均一立方体的转换成均一立方体
  382. // 要求用来融合的立方体的spacing要一致
  383. for (int ni = 0; ni < volumeDataCount; ni++)
  384. {
  385. if (!rawVolumeDatas[ni].IsUniformCube || !MathHelper.AlmostEqual(rawVolumeDatas[ni].SpacingXY, _expectedSpacing))
  386. {
  387. if (!rawVolumeDatas[ni].TurnToDesiredUniformCube(_expectedSpacing))
  388. {
  389. throw new System.Exception("Uniform cube is needed for fusion.");
  390. }
  391. }
  392. }
  393. // 转成CppCore所需的格式,按照fusionOrder里指定的顺序依次加到数组里
  394. StructUniformVolumeDataInfo[] volumeDataInfos = new StructUniformVolumeDataInfo[volumeDataCount];
  395. byte[][] dataBuffers = new byte[volumeDataCount][];
  396. for (int ni = 0; ni < volumeDataCount; ni++)
  397. {
  398. int index = fusionOrder[ni];
  399. var volumeData = rawVolumeDatas[index];
  400. var dataPointer = Marshal.UnsafeAddrOfPinnedArrayElement(volumeData.DataBuffer, 0);
  401. volumeDataInfos[ni] = new StructUniformVolumeDataInfo(volumeData.Width, volumeData.Depth,
  402. volumeData.ImageCount,volumeData.SpacingXY, volumeData.ColorType, dataPointer);
  403. dataBuffers[ni] = volumeData.DataBuffer;
  404. }
  405. // 给融合后的体数据赋初值
  406. // 看总共需要多大空间(一般认为融合后所占内存空间小于融合前的总和)
  407. // 注意:如果融合后所需内存空间大于融合前的总和,会直接抛访问越界的异常
  408. int totalSizeReserve = 0;
  409. for (int ni = 0; ni < volumeDataCount; ni++)
  410. {
  411. totalSizeReserve += rawVolumeDatas[ni].DataBufferByteCounts;
  412. }
  413. if (_fusedDataBuffer == null)
  414. {
  415. _fusedDataBuffer = new byte[totalSizeReserve];
  416. }
  417. if (_fusedDataBuffer.Length < totalSizeReserve)
  418. {
  419. Array.Resize(ref _fusedDataBuffer, totalSizeReserve);
  420. }
  421. StructUniformVolumeDataInfo fusedResult = new StructUniformVolumeDataInfo();
  422. fusedResult.DataPointer = Marshal.UnsafeAddrOfPinnedArrayElement(_fusedDataBuffer, 0); ;
  423. // 调用ITKBasedMultiStageFusion
  424. if (!ITKBasedFusionAlongX(volumeDataInfos, volumeDataCount, ref fusedResult))
  425. {
  426. int errorMaxLen = 256;
  427. StringBuilder errorMsg = new StringBuilder(errorMaxLen);
  428. EnumCppCoreErrorCode errorCode = EnumCppCoreErrorCode.None;
  429. GetErrorCodeAndMsg(ref errorCode, errorMsg, errorMaxLen);
  430. throw new Exception("Failed at ITKBasedFusionAlongX, error code: "
  431. + errorCode.ToString() + " , details: " + errorMsg.ToString() + " .");
  432. }
  433. // 给fusedVolumeData赋值
  434. fusedVolumeData.ReadFromFusedResult(fusedResult);
  435. // 记一下log
  436. NotifyLog?.Invoke(this, new LogEventArgs(EnumLogType.InfoLog, "ThyroidThreeDFusion finished."));
  437. return true;
  438. }
  439. catch (Exception excep)
  440. {
  441. NotifyLog?.Invoke(this, new LogEventArgs(EnumLogType.ErrorLog, "Failed at SaveSurface:" + excep.Message));
  442. NotifyError?.Invoke(this, new ErrorEventArgs(excep));
  443. return false;
  444. }
  445. }
  446. }
  447. }