using Aurora; using Aurora.Api; using Aurora.View; using AuroraLaser; using OpenCvSharp; using OpenCvSharp.WpfExtensions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; //using System.Windows.Forms; using System.Windows.Input; using System.Windows.Media.Imaging; using WeldingCommon.Settings; namespace WeldingCommon.Views { public class UCCameraPixelResolutionViewModel : NotifyPropertyChangedBase { private readonly Logger Log = new Logger("UCCameraPixelResolutionViewModel"); public ICommand CMD_VisibleChanged { get; } public ICommand CMD_LoadImage { get; } public ICommand CMD_FindCrossLine { get; } public ICommand CMD_CalculateDistance { get; } public ICommand CMD_SortDataList { get; } private readonly IAuroraEnvironment _Aurora; private SystemParameter _SystemParameter; // TODO: view changed 될 때 reload 필요 or Setting 이 바뀔 때 reload 필요 // Support VisionCorrectionList Selection. private ObservableCollection _SelectedVisionCrossLineCoordinateList; public ObservableCollection SelectedVisionCrossLineCoordinateList { get => _SelectedVisionCrossLineCoordinateList; set => SetProperty(ref _SelectedVisionCrossLineCoordinateList, value); } private ObservableCollection _VisionCrossLineCoordinateList; public ObservableCollection VisionCrossLineCoordinateList { get => _VisionCrossLineCoordinateList; set => SetProperty(ref _VisionCrossLineCoordinateList, value); } private string _LoadImagePath; private Mat _LoadImageMat; private BitmapSource _LoadImage; public BitmapSource LoadImage { get => _LoadImage; set => SetProperty(ref _LoadImage, value); } // Find Cross Line private double _CannyThreshold1; public double CannyThreshold1 { get => _CannyThreshold1; set => SetProperty(ref _CannyThreshold1, value); } private double _CannyThreshold2; public double CannyThreshold2 { get => _CannyThreshold2; set => SetProperty(ref _CannyThreshold2, value); } private double _RHO; public double RHO { get => _RHO; set => SetProperty(ref _RHO, value); } private int _LineThreshold; public int LineThreshold { get => _LineThreshold; set => SetProperty(ref _LineThreshold, value); } private double _MinLineLength; public double MinLineLength { get => _MinLineLength; set => SetProperty(ref _MinLineLength, value); } private double _MaxLineGaps; public double MaxLineGaps { get => _MaxLineGaps; set => SetProperty(ref _MaxLineGaps, value); } private double _MergeClosePointDistance; public double MergeClosePointDistance { get => _MergeClosePointDistance; set => SetProperty(ref _MergeClosePointDistance, value); } // Calculate Distance private double _PixelDistanceMin; public double PixelDistanceMin { get => _PixelDistanceMin; set => SetProperty(ref _PixelDistanceMin, value); } private double _PixelDistanceMax; public double PixelDistanceMax { get => _PixelDistanceMax; set => SetProperty(ref _PixelDistanceMax, value); } private double _CrossDesignDistance; public double CrossDesignDistance { get => _CrossDesignDistance; set => SetProperty(ref _CrossDesignDistance, value); } private double _CrossDistanceAverage; public double CrossDistanceAverage { get => _CrossDistanceAverage; set => SetProperty(ref _CrossDistanceAverage, value); } private double _CameraMagnificationResult; public double CameraMagnificationResult { get => _CameraMagnificationResult; set => SetProperty(ref _CameraMagnificationResult, value); } public UCCameraPixelResolutionViewModel(IAuroraEnvironment aurora) { _Aurora = aurora; _SystemParameter = aurora.SettingEntry[CommonSettingIds.SystemParameter] as SystemParameter; VisionCrossLineCoordinateList = new ObservableCollection(); SelectedVisionCrossLineCoordinateList = new ObservableCollection(); InitVariable(); CMD_LoadImage = new DelegateCommand(PerformCmdLoadImage); CMD_FindCrossLine = new DelegateCommand(PerformCmdFindCrossLine); CMD_CalculateDistance = new DelegateCommand(PerformCmdCalculateDistance); CMD_SortDataList = new DelegateCommand(PerformCmdSortDataList); } private void InitVariable() { CannyThreshold1 = 50; CannyThreshold2 = 150; // RHO ??? setting 이 없음. 왜 입력 부분이 있는거야?? LineThreshold = 50; MinLineLength = 500; MaxLineGaps = 50; MergeClosePointDistance = 20; CrossDesignDistance = 1.0; CrossDistanceAverage = 0.0; CameraMagnificationResult = 0.0; PixelDistanceMin = 70; PixelDistanceMax = 90; } private void PerformCmdLoadImage(object obj) { System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog(); System.Windows.Forms.DialogResult dr = ofd.ShowDialog(); if (dr == System.Windows.Forms.DialogResult.OK) { _LoadImagePath = ofd.FileName; } if (_LoadImagePath == null) { return; } _LoadImageMat = Cv2.ImRead(_LoadImagePath); LoadImage = _LoadImageMat.ToBitmapSource(); } private void PerformCmdFindCrossLine(object obj) { if (_LoadImageMat == null) { return; } Mat blurred = new Mat(); Cv2.GaussianBlur(_LoadImageMat, blurred, new Size(5, 5), 0); // Canny Edge Detection Mat edges = new Mat(); Cv2.Canny(blurred, edges, CannyThreshold1, CannyThreshold2); double rho = 1; //int LineThreshold = 50; //double m_minLineLength = 50; //double m_maxLineGaps = 50; LineSegmentPoint[] lines = Cv2.HoughLinesP(edges, rho, Math.PI / 180, LineThreshold, minLineLength: MinLineLength, maxLineGap: MaxLineGaps); // 교차점 계산을 위한 선분 리스트 List intersections = new List(); for (int i = 0; i < lines.Length; i++) { for (int j = i + 1; j < lines.Length; j++) { Point? intersection = GetIntersection(lines[i].P1, lines[i].P2, lines[j].P1, lines[j].P2); if (intersection == null) { continue; } if (intersection.HasValue) { intersections.Add(intersection.Value); } } } List mergedpoints = MergeClosePoints(intersections); // 교차점 표시 Mat output = _LoadImageMat.Clone(); VisionCrossLineCoordinateList.Clear(); foreach (var point in mergedpoints) { if (point.X < 0 || point.Y < 0 || point.X > _LoadImageMat.Width || point.Y > _LoadImageMat.Height) continue; Cv2.Circle(output, point, 3, new Scalar(0, 0, 255), -1); CrossCoordinate crossCoordinate = new CrossCoordinate(); crossCoordinate.Index = VisionCrossLineCoordinateList.Count + 1; crossCoordinate.VisionX = point.X; crossCoordinate.VisionY = point.Y; crossCoordinate.Distance = 0; VisionCrossLineCoordinateList.Add(crossCoordinate); } LoadImage = output.ToBitmapSource(); } private void PerformCmdCalculateDistance(object obj) { // sorting 을 해두면 계산량을 줄일수 있다. // 자주쓰는 기능이 아니니까 일단 전부 계산하자. try { foreach (var item1 in VisionCrossLineCoordinateList) { double dDistanceMin = 9999; List lsDistances = new List(); foreach (var item2 in VisionCrossLineCoordinateList) { if (item1 == item2) { continue; } double dDistance = Math.Sqrt(Math.Pow(item2.VisionX - item1.VisionX, 2) + Math.Pow(item2.VisionY - item1.VisionY, 2)); if (dDistance > PixelDistanceMin && dDistance < PixelDistanceMax) { lsDistances.Add(dDistance); } if (dDistance < dDistanceMin) { dDistanceMin = dDistance; } } if (lsDistances.Count > 0) { item1.Distance = lsDistances.Average(); } else { item1.Distance = dDistanceMin; } } if (VisionCrossLineCoordinateList.Count > 0) { CrossDistanceAverage = VisionCrossLineCoordinateList.Average(x => x.Distance); } else { CrossDistanceAverage = 0; } CameraMagnificationResult = (CrossDistanceAverage * _SystemParameter.CameraPixelSize) / (CrossDesignDistance * 1000.0); } catch (Exception e) { } } private void PerformCmdSortDataList(object commandParameter) { var crossLines = VisionCrossLineCoordinateList.ToList(); switch ((string)commandParameter) { case "X": { crossLines.Sort((x, y) => x.VisionX.CompareTo(y.VisionX)); VisionCrossLineCoordinateList.Clear(); foreach (var items in crossLines) { VisionCrossLineCoordinateList.Add(items); } } break; case "Y": { crossLines.Sort((x, y) => x.VisionY.CompareTo(y.VisionY)); VisionCrossLineCoordinateList.Clear(); foreach (var items in crossLines) { VisionCrossLineCoordinateList.Add(items); } } break; default: return; } } public void DataGridItemDelete(int index) { try { if (index > VisionCrossLineCoordinateList.Count - 1) { return; } VisionCrossLineCoordinateList.RemoveAt(index); Mat output = _LoadImageMat.Clone(); for (int i = 0; i < VisionCrossLineCoordinateList.Count; i++) { VisionCrossLineCoordinateList[i].Index = i + 1; Point point = new Point(VisionCrossLineCoordinateList[i].VisionX, VisionCrossLineCoordinateList[i].VisionY); Cv2.Circle(output, point, 3, new Scalar(0, 0, 255), -1); } LoadImage = output.ToBitmapSource(); } catch (Exception e) { } } public void ReDrawIntersections(int index) { // index 는 파란색, 나머지는 빨간색으로 그린다 if (index > VisionCrossLineCoordinateList.Count - 1) { return; } Mat output = _LoadImageMat.Clone(); foreach (var item in VisionCrossLineCoordinateList) { Point point = new Point(item.VisionX, item.VisionY); if (item.Index == index + 1) { Cv2.Circle(output, point, 10, new Scalar(255, 0, 0), -1); } else { Cv2.Circle(output, point, 3, new Scalar(0, 0, 255), -1); } } LoadImage = output.ToBitmapSource(); } private Point? GetIntersection(Point p1, Point p2, Point p3, Point p4) { // 선분의 방정식을 통해 교차점 계산 float denominator = (p1.X - p2.X) * (p3.Y - p4.Y) - (p1.Y - p2.Y) * (p3.X - p4.X); if (denominator == 0) { return null; } float x = ((p1.X * p2.Y - p1.Y * p2.X) * (p3.X - p4.X) - (p1.X - p2.X) * (p3.X * p4.Y - p3.Y * p4.X)) / denominator; float y = ((p1.X * p2.Y - p1.Y * p2.X) * (p3.Y - p4.Y) - (p1.Y - p2.Y) * (p3.X * p4.Y - p3.Y * p4.X)) / denominator; return new Point(x, y); } private List MergeClosePoints(List points) { Func GetDistance = (p1, p2) => Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); if (points == null) return null; List mergePoints = new List(); while (points.Count > 0) { Point currentPoint = points[0]; points.RemoveAt(0); List closePoints = new List { currentPoint }; for (int i = points.Count - 1; i >= 0; i--) { if (GetDistance(currentPoint, points[i]) <= MergeClosePointDistance) { closePoints.Add(points[i]); points.RemoveAt(i); } } // 가까운 점들의 평균 구하기 double avrX = closePoints.Average(p => p.X); double avrY = closePoints.Average(p => p.Y); mergePoints.Add(new Point(avrX, avrY)); } return mergePoints; } public class CrossCoordinate : NotifyPropertyChangedBase { private int _Index; public int Index { get => _Index; set => SetProperty(ref _Index, value); } private int _VisionX; public int VisionX { get => _VisionX; set => SetProperty(ref _VisionX, value); } private int _VisionY; public int VisionY { get => _VisionY; set => SetProperty(ref _VisionY, value); } // Distance : 가장 가까운 cross line 과의 거리 private double _Distance; public double Distance { get => _Distance; set => SetProperty(ref _Distance, value); } } } }