341 lines
14 KiB
C#
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); }
|
|
}
|
|
}
|
|
}
|