using AI.Common; using AI.Common.Log; using AI.DiagSystem; using AIDiagnosis.Common.Enums; using AIDiagnosis.Common.Interfaces; using AIDiagnosisDemo.GC; using AIDiagnosisDemo.Infrastucture; using AIDiagnosisDemo.Service; using AIDiagnosisDemo.Settings; using AIDiagnosisSDK.Enums; using MyocardialDiagnosisSDK; using MyocardialSegmentLib; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using ThyroidClassificationSDK; using ThyroidSectionClassificaionLib; using EnumThyroidSectionType = ThyroidSectionClassificaionLib.EnumThyroidSectionType; using Rectangle = System.Drawing.Rectangle; namespace AIDiagnosisDemo.Presentation { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { private AIDiagnosisSDK.AIDiagnosis _diagnosis; private int _startTickCount; private bool _isPlaying; private Bitmap _image; private List _rois; private List _organs; private MyTransform _transform; private Dictionary> _conclusions; private List _ramUsedList; private int _cpuUsed = 0; private int _ramUsed = 0; private PerformanceCounter _processRAMCounter; private PerformanceCounter _processCPUCounter; private Timer _usedStatustTimer; private double _ramSize; private List _cpuThreads = new List(); private Task _ramThread = null; private List _cycleImagesList = null; private Thread _cycleTestThread; private IPlayer _player; private IImageProvider _imageProvider; private MyocardialDiagnosis _myocardialDiagnosis; private DetectedObject _currentDetectedObject; private GCController _gcController; private IImageProvider _myocardialImageProvider; private IImageProvider _thyroidClassificationImageProvider; private ThyroidClassification _thyroidClassification; public MainWindow() { InitializeComponent(); Logger.Info("Open the program!"); GcAdapter.Instance.RegisterForFullGCNotification(99, 99); _gcController = new GCController(); _ramSize = RAMHelper.ToFileFormat(RAMHelper.GetTotalPhysicalMemory(), FileSizeUnit.GB); var processName = Process.GetCurrentProcess().ProcessName; _processRAMCounter = new PerformanceCounter("Process", "Working Set", processName); _processCPUCounter = new PerformanceCounter("Process", "% Processor Time", processName); _usedStatustTimer = new Timer(TimerCallBack, null, Timeout.Infinite, 1000);//不启动,调用CallBack周期为1000ms. MaxButton.Visibility = Visibility.Collapsed; RestoreButton.Visibility = Visibility.Visible; InitializePlayer(); PlayerManager.Instance.PlayerChanged += OnPlayerChanged; SettingConfig.Instance.ValueChangedEvent += OnValueChanged; OnValueChanged(null, new List { SettingProperty.IsSimulation, SettingProperty.IsEnableAI, SettingProperty.IsCycleImagesPlay }); } private void InitializePlayer() { if (_player != null) { _player.Stop(); _player.InputFrameReceived -= OnInputFrameReceived; _player.FPSChanged -= OnFPSChanged; } _player = PlayerManager.Instance.Player; if (_player != null) { _player.InputFrameReceived += OnInputFrameReceived; _player.FPSChanged += OnFPSChanged; _player.Play(); _isPlaying = true; Dispatcher.Invoke(() => { PlayButton.IsEnabled = SettingConfig.Instance.IsVideo; if (PlayButton.Visibility == Visibility.Visible) { PlayButton.Visibility = Visibility.Collapsed; } }); } } private void OnPlayerChanged(object sender, EventArgs e) { if (_player != null) { //StopDiagnosis(); CleanAIResult(); _player.Stop(); _player.InputFrameReceived -= OnInputFrameReceived; _player.FPSChanged -= OnFPSChanged; } _player = PlayerManager.Instance.Player; Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Player Changed:{SettingConfig.Instance.UltrasoundImageSource}"); if (_player != null) { _player.InputFrameReceived += OnInputFrameReceived; _player.FPSChanged += OnFPSChanged; _player.Play(); _isPlaying = true; Dispatcher.Invoke(() => { PlayButton.IsEnabled = SettingConfig.Instance.IsVideo; if (PlayButton.Visibility == Visibility.Visible) { PlayButton.Visibility = Visibility.Collapsed; } }); if (SettingConfig.Instance.IsEnableAI) { StartDiagnosis(); if (SettingConfig.Instance.IsVideo == false)//当源文件为图片的情况下,就重新Show一次给AI提供图片信息。 { DetectImage(); } } } } private void DetectImage() { if (_image != null) { if (_myocardialDiagnosis != null) { var rawImage = BitmapHelper.BitmapToRawImage(_image, true); _myocardialDiagnosis.DetectOneImage(rawImage, false); } else if (_thyroidClassification != null) { var rawImage = BitmapHelper.BitmapToRawImage(_image, true); _thyroidClassification.DetectOneImage(rawImage); } else { var rawImage = BitmapHelper.BitmapToRawImage(_image, true); _diagnosis.DetectOneImage(rawImage, true, true, true); } } } private void TimerCallBack(object state) { var processRAMUsedValue = Math.Round(_processRAMCounter.NextValue() / 1024 / 1024 / 1024, 2); var processCpuUsedValue = Math.Round(_processCPUCounter.NextValue(), 2); var ramUsedRate = Math.Round(processRAMUsedValue / _ramSize * 100, 2); var cpuUsedRate = Math.Round(processCpuUsedValue / Environment.ProcessorCount, 2); Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { if (UsedStatusText.Visibility == Visibility.Collapsed) { UsedStatusText.Visibility = Visibility.Visible; } UsedStatusText.Text = $"当前CPU占用率: {cpuUsedRate}%\r\n当前内存占用率:{ramUsedRate}%,({processRAMUsedValue}GB/{_ramSize}GB) "; })); } private T FindVisualChild(DependencyObject obj) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { var child = VisualTreeHelper.GetChild(obj, i); if (child is T variable) { return variable; } return FindVisualChild(child); } return null; } private void RefreshUsedStatus() { if (SettingConfig.Instance.IsSimulation) { _usedStatustTimer.Change(0, 1000); if (_cpuUsed != SettingConfig.Instance.CPUUsed) { StopCPUUsed(); StartCPUUsed(); } if (_ramUsed != SettingConfig.Instance.RAMUsed) { StopRAMUsed(); StartRAMUsed(); } } else { _usedStatustTimer.Change(-1, 1000); Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { if (UsedStatusText.Visibility == Visibility.Visible) { UsedStatusText.Visibility = Visibility.Collapsed; UsedStatusText.Text = ""; } })); if (_cpuUsed != 0) { StopCPUUsed(); } if (_ramUsed != 0) { StopRAMUsed(); } } } private void OnOpenSettingWindow(object sender, RoutedEventArgs e) { var settingWindow = new SettingWindow(); settingWindow.ShowDialog(); } protected override void OnClosing(CancelEventArgs e) { PlayerManager.Instance.PlayerChanged -= OnPlayerChanged; PlayerManager.Instance.Dispose(); if (_imageProvider != null) { ((ImageProvider)_imageProvider).Dispose(); } StopPlay(); CloseDiagnosis(); CleanAIResult(); SettingConfig.Instance.ValueChangedEvent -= OnValueChanged; StopCPUUsed(); StopRAMUsed(); _gcController.Close(); Logger.Info("Close the program!"); base.OnClosing(e); } private void StartDiagnosis() { switch (SettingConfig.Instance.AIDiagnosisType) { case AIDiagnosisType.Myocardial: if (_myocardialDiagnosis == null) { if (_myocardialImageProvider == null) { _myocardialImageProvider = new ImageProvider(); } var cpuNumber = 1; switch (SettingConfig.Instance.Performance) { case EnumPerformance.Low: cpuNumber = 1; break; case EnumPerformance.Medium: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.4); break; case EnumPerformance.High: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.6); break; case EnumPerformance.Maximum: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.8); break; } var modelFileFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks"); _myocardialDiagnosis = new MyocardialDiagnosis(false, false, null, _myocardialImageProvider, cpuNumber, modelFileFolder, EnumMyocardialInferWorkName.HumanMyocardialSegment, (EnumDetectMode)SettingConfig.Instance.DetectMode, SettingConfig.Instance.DetectTps, SettingConfig.Instance.IntervalTime); _myocardialDiagnosis.CompleteMyocardialImg = SettingConfig.Instance.CompleteMyocardialImg; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Initialize Diagnosis"); } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.StartEvaluation += OnStartEvaluation; _myocardialDiagnosis.FinishEvaluation += OnMyocardialFinishEvaluation; _myocardialDiagnosis.NotifyError += OnNotifyError; _myocardialDiagnosis.NotifyLog += OnNotifyLog; _myocardialDiagnosis.Start(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start Diagnosis"); } Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Visible; }); break; case AIDiagnosisType.MiceMyocardial: if (_myocardialDiagnosis == null) { if (_myocardialImageProvider == null) { _myocardialImageProvider = new ImageProvider(); } var cpuNumber = 1; switch (SettingConfig.Instance.Performance) { case EnumPerformance.Low: cpuNumber = 1; break; case EnumPerformance.Medium: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.4); break; case EnumPerformance.High: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.6); break; case EnumPerformance.Maximum: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.8); break; } var modelFileFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks"); _myocardialDiagnosis = new MyocardialDiagnosis(false, false, null, _myocardialImageProvider, cpuNumber, modelFileFolder, EnumMyocardialInferWorkName.MiceMyocardialSegment, (EnumDetectMode)SettingConfig.Instance.DetectMode, SettingConfig.Instance.DetectTps, SettingConfig.Instance.IntervalTime); _myocardialDiagnosis.CompleteMyocardialImg = SettingConfig.Instance.CompleteMyocardialImg; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Initialize Diagnosis"); } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.StartEvaluation += OnStartEvaluation; _myocardialDiagnosis.FinishEvaluation += OnMyocardialFinishEvaluation; _myocardialDiagnosis.NotifyError += OnNotifyError; _myocardialDiagnosis.NotifyLog += OnNotifyLog; _myocardialDiagnosis.Start(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start Diagnosis"); } Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Visible; }); break; case AIDiagnosisType.RatMyocardial: if (_myocardialDiagnosis == null) { if (_myocardialImageProvider == null) { _myocardialImageProvider = new ImageProvider(); } var cpuNumber = 1; switch (SettingConfig.Instance.Performance) { case EnumPerformance.Low: cpuNumber = 1; break; case EnumPerformance.Medium: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.4); break; case EnumPerformance.High: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.6); break; case EnumPerformance.Maximum: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.8); break; } var modelFileFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks"); _myocardialDiagnosis = new MyocardialDiagnosis(false, false, null, _myocardialImageProvider, cpuNumber, modelFileFolder, EnumMyocardialInferWorkName.RatMyocardialSegment, (EnumDetectMode)SettingConfig.Instance.DetectMode, SettingConfig.Instance.DetectTps, SettingConfig.Instance.IntervalTime); _myocardialDiagnosis.CompleteMyocardialImg = SettingConfig.Instance.CompleteMyocardialImg; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Initialize Diagnosis"); } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.StartEvaluation += OnStartEvaluation; _myocardialDiagnosis.FinishEvaluation += OnMyocardialFinishEvaluation; _myocardialDiagnosis.NotifyError += OnNotifyError; _myocardialDiagnosis.NotifyLog += OnNotifyLog; _myocardialDiagnosis.Start(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start Diagnosis"); } Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Visible; }); break; case AIDiagnosisType.Normal: case AIDiagnosisType.OrganIdentification: var aiModuleType = SettingConfig.Instance.AIDiagnosisType == AIDiagnosisType.Normal ? EnumAIModule.NotSpecified : EnumAIModule.OrganIdentification; Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Collapsed; }); if (_diagnosis == null) { if (_imageProvider == null) { _imageProvider = new ImageProvider(); } bool enableLesionSeg; bool enableDescription; if (SettingConfig.Instance.DisplayType == EnumDisplayType.Border) { enableLesionSeg = false; enableDescription = false; } else { enableLesionSeg = true; enableDescription = true; } _diagnosis = new AIDiagnosisSDK.AIDiagnosis(aiModuleType, false, false, AppDomain.CurrentDomain.BaseDirectory, _imageProvider, SettingConfig.Instance.Performance, SettingConfig.Instance.DetectMode, !SettingConfig.Instance.CropImage, SettingConfig.Instance.DetectTps, SettingConfig.Instance.IntervalTime, "", enableLesionSeg, enableDescription, true, false, 0, 0, enableDescription); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Initialize Diagnosis"); } if (_diagnosis != null) { _diagnosis.StartEvaluation += OnStartEvaluation; _diagnosis.FinishEvaluation += OnFinishEvaluation; _diagnosis.NotifyError += OnNotifyError; _diagnosis.NotifyLog += OnNotifyLog; _diagnosis.Start(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start Diagnosis"); } break; case AIDiagnosisType.ThyroidClassification: Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Collapsed; }); if (_thyroidClassification == null) { if (_thyroidClassificationImageProvider == null) { _thyroidClassificationImageProvider = new ImageProvider(); } var cpuNumber = 1; switch (SettingConfig.Instance.Performance) { case EnumPerformance.Low: cpuNumber = 1; break; case EnumPerformance.Medium: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.4); break; case EnumPerformance.High: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.6); break; case EnumPerformance.Maximum: cpuNumber = (int)Math.Ceiling(Environment.ProcessorCount * 0.8); break; } var modelFileFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Networks"); _thyroidClassification = new ThyroidClassification(cpuNumber, modelFileFolder, false, false, null, _thyroidClassificationImageProvider, SettingConfig.Instance.DetectMode, SettingConfig.Instance.DetectTps, SettingConfig.Instance.IntervalTime, true); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Initialize ThyroidClassificaion"); } if (_thyroidClassification != null) { _thyroidClassification.StartEvaluation += OnStartEvaluation; _thyroidClassification.FinishEvaluation += OnThyroidClassificationFinishEvaluationNotification; _thyroidClassification.NotifyError += OnNotifyError; _thyroidClassification.Start(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start ThyroidClassificaion"); } Dispatcher.Invoke(() => { ExtractContoursBtn.Visibility = Visibility.Visible; }); break; } } private void OnNotifyLog(object sender, LogEventArgs e) { } private void OnNotifyError(object sender, ErrorEventArgs e) { Dispatcher.Invoke(() => { ErrorInfoText.Text = "AI推理过程中发生错误:" + e.GetException(); }); } private void CloseDiagnosis() { if (_diagnosis != null) { _diagnosis.StartEvaluation -= OnStartEvaluation; _diagnosis.FinishEvaluation -= OnFinishEvaluation; _diagnosis.NotifyError -= OnNotifyError; _diagnosis.Close(); _diagnosis = null; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Close Diagnosis"); } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.StartEvaluation -= OnStartEvaluation; _myocardialDiagnosis.NotifyError -= OnNotifyError; _myocardialDiagnosis.NotifyLog -= OnNotifyLog; _myocardialDiagnosis.FinishEvaluation -= OnMyocardialFinishEvaluation; _myocardialDiagnosis.Close(); _myocardialDiagnosis = null; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Close Diagnosis"); } if (_thyroidClassification != null) { _thyroidClassification.StartEvaluation -= OnStartEvaluation; _thyroidClassification.NotifyError -= OnNotifyError; _thyroidClassification.FinishEvaluation -= OnThyroidClassificationFinishEvaluationNotification; _thyroidClassification.Close(); _thyroidClassification = null; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Close ThyroidClassificaion"); } } private void OnThyroidClassificationFinishEvaluationNotification(object sender, ThyroidSectionResult thyroidSectionResult) { try { var elapsedTime = Environment.TickCount - _startTickCount; if (thyroidSectionResult != null) { Logger.Info("Finish AI Detection"); var labelName = ""; var confidence = thyroidSectionResult.Score.ToString("0.0" + "%"); switch (thyroidSectionResult.ThyroidSectionType) { case EnumThyroidSectionType.Null: labelName = "其他切面"; break; case EnumThyroidSectionType.LeftCrossSection: labelName = "左侧甲状腺横切切面"; break; case EnumThyroidSectionType.RightCrossSection: labelName = "右侧甲状腺横切切面"; break; case EnumThyroidSectionType.LongitudinalSection: labelName = "甲状腺纵切切面"; break; case EnumThyroidSectionType.IsthmusSection: labelName = "甲状腺峡部切面"; break; } Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { ResultInfoText.Text = $"{labelName}\r\n置信度:{confidence}"; ElapsedTimeText.Text = $"AI耗时: {elapsedTime}ms"; })); } else { Logger.Info("diag Result is null!"); } } catch { } } private void OnMyocardialFinishEvaluation(object sender, DetectedObject diagResult) { try { _currentDetectedObject = diagResult; if (diagResult != null) { Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Finish AI Detection"); var elapsedTime = Environment.TickCount - _startTickCount; UpdateTransforms(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Transforms"); // 删掉旧的Roi和Organ RemoveRois(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Remove Rois"); RemoveOrgans(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Remove Ograns"); var roiList = new List(); if (diagResult.Contours != null && diagResult.Contours.Count() > 0) { var bondingBox = new Infrastucture.MyRect(diagResult.BoundingBox.Left, diagResult.BoundingBox.Top, diagResult.BoundingBox.Width, diagResult.BoundingBox.Height); var roi = new Roi(EnumOrgans.Null, bondingBox, diagResult.Contours, System.Windows.Media.Brushes.Green, string.Empty, diagResult.Confidence.ToString("0.0" + "%"), null); roiList.Add(roi); } _rois = roiList; // 更新画布上显示的Roi和Organ UpdateRois(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Rois"); if (SettingConfig.Instance.IsShowOrgansContour) { UpdateOrgans(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Organs"); } var priorityScore = diagResult.Confidence; // 更新显示在侧面的诊断详情 string strResult = ResultInfoToString(priorityScore); Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { ResultInfoText.Text = strResult; ElapsedTimeText.Text = $"AI耗时: {elapsedTime}ms"; })); } else { Logger.Info("diag Result is null!"); } } catch { } } private void StopDiagnosis() { if (_diagnosis != null) { _diagnosis.StartEvaluation -= OnStartEvaluation; _diagnosis.FinishEvaluation -= OnFinishEvaluation; _diagnosis.NotifyError -= OnNotifyError; _diagnosis.Stop(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Stop Diagnosis"); } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.NotifyError -= OnNotifyError; _myocardialDiagnosis.StartEvaluation -= OnStartEvaluation; _myocardialDiagnosis.FinishEvaluation -= OnMyocardialFinishEvaluation; _myocardialDiagnosis.NotifyLog -= OnNotifyLog; _myocardialDiagnosis.Stop(); _myocardialDiagnosis = null; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Close Diagnosis"); } if (_thyroidClassification != null) { _thyroidClassification.StartEvaluation -= OnStartEvaluation; _thyroidClassification.NotifyError -= OnNotifyError; _thyroidClassification.FinishEvaluation -= OnThyroidClassificationFinishEvaluationNotification; _thyroidClassification.Close(); _thyroidClassification = null; Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Close ThyroidClassificaion"); } } private void OnPlayClick(object sender, RoutedEventArgs e) { if (_player != null) { if (_myocardialDiagnosis != null) { ExtractContoursBtn.IsChecked = false; } _player.Continue(); _isPlaying = true; PlayButton.Visibility = Visibility.Collapsed; PauseButton.Visibility = Visibility.Visible; } } private void OnPauseClick(object sender, RoutedEventArgs e) { if (_player != null) { _player.Pause(); _isPlaying = false; PlayButton.Visibility = Visibility.Visible; PauseButton.Visibility = Visibility.Collapsed; DetectImage(); } } private void OnValueChanged(object sender, List e) { if (e.Contains(SettingProperty.CompleteMyocardialImg)) { if (_myocardialDiagnosis != null) { _myocardialDiagnosis.CompleteMyocardialImg = SettingConfig.Instance.CompleteMyocardialImg; } } if (e.Contains(SettingProperty.IsSimulation) || e.Contains(SettingProperty.RAMUsed) || e.Contains(SettingProperty.CPUUsed))//当模拟占用功能变更 { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} IsSimulation:\"{SettingConfig.Instance.IsSimulation}\",CPU Used:\"{SettingConfig.Instance.CPUUsed}\",RAM Used:\"{SettingConfig.Instance.RAMUsed}\""); RefreshUsedStatus(); } //当AI功能变更 if (e.Contains(SettingProperty.Performance) || e.Contains(SettingProperty.IsEnableAI) || e.Contains(SettingProperty.AIDiagnosisType)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Enable AI:\"{SettingConfig.Instance.IsEnableAI}\",AI Performance:\"{SettingConfig.Instance.Performance}\",Detect Mode:\"{SettingConfig.Instance.DetectMode}\",Is Cropped:\"{!SettingConfig.Instance.CropImage}\",Detect Tps:\"{SettingConfig.Instance.DetectTps}\",Interval Time:\"{SettingConfig.Instance.IntervalTime}\""); CloseDiagnosis(); CleanAIResult(); if (SettingConfig.Instance.IsEnableAI) { StartDiagnosis(); if (SettingConfig.Instance.IsVideo == false)//当源文件为图片的情况下,就重新Show一次给AI提供图片信息。 { DetectImage(); } } } else { if (SettingConfig.Instance.IsEnableAI) { if (e.Contains(SettingProperty.CropImage)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Is Cropped:\"{!SettingConfig.Instance.CropImage}\""); if (_diagnosis != null) { _diagnosis.IsCropped = !SettingConfig.Instance.CropImage; } } if (e.Contains(SettingProperty.DetectTps)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Detect Tps:\"{SettingConfig.Instance.DetectTps}\""); if (_diagnosis != null) { _diagnosis.DetectTps = SettingConfig.Instance.DetectTps; } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.DetectTps = SettingConfig.Instance.DetectTps; } if (_thyroidClassification != null) { _thyroidClassification.DetectTps = SettingConfig.Instance.DetectTps; } } if (e.Contains(SettingProperty.IntervalTime)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Interval Time:\"{SettingConfig.Instance.IntervalTime}\""); if (_diagnosis != null) { _diagnosis.IntervalTime = SettingConfig.Instance.IntervalTime; } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.IntervalTime = SettingConfig.Instance.IntervalTime; } if (_thyroidClassification != null) { _thyroidClassification.IntervalTime = SettingConfig.Instance.IntervalTime; } } if (e.Contains(SettingProperty.DetectMode)) { Logger.Info($"Detect Mode:\"{SettingConfig.Instance.DetectMode}\""); if (_diagnosis != null) { _diagnosis.DetectMode = SettingConfig.Instance.DetectMode; } if (_myocardialDiagnosis != null) { _myocardialDiagnosis.DetectMode = (EnumDetectMode)SettingConfig.Instance.DetectMode; } if (_thyroidClassification != null) { _thyroidClassification.DetectMode = SettingConfig.Instance.DetectMode; } } } } if (e.Contains(SettingProperty.IsShowOrgansContour)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Is Show Organs Contour:\"{SettingConfig.Instance.IsShowOrgansContour}\""); if (SettingConfig.Instance.IsShowOrgansContour) { UpdateOrgans(); } else { RemoveOrgans(); } } if (e.Contains(SettingProperty.DisplayType)) { Logger.Info($"Thread Id: {Thread.CurrentThread.ManagedThreadId} Display Type:\"{SettingConfig.Instance.DisplayType}\""); if (SettingConfig.Instance.DisplayType == EnumDisplayType.Border) { if (_diagnosis != null) { _diagnosis.EnableLesionDescription = false; _diagnosis.EnableOrganDescription = false; _diagnosis.EnableLesionSeg = false; } } else { if (_diagnosis != null) { _diagnosis.EnableLesionDescription = true; _diagnosis.EnableOrganDescription = true; _diagnosis.EnableLesionSeg = true; } } UpdateRois(); } if (e.Contains(SettingProperty.IsCycleImagesPlay)) { if (SettingConfig.Instance.IsCycleImagesPlay)//当循环图片播放未开启 { var imagesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestImages"); if (Directory.Exists(imagesPath)) { string[] images = Directory.GetFiles(imagesPath); _cycleImagesList = new List(); foreach (string image in images) { if (image.EndsWith(".jpg") || image.EndsWith(".bmp") || image.EndsWith(".png") || image.EndsWith(".jpeg")) { _cycleImagesList.Add(image); } } if (_cycleImagesList.Count == 0) { Dispatcher.Invoke(() => { MessageBox.Show($"Cycle Image Test Failed.The List is empty. Please check."); Close(); }); } StartCycleImagesTest(); } else { Dispatcher.Invoke(() => { MessageBox.Show($"TestImages file missed. The path is {imagesPath}. Please check."); Close(); }); } } } } private void StartCycleImagesTest() { if (_cycleTestThread == null || !_cycleTestThread.IsAlive) { _cycleTestThread = new Thread(() => DoCycleImagesTest()) { IsBackground = true, Name = "CycleImageTest" }; _cycleTestThread.Start(); } } private void DoCycleImagesTest() { while (true) { foreach (var image in _cycleImagesList) { Thread.Sleep(SettingConfig.Instance.IntervalTime); SettingConfig.Instance.UltrasoundImageSourceType = Sourcetype.PictureOrVideoFile; SettingConfig.Instance.UltrasoundImageSource = image; SettingConfig.Instance.RaiseSavedEvent(new List { SettingProperty.UltrasoundImageSourceType, SettingProperty.UltrasoundImageSource }); } } } private void StartCPUUsed() { _cpuUsed = SettingConfig.Instance.CPUUsed; if (SettingConfig.Instance.CPUUsed == 0) { return; } for (int i = 0; i < Environment.ProcessorCount; i++) { var cpuThread = new Thread(() => { try { Stopwatch watch = new Stopwatch(); watch.Start(); while (true) { //Make the loop go on for "percentage" milliseconds then sleep the //remaining percentage milliseconds. So 40% utilization means work 40ms and sleep 60ms. if (watch.ElapsedMilliseconds > SettingConfig.Instance.CPUUsed) { Thread.Sleep(100 - SettingConfig.Instance.CPUUsed); watch.Reset(); watch.Start(); } } } catch { } }) { Name = "CPUUsedThread" + i } ; cpuThread.Start(); _cpuThreads.Add(cpuThread); } } private void StopCPUUsed() { var oldThread = _cpuThreads.ToList(); Thread.Sleep(100 - SettingConfig.Instance.CPUUsed); foreach (var t in oldThread) { try { t.Abort(); } catch { } } _cpuUsed = 0; } private void StopRAMUsed() { if (_ramThread != null && !_ramThread.IsCompleted) { Task.WaitAll(_ramThread); } _ramUsedList = null; System.GC.Collect(); System.GC.WaitForPendingFinalizers(); _ramUsed = 0; } private void StartRAMUsed() { _ramThread = new Task(() => { _ramUsed = SettingConfig.Instance.RAMUsed; _ramUsedList = new List(); var ramCoefficient = _ramSize / 16;//只有当内存为16GB的时候,内存系数为1,否则内存系数按比例变化。 int ramListCount; switch (SettingConfig.Instance.RAMUsed / 10) { case 0: return; case 1: ramListCount = 5; break; case 2: ramListCount = 10; break; case 3: ramListCount = 15; break; case 4: ramListCount = 20; break; case 5: ramListCount = 25; break; case 6: ramListCount = 30; break; case 7: ramListCount = 35; break; case 8: ramListCount = 40; break; case 9: ramListCount = 45; break; default: throw new Exception($"RAM Used Failed. RAMUsed value is {SettingConfig.Instance.RAMUsed}"); } for (int ramlist = 0; ramlist < ramListCount; ramlist++) { int[] ramUsedArray = new int[(int)(82000000 * ramCoefficient)];//内存系数为1时,约占用320MB的内存,约为16G的百分之2 for (int i = 0; i < ramUsedArray.Length; i++) { ramUsedArray[i] = i; } _ramUsedList.Add(ramUsedArray); ramUsedArray = null; } }); _ramThread.Start(); } private void StopPlay() { if (_player != null) { _player.InputFrameReceived -= OnInputFrameReceived; _player.FPSChanged -= OnFPSChanged; _player.Stop(); } _isPlaying = false; PlayButton.IsEnabled = false; PlayButton.Content = "暂停播放"; UsImage.Source = null; FPSText.Text = ""; } private void CleanAIResult() { Dispatcher.Invoke(() => { RemoveOrgans(); RemoveRois(); ResultInfoText.Text = ""; ElapsedTimeText.Text = ""; }); _rois = null; _conclusions = null; _organs = null; } private void OnInputFrameReceived(object sender, Bitmap e) { _image = e.Clone(new Rectangle(0, 0, e.Width, e.Height), e.PixelFormat); Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { var bitmapimage = BitmapHelper.BitmapToBitmapImage(e); UsImage.Source = bitmapimage; })); } private void OnFPSChanged(object sender, int fps) { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { if (fps > 0) { FPSText.Text = $"实际帧率:{fps} fps"; } else { FPSText.Text = ""; } })); } private void OnFinishEvaluation(object sender, AIDiagResultPerImg diagResult) { try { var organString = ""; if (diagResult != null) { Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Finish AI Detection"); var elapsedTime = Environment.TickCount - _startTickCount; // 整理诊断结果中相关信息 List rois = new List(); Dictionary> conclusions = new Dictionary>(); List organs = new List(); UpdateTransforms(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Transforms"); foreach (var resultsPerOrgan in diagResult.DiagResultsForEachOrgan) { organString = organString + resultsPerOrgan.Organ.ToString(); foreach (var detectedObject in resultsPerOrgan.DetectedObjects) { bool hasConclusion = true; bool hasRoi = false; string labelName = string.Empty; System.Windows.Media.Brush brush = null; string confidence = string.Empty; switch (resultsPerOrgan.Organ) { case EnumOrgans.Breast: switch (detectedObject.Label) { case 0: labelName = "未见明显异常"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 1: labelName = "脂肪瘤"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 2: labelName = "BIRads2"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 3: labelName = "BIRads3"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 4: labelName = "BIRads4a"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 5: labelName = "BIRads4b"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 6: labelName = "BIRads4c"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 7: labelName = "BIRads5"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; default: hasConclusion = false; break; } break; case EnumOrgans.Liver: switch (detectedObject.Label) { case 0: labelName = "未见明显异常"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 1: labelName = "肝内强回声灶"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 2: labelName = "肝血管瘤"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 3: labelName = "肝囊肿"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 4: labelName = "肝癌可能"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 5: labelName = "脂肪肝"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 6: labelName = "肝脏弥漫性病变"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 7: labelName = "肝硬化"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 8: labelName = "多囊肝"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; default: hasConclusion = false; break; } break; case EnumOrgans.GallBladder: switch (detectedObject.Label) { case 0: labelName = "未见明显异常"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 1: labelName = "胆囊息肉"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 2: labelName = "胆囊结石"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 3: labelName = "胆囊泥沙样结石"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 4: labelName = "胆汁存在异常"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 5: labelName = "胆囊壁增厚增粗"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 6: labelName = "腺肌病"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; default: hasConclusion = false; break; } break; case EnumOrgans.Thyroid: switch (detectedObject.Label) { case 0: labelName = "未见明显异常"; confidence = detectedObject.Confidence.ToString("0.0" + "%"); break; case 1: labelName = "TIRADS 2"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 2: labelName = "TIRADS 3"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 3: labelName = "TIRADS 4a"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 4: labelName = "TIRADS 4b"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 5: labelName = "TIRADS 4c"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 6: labelName = "TIRADS 5"; brush = System.Windows.Media.Brushes.Red; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; case 7: labelName = "存在弥漫性疾病"; brush = System.Windows.Media.Brushes.Orange; confidence = detectedObject.Confidence.ToString("0.0" + "%"); hasRoi = true; break; default: hasConclusion = false; break; } break; default: hasConclusion = false; break; } if (hasConclusion) { if (hasRoi) { var bondingBox = new MyRect(detectedObject.BoundingBox.Left, detectedObject.BoundingBox.Top, detectedObject.BoundingBox.Width, detectedObject.BoundingBox.Height); var roi = new Roi(resultsPerOrgan.Organ, bondingBox, detectedObject.Contours, brush, labelName, confidence, detectedObject.Descriptions.ToList()); rois.Add(roi); } else { if (!conclusions.ContainsKey(resultsPerOrgan.Organ)) { conclusions.Add(resultsPerOrgan.Organ, new List()); } conclusions[resultsPerOrgan.Organ].Add(labelName + "," + detectedObject.Confidence.ToString("0.0" + " % "));//结论,置信度 } } } var organBondingBox = new MyRect(resultsPerOrgan.OrganBoundBox.Left, resultsPerOrgan.OrganBoundBox.Right, resultsPerOrgan.OrganBoundBox.Width, resultsPerOrgan.OrganBoundBox.Height); //var organContor = resultsPerOrgan.OrganContours[0].Select(c => new Infrastucture.MyPoint(c.X, c.Y)).ToArray(); organs.Add(new Organ(resultsPerOrgan.Organ.ToString(), organBondingBox, resultsPerOrgan.OrganContours)); } // 删掉旧的Roi和Organ RemoveRois(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Remove Rois"); RemoveOrgans(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Remove Ograns"); // 设置 _rois = rois; _conclusions = conclusions; _organs = organs; // 更新画布上显示的Roi和Organ UpdateRois(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Rois"); if (SettingConfig.Instance.IsShowOrgansContour) { UpdateOrgans(); Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Update Organs"); } var priorityScore = diagResult.PriorityScore; // 更新显示在侧面的诊断详情 string strResult = ResultInfoToString(priorityScore); Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { ResultInfoText.Text = $"Organ:{organString}\r\n" + strResult; ElapsedTimeText.Text = $"AI耗时: {elapsedTime}ms"; })); } else { Logger.Info("diag Result is null!"); } } catch { } } private string ResultInfoToString(float priorityScore) { string results = $"Priority Score: {priorityScore}"; Dictionary resultPerOrgan = new Dictionary(); // 取出结论 if (_conclusions != null) { foreach (var organ in _conclusions.Keys) { if (!resultPerOrgan.ContainsKey(organ)) { resultPerOrgan.Add(organ, string.Empty); } foreach (var conclusion in _conclusions[organ]) { string existed = resultPerOrgan[organ]; if (existed.Length != 0) { existed += "\r\n"; } var arrConclusion = conclusion.Split(','); var strConclusion = arrConclusion[0]; var strconfidence = arrConclusion[1]; existed += $"{organ}: {strConclusion}\r\n"; existed += $"置信度: {strconfidence}\r\n"; resultPerOrgan[organ] = existed; } } } // 取出roi的描述 if (_rois != null) { for (int ni = 0; ni < _rois.Count; ni++) { var roi = _rois[ni]; if (!resultPerOrgan.ContainsKey(roi.Organ)) { resultPerOrgan.Add(roi.Organ, string.Empty); } string existed = resultPerOrgan[roi.Organ]; if (existed.Length != 0) { existed += "\r\n"; } string newroi = "病灶 " + ni + ":\r\n"; newroi += $"{roi.Organ}: {roi.LabelName}\r\n"; newroi += $"置信度: {roi.Confidence}\r\n"; if (roi.Descriptions != null) { newroi += DescriptionsToString(roi.Descriptions); } existed += newroi; resultPerOrgan[roi.Organ] = existed; } } // 合到一起 foreach (var organ in resultPerOrgan.Keys) { if (results.Length != 0) { results += "\r\n"; } results += resultPerOrgan[organ]; } return results; } private string DescriptionsToString(List descriptions) { string strDescription = string.Empty; var lesionSize = (DescriptionLesionSize)descriptions.Find(x => x.Type == EnumDescriptionType.LesionSize); if (lesionSize != null) { string strSize = string.Empty; if (lesionSize.Value.HorizontalLengthInPixel > lesionSize.Value.VerticalLengthInPixel) { strSize = "<1"; } else { strSize = ">=1"; } strDescription += "纵横比:" + strSize + "\r\n"; } var shape = (DescriptionShape)descriptions.Find(x => x.Type == EnumDescriptionType.Shape); if (shape != null) { string strShape = string.Empty; switch (shape.Value) { case EnumDesShapeValue.Oval: strShape = "椭圆形"; break; case EnumDesShapeValue.Round: strShape = "圆形"; break; case EnumDesShapeValue.Irregular: strShape = "不规则"; break; } strDescription += "形状:" + strShape + "\r\n"; } var orientation = (DescriptionOrientation)descriptions.Find(x => x.Type == EnumDescriptionType.Orientation); if (orientation != null) { string strOrientation = string.Empty; switch (orientation.Value) { case EnumDesOrientationValue.Parallel: strOrientation = "平行"; break; case EnumDesOrientationValue.NonParallel: strOrientation = "非平行"; break; } strDescription += "方向:" + strOrientation + "\r\n"; var echo = (DescriptionEchoPattern)descriptions.Find(x => x.Type == EnumDescriptionType.EchoPattern); if (echo != null) { string strEcho = string.Empty; switch (echo.Value) { case EnumDesEchoPatternValue.Anechoic: strEcho = "无回声"; break; case EnumDesEchoPatternValue.Hypoechoic: strEcho = "低回声"; break; case EnumDesEchoPatternValue.Hyperechoic: strEcho = "高回声"; break; case EnumDesEchoPatternValue.Strongechoic: strEcho = "强回声"; break; case EnumDesEchoPatternValue.Complex: strEcho = "混合回声"; break; case EnumDesEchoPatternValue.Isoechoic: strEcho = "等回声"; break; } strDescription += "回声类型:" + strEcho + "\r\n"; } var bound = (DescriptionLesionBoundary)descriptions.Find(x => x.Type == EnumDescriptionType.LesionBoundary); if (bound != null) { string strBound = string.Empty; switch (bound.Value) { case EnumDesLesionBoundaryValue.AbruptInterface: strBound = "清晰"; break; case EnumDesLesionBoundaryValue.EchogenicHalo: strBound = "模糊"; break; } strDescription += "边界:" + strBound + "\r\n"; } var margin = (DescriptionMargin)descriptions.Find(x => x.Type == EnumDescriptionType.Margin); if (margin != null) { string strMargin = string.Empty; switch (margin.Value) { case EnumDesMarginValue.Circumscribed: strMargin = "光整"; break; case EnumDesMarginValue.NonCircumscribed: strMargin = "不光整"; break; } strDescription += "边缘:" + strMargin + "\r\n"; } } var calcifications = (DescriptionCalcifications)descriptions.Find(x => x.Type == EnumDescriptionType.Calcifications); if (calcifications != null) { string strCalcifications = string.Empty; switch (calcifications.Value) { case EnumCalcificationsValue.NoCalcifications: strCalcifications = "无钙化"; break; case EnumCalcificationsValue.Macrocalcifications: strCalcifications = "粗钙化"; break; case EnumCalcificationsValue.Microcalcifications: strCalcifications = "微钙化"; break; } strDescription += "钙化:" + strCalcifications + "\r\n"; } var thyroidMargin = (DescriptionThyroidMargin)descriptions.Find(x => x.Type == EnumDescriptionType.ThyroidMargin); if (thyroidMargin != null) { string strMargin = string.Empty; switch (thyroidMargin.Value) { case EnumDesThyroidMarginValue.Smooth: strMargin = "光滑"; break; case EnumDesThyroidMarginValue.IllDefined: strMargin = "不清晰"; break; case EnumDesThyroidMarginValue.Lobulated: strMargin = "分叶"; break; case EnumDesThyroidMarginValue.Irregular: strMargin = "不规则"; break; case EnumDesThyroidMarginValue.ExtraThyroidalExtension: strMargin = "甲状腺外扩"; break; } strDescription += "边缘:" + strMargin + "\r\n"; } var thyroidShape = (DescriptionThyroidShape)descriptions.Find(x => x.Type == EnumDescriptionType.ThyroidShape); if (thyroidShape != null) { string strShape = string.Empty; switch (thyroidShape.Value) { case EnumDesThyroidShapeValue.WiderThanTall: strShape = "水平位"; break; case EnumDesThyroidShapeValue.TallThanWider: strShape = "垂直位"; break; } strDescription += "形状:" + strShape + "\r\n"; } var thyroidEchogenicFoci = (DescriptionThyroidEchogenicFoci)descriptions.Find(x => x.Type == EnumDescriptionType.ThyroidEchogenicFoci); if (thyroidEchogenicFoci != null) { string strEchogenicFoci = string.Empty; switch (thyroidEchogenicFoci.Value) { case EnumDesThyroidEchogenicFociValue.NoCifications: strEchogenicFoci = "无钙化"; break; case EnumDesThyroidEchogenicFociValue.Coarsecalcifications: strEchogenicFoci = "粗钙化"; break; case EnumDesThyroidEchogenicFociValue.Microcalcifications: strEchogenicFoci = "微钙化"; break; } strDescription += "局灶性强回声:" + strEchogenicFoci + "\r\n"; } var thyroidEchoPattern = (DescriptionThyroidEchoPattern)descriptions.Find(x => x.Type == EnumDescriptionType.ThyroidEchoPattern); if (thyroidEchoPattern != null) { string strEcho = string.Empty; switch (thyroidEchoPattern.Value) { case EnumDesThyroidEchoPatternValue.Anechoic: strEcho = "无回声"; break; case EnumDesThyroidEchoPatternValue.Hyperechoic: strEcho = "高回声"; break; case EnumDesThyroidEchoPatternValue.Isoechoic: strEcho = "等回声"; break; case EnumDesThyroidEchoPatternValue.Hypoechoic: strEcho = "低回声"; break; case EnumDesThyroidEchoPatternValue.HypoechoicLower: strEcho = "极低回声"; break; } strDescription += "回声类型:" + strEcho + "\r\n"; } return strDescription; } private void OnStartEvaluation(object sender, EventArgs e) { Logger.Info("Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + " Start AI Detection"); _startTickCount = Environment.TickCount; Dispatcher.Invoke(() => { ErrorInfoText.Text = null; }); } private void RemoveRois() { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { // 移除所有旧的ROI if (_rois != null) { for (int ni = 0; ni < _rois.Count; ni++) { RemoveRoiInCanvas(ni.ToString(), _rois[ni].Contours.Length); } } })); } private void RemoveOrgans() { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { // 移除所有旧的Organ if (_organs != null) { for (int ni = 0; ni < _organs.Count; ni++) { RemoveOrganInCanvas(_organs[ni]); } } })); } private void UpdateTransforms() { if (_image != null) { // 使图片保持长宽比例,居中显示,需要平移和缩放 var imgWidth = _image.Width; var imgHeight = _image.Height; var canvasWidth = UsCanvas.ActualWidth; var canvasHeight = UsCanvas.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; } _transform = new MyTransform(scale, offsetX, offsetY); } } private void UpdateRois(bool isLine = false) { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { if (_rois != null) { for (int ni = 0; ni < _rois.Count; ni++) { DrawRoiInCanvas(_rois[ni], ni.ToString(), isLine); } } })); } private void UpdateOrgans() { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { if (_organs != null) { for (int ni = 0; ni < _organs.Count; ni++) { DrawOrganInCanvas(_organs[ni]); } } })); } private void DrawRoiInCanvas(Roi roi, string roiName, bool isLine = false) { if (_transform == null) { return; } // 已有则删除 RemoveRoiInCanvas(roiName, roi.Contours.Length); int contoursLength = roi.Contours.Length; if ((!_isPlaying || !SettingConfig.Instance.IsVideo || SettingConfig.Instance.DisplayType == EnumDisplayType.Contour) && contoursLength > 0)//当源图像为视频暂停播放时或者当源图像为图片时或者病灶显示方式为轮廓时,都显示为轮廓,其他情况为画框 { //病灶画轮廓 for (int ni = 0; ni < contoursLength; ni++) { Point2D[] contour = roi.Contours[ni]; PointCollection contourPoints = new PointCollection(); for (int nj = 0; nj < contour.Length; nj++) { var point = new System.Windows.Point(contour[nj].X, contour[nj].Y); var transPoint = _transform.Transform(point); contourPoints.Add(transPoint); } if (isLine) { System.Windows.Shapes.Polyline contourDraw = new System.Windows.Shapes.Polyline(); contourDraw.Points = contourPoints; contourDraw.StrokeThickness = 3; contourDraw.Stroke = roi.Color; contourDraw.StrokeLineJoin = PenLineJoin.Round; UsCanvas.Children.Add(contourDraw); string contourName = "ROI_Contour_" + roiName + "_" + ni; UsCanvas.RegisterName(contourName, contourDraw); } else { System.Windows.Shapes.Polygon contourDraw = new System.Windows.Shapes.Polygon(); contourDraw.Points = contourPoints; contourDraw.StrokeThickness = 3; contourDraw.Stroke = roi.Color; contourDraw.StrokeLineJoin = PenLineJoin.Round; UsCanvas.Children.Add(contourDraw); string contourName = "ROI_Contour_" + roiName + "_" + ni; UsCanvas.RegisterName(contourName, contourDraw); } } // 画横轴纵轴 if (roi.Descriptions != null) { var lesionSize = (DescriptionLesionSize)roi.Descriptions.Find(x => x.Type == EnumDescriptionType.LesionSize); if (lesionSize != null) { var pointLineH1 = new System.Windows.Point(lesionSize.Value.HorizontalPoint1.X, lesionSize.Value.HorizontalPoint1.Y); var pointLineH2 = new System.Windows.Point(lesionSize.Value.HorizontalPoint2.X, lesionSize.Value.HorizontalPoint2.Y); var pointLineV1 = new System.Windows.Point(lesionSize.Value.VerticalPoint1.X, lesionSize.Value.VerticalPoint1.Y); var pointLineV2 = new System.Windows.Point(lesionSize.Value.VerticalPoint2.X, lesionSize.Value.VerticalPoint2.Y); var transPointH1 = _transform.Transform(pointLineH1); var transPointH2 = _transform.Transform(pointLineH2); var transPointV1 = _transform.Transform(pointLineV1); var transPointV2 = _transform.Transform(pointLineV2); System.Windows.Shapes.Line lineH = new System.Windows.Shapes.Line { StrokeThickness = 1, Stroke = roi.Color, X1 = transPointH1.X, Y1 = transPointH1.Y, X2 = transPointH2.X, Y2 = transPointH2.Y }; System.Windows.Shapes.Line lineV = new System.Windows.Shapes.Line { StrokeThickness = 1, Stroke = roi.Color, X1 = transPointV1.X, Y1 = transPointV1.Y, X2 = transPointV2.X, Y2 = transPointV2.Y }; UsCanvas.Children.Add(lineH); string lineHName = "ROI_LineH_" + roiName; UsCanvas.RegisterName(lineHName, lineH); UsCanvas.Children.Add(lineV); string lineVName = "ROI_LineV_" + roiName; UsCanvas.RegisterName(lineVName, lineV); } } } else { //病灶画框 var rect = new System.Windows.Rect(roi.BoundBox.Left, roi.BoundBox.Top, roi.BoundBox.Width, roi.BoundBox.Height); var transRect = _transform.Transform(rect); System.Windows.Shapes.Rectangle rectDraw = new System.Windows.Shapes.Rectangle(); rectDraw.StrokeThickness = 3; rectDraw.Stroke = roi.Color; rectDraw.Width = transRect.Width; rectDraw.Height = transRect.Height; Canvas.SetLeft(rectDraw, transRect.Left); Canvas.SetTop(rectDraw, transRect.Top); UsCanvas.Children.Add(rectDraw); string rectName = "ROI_Rect_" + roiName; UsCanvas.RegisterName(rectName, rectDraw); } // 将Roi的序号写在左上角 var pointLT = new System.Windows.Point(roi.BoundBox.Left, roi.BoundBox.Top); var transPointLT = _transform.Transform(pointLT); var textLeft = transPointLT.X - 16; var textTop = transPointLT.Y; TextBlock text = new TextBlock { Text = roiName, FontSize = 16, FontWeight = System.Windows.FontWeights.Bold, Foreground = roi.Color, VerticalAlignment = VerticalAlignment.Top }; Canvas.SetLeft(text, textLeft); Canvas.SetTop(text, textTop); UsCanvas.Children.Add(text); string textName = "ROI_Text_" + roiName; UsCanvas.RegisterName(textName, text); } private void DrawOrganInCanvas(Organ organ) { if (_transform == null) { return; } // 已有则删除 RemoveOrganInCanvas(organ); if (organ.Contours == null) { return; } // 有轮廓就显示轮廓,没有轮廓就显示方框 string organName = organ.OrganName; if (organ.Contours.Length > 0) { int count = organ.Contours.Length; for (int ni = 0; ni < count; ni++) { Point2D[] contour = organ.Contours[ni]; PointCollection contourPoints = new PointCollection(); for (int nj = 0; nj < contour.Length; nj++) { var point = new System.Windows.Point(contour[nj].X, contour[nj].Y); var transPoint = _transform.Transform(point); contourPoints.Add(transPoint); } System.Windows.Shapes.Polygon contourDraw = new System.Windows.Shapes.Polygon(); contourDraw.Points = contourPoints; contourDraw.StrokeThickness = 3; contourDraw.Stroke = organ.Color; UsCanvas.Children.Add(contourDraw); string contourName = "Organ_Contour_" + organName + "_" + ni; UsCanvas.RegisterName(contourName, contourDraw); } } else { var rect = new System.Windows.Rect(organ.BoundBox.Left, organ.BoundBox.Top, organ.BoundBox.Width, organ.BoundBox.Height); var transRect = _transform.Transform(rect); System.Windows.Shapes.Rectangle rectDraw = new System.Windows.Shapes.Rectangle { StrokeThickness = 3, Stroke = organ.Color, Width = transRect.Width, Height = transRect.Height }; Canvas.SetLeft(rectDraw, transRect.Left); Canvas.SetTop(rectDraw, transRect.Top); UsCanvas.Children.Add(rectDraw); string rectName = "Organ_Rect_" + organName; UsCanvas.RegisterName(rectName, rectDraw); } // 将Organ的序号写在左上角 var pointLT = new System.Windows.Point(organ.BoundBox.Left, organ.BoundBox.Top); var transPointLT = _transform.Transform(pointLT); var textLeft = transPointLT.X + 16; var textTop = transPointLT.Y; TextBlock text = new TextBlock { Text = organName, FontSize = 16, FontWeight = FontWeights.Bold, Foreground = organ.Color, VerticalAlignment = VerticalAlignment.Top }; Canvas.SetLeft(text, textLeft); Canvas.SetTop(text, textTop); UsCanvas.Children.Add(text); string textName = "Organ_Text_" + organName; UsCanvas.RegisterName(textName, text); } private void RemoveOrganInCanvas(Organ organ) { string organName = organ.OrganName; // 框 string rectName = "Organ_Rect_" + organName; System.Windows.Shapes.Rectangle rectExist = UsCanvas.FindName(rectName) as System.Windows.Shapes.Rectangle; if (rectExist != null) { UsCanvas.Children.Remove(rectExist); UsCanvas.UnregisterName(rectName); } // 轮廓 int count = organ.Contours.Length; for (int ni = 0; ni < count; ni++) { string contourName = "Organ_Contour_" + organName + "_" + ni; System.Windows.Shapes.Polygon contourExist = UsCanvas.FindName(contourName) as System.Windows.Shapes.Polygon; if (contourExist != null) { UsCanvas.Children.Remove(contourExist); UsCanvas.UnregisterName(contourName); } } // 文字 string textName = "Organ_Text_" + organName; TextBlock textExist = UsCanvas.FindName(textName) as TextBlock; if (textExist != null) { UsCanvas.Children.Remove(textExist); UsCanvas.UnregisterName(textName); } } private void RemoveRoiInCanvas(string roiName, int roiContourNum) { // 框 string rectName = "ROI_Rect_" + roiName; System.Windows.Shapes.Rectangle rectExist = UsCanvas.FindName(rectName) as System.Windows.Shapes.Rectangle; if (rectExist != null) { UsCanvas.Children.Remove(rectExist); UsCanvas.UnregisterName(rectName); } // 轮廓 for (int ni = 0; ni < roiContourNum; ni++) { string contourName = "ROI_Contour_" + roiName + "_" + ni; System.Windows.Shapes.Polygon contourExist = UsCanvas.FindName(contourName) as System.Windows.Shapes.Polygon; if (contourExist != null) { UsCanvas.Children.Remove(contourExist); UsCanvas.UnregisterName(contourName); } System.Windows.Shapes.Polyline contourLineExist = UsCanvas.FindName(contourName) as System.Windows.Shapes.Polyline; if (contourLineExist != null) { UsCanvas.Children.Remove(contourLineExist); UsCanvas.UnregisterName(contourName); } } // 文字 string textName = "ROI_Text_" + roiName; TextBlock textExist = UsCanvas.FindName(textName) as TextBlock; if (textExist != null) { UsCanvas.Children.Remove(textExist); UsCanvas.UnregisterName(textName); } // 横轴 string lineHName = "ROI_LineH_" + roiName; System.Windows.Shapes.Line lineHExist = UsCanvas.FindName(lineHName) as System.Windows.Shapes.Line; if (lineHExist != null) { UsCanvas.Children.Remove(lineHExist); UsCanvas.UnregisterName(lineHName); } // 纵轴 string lineVName = "ROI_LineV_" + roiName; System.Windows.Shapes.Line lineVExist = UsCanvas.FindName(lineVName) as System.Windows.Shapes.Line; if (lineVExist != null) { UsCanvas.Children.Remove(lineVExist); UsCanvas.UnregisterName(lineVName); } } private void CanvasSizeChanged(object sender, SizeChangedEventArgs e) { UpdateTransforms(); UpdateRois(); if (SettingConfig.Instance.IsShowOrgansContour) { UpdateOrgans(); } } private void OnCloseClick(object sender, RoutedEventArgs e) { if (_player != null) { _player.Stop(); _player.InputFrameReceived -= OnInputFrameReceived; _player.FPSChanged -= OnFPSChanged; } StopDiagnosis(); Close(); } private void OnRestoreClick(object sender, RoutedEventArgs e) { WindowState = WindowState.Normal; } private void OnMinClick(object sender, RoutedEventArgs e) { WindowState = WindowState.Minimized; } private void OnMaxClick(object sender, RoutedEventArgs e) { WindowState = WindowState.Maximized; } private void Window_StateChanged(object sender, EventArgs e) { if (WindowState == WindowState.Maximized) { MaxButton.Visibility = Visibility.Collapsed; RestoreButton.Visibility = Visibility.Visible; } else { MaxButton.Visibility = Visibility.Visible; RestoreButton.Visibility = Visibility.Collapsed; } } private void Grid_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { if (SettingConfig.Instance.IsVideo) { if (_isPlaying) { PauseButton.Visibility = Visibility.Visible; } } } private void Grid_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) { PauseButton.Visibility = Visibility.Collapsed; } private void OnExtractContours(object sender, RoutedEventArgs e) { if (_currentDetectedObject == null) { return; } var detectedObject = _currentDetectedObject; var isLine = ExtractContoursBtn.IsChecked.Value; if (ExtractContoursBtn.IsChecked.Value) { if (_myocardialDiagnosis != null) { detectedObject = _myocardialDiagnosis.ExtractContours(_currentDetectedObject); } } RemoveRois(); var roiList = new List(); if (detectedObject.Contours != null && detectedObject.Contours.Count() > 0) { var bondingBox = new MyRect(detectedObject.BoundingBox.Left, detectedObject.BoundingBox.Top, detectedObject.BoundingBox.Width, detectedObject.BoundingBox.Height); var roi = new Roi(EnumOrgans.Null, bondingBox, detectedObject.Contours, System.Windows.Media.Brushes.Green, string.Empty, detectedObject.Confidence.ToString("0.0" + "%"), null); roiList.Add(roi); } _rois = roiList; // 更新画布上显示的Roi和Organ UpdateRois(isLine); } } }