2026-01-26 18:56:53 +09:00

341 lines
14 KiB
C#

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<CrossCoordinate> _SelectedVisionCrossLineCoordinateList;
public ObservableCollection<CrossCoordinate> SelectedVisionCrossLineCoordinateList { get => _SelectedVisionCrossLineCoordinateList; set => SetProperty(ref _SelectedVisionCrossLineCoordinateList, value); }
private ObservableCollection<CrossCoordinate> _VisionCrossLineCoordinateList;
public ObservableCollection<CrossCoordinate> 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<CrossCoordinate>();
SelectedVisionCrossLineCoordinateList = new ObservableCollection<CrossCoordinate>();
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<Point> intersections = new List<Point>();
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<Point> 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<double> lsDistances = new List<double>();
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<Point> MergeClosePoints(List<Point> points)
{
Func<Point, Point, double> 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<Point> mergePoints = new List<Point>();
while (points.Count > 0) {
Point currentPoint = points[0];
points.RemoveAt(0);
List<Point> closePoints = new List<Point> { 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); }
}
}
}