123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using Emgu.CV;
- using Emgu.CV.Structure;
- using Emgu.CV.CvEnum;
- using WingServerCommon.Log;
- namespace WingAIDiagnosisService.Carotid.Utilities.DetectIntima
- {
- /// <summary>
- /// 颈动脉上内膜检测
- /// </summary>
- public class CarotidUpperIntimaMeasurement : CarotidIntimaMeasurement
- {
- //磨平坑时修改的点个数
- private int _invalidPitPointsNum;
- //被平滑掉的点个数
- private int _invalidSmoothedPointNum;
- private CarotidIntimaMeasureParameter _measureParameter;
- private Image<Gray, byte> _imageRoiBinary;
- private Image<Gray, byte> _imageRoiCanny;
- private readonly List<Point> _upperPoint = new List<Point>();
- private readonly List<Point> _lowerPoint = new List<Point>();
- /// <summary>
- /// 上壁颈动脉自动测量对外接口
- /// </summary>
- /// <param name="measureParameter"></param>
- /// <returns></returns>
- public CarotidIntimaMeasureResult MeasureApi(CarotidIntimaMeasureParameter measureParameter)
- {
- if (measureParameter.Image == null)
- {
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- var imageGray = measureParameter.Image.Copy();
- try
- {
- OptimizeImageQuality(imageGray, measureParameter.ImageProcessState.ContrastChanged,
- measureParameter.ImageProcessState.SharpnessChanged);
- var originalImageRectRoi = GetImageRoi(measureParameter.SelectRoi, imageGray.Size);
- var result = ImagePreprocessingUpper(imageGray, originalImageRectRoi, 0);
- if (result.IsSuccess)
- {
- return MeasureUpperWall(result.ImageRoiBinary,
- result.ImageRoiCanny,
- originalImageRectRoi,
- result.ImageRoiBinary.ROI,
- measureParameter.PhysicalPerPixel);
- }
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement MeasureApi error." + e.Message + "," + e.StackTrace);
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- finally
- {
- imageGray.Dispose();
- }
- }
- /// <summary>
- /// 内膜自动测量用的图像预处理
- /// </summary>
- /// <param name="measureParameter"></param>
- /// <returns></returns>
- public static Image<Gray, byte> ImagePreprocessing(CarotidIntimaMeasureParameter measureParameter)
- {
- var imageGray = measureParameter.Image.Copy();
- //优化图像质量
- OptimizeImageQuality(imageGray, measureParameter.ImageProcessState.ContrastChanged, measureParameter.ImageProcessState.SharpnessChanged);
- //图像预处理
- return imageGray;
- }
- /// <summary>
- /// 优化图像质量
- /// </summary>
- /// <param name="imageGray"></param>
- /// <param name="sharpnessChanged"></param>
- private static void OptimizeImageQuality(Image<Gray, byte> imageGray, bool contrastChanged, bool sharpnessChanged)
- {
- if (contrastChanged == false)
- {
- //contrastDegree = 1.5,brightness = -10;
- CvInvoke.cvConvertScale(imageGray, imageGray, 1.5, -10);
- }
- if (sharpnessChanged == false)
- {
- const int sharpFactor = 2;
- float[,] temp = { { 0, -sharpFactor, 0 }, { -sharpFactor, 1 + 4 * sharpFactor, -sharpFactor }, { 0, -sharpFactor, 0 } };
- var kernel = new ConvolutionKernelF(temp);
- CvInvoke.Filter2D(imageGray, imageGray, kernel, new Point(-1, -1));
- }
- }
- /// <summary>
- /// 上壁颈动脉自动测量
- /// </summary>
- /// <param name="imageGray"></param>
- /// <param name="rectRoi"></param>
- /// <returns></returns>
- public CarotidIntimaMeasureResult MeasureUpperWall(Image<Gray, byte> imageRoiBinary, Image<Gray, byte> imageRoiCanny, Rectangle originalImageRectRoi, Rectangle rectRoi, float physicalPerPixel)
- {
- try
- {
- //限制最小roi
- if (rectRoi.Width < 3 || rectRoi.Height < 3)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement MeasureUpperWall error." + "Selected roi is too small.");
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- _upperPoint.Clear();
- _lowerPoint.Clear();
- _invalidPitPointsNum = 0;
- _invalidSmoothedPointNum = 0;
- PhysicalPerPixel = physicalPerPixel;
- var normalThick = UpperWallMaxIntimaThick * (1 / PhysicalPerPixel);
- MaxIntimaThickPix = Math.Min((int)normalThick, rectRoi.Height);
- MinIntimaThickPix = (int)(UpperMinIntimaThick * (1 / PhysicalPerPixel));
- _imageRoiBinary?.Dispose();
- _imageRoiCanny?.Dispose();
- _imageRoiBinary = imageRoiBinary.Copy(rectRoi);
- _imageRoiCanny = imageRoiCanny.Copy(rectRoi);
- //上壁检测算法
- var result1 = GetUpperMeasureCarotidPoint(originalImageRectRoi, ref rectRoi, MaxIntimaThickPix);
- //修改rect后再次回调
- if (result1 == 2)
- {
- return MeasureUpperWall(imageRoiBinary, imageRoiCanny, originalImageRectRoi, rectRoi,
- physicalPerPixel);
- }
- if (result1 != 0)
- {
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- var result2 = GetDataStatistics(_upperPoint, _lowerPoint, _invalidPitPointsNum, _invalidSmoothedPointNum);
- if (result2.ErrorCode != CarotidIntimaMeasureErrorCode.Success)
- {
- return new CarotidIntimaMeasureResult(result2.ErrorCode);
- }
- //每个像素代表的毫米数
- double ratio = 10* PhysicalPerPixel;
- var upperMaxThickness = (float)(result2.MaxThick * ratio);
- var upperMinThickness = (float)(result2.MinThick * ratio);
- var upperAverageThickness = (float)(result2.AverageThick * ratio);
- var upperValidPointsNum = result2.ValidPointsNum;
- var upperSdThickness = result2.SdThickness;
- var std = result2.ThicknessStd;
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Success, new List<Point>(_upperPoint),
- new List<Point>(_lowerPoint),
- upperMaxThickness, upperMinThickness, upperAverageThickness,
- upperSdThickness, upperValidPointsNum, std);
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement MeasureUpperWall error." + e.Message + "," + e.StackTrace);
- return new CarotidIntimaMeasureResult(CarotidIntimaMeasureErrorCode.Failed);
- }
- finally
- {
- _imageRoiCanny?.Dispose();
- _imageRoiBinary?.Dispose();
- }
- }
- /// <summary>
- /// 计算上壁内膜下边沿点
- /// </summary>
- /// <param name="imageCanny"></param>
- /// <returns></returns>
- private List<Point> GetOriginalLowerPoint(Image<Gray, byte> imageCanny)
- {
- RemoveImpurity(imageCanny);
- var lowerPointListTemp = new List<Point>();
- var dataList = new List<byte>();
- var width = imageCanny.Width;
- var height = imageCanny.Height;
- //获得每一列,从下往上第一个边缘点
- for (var w = 0; w < width; ++w)
- {
- dataList.Clear();
- for (var h = height - 1; h > -1; --h)
- {
- dataList.Add(imageCanny.Data[h, w, 0]);
- }
- var y1 = GetUpperIntimaLowerEdgePoints(dataList);
- lowerPointListTemp.Add(new Point(w, y1));
- }
- return lowerPointListTemp;
- }
- /// <summary>
- /// 颈动脉上内膜检测
- /// </summary>
- /// <param name="_imageRoiBinary"></param>
- /// <param name="_imageRoiCanny"></param>
- /// <param name="rectRoi"></param>
- /// <param name="thick"></param>
- /// <returns></returns>
- private int GetUpperMeasureCarotidPoint(Rectangle originalImageRectRoi, ref Rectangle rectRoi, int thick)
- {
- try
- {
- var upperWall = new List<Point>();
- var lowerWall = new List<Point>();
- var lowerPointListTemp = GetOriginalLowerPoint(_imageRoiCanny);
- //排除异常点---------------------------------
- ExcludeExceptionsOnePoint(lowerPointListTemp);
- ExcludeExceptionsPoints(lowerPointListTemp);
- //对下壁作平滑处理
- var smoothResult = PointsSmooth.SmoothLine(lowerPointListTemp);
- lowerPointListTemp = smoothResult.Points.ToList();
- _invalidSmoothedPointNum += smoothResult.InvalidNumber;
- //缩小上内膜下边缘下方的血管区域
- var maxY = lowerPointListTemp.Max(o => o.Y);
- if (_imageRoiBinary.Height - maxY > 10)
- {
- rectRoi = new Rectangle(rectRoi.Left, rectRoi.Top, rectRoi.Width, maxY + 9);
- return 2;
- }
- //求上壁
- var upperPointListTemp = GetUpperEdgePointsInUpperIntima(_imageRoiBinary, lowerPointListTemp, thick);
- if (upperPointListTemp.Count == 0)
- {
- return 1;
- }
- smoothResult = PointsSmooth.SmoothLineUpperWall(upperPointListTemp, lowerPointListTemp);
- upperPointListTemp = smoothResult.Points.ToList();
- _invalidSmoothedPointNum += smoothResult.InvalidNumber;
- RemoveInvalidUpperWallPoints(upperPointListTemp, lowerPointListTemp, out var smoothedNum3);
- _invalidSmoothedPointNum = _invalidSmoothedPointNum + smoothedNum3;
- //上下壁同时平滑处理
- var result1 = PointsSmooth.SmoothUpperIntimaTwoLine(upperPointListTemp, lowerPointListTemp, out var smoothedNum4);
- _invalidSmoothedPointNum = _invalidSmoothedPointNum + smoothedNum4;
- if (result1 != CarotidIntimaMeasureErrorCode.Success)
- {
- return 1;
- }
- var count = Math.Min(upperPointListTemp.Count, lowerPointListTemp.Count);
- //把小图中的点还原到ROI中正确坐标
- for (var i = 0; i < count; ++i)
- {
- var upperPointY = Math.Max(upperPointListTemp[i].Y, 0);
- var lowerPointY = Math.Min(lowerPointListTemp[i].Y, rectRoi.Height - 1);
- upperWall.Add(new Point(upperPointListTemp[i].X + rectRoi.Left, upperPointY + rectRoi.Top));
- lowerWall.Add(new Point(lowerPointListTemp[i].X + rectRoi.Left, lowerPointY + rectRoi.Top));
- }
- //把ROI中的点还原到原始图中正确坐标
- _upperPoint.Clear();
- _lowerPoint.Clear();
- for (var i = 0; i < count; ++i)
- {
- var upperPointY = Math.Max(upperWall[i].Y, 0);
- var lowerPointY = Math.Min(lowerWall[i].Y, originalImageRectRoi.Height - 1);
- _upperPoint.Add(new Point(upperPointListTemp[i].X + originalImageRectRoi.Left, upperPointY + originalImageRectRoi.Top));
- _lowerPoint.Add(new Point(lowerPointListTemp[i].X + originalImageRectRoi.Left, lowerPointY + originalImageRectRoi.Top));
- }
- return 0;
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement GetUpperMeasureCarotidPoint error." + e.Message + "," + e.StackTrace);
- return 1;
- }
- }
- /// <summary>
- /// 去除掉下壁中的异常点
- /// </summary>
- /// <param name="upperPointListTemp"></param>
- /// <param name="lowerPointListTemp"></param>
- /// <param name="num"></param>
- private void RemoveInvalidUpperWallPoints(IList<Point> upperPointListTemp,
- IReadOnlyList<Point> lowerPointListTemp, out int num)
- {
- num = 0;
- var count = lowerPointListTemp.Count;
- for (var i = 0; i < count; ++i)
- {
- if (upperPointListTemp[i].Y <= Math.Max(lowerPointListTemp[i].Y - MinIntimaThickPix, 0)) continue;
- upperPointListTemp[i] = new Point(upperPointListTemp[i].X, Math.Max(lowerPointListTemp[i].Y - MinIntimaThickPix, 0));
- num++;
- }
- }
-
- /// <summary>
- /// 求上内膜时,求上壁边缘点
- /// </summary>
- /// <param name="imageRoiBinary"></param>
- /// <param name="lowerWallPoints"></param>
- /// <param name="thick"></param>
- /// <returns></returns>
- private List<Point> GetUpperEdgePointsInUpperIntima(Image<Gray, byte> imageRoiBinary, IReadOnlyList<Point> lowerWallPoints, int thick)
- {
- try
- {
- if (thick <= 0)
- {
- return new List<Point>();
- }
- if (imageRoiBinary.Height < MinRoiHeight)
- {
- return new List<Point>();
- }
- thick = (MinIntimaThickPix + thick) / 2;
- var upperWallPoints = lowerWallPoints.Select(point => new Point(point.X, Math.Max(0, point.Y - thick))).ToList();
- //微调上边缘点
- var res = UpperIntimaUpperEdgePointTrim(imageRoiBinary, upperWallPoints, lowerWallPoints);
- return res != CarotidIntimaMeasureErrorCode.Success ? new List<Point>() : upperWallPoints;
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement GetUpperEdgePointsInUpperIntima error." + e.Message + "," + e.StackTrace);
- return new List<Point>();
- }
- }
- /// <summary>
- /// 颈动脉上内膜,上壁点微调
- /// </summary>
- /// <param name="imageRoiBinary"></param>
- /// <param name="upperWall"></param>
- /// <param name="lowerWall"></param>
- /// <returns></returns>
- private CarotidIntimaMeasureErrorCode UpperIntimaUpperEdgePointTrim(Image<Gray, byte> imageRoiBinary, List<Point> upperWall, IReadOnlyList<Point> lowerWall)
- {
- try
- {
- var trimRange = (MaxIntimaThickPix - MinIntimaThickPix) / 2;
- var tempList = new List<Point>();
- for (var i = 0; i < upperWall.Count; ++i)
- {
- var point = upperWall[i];
- var xValue = point.X;
- var startYValue = Math.Min(lowerWall[i].Y, point.Y + trimRange);
- var endYValue = Math.Max(0, point.Y - trimRange);
- if (endYValue == startYValue)
- {
- tempList.Add(upperWall[i]);
- continue;
- }
- var count = startYValue - endYValue + 1;
- var yRange = new int[count];
- for (var j = 0; j < count; ++j)
- {
- yRange[j] = Math.Max(startYValue - j, 0);
- }
- var grayValue = new byte[count];
- for (var m = 0; m < grayValue.Length; ++m)
- {
- grayValue[m] = imageRoiBinary.Data[yRange[m], xValue, 0];
- }
- //求变化率----------------------------------------------------------------------------
- var grayDiff = new int[count - 1];
- for (var n = 0; n < grayDiff.Length; ++n)
- {
- grayDiff[n] = grayValue[n + 1] - grayValue[n];
- }
- var maxDiff = 0;
- var maxId = 0;
- var isFind = false;
- //找到变化率最大的
- for (var n = 0; n < grayDiff.Length; ++n)
- {
- var temp = grayDiff[n];
- if (temp <= maxDiff) continue;
- maxDiff = temp;
- maxId = n;
- isFind = true;
- }
- for (var m = 0; m < grayValue.Length; ++m)
- {
- if (grayValue[m] > ImageThreshold)
- {
- grayValue[m] = 255;
- }
- grayValue[m] = 0;
- }
- tempList.Add(isFind ? new Point(lowerWall[i].X, yRange[maxId]) : lowerWall[i]);
- }
- upperWall.Clear();
- upperWall.AddRange(tempList);
- return CarotidIntimaMeasureErrorCode.Success;
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement UpperIntimaUpperEdgePointTrim error." + e.Message + "," + e.StackTrace);
- return CarotidIntimaMeasureErrorCode.Failed;
- }
- }
- /// <summary>
- /// 求上内膜下壁边缘点
- /// </summary>
- /// <param name="dataList"></param>
- private static int GetUpperIntimaLowerEdgePoints(IReadOnlyList<byte> dataList)
- {
- var y1 = 0;
- var count = dataList.Count;
- //找到变化最大的两个点
- for (var i = 0; i < count; ++i)
- {
- if (dataList[i] <= 10) continue;
- y1 = i + 1;
- break;
- }
- return count - y1;
- }
- /// <summary>
- /// 图像初始化
- /// </summary>
- /// <param name="imageGray"></param>
- /// <param name="rectRoi"></param>
- /// <param name="subtractTheThreshold"></param>
- /// <returns></returns>
- public ImagePreprocessingResult ImagePreprocessingUpper(Image<Gray, byte> imageGray, Rectangle rectRoi, int subtractTheThreshold)
- {
- try
- {
- var imageRoiBinary = imageGray.Copy(rectRoi);
- //求二值化阈值-----------------------------------------------------------------------------------
- var threshold = ImageTools.GetThreshold(imageRoiBinary);
- threshold -= subtractTheThreshold;
- if (threshold < 0)
- {
- threshold = 0;
- }
- ImageThreshold = threshold;
- //另一二值化的图像
- var imageRoiCanny = new Image<Gray, byte>(imageRoiBinary.Width, imageRoiBinary.Height);
- //二值化
- CvInvoke.Threshold(imageRoiBinary, imageRoiCanny, threshold, 255, ThresholdType.ToZero);
- return new ImagePreprocessingResult(imageRoiBinary, imageRoiCanny, true);
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement ImagePreprocessingUpper error." + e.Message + "," + e.StackTrace);
- //图像预处理过程出错
- return new ImagePreprocessingResult(false);
- }
- }
- /// <summary>
- /// 去掉异常的一个点
- /// </summary>
- /// <param name="wallPointList"></param>
- private void ExcludeExceptionsOnePoint(List<Point> wallPointList)
- {
- try
- {
- var length = wallPointList.Count;
- if (length < 3)
- {
- return;
- }
- const int threshold = 2;
- var temp = new List<Point>();
- var firstPointY = wallPointList[0].Y;
- var secondPointY = wallPointList[1].Y;
- if (Math.Abs(firstPointY - secondPointY) > threshold)
- {
- wallPointList[0] = new Point(wallPointList[0].X, secondPointY);
- _invalidSmoothedPointNum++;
- }
- temp.Add(new Point(wallPointList[0].X, secondPointY));
- for (var i = 1; i < length - 1; ++i)
- {
- var pointY = wallPointList[i].Y;
- var beforePointY = wallPointList[i - 1].Y;
- var nextPointY = wallPointList[i + 1].Y;
- if (Math.Abs(pointY - beforePointY) > 2 && Math.Abs(pointY - nextPointY) > threshold)
- {
- temp.Add(new Point(wallPointList[i].X, (beforePointY + nextPointY) / 2));
- _invalidSmoothedPointNum++;
- }
- else
- {
- temp.Add(new Point(wallPointList[i].X, wallPointList[i].Y));
- }
- }
- var lastPointY = wallPointList.Last().Y;
- if (Math.Abs(lastPointY - wallPointList[length - 1].Y) > threshold)
- {
- temp.Add(new Point(wallPointList[length - 1].X, wallPointList[length - 1].Y));
- _invalidSmoothedPointNum++;
- }
- else
- {
- temp.Add(wallPointList.Last());
- }
- wallPointList.Clear();
- wallPointList.AddRange(temp);
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement ExcludeExceptionsOnePoint error." + e.Message + "," + e.StackTrace);
- }
- }
- /// <summary>
- /// 去掉异常的多个点
- /// </summary>
- /// <param name="wallPointList"></param>
- private void ExcludeExceptionsPoints(IList<Point> wallPointList)
- {
- try
- {
- var length = wallPointList.Count;
- if (length < 3)
- {
- return;
- }
- //把list分段
- var sectionList = new List<List<Point>>();
- var beginIndex = 0;
- for (var i = 1; i < length; ++i)
- {
- if (Math.Abs(wallPointList[i].Y - wallPointList[i - 1].Y) <= 2) continue;
- sectionList.Add(wallPointList.Skip(beginIndex).Take(i - beginIndex).ToList());
- beginIndex = i;
- }
- sectionList.Add(wallPointList.Skip(beginIndex).Take(length - beginIndex).ToList());
- if (beginIndex == 0)
- {
- return;
- }
- //比较前两个区域
- var num1 = sectionList[0].Count;
- var num2 = sectionList[1].Count;
- var changePointList = new List<Point>();
- var newY = 0;
- if (num1 < num2)
- {
- changePointList = sectionList[0];
- newY = sectionList[1][0].Y;
- }
- if (num1 == num2)
- {
- if (sectionList[0].Last().Y > sectionList[1][0].Y)
- {
- changePointList = sectionList[1];
- newY = sectionList[0].Last().Y;
- }
- else
- {
- changePointList = sectionList[0];
- newY = sectionList[1][0].Y;
- }
- }
- if (num1 > num2)
- {
- changePointList = sectionList[1];
- newY = sectionList[0].Last().Y;
- }
- _invalidSmoothedPointNum += changePointList.Count;
- foreach (var point in changePointList)
- {
- wallPointList[point.X] = new Point(point.X, newY);
- }
- ExcludeExceptionsPoints(wallPointList);
- }
- catch (Exception e)
- {
- Logger.WriteLineError("CarotidUpperIntimaMeasurement ExcludeExceptionsPoints error." + e.Message + "," + e.StackTrace);
- }
- }
- }
- }
|