using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;
using AI.Common;
using AutoEFInferenceCalcLib;
using System.Windows.Threading;
using ImageShowUtilsLib;
using System.Collections.Concurrent;
using Newtonsoft.Json;
namespace AutoEFCalculationDemo
{
///
/// 界面大小变化
///
public class MyTransform
{
public double Scale { get; set; }
public double OffsetX { get; set; }
public double OffsetY { get; set; }
public MyTransform(double scale, double offsetX, double offsetY)
{
Scale = scale;
OffsetX = offsetX;
OffsetY = offsetY;
}
public System.Windows.Point Transform(System.Windows.Point point)
{
double x = OffsetX + Scale * point.X;
double y = OffsetY + Scale * point.Y;
return new System.Windows.Point(x, y);
}
public System.Windows.Rect Transform(System.Windows.Rect rect)
{
double left = OffsetX + Scale * rect.Left;
double top = OffsetY + Scale * rect.Top;
double width = Scale * rect.Width;
double height = Scale * rect.Height;
return new System.Windows.Rect(left, top, width, height);
}
}
internal class Roi
{
internal AI.Common.Rect BoundingBox { get; set; }
internal System.Windows.Media.Brush Color { get; set; }
internal Point2D[] Contour { get; set; }
internal string LabelName { get; set; }
internal Roi(Point2D[] contour, System.Windows.Media.Brush color, string labelName)
{
Contour = contour;
LabelName = labelName;
Color = color;
}
}
public class Organ
{
public string OrganName { get; set; }
public AI.Common.Rect BoundingBox { get; set; }
public Point2D[][] Contour { get; set; }
public System.Windows.Media.Brush Color => System.Windows.Media.Brushes.Green;
public Organ(string organName, AI.Common.Rect boundBox, Point2D[][] contour)
{
OrganName = organName;
BoundingBox = boundBox;
Contour = contour;
}
}
///
/// ImageInfo.xaml 的交互逻辑
///
public partial class ImageInfo : UserControl
{
#region private
private volatile bool _enableAI;
private volatile bool _enableShowObjectContour;
private volatile bool _enableShowMeasureLine;
private volatile bool _enableShowMeasureCurve;
private volatile bool _enableShowTimeSeriesResult;
private Bitmap _image;
private MyTransform _transform;
private List _rois;
private CardiacCurveInfos _cardiacCurveInfos;
private MeasureCurveInfo _measureCurve = new MeasureCurveInfo(10000, 1000, 0, 200, 40.0,new List {"AutoEF.Volume", "AutoEF.CardiacCycle" });
private ConcurrentDictionary _imageInfo = new ConcurrentDictionary();
private Dictionary _hasShow = new Dictionary();
#endregion
#region properties
private void CanvasChangeSize(object sender, SizeChangedEventArgs e)
{
RoiCanvas.Width = GridOrigImg.ActualWidth;
RoiCanvas.Height = GridOrigImg.ActualHeight;
UpdateTransforms();
UpdateRois();
UpdateShowMeasureLine();
UpdateTimeSeriesResultPanelItems();
}
#endregion
#region public func
public ImageInfo()
{
InitializeComponent();
}
public void SetImageShowsParam(bool enableAI, bool showObject)
{
_enableAI = enableAI;
_enableShowObjectContour = showObject;
if(_rois != null)
{
UpdateRois();
}
if(_cardiacCurveInfos != null)
{
UpdateShowMeasureLine();
}
}
public void SetShowMeasureLine(bool showMeasureLine)
{
_enableShowMeasureLine = showMeasureLine;
if( _rois != null)
{
UpdateRois();
}
if(_cardiacCurveInfos != null)
{
UpdateShowMeasureLine();
}
}
public void SetShowTimeSeriesResult(bool showTimeSeriesResult)
{
_enableShowTimeSeriesResult = showTimeSeriesResult;
if( _rois != null)
{
UpdateRois();
}
if (_cardiacCurveInfos != null)
{
UpdateShowMeasureLine();
}
//if ( _cardiacCurveInfos != null)
//{
// UpdateTimeSeriesResultPanelItems();
//}
//if( _cardiacCurveInfos != null)
//{
// UpdateShowMeasureCurve();
//}
}
public void SetShowMeasureCurve(bool showMeasureCurve)
{
_enableShowMeasureCurve = showMeasureCurve;
if( _rois != null)
{
UpdateRois();
}
if( _cardiacCurveInfos != null)
{
UpdateShowMeasureLine();
}
}
public void UpdateImage(double frameTime, Bitmap image)
{
_image = image.Clone(new System.Drawing.Rectangle(0,0,image.Width, image.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
_imageInfo.TryAdd(frameTime, _image);
//更新缩放比例
UpdateTransforms();
//更新图像显示
var bitmapImage = RawImageShowUtils.BitmapToBitmapImage(_image);
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
ImageShow.Source = bitmapImage;
}));
}
public void UpdateDiagResult(string dataID, double currentTimeStamp, CardiacCurveInfos cardiacCurveInfos = null)
{
List rois = new List();
LVVolumeCalcResult detectResult =cardiacCurveInfos.ResultPerImage.Last().Value;
if(detectResult.Contour != null)
{
string labelName = "左心室内轮廓";
System.Windows.Media.Brush brush = System.Windows.Media.Brushes.DodgerBlue;
Roi roi = new Roi(detectResult.Contour, brush, labelName);
rois.Add(roi);
}
//删掉旧的Roi和Organ
RemoveMaesureLine();
RemoveRois();
RemoveMeasureCurve();
//RemoveTimeSeriesResult();
//设置
_rois = rois;
_cardiacCurveInfos = cardiacCurveInfos;
//更新画布上显示的Roi
UpdateRois();
UpdateShowMeasureLine();
UpdateShowMeasureCurve(dataID, currentTimeStamp);
UpdateTimeSeriesResultPanelItems();
}
public void ClearDiagResult()
{
//删掉已有的Roi
RemoveRois();
RemoveMaesureLine() ;
RemoveMeasureCurve();
RemoveTimeSeriesResult();
//设置
_rois = null;
_cardiacCurveInfos = null;
_measureCurve.ClaerResult();
_hasShow.Clear();
_imageInfo.Clear();
}
#endregion
#region Draw
///
/// 在画布上画出轮廓
///
///
///
private void DrawRoiInCanvas(Roi roi, string roiIndex)
{
if(_transform == null)
{
return;
}
RemoveRoiInCanvas(roiIndex);
//如果不启用AI,则直接退出‘
if(!_enableAI)
{
return;
}
//直接画轮廓
if(_enableShowObjectContour)
{
if(roi.Contour?.Length <= 0)
{
return;
}
PointCollection contourPoint = new PointCollection();
for(int i = 0; i < roi.Contour.Length; i++)
{
var point = new System.Windows.Point(roi.Contour[i].X, roi.Contour[i].Y);
var transPoint = _transform.Transform(point);
contourPoint.Add(transPoint);
}
//画出识别到的目标的结构
System.Windows.Shapes.Polyline contourDraw = new System.Windows.Shapes.Polyline();
contourDraw.Points = contourPoint;
contourDraw.StrokeThickness = 2;
contourDraw.Stroke = roi.Color;
RoiCanvas.Children.Add(contourDraw);
string roiName = "Roi_Contour" + roiIndex;
RoiCanvas.RegisterName(roiName, contourDraw);
//画出心肌末端的连线
System.Windows.Shapes.Line endLine = new System.Windows.Shapes.Line();
endLine.X1 = contourPoint[0].X;
endLine.Y1 = contourPoint[0].Y;
endLine.X2 = contourPoint[contourPoint.Count - 1].X;
endLine.Y2 = contourPoint[contourPoint.Count - 1].Y;
endLine.StrokeThickness = 2;
endLine.Stroke = roi.Color;
RoiCanvas.Children.Add(endLine);
string lineName = "EndLine" + roiIndex;
RoiCanvas.RegisterName(lineName, endLine);
//TODO:显示识别部位的名称
}
}
private void DrawMeasureLines(CardiacCurveInfos curveInfos)
{
if(_transform == null) { return;}
//已有则删除
RemoveMaesureLine();
//若不启用AI则退出
if(!_enableAI)
{
return;
}
LVVolumeCalcResult resultPerImage = curveInfos.ResultPerImage.Last().Value;
if (resultPerImage == LVVolumeCalcResult.Empty)
{
return;
}
//画测量线
if (_enableShowMeasureLine)
{
//显示切片点
PointCollection slicePoints = new PointCollection();
var slicePointOri = resultPerImage.MeasureMarks.SlicePoint;
var controlPointOri = resultPerImage.MeasureMarks.ControlPoints;
for (int i = 0; i < slicePointOri.Length; i++)
{
var slicePoint = new System.Windows.Point(slicePointOri[i].X, slicePointOri[i].Y);
slicePoints.Add(slicePoint);
}
//显示切片连线
for(int i = 0; i < slicePointOri.Length / 2; i++)
{
var transPoint1 = _transform.Transform(slicePoints[2 * i]);
var transPoint2 = _transform.Transform(slicePoints[2 * i + 1]);
System.Windows.Shapes.Line sliceLine = new System.Windows.Shapes.Line();
sliceLine.X1 = transPoint1.X;
sliceLine.Y1 = transPoint1.Y;
sliceLine.X2 = transPoint2.X;
sliceLine.Y2 = transPoint2.Y;
sliceLine.StrokeThickness = 1;
sliceLine.Stroke = System.Windows.Media.Brushes.Coral;
RoiCanvas.Children.Add(sliceLine);
string lineName = "MeasureLine_" + i.ToString();
RoiCanvas.RegisterName(lineName, sliceLine);
}
//显示控制点
for(int i = 0; i < controlPointOri.Length; i++)
{
var controlPoint = new System.Windows.Point(controlPointOri[i].X, controlPointOri[i].Y);
var transPoint = _transform.Transform(controlPoint);
System.Windows.Shapes.Path circleDraw = new System.Windows.Shapes.Path();
EllipseGeometry ellipseGeometry = new EllipseGeometry();
ellipseGeometry.Center = transPoint;
ellipseGeometry.RadiusX = 3;
ellipseGeometry.RadiusY = 3;
circleDraw.Data = ellipseGeometry;
circleDraw.Fill = System.Windows.Media.Brushes.DarkRed;
RoiCanvas.Children.Add(circleDraw);
string circleName = "ControlPoint_" + i.ToString();
RoiCanvas.RegisterName(circleName, circleDraw);
}
//显示左心室中轴线
var apexPoint = new System.Windows.Point(resultPerImage.MeasureMarks.ApexPoint.X,
resultPerImage.MeasureMarks.ApexPoint.Y);
var endPoints = resultPerImage.MeasureMarks.EndPoints;
System.Windows.Point centerPoint = new System.Windows.Point();
centerPoint.X = (endPoints[0].X + endPoints[1].X) / 2;
centerPoint.Y = (endPoints[0].Y + endPoints[1].Y) / 2;
var transApexPoint = _transform.Transform(apexPoint);
var transCenterPoint = _transform.Transform(centerPoint);
System.Windows.Shapes.Line axisLine = new System.Windows.Shapes.Line();
axisLine.X1 = transApexPoint.X;
axisLine.Y1 = transApexPoint.Y;
axisLine.X2 = transCenterPoint.X;
axisLine.Y2 = transCenterPoint.Y;
axisLine.StrokeThickness = 1;
axisLine.Stroke = System.Windows.Media.Brushes.Coral;
RoiCanvas.Children.Add(axisLine);
string axisLineName = "AxisLine";
RoiCanvas.RegisterName(axisLineName, axisLine);
}
}
#endregion
#region remove
///
/// 删除Roi轮廓
///
///
private void RemoveRoiInCanvas(string roiIndex)
{
//删除轮廓
string contourName = "Roi_Contour" + roiIndex;
System.Windows.Shapes.Polyline contourExist = RoiCanvas.FindName(contourName) as System.Windows.Shapes.Polyline;
if (contourExist != null)
{
RoiCanvas.Children.Remove(contourExist);
RoiCanvas.UnregisterName(contourName);
}
string lineName = "EndLine" + roiIndex;
System.Windows.Shapes.Line lineExist = RoiCanvas.FindName(lineName) as System.Windows.Shapes.Line;
if(lineExist != null)
{
RoiCanvas.Children.Remove(lineExist);
RoiCanvas.UnregisterName(lineName);
}
}
private void RemoveMaesure()
{
//删除测量线
for (int i = 0; i < _cardiacCurveInfos.ResultPerImage.Last().Value.MeasureMarks.SlicePoint.Length / 2; i++)
{
string measureLineName = "MeasureLine_" + i.ToString();
System.Windows.Shapes.Line lineExist = RoiCanvas.FindName(measureLineName) as System.Windows.Shapes.Line;
if (lineExist != null)
{
RoiCanvas.Children.Remove(lineExist);
RoiCanvas.UnregisterName(measureLineName);
}
}
//删除控制点
for(int i = 0; i < _cardiacCurveInfos.ResultPerImage.Last().Value.MeasureMarks.ControlPoints.Length; i++)
{
string circleName = "ControlPoint_" + i.ToString();
System.Windows.Shapes.Path circleExist = RoiCanvas.FindName(circleName) as System.Windows.Shapes.Path;
if (circleExist != null)
{
RoiCanvas.Children.Remove(circleExist);
RoiCanvas.UnregisterName(circleName);
}
}
//删除中轴线
string axisLineName = "AxisLine";
System.Windows.Shapes.Line axisLineExist = RoiCanvas.FindName(axisLineName) as System.Windows.Shapes.Line;
if (axisLineExist != null)
{
RoiCanvas.Children.Remove(axisLineExist);
RoiCanvas.UnregisterName(axisLineName);
}
}
private void RemoveRois()
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
if(_rois != null)
{
for(int i = 0; i < _rois.Count;i++)
{
RemoveRoiInCanvas(i.ToString());
}
}
}));
}
private void RemoveMaesureLine()
{
Dispatcher.Invoke((Action)(() =>
{
if(_cardiacCurveInfos != null)
{
RemoveMaesure();
}
}));
}
private void RemoveMeasureCurve()
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
PanelMeasureCurves.Children.Clear();
}));
}
private void RemoveTimeSeriesResult()
{
Dispatcher.Invoke(() =>
{
PanelTimeSeriesBasedResults.Children.Clear();
});
}
#endregion
#region private
private void UpdateTransforms()
{
if(_image != null)
{
var imgWidth = _image.Width;
var imgHeight = _image.Height;
var canvasWidth = GridOrigImg.ActualWidth;
var canvasHeight = GridOrigImg.ActualHeight;
var scaleX = canvasWidth / imgWidth;
var scaleY = canvasHeight / imgHeight;
var scale = scaleX < scaleY ? scaleX : scaleY;
double offsetX, offsetY;
if (Math.Abs(scale - scaleX) < 0.0001)
{
offsetY = 0.5 * (scaleY - scale) * imgHeight;
offsetX = 0;
}
else
{
offsetX = 0.5 * (scaleX - scale) * imgWidth;
offsetY = 0;
}
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
//更新画布尺寸,使其刚好只覆盖图像区域
RoiCanvas.Width = scale * _image.Width;
RoiCanvas.Height = scale * _image.Height;
Canvas.SetLeft(RoiCanvas, offsetX);
Canvas.SetTop(RoiCanvas, offsetY);
OrigImgScaleTransform.CenterX = 0;
OrigImgScaleTransform.CenterY = 0;
OrigImgScaleTransform.ScaleX = scale;
OrigImgScaleTransform.ScaleY = scale;
}));
_transform = new MyTransform(scale, 0, 0);
}
}
private void UpdateRois()
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
if(_rois != null)
{
for(int i = 0; i < _rois.Count; i++)
{
DrawRoiInCanvas(_rois[i], i.ToString());
}
}
}));
}
private void UpdateShowMeasureLine()
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
if(_cardiacCurveInfos != null)
{
DrawMeasureLines(_cardiacCurveInfos);
}
}));
}
private void UpdateShowMeasureCurve(string dataID, double currentTimeStamp)
{
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
var curve = new CurveInfo(_measureCurve);
PanelMeasureCurves.Children.Add(curve);
List> cardiacCurveInfos = new List>();
cardiacCurveInfos.Add(_cardiacCurveInfos.ResultPerImage);
CardiacCycleInfos[] cardiacCycles = _cardiacCurveInfos.CardiacCycles;
_measureCurve.UpdateCurveResult(dataID, currentTimeStamp, cardiacCurveInfos, cardiacCycles);
}));
}
private void UpdateTimeSeriesResultPanelItems()
{
List> _readyShowSingleFrame = new List>();
bool hasTimeSeriesNoShow = false;
if (_cardiacCurveInfos != null)
{
foreach (var cycleInfo in _cardiacCurveInfos.CardiacCycles)
{
var edesResult = new Dictionary();
var edInfo = cycleInfo.EDTimeStamp;
var esInfo = cycleInfo.ESTimeStamp;
if(cycleInfo.EDVolume == 0 || cycleInfo.ESVolume == 0)
{
continue;
}
if (!_cardiacCurveInfos.ResultPerImage.ContainsKey(cycleInfo.EDTimeStamp) ||
!_cardiacCurveInfos.ResultPerImage.ContainsKey(cycleInfo.ESTimeStamp))
{
continue;
}
edesResult.Add(edInfo, _cardiacCurveInfos.ResultPerImage[cycleInfo.EDTimeStamp]);
edesResult.Add(esInfo, _cardiacCurveInfos.ResultPerImage[cycleInfo.ESTimeStamp]);
if(!_hasShow.ContainsKey(edInfo) && !_hasShow.ContainsKey(esInfo))
{
hasTimeSeriesNoShow = true;
_readyShowSingleFrame.Add(edesResult);
_hasShow.Add(edInfo, _cardiacCurveInfos.ResultPerImage[cycleInfo.EDTimeStamp]);
_hasShow.Add(esInfo, _cardiacCurveInfos.ResultPerImage[cycleInfo.ESTimeStamp]);
}
}
if(_readyShowSingleFrame.Count > 0 && hasTimeSeriesNoShow)
{
RemoveTimeSeriesResult();
foreach(var timeSeries in _readyShowSingleFrame)
{
//取出当前图像对应的结果,得分等,生成对应的UserControl添加到Children中去
string resultName = "AutoEFCalculation";
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock(resultName + ":"));
}));
var keyList = timeSeries.Keys.ToList();
var edVolumeName = "EDVloume";
var esVolumeName = "ESVloume";
var efValueName = "EFValue";
var edVolume = timeSeries[keyList[0]].Volume;
var esVolume = timeSeries[keyList[1]].Volume;
var efValue = (edVolume - esVolume) / edVolume;
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock(edVolumeName + ":" + edVolume.ToString("f2")));
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock(esVolumeName + ":" + esVolume.ToString("f2")));
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock(efValueName + ":" + (efValue * 100).ToString("f2") + "%"));
}));
var edImage = _imageInfo[keyList[0]];
var esImage = _imageInfo[keyList[1]];
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock("ED Frame" + "(" + keyList[0].ToString("f2") + "):"));
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock("ED ControlPoints Number:" +
timeSeries[keyList[0]].MeasureMarks.ControlPoints.Length.ToString()));
PanelTimeSeriesBasedResults.Children.Add(new RecognizedImages(edImage, timeSeries[keyList[0]],
_enableShowObjectContour, _enableShowMeasureLine));
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock("ES Frame" + "(" + keyList[1].ToString("f2") + "):"));
PanelTimeSeriesBasedResults.Children.Add(GetTextBlock("ES ControlPoints Number:" +
timeSeries[keyList[1]].MeasureMarks.ControlPoints.Length.ToString()));
PanelTimeSeriesBasedResults.Children.Add(new RecognizedImages(esImage, timeSeries[keyList[1]],
_enableShowObjectContour, _enableShowMeasureLine));
}));
}
}
}
//把存储的超出时间的图像和数据删掉
var imageKey = _imageInfo.Keys.ToList();
int imagCount = imageKey.Count;
for( int i = imagCount - 1; i >= 0; i-- )
{
var key = imageKey[i];
if( key < _cardiacCurveInfos.ResultPerImage.Keys.First())
{
_imageInfo.TryRemove(key, out _);
}
}
var showKey = _hasShow.Keys.ToList();
int showCount = showKey.Count;
for ( int i = showCount - 1; i >= 0; i-- )
{
var key = showKey[i];
if(key < _cardiacCurveInfos.ResultPerImage.Keys.First() )
{
_hasShow.Remove(key);
}
}
}
private TextBlock GetTextBlock(string text)
{
var textBlock = new TextBlock();
textBlock.Text = text;
textBlock.Margin = new Thickness(4,4,4,4);
return textBlock;
}
#endregion
}
}