용접 결과 보고 로직 수정 - LBAD 판정 결과를 리스트에 받아놓고 첫번째 판정 데이터만 사용하는데, 검사모드로 검사를 진행했을때 리스트에 데이터가 쌓여서 다음 일반 용접을 진행했을때는 검사모드로 생성된 판정 데이터를 업로드 하도록 되어있음 - 검사모드에서 판정정보를 강제로 변경하는데(NG여도 OK로 보고) 강제로 변경된 데이터를 업로드 시키지 않음
2993 lines
162 KiB
C#
2993 lines
162 KiB
C#
using CoreUnit.Control.UserControl.Canvas;
|
||
using CoreUnit.Scanner.UserControl.ScannerCanvas;
|
||
using Matrox.MatroxImagingLibrary;
|
||
using OpenCvSharp;
|
||
using OpenCvSharp.WpfExtensions;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Configuration;
|
||
using System.Drawing;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Windows;
|
||
using System.Windows.Media.Imaging;
|
||
using Logger = Aurora.Api.Logger;
|
||
using System.Diagnostics;
|
||
using WeldingCommon.Vision.WeldingObjectGenerator.PathEditorLibrary;
|
||
using WeldingCommon.Data;
|
||
using WeldingCommon.Utils;
|
||
using LiveChartsCore.Drawing;
|
||
using WeldingCommon.Settings;
|
||
using System.Runtime.InteropServices;
|
||
using Aurora;
|
||
using Microsoft.ML.OnnxRuntime;
|
||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||
|
||
namespace WeldingCommon.Vision.WeldingObjectGenerator {
|
||
using TPolygon = List<GeometryLibrary.TPoint>;
|
||
using TPoint = GeometryLibrary.TPoint;
|
||
using Vector2 = GeometryLibrary.Vector2;
|
||
using Canvas = CoreUnit.Control.UserControl.Canvas;
|
||
using static CoreUnit.Control.UserControl.Canvas.WeldingParameterIDEditor;
|
||
using static OpenCvSharp.LineIterator;
|
||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Menu;
|
||
using Point = OpenCvSharp.Point;
|
||
using Size = OpenCvSharp.Size;
|
||
|
||
|
||
// Pixel Data 로부터 Welding Object 를 생성하는 Path List 를 만드는 Class
|
||
public class ObjectGenerator {
|
||
#region Properties
|
||
|
||
private readonly Logger Log = new Logger("WeldingObjectGenerator");
|
||
|
||
//private TPolygon polygon;
|
||
//private TPolygon merged_polygon;
|
||
//private TPolygon rearranged_polygon;
|
||
private PathEditor patheditor;
|
||
|
||
List<PathItem> paths;
|
||
|
||
public double dMergeDist { get; set; }
|
||
public uint nMaxArcRadius { get; set; }
|
||
public uint nLineTolerance { get; set; }
|
||
public uint nArcTolerance { get; set; }
|
||
public float fLineDefualtWidth { get; set; }
|
||
public Int64 nImageWidth { get; set; }
|
||
public Int64 nImageHeight { get; set; }
|
||
public double PixelResolution { get; set; }
|
||
public double DrawingImageCenterOffsetX { get; set; }
|
||
public double DrawingImageCenterOffsetY { get; set; }
|
||
|
||
public double dInterlockArcLength { get; set; }
|
||
public double dInterlockDistRange { get; set; }
|
||
public double dInterlockLengthDiffRange { get; set; }
|
||
public double dPathLengthSum { get; set; }
|
||
public double dInterlockCenterXYDiffRange { get; set; }
|
||
public int eInterlockMaxMinDiffLow { get; set; }
|
||
public int eInterlockMaxMinDiffHigh { get; set; }
|
||
public int eCheckCenterDiff { get; set; }
|
||
public int eDistortionCenterDiff { get; set; }
|
||
public float eInterlockMissAlignHigh { get; set; }
|
||
public float eInterlockMissAlignLow { get; set; }
|
||
public UnitPoint DetectedContourCenter;
|
||
public UnitPoint DetectedContourCenterPixel;
|
||
public float fDetectedContourRadius;
|
||
#endregion
|
||
private double[] theta_list = new double[3600];
|
||
private double[] offset_list = new double[3600];
|
||
private bool is_manual = true;
|
||
private bool EnableAutoMissAlign = false;
|
||
private int end_point_index = 0;
|
||
|
||
#region Original Path 의 정보를 가진 properties
|
||
//private bool bArcHasRamping; // m_OriginArc 가 Ramping 을 하는 경우 true
|
||
//private stRampingInfo m_RampingStartInfo;
|
||
//private stRampingInfo m_RampingEndInfo;
|
||
struct stRampingInfo {
|
||
public int m_Count; // Ramping Count
|
||
public double length; // Ramping 전체 구간 길이
|
||
public double Intervallength; // 1-count 의 원호 길이. rampling 의 전체 길이 / Ramping Count 임. parameter 에 인터벌 값을 직접 입력해서 쓰는듯.
|
||
public double PowerRatioCh1; // center power 비율. 100% 는 고정이므로 시작점, 끝점만 알면 된다. range(0~100)
|
||
public double PowerRatioCh2; // ring power 비율. 100% 는 고정이므로 시작점, 끝점만 알면 된다. range(0~100)
|
||
}
|
||
#endregion
|
||
|
||
public ObjectGenerator()
|
||
{
|
||
//polygon = new TPolygon();
|
||
patheditor = new PathEditor();
|
||
|
||
// Defualt setting. sw 의 setting parameter 에서 넣도록 하자.
|
||
dMergeDist = 10.0;
|
||
nMaxArcRadius = 1000;
|
||
nLineTolerance = 10;
|
||
nArcTolerance = 10;
|
||
fLineDefualtWidth = 0.01f;
|
||
PixelResolution = 1.0f; // setting 에서 가져올것
|
||
nImageWidth = 0; nImageHeight = 0; // image 생성한 후에 넣을수 있으므로 바뀔수 있음
|
||
DrawingImageCenterOffsetX = 0; DrawingImageCenterOffsetY = 0;
|
||
dInterlockDistRange = 1.0;
|
||
dInterlockArcLength = 10.0;
|
||
eInterlockMaxMinDiffLow = 5;
|
||
eInterlockMaxMinDiffHigh = 10;
|
||
eCheckCenterDiff = 10;
|
||
eDistortionCenterDiff = 25;
|
||
eInterlockMissAlignHigh = 4;
|
||
eInterlockMissAlignLow = 2;
|
||
}
|
||
|
||
public ObjectGenerator(double dPixelResolution)
|
||
{
|
||
//polygon = new TPolygon();
|
||
patheditor = new PathEditor();
|
||
|
||
// Defualt setting. sw 의 setting parameter 에서 넣도록 하자.
|
||
dMergeDist = 10.0;
|
||
nMaxArcRadius = 1000;
|
||
nLineTolerance = 10;
|
||
nArcTolerance = 10;
|
||
fLineDefualtWidth = 0.01f;
|
||
PixelResolution = dPixelResolution;
|
||
DrawingImageCenterOffsetX = 0; DrawingImageCenterOffsetY = 0;
|
||
dInterlockDistRange = 1.0;
|
||
dInterlockArcLength = 10.0;
|
||
eInterlockMaxMinDiffLow = 5;
|
||
eInterlockMaxMinDiffHigh = 10;
|
||
eCheckCenterDiff = 10;
|
||
eDistortionCenterDiff = 25;
|
||
eInterlockMissAlignHigh = 4;
|
||
eInterlockMissAlignLow = 2;
|
||
}
|
||
|
||
public Point2f find_circle_center(Mat imageColor, OpenCvSharp.Rect ROI, int threshold, double size_margin, double recipe_radius)
|
||
{
|
||
//ROI 영역 내에서 원 형태의 용접 부위를 찾아 중심점 return
|
||
//threshold : 인식할 영역의 밝기 범위 (threshold 보다 밝게)
|
||
//size margin : 찾을 원의 반지름 범위 (size margin 이 0.1이고 반지름이 10이라면 반지름이 9부터 11까지인 원을 찾음)
|
||
//recipe_radius : recipe 상 원의 반지름 DateTime currentTime = DateTime.Now;
|
||
Mat gray_img = new Mat(imageColor, ROI);
|
||
//Cv2.CvtColor(gray_img, gray_img, ColorConversionCodes.BGR2GRAY);
|
||
//Cv2.ImWrite($"D:/gray_img_{currentTime.ToString("HHmmssff")}.png", gray_img);
|
||
Mat thres_img = new Mat();
|
||
Cv2.Threshold(gray_img, thres_img, threshold, 255, ThresholdTypes.BinaryInv);
|
||
//Cv2.ImWrite($"D:/thres_img_{currentTime.ToString("HHmmssff")}.png", thres_img);
|
||
//recipe_radius = 104;
|
||
//var _circleSegments = Cv2.HoughCircles(thres_img, HoughModes.Gradient, 1.2, ROI.Size.Height, 10, 10, (int)(recipe_radius * (1 - size_margin*2)), (int)(recipe_radius * (1 + size_margin)));
|
||
var _circleSegments = Cv2.HoughCircles(thres_img, HoughModes.Gradient, 1.5, ROI.Size.Height, 10, 10, (int)(recipe_radius * (1 - size_margin)), (int)(recipe_radius * (1 + size_margin)));
|
||
//Mat out_img = new Mat();
|
||
//Cv2.Merge(new Mat[] { gray_img, gray_img, gray_img }, out_img);
|
||
if (_circleSegments.Length == 0) return new Point2f(0, 0);
|
||
//Cv2.Circle(out_img, (Point)_circleSegments[0].Center, (int)_circleSegments[0].Radius, Scalar.AliceBlue);
|
||
//Cv2.ImWrite($"D:/RCC_img_{currentTime.ToString("HHmmssff")}.png", out_img);
|
||
Log.I($"Center Point : ({_circleSegments[0].Center.X},{_circleSegments[0].Center.Y}), Radius : {_circleSegments[0].Radius}");
|
||
return _circleSegments[0].Center;
|
||
}
|
||
public RotatedRect find_line_center(Mat imageColor, OpenCvSharp.Rect ROI, int threshold1, int threshold2)
|
||
{
|
||
//ROI 영역 내에서 직선 형태의 용접 부위를 찾아 중심점 return
|
||
//threshold1, threshold2 : 인식할 영역의 밝기 범위 (threshold1보다 밝고 threshol2 보다 어둡도록)
|
||
DateTime currentTime = DateTime.Now;
|
||
Mat gray_img = new Mat(imageColor, ROI);
|
||
Cv2.CvtColor(gray_img, gray_img, ColorConversionCodes.BGR2GRAY);
|
||
//Cv2.ImWrite($"D:/gray_img_{currentTime.ToString("HHmmssff")}.png", gray_img);
|
||
Mat thres_img = new Mat();
|
||
Cv2.InRange(gray_img, threshold1, threshold2, thres_img);
|
||
|
||
//Cv2.ImWrite($"D:/thres_img_{currentTime.ToString("HHmmssff")}.png", thres_img);
|
||
Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(thres_img, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
if (contours.Length > 0) {
|
||
contours = contours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
RotatedRect rotatedRect = Cv2.MinAreaRect(contours[0]);
|
||
return rotatedRect;
|
||
}
|
||
else {
|
||
return new RotatedRect();
|
||
}
|
||
|
||
}
|
||
public (Point, float, Point2f[]) find_rectangle_center(Mat imageColor, OpenCvSharp.Rect[] ROIs, int threshold)
|
||
{
|
||
Point2f[] points = new Point2f[4];
|
||
for (int i = 0; i < 4; i++) {
|
||
Mat gray_img = new Mat(imageColor, ROIs[i]);
|
||
Cv2.CvtColor(gray_img, gray_img, ColorConversionCodes.BGR2GRAY);
|
||
Mat thres_img = new Mat();
|
||
Cv2.Threshold(gray_img, thres_img, threshold, 255, ThresholdTypes.BinaryInv);
|
||
Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(thres_img, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
if (contours.Length > 0) {
|
||
contours = contours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
RotatedRect rotatedRect = Cv2.MinAreaRect(contours[0]);
|
||
points[i] = new Point(rotatedRect.Center.X + ROIs[i].X, rotatedRect.Center.Y + ROIs[i].Y);
|
||
}
|
||
else {
|
||
return (new Point(), 0, new Point2f[0]);
|
||
}
|
||
}
|
||
Point center = new Point(points.Average(p => p.X), points.Average(p => p.Y));
|
||
RotatedRect rotatedRect_final = Cv2.MinAreaRect(points);
|
||
return (center, rotatedRect_final.Angle, rotatedRect_final.Points());
|
||
}
|
||
|
||
public (int[], Point) make_sum_array(Mat Image, int ROIW, int ROIH, double theta, Point center)
|
||
{
|
||
var M = Cv2.GetRotationMatrix2D(new Point(Image.Width / 2, Image.Height / 2), theta, 1);
|
||
|
||
Mat rotated_Image = new Mat();
|
||
Cv2.WarpAffine(Image, rotated_Image, M, new Size(Image.Width, Image.Height));
|
||
//Cv2.ImWrite($"D:/r_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", rotated_Image);
|
||
|
||
Mat rotated_Image_ROI = new Mat(rotated_Image, new OpenCvSharp.Rect(Image.Width / 2 - ROIW, Image.Height / 2 - ROIH / 2, ROIW, ROIH));
|
||
//Cv2.ImWrite($"D:/r_i_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", rotated_Image_ROI);
|
||
int[] sum_array = new int[ROIW + 1];
|
||
for (int i = 0; i < ROIW; i++) {
|
||
var sum_val = 0;
|
||
for (int j = 0; j < ROIH; j++) sum_val += (int)rotated_Image_ROI.At<byte>(j, i);
|
||
sum_array[i + 1] = sum_val + sum_array[i];
|
||
}
|
||
return (sum_array, new Point(center.X + ROIW * Math.Cos(Math.PI + theta * Math.PI / 180), center.Y + ROIW * Math.Sin(Math.PI + theta * Math.PI / 180)));
|
||
}
|
||
public (int, Point, Point) get_bead_size_w_degree(Mat Image, OpenCvSharp.Rect ROI, double theta)
|
||
{
|
||
int search_gap = 80;
|
||
|
||
int[] sum_array;
|
||
Point arc_center = new Point(ROI.X + ROI.Width, ROI.Y + ROI.Height / 2);
|
||
Point arc_outer = new Point(0, 0);
|
||
Mat ROI_Image = new Mat(Image, new OpenCvSharp.Rect(ROI.X - ROI.Width, ROI.Y + ROI.Height / 2 - ROI.Width * 2, ROI.Width * 4, ROI.Width * 4));
|
||
(sum_array, arc_outer) = make_sum_array(ROI_Image, ROI.Width, ROI.Height, theta, arc_center);
|
||
//start point 찾기
|
||
double max_diff = -Double.MaxValue;
|
||
var end_bead = ROI.Width - 10;
|
||
for (int i = ROI.Width - 10; i > 50; i--) {
|
||
double my_diff = (double)(sum_array[ROI.Width - 1] - sum_array[i + 1]) / (double)(ROI.Width - i - 2) - (double)(sum_array[i]) / (double)(i);
|
||
|
||
if (my_diff > max_diff) {
|
||
max_diff = my_diff;
|
||
end_bead = i;
|
||
}
|
||
}
|
||
//end point 찾기
|
||
max_diff = -Double.MaxValue;
|
||
var start_bead = 5;
|
||
for (int i = end_bead - search_gap; i > 5; i--) {
|
||
double my_diff = (double)(sum_array[end_bead] - sum_array[i + 1]) / (double)(end_bead - i - 1) - (double)sum_array[i] / (double)i;
|
||
if (-my_diff > max_diff) {
|
||
max_diff = -my_diff;
|
||
start_bead = i;
|
||
}
|
||
}
|
||
|
||
Point start_bead_point = new Point((int)(((double)arc_outer.X * (double)(ROI.Width - start_bead) + (double)arc_center.X * (double)start_bead) / (double)ROI.Width), (int)(((double)arc_outer.Y * (double)(ROI.Width - start_bead) + (double)arc_center.Y * (double)start_bead) / (double)ROI.Width));
|
||
Point end_bead_point = new Point((int)(((double)arc_outer.X * (double)(ROI.Width - end_bead) + (double)arc_center.X * (double)end_bead) / (double)ROI.Width), (int)(((double)arc_outer.Y * (double)(ROI.Width - end_bead) + (double)arc_center.Y * (double)end_bead) / (double)ROI.Width));
|
||
|
||
return (end_bead - start_bead, start_bead_point, end_bead_point);
|
||
}
|
||
|
||
public (int[], Point[], Point[], Point[][]) check_bead_size(Mat prevImage, Mat nextImage, OpenCvSharp.Rect ROI, int type, int search_size)
|
||
{
|
||
Mat gray_prev_img = new Mat(prevImage, ROI);
|
||
if (gray_prev_img.Type() == MatType.CV_8UC3)
|
||
Cv2.CvtColor(gray_prev_img, gray_prev_img, ColorConversionCodes.BGR2GRAY);
|
||
Mat gray_next_img = new Mat(nextImage, ROI);
|
||
if (gray_next_img.Type() == MatType.CV_8UC3)
|
||
Cv2.CvtColor(gray_next_img, gray_next_img, ColorConversionCodes.BGR2GRAY);
|
||
Mat out_img = new Mat();
|
||
Mat out_img_diff = new Mat();
|
||
nextImage.CopyTo(out_img);
|
||
DateTime currentTime = DateTime.Now;
|
||
|
||
int search_gap = 80;
|
||
// 차이 계산
|
||
Mat diff = new Mat();
|
||
diff = gray_next_img.Clone();
|
||
//type 0 : Arc, type 1: Spiral
|
||
if (type == 0) {
|
||
string modelPath = "D:\\ILSOSConfig\\_config_model\\Unet_ring.onnx";
|
||
//Cv2.ImWrite($"D:/ring_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", diff);
|
||
|
||
// ONNX 모델 로드
|
||
var session = new InferenceSession(modelPath);
|
||
Mat resized_image = new Mat();
|
||
// 테스트 이미지 로드 및 전처리
|
||
Cv2.Resize(gray_next_img, resized_image, new Size(256, 256));
|
||
var inputTensor = new DenseTensor<float>(new[] { 1, 1, 256, 256 });
|
||
|
||
for (int y = 0; y < 256; y++) {
|
||
for (int x = 0; x < 256; x++) {
|
||
inputTensor[0, 0, y, x] = (resized_image.At<byte>(y, x) / 255.0f - 0.5f) / 0.5f;
|
||
//inputTensor[0, 0, y, x] = (resized_image.At<byte>(y, x) / 255.0f);
|
||
}
|
||
}
|
||
|
||
// 입력 데이터 생성
|
||
var inputs = new List<NamedOnnxValue>
|
||
{
|
||
NamedOnnxValue.CreateFromTensor("input.1", inputTensor)
|
||
};
|
||
|
||
// 추론 실행
|
||
IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = session.Run(inputs);
|
||
|
||
// 결과 처리
|
||
var output = results.First().AsEnumerable<float>().ToArray();
|
||
var outputImage = new Mat(256, 256, MatType.CV_8UC1);
|
||
|
||
for (int y = 0; y < 256; y++) {
|
||
for (int x = 0; x < 256; x++) {
|
||
outputImage.Set(y, x, (byte)(output[y * 256 + x] > 0.2 ? 255 : 0));
|
||
}
|
||
}
|
||
Cv2.Resize(outputImage, outputImage, new Size(gray_next_img.Width, gray_next_img.Height));
|
||
|
||
// 추가 2025/09/04 >> 260206 변경
|
||
//Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(9, 9));
|
||
//Cv2.MorphologyEx(outputImage, outputImage, MorphTypes.Close, kernel, iterations: 10);
|
||
|
||
Cv2.FindContours(outputImage, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
|
||
// Assuming the largest contour is the ring
|
||
Point[] largestContour = null;
|
||
double maxArea = 0;
|
||
foreach (var contour in contours) {
|
||
double area = Cv2.ContourArea(contour);
|
||
if (area > maxArea) {
|
||
maxArea = area;
|
||
largestContour = contour;
|
||
}
|
||
}
|
||
|
||
//260206 변경
|
||
var sorted = contours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
for (int i = 1; i < sorted.Length; i++)
|
||
Cv2.DrawContours(outputImage, new[] { sorted[i] }, -1, Scalar.Black, -1);
|
||
|
||
// 추가 2025/09/04
|
||
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(9, 9));
|
||
Cv2.MorphologyEx(outputImage, outputImage, MorphTypes.Close, kernel, iterations: 10);
|
||
|
||
int[] bead_size_info = new int[4];
|
||
Point[] start_bead_point_info = new Point[4];
|
||
Point[] end_bead_point_info = new Point[4];
|
||
double[] check_theata_list = new double[4] { -80, 100, 10, 190 };
|
||
if (largestContour != null) {
|
||
// Find the center of the ring
|
||
Moments moments = Cv2.Moments(largestContour);
|
||
int centerX = (int)(moments.M10 / moments.M00);
|
||
int centerY = (int)(moments.M01 / moments.M00);
|
||
|
||
// Define the angles in radians
|
||
double[] angles = { 4 * Math.PI / 6, 7 * Math.PI / 6, 10 * Math.PI / 6, 1 * Math.PI / 6 };
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
var angle = angles[i];
|
||
// Calculate the coordinates of the points at the specified angles
|
||
int x1 = centerX + (int)(1000 * Math.Cos(angle));
|
||
int y1 = centerY + (int)(1000 * Math.Sin(angle));
|
||
int x2 = centerX - (int)(1000 * Math.Cos(angle));
|
||
int y2 = centerY - (int)(1000 * Math.Sin(angle));
|
||
|
||
// Create a line iterator to measure the thickness
|
||
LineIterator lineIterator = new LineIterator(outputImage, new Point(centerX, centerY), new Point(x1, y1));
|
||
|
||
int thickness = 0;
|
||
bool insideRing = false;
|
||
Point start_bead_point = new Point(0, 0);
|
||
Point end_bead_point = new Point(0, 0);
|
||
int cnt = 0;
|
||
|
||
// 추가 2025/09/04
|
||
foreach (var point in lineIterator) {
|
||
cnt += 1;
|
||
if (outputImage.At<byte>(point.Pos.Y, point.Pos.X) == 255) {
|
||
if (!insideRing) {
|
||
insideRing = true;
|
||
start_bead_point = new Point(ROI.X + point.Pos.X, point.Pos.Y + ROI.Y);
|
||
}
|
||
end_bead_point = new Point(ROI.X + point.Pos.X, point.Pos.Y + ROI.Y);
|
||
|
||
}
|
||
}
|
||
bead_size_info[i] = (int)Math.Sqrt((start_bead_point.X - end_bead_point.X) * (start_bead_point.X - end_bead_point.X) + (start_bead_point.Y - end_bead_point.Y) * (start_bead_point.Y - end_bead_point.Y));
|
||
start_bead_point_info[i] = start_bead_point;
|
||
end_bead_point_info[i] = end_bead_point;
|
||
|
||
//foreach (var point in lineIterator)
|
||
//{
|
||
// cnt += 1;
|
||
// if (outputImage.At<byte>(point.Pos.Y, point.Pos.X) == 255)
|
||
// {
|
||
// if (!insideRing)
|
||
// {
|
||
// insideRing = true;
|
||
// start_bead_point = new Point(ROI.X + point.Pos.X, point.Pos.Y + ROI.Y);
|
||
// }
|
||
// thickness++;
|
||
// }
|
||
// else
|
||
// {
|
||
// if (insideRing)
|
||
// {
|
||
// end_bead_point = new Point(ROI.X + point.Pos.X, point.Pos.Y + ROI.Y);
|
||
|
||
// break;
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
//bead_size_info[i] = thickness;
|
||
//start_bead_point_info[i] = start_bead_point;
|
||
//end_bead_point_info[i] = end_bead_point;
|
||
}
|
||
}
|
||
return (bead_size_info, start_bead_point_info, end_bead_point_info, new Point[0][]);
|
||
//int[] bead_size_info = new int[4];
|
||
//Point[] start_bead_point_info = new Point[4];
|
||
//Point[] end_bead_point_info = new Point[4];
|
||
//double[] check_theata_list = new double[4] { -80, 100, 10, 190 };
|
||
//for (int i = 0; i < check_theata_list.Length; i++)
|
||
//{
|
||
// Point start_bead_point = new Point(0, 0);
|
||
// Point end_bead_point = new Point(0, 0);
|
||
// int bead_size = 0;
|
||
// (bead_size, start_bead_point, end_bead_point) = get_bead_size_w_degree(prevImage, ROI, check_theata_list[i]);
|
||
// bead_size_info[i] = bead_size;
|
||
// start_bead_point_info[i] = start_bead_point;
|
||
// end_bead_point_info[i] = end_bead_point;
|
||
//}
|
||
//return (bead_size_info, start_bead_point_info, end_bead_point_info, new Point[0][]);
|
||
}
|
||
else {
|
||
string modelPath = "D:\\ILSOSConfig\\_config_model\\Unet_spiral.onnx";
|
||
|
||
// ONNX 모델 로드
|
||
var session = new InferenceSession(modelPath);
|
||
Mat resized_image = new Mat();
|
||
// 테스트 이미지 로드 및 전처리
|
||
Cv2.Resize(gray_next_img, resized_image, new Size(256, 256));
|
||
var inputTensor = new DenseTensor<float>(new[] { 1, 1, 256, 256 });
|
||
|
||
for (int y = 0; y < 256; y++) {
|
||
for (int x = 0; x < 256; x++) {
|
||
inputTensor[0, 0, y, x] = (resized_image.At<byte>(y, x) / 255.0f - 0.5f) / 0.5f;
|
||
}
|
||
}
|
||
|
||
// 입력 데이터 생성
|
||
var inputs = new List<NamedOnnxValue>
|
||
{
|
||
NamedOnnxValue.CreateFromTensor("input.1", inputTensor)
|
||
};
|
||
|
||
// 추론 실행
|
||
IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results = session.Run(inputs);
|
||
|
||
// 결과 처리
|
||
var output = results.First().AsEnumerable<float>().ToArray();
|
||
var outputImage = new Mat(256, 256, MatType.CV_8UC1);
|
||
|
||
for (int y = 0; y < 256; y++) {
|
||
for (int x = 0; x < 256; x++) {
|
||
outputImage.Set(y, x, (byte)(output[y * 256 + x] > 0.6 ? 255 : 0));
|
||
}
|
||
}
|
||
Cv2.Resize(outputImage, outputImage, new Size(gray_next_img.Width, gray_next_img.Height));
|
||
|
||
// 컨투어 추출 및 그리기
|
||
Cv2.FindContours(outputImage, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
Array.Sort(contours, (a, b) => Cv2.ContourArea(b).CompareTo(Cv2.ContourArea(a)));
|
||
|
||
int[] bead_size_info = new int[2];
|
||
Point[] start_bead_point_info = new Point[2];
|
||
Point[] end_bead_point_info = new Point[2];
|
||
// 가장 큰 윤곽선 선택
|
||
if (contours.Length > 0) {
|
||
contours = contours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
if (contours[0].Length < 5) return (new int[2], new Point[1], new Point[0], new Point[0][]);
|
||
Mat maxCntImg = new Mat(gray_next_img.Width, gray_next_img.Height, MatType.CV_8UC1, Scalar.Black);
|
||
Cv2.DrawContours(maxCntImg, contours, 0, Scalar.White, -1);
|
||
|
||
// Apply morphological operations
|
||
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(11, 11));
|
||
//Cv2.MorphologyEx(maxCntImg, maxCntImg, MorphTypes.Erode, kernel, iterations: 3);
|
||
Cv2.MorphologyEx(maxCntImg, maxCntImg, MorphTypes.Open, kernel, iterations: 5);
|
||
|
||
// Find contours again on the eroded image
|
||
Point[][] erodeContours;
|
||
Cv2.FindContours(maxCntImg, out erodeContours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
erodeContours = erodeContours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
if (erodeContours.Length == 0 || erodeContours[0].Length < 5) return (new int[2], new Point[1], new Point[0], new Point[0][]);
|
||
|
||
var boundingRect = Cv2.BoundingRect(erodeContours[0]);
|
||
|
||
//RotatedRect ellipse = Cv2.FitEllipse(contours[0]);
|
||
//Point2f[] ellipse_ps = ellipse.Points();
|
||
//float majorAxisLength = Math.Max(ellipse.Size.Width, ellipse.Size.Height);
|
||
//float minorAxisLength = Math.Min(ellipse.Size.Width, ellipse.Size.Height);
|
||
|
||
|
||
////var ellipse_br = ellipse.BoundingRect();
|
||
//var bead_width = ellipse.Size.Width * Math.Cos(ellipse.Angle * Math.PI / 180.0);
|
||
//var bead_height = ellipse.Size.Height * Math.Cos(ellipse.Angle * Math.PI / 180.0);
|
||
//return ((int)ellipse.Size.Width, (int)ellipse.Size.Height, new Point(ellipse.Center.X + ROI.X, ellipse.Center.Y + ROI.Y), new Point(0, 0), contours);
|
||
bead_size_info[0] = (int)boundingRect.Width;
|
||
bead_size_info[1] = (int)boundingRect.Height;
|
||
start_bead_point_info[0] = new Point((boundingRect.Left + boundingRect.Right) / 2 + ROI.X, (boundingRect.Top + boundingRect.Bottom) / 2 + ROI.Y);
|
||
|
||
//Cv2.CvtColor(gray_next_img, gray_next_img, ColorConversionCodes.GRAY2BGR);
|
||
//Cv2.DrawContours(gray_next_img, contours, 0, Scalar.Red, 5);
|
||
//Cv2.DrawContours(gray_next_img, erodeContours, 0, Scalar.Green, 5);
|
||
|
||
//Cv2.Rectangle(gray_next_img, boundingRect, Scalar.Blue, 5);
|
||
|
||
//Cv2.ImWrite($"D:/spiral_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", gray_next_img);
|
||
|
||
return (bead_size_info, start_bead_point_info, end_bead_point_info, contours);
|
||
|
||
}
|
||
return (new int[2], new Point[1], new Point[0], new Point[0][]);
|
||
// //spline type
|
||
// //Cv2.ImWrite($"D:/plate_{DateTime.Now.ToString("MMdd-HHmmss")}.png", plate_img);
|
||
// Mat plate_img = new Mat(nextImage, new OpenCvSharp.Rect(ROI.X, search_size, ROI.Width, ROI.Height));
|
||
// Cv2.CvtColor(plate_img, plate_img, ColorConversionCodes.BGR2GRAY);
|
||
// Cv2.Absdiff(plate_img, gray_next_img, diff);
|
||
// //Cv2.ImWrite($"D:/spiral_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", diff);
|
||
// Mat thres_img = new Mat();
|
||
// if (ROI.Y < nextImage.Width / 2)
|
||
// Cv2.Threshold(diff, thres_img, 40, 255, ThresholdTypes.Binary);
|
||
// else
|
||
// Cv2.Threshold(diff, thres_img, 30, 255, ThresholdTypes.Binary);
|
||
// // Cv2.ImWrite($"D:/spiral_{DateTime.Now.ToString("MMdd-HHmmssff")}.png", thres_img);
|
||
|
||
|
||
|
||
// // 모폴로지 연산을 위한 커널 생성
|
||
// int morpKernelSize = 5; // 예시 값
|
||
// int morpIteration = 3; // 예시 값
|
||
// Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(morpKernelSize, morpKernelSize));
|
||
|
||
// // 팽창 및 침식 연산
|
||
// Cv2.MorphologyEx(thres_img, thres_img, MorphTypes.Dilate, kernel, iterations: morpIteration);
|
||
|
||
// Cv2.MorphologyEx(thres_img, thres_img, MorphTypes.Close, kernel, iterations: morpIteration);
|
||
|
||
// //Cv2.MorphologyEx(thres_img, thres_img, MorphTypes.Erode, kernel, iterations: morpIteration);
|
||
// // 윤곽선 검출
|
||
// Point[][] contours;
|
||
// HierarchyIndex[] hierarchy;
|
||
// Cv2.FindContours(thres_img, out contours, out hierarchy, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);
|
||
// int[] bead_size_info = new int[2];
|
||
// Point[] start_bead_point_info = new Point[2];
|
||
// Point[] end_bead_point_info = new Point[2];
|
||
|
||
// // 가장 큰 윤곽선 선택
|
||
// //if (contours.Length > 0 || contours[0].Length > 5)
|
||
// if (contours.Length > 0)
|
||
// {
|
||
// contours = contours.OrderByDescending(c => Cv2.ContourArea(c)).ToArray();
|
||
// if (contours[0].Length > 5)
|
||
// {
|
||
// // 가장 큰 윤곽선에 타원 피팅
|
||
// RotatedRect ellipse = Cv2.FitEllipse(contours[0]);
|
||
// var bead_width = ellipse.Size.Width;
|
||
// var bead_size = ellipse.Size.Width;
|
||
// bead_size_info[0] = (int)ellipse.Size.Width;
|
||
// bead_size_info[1] = (int)ellipse.Size.Height;
|
||
// start_bead_point_info[0] = new Point(ellipse.Center.X + ROI.X, ellipse.Center.Y + ROI.Y);
|
||
//// return ((int)ellipse.Size.Width, (int)ellipse.Size.Height, new Point(ellipse.Center.X + ROI.X, ellipse.Center.Y + ROI.Y), new Point(0, 0), contours);
|
||
// }
|
||
|
||
// }
|
||
// return (bead_size_info, start_bead_point_info, end_bead_point_info, contours);
|
||
|
||
// }
|
||
// return (new int[0], new Point[0], new Point[0], new Point[0][]);
|
||
}
|
||
}
|
||
public void SetOffsetType(string offsetType)
|
||
{
|
||
if (offsetType == "Manual")
|
||
is_manual = true;
|
||
else
|
||
is_manual = false;
|
||
}
|
||
|
||
public void SetInterlockProperties(double DistRange, double dArcLength, double dPathLengthSumRange, double dCenterXYDiffRange, int eMaxMinDiffHigh, int eMaxMinDiffLow, int checkCenterDiff, int distortionCenterDiff, float MissAlignHigh, float MissAlignLow)
|
||
{
|
||
if (DistRange < 0 || dArcLength < 0) {
|
||
dInterlockDistRange = 1.0;
|
||
return;
|
||
}
|
||
|
||
dInterlockDistRange = DistRange;
|
||
dInterlockArcLength = dArcLength;
|
||
dInterlockLengthDiffRange = dPathLengthSumRange;
|
||
dInterlockCenterXYDiffRange = dCenterXYDiffRange;
|
||
eInterlockMaxMinDiffHigh = eMaxMinDiffHigh;
|
||
eInterlockMaxMinDiffLow = eMaxMinDiffLow;
|
||
eCheckCenterDiff = checkCenterDiff;
|
||
eDistortionCenterDiff = distortionCenterDiff;
|
||
eInterlockMissAlignHigh = MissAlignHigh;
|
||
eInterlockMissAlignLow = MissAlignLow;
|
||
}
|
||
|
||
public void SetImageSize(Int64 nWidth, Int64 nHeight)
|
||
{
|
||
nImageWidth = nWidth; nImageHeight = nHeight; // image 생성한 후에 넣을수 있으므로 바뀔수 있음
|
||
}
|
||
|
||
public void SetdPixelResolution(double dPixelResolution, double dDrawingImageCenterOffsetX = 0, double dDrawingImageCenterOffsetY = 0)
|
||
{
|
||
PixelResolution = dPixelResolution;
|
||
DrawingImageCenterOffsetX = dDrawingImageCenterOffsetX;
|
||
DrawingImageCenterOffsetY = dDrawingImageCenterOffsetY;
|
||
}
|
||
public void SetDrawingImageCenterOffset(double dDrawingImageCenterOffsetX, double dDrawingImageCenterOffsetY)
|
||
{
|
||
DrawingImageCenterOffsetX = dDrawingImageCenterOffsetX;
|
||
DrawingImageCenterOffsetY = dDrawingImageCenterOffsetY;
|
||
}
|
||
//public bool SetPolygonFromFile(string strfilename)
|
||
//{
|
||
// // csv file 로 부터 읽어온다.
|
||
// // 줄단위로 읽어서 앞의 숫자 2개를 x,y 좌표로 하는 List 양식을 polygon 에 넣어준다.
|
||
// try {
|
||
// Stream rs = new FileStream(strfilename, FileMode.Open);
|
||
// StreamReader sr = new StreamReader(rs);
|
||
|
||
// polygon.Clear();
|
||
|
||
// while (!sr.EndOfStream) {
|
||
// String s = sr.ReadLine();
|
||
// string[] words = s.Split(',');
|
||
// polygon.Add(new TPoint(Int32.Parse(words[0]), Int32.Parse(words[1])));
|
||
// }
|
||
// return true;
|
||
|
||
// } catch (Exception e) {
|
||
// MessageBox.Show(e.Message);
|
||
// return false;
|
||
// }
|
||
//}
|
||
//public void SetPolygonFromList(List<GeometryLibrary.TPoint> list)
|
||
//{
|
||
// polygon = list;
|
||
//}
|
||
//public void SetPolygonFromList(List<OpenCvSharp.Point> list)
|
||
//{
|
||
// // 2point double list 변수를 TPoint 에 넣는다. 정수형으로 바꿔주므로 정보손실은 있다.
|
||
// polygon.Clear();
|
||
// foreach (var item in list) {
|
||
// polygon.Add(new TPoint(Convert.ToInt32(item.X), Convert.ToInt32(item.Y)));
|
||
// }
|
||
//}
|
||
public void SetPolygonFromList(OpenCvSharp.Point[][] list, out TPolygon polygon)
|
||
{
|
||
// 2point double list 변수를 TPoint 에 넣는다. 정수형으로 바꿔주므로 정보손실은 있다.
|
||
//polygon.Clear();
|
||
polygon = new TPolygon();
|
||
for (int i = 0; i < list.Length; i++)
|
||
for (int j = 0; j < list[i].Length; j++)
|
||
polygon.Add(new TPoint(Convert.ToInt32(list[i][j].X), Convert.ToInt32(list[i][j].Y)));
|
||
}
|
||
//public void ClearPolygonData()
|
||
//{
|
||
// polygon.Clear();
|
||
//}
|
||
public void CalculateAutoPathAngle(Mat image, Arc originArc, TPolygon polygon, double dMergeDist, int ObjectN, UnitPoint detectedContourCenter, out List<PathItem> lsPaths)
|
||
{
|
||
MergePolygonSample(polygon, image, dMergeDist, detectedContourCenter, originArc, out TPolygon merged_polygon);
|
||
patheditor.GeneratePathWDegree(merged_polygon, ObjectN, out lsPaths);
|
||
//paths = patheditor.GetAutoPathItem();
|
||
|
||
}
|
||
|
||
public void CalculateAutoPath(Mat image, Arc originArc, TPolygon polygon, double dMergeDist, uint nMaxArcRadius, uint nLineTolerance, uint nArcTolerance, UnitPoint detectedContourCenter, out List<PathItem> lsPaths)
|
||
{
|
||
MergePolygonSample(polygon, image, dMergeDist, detectedContourCenter, originArc, out TPolygon merged_polygon);
|
||
ReArrangePoints(merged_polygon, out TPolygon rearranged_polygon);
|
||
//detectedContourCenter를 Pixel로 변경
|
||
ConvertUnitPointToPixel(detectedContourCenter, image.Width, image.Height, out uint detectedContourCenterPixelX, out uint detectedContourCenterPixelY);
|
||
patheditor.GenerateAutoPath(rearranged_polygon, nMaxArcRadius, nLineTolerance, nArcTolerance, detectedContourCenterPixelX, detectedContourCenterPixelY, out lsPaths);
|
||
//paths = patheditor.GetAutoPathItem();
|
||
}
|
||
private void SplitReArrangedPolygonbyRampingLength(TPolygon rearranged_polygon, double dRampingStartLength, double dRampingEndLength, out List<TPolygon> lsRearrangedPolygon)
|
||
{
|
||
// 현재는 안쓴다. 나중을 위해서 남겨둔다.
|
||
// rearranged_polygon 를 길이로 해서 3개로 쪼갠 list 로 바꿔준다.
|
||
// 쪼갤때 end point 를 다음 list 의 start 와 겹쳐지도록 할 것.
|
||
// dRampingStartLength, dRampingEndLength 는 mm 단위로 받아서 이 함수내에서 pixel 로 바꿔준다
|
||
|
||
lsRearrangedPolygon = new List<TPolygon>();
|
||
|
||
if (PixelResolution == 0)
|
||
return;
|
||
double dRampingStartPixelLength = dRampingStartLength / PixelResolution;
|
||
double dRampingEndPixelLength = dRampingEndLength / PixelResolution;
|
||
|
||
int RampingStartFinalIndex = 0;
|
||
int RampingEndFinalIndex = 0;
|
||
|
||
double dCalStartDist = 0.0;
|
||
for (int i = 1; i < rearranged_polygon.Count; i++) {
|
||
double distx = rearranged_polygon[i].x - rearranged_polygon[i - 1].x;
|
||
double disty = rearranged_polygon[i].y - rearranged_polygon[i - 1].y;
|
||
dCalStartDist += Math.Sqrt(distx * distx + disty * disty);
|
||
if (dCalStartDist > dRampingStartPixelLength) {
|
||
RampingStartFinalIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
double dCalEndDist = 0.0;
|
||
|
||
for (int i = rearranged_polygon.Count - 1; i > 1; i--) {
|
||
double distx = rearranged_polygon[i].x - rearranged_polygon[i - 1].x;
|
||
double disty = rearranged_polygon[i].y - rearranged_polygon[i - 1].y;
|
||
dCalEndDist += Math.Sqrt(distx * distx + disty * disty);
|
||
if (dCalEndDist > dRampingEndPixelLength) {
|
||
RampingEndFinalIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
lsRearrangedPolygon.Add(rearranged_polygon.GetRange(0, RampingStartFinalIndex));
|
||
lsRearrangedPolygon.Add(rearranged_polygon.GetRange(RampingStartFinalIndex - 1, RampingEndFinalIndex - RampingStartFinalIndex + 2)); // point 겹쳐지게 시작위치를 1칸 당기고 끝나는 위치를 1칸 늘린다
|
||
lsRearrangedPolygon.Add(rearranged_polygon.GetRange(RampingEndFinalIndex, rearranged_polygon.Count - RampingEndFinalIndex));
|
||
}
|
||
|
||
public void GetAutoPath(out List<PathItem> lsPaths)
|
||
{
|
||
lsPaths = paths;
|
||
}
|
||
|
||
public void ConvertPixelAxisFromDrawingObjectAxis(UnitPoint DOPoint, Int64 imageWidth, Int64 imageHeight, out double pixelX, out double pixelY)
|
||
{
|
||
// ConvertDrawingObjectAxis 의 역함수 구하자.
|
||
pixelX = (DOPoint.X - DrawingImageCenterOffsetX) / PixelResolution + imageWidth / 2;
|
||
pixelY = -((DOPoint.Y - DrawingImageCenterOffsetY) / PixelResolution - imageHeight / 2);
|
||
}
|
||
|
||
public void ConvertDrawingObjectAxis(Int64 pixelX, Int64 pixelY, Int64 imageWidth, Int64 imageHeight, out UnitPoint UPDest)
|
||
{
|
||
// Pixel 로 된 좌표계를 Drawing Object 의 좌표계로 변환. 스캐너의 레졸루션까지 반영되지는 않았다.
|
||
// Pixel resolution x, y 가 다르면 Radius 계산이 매우 어려워진다. vector 방향에 따라서 resoultion 계산을 따로 해줘야하기 때문
|
||
UnitPoint up = new UnitPoint();
|
||
double dPixelX = Convert.ToDouble(pixelX) - imageWidth / 2;
|
||
double dPixelY = -Convert.ToDouble(pixelY) + imageHeight / 2;
|
||
up.X = dPixelX * PixelResolution + DrawingImageCenterOffsetX;
|
||
up.Y = dPixelY * PixelResolution + DrawingImageCenterOffsetY;
|
||
UPDest = up;
|
||
}
|
||
public void ConvertUnitPointToPixel(UnitPoint UPDest, Int64 imageWidth, Int64 imageHeight, out UInt32 pixelX, out UInt32 pixelY)
|
||
{
|
||
// UnitPoint를 Pixel 좌표로 변환
|
||
double dPixelX = (UPDest.X - DrawingImageCenterOffsetX) / PixelResolution + imageWidth / 2;
|
||
double dPixelY = -(UPDest.Y - DrawingImageCenterOffsetY) / PixelResolution + imageHeight / 2;
|
||
|
||
pixelX = Convert.ToUInt32(dPixelX);
|
||
pixelY = Convert.ToUInt32(dPixelY);
|
||
}
|
||
//public void ConvertDrawingObjectAxis(float pixelX, float pixelY, out UnitPoint UPDest)
|
||
//{
|
||
// // Pixel 로 된 좌표계를 Drawing Object 의 좌표계로 변환. 스캐너의 레졸루션까지 반영되지는 않았다.
|
||
// // Pixel resolution x, y 가 다르면 Radius 계산이 매우 어려워진다. vector 방향에 따라서 resoultion 계산을 따로 해줘야하기 때문
|
||
// UnitPoint up = new UnitPoint();
|
||
// double dPixelX = Convert.ToDouble(pixelX) - nImageWidth / 2;
|
||
// double dPixelY = -Convert.ToDouble(pixelY) + nImageHeight / 2;
|
||
// up.X = dPixelX * PixelResolution + DrawingImageCenterOffsetX;
|
||
// up.Y = dPixelY * PixelResolution + DrawingImageCenterOffsetY;
|
||
// UPDest = up;
|
||
//}
|
||
|
||
public void ConvertDrawingObjectAxis(Int32 pixel, out float dDest)
|
||
{
|
||
// Pixel 로 된 좌표계를 Drawing Object 의 좌표계로 변환. 스캐너의 레졸루션까지 반영되지는 않았다.
|
||
// Pixel resolution x, y 가 다르면 Radius 계산이 매우 어려워진다. vector 방향에 따라서 resoultion 계산을 따로 해줘야하기 때문
|
||
// data 손실 주의..
|
||
dDest = (float)(Convert.ToDouble(pixel) * PixelResolution);
|
||
}
|
||
private void ConvertPathArcToDrawingArc(PathItem item, Mat image, Arc originArc, out Arc arc)
|
||
{
|
||
arc = new Arc();
|
||
UnitPoint dCenterPoint, dStartPoint, dEndPoint;
|
||
Int64 lx, ly;
|
||
|
||
arc.Direction = originArc.Direction;
|
||
item.GetCenter(out lx, out ly);
|
||
ConvertDrawingObjectAxis(lx, ly, image.Width, image.Height, out dCenterPoint);
|
||
arc.Center = dCenterPoint;
|
||
|
||
item.GetStart(out lx, out ly);
|
||
ConvertDrawingObjectAxis(lx, ly, image.Width, image.Height, out dStartPoint);
|
||
item.GetEnd(out lx, out ly);
|
||
ConvertDrawingObjectAxis(lx, ly, image.Width, image.Height, out dEndPoint);
|
||
|
||
float radius;
|
||
ConvertDrawingObjectAxis(Convert.ToInt32(item.GetRadius()), out radius);
|
||
arc.Radius = radius;
|
||
|
||
double dStartAngle = Math.Atan2((dStartPoint.Y - dCenterPoint.Y), (dStartPoint.X - dCenterPoint.X)) * (180 / Math.PI);
|
||
// arc.StartAngle 는 0~360 범위가 아니면 입력자체가 안된다.
|
||
if (dStartAngle < 0) dStartAngle += 360.0f;
|
||
arc.StartAngle = (float)dStartAngle;
|
||
|
||
double dEndAngle = Math.Atan2((dEndPoint.Y - dCenterPoint.Y), (dEndPoint.X - dCenterPoint.X)) * (180 / Math.PI);
|
||
if (dEndAngle < 0.0) arc.EndAngle = (float)(dEndAngle + 360.0);
|
||
else arc.EndAngle = (float)dEndAngle;
|
||
arc.SweepAngle = arc.EndAngle - arc.StartAngle;
|
||
if (arc.SweepAngle > 0 && arc.Direction == Arc.eDirection.CW) {
|
||
arc.SweepAngle -= 360;
|
||
}
|
||
|
||
//double dCenterDist = Math.Sqrt(dCenterPoint.X * dCenterPoint.X + dCenterPoint.Y * dCenterPoint.Y);
|
||
//double dStartDist = Math.Sqrt(dStartPoint.X * dStartPoint.X + dStartPoint.Y * dStartPoint.Y);
|
||
|
||
//if (dCenterDist > dStartDist)
|
||
//{
|
||
// arc.Direction = Arc.eDirection.CW;
|
||
//}
|
||
//else
|
||
//{
|
||
// arc.Direction = Arc.eDirection.CCW;
|
||
//}
|
||
|
||
|
||
// Radius 가 큰 경우는 무조건 예각만 가능하다. 크다는 기준은? Cell 의 50% 이상으로 일단 설정해봄
|
||
//if (/*arc.Radius > dStartDist*0.5 && */Math.Abs(arc.SweepAngle) > 180.0)
|
||
//{
|
||
// //Direction 바꿔주고 EndAngle 을 재설정해주자. 그러면 SweepAngle 재설정된다.
|
||
// arc.Direction = arc.Direction == Arc.eDirection.CCW ? Arc.eDirection.CW : Arc.eDirection.CCW;
|
||
// arc.EndAngle = (float)dEndAngle;
|
||
//}
|
||
}
|
||
|
||
public void ConvertPathItemToDrawingObjects(List<PathItem> lsPaths, float fLineDefualtWidth, Mat image, Arc originArc, out List<IDrawObject> destDrawObjects)
|
||
{
|
||
List<IDrawObject> ConvertedDrawObjects = new List<IDrawObject>();
|
||
int no = 0;
|
||
foreach (var item in lsPaths) {
|
||
if (item.GetType() == PathItem._PathType.Line) {
|
||
UnitPoint dStartPoint, dEndPoint;
|
||
Int64 lx, ly;
|
||
item.GetStart(out lx, out ly);
|
||
ConvertDrawingObjectAxis(lx, ly, image.Width, image.Height, out dStartPoint);
|
||
item.GetEnd(out lx, out ly);
|
||
ConvertDrawingObjectAxis(lx, ly, image.Width, image.Height, out dEndPoint);
|
||
|
||
Canvas.Line lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
lineitem.No = no;
|
||
lineitem.MadebyRCC = true;
|
||
ConvertedDrawObjects.Add(lineitem);
|
||
// parameter ID 는 어떻게?
|
||
}
|
||
else if (item.GetType() == PathItem._PathType.Arc) {
|
||
Canvas.Arc arcitem;
|
||
|
||
ConvertPathArcToDrawingArc(item, image, originArc, out arcitem);
|
||
arcitem.No = no;
|
||
arcitem.MadebyRCC = true;
|
||
//if(ConvertedDrawObjects.Count > 1)
|
||
//{
|
||
// arcitem.PF1 = ConvertedDrawObjects[ConvertedDrawObjects.Count - 1].PF2;
|
||
//}
|
||
ConvertedDrawObjects.Add(arcitem);
|
||
|
||
}
|
||
else {
|
||
// 이런 경우 없다
|
||
}
|
||
no++;
|
||
}
|
||
//Ramping 사용하지 않을 경우
|
||
//destDrawObjects = ConvertedDrawObjects;
|
||
|
||
//Ramping 사용할 경우
|
||
SplitDrawingObjectRampingInterval(originArc, ConvertedDrawObjects, out destDrawObjects);
|
||
}
|
||
|
||
public void SplitDrawingObjectRampingInterval(Arc originArc, List<IDrawObject> srcDrawObjects, out List<IDrawObject> destDrawObjects)
|
||
{
|
||
bool bArcHasRamping = false;
|
||
stRampingInfo m_RampingStartInfo;
|
||
stRampingInfo m_RampingEndInfo;
|
||
m_RampingStartInfo.length = 0;
|
||
m_RampingEndInfo.length = 0;
|
||
m_RampingStartInfo.Intervallength = 0;
|
||
m_RampingEndInfo.Intervallength = 0;
|
||
m_RampingStartInfo.PowerRatioCh1 = 0;
|
||
m_RampingEndInfo.PowerRatioCh1 = 0;
|
||
|
||
#region ramping 설정 추출. ramping 이 있을 경우에는 polygon sample 을 slpit 해야한다. 이 정보를 여기서 추출하고 generate 할때 사용한다.
|
||
// Arc 받아온 것에서 Ramping 정보 및 출력 정보 추출
|
||
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[originArc.ScannerParamNo];
|
||
|
||
// LoadArc Code 참고함. scanner 에 넣어주는 부분만 list 로 저장해두고 ramping 구간 object 생성될때 power 를 적용한다.
|
||
double radius = Math.Sqrt((originArc.CenterPointF.X - originArc.PF1.X) * (originArc.CenterPointF.X - originArc.PF1.X) + (originArc.CenterPointF.Y - originArc.PF1.Y) * (originArc.CenterPointF.Y - originArc.PF1.Y));
|
||
double length = param.RampingInterval; // ramping 1count 단위 길이. 보통 0.1mm 를 추천함. 현재는 Start 와 End 를 같이 쓰는듯.
|
||
double theta = 0;
|
||
|
||
// 이 함수에서 출력 비율에 대한 정의가 중요하다.
|
||
// Ramping 의 비율 : ex) 50% 라고 한다면 원래 출력이 Laser source 의 10% 였다면 5% 가 레이저출력비율 (이것은 CalibrateWattToPercent 에서 설정되는 값일것이다.)
|
||
// 이 함수에서는 계산된 source 비율이 아니라 Ramping 의 비율만 정해주도록 하자 (50% 로 쓰겠다는 말임)
|
||
// 즉, Laser 가 2KW 짜리이고, parameter 에서 100W 설정하고 Ramping start 가 50% 이면 시작 출력은 2.5% 가 되지만, 이 함수에서는 50% 로 저장하겠다는 얘기임
|
||
// LoadArcScannerParam 에서 MadebyRCC object 출력은 새로 만들어줘야하기 때문에 그 함수에서 laser 출력을 계산해서 스캐너에 넣겠음.
|
||
|
||
|
||
// 0. Sweep 방향에 따른 회전 방향 설정
|
||
if (originArc.Direction == Arc.eDirection.CCW) theta = (length / radius) * (180 / Math.PI);
|
||
if (originArc.Direction == Arc.eDirection.CW) theta = (-length / radius) * (180 / Math.PI);
|
||
|
||
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0)
|
||
&& (param.RampingStartDist + param.RampingEndDist < radius * Math.Abs(originArc.SweepAngle))
|
||
&& !param.WobbelEnable && !param.SnowManEnable) {
|
||
bArcHasRamping = true;
|
||
// 1.1. 데이터테이블 기준으로 파워 동작
|
||
//percentLaser1 = (float)_laserPowerCalParam.CalibrateWattToPercent(param.LaserPower1, (uint)eAnalogPortOut.DA1);
|
||
|
||
// 1.2. Ramping을 위한 변수 설정
|
||
m_RampingStartInfo.m_Count = (int)(param.RampingStartDist / length);
|
||
m_RampingEndInfo.m_Count = (int)(param.RampingEndDist / length);
|
||
|
||
m_RampingStartInfo.length = param.RampingStartDist;
|
||
m_RampingEndInfo.length = param.RampingEndDist;
|
||
|
||
// 2.1. Rising Edge 시작 파워 설정. 현재는 ring, center 구분이 없다. 기존에는 center 만 쓰게 되어있는데, 일단 둘다 적용해주자.
|
||
m_RampingStartInfo.PowerRatioCh1 = (float)param.RampingStartPower1 / 100 <= 100 ?
|
||
(float)param.RampingStartPower1 / 100 : 100;
|
||
m_RampingStartInfo.PowerRatioCh2 = m_RampingStartInfo.PowerRatioCh1; // parameter 구분이 없어서 덮어써준다. 나중에 확장하면 여기만 추가해주자.
|
||
m_RampingStartInfo.Intervallength = length;
|
||
|
||
m_RampingEndInfo.PowerRatioCh1 = (float)param.RampingEndPower1 / 100 <= 100 ?
|
||
(float)param.RampingEndPower1 / 100 : 100;
|
||
m_RampingEndInfo.PowerRatioCh2 = m_RampingEndInfo.PowerRatioCh1; // parameter 구분이 없어서 덮어써준다. 나중에 확장하면 여기만 추가해주자.
|
||
m_RampingEndInfo.Intervallength = length;
|
||
|
||
// interlock 처리
|
||
// start ramping length + end ramping length 가 전체 길이보다 길면 안된다.
|
||
if (originArc.Length < param.RampingStartDist + param.RampingEndDist)
|
||
bArcHasRamping = false; // ramping 을 쓰지 않는다.
|
||
}
|
||
#endregion
|
||
|
||
if (bArcHasRamping == false) {
|
||
destDrawObjects = srcDrawObjects;
|
||
return;
|
||
}
|
||
else {
|
||
destDrawObjects = new List<IDrawObject>();
|
||
int no = 0;
|
||
|
||
// 길이를 단위길이로 나눴을때 나머지 구하는 람다함수
|
||
Func<double, double, double> remainder = (dividend, divisor) => dividend - (Math.Floor(dividend / divisor) * divisor);
|
||
// 길이를 단위길이로 나눴을때 몫을 구하는 람다함수. step 갯수를 구하기 위함.
|
||
Func<double, double, int> floor = (dividend, divisor) => Convert.ToInt32(Math.Floor(dividend / divisor));
|
||
|
||
double dStartRampingLengthSum = 0; // 누적합을 저장해뒀다가 start 전체 length 에 도달하면 완료.
|
||
double dEndRampingLengthSum = 0; // 누적합을 저장해뒀다가 start 전체 length 에 도달하면 완료.
|
||
double dRampingResidueLength = 0; // 예를 들면 0.1mm interval 인데 채운 object 길이가 0.07mm 였다면 0.03mm 가 모자란다. 다음번에 0.03mm 채울값을 여기에 저장.
|
||
int rampingstartfinishedindex = 0;
|
||
int rampingendbeginindex = 0;
|
||
|
||
|
||
// raming start 구간이 끝나는 index 를 먼저 찾는다.
|
||
for (int i = 0; i < srcDrawObjects.Count; i++) {
|
||
double objlength = 0;
|
||
if (srcDrawObjects[i] is Line) objlength = ((Line)srcDrawObjects[i]).Distance;
|
||
else objlength = ((Arc)srcDrawObjects[i]).Length;
|
||
dStartRampingLengthSum += objlength;
|
||
if (dStartRampingLengthSum > m_RampingStartInfo.length) {
|
||
rampingstartfinishedindex = i;
|
||
break;
|
||
}
|
||
}
|
||
// raming end 구간이 시작되는 index 를 먼저 찾는다.
|
||
double dEndrampingPreDistance = 0;
|
||
for (int i = srcDrawObjects.Count - 1; i >= 0; i--) {
|
||
double objlength = 0;
|
||
if (srcDrawObjects[i] is Line) objlength = ((Line)srcDrawObjects[i]).Distance;
|
||
else objlength = ((Arc)srcDrawObjects[i]).Length;
|
||
dEndRampingLengthSum += objlength;
|
||
if (dEndRampingLengthSum > m_RampingEndInfo.length) {
|
||
rampingendbeginindex = i;
|
||
dEndrampingPreDistance = dEndRampingLengthSum - m_RampingEndInfo.length;
|
||
break;
|
||
}
|
||
}
|
||
dStartRampingLengthSum = 0;
|
||
dEndRampingLengthSum = 0;
|
||
|
||
|
||
// ramping start 구간
|
||
// 앞쪽부터 채운다.
|
||
if (m_RampingStartInfo.length > 0) {
|
||
for (int i = 0; i <= rampingstartfinishedindex; i++) {
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
var obj = srcDrawObjects[i];
|
||
|
||
if (obj is Line) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Line lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
|
||
if (dRampingResidueLength > 0) {
|
||
// 전회차에 남은 거리를 object 로 만들어줬다면 모자란 만큼을 처음 object 로 만들어준다.
|
||
|
||
if (lineitem.Distance < dRampingResidueLength) {
|
||
// 다음 object 가 모자란 양을 다 못채우는 경우. 즉, 길이가 더 짧은 경우임. (사실상 ramping interval 이 더 짧아서 이런경우는 없을듯)
|
||
dRampingResidueLength -= lineitem.Distance;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dStartRampingLengthSum += lineitem.Distance;
|
||
continue;
|
||
}
|
||
else {
|
||
lineitem.Distance = dRampingResidueLength;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dStartRampingLengthSum += lineitem.Distance;
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 object 이어 붙이고 종료
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
// Ramping Object 아님
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
// 중간지점 interval 길이만큼 반복으로 채워준다.
|
||
while (lineitem.Distance > m_RampingStartInfo.Intervallength) {
|
||
lineitem.Distance = m_RampingStartInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dStartRampingLengthSum += lineitem.Distance;
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dStartRampingLengthSum += lineitem.Distance;
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
else {
|
||
// 여기까지오면 interval 보다 짧은 구간만 남는다. 채워넣어주고 모자란 길이를 residue 로 기록하자.
|
||
dRampingResidueLength = m_RampingStartInfo.Intervallength - lineitem.Distance;
|
||
}
|
||
|
||
}
|
||
else if (obj is Arc) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Arc arcitem = (Canvas.Arc)obj.Clone();
|
||
|
||
if (dRampingResidueLength > 0) {
|
||
// 전회차에 남은 거리를 object 로 만들어줬다면 모자란 만큼을 처음 object 로 만들어준다.
|
||
|
||
if (arcitem.Length < dRampingResidueLength) {
|
||
// 다음 object 가 모자란 양을 다 못채우는 경우. 즉, 길이가 더 짧은 경우임. (사실상 ramping interval 이 더 짧아서 이런경우는 없을듯)
|
||
dRampingResidueLength -= arcitem.Length;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dStartRampingLengthSum += arcitem.Length;
|
||
continue;
|
||
}
|
||
else {
|
||
dEndPoint = new UnitPoint(arcitem.PF2.X, arcitem.PF2.Y);
|
||
arcitem.Length = (float)dRampingResidueLength;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dStartRampingLengthSum += (double)(arcitem.Length);
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 object 이어 붙이고 종료
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
// Ramping Object 아님
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
arcitem = (Canvas.Arc)obj.Clone(); arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
|
||
// 중간지점 interval 길이만큼 반복으로 채워준다.
|
||
while (arcitem.Length > m_RampingStartInfo.Intervallength) {
|
||
dEndPoint = new UnitPoint(arcitem.PF2.X, arcitem.PF2.Y);
|
||
arcitem.Length = (float)m_RampingStartInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dStartRampingLengthSum += arcitem.Length;
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dStartRampingLengthSum += arcitem.Length;
|
||
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
rampingstartfinishedindex = i;
|
||
break;
|
||
}
|
||
else {
|
||
// 여기까지오면 interval 보다 짧은 구간만 남는다. 채워넣어주고 모자란 길이를 residue 로 기록하자.
|
||
dRampingResidueLength = m_RampingStartInfo.Intervallength - arcitem.Length;
|
||
}
|
||
}
|
||
else {
|
||
// 없음
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
//StartRamping을 안쓸경우 rampingstartfinishedindex를 0번부터 만들어줘야하는데.. 쓸 경우는 아래 for문에서 +1을 줘야하니깐 여기서 빼주자
|
||
rampingstartfinishedindex -= 1;
|
||
}
|
||
|
||
// no ramping 구간
|
||
// addrange 를 쓰면 속성값을 입력할 수 없다. for loop 로 변경한다.
|
||
//List<IDrawObject> NoRampingtDrawObjects = srcDrawObjects.GetRange(rampingstartfinishedindex+1, rampingendbeginindex - rampingstartfinishedindex);
|
||
//destDrawObjects.AddRange(NoRampingtDrawObjects);
|
||
|
||
//startramping만 사용할 때 rampingendbeginindex를 +1 해주지않으면 마지막객체가 생성되지 않음
|
||
if (m_RampingStartInfo.length > 0 && m_RampingEndInfo.length == 0)
|
||
rampingendbeginindex += 1;
|
||
|
||
for (int i = rampingstartfinishedindex + 1; i < rampingendbeginindex; i++) {
|
||
|
||
var obj = srcDrawObjects[i];
|
||
obj.No = no++;
|
||
|
||
if (obj is Line) {
|
||
((Line)obj).MadebyRCC = true;
|
||
}
|
||
else if (obj is Arc) {
|
||
((Arc)obj).MadebyRCC = true;
|
||
}
|
||
else {
|
||
// 이런 경우가 없음 여기로 오면 예외처리 할 것.
|
||
}
|
||
destDrawObjects.Add(obj);
|
||
}
|
||
|
||
|
||
if (m_RampingEndInfo.length > 0) {
|
||
// ramping end 구간
|
||
List<IDrawObject> RampingEndtDrawObjects = new List<IDrawObject>();
|
||
|
||
// ramping end 구간 첫번째 item 은 별도로 처리. ramping 이 아닌곳과 ramping 인곳이 섞여있음.
|
||
dRampingResidueLength = 0;
|
||
dEndRampingLengthSum = 0;
|
||
var obj = srcDrawObjects[rampingendbeginindex];
|
||
// ramping 아닌 구간을 먼저 채운다.
|
||
if (obj is Line) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Line lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
// dEndrampingPreDistance
|
||
lineitem.Distance = dEndrampingPreDistance;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
// 중간지점 interval 길이만큼 반복으로 채워준다.
|
||
while (lineitem.Distance > m_RampingEndInfo.Intervallength) {
|
||
lineitem.Distance = m_RampingEndInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
}
|
||
else {
|
||
// 여기까지오면 interval 보다 짧은 구간만 남는다. 채워넣어주고 모자란 길이를 residue 로 기록하자.
|
||
dRampingResidueLength = m_RampingEndInfo.Intervallength - lineitem.Distance;
|
||
}
|
||
}
|
||
else if (obj is Arc) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Arc arcitem = (Canvas.Arc)obj.Clone();
|
||
|
||
// ramping 이 아닌 구간을 먼저 넣어줌
|
||
arcitem.Length = (float)dEndrampingPreDistance;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
|
||
// 여기부터 ramping 구간
|
||
while (arcitem.Length > m_RampingEndInfo.Intervallength) {
|
||
arcitem.Length = (float)m_RampingEndInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += arcitem.Length;
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
if (arcitem.Length > 0 && dEndRampingLengthSum < m_RampingEndInfo.length) {
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += arcitem.Length;
|
||
dRampingResidueLength = m_RampingEndInfo.Intervallength - arcitem.Length;
|
||
}
|
||
}
|
||
|
||
// 첫번째 item 입력 완료
|
||
// 2번째 이후가 있다면 아래 실행.
|
||
|
||
for (int i = rampingendbeginindex + 1; i < srcDrawObjects.Count; i++) {
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
obj = srcDrawObjects[i];
|
||
|
||
if (obj is Line) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Line lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
|
||
if (dRampingResidueLength > 0) {
|
||
// 전회차에 남은 거리를 object 로 만들어줬다면 모자란 만큼을 처음 object 로 만들어준다.
|
||
|
||
if (lineitem.Distance < dRampingResidueLength) {
|
||
// 다음 object 가 모자란 양을 다 못채우는 경우. 즉, 길이가 더 짧은 경우임. (사실상 ramping interval 이 더 짧아서 이런경우는 없을듯)
|
||
dRampingResidueLength -= lineitem.Distance;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
continue;
|
||
}
|
||
else {
|
||
lineitem.Distance = dRampingResidueLength;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 object 이어 붙이고 종료
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue);
|
||
// Ramping Object 아님
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
// 중간지점 interval 길이만큼 반복으로 채워준다.
|
||
while (lineitem.Distance > m_RampingEndInfo.Intervallength) {
|
||
lineitem.Distance = m_RampingEndInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
dStartPoint = new UnitPoint(lineitem.PF2);
|
||
lineitem = new Canvas.Line(dStartPoint, dEndPoint, fLineDefualtWidth, Color.Blue); // while 돌리기 전에 비교를 위해서 라인을 먼저 만든다.
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
lineitem.No = no++;
|
||
lineitem.MadebyRCC = true;
|
||
destDrawObjects.Add(lineitem);
|
||
dEndRampingLengthSum += lineitem.Distance;
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
else {
|
||
// 여기까지오면 interval 보다 짧은 구간만 남는다. 채워넣어주고 모자란 길이를 residue 로 기록하자.
|
||
dRampingResidueLength = m_RampingEndInfo.Intervallength - lineitem.Distance;
|
||
}
|
||
|
||
}
|
||
else if (obj is Arc) {
|
||
UnitPoint dStartPoint = new UnitPoint(obj.PF1);
|
||
UnitPoint dEndPoint = new UnitPoint(obj.PF2);
|
||
|
||
Canvas.Arc arcitem = (Canvas.Arc)obj.Clone();
|
||
|
||
if (dRampingResidueLength > 0) {
|
||
// 전회차에 남은 거리를 object 로 만들어줬다면 모자란 만큼을 처음 object 로 만들어준다.
|
||
|
||
if (arcitem.Length < dRampingResidueLength) {
|
||
// 다음 object 가 모자란 양을 다 못채우는 경우. 즉, 길이가 더 짧은 경우임. (사실상 ramping interval 이 더 짧아서 이런경우는 없을듯)
|
||
dRampingResidueLength -= arcitem.Length;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += arcitem.Length;
|
||
continue;
|
||
}
|
||
else {
|
||
arcitem.Length = (float)dRampingResidueLength;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += (double)(arcitem.Length);
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 object 이어 붙이고 종료
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
|
||
// 중간지점 interval 길이만큼 반복으로 채워준다.
|
||
while (arcitem.Length > m_RampingEndInfo.Intervallength) {
|
||
arcitem.Length = (float)m_RampingEndInfo.Intervallength;
|
||
dRampingResidueLength = 0;
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += arcitem.Length;
|
||
dStartPoint = new UnitPoint(arcitem.PF2);
|
||
arcitem = (Canvas.Arc)obj.Clone();
|
||
arcitem.PF1 = new PointF((float)dStartPoint.X, (float)dStartPoint.Y);
|
||
arcitem.PF2 = new PointF((float)dEndPoint.X, (float)dEndPoint.Y);
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (dEndRampingLengthSum < m_RampingEndInfo.length) {
|
||
// 마지막 남은 잔여 길이 넣음. 위에 while 문에서 경로 생성이 끝났다면 남은 구간 전체를 넣고 종료. 생성이 안끝났어도 남은 구간을 다 넣고 다음 경로로 이동. 남은양을 전달한다.
|
||
arcitem.No = no++;
|
||
arcitem.MadebyRCC = true;
|
||
destDrawObjects.Add(arcitem);
|
||
dEndRampingLengthSum += arcitem.Length;
|
||
}
|
||
|
||
if (dEndRampingLengthSum >= m_RampingEndInfo.length) {
|
||
// 구간길이를 모두 채우면 다음 index 기록하고 ramping 생성 종료
|
||
dRampingResidueLength = 0;
|
||
break;
|
||
}
|
||
else {
|
||
// 여기까지오면 interval 보다 짧은 구간만 남는다. 채워넣어주고 모자란 길이를 residue 로 기록하자.
|
||
dRampingResidueLength = m_RampingEndInfo.Intervallength - arcitem.Length;
|
||
}
|
||
}
|
||
else {
|
||
// 없음
|
||
}
|
||
}
|
||
}
|
||
|
||
// ramping power 를 설정해준다.
|
||
dStartRampingLengthSum = 0;
|
||
dEndRampingLengthSum = 0;
|
||
|
||
// ramping start 구간 power 설정
|
||
//
|
||
for (int i = 0; i < destDrawObjects.Count; i++) {
|
||
var obj = destDrawObjects[i];
|
||
if (dStartRampingLengthSum >= m_RampingStartInfo.length) {
|
||
if (obj is Line) {
|
||
Line line = (Line)obj;
|
||
line.RCC_PowerRatio = 1;
|
||
}
|
||
if (obj is Arc) {
|
||
Arc arc = (Arc)obj;
|
||
arc.RCC_PowerRatio = 1;
|
||
}
|
||
}
|
||
else {
|
||
float powerratio = (float)((1 - m_RampingStartInfo.PowerRatioCh1) * dStartRampingLengthSum / m_RampingStartInfo.length + m_RampingStartInfo.PowerRatioCh1);
|
||
// 상한, 하한 설정 (ratio 는 0보다 크거나 같고 1보다 작거나 같다)
|
||
if (powerratio < 0) { powerratio = 0; } else if (powerratio > 1) { powerratio = 1; }
|
||
if (obj is Line) {
|
||
Line line = (Line)obj;
|
||
dStartRampingLengthSum += line.Distance;
|
||
line.RCC_PowerRatio = powerratio;
|
||
}
|
||
if (obj is Arc) {
|
||
Arc arc = (Arc)obj;
|
||
dStartRampingLengthSum += arc.Length;
|
||
arc.RCC_PowerRatio = powerratio;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ramping end 구간 power 설정
|
||
//
|
||
for (int i = destDrawObjects.Count - 1; i > 0; i--) {
|
||
if (dEndRampingLengthSum > m_RampingEndInfo.length) {
|
||
break;
|
||
}
|
||
var obj = destDrawObjects[i];
|
||
|
||
float powerratio = (float)((1 - m_RampingEndInfo.PowerRatioCh1) * dEndRampingLengthSum / m_RampingEndInfo.length + m_RampingEndInfo.PowerRatioCh1);
|
||
// 상한, 하한 설정 (ratio 는 0보다 크거나 같고 1보다 작거나 같다)
|
||
if (powerratio < 0) { powerratio = 0; } else if (powerratio > 1) { powerratio = 1; }
|
||
|
||
if (obj is Line) {
|
||
Line line = (Line)obj;
|
||
dEndRampingLengthSum += line.Distance;
|
||
line.RCC_PowerRatio = powerratio;
|
||
}
|
||
if (obj is Arc) {
|
||
Arc arc = (Arc)obj;
|
||
dEndRampingLengthSum += arc.Length;
|
||
arc.RCC_PowerRatio = powerratio;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool CheckInterlockDrawingObjectPosition(List<IDrawObject> lsDrawObjects, UnitPoint detectedContourCenter, Arc originArc)
|
||
{
|
||
Func<double, double, double> GetDistance = (x, y) => Math.Sqrt(x * x + y * y);
|
||
//Func<double, double, double, double, double> GetDistance = (x,y, centerx, centery) => Math.Sqrt(Math.Pow((x-centerx),2) + Math.Pow((y - centery), 2));
|
||
Func<double, double, double, bool> CheckInRangeMinMax = (x, min, max) => {
|
||
if (x < max && x > min) return true;
|
||
else return false;
|
||
};
|
||
Func<double, double, bool> CheckInRange = (x, range) => {
|
||
if (x < range && x > -range) return true;
|
||
else return false;
|
||
};
|
||
bool bRet = true;
|
||
dPathLengthSum = 0;
|
||
foreach (var item in lsDrawObjects) {
|
||
if (item is Line) {
|
||
var line = (Line)item;
|
||
dPathLengthSum += line.Distance;
|
||
if (!CheckInRange(GetDistance(line.P1.X - detectedContourCenter.X, line.P1.Y - detectedContourCenter.Y) - originArc.Radius, dInterlockDistRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, Distance(LinePF1, Center) - Distance(ArcRadius) : {GetDistance(line.P1.X - detectedContourCenter.X, line.P1.Y - detectedContourCenter.Y) - originArc.Radius} mm");
|
||
bRet = false;
|
||
}
|
||
if (!CheckInRange(GetDistance(line.P2.X - detectedContourCenter.X, line.P2.Y - detectedContourCenter.Y) - originArc.Radius, dInterlockDistRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, Distance(LinePF2, Center) - Distance(ArcRadius) : {GetDistance(line.P2.X - detectedContourCenter.X, line.P2.Y - detectedContourCenter.Y) - originArc.Radius} mm");
|
||
bRet = false;
|
||
}
|
||
}
|
||
else if (item is Arc) {
|
||
var arc = (Arc)item;
|
||
dPathLengthSum += arc.Length;
|
||
if (!CheckInRange(GetDistance(arc.PF1.X - detectedContourCenter.X, arc.PF1.Y - detectedContourCenter.Y) - originArc.Radius, dInterlockDistRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, Distance(ArcPF1, Center) - Distance(ArcRadius) : {GetDistance(arc.PF1.X - detectedContourCenter.X, arc.PF1.Y - detectedContourCenter.Y) - originArc.Radius} mm");
|
||
bRet = false;
|
||
}
|
||
if (!CheckInRange(GetDistance(arc.PF2.X - detectedContourCenter.X, arc.PF2.Y - detectedContourCenter.Y) - originArc.Radius, dInterlockDistRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, Distance(ArcPF2, Center) - Distance(ArcRadius) : {GetDistance(arc.PF2.X - detectedContourCenter.X, arc.PF2.Y - detectedContourCenter.Y) - originArc.Radius} mm");
|
||
bRet = false;
|
||
}
|
||
if (arc.Length > dInterlockArcLength) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, ArcLength : {arc.Length} mm");
|
||
bRet = false;
|
||
}
|
||
}
|
||
else {
|
||
|
||
}
|
||
}
|
||
if (!CheckInRange(dPathLengthSum - originArc.Length, dInterlockLengthDiffRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, TotalPathLength - OriginArcLength : {dPathLengthSum - originArc.Length} mm");
|
||
bRet = false;
|
||
}
|
||
if (!CheckInRange(originArc.Center.X - detectedContourCenter.X, dInterlockCenterXYDiffRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, OriginArcCenterX - DetectEdgeCenterX : {(originArc.Center.X - detectedContourCenter.X).ToString("F2")} mm");
|
||
bRet = false;
|
||
}
|
||
if (!CheckInRange(originArc.Center.Y - detectedContourCenter.Y, dInterlockCenterXYDiffRange)) {
|
||
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, OriginArcCenterY - DetectEdgeCenterY : {(originArc.Center.Y - detectedContourCenter.Y).ToString("F2")} mm");
|
||
bRet = false;
|
||
}
|
||
return bRet;
|
||
}
|
||
|
||
public bool SaveDrawingObjectsToCSV(List<IDrawObject> lsDrawObjects, string strMode)
|
||
{
|
||
System.Data.DataTable dataTable = new System.Data.DataTable();
|
||
|
||
string[] StrColumnTitles = {"No", "Line_P1X","Line_P1Y","Line_P2X","Line_P2Y","Arc_P1X","Arc_P1Y","Arc_P2X","Arc_P2Y",
|
||
"Arc_CenterX","Arc_CenterY","Arc_StartAngle","Arc_EndAngle","Arc_SweepAngle", "Length", "PowerRatio"};
|
||
|
||
dataTable.Columns.Add(new System.Data.DataColumn("Index", typeof(int)));
|
||
|
||
foreach (var item in StrColumnTitles) {
|
||
if (item == "No")
|
||
dataTable.Columns.Add(new System.Data.DataColumn(item, typeof(int)));
|
||
else
|
||
dataTable.Columns.Add(new System.Data.DataColumn(item, typeof(double)));
|
||
}
|
||
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("No.", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Line_P1X", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Line_P1Y", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Line_P2X", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Line_P2Y", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_P1X", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_P1Y", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_P2X", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_P2Y", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_CenterX", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_CenterY", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_StartAngle", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_EndAngle", typeof(int)));
|
||
//dataTable.Columns.Add(new System.Data.DataColumn("Arc_SweepAngle", typeof(int)));
|
||
|
||
for (int i = 0; i < lsDrawObjects.Count; i++) {
|
||
System.Data.DataRow row = dataTable.NewRow();
|
||
int iRowCount = dataTable.Rows.Count;
|
||
object[] rowArray = new object[StrColumnTitles.Length + 1];
|
||
for (int j = 0; j < rowArray.Length; j++)
|
||
rowArray[j] = 0;
|
||
|
||
rowArray[0] = iRowCount + 1;
|
||
//rowArray[1] = lsDrawObjects[i].No;
|
||
|
||
if (lsDrawObjects[i] is Line) {
|
||
Line line = lsDrawObjects[i] as Line;
|
||
rowArray[1] = line.No + 1;
|
||
rowArray[2] = Math.Round(line.P1.X, 4);
|
||
rowArray[3] = Math.Round(line.P1.Y, 4);
|
||
rowArray[4] = Math.Round(line.P2.X, 4);
|
||
rowArray[5] = Math.Round(line.P2.Y, 4);
|
||
rowArray[15] = Math.Round(line.Distance, 4);
|
||
rowArray[16] = Math.Round(line.RCC_PowerRatio, 4);
|
||
}
|
||
else if (lsDrawObjects[i] is Arc) {
|
||
Arc arc = lsDrawObjects[i] as Arc;
|
||
rowArray[1] = arc.No + 1;
|
||
rowArray[6] = Math.Round(arc.PF1.X, 4);
|
||
rowArray[7] = Math.Round(arc.PF1.Y, 4);
|
||
rowArray[8] = Math.Round(arc.PF2.X, 4);
|
||
rowArray[9] = Math.Round(arc.PF2.Y, 4);
|
||
rowArray[10] = Math.Round(arc.Center.X, 4);
|
||
rowArray[11] = Math.Round(arc.Center.Y, 4);
|
||
rowArray[12] = Math.Round(arc.StartAngle, 4);
|
||
rowArray[13] = Math.Round(arc.EndAngle, 4);
|
||
rowArray[14] = Math.Round(arc.SweepAngle, 4);
|
||
rowArray[15] = Math.Round(arc.Length, 4);
|
||
rowArray[16] = Math.Round(arc.RCC_PowerRatio, 4);
|
||
}
|
||
row.ItemArray = rowArray;
|
||
dataTable.Rows.Add(row);
|
||
}
|
||
|
||
//ILSOSConfig로 파일 경로 수정
|
||
var DefaultPath = ConfigurationManager.AppSettings.Get("DATA_PATH");
|
||
string fullPath = Path.Combine(DefaultPath, "_vision", "GenerateObject");
|
||
string fileName = fullPath + string.Format("\\{0}_{1}.csv", strMode, DateTime.Now.ToString("yyyyMMdd_HHmmss.fff"));
|
||
|
||
ExcelManagementUnit.ExportToCSV(fileName, dataTable);
|
||
|
||
return true;
|
||
}
|
||
|
||
public void MergePolygonSample(TPolygon polygon, Mat image, double dMergeDist, UnitPoint detectedContourCenter, Arc originArc, out TPolygon merged_polygon)
|
||
{
|
||
//Pixel의 시작위치를 Merge전에 첫 Pixel은 가지고있어야 시작점 위치가 변경되지 않을듯 하다
|
||
// Arc 정보가없으면 그냥 default 생성된 rearranged_polygon list 를 사용
|
||
if (originArc == null) {
|
||
merged_polygon = null;
|
||
return;
|
||
}
|
||
|
||
// 본래 고객이 그린 Arc 방향에 따라서 point array 방향을 돌려준다. rearrange 한 후에 돌려주는게 계산량이 적을듯.
|
||
// Find Edge 로 찾은 방향은 FindContour 방향과 반대로 되어 있어서 수정함.
|
||
//if (m_OriginArc.Direction == Arc.eDirection.CW)
|
||
if (originArc.Direction == Arc.eDirection.CCW)
|
||
polygon.Reverse();
|
||
|
||
// 시작위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
|
||
double startX, startY;
|
||
CalAnglePoint(detectedContourCenter.X, detectedContourCenter.Y, originArc.Radius, originArc.StartAngle, out startX, out startY);
|
||
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(startX, startY), image.Width, image.Height, out startX, out startY);
|
||
List<double> DistancelistFromStartPoint = new List<double>();
|
||
for (int i = 0; i < polygon.Count; i++) {
|
||
DistancelistFromStartPoint.Add(Math.Sqrt(Math.Pow(Convert.ToDouble(polygon[i].x) - startX, 2) + Math.Pow(Convert.ToDouble(polygon[i].y) - startY, 2)));
|
||
}
|
||
// 리스트에서 가장 작은 값 찾기
|
||
double minValueFromStartPoint = DistancelistFromStartPoint.Min();
|
||
// 가장 작은 값의 인덱스 찾기
|
||
int minIndexFromStartPoint = DistancelistFromStartPoint.IndexOf(minValueFromStartPoint);
|
||
// 처음부터 minIndex개의 아이템을 잘라내기
|
||
TPolygon firstpolygons = polygon.GetRange(0, minIndexFromStartPoint + 1);
|
||
|
||
// 원래 리스트에서 첫 minIndex개의 아이템 제거
|
||
polygon.RemoveRange(0, minIndexFromStartPoint + 1);
|
||
|
||
// 잘라낸 minIndex개의 아이템을 뒤에 붙임
|
||
polygon.AddRange(firstpolygons);
|
||
|
||
if (originArc.AbsSweepAngle > 360) {
|
||
// 종료위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
|
||
double endX, endY;
|
||
CalAnglePoint(detectedContourCenter.X, detectedContourCenter.Y, originArc.Radius, originArc.EndAngle, out endX, out endY);
|
||
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(endX, endY), image.Width, image.Height, out endX, out endY);
|
||
List<double> DistancelistFromEndPoint = new List<double>();
|
||
for (int i = 0; i < polygon.Count; i++) {
|
||
DistancelistFromEndPoint.Add(Math.Sqrt(Math.Pow(Convert.ToDouble(polygon[i].x) - endX, 2) + Math.Pow(Convert.ToDouble(polygon[i].y) - endY, 2)));
|
||
}
|
||
// 리스트에서 가장 작은 값 찾기
|
||
double minValueFromEndPoint = DistancelistFromEndPoint.Min();
|
||
// 가장 작은 값의 인덱스 찾기
|
||
int minIndexFromEndPoint = DistancelistFromEndPoint.IndexOf(minValueFromEndPoint);
|
||
// 처음부터 minIndex개의 아이템을 복사하기
|
||
TPolygon addpolygons = polygon.GetRange(0, minIndexFromEndPoint + 1);
|
||
// 복사한 minIndex개의 아이템을 뒤에 붙임
|
||
polygon.AddRange(addpolygons);
|
||
}
|
||
else if (originArc.AbsSweepAngle < 360) {
|
||
// sweep angle 이 360보다 작은 경우 end 까지만 사용하고 나머지는 버린다.
|
||
|
||
// 종료위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
|
||
double endX, endY;
|
||
CalAnglePoint(detectedContourCenter.X, detectedContourCenter.Y, originArc.Radius, originArc.EndAngle, out endX, out endY);
|
||
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(endX, endY), image.Width, image.Height, out endX, out endY);
|
||
|
||
List<double> DistancelistFromEndPoint = new List<double>();
|
||
for (int i = 0; i < polygon.Count; i++) {
|
||
DistancelistFromEndPoint.Add(Math.Sqrt(Math.Pow(Convert.ToDouble(polygon[i].x) - endX, 2) + Math.Pow(Convert.ToDouble(polygon[i].y) - endY, 2)));
|
||
}
|
||
// 리스트에서 가장 작은 값 찾기
|
||
double minValueFromEndPoint = DistancelistFromEndPoint.Min();
|
||
// 가장 작은 값의 인덱스 찾기
|
||
int minIndexFromEndPoint = DistancelistFromEndPoint.IndexOf(minValueFromEndPoint);
|
||
|
||
// 원래 리스트에서 마지막 minIndex개의 아이템 제거
|
||
polygon.RemoveRange(minIndexFromEndPoint, polygon.Count - minIndexFromEndPoint);
|
||
}
|
||
else {
|
||
// 360도 일때는 시작 point 를 end point 에 하나 더 붙여줘야 원이 완성된다
|
||
polygon.Add(polygon[0]);
|
||
}
|
||
|
||
patheditor.MergePoints(polygon, out merged_polygon, dMergeDist);
|
||
}
|
||
//public void MergePolygonSample(out TPolygon ptPolygon)
|
||
//{
|
||
// MergePolygonSample();
|
||
// ptPolygon = merged_polygon;
|
||
//}
|
||
public void ReArrangePoints(TPolygon merged_polygon, out TPolygon rearranged_polygon)
|
||
{
|
||
patheditor.RearrangePoints(merged_polygon, out rearranged_polygon);
|
||
}
|
||
|
||
private void CalAnglePoint(double cx, double cy, double radius, double degree, out double px, out double py)
|
||
{
|
||
double radian = degree * Math.PI / 180;
|
||
px = cx + radius * Math.Cos(radian);
|
||
py = cy + radius * Math.Sin(radian);
|
||
}
|
||
public void ConvertMILToMat(MIL_ID MILImage, out Mat convMatImage)
|
||
{
|
||
//WPF Bitmapsource로 변환
|
||
int width = (int)MIL.MbufInquire(MILImage, MIL.M_SIZE_X, MIL.M_NULL);
|
||
int height = (int)MIL.MbufInquire(MILImage, MIL.M_SIZE_Y, MIL.M_NULL);
|
||
|
||
#region MIL -> Bitmap -> Mat
|
||
//int stride = width; // Assuming 8-bit grayscale image
|
||
////int stride = width * 3; // Assuming 24-bit BRG image
|
||
//byte[] imageData = new byte[width * height]; // Assuming 8-bit grayscale image
|
||
////byte[] imageData = new byte[width * height * 3]; // Assuming 24-bit BRG image
|
||
//MIL.MbufGet(MILImage, imageData); // 8-bit일 경우 사용
|
||
////MIL.MbufGetColor(MILImage, MIL.M_PACKED + MIL.M_BGR24, MIL.M_ALL_BANDS, imageData);
|
||
|
||
////8-bit 1ch gray일 경우 사용
|
||
//BitmapSource bitmapSource = BitmapSource.Create(
|
||
// width, height, 96, 96, System.Windows.Media.PixelFormats.Gray8, null, imageData, stride);
|
||
|
||
////32-bit 3ch bgr
|
||
////BitmapSource bitmapSource = BitmapSource.Create(
|
||
//// width, height, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null, imageData, stride);
|
||
|
||
////Convert bitmapsource to Mat
|
||
//convMatImage = bitmapSource.ToMat();
|
||
#endregion
|
||
|
||
// 방식을 아래와같이 변경하여도 MbufGet2d와 Copy에서 시간 소요 발생
|
||
byte[] imageData = new byte[width * height];
|
||
|
||
MIL.MbufGet2d(MILImage, 0, 0, width, height, imageData);
|
||
convMatImage = new Mat(height, width, MatType.CV_8UC1);
|
||
Marshal.Copy(imageData, 0, convMatImage.Data, imageData.Length);
|
||
|
||
// MIL 해제
|
||
MIL.MbufFree(MILImage);
|
||
}
|
||
public MIL_ID ConvertMatToMilID(Mat inputMat)
|
||
{
|
||
if (inputMat.Empty())
|
||
throw new ArgumentException("입력된 Mat 이미지가 비어 있습니다.");
|
||
|
||
int width = inputMat.Width;
|
||
int height = inputMat.Height;
|
||
|
||
MIL_ID milImage;
|
||
|
||
if (inputMat.Type() == MatType.CV_8UC1) {
|
||
// 1채널 8비트 Grayscale
|
||
milImage = MIL.MbufAlloc2d(
|
||
MIL.M_DEFAULT_HOST,
|
||
(uint)width,
|
||
(uint)height,
|
||
8 + MIL.M_UNSIGNED,
|
||
MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP,
|
||
MIL.M_NULL
|
||
);
|
||
|
||
byte[] grayData = new byte[width * height];
|
||
Marshal.Copy(inputMat.Data, grayData, 0, grayData.Length);
|
||
|
||
MIL.MbufPut2d(milImage, 0, 0, width, height, grayData);
|
||
}
|
||
else if (inputMat.Type() == MatType.CV_8UC3) {
|
||
// 3채널 8비트 BGR 컬러
|
||
// MIL은 기본적으로 BGR24 (packed) 형식을 지원함
|
||
|
||
milImage = MIL.MbufAllocColor(
|
||
MIL.M_DEFAULT_HOST,
|
||
3, // BGR
|
||
(uint)width,
|
||
(uint)height,
|
||
8 + MIL.M_UNSIGNED,
|
||
MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP,
|
||
MIL.M_NULL
|
||
);
|
||
|
||
byte[] colorData = new byte[width * height * 3];
|
||
Marshal.Copy(inputMat.Data, colorData, 0, colorData.Length);
|
||
|
||
// packed BGR 형식으로 복사
|
||
MIL.MbufPutColor(milImage, MIL.M_PACKED + MIL.M_BGR24, MIL.M_ALL_BANDS, colorData);
|
||
}
|
||
else
|
||
throw new NotSupportedException($"지원되지 않는 Mat 타입: {inputMat.Type()}");
|
||
|
||
|
||
return milImage;
|
||
}
|
||
|
||
public Point[][] FindMaxContour_old(Mat oriImg, double threshold, int morpIterations, int morpMarkSize)
|
||
{
|
||
// Convert to grayscale
|
||
Mat grayImg = new Mat();
|
||
Cv2.CvtColor(oriImg, grayImg, ColorConversionCodes.BGR2GRAY);
|
||
|
||
// Apply threshold
|
||
Mat thresImg = new Mat();
|
||
Cv2.Threshold(grayImg, thresImg, threshold, 200, ThresholdTypes.Binary);
|
||
|
||
// Find contours
|
||
Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(thresImg, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
|
||
// Find the largest contour
|
||
double maxArea = 0;
|
||
int maxCntNo = 0;
|
||
for (int i = 0; i < contours.Length; i++) {
|
||
double area = Cv2.ContourArea(contours[i]);
|
||
if (maxArea < area) {
|
||
maxArea = area;
|
||
maxCntNo = i;
|
||
}
|
||
}
|
||
// Draw the largest contour
|
||
Mat maxCntImg = new Mat(oriImg.Rows, oriImg.Cols, MatType.CV_8UC1, Scalar.Black);
|
||
Cv2.DrawContours(maxCntImg, contours, maxCntNo, Scalar.White, -1);
|
||
|
||
// Apply morphological operations
|
||
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(morpMarkSize, morpMarkSize));
|
||
Cv2.MorphologyEx(maxCntImg, maxCntImg, MorphTypes.Erode, kernel, iterations: morpIterations);
|
||
|
||
// Find contours again on the eroded image
|
||
Point[][] erodeContours;
|
||
Cv2.FindContours(maxCntImg, out erodeContours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
|
||
return erodeContours;
|
||
}
|
||
|
||
//RCC용 MaxContour 알고리즘으로 변경
|
||
public Point[][] FindMaxContour(Mat oriImg, double threshold, int morpIterations, int morpMarkSize)
|
||
{
|
||
// Convert to grayscale
|
||
Mat grayImg = new Mat();
|
||
Cv2.CvtColor(oriImg, grayImg, ColorConversionCodes.BGR2GRAY);
|
||
|
||
//// Apply Sobel filter
|
||
//Mat sobelx = new Mat();
|
||
//Mat sobely = new Mat();
|
||
//Cv2.Sobel(grayImg, sobelx, MatType.CV_64F, 1, 0, 3);
|
||
//Cv2.Sobel(grayImg, sobely, MatType.CV_64F, 0, 1, 3);
|
||
|
||
//// Convert scale to absolute
|
||
//Cv2.ConvertScaleAbs(sobelx, sobelx);
|
||
//Cv2.ConvertScaleAbs(sobely, sobely);
|
||
|
||
//// Combine Sobel results
|
||
//Mat sobelCombined = new Mat();
|
||
//Cv2.AddWeighted(sobelx, 0.5, sobely, 0.5, 0, sobelCombined);
|
||
|
||
|
||
// Apply threshold
|
||
Mat thresImg = new Mat();
|
||
Cv2.Threshold(grayImg, thresImg, threshold, 200, ThresholdTypes.Binary);
|
||
|
||
// Find contours
|
||
Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(thresImg, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
|
||
// Find the largest contour
|
||
double maxArea = 0;
|
||
int maxCntNo = 0;
|
||
for (int i = 0; i < contours.Length; i++) {
|
||
double area = Cv2.ContourArea(contours[i]);
|
||
if (maxArea < area) {
|
||
maxArea = area;
|
||
maxCntNo = i;
|
||
}
|
||
}
|
||
// Draw the largest contour
|
||
Mat maxCntImg = new Mat(oriImg.Rows, oriImg.Cols, MatType.CV_8UC1, Scalar.Black);
|
||
Cv2.DrawContours(maxCntImg, contours, maxCntNo, Scalar.White, -1);
|
||
|
||
// Apply morphological operations
|
||
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(morpMarkSize, morpMarkSize));
|
||
Cv2.MorphologyEx(maxCntImg, maxCntImg, MorphTypes.Erode, kernel, iterations: morpIterations);
|
||
|
||
// Find contours again on the eroded image
|
||
Point[][] erodeContours;
|
||
Cv2.FindContours(maxCntImg, out erodeContours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
|
||
Point2f center;
|
||
float radius;
|
||
Point[] p = erodeContours[0];
|
||
Cv2.MinEnclosingCircle(p, out center, out radius);
|
||
//ConvertDrawingObjectAxis(center.X, center.Y, out DetectedContourCenter);
|
||
//ConvertDrawingObjectAxis(radius, out fDetectedContourRadius);
|
||
|
||
return erodeContours;
|
||
}
|
||
public Point2f CalculateCentroid(Point[] contour)
|
||
{
|
||
Moments moments = Cv2.Moments(contour);
|
||
return new Point2f((float)(moments.M10 / moments.M00), (float)(moments.M01 / moments.M00));
|
||
}
|
||
public Point[][] ShrinkPixelContour(Point[] contour, int shrinkBy)
|
||
{
|
||
List<Point> shrunkContour = new List<Point>();
|
||
Point2f centroid = CalculateCentroid(contour);
|
||
foreach (var point in contour) {
|
||
Point2f point2f = new Point2f(point.X, point.Y); // Convert Point to Point2f
|
||
Point2f direction = point2f - centroid;
|
||
Mat directionMat = new Mat(1, 2, MatType.CV_32F);
|
||
directionMat.Set<float>(0, 0, direction.X);
|
||
directionMat.Set<float>(0, 1, direction.Y);
|
||
float length = (float)Cv2.Norm(directionMat);
|
||
float shrinkRatio = (length - shrinkBy) / length;
|
||
Point2f newPoint = centroid + direction * shrinkRatio;
|
||
shrunkContour.Add(new Point((int)newPoint.X, (int)newPoint.Y)); // Convert Point2f back to Point
|
||
}
|
||
return new Point[][] { shrunkContour.ToArray() }; // Wrap in a 2D array
|
||
}
|
||
|
||
public Point[][] ShrinkPixelContourCSV(Point[] contour, double[] theta_list, double[] offset_list)
|
||
{
|
||
List<Point> shrunkContour = new List<Point>();
|
||
Point2f centroid = CalculateCentroid(contour);
|
||
foreach (var point in contour) {
|
||
Point2f point2f = new Point2f(point.X, point.Y); // Convert Point to Point2f
|
||
Point2f direction = point2f - centroid;
|
||
Mat directionMat = new Mat(1, 2, MatType.CV_32F);
|
||
directionMat.Set<float>(0, 0, direction.X);
|
||
directionMat.Set<float>(0, 1, direction.Y);
|
||
|
||
// 각 사분면마다 다르게 angle 계산 (오른쪽부터 시계방향으로 0~360도가 되도록 통일)
|
||
float angle = 0;
|
||
if (direction.X > 0 && direction.Y >= 0) //제 4사분면
|
||
angle = (float)Math.Atan2(direction.Y, direction.X);
|
||
else if (direction.X <= 0 && direction.Y > 0) //제 3사분면
|
||
angle = (float)(Math.Atan2(-direction.X, direction.Y) + Math.PI / 2);
|
||
else if (direction.X <= 0 && direction.Y < 0)//제 2사분면
|
||
angle = (float)(Math.Atan2(-direction.Y, -direction.X) + Math.PI);
|
||
else
|
||
angle = (float)(Math.Atan2(direction.X, -direction.Y) + Math.PI * 3 / 2); //제 1사분면
|
||
float angleInDegrees = angle * (180f / (float)Math.PI);
|
||
//int cnt = 0;
|
||
|
||
int cnt = 0;
|
||
while (cnt < contour.Length - 1) {
|
||
if (Math.Abs(theta_list[cnt] - angleInDegrees) < Math.Abs(theta_list[cnt + 1] - angleInDegrees))
|
||
break;
|
||
cnt += 1;
|
||
}
|
||
|
||
|
||
float length = (float)Cv2.Norm(directionMat);
|
||
float shrinkRatio = (length - (float)offset_list[(int)(angleInDegrees * 10)]) / length;
|
||
Point2f newPoint = centroid + direction * shrinkRatio;
|
||
shrunkContour.Add(new Point((int)newPoint.X, (int)newPoint.Y));
|
||
}
|
||
return new Point[][] { shrunkContour.ToArray() };
|
||
}
|
||
|
||
public Point[][] ShrinkPixelContourQuad(Point[] contour, int shrinkPixel1, int shrinkPixel2, int shrinkPixel3, int shrinkPixel4, Func<float, int, int, int, int, int> shrinkByFunc)
|
||
{
|
||
List<Point> shrunkContour = new List<Point>();
|
||
Point2f centroid = CalculateCentroid(contour);
|
||
//Contour를 특정각도로 나누어서 shrinkRatio를 바꿔보자 우선 하드코딩으로
|
||
|
||
foreach (var point in contour) {
|
||
Point2f point2f = new Point2f(point.X, point.Y); // Convert Point to Point2f
|
||
Point2f direction = point2f - centroid;
|
||
Mat directionMat = new Mat(1, 2, MatType.CV_32F);
|
||
directionMat.Set<float>(0, 0, direction.X);
|
||
directionMat.Set<float>(0, 1, direction.Y);
|
||
|
||
// Atan2는 0도 기준으로 아래쪽 방향이 +라서 각도에 -를 붙여줌
|
||
float angle = -(float)Math.Atan2(direction.Y, direction.X);
|
||
|
||
float angleInDegrees = angle * (180f / (float)Math.PI);
|
||
|
||
int shrinkBy = shrinkByFunc(angleInDegrees, shrinkPixel1, shrinkPixel2, shrinkPixel3, shrinkPixel4);
|
||
|
||
float length = (float)Cv2.Norm(directionMat);
|
||
float shrinkRatio = (length - shrinkBy) / length;
|
||
Point2f newPoint = centroid + direction * shrinkRatio;
|
||
shrunkContour.Add(new Point((int)newPoint.X, (int)newPoint.Y));
|
||
}
|
||
return new Point[][] { shrunkContour.ToArray() };
|
||
}
|
||
public int ShrinkByFunction(float angle, int shrinkPixel1, int shrinkPixel2, int shrinkPixel3, int shrinkPixel4)
|
||
{
|
||
if (angle >= 0 && angle < 90)
|
||
return shrinkPixel1;
|
||
else if (angle >= 90 && angle < 180)
|
||
return shrinkPixel2;
|
||
else if (angle >= -180 && angle < -90)
|
||
return shrinkPixel3;
|
||
else
|
||
return shrinkPixel4;
|
||
}
|
||
public Mat MaskingImage(Mat origImag, double dInnerCircleRadius, double dOuterCircleRadius)
|
||
{
|
||
// origImag : origin image
|
||
// dCircleRadius : Mask 할 원의 반경. mm 단위로 받아서 pixel 전환해주자
|
||
// bWhite : true 면 원안의 영역을 사용. false 면 원안의 영역을 미사용.
|
||
// return value : mask 이후의 image.
|
||
Mat retImg = new Mat();
|
||
Mat MaskImg = new Mat(origImag.Size(), MatType.CV_8UC1, Scalar.All(0));
|
||
int nInnerCircleRadius = Convert.ToInt32(dInnerCircleRadius / PixelResolution);
|
||
int nOuterCircleCircleRadius = Convert.ToInt32(dOuterCircleRadius / PixelResolution);
|
||
Cv2.Circle(MaskImg, new Point(origImag.Width / 2, origImag.Height / 2), nOuterCircleCircleRadius, Scalar.All(255), -1);
|
||
Cv2.Circle(MaskImg, new Point(origImag.Width / 2, origImag.Height / 2), nInnerCircleRadius, Scalar.All(0), -1);
|
||
Cv2.BitwiseAnd(origImag, origImag, retImg, MaskImg);
|
||
return retImg;
|
||
}
|
||
public int ReplaceRCCObject(eArcCount arcCount, List<IDrawObject> sourcelist, List<IDrawObject> targetlist, UnitPoint detectedContourCenter)
|
||
{
|
||
try {
|
||
// sourcelist 의 item 중 RCC mode 가 true 인 것을 targetlist 로 대체.
|
||
// targetlist 는 group 으로 묶어서 1개 object 로 인식하게 한다.
|
||
if (sourcelist == null || targetlist == null)
|
||
return -1;
|
||
|
||
// Group Object 를 만든다.
|
||
Group group = new Group();
|
||
|
||
// RCC Generated Object 를 생성한 Group 의 sublist 로 넣어준다. 참조가 아닌 깊은 복사로 하자.
|
||
List<IDrawObject> tempobject = new List<IDrawObject>();
|
||
int startIndex = 0; int nCount = targetlist.Count;
|
||
for (int i = startIndex; i < targetlist.Count; i++) {
|
||
if (i >= startIndex + nCount)
|
||
break;
|
||
tempobject.Add(targetlist[i]);
|
||
}
|
||
group.SubObjects = tempobject;
|
||
|
||
// Recipe 에서 RCCArc True object 를 찾는다
|
||
// RCCArc 를 Group 으로 Replace 한다.
|
||
List<Arc> arcs = new List<Arc>();
|
||
Arc selectedArc = null;
|
||
foreach (var item in sourcelist) {
|
||
if (item is Arc arc && arc.RCCArc == true) {
|
||
arcs.Add(arc);
|
||
}
|
||
}
|
||
//arcCount가 2일때 레시피상 첫번째 arc가 이미 group으로 변경되어있으니깐 첫번째 인덱스를 가져와도 됨
|
||
selectedArc = arcs[0];
|
||
|
||
// arcCount가 2일때는 2번째 Arc를 그룹으로 변경해줘야 함.
|
||
// TODO : startIndex와 EndIndex가 고정되는 문제가있음.. 추후변경 필요
|
||
startIndex = GetStartIndex(arcCount);
|
||
int endIndex = GetEndIndex(arcCount);
|
||
|
||
// TODO : arcCount가 1일때는 sourcelist가 4개이다.. sourcelitst로 인터락을 거는건 맞지않아보임..
|
||
//if (sourcelist.Count != 8) {
|
||
// Log.W($"Check Recipe object count. Current Recipe Object count: {sourcelist.Count}");
|
||
// return -1;
|
||
//}
|
||
|
||
// iteration 은 읽기 전용이므로 foreach 말고 for 문을 써야한다.
|
||
for (int i = startIndex; i < endIndex; i++) {
|
||
IDrawObject obj = sourcelist[i];
|
||
|
||
//Arc가 RCC Mode True이면 해당 Arc를 Group으로 변경한다.
|
||
if (CheckReplaceRCCObject(group, obj) == true) {
|
||
// 부모를 넘겨줘야 item 이 replace 된다
|
||
ReplaceArcToRCCObjectGroup(group, sourcelist, i);
|
||
}
|
||
else {
|
||
// Arc가 아닌 다른 객체는 기존 Arc의 Center와 RCC로 찾은 Center의 차이 Offset만큼 이동시켜줘야함
|
||
// Arc외 객체에 Offset을 주기위해 m_OriginArc.center를 사용하지 않고 현재 sourcelist에서 받아온 Arc에서 arcCount에 따라서 Center를 받아온다
|
||
// TODO : EdgeDetect PixelShrink를 고려하지않고 Center값 차이만큼 offset을 고려하니깐 Shrink가 커지고 Offset양이 작으면 가접포인트가 RCC로 생성된 객체에 근사하지않음
|
||
//UnitPoint offset = detectedContourCenter - selectedArc.Center;
|
||
// SMW 양산에서는 Align유닛이 없기 때문에 Dot 객체를 DetectedCenter만큼 옮겨주고 시퀀스에서 Recipe Arc의 Center만큼만 Offset을 준다
|
||
UnitPoint offset = detectedContourCenter;
|
||
IDrawObject objclone = obj.Clone();
|
||
objclone.Move(offset);
|
||
sourcelist[i] = objclone;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
catch (Exception e) {
|
||
Log.W($"{e.Message}");
|
||
return -1;
|
||
}
|
||
}
|
||
private int GetStartIndex(eArcCount arcCount)
|
||
{
|
||
return arcCount == eArcCount.FirstArc ? 0 : 4;
|
||
}
|
||
private int GetEndIndex(eArcCount arcCount)
|
||
{
|
||
return arcCount == eArcCount.FirstArc ? 4 : 8;
|
||
}
|
||
public int ReplaceObject(List<IDrawObject> sourcelist, double offset_X, double offset_Y, double offset_t, UnitPoint inji_center)
|
||
{
|
||
if (sourcelist == null)
|
||
return -1;
|
||
offset_t = offset_t * (180 / Math.PI);
|
||
// iteration 은 읽기 전용이므로 foreach 말고 for 문을 써야한다.
|
||
for (int i = 0; i < sourcelist.Count; i++) {
|
||
IDrawObject obj = sourcelist[i] as IDrawObject;
|
||
|
||
UnitPoint offset = new UnitPoint(offset_X, -offset_Y);
|
||
|
||
|
||
IDrawObject objclone = obj.Clone();
|
||
objclone.Move(offset);
|
||
//회전방향:반시계, 단위 : degree
|
||
objclone.Rotate((float)offset_t, inji_center);
|
||
|
||
sourcelist[i] = objclone;
|
||
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
private static bool CheckReplaceRCCObject(Group SrcGroup, IDrawObject obj)
|
||
{
|
||
// CheckReplaceRCCObject 는 Group 에 대하여 Recursive call 한다.
|
||
// group 안에 arc 가 있으면 check 만하는게 아니고 replace 까지 한다.
|
||
|
||
if (obj.Enabled == true) {
|
||
if (obj is Arc) {
|
||
if (((Arc)obj).RCCArc == true)
|
||
return true;
|
||
}
|
||
else if (obj is Group) {
|
||
CheckRCCGroupObject(SrcGroup, (Group)obj);
|
||
return false;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private static void CheckRCCGroupObject(Group SrcGroup, Group Targetobj)
|
||
{
|
||
// iteration 은 읽기 전용이므로 foreach 말고 for 문을 써야한다.
|
||
for (int i = 0; i < Targetobj.SubObjects.Count; i++) {
|
||
IDrawObject subObj = Targetobj.SubObjects[i] as IDrawObject;
|
||
if (CheckReplaceRCCObject(SrcGroup, subObj) == true) {
|
||
// 부모와 index를 넘겨줘야 item 이 replace 된다
|
||
ReplaceArcToRCCObjectGroup(SrcGroup, Targetobj.SubObjects, i);
|
||
return;
|
||
}
|
||
else {
|
||
//UnitPoint offset = DetectedContourCenter - m_OriginArc.Center;
|
||
//subObj.Move(offset);
|
||
}
|
||
}
|
||
}
|
||
private static void ReplaceArcToRCCObjectGroup(Group SrcGroup, List<IDrawObject> parentlist, int index)
|
||
{
|
||
if (parentlist.Count == 0 || parentlist.Count < index)
|
||
return;
|
||
Arc obj = (Arc)parentlist[index];
|
||
int paramNo = obj.ScannerParamNo;
|
||
int paramID = obj.ScannerParamID;
|
||
int itemNo = obj.No;
|
||
//여기서 Clone시 parentlist에 powerratio가 1로 바뀜
|
||
parentlist[index] = SrcGroup.Clone();
|
||
parentlist[index].No = itemNo;
|
||
|
||
foreach (IDrawObject objitem in ((Group)parentlist[index]).SubObjects) {
|
||
if (objitem is Line)
|
||
((Line)objitem).ScannerParamNo = paramNo;
|
||
else if (objitem is Arc)
|
||
((Arc)objitem).ScannerParamNo = paramNo;
|
||
objitem.ScannerParamID = paramID;
|
||
}
|
||
}
|
||
public bool GetArcInfoFromRecipe(List<IDrawObject> recipeDrawObject, out List<Arc> selectedArcs)
|
||
{
|
||
//해당 함수는 한번만 실행하고 가져다 쓰는곳에서 arcCount에 따라서 selectedArcs[0], selectedArcs[1]을 사용하자
|
||
selectedArcs = new List<Arc>();
|
||
|
||
foreach (var item in recipeDrawObject) {
|
||
if (item is Arc arc && arc.RCCArc == true) {
|
||
selectedArcs.Add(arc);
|
||
}
|
||
}
|
||
if (selectedArcs.Count > 0) {
|
||
return true;
|
||
}
|
||
// SetBaseArc를 따로 호출해서 selectedArc를 넣어주자..
|
||
//if (selectedArc != null) {
|
||
// SetBaseArc(selectedArc);
|
||
// return true; // 우선 한개라서 여기서 return. 여러개 할때는 삭제할것. 여러개하려면 arc 를 list 로 보내야할듯.
|
||
//}
|
||
// 여기까지 오면 Arc 등록이 안된것임.
|
||
//MessageUtil.CreateAndShowMessageDialog("Check the recipe.", "RCC Arc Unit is not in Recipe.");
|
||
return false;
|
||
}
|
||
public (Point, Point, Point) FindCenterPoint(Mat srcImg, double diff_Threshold, out Point center_point, int centerX, int centerY, int jig_size)
|
||
{
|
||
// divide 는 각도를 쪼개는 갯수
|
||
Mat grayImg = new Mat();
|
||
Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
|
||
int height = grayImg.Height;
|
||
int width = grayImg.Width;
|
||
int point_num = 2;
|
||
Point center = new Point(0, 0);
|
||
int p_x1 = width - jig_size;
|
||
|
||
int p_y1 = height / 2;
|
||
Point P1 = new Point(0, 0);
|
||
int p_x2 = jig_size;
|
||
int p_y2 = height / 2;
|
||
Point P2 = new Point(0, 0);
|
||
int p_x3 = width / 2;
|
||
int p_y3 = jig_size;
|
||
Point P3 = new Point(0, 0);
|
||
for (int i = 0; i <= 20; i++) {
|
||
|
||
while (p_x1 > (width / 2)) {
|
||
p_x1--;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 + j) + grayImg.At<byte>(p_y1 - 1, p_x1 + j) + grayImg.At<byte>(p_y1 + 1, p_x1 + j));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 - j) + grayImg.At<byte>(p_y1 - 1, p_x1 - j) + grayImg.At<byte>(p_y1 + 1, p_x1 - j));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P1 = new Point(p_x1, p_y1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
while (p_x2 < (width / 2)) {
|
||
p_x2++;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 - j) + grayImg.At<byte>(p_y2 + 1, p_x2 - j) + grayImg.At<byte>(p_y2 - 1, p_x2 - j));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 + j) + grayImg.At<byte>(p_y2 + 1, p_x2 + j) + grayImg.At<byte>(p_y2 - 1, p_x2 + j));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P2 = new Point(p_x2, p_y2);
|
||
break;
|
||
}
|
||
}
|
||
|
||
while (p_y3 < (height / 2)) {
|
||
p_y3++;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y3 - j, p_x3) + grayImg.At<byte>(p_y3 - j, p_x3 + 1) + grayImg.At<byte>(p_y3 - j, p_x3 - 1));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y3 + j, p_x3) + grayImg.At<byte>(p_y3 + j, p_x3 + 1) + grayImg.At<byte>(p_y3 + j, p_x3 - 1));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P3 = new Point(p_x3, p_y3);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 여기까지하면 theshold 차이가 나는 점 3개를 찾은것임.
|
||
// 점 3개로 원의 중심을 찾고, 원 중심에서부터 각도방향으로 threshod 차이나는 엣지를 찾는다.
|
||
center = find_circle_center_3Point(P1, P2, P3);
|
||
Log.I(string.Format($" [{i}] (Distortion Center Diff : ({center.X - centerX},{center.Y - centerY})"));
|
||
if (Math.Abs(center.X - centerX) < eDistortionCenterDiff && Math.Abs(center.Y - centerY) < eDistortionCenterDiff) {
|
||
break;
|
||
}
|
||
if (i < 20) {
|
||
p_x1 = width - jig_size;
|
||
p_y1 = ((10 + i) * height / 40);
|
||
p_x2 = jig_size;
|
||
p_y2 = ((10 + i) * height / 40);
|
||
p_x3 = ((10 + i) * width / 40);
|
||
p_y3 = jig_size;
|
||
}
|
||
else {
|
||
|
||
Log.E(string.Format($"Failed to find a center."));
|
||
}
|
||
}
|
||
|
||
center_point = find_circle_center_3Point(P1, P2, P3);
|
||
|
||
return (P1, P2, P3);
|
||
}
|
||
|
||
public void SetOffsetTable(string csv_path, double LensMagnification, double CameraPixelSize)
|
||
{
|
||
StreamReader reader = new StreamReader(csv_path);
|
||
string[] headers = reader.ReadLine().Split('\t');
|
||
int cnt = 0;
|
||
theta_list = new double[3600];
|
||
offset_list = new double[3600];
|
||
while (!reader.EndOfStream) {
|
||
string[] rows = reader.ReadLine().Split(',');
|
||
theta_list[cnt] = Double.Parse(rows[0]);
|
||
offset_list[cnt] = Double.Parse(rows[1]) * LensMagnification / CameraPixelSize * 1000;
|
||
cnt += 1;
|
||
}
|
||
|
||
}
|
||
|
||
// 이은지 연구원 코드 변환 부분
|
||
public bool FindEdge(Mat srcImg, double radius, double margin, int divide, double diff_Threshold, int centerX, int centerY, double DetectPointPercent, int jig_size,
|
||
out Point[][] points_list, out UnitPoint detectedContourCenter)
|
||
{
|
||
detectedContourCenter = new UnitPoint(0, 0);
|
||
points_list = new Point[1][];
|
||
points_list[0] = new Point[0];
|
||
int height = srcImg.Height;
|
||
int width = srcImg.Width;
|
||
if (radius == 0 || margin == 0 || divide == 0 || diff_Threshold == 0) {
|
||
Log.E(string.Format("Check the Input Parameters"));
|
||
return false;
|
||
|
||
}
|
||
int point_num = 2;
|
||
int point_num_theta = 2;
|
||
|
||
Mat grayImg = new Mat();
|
||
if (srcImg.Channels() == 3) {
|
||
Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
|
||
}
|
||
else {
|
||
grayImg = srcImg;
|
||
}
|
||
Point center = new Point(0, 0);
|
||
|
||
int p_x1 = width - jig_size;
|
||
int p_y1 = height / 2;
|
||
Point P1 = new Point(0, 0);
|
||
int p_x2 = jig_size;
|
||
int p_y2 = height / 2;
|
||
Point P2 = new Point(0, 0);
|
||
int p_x3 = width / 2;
|
||
int p_y3 = jig_size;
|
||
Point P3 = new Point(0, 0);
|
||
for (int i = 0; i <= 20; i++) {
|
||
|
||
while (p_x1 > (width / 2)) {
|
||
p_x1--;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 + j) + grayImg.At<byte>(p_y1 - 1, p_x1 + j) + grayImg.At<byte>(p_y1 + 1, p_x1 + j));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 - j) + grayImg.At<byte>(p_y1 - 1, p_x1 - j) + grayImg.At<byte>(p_y1 + 1, p_x1 - j));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P1 = new Point(p_x1, p_y1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
while (p_x2 < (width / 2)) {
|
||
p_x2++;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 - j) + grayImg.At<byte>(p_y2 + 1, p_x2 - j) + grayImg.At<byte>(p_y2 - 1, p_x2 - j));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 + j) + grayImg.At<byte>(p_y2 + 1, p_x2 + j) + grayImg.At<byte>(p_y2 - 1, p_x2 + j));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P2 = new Point(p_x2, p_y2);
|
||
break;
|
||
}
|
||
}
|
||
|
||
while (p_y3 < (height / 2)) {
|
||
p_y3++;
|
||
double outer = 0; double inner = 0;
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
outer += Convert.ToDouble(grayImg.At<byte>(p_y3 - j, p_x3) + grayImg.At<byte>(p_y3 - j, p_x3 + 1) + grayImg.At<byte>(p_y3 - j, p_x3 - 1));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(p_y3 + j, p_x3) + grayImg.At<byte>(p_y3 + j, p_x3 + 1) + grayImg.At<byte>(p_y3 + j, p_x3 - 1));
|
||
}
|
||
|
||
if ((inner - outer) / (3 * point_num) > diff_Threshold) {
|
||
P3 = new Point(p_x3, p_y3);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 여기까지하면 theshold 차이가 나는 점 3개를 찾은것임.
|
||
// 점 3개로 원의 중심을 찾고, 원 중심에서부터 각도방향으로 threshod 차이나는 엣지를 찾는다.
|
||
center = find_circle_center_3Point(P1, P2, P3);
|
||
Log.I(string.Format($" [{i}] (Distortion Center Diff : ({center.X - centerX},{center.Y - centerY}))"));
|
||
if (Math.Abs(center.X - centerX) < eDistortionCenterDiff && Math.Abs(center.Y - centerY) < eDistortionCenterDiff) {
|
||
break;
|
||
}
|
||
if (i < 20) {
|
||
p_x1 = width - jig_size;
|
||
p_y1 = ((10 + i) * height / 40);
|
||
p_x2 = jig_size;
|
||
p_y2 = ((10 + i) * height / 40);
|
||
p_x3 = ((10 + i) * width / 40);
|
||
p_y3 = jig_size;
|
||
}
|
||
else {
|
||
Log.E(string.Format($"Failed to find a center."));
|
||
return false;
|
||
}
|
||
|
||
}
|
||
if (radius + margin > center.X ||
|
||
radius + margin > center.Y ||
|
||
grayImg.Width - (radius + margin) < center.X ||
|
||
grayImg.Height - (radius + margin) < center.Y) {
|
||
|
||
points_list = new Point[1][];
|
||
points_list[0] = new Point[0];
|
||
Log.E("Center Point is outside the radius + margin area.");
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
List<Point> edge_list = new List<Point>();
|
||
List<double> l_list = new List<double>();
|
||
for (int i = 0; i < divide; i++) {
|
||
double degree = Convert.ToDouble(i) * 360.0 / Convert.ToDouble(divide);
|
||
double l = radius + margin;
|
||
|
||
double[] theta_l = new double[1 + 2 * point_num_theta];
|
||
|
||
theta_l[0] = degree * Math.PI / 180;
|
||
for (int t = 0; t < point_num_theta; t++) {
|
||
theta_l[t * 2 + 1] = (degree - t * (360 / (Convert.ToDouble(divide * (1 + 2 * point_num_theta))))) * Math.PI / 180;
|
||
theta_l[t * 2 + 2] = (degree + t * (360 / (Convert.ToDouble(divide * (1 + 2 * point_num_theta))))) * Math.PI / 180;
|
||
}
|
||
|
||
Point max_p = new Point(0, 0);
|
||
double outer = 0.0;
|
||
double inner = 0.0;
|
||
while (l > 0) {
|
||
outer = 0.0;
|
||
inner = 0.0;
|
||
double point_x = center.X + l * Math.Cos(theta_l[0]);
|
||
double point_y = center.Y + l * Math.Sin(theta_l[0]);
|
||
|
||
foreach (var theta in theta_l) {
|
||
for (int j = 1; j < point_num + 1; j++) {
|
||
int indexX = Convert.ToInt32(center.Y + (l + j) * Math.Sin(theta));
|
||
int indexY = Convert.ToInt32(center.X + (l + j) * Math.Cos(theta));
|
||
outer += Convert.ToDouble(grayImg.At<byte>(indexX, indexY));
|
||
indexX = Convert.ToInt32(center.Y + (l - j) * Math.Sin(theta));
|
||
indexY = Convert.ToInt32(center.X + (l - j) * Math.Cos(theta));
|
||
inner += Convert.ToDouble(grayImg.At<byte>(indexX, indexY));
|
||
}
|
||
}
|
||
|
||
if ((inner - outer) / ((1 + 2 * point_num_theta) * point_num) >= diff_Threshold) {
|
||
|
||
Point point = new Point(point_x, point_y);
|
||
edge_list.Add(point);
|
||
l_list.Add(l);
|
||
break;
|
||
}
|
||
l = l - 1;
|
||
|
||
if (l == radius - margin) {
|
||
l = radius + margin;
|
||
degree += (360.0 / Convert.ToDouble(divide * 10));
|
||
theta_l[0] = degree * Math.PI / 180;
|
||
for (int t = 0; t < point_num_theta; t++) {
|
||
theta_l[t * 2 + 1] = (degree - t * (360 / (Convert.ToDouble(divide * (1 + 2 * point_num_theta))))) * Math.PI / 180;
|
||
theta_l[t * 2 + 2] = (degree + t * (360 / (Convert.ToDouble(divide * (1 + 2 * point_num_theta))))) * Math.PI / 180;
|
||
}
|
||
if (degree >= Convert.ToDouble(i + 1) * 360.0 / Convert.ToDouble(divide))
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
double DetectPercent = (double)edge_list.Count / (double)divide * 100;
|
||
if (DetectPercent <= DetectPointPercent) {
|
||
Log.E(string.Format("Detected Points Percentage is {0}", DetectPercent));
|
||
|
||
return false;
|
||
|
||
}
|
||
Log.I(string.Format("Detection Points Percentage is {0}", DetectPercent));
|
||
|
||
|
||
int index = 0;
|
||
points_list = new Point[1][];
|
||
points_list[0] = new Point[edge_list.Count];
|
||
foreach (var item in edge_list)
|
||
points_list[0][index++] = item;
|
||
// Center.X, Y는 세점의 center고 list로 나온 Center를 detectedContourCenter 꺼내줘야한다..
|
||
Point2f contourCenter = CalculateCentroid(points_list[0]);
|
||
ConvertDrawingObjectAxis((long)contourCenter.X, (long)contourCenter.Y, srcImg.Width, srcImg.Height, out detectedContourCenter);
|
||
Log.I(string.Format($"Find Edge Result DetectedContourCenterX : {Math.Round(detectedContourCenter.X, 3)}, DetectedContourCenterY : {Math.Round(detectedContourCenter.Y, 3)}"));
|
||
return true;
|
||
}
|
||
|
||
public int CheckEdge(Point[][] input_points_list, int[] shrinkPixel, int height, int width, int centerX, int centerY, bool EnableAutoMissAlign, out Point[][] points_list)
|
||
{
|
||
// summary
|
||
// return 3 : eInterlockMaxMinDiffHigh 초과
|
||
// return 2 : eInterlockMaxMinDiffHigh 이하, eInterlockMaxMinDiffLow 초과
|
||
// return 1 : eInterlockMaxMinDiffLow 이하, eCheckCenterDiff 초과
|
||
// return 0 : eInterlockMaxMinDiffLow 이하, eCheckCenterDiff 이하
|
||
if (input_points_list[0].Length == 0) {
|
||
points_list = new Point[1][];
|
||
points_list[0] = new Point[0];
|
||
return 3;
|
||
}
|
||
bool is_OK = true;
|
||
bool is_Circle = true;
|
||
List<double> l_list = new List<double>();
|
||
List<int> grid_index = new List<int> { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };//10개 영역의 시작과 끝에 해당하는 index 저장
|
||
Point center = new Point((int)input_points_list[0].Average(p => p.X), (int)input_points_list[0].Average(p => p.Y));
|
||
#region grid 경계 index 계산
|
||
for (int i = 0; i < input_points_list[0].Length; i++) {
|
||
int point_x = input_points_list[0][i].X;
|
||
int point_y = input_points_list[0][i].Y;
|
||
l_list.Add(Math.Sqrt((center.X - input_points_list[0][i].X) * (center.X - input_points_list[0][i].X) + (center.Y - input_points_list[0][i].Y) * (center.Y - input_points_list[0][i].Y)));
|
||
if (i == 0) {
|
||
|
||
}
|
||
else if (point_y < height / 4 && input_points_list[0][i - 1].Y >= height / 4) {
|
||
//1번영역 start
|
||
grid_index[19] = i - 1;
|
||
grid_index[0] = i;
|
||
}
|
||
else if (point_x > width / 3 && input_points_list[0][i - 1].X <= width / 3) {
|
||
//2번영역 start
|
||
grid_index[1] = i - 1;
|
||
grid_index[2] = i;
|
||
}
|
||
else if (point_x > 2 * width / 3 && input_points_list[0][i - 1].X <= 2 * width / 3) {
|
||
//3번영역 start
|
||
grid_index[3] = i - 1;
|
||
grid_index[4] = i;
|
||
}
|
||
else if (point_y > height / 4 && input_points_list[0][i - 1].Y <= height / 4) {
|
||
//4번영역 start
|
||
grid_index[5] = i - 1;
|
||
grid_index[6] = i;
|
||
}
|
||
else if (point_y > 2 * height / 4 && input_points_list[0][i - 1].Y <= 2 * height / 4) {
|
||
//5번영역 start
|
||
grid_index[7] = i - 1;
|
||
grid_index[8] = i;
|
||
}
|
||
else if (point_y > 3 * height / 4 && input_points_list[0][i - 1].Y <= 3 * height / 4) {
|
||
//6번영역 start
|
||
grid_index[9] = i - 1;
|
||
grid_index[10] = i;
|
||
}
|
||
else if (point_x < 2 * width / 3 && input_points_list[0][i - 1].X >= 2 * width / 3) {
|
||
//7번영역 start
|
||
grid_index[11] = i - 1;
|
||
grid_index[12] = i;
|
||
}
|
||
else if (point_x < width / 3 && input_points_list[0][i - 1].X >= width / 3) {
|
||
//8번영역 start
|
||
grid_index[13] = i - 1;
|
||
grid_index[14] = i;
|
||
}
|
||
else if (point_y < 3 * height / 4 && input_points_list[0][i - 1].Y >= 3 * height / 4) {
|
||
//9번영역 start
|
||
grid_index[15] = i - 1;
|
||
grid_index[16] = i;
|
||
}
|
||
else if (point_y < 2 * height / 4 && input_points_list[0][i - 1].Y >= 2 * height / 4) {
|
||
//10번영역 start
|
||
grid_index[17] = i - 1;
|
||
grid_index[18] = i;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Miss-Align 보정
|
||
// Image Grid 영역 변경 시 하기 값은 수정 필요
|
||
List<double> grid_length = new List<double>() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //10개 영역의 거리(중심과의) 평균
|
||
double max_missalign = 0;
|
||
List<double> grid_miss_align = new List<double>(); //모서리 8포인트에 대해 miss-align값 확인
|
||
for (int i = 0; i < 10; i++) {
|
||
double miss_align = 0;
|
||
for (int j = 0; j < 10; j++) {
|
||
miss_align += l_list[(grid_index[(i * 2 + 1 + 20) % 20] + j + l_list.Count) % l_list.Count];
|
||
miss_align -= l_list[(grid_index[(i * 2 + 2 + 20) % 20] - j + l_list.Count) % l_list.Count];
|
||
}
|
||
if (Math.Abs(miss_align / 10) > max_missalign) max_missalign = Math.Abs(miss_align / 10);
|
||
grid_miss_align.Add(miss_align / 10);
|
||
}
|
||
Log.I($"Maximum Miss-Align: {max_missalign}");
|
||
|
||
Point[] align_offset = new Point[4]; //0:LeftTop, 1:RightTop, 2:RightBottom, 3:LeftBottom
|
||
if (EnableAutoMissAlign) {
|
||
if (Math.Abs(grid_miss_align[0]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[1]) <= eInterlockMissAlignLow) {
|
||
align_offset[0].Y = (int)grid_miss_align[0];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[9]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[8]) <= eInterlockMissAlignLow) {
|
||
align_offset[0].X = (int)grid_miss_align[9];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[1]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[0]) <= eInterlockMissAlignLow) {
|
||
align_offset[1].Y = (int)grid_miss_align[1];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[2]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[3]) <= eInterlockMissAlignLow) {
|
||
align_offset[1].X = (int)grid_miss_align[2];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[5]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[6]) <= eInterlockMissAlignLow) {
|
||
align_offset[2].Y = (int)grid_miss_align[5];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[4]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[3]) <= eInterlockMissAlignLow) {
|
||
align_offset[2].X = (int)grid_miss_align[4];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[6]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[5]) <= eInterlockMissAlignLow) {
|
||
align_offset[3].Y = (int)grid_miss_align[6];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
if (Math.Abs(grid_miss_align[7]) >= eInterlockMissAlignHigh && Math.Abs(grid_miss_align[8]) <= eInterlockMissAlignLow) {
|
||
align_offset[3].X = (int)grid_miss_align[7];
|
||
Log.I("Miss-Align Correction applied");
|
||
}
|
||
|
||
for (int i = grid_index[0]; i <= grid_index[1]; i++) {
|
||
input_points_list[0][i] = new Point(input_points_list[0][i].X + align_offset[0].X, input_points_list[0][i].Y - align_offset[0].Y);
|
||
}
|
||
for (int i = grid_index[4]; i <= grid_index[5]; i++) {
|
||
input_points_list[0][i] = new Point(input_points_list[0][i].X + align_offset[1].X, input_points_list[0][i].Y + align_offset[1].Y);
|
||
}
|
||
for (int i = grid_index[10]; i <= grid_index[11]; i++) {
|
||
input_points_list[0][i] = new Point(input_points_list[0][i].X - align_offset[2].X, input_points_list[0][i].Y + align_offset[2].Y);
|
||
}
|
||
for (int i = grid_index[14]; i <= grid_index[15]; i++) {
|
||
input_points_list[0][i] = new Point(input_points_list[0][i].X - align_offset[3].X, input_points_list[0][i].Y - align_offset[3].Y);
|
||
}
|
||
}
|
||
center = new Point((int)input_points_list[0].Average(p => p.X), (int)input_points_list[0].Average(p => p.Y));
|
||
l_list = new List<double>();
|
||
for (int i = 0; i < input_points_list[0].Length; i++) {
|
||
l_list.Add(Math.Sqrt((center.X - input_points_list[0][i].X) * (center.X - input_points_list[0][i].X) + (center.Y - input_points_list[0][i].Y) * (center.Y - input_points_list[0][i].Y)));
|
||
}
|
||
#endregion
|
||
|
||
#region 찌그러짐 Test
|
||
float window_size = (float)l_list.Count / 72; //5도 간격
|
||
List<double> diff_W_list = new List<double>();
|
||
for (int w = 0; w < 72; w++) {
|
||
double max_val = 0;
|
||
double min_val = double.MaxValue;
|
||
for (int i = (int)(w * window_size); i < (int)((w + 1) * window_size); i++) {
|
||
|
||
if (l_list[i] > max_val) max_val = l_list[i];
|
||
if (l_list[i] < min_val) min_val = l_list[i];
|
||
}
|
||
double diff_W = max_val - min_val;
|
||
diff_W_list.Add(diff_W);
|
||
if (max_val - min_val > eInterlockMaxMinDiffHigh) {
|
||
Log.E($"Invalid Shape in {(w / 6 + 3) % 12} O'Clock position.(local difference : {max_val - min_val})");
|
||
is_OK = false;
|
||
}
|
||
if (max_val - min_val > eInterlockMaxMinDiffLow && is_OK) {
|
||
is_Circle = false;
|
||
}
|
||
}
|
||
Log.I($"RCC MaxMin Diff Check. MaxMinDiff Max : {diff_W_list.Max().ToString("F2")}, MaxMinDiff Min : {diff_W_list.Min().ToString("F2")}");
|
||
#endregion
|
||
|
||
int ind = 0;
|
||
points_list = new Point[1][];
|
||
points_list[0] = new Point[input_points_list[0].Length];
|
||
foreach (var item in input_points_list[0])
|
||
points_list[0][ind++] = item;
|
||
|
||
Point[] mxCnt = points_list.OrderByDescending(c => Cv2.ContourArea(c)).First();
|
||
|
||
double centerDiff = Math.Sqrt((centerX - center.X) * (centerX - center.X) + (centerY - center.Y) + (centerY - center.Y));
|
||
Log.I($"Center Diff : {centerDiff.ToString("F2")}");
|
||
//Angle Test
|
||
if (is_manual)
|
||
points_list = ShrinkPixelContourQuad(mxCnt, shrinkPixel[0], shrinkPixel[1], shrinkPixel[2], shrinkPixel[3], ShrinkByFunction);
|
||
else
|
||
points_list = ShrinkPixelContourCSV(mxCnt, theta_list, offset_list);
|
||
if (!is_OK) {
|
||
Log.I("RCC Check Result : Interlock");
|
||
return 3;
|
||
}
|
||
if (!is_Circle) {
|
||
Log.I("RCC Check Result : Generate Object");
|
||
return 2;
|
||
}
|
||
if (centerDiff > eCheckCenterDiff) {
|
||
Log.I("RCC Check Result : Apply Offset Object");
|
||
return 1;
|
||
}
|
||
Log.I("RCC Check Result : Default Recipe");
|
||
return 0;
|
||
}
|
||
|
||
private Point find_circle_center_3Point(Point left, Point right, Point top)
|
||
{
|
||
Point center = new Point(0, 0);
|
||
|
||
Point mid_AB = (left + right) * 0.5;
|
||
Point mid_BC = (right + top) * 0.5;
|
||
|
||
double perp_slope_BC;
|
||
|
||
// 나누기 0일 경우
|
||
if (top.X - right.X == 0)
|
||
return center;
|
||
|
||
double slope_BC = (Convert.ToDouble(top.Y - right.Y) / Convert.ToDouble(top.X - right.X));
|
||
if (slope_BC != 0)
|
||
perp_slope_BC = -1 / slope_BC;
|
||
else
|
||
perp_slope_BC = mid_AB.Y;
|
||
double x_center = mid_AB.X;
|
||
double y_center = perp_slope_BC * x_center + (mid_BC.Y - perp_slope_BC * mid_BC.X);
|
||
|
||
center = new Point(x_center, y_center);
|
||
|
||
return center;
|
||
}
|
||
|
||
public (int, int, double, double, int, int, int, int, double) FindOffset(Mat imageColor, double threshold, double X, double Y, double T, out Point[][] out_contours, string SelectedOption)
|
||
{
|
||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||
if (X == 0 && Y == 0) {
|
||
Log.E("No Reference Information");
|
||
out_contours = new Point[1][];
|
||
out_contours[0] = new Point[0];
|
||
return (-1, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
}
|
||
if (SelectedOption == "Inzi") {
|
||
Mat grayImage = new Mat();
|
||
Cv2.CvtColor(imageColor, grayImage, ColorConversionCodes.BGR2GRAY);
|
||
|
||
int width = (int)(grayImage.Width / 4);
|
||
int height = (int)(grayImage.Height / 4);
|
||
Mat resizedImage = new Mat();
|
||
Cv2.Resize(grayImage, resizedImage, new Size(width, height), interpolation: InterpolationFlags.Linear);
|
||
Mat thres = new Mat();
|
||
Cv2.Threshold(resizedImage, thres, threshold, 200, ThresholdTypes.Binary);
|
||
|
||
Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(thres, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
Mat thres_img = new Mat();
|
||
Cv2.Threshold(resizedImage, thres_img, 1, 200, ThresholdTypes.Binary);
|
||
Point[][] contours_image;
|
||
HierarchyIndex[] hierarchy_;
|
||
Cv2.FindContours(thres_img, out contours_image, out hierarchy_, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
|
||
OpenCvSharp.Rect ImgRect = Cv2.BoundingRect(contours_image[0]);
|
||
Point topleft = ImgRect.TopLeft;
|
||
Point bottomright = ImgRect.BottomRight;
|
||
List<(double area, int index)> contourList = new List<(double, int)>();
|
||
for (int i = 0; i < contours.Length; i++) {
|
||
double area = Cv2.ContourArea(contours[i]);
|
||
if (area < 100) continue;
|
||
OpenCvSharp.Rect boundingRect = Cv2.BoundingRect(contours[i]);
|
||
if (boundingRect.TopLeft.X - topleft.X < 10 || boundingRect.TopLeft.Y - topleft.Y < 10 || bottomright.X - boundingRect.BottomRight.X < 10 || bottomright.Y - boundingRect.BottomRight.Y < 10) {
|
||
//가장자리 Case
|
||
continue;
|
||
}
|
||
contourList.Add((area, i));
|
||
}
|
||
if (contourList.Count < 2) {
|
||
out_contours = new Point[2][];
|
||
out_contours[0] = new Point[0];
|
||
out_contours[1] = new Point[0];
|
||
return (-1, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
}
|
||
contourList.Sort((x, y) => x.area.CompareTo(y.area));
|
||
var circle = contours[contourList[contourList.Count - 1].index];
|
||
var square = contours[contourList[contourList.Count - 2].index];
|
||
|
||
Moments circleMoments = Cv2.Moments(circle);
|
||
double cX = (circleMoments.M10 / circleMoments.M00) * 4;
|
||
double cY = (circleMoments.M01 / circleMoments.M00) * 4;
|
||
|
||
Moments squareMoments = Cv2.Moments(square);
|
||
double ScX = (squareMoments.M10 / squareMoments.M00) * 4;
|
||
double ScY = (squareMoments.M01 / squareMoments.M00) * 4;
|
||
|
||
double Otheta = ScX == cX ? 0 : Math.Atan((ScY - cY) / (ScX - cX));
|
||
if (Otheta < 0 && (ScX - cX) >= 0) {
|
||
Otheta = -Otheta;
|
||
}
|
||
else if (Otheta >= 0 && (ScX - cX) >= 0) {
|
||
Otheta = -Otheta;
|
||
}
|
||
else if (Otheta >= 0 && (ScX - cX) < 0) {
|
||
Otheta = Math.PI - Otheta;
|
||
}
|
||
else {
|
||
Otheta = Math.PI - Otheta;
|
||
}
|
||
|
||
|
||
for (int i = 0; i < square.Length; i++) {
|
||
square[i].X *= 4;
|
||
square[i].Y *= 4;
|
||
}
|
||
double dist = 0;
|
||
for (int i = 0; i < circle.Length; i++) {
|
||
circle[i].X *= 4;
|
||
circle[i].Y *= 4;
|
||
dist += Math.Sqrt((circle[i].X - cX) * (circle[i].X - cX) + (circle[i].Y - cY) * (circle[i].Y - cY));
|
||
}
|
||
dist = dist / circle.Length;
|
||
out_contours = new Point[2][];
|
||
out_contours[0] = circle;
|
||
out_contours[1] = square;
|
||
return ((int)cX - (int)X, (int)cY - (int)Y, Otheta, Otheta - T, (int)cX, (int)cY, (int)ScX, (int)ScY, dist);
|
||
}
|
||
else {
|
||
int point_num = 1000;
|
||
Point[] points = new Point[point_num];
|
||
int[] l_l = new int[point_num];
|
||
int[] diff_l = new int[point_num];
|
||
//TODO : Start, End(610) 파라미터화
|
||
for (int t_i = 0; t_i < point_num; t_i++) {
|
||
double t = (double)t_i / (double)point_num * 360.0;
|
||
int sum = 0;
|
||
int max_diff = -int.MaxValue;
|
||
int outer = 0;
|
||
int inner = 0;
|
||
int max_sum = 0;
|
||
int max_l = 0;
|
||
int start = 410;
|
||
int end = 610;
|
||
int line_width = 30;
|
||
if (X + end >= imageColor.Width || Y + end >= imageColor.Width || X - end <= 0 || Y - end <= 0) {
|
||
Log.E("Wrong Reference Point.");
|
||
out_contours = new Point[1][];
|
||
out_contours[0] = new Point[0];
|
||
return (-1, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
}
|
||
for (int l = start; l < end; l = l + 3) {
|
||
Point p = new Point(X + (int)(l * Math.Cos(t * Math.PI / 180)), Y + (int)(l * Math.Sin(t * Math.PI / 180)));
|
||
sum += imageColor.At<byte>(p.Y, p.X);
|
||
|
||
if (l < start + line_width) inner += imageColor.At<byte>(p.Y, p.X);
|
||
else if (l == start + line_width) outer += imageColor.At<byte>(p.Y, p.X);
|
||
else if (l < start + line_width * 2) {
|
||
outer += imageColor.At<byte>(p.Y, p.X);
|
||
inner += imageColor.At<byte>((int)Y + (int)((l - (l - start - line_width)) * Math.Sin(t * Math.PI / 180)), (int)X + (int)((l - (l - start - line_width)) * Math.Cos(t * Math.PI / 180)));
|
||
inner -= imageColor.At<byte>((int)Y + (int)((l - (l - start - line_width * 2)) * Math.Sin(t * Math.PI / 180)), (int)X + (int)((l - (l - start - line_width * 2)) * Math.Cos(t * Math.PI / 180)));
|
||
}
|
||
else {
|
||
outer += imageColor.At<byte>(p.Y, p.X);
|
||
outer -= imageColor.At<byte>((int)Y + (int)((l - line_width) * Math.Sin(t * Math.PI / 180)), (int)X + (int)((l - line_width) * Math.Cos(t * Math.PI / 180)));
|
||
inner += imageColor.At<byte>((int)Y + (int)((l - line_width) * Math.Sin(t * Math.PI / 180)), (int)X + (int)((l - line_width) * Math.Cos(t * Math.PI / 180)));
|
||
inner -= imageColor.At<byte>((int)Y + (int)((l - line_width * 2) * Math.Sin(t * Math.PI / 180)), (int)X + (int)((l - line_width * 2) * Math.Cos(t * Math.PI / 180)));
|
||
}
|
||
int diff = outer - inner;
|
||
if (diff > max_diff) {
|
||
max_diff = diff;
|
||
max_l = l - line_width;
|
||
points[t_i] = new Point(X + (int)((l - line_width) * Math.Cos(t * Math.PI / 180)), Y + (int)((l - line_width) * Math.Sin(t * Math.PI / 180)));
|
||
}
|
||
if (sum > max_sum) max_sum = sum;
|
||
}
|
||
l_l[t_i] = max_l;
|
||
diff_l[t_i] = max_diff;
|
||
}
|
||
|
||
double diff_l_avg = diff_l.Average();
|
||
out_contours = new Point[1][];
|
||
out_contours[0] = points;
|
||
double min_sum_ = double.MaxValue;
|
||
int min_t = 0;
|
||
int sum_distance = 0;
|
||
int sum_diff = 0;
|
||
int search_size = 30;
|
||
int x_cor_sum = 0; int x_cor_n = 0;
|
||
int y_cor_sum = 0; int y_cor_n = 0;
|
||
for (int t = 0; t < point_num / 3; t++) {
|
||
sum_distance += (l_l[t] + l_l[t + point_num / 3] + l_l[t + point_num * 2 / 3]);
|
||
sum_diff += (diff_l[t] + diff_l[t + point_num / 3] + diff_l[t + point_num * 2 / 3]);
|
||
if (t < search_size) continue;
|
||
sum_distance -= (l_l[t - search_size] + l_l[t + point_num / 3 - search_size] + l_l[t + point_num * 2 / 3 - search_size]);
|
||
sum_diff -= (diff_l[t - search_size] + diff_l[t + point_num / 3 - search_size] + diff_l[t + point_num * 2 / 3 - search_size]);
|
||
if ((float)sum_diff < diff_l_avg * (float)search_size * 3) continue;
|
||
if ((float)sum_diff > diff_l_avg * (float)search_size * 3) {
|
||
x_cor_sum += (points[t].X + points[t + point_num / 3].X + points[t + point_num * 2 / 3].X);
|
||
y_cor_sum += (points[t].Y + points[t + point_num / 3].Y + points[t + point_num * 2 / 3].Y);
|
||
x_cor_n += 3;
|
||
y_cor_n += 3;
|
||
}
|
||
|
||
if (sum_distance < min_sum_) {
|
||
min_sum_ = sum_distance;
|
||
min_t = t - search_size / 2;
|
||
}
|
||
|
||
}
|
||
if (x_cor_n == 0) {
|
||
out_contours = new Point[1][];
|
||
out_contours[0] = new Point[0];
|
||
return (-1, -1, -1, -1, -1, -1, -1, -1, -1);
|
||
}
|
||
double theta = (double)min_t / (double)point_num * 360.0;
|
||
Console.WriteLine($"Delta theta: {theta}");
|
||
//Log.I($"Elapsed time: {stopwatch.Elapsed.TotalSeconds}");
|
||
int new_X = (int)(x_cor_sum / x_cor_n);
|
||
int new_Y = (int)(y_cor_sum / y_cor_n);
|
||
return (new_X - (int)X, new_Y - (int)Y, theta, -theta + T * 180 / Math.PI, new_X, new_Y, (int)X + (int)(l_l[min_t] * Math.Cos(theta * Math.PI / 180)), (int)Y + (int)(l_l[min_t] * Math.Sin(theta * Math.PI / 180)), 0);
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|