using System; using System.IO; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using AI.Common.Log; using AI.Common; using AI.Common.Tools; namespace AI.Reconstruction { /// /// 专门负责三维重建相关工作的一个类 /// public class AIReconstructor { #region internal /// /// 图像读入时的模式 /// public enum EnumImreadMode { Unchanged = -1, Grayscale = 0, Color = 1, AnyDepth = 2, AnyColor = 4, LoadGdal = 8, } /// /// 用来做体数据预处理的一些信息 /// internal struct StructVolumeDataPreProcessorInfo { private int _origImgWidth; private int _origImgHeight; private int _origImgCount; private int _colorType; private Rect _cropRect; private int _desiredImgWidth; private int _desiredImgHeight; private int _desiredImgCount; private IntPtr _dataPointer; public int OrigImgWidth { get => _origImgWidth; set => _origImgWidth = value; } public int OrigImgHeight { get => _origImgHeight; set => _origImgHeight = value; } public int OrigImgCount { get => _origImgCount; set => _origImgCount = value; } public int ColorType { get => _colorType; set => _colorType = value; } public Rect CropRect { get => _cropRect; set => _cropRect = value; } public int DesiredImgWidth { get => _desiredImgWidth; set => _desiredImgWidth = value; } public int DesiredImgHeight { get => _desiredImgHeight; set => _desiredImgHeight = value; } public int DesiredImgCount { get => _desiredImgCount; set => _desiredImgCount = value; } public IntPtr DataPointer { get => _dataPointer; set => _dataPointer = value; } public StructVolumeDataPreProcessorInfo(int origImgWidth, int origImgHeight, int origImgCount, EnumColorType colorType, Rect cropRect, int desiredImgWidth, int desiredImgHeight, int desiredImgCount, IntPtr dataPointer) { _origImgWidth = origImgWidth; _origImgHeight = origImgHeight; _origImgCount = origImgCount; _colorType = (int)colorType; _cropRect = cropRect; _desiredImgWidth = desiredImgWidth; _desiredImgHeight = desiredImgHeight; _desiredImgCount = desiredImgCount; _dataPointer = dataPointer; } } /// /// 图像写入时可以设置的一些标志 /// public enum EnumImwriteFlags { JpegQuality, JpegProgressive, JpegOptimize, JpegRstInterval, JpegLumaQuality, JpegChromaQuality, PngCompression, PngStrategy, PngBilevel, } /// /// 图像写入时参数传入的格式 /// public struct StructImwriteParam { private EnumImwriteFlags _flagName; private int _flagValue; public EnumImwriteFlags FlagName { get => _flagName; set => _flagName = value; } public int FlagValue { get => _flagValue; set => _flagValue = value; } public StructImwriteParam(EnumImwriteFlags flagName, int flagValue) { _flagName = flagName; _flagValue = flagValue; } } /// /// 图像写入时的扩展名 /// public enum EnumImwriteExtension { Png, Jpg, Bmp, } /// /// 表面类型 /// internal enum EnumSurfacePicType { Right, Left, Behind, Front, Top, Bottom, } /// /// 均一立方体数据信息 /// internal struct StructUniformVolumeDataInfo { private int _x; private int _y; private int _z; private float _spacing; private EnumColorType _colorType; private IntPtr _dataPointer; public int X { get => _x; set => _x = value; } public int Y { get => _y; set => _y = value; } public int Z { get => _z; set => _z = value; } public float Spacing { get => _spacing; set => _spacing = value; } public EnumColorType ColorType { get => _colorType; set => _colorType = value; } public IntPtr DataPointer { get => _dataPointer; set => _dataPointer = value; } public StructUniformVolumeDataInfo(int x, int y, int z, float spacing, EnumColorType colorType, IntPtr dataPointer) { _x = x; _y = y; _z = z; _spacing = spacing; _colorType = colorType; _dataPointer = dataPointer; } } /// /// 图像信息 /// [StructLayout(LayoutKind.Sequential)] public struct StructImageInfo { private int _width; private int _height; private EnumColorType _colorType; private IntPtr _dataPointer; public int Width { get => _width; set => _width = value; } public int Height { get => _height; set => _height = value; } public EnumColorType ColorType { get => _colorType; set => _colorType = value; } public IntPtr DataPointer { get => _dataPointer; set => _dataPointer = value; } public StructImageInfo(int width, int height, EnumColorType colorType, IntPtr dataPointer) { _width = width; _height = height; _colorType = colorType; _dataPointer = dataPointer; } } /// /// 表面图像的信息 /// internal struct StructSurfacePicInfo { private EnumSurfacePicType _surfaceType; private StructImageInfo _imageInfo; public EnumSurfacePicType SurfaceType { get => _surfaceType; set => _surfaceType = value; } public StructImageInfo ImageInfo { get => _imageInfo; set => _imageInfo = value; } public StructSurfacePicInfo(EnumSurfacePicType surfaceType, StructImageInfo imageInfo) { _surfaceType = surfaceType; _imageInfo = imageInfo; } } /// /// 错误代码 /// public enum EnumCppCoreErrorCode { None = 0, EncodeError = 1, DecodeError = 2, StraightScanDataUniformError = 3, LinearInterpolationError = 4, GetSurfaceError = 5, FusionError = 6, } #endregion #region dllimport [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern void GetErrorCodeAndMsg(ref EnumCppCoreErrorCode errorCode, StringBuilder error, int errorMaxLen); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool ImDataDecode(byte[] srcImData, int srcDataSize, byte[] dstImData, int dstDataSize, EnumImreadMode imreadMode = EnumImreadMode.Unchanged); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool ImDataEncode(StructImageInfo imageInfo, EnumImwriteExtension extension, StructImwriteParam[] imwriteParams, int paramCount, byte[] dstImgData, ref int dstDataSize); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool ImDataSave(StructImageInfo imageInfo, EnumImwriteExtension extension, StructImwriteParam[] imwriteParams, int paramCount, string savePath); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool StraightScanDataToUniformCube(StructVolumeDataPreProcessorInfo dataInfo, byte[] dstDataBuffer); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool GetSurfacePicsFromUniformCube(StructUniformVolumeDataInfo volumeDataInfo, int surfaceNum, [In, Out] StructSurfacePicInfo[] surfaceInfos); [DllImport(@"AIReconstructionCppCore.dll", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool ITKBasedFusionAlongX(StructUniformVolumeDataInfo[] volumeDataInfos, int volumeDataCount, ref StructUniformVolumeDataInfo fusedDataInfo); #endregion #region private private byte[] _fusedDataBuffer = null; private float _expectedSpacing = 0.01f; #endregion #region events /// /// 通知订阅者,重建过程中发生了错误 /// public event EventHandler NotifyError; /// /// 通知订阅者,有log要记 /// public event EventHandler NotifyLog; #endregion /// /// 甲状腺3D融合 /// 目前主要负责将左右两次扫查的甲状腺数据融合成一个完整的甲状腺三维体数据 /// public bool ThyroidThreeDFusion(List rawVolumeDatas, ref RawVolumeData fusedVolumeData) { try { int volumeDataCount = rawVolumeDatas.Count; if (volumeDataCount < 2) { throw new ArgumentException("too few ( " + volumeDataCount + " ) volume datas for fusion"); } // 由于C++代码里是直接按照Volume的Index从小到大决定融合的顺序,因此这里需要整理一下每个Volume被输入的顺序 // 如果有左甲状腺直扫或右甲状腺直扫,就一定要有且仅有这两个(目前暂不支持甲状腺峡部的扫查) // 这时,输入到C++里时的顺序为左甲状腺、右甲状腺(如果后续支持甲状腺峡部的扫查,输入顺序应为左甲状腺、峡部、右甲状腺) // 如果没有指定是左侧还是右侧的直扫(包括 NotSpecified ComputerReconstructed 和 FreeScan 三种情况) // 都应该根据每个体数据扫查的坐标,按X方向从小到大依次融合 // 如果每个体数据的OriginX都一样,则直接按照其在rawVolumeDatas里的Index序号依次融合 List fusionOrder = new List(); int findIndexLeft = rawVolumeDatas.FindIndex(x => x.ScanType == EnumVolumeDataScanType.ThyroidLeftStraightScan); int findIndexRight = rawVolumeDatas.FindIndex(x => x.ScanType == EnumVolumeDataScanType.ThyroidRightStraightScan); // 如果有左甲状腺扫查或右甲状腺扫查 if (findIndexLeft != -1 || findIndexRight != -1) { // 确定有且只有左甲状腺或右甲状腺 if (findIndexLeft != -1 && findIndexRight != -1 && volumeDataCount==2) { fusionOrder.Add(findIndexLeft); fusionOrder.Add(findIndexRight); } else { throw new ArgumentException("left thyroid and right thyroid should appear at the same time."); } } else { // 按照OriginX从小到大的顺序排列 List> originX = new List>(); for (int ni = 0; ni < volumeDataCount; ni++) { originX.Add(new KeyValuePair(ni, rawVolumeDatas[ni].TransducerPoses[0].TransX)); } originX.Sort((one,another)=>one.Value.CompareTo(another.Value)); for (int ni = 0; ni < volumeDataCount; ni++) { fusionOrder.Add(originX[ni].Key); } } // 由于融合需要均一立方体,因此,先把不是均一立方体的转换成均一立方体 // 要求用来融合的立方体的spacing要一致 for (int ni = 0; ni < volumeDataCount; ni++) { if (!rawVolumeDatas[ni].IsUniformCube || !MathHelper.AlmostEqual(rawVolumeDatas[ni].SpacingXY, _expectedSpacing)) { if (!rawVolumeDatas[ni].TurnToDesiredUniformCube(_expectedSpacing)) { throw new System.Exception("Uniform cube is needed for fusion."); } } } // 转成CppCore所需的格式,按照fusionOrder里指定的顺序依次加到数组里 StructUniformVolumeDataInfo[] volumeDataInfos = new StructUniformVolumeDataInfo[volumeDataCount]; byte[][] dataBuffers = new byte[volumeDataCount][]; for (int ni = 0; ni < volumeDataCount; ni++) { int index = fusionOrder[ni]; var volumeData = rawVolumeDatas[index]; var dataPointer = Marshal.UnsafeAddrOfPinnedArrayElement(volumeData.DataBuffer, 0); volumeDataInfos[ni] = new StructUniformVolumeDataInfo(volumeData.Width, volumeData.Depth, volumeData.ImageCount,volumeData.SpacingXY, volumeData.ColorType, dataPointer); dataBuffers[ni] = volumeData.DataBuffer; } // 给融合后的体数据赋初值 // 看总共需要多大空间(一般认为融合后所占内存空间小于融合前的总和) // 注意:如果融合后所需内存空间大于融合前的总和,会直接抛访问越界的异常 int totalSizeReserve = 0; for (int ni = 0; ni < volumeDataCount; ni++) { totalSizeReserve += rawVolumeDatas[ni].DataBufferByteCounts; } if (_fusedDataBuffer == null) { _fusedDataBuffer = new byte[totalSizeReserve]; } if (_fusedDataBuffer.Length < totalSizeReserve) { Array.Resize(ref _fusedDataBuffer, totalSizeReserve); } StructUniformVolumeDataInfo fusedResult = new StructUniformVolumeDataInfo(); fusedResult.DataPointer = Marshal.UnsafeAddrOfPinnedArrayElement(_fusedDataBuffer, 0); ; // 调用ITKBasedMultiStageFusion if (!ITKBasedFusionAlongX(volumeDataInfos, volumeDataCount, ref fusedResult)) { int errorMaxLen = 256; StringBuilder errorMsg = new StringBuilder(errorMaxLen); EnumCppCoreErrorCode errorCode = EnumCppCoreErrorCode.None; GetErrorCodeAndMsg(ref errorCode, errorMsg, errorMaxLen); throw new Exception("Failed at ITKBasedFusionAlongX, error code: " + errorCode.ToString() + " , details: " + errorMsg.ToString() + " ."); } // 给fusedVolumeData赋值 fusedVolumeData.ReadFromFusedResult(fusedResult); // 记一下log NotifyLog?.Invoke(this, new LogEventArgs(EnumLogType.InfoLog, "ThyroidThreeDFusion finished.")); return true; } catch (Exception excep) { NotifyLog?.Invoke(this, new LogEventArgs(EnumLogType.ErrorLog, "Failed at SaveSurface:" + excep.Message)); NotifyError?.Invoke(this, new ErrorEventArgs(excep)); return false; } } } }