using AI.Reconstruction; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using AI.DiagSystem; namespace Reconstruction3DHelper { [StructLayout(LayoutKind.Sequential)] public struct PlaqueProperty { public int SerialNumber; public Rectangle ImageRect; public Point[] ArteryContour; public Point[] PlaqueContour; public double PlaqueArea; //狭窄率 public float Ratio; //在原图中的斑块外界矩形 public RotatedRectangle RealRect; //在原图中的斑块中心 public Point RealCenterPoint; public float Credibility; } [StructLayout(LayoutKind.Sequential)] public struct RotatedRectangle { public float Angle; public float Cx; public float Cy; public float Height; public float Width; } /// /// 图像信息 /// public struct StructImageInfo { private int _width; private int _height; private AI.Common.EnumColorType _colorType; private IntPtr _dataPointer; public int Width { get => _width; set => _width = value; } public int Height { get => _height; set => _height = value; } public AI.Common.EnumColorType ColorType { get => _colorType; set => _colorType = value; } public IntPtr DataPointer { get => _dataPointer; set => _dataPointer = value; } public StructImageInfo(int width, int height, AI.Common.EnumColorType colorType, IntPtr dataPointer) { _width = width; _height = height; _colorType = colorType; _dataPointer = dataPointer; } } public enum PlaqueType { SoftPlaque, HardPlaque } public enum PlaquePostionType { UpperIntima, LowerIntima } public enum PlaqueCountType { NoPlaque, OnePlaque, MultiPlaque } public class PlaqueResult { /// /// 斑块数量 无、单发、多发 /// public PlaqueCountType PlaqueCountType { get; } /// /// 斑块特性 软斑、硬斑 /// public PlaqueType PlaqueType { get; } /// /// 最大斑块宽度 /// public float PlaqueWidth { get; } /// /// 最大斑块高度 /// public float PlaqueHeight { get; } /// /// 狭窄率 /// public float Stenosis { get; } /// /// 斑块位置,上内膜/下内膜 /// public PlaquePostionType PlaquePostion; /// /// 检测切面的三维点 /// public List ClipPoints { get; } /// /// 是否识别成功 /// public bool IsSuccess { get; } /// /// 斑块的轮廓 /// public Point[] PlaqueContour { get; } public PlaqueResult() { } public PlaqueResult(bool isSuccess) { IsSuccess = isSuccess; } public PlaqueResult(bool isSuccess, PlaqueCountType plaqueCountType) { IsSuccess = isSuccess; PlaqueCountType = plaqueCountType; } public PlaqueResult(bool isSuccess, PlaqueCountType plaqueCountType, PlaqueType plaqueType, float plaqueWidth, float plaqueHeight, float stenosis, List clipPoints, Point[] plaqueContour) { IsSuccess = isSuccess; PlaqueCountType = plaqueCountType; PlaqueType = plaqueType; PlaqueWidth = plaqueWidth; PlaqueHeight = plaqueHeight; Stenosis = stenosis; ClipPoints = clipPoints; PlaqueContour = plaqueContour; } } public class PlaquePostProcessHelper { public ModelSize _modelSize; public string _plaqueImagePath; [DllImport(@"PlaqueProcessingCpp.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool PointInPolygon(IntPtr contours, int pointCount, IntPtr point); [DllImport(@"PlaqueProcessingCpp.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool CalcMaxPlaque(IntPtr arteryContour, IntPtr plaqueContour, int arteryCount, int plaqueCount, int left, int top, ref float ratio, ref RotatedRectangle RealRect); [DllImport(@"PlaqueProcessingCpp.dll", CallingConvention = CallingConvention.Cdecl)] public static extern bool IsBrightPlaque(StructImageInfo srcImgInfo, IntPtr contours, int pointCount); /// /// 斑块自动检测 /// /// 颈动脉图像 /// 每个像素代表实际长度 /// 斑块位置 public PlaqueResult CalcPlaqueProperty(byte[][] model, ModelSize modelSize, float physicalPerPixel, Dictionary vesselsAndPlaques) { _modelSize = modelSize; var allPlaque = GetAllPlaqueContours(vesselsAndPlaques); if (allPlaque.Count == 0) { return new PlaqueResult(false); } var maxPlaque = GetMaxPlaque(allPlaque); if (maxPlaque.RealRect.Width < 15 || maxPlaque.RealRect.Height < 15) { var detectPlaqueResult1 = new PlaqueResult(true, PlaqueCountType.NoPlaque, PlaqueType.SoftPlaque, 0, 0, 0, new List { }, new Point[] { }); return detectPlaqueResult1; } var decodedPointer = Marshal.UnsafeAddrOfPinnedArrayElement(model[maxPlaque.SerialNumber], 0); StructImageInfo imageInfo = new StructImageInfo(_modelSize.ModelLengthX, _modelSize.ModelLengthY, AI.Common.EnumColorType.Gray8, decodedPointer); GCHandle hObject1 = GCHandle.Alloc(maxPlaque.PlaqueContour, GCHandleType.Pinned); IntPtr plaqueContour = hObject1.AddrOfPinnedObject(); var isBright = IsBrightPlaque(imageInfo, plaqueContour, maxPlaque.PlaqueContour.Length); List clipPoints = new List {new Point3DF(0, _modelSize.ModelLengthX, maxPlaque.SerialNumber), new Point3DF(_modelSize.ModelLengthY, 0, maxPlaque.SerialNumber), new Point3DF(0, 0, maxPlaque.SerialNumber)}; //计算斑块的参数 var width = maxPlaque.RealRect.Width * physicalPerPixel; var height = maxPlaque.RealRect.Height * physicalPerPixel; width = (float)Math.Round(width, 2, MidpointRounding.AwayFromZero); height = (float)Math.Round(height, 2, MidpointRounding.AwayFromZero); var plaqueResult = new PlaqueResult(true, PlaqueCountType.MultiPlaque, isBright ? PlaqueType.HardPlaque : PlaqueType.SoftPlaque, width, height, maxPlaque.Ratio, clipPoints, maxPlaque.PlaqueContour); return plaqueResult; } /// /// 判断斑块是否在血管内 /// /// /// private List GetPlaqueInArtery(IList allPlaque) { var plaqueList = new List(); foreach (PlaqueProperty plaque in allPlaque) { var count = plaque.PlaqueContour.Length; var num = 0; GCHandle hObject = GCHandle.Alloc(plaque.ArteryContour, GCHandleType.Pinned); IntPtr pObject = hObject.AddrOfPinnedObject(); for (var i = 0; i < count; ++i) { GCHandle hObject2 = GCHandle.Alloc(plaque.PlaqueContour[i], GCHandleType.Pinned); IntPtr pObject2 = hObject2.AddrOfPinnedObject(); if (PointInPolygon(pObject, plaque.ArteryContour.Length, pObject2)) { num++; } } if (num > count * 3 / 4) { plaqueList.Add(plaque); } } return plaqueList; } /// /// 计算最大斑块的属性 /// /// /// private PlaqueProperty GetMaxPlaque(IList allPlaqueSrc) { if (allPlaqueSrc.Count < 1) { return new PlaqueProperty(); } var allPlaque = GetPlaqueInArtery(allPlaqueSrc); //判断斑块是否在血管内 if (allPlaque.Count < 1) { return new PlaqueProperty(); } allPlaque = allPlaque.OrderByDescending(o => o.PlaqueArea).ToList(); var maxPlaque = allPlaque[0]; //计算中心点 var centerPoint = Get2DContoursCenterPoints(maxPlaque.PlaqueContour); //计算动脉轮廓上距离斑块中心最远的点 GCHandle hObject1 = GCHandle.Alloc(maxPlaque.ArteryContour, GCHandleType.Pinned); IntPtr arteryIntPtr = hObject1.AddrOfPinnedObject(); GCHandle hObject2 = GCHandle.Alloc(maxPlaque.PlaqueContour, GCHandleType.Pinned); IntPtr plaqueIntPtr = hObject2.AddrOfPinnedObject(); float ratio = 0.0F; RotatedRectangle rotatedRectangle = new RotatedRectangle(); var result = CalcMaxPlaque(arteryIntPtr, plaqueIntPtr, maxPlaque.ArteryContour.Length, maxPlaque.PlaqueContour.Length, maxPlaque.ImageRect.Left, maxPlaque.ImageRect.Top, ref ratio, ref rotatedRectangle); maxPlaque.RealRect = rotatedRectangle; //计算实际的中心点 maxPlaque.RealCenterPoint = new Point(centerPoint.X + maxPlaque.ImageRect.Left, centerPoint.Y + maxPlaque.ImageRect.Top); maxPlaque.Ratio = ratio; return maxPlaque; } /// /// 计算轮廓的中心点 /// /// /// private Point Get2DContoursCenterPoints(Point[] contourPoints) { var len = contourPoints.Length; var sumX = 0; var sumY = 0; for (var i = 0; i < len; ++i) { sumX += contourPoints[i].X; sumY += contourPoints[i].Y; } var centerX = sumX / (float)len; var centerY = sumY / (float)len; //加上离最远点最远的点 return new Point((int)Math.Round(centerX, 0, MidpointRounding.AwayFromZero), (int)Math.Round(centerY, 0, MidpointRounding.AwayFromZero)); } /// /// 获取AI预测结果中所有斑块的轮廓点、外接矩形等尺寸 /// /// /// private List GetAllPlaqueContours(Dictionary modelResults) { List allPlaqueContours = new List(); foreach (var modelResult in modelResults) { if (modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects.Length > 0) { if (modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects[0].Label == 0) { continue; } ///modelResults赋值给_allPlaqueContours var plaqueContours = new PlaqueProperty(); AI.Common.Point2D[] contour1 = modelResult.Value.DiagResultsForEachOrgan[0].OrganContours[0]; Point[] arteryPoints = new Point[contour1.Length]; for (int i = 0; i < contour1.Length; i++) { arteryPoints[i].X = contour1[i].X; arteryPoints[i].Y = contour1[i].Y; } plaqueContours.ArteryContour = arteryPoints; AI.Common.Point2D[] contour2 = modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects[0].Contours[0]; Point[] plaquePoints = new Point[contour2.Length]; for (int i = 0; i < contour2.Length; i++) { plaquePoints[i].X = contour2[i].X; plaquePoints[i].Y = contour2[i].Y; } plaqueContours.PlaqueContour = plaquePoints; AI.Common.Rect rect2 = modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects[0].BoundingBox; plaqueContours.ImageRect = new Rectangle(rect2.Left, rect2.Top, rect2.Width, rect2.Height); plaqueContours.SerialNumber = modelResult.Key; plaqueContours.PlaqueArea = modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects[0].BoundingBox.Area; plaqueContours.Credibility = modelResult.Value.DiagResultsForEachOrgan[0].DetectedObjects[0].Confidence; allPlaqueContours.Add(plaqueContours); } } return allPlaqueContours; } } }