2026-03-30 17:09:29 +09:00

2361 lines
123 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;
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 double eInterlockMaxMinDiff { get; set; }
public double eInterlockMaxMinDiff_local { get; set; }
public UnitPoint DetectedContourCenter;
public UnitPoint DetectedContourCenterPixel;
public float fDetectedContourRadius;
#endregion
private double[] theta_list = new double[36000];
private double[] offset_list = new double[36000];
private bool is_manual = true;
private int end_point_index = 0;
#region Original Path 의 정보를 가진 properties
public Arc m_OriginArc;
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;
eInterlockMaxMinDiff = 20;
eInterlockMaxMinDiff_local = 10;
}
public ObjectGenerator(Int64 nWidth, Int64 nHeight, double dPixelResolution, double dDrawingImageCenterOffsetX = 0, double dDrawingImageCenterOffsetY = 0)
{
polygon = new TPolygon();
patheditor = new PathEditor();
// Defualt setting. sw 의 setting parameter 에서 넣도록 하자.
dMergeDist = 10.0;
nMaxArcRadius = 1000;
nLineTolerance = 10;
nArcTolerance = 10;
fLineDefualtWidth = 0.01f;
nImageWidth = nWidth; nImageHeight = nHeight; // image 생성한 후에 넣을수 있으므로 바뀔수 있음
PixelResolution = dPixelResolution;
DrawingImageCenterOffsetX = 0; DrawingImageCenterOffsetY = 0;
dInterlockDistRange = 1.0;
dInterlockArcLength = 10.0;
eInterlockMaxMinDiff = 20;
eInterlockMaxMinDiff_local = 10;
}
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)
{
if (DistRange < 0 || dArcLength < 0) {
dInterlockDistRange = 1.0;
return;
}
dInterlockDistRange = DistRange;
dInterlockArcLength = dArcLength;
dInterlockLengthDiffRange = dPathLengthSumRange;
dInterlockCenterXYDiffRange = dCenterXYDiffRange;
}
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)
{
// 2point double list 변수를 TPoint 에 넣는다. 정수형으로 바꿔주므로 정보손실은 있다.
polygon.Clear();
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(int ObjectN)
{
MergePolygonSample(dMergeDist);
patheditor.GeneratePathWDegree(merged_polygon, ObjectN, end_point_index);
paths = patheditor.GetAutoPathItem();
}
public void CalculateAutoPath(double dMergeDist, uint nMaxArcRadius, uint nLineTolerance, uint nArcTolerance)
{
MergePolygonSample(dMergeDist);
ReArrangePoints();
patheditor.GenerateAutoPath(rearranged_polygon, nMaxArcRadius, nLineTolerance, nArcTolerance, (UInt32)DetectedContourCenterPixel.X, (UInt32)DetectedContourCenterPixel.Y);
paths = patheditor.GetAutoPathItem();
}
public void CalculateAutoPath(double dMergeDist, uint nMaxArcRadius, uint nLineTolerance, uint nArcTolerance, double dRampingStartLength, double dRampingEndLength)
{
MergePolygonSample(dMergeDist);
ReArrangePoints();
patheditor.GenerateAutoPath(rearranged_polygon, nMaxArcRadius, nLineTolerance, nArcTolerance, (UInt32)DetectedContourCenterPixel.X, (UInt32)DetectedContourCenterPixel.Y);
paths = patheditor.GetAutoPathItem();
}
private void SplitReArrangedPolygonbyRampingLength(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, out double pixelX, out double pixelY)
{
// ConvertDrawingObjectAxis 의 역함수 구하자.
pixelX = (DOPoint.X - DrawingImageCenterOffsetX) / PixelResolution + nImageWidth / 2;
pixelY = -((DOPoint.Y - DrawingImageCenterOffsetY) / PixelResolution - nImageHeight / 2);
}
public void ConvertDrawingObjectAxis(Int64 pixelX, Int64 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(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);
}
public void ConvertDrawingObjectAxis(float pixel, out float dDest)
{
// Pixel 로 된 좌표계를 Drawing Object 의 좌표계로 변환. 스캐너의 레졸루션까지 반영되지는 않았다.
// Pixel resolution x, y 가 다르면 Radius 계산이 매우 어려워진다. vector 방향에 따라서 resoultion 계산을 따로 해줘야하기 때문
// data 손실 주의..
dDest = pixel * (float)PixelResolution;
}
private void ConvertPathArcToDrawingArc(PathItem item, out Arc arc)
{
arc = new Arc();
UnitPoint dCenterPoint, dStartPoint, dEndPoint;
Int64 lx, ly;
arc.Direction = m_OriginArc.Direction;
item.GetCenter(out lx, out ly);
ConvertDrawingObjectAxis(lx, ly, out dCenterPoint);
arc.Center = dCenterPoint;
item.GetStart(out lx, out ly);
ConvertDrawingObjectAxis(lx, ly, out dStartPoint);
item.GetEnd(out lx, out ly);
ConvertDrawingObjectAxis(lx, ly, 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;
//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(float fLineDefualtWidth, out List<IDrawObject> destDrawObjects)
{
List<IDrawObject> ConvertedDrawObjects = new List<IDrawObject>();
int no = 0;
foreach (var item in paths) {
if (item.GetType() == PathItem._PathType.Line) {
UnitPoint dStartPoint, dEndPoint;
Int64 lx, ly;
item.GetStart(out lx, out ly);
ConvertDrawingObjectAxis(lx, ly, out dStartPoint);
item.GetEnd(out lx, out ly);
ConvertDrawingObjectAxis(lx, ly, 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, 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(ConvertedDrawObjects, out destDrawObjects);
}
public void SplitDrawingObjectRampingInterval(List<IDrawObject> srcDrawObjects, out List<IDrawObject> destDrawObjects)
{
//Ramping 사용 시 End만 사용할 때 시작 객체가 사라지는 문제 발생
if (bArcHasRamping == false) {
destDrawObjects = srcDrawObjects;
return;
}
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)
{
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) - m_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) - m_OriginArc.Radius} mm");
bRet = false;
}
if (!CheckInRange(GetDistance(line.P2.X - DetectedContourCenter.X, line.P2.Y - DetectedContourCenter.Y) - m_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) - m_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) - m_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) - m_OriginArc.Radius} mm");
bRet = false;
}
if (!CheckInRange(GetDistance(arc.PF2.X - DetectedContourCenter.X, arc.PF2.Y - DetectedContourCenter.Y) - m_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) - m_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 - m_OriginArc.Length, dInterlockLengthDiffRange)) {
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, TotalPathLength - OriginArcLength : {dPathLengthSum - m_OriginArc.Length} mm");
bRet = false;
}
if (!CheckInRange(m_OriginArc.Center.X - DetectedContourCenter.X, dInterlockCenterXYDiffRange)) {
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, OriginArcCenterX - DetectEdgeCenterX : {(m_OriginArc.Center.X - DetectedContourCenter.X).ToString("F2")} mm");
bRet = false;
}
if (!CheckInRange(m_OriginArc.Center.Y - DetectedContourCenter.Y, dInterlockCenterXYDiffRange)) {
Log.E($"[RCCCheckInterlock] Distance interlock Range Over, OriginArcCenterY - DetectEdgeCenterY : {(m_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, "_device", "_drawobject", "DrawObject");
string fileName = fullPath + string.Format("\\{0}_{1}.csv", strMode, DateTime.Now.ToString("yyyyMMdd_HHmmss.fff"));
ExcelManagementUnit.ExportToCSV(fileName, dataTable,",",false);
return true;
}
public void MergePolygonSample(double dMergeDist)
{
//Pixel의 시작위치를 Merge전에 첫 Pixel은 가지고있어야 시작점 위치가 변경되지 않을듯 하다
// Arc 정보가없으면 그냥 default 생성된 rearranged_polygon list 를 사용
if (m_OriginArc == null)
return;
// 본래 고객이 그린 Arc 방향에 따라서 point array 방향을 돌려준다. rearrange 한 후에 돌려주는게 계산량이 적을듯.
// Find Edge 로 찾은 방향은 FindContour 방향과 반대로 되어 있어서 수정함.
//if (m_OriginArc.Direction == Arc.eDirection.CW)
if (m_OriginArc.Direction == Arc.eDirection.CCW)
polygon.Reverse();
// 시작위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
double startX, startY;
CalAnglePoint(DetectedContourCenter.X, DetectedContourCenter.Y, m_OriginArc.Radius, m_OriginArc.StartAngle, out startX, out startY);
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(startX, startY), 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 (m_OriginArc.AbsSweepAngle > 360) {
// 종료위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
double endX, endY;
CalAnglePoint(DetectedContourCenter.X, DetectedContourCenter.Y, m_OriginArc.Radius, m_OriginArc.EndAngle, out endX, out endY);
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(endX, endY), 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);
end_point_index = polygon.Count;
// 처음부터 minIndex개의 아이템을 복사하기
TPolygon addpolygons = polygon.GetRange(0, minIndexFromEndPoint + 1);
// 복사한 minIndex개의 아이템을 뒤에 붙임
polygon.AddRange(addpolygons);
} else if (m_OriginArc.AbsSweepAngle < 360) {
// sweep angle 이 360보다 작은 경우 end 까지만 사용하고 나머지는 버린다.
// 종료위치를 찾아서 순서 보정. 좌표계 pixel 좌표계 주의.
double endX, endY;
CalAnglePoint(DetectedContourCenter.X, DetectedContourCenter.Y, m_OriginArc.Radius, m_OriginArc.EndAngle, out endX, out endY);
ConvertPixelAxisFromDrawingObjectAxis(new UnitPoint(endX, endY), 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()
{
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);
//int stride = width; // Assuming 8-bit grayscale image
int stride = width * 3; // Assuming 24-bit BRG image
byte[] imageData = new byte[width * height * 3];
//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();
// MIL 해제
MIL.MbufFree(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);
int cnt = 0;
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);
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[cnt]) / 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 Point[][] FindMaxContour(Mat oriImg, double threshold, int shrinkPixel)
{
// Convert to grayscale
Mat grayImg = new Mat();
Cv2.CvtColor(oriImg, grayImg, ColorConversionCodes.BGR2GRAY);
// Threshold
Mat thresImg = new Mat();
Cv2.Threshold(grayImg, thresImg, threshold, 200, ThresholdTypes.Binary);
// Find contours (1st)
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(thresImg, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
// Find max contour
Point[] maxCnt = contours.OrderByDescending(c => Cv2.ContourArea(c)).First();
// Shrink max contour
Point[][] shrunkCnt = ShrinkPixelContour(maxCnt, shrinkPixel);
return shrunkCnt;
}
public Point[][] ReSizeFindMaxContour(Mat oriImg, double scale, double threshold, int shrinkPixel)
{
// Convert to grayscale
Mat grayImg = new Mat();
Cv2.CvtColor(oriImg, grayImg, ColorConversionCodes.BGR2GRAY);
// Image Resize
// Scale 다운 후 아래서 Contour에 scale을 다시 곱해줌
double imgScale = 1.0 / scale;
Mat reGrayImg = new Mat();
Cv2.Resize(grayImg, reGrayImg, new Size(0, 0), imgScale, imgScale, InterpolationFlags.Linear);
//// Apply Sobel filter
//Mat sobelx = new Mat();
//Mat sobely = new Mat();
//Cv2.Sobel(reGrayImg, sobelx, MatType.CV_64F, 1, 0, 3);
//Cv2.Sobel(reGrayImg, 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);
//Gaussian blur
Mat blurredImg = new Mat();
//Cv2.MedianBlur(reGrayImg, blurredImg, 5); // 5 is the kernel size, must be odd
Cv2.GaussianBlur(reGrayImg, blurredImg, new Size(9, 9), 0);
string filepath = Path.Combine(ConfigurationManager.AppSettings.Get("DATA_PATH"), "_vision", "Test",
string.Format($"{DateTime.Now.Year}_{DateTime.Now.Month}_{DateTime.Now.Day}/{DateTime.Now.ToString("HHmmss")}_BlurImg.PNG"));
Cv2.ImWrite(filepath, blurredImg);
//AddaptiveThresImg
//Mat adaptiveThresImg = new Mat();
//Cv2.AdaptiveThreshold(blurredImg, adaptiveThresImg, 255,
// AdaptiveThresholdTypes.GaussianC,
// ThresholdTypes.Binary, blockSize, constant);
// Apply Bilateral Filter to preserve edges while reducing noise
Mat bilateralFilteredImg = new Mat();
Cv2.BilateralFilter(blurredImg, bilateralFilteredImg, 9, 75, 75);
// Threshold
Mat thresImg = new Mat();
Cv2.Threshold(bilateralFilteredImg, thresImg, threshold, 200, ThresholdTypes.Binary);
filepath = Path.Combine(ConfigurationManager.AppSettings.Get("DATA_PATH"), "_vision", "Test",
string.Format($"{DateTime.Now.Year}_{DateTime.Now.Month}_{DateTime.Now.Day}/{DateTime.Now.ToString("HHmmss")}_ThresImg.PNG"));
Cv2.ImWrite(filepath, thresImg);
// Apply morphological operations
//Mat morpImg = new Mat();
//Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(morpMaskSize, morpMaskSize));
//Cv2.MorphologyEx(thresImg, morpImg, MorphTypes.Erode, kernel);
//filepath = Path.Combine(ConfigurationManager.AppSettings.Get("DATA_PATH"), "_vision", "Test",
// string.Format($"{DateTime.Now.Year}_{DateTime.Now.Month}_{DateTime.Now.Day}/{DateTime.Now.ToString("HHmmss")}_MorpImg.PNG"));
//Cv2.ImWrite(filepath, morpImg);
// Find contours (1st)
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(thresImg, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
// Find max contour
Point[] maxCnt = contours.OrderByDescending(c => Cv2.ContourArea(c)).First();
// Shrink max contour
for (int i = 0; i < maxCnt.Length; i++) {
maxCnt[i].X = (int)(maxCnt[i].X * scale);
maxCnt[i].Y = (int)(maxCnt[i].Y * scale);
}
Point[][] shrunkCnt = ShrinkPixelContour(maxCnt, shrinkPixel);
Point2f center;
float radius;
Point[] p = shrunkCnt[0];
Cv2.MinEnclosingCircle(p, out center, out radius);
ConvertDrawingObjectAxis(center.X, center.Y, out DetectedContourCenter);
ConvertDrawingObjectAxis(radius, out fDetectedContourRadius);
return shrunkCnt;
}
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(List<IDrawObject> sourcelist, List<IDrawObject> targetlist)
{
// sourcelist 의 item 중 RCC mode 가 true 인 것을 targetlist 로 대체.
// targetlist 는 group 으로 묶어서 1개 object 로 인식하게 한다.
// RCC Generated 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 한다.
// iteration 은 읽기 전용이므로 foreach 말고 for 문을 써야한다.
for (int i = 0; i < sourcelist.Count; i++) {
IDrawObject obj = sourcelist[i] as IDrawObject;
if (CheckReplaceRCCObject(group, obj) == true) {
// 부모를 넘겨줘야 item 이 replace 된다
ReplaceArcToRCCObjectGroup(group, sourcelist, i);
} else {
UnitPoint offset = DetectedContourCenter - m_OriginArc.Center;
IDrawObject objclone = obj.Clone();
objclone.Move(offset);
sourcelist[i] = objclone;
}
}
return 0;
}
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 void SetBaseArc(Arc arc)
{
// 이 함수는 한번만 호출해도 계속 사용할 수 있도록 하자. (참조로 덮어써지지 않도록 주의)
// Generate Object 의 기본 정보를 제공하는 Arc Object 를 등록한다
m_OriginArc = arc;
bArcHasRamping = false;
#region ramping 설정 추출. ramping 이 있을 경우에는 polygon sample 을 slpit 해야한다. 이 정보를 여기서 추출하고 generate 할때 사용한다.
// Arc 받아온 것에서 Ramping 정보 및 출력 정보 추출
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[m_OriginArc.ScannerParamNo];
// LoadArc Code 참고함. scanner 에 넣어주는 부분만 list 로 저장해두고 ramping 구간 object 생성될때 power 를 적용한다.
double radius = Math.Sqrt((m_OriginArc.CenterPointF.X - m_OriginArc.PF1.X) * (m_OriginArc.CenterPointF.X - m_OriginArc.PF1.X) + (m_OriginArc.CenterPointF.Y - m_OriginArc.PF1.Y) * (m_OriginArc.CenterPointF.Y - m_OriginArc.PF1.Y));
double length = param.ArcRampingInterval; // 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 (m_OriginArc.Direction == Arc.eDirection.CCW) theta = (length / radius) * (180 / Math.PI);
if (m_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(m_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.RampingStartPower / 100 <= 100 ?
(float)param.RampingStartPower / 100 : 100;
m_RampingStartInfo.PowerRatioCh2 = m_RampingStartInfo.PowerRatioCh1; // parameter 구분이 없어서 덮어써준다. 나중에 확장하면 여기만 추가해주자.
m_RampingStartInfo.Intervallength = length;
m_RampingEndInfo.PowerRatioCh1 = (float)param.RampingEndPower / 100 <= 100 ?
(float)param.RampingEndPower / 100 : 100;
m_RampingEndInfo.PowerRatioCh2 = m_RampingEndInfo.PowerRatioCh1; // parameter 구분이 없어서 덮어써준다. 나중에 확장하면 여기만 추가해주자.
m_RampingEndInfo.Intervallength = length;
// interlock 처리
// start ramping length + end ramping length 가 전체 길이보다 길면 안된다.
if (m_OriginArc.Length < param.RampingStartDist + param.RampingEndDist)
bArcHasRamping = false; // ramping 을 쓰지 않는다.
}
#endregion
}
public void FindCenterPoint(Mat srcImg, double diff_Threshold, out Point center_point)
{
// divide 는 각도를 쪼개는 갯수
Mat grayImg = new Mat();
Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
int height = grayImg.Height;
int width = grayImg.Width;
// Right
int p_x = width - 3;
int p_y = height / 2;
Point right = new Point(0, 0);
while (p_x > (width / 2)) {
p_x--;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y, p_x + 1) + grayImg.At<byte>(p_y, p_x + 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x + 1) + grayImg.At<byte>(p_y - 1, p_x + 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x + 1) + grayImg.At<byte>(p_y + 1, p_x + 2));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y, p_x - 2) + grayImg.At<byte>(p_y, p_x - 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x - 2) + grayImg.At<byte>(p_y - 1, p_x - 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x - 2) + grayImg.At<byte>(p_y + 1, p_x - 1));
if ((inner - outer) / 6 > diff_Threshold) {
right = new Point(p_x, p_y);
break;
}
}
// Left
p_x = 2;
p_y = height / 2;
Point left = new Point(0, 0);
while (p_x < (width / 2)) {
p_x++;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y, p_x - 1) + grayImg.At<byte>(p_y, p_x - 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x - 1) + grayImg.At<byte>(p_y + 1, p_x - 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x - 1) + grayImg.At<byte>(p_y - 1, p_x - 2));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y, p_x + 2) + grayImg.At<byte>(p_y, p_x + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x + 2) + grayImg.At<byte>(p_y + 1, p_x + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x + 2) + grayImg.At<byte>(p_y - 1, p_x + 1));
if ((inner - outer) / 6 > diff_Threshold) {
left = new Point(p_x, p_y);
break;
}
}
// Top
p_x = width / 2;
p_y = 2;
Point top = new Point(0, 0);
while (p_y < (height / 2)) {
p_y++;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x) + grayImg.At<byte>(p_y - 2, p_x));
outer += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x + 1) + grayImg.At<byte>(p_y - 2, p_x + 1));
outer += Convert.ToDouble(grayImg.At<byte>(p_y - 1, p_x - 1) + grayImg.At<byte>(p_y - 2, p_x - 1));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x) + grayImg.At<byte>(p_y + 2, p_x));
inner += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x + 1) + grayImg.At<byte>(p_y + 2, p_x + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y + 1, p_x - 1) + grayImg.At<byte>(p_y + 2, p_x - 1));
if ((inner - outer) / 6 > diff_Threshold) {
top = new Point(p_x, p_y);
break;
}
}
center_point = find_circle_center_3Point(left, right, top);
}
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[36000];
offset_list = new double[36000];
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, int shrinkPixel1, int shrinkPixel2, int shrinkPixel3, int shrinkPixel4, double DetectPointPercent, out Point[][] points_list)
{
if (radius==0 || margin==0 || divide==0 || diff_Threshold==0) {
points_list = new Point[1][];
points_list[0] = new Point[0];
Log.E(string.Format("Check the Input Parameters"));
return false;
}
Mat grayImg = new Mat();
Cv2.CvtColor(srcImg, grayImg, ColorConversionCodes.BGR2GRAY);
int height = grayImg.Height;
int width = grayImg.Width;
Point center = new Point(0,0);
int p_x1 = width - 10;
int p_y1 = height / 2;
Point P1 = new Point(0, 0);
int p_x2 = 10;
int p_y2 = height / 2;
Point P2 = new Point(0, 0);
int p_x3 = width / 2;
int p_y3 = 10;
Point P3 = new Point(0, 0);
for (int i = 0; i <= 5; i++) {
while (p_x1 > (width / 2)) {
p_x1--;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 + 1) + grayImg.At<byte>(p_y1, p_x1 + 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y1 - 1, p_x1 + 1) + grayImg.At<byte>(p_y1 - 1, p_x1 + 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y1 + 1, p_x1 + 1) + grayImg.At<byte>(p_y1 + 1, p_x1 + 2));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y1, p_x1 - 2) + grayImg.At<byte>(p_y1, p_x1 - 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y1 - 1, p_x1 - 2) + grayImg.At<byte>(p_y1 - 1, p_x1 - 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y1 + 1, p_x1 - 2) + grayImg.At<byte>(p_y1 + 1, p_x1 - 1));
if ((inner - outer) / 6 > diff_Threshold) {
P1 = new Point(p_x1, p_y1);
break;
}
}
while (p_x2 < (width / 2)) {
p_x2++;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 - 1) + grayImg.At<byte>(p_y2, p_x2 - 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y2 + 1, p_x2 - 1) + grayImg.At<byte>(p_y2 + 1, p_x2 - 2));
outer += Convert.ToDouble(grayImg.At<byte>(p_y2 - 1, p_x2 - 1) + grayImg.At<byte>(p_y2 - 1, p_x2 - 2));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y2, p_x2 + 2) + grayImg.At<byte>(p_y2, p_x2 + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y2 + 1, p_x2 + 2) + grayImg.At<byte>(p_y2 + 1, p_x2 + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y2 - 1, p_x2 + 2) + grayImg.At<byte>(p_y2 - 1, p_x2 + 1));
if ((inner - outer) / 6 > diff_Threshold) {
P2 = new Point(p_x2, p_y2);
break;
}
}
while (p_y3 < (height / 2)) {
p_y3++;
double outer = Convert.ToDouble(grayImg.At<byte>(p_y3 - 1, p_x3) + grayImg.At<byte>(p_y3 - 2, p_x3));
outer += Convert.ToDouble(grayImg.At<byte>(p_y3 - 1, p_x3 + 1) + grayImg.At<byte>(p_y3 - 2, p_x3 + 1));
outer += Convert.ToDouble(grayImg.At<byte>(p_y3 - 1, p_x3 - 1) + grayImg.At<byte>(p_y3 - 2, p_x3 - 1));
double inner = Convert.ToDouble(grayImg.At<byte>(p_y3 + 1, p_x3) + grayImg.At<byte>(p_y3 + 2, p_x3));
inner += Convert.ToDouble(grayImg.At<byte>(p_y3 + 1, p_x3 + 1) + grayImg.At<byte>(p_y3 + 2, p_x3 + 1));
inner += Convert.ToDouble(grayImg.At<byte>(p_y3 + 1, p_x3 - 1) + grayImg.At<byte>(p_y3 + 2, p_x3 - 1));
if ((inner - outer) / 6 > diff_Threshold) {
P3 = new Point(p_x3, p_y3);
break;
}
}
// 여기까지하면 theshold 차이가 나는 점 3개를 찾은것임.
// 점 3개로 원의 중심을 찾고, 원 중심에서부터 각도방향으로 threshod 차이나는 엣지를 찾는다.
center = find_circle_center_3Point(P1, P2, P3);
if (Math.Abs(center.X-centerX) < margin*1.5 && Math.Abs(center.Y - centerY) < margin*1.5) {
break;
}
if (i == 0) {
p_x1 = width - 10;
p_y1 = 2*height / 3;
p_x2 = 10;
p_y2 = 2 * height / 3;
p_x3 = 2*width / 3;
p_y3 = 10;
} else if(i == 1){
p_x1 = width - 10;
p_y1 = height / 3;
p_x2 = 10;
p_y2 = height / 3;
p_x3 = width / 3;
p_y3 = 10;
} else if (i == 3) {
p_x1 = width - 10;
p_y1 = 2*height / 5;
p_x2 = 10;
p_y2 = 2*height / 5;
p_x3 = 2*width / 5;
p_y3 = 10;
} else if (i == 4) {
p_x1 = width - 10;
p_y1 = 3 * height / 5;
p_x2 = 10;
p_y2 = 3 * height / 5;
p_x3 = 3 * width / 5;
p_y3 = 10;
} else {
points_list = new Point[1][];
points_list[0] = new Point[0];
Log.E(string.Format("Failed to find a center"));
return false;
}
}
// Center Point가 radius + margin 영역 밖인 경우 예외 처리
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[3];
theta_l[0] = degree * Math.PI / 180;
theta_l[1] = (degree - (360 / (Convert.ToDouble(divide * 3)))) * Math.PI / 180;
theta_l[2] = (degree + (360 / (Convert.ToDouble(divide * 3)))) * Math.PI / 180;
while (l > 0) {
double point_x = center.X + l * Math.Cos(theta_l[0]);
double point_y = center.Y + l * Math.Sin(theta_l[0]);
double outer = 0.0;
foreach (var theta in theta_l) {
int indexX1 = Convert.ToInt32(center.Y + (l + 1) * Math.Sin(theta));
int indexX2 = Convert.ToInt32(center.Y + (l + 2) * Math.Sin(theta));
int indexY1 = Convert.ToInt32(center.X + (l + 1) * Math.Cos(theta));
int indexY2 = Convert.ToInt32(center.X + (l + 2) * Math.Cos(theta));
outer += Convert.ToDouble(grayImg.At<byte>(indexX1, indexY1)) +
Convert.ToDouble(grayImg.At<byte>(indexX2, indexY2));
}
double inner = 0.0;
foreach (var theta in theta_l) {
int indexX1 = Convert.ToInt32(center.Y + (l - 1) * Math.Sin(theta));
int indexX2 = Convert.ToInt32(center.Y + (l - 2) * Math.Sin(theta));
int indexY1 = Convert.ToInt32(center.X + (l - 1) * Math.Cos(theta));
int indexY2 = Convert.ToInt32(center.X + (l - 2) * Math.Cos(theta));
//if (indexX1 < 0) indexX1 += grayImg.Width;
//if (indexX2 < 0) indexX2 += grayImg.Width;
//if (indexY1 < 0) indexY1 += grayImg.Height;
//if (indexY2 < 0) indexY2 += grayImg.Height;
inner += Convert.ToDouble(grayImg.At<byte>(indexX1, indexY1)) +
Convert.ToDouble(grayImg.At<byte>(indexX2, indexY2));
}
if ((inner - outer) / 6 > 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;
theta_l[1] = (degree - (360 / (divide * 3))) * Math.PI / 180;
theta_l[2] = (degree + (360 / (divide * 3))) * 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) {
points_list = new Point[1][];
points_list[0] = new Point[0];
Log.E(string.Format("Detected Points Percentage is {0}", DetectPercent));
return false;
}
Log.I(string.Format("Detection Points Percentage is {0}", DetectPercent));
//찌그러짐 interlock
//1) 전체 Max-Min 값
//2) 부분(3도 단위) Max-Min 값
Stopwatch stopwatch = Stopwatch.StartNew();
double max_diff = l_list.Max()-l_list.Min();
if (max_diff > eInterlockMaxMinDiff) {
Log.E($"Invalid Shape (max difference : {max_diff})");
int ind = 0;
points_list = new Point[1][];
points_list[0] = new Point[edge_list.Count];
foreach (var item in edge_list)
points_list[0][ind++] = item;
// Find max contour
Point[] mxCnt = points_list.OrderByDescending(c => Cv2.ContourArea(c)).First();
// Shrink max contour
//points_list = ShrinkPixelContour(maxCnt, shrinkPixel);
//Angle Test
if (is_manual)
points_list = ShrinkPixelContourQuad(mxCnt, shrinkPixel1, shrinkPixel2, shrinkPixel3, shrinkPixel4, ShrinkByFunction);
else
points_list = ShrinkPixelContourCSV(mxCnt, theta_list, offset_list);
return false;
}
int window_size = l_list.Count / 12;
for (int i=0; i<l_list.Count-window_size; i++) {
double max_val = 0;
double min_val = double.MaxValue;
for (int j=0; j < window_size; j++) {
if (l_list[i + j] > max_val) {
max_val = l_list[i + j];
}
if (l_list[i + j] < min_val) {
min_val = l_list[i + j];
}
}
if (max_val - min_val > eInterlockMaxMinDiff_local) {
Log.E($"Invalid Shape.(max difference : {max_diff})");
int ind = 0;
points_list = new Point[1][];
points_list[0] = new Point[edge_list.Count];
foreach (var item in edge_list)
points_list[0][ind++] = item;
// Find max contour
Point[] mxCnt = points_list.OrderByDescending(c => Cv2.ContourArea(c)).First();
// Shrink max contour
//points_list = ShrinkPixelContour(maxCnt, shrinkPixel);
//Angle Test
if (is_manual)
points_list = ShrinkPixelContourQuad(mxCnt, shrinkPixel1, shrinkPixel2, shrinkPixel3, shrinkPixel4, ShrinkByFunction);
else
points_list = ShrinkPixelContourCSV(mxCnt, theta_list, offset_list);
return false;
}
}
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;
Point[][] contours;
HierarchyIndex[] hierarchy;
// Find max contour
Point[] maxCnt = points_list.OrderByDescending(c => Cv2.ContourArea(c)).First();
// Shrink max contour
//points_list = ShrinkPixelContour(maxCnt, shrinkPixel);
//Angle Test
if (is_manual)
points_list = ShrinkPixelContourQuad(maxCnt, shrinkPixel1, shrinkPixel2, shrinkPixel3, shrinkPixel4, ShrinkByFunction);
else
points_list = ShrinkPixelContourCSV(maxCnt, theta_list, offset_list);
//Point2f center;
//float radius;
//Point[] p = shrunkCnt[0];
//.MinEnclosingCircle(p, out center, out radius);
DetectedContourCenterPixel.X = center.X;
DetectedContourCenterPixel.Y = center.Y;
ConvertDrawingObjectAxis(center.X, center.Y, out DetectedContourCenter);
ConvertDrawingObjectAxis((float)radius, out fDetectedContourRadius);
return true;
}
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, int X, int 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 - X, (int)cY - 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>(Y + (int)((l - (l - start- line_width)) * Math.Sin(t * Math.PI / 180)), X + (int)((l - (l- start- line_width)) * Math.Cos(t * Math.PI / 180)));
inner -= imageColor.At<byte>(Y + (int)((l - (l - start- line_width*2)) * Math.Sin(t * Math.PI / 180)), 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>(Y + (int)((l - line_width) * Math.Sin(t * Math.PI / 180)), X + (int)((l - line_width) * Math.Cos(t * Math.PI / 180)));
inner += imageColor.At<byte>(Y + (int)((l - line_width) * Math.Sin(t * Math.PI / 180)), X + (int)((l - line_width) * Math.Cos(t * Math.PI / 180)));
inner -= imageColor.At<byte>(Y + (int)((l - line_width*2) * Math.Sin(t * Math.PI / 180)), 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 - X, new_Y - Y, theta, -theta + T * 180 / Math.PI, new_X, new_Y, X + (int)(l_l[min_t] * Math.Cos(theta * Math.PI / 180)), Y + (int)(l_l[min_t] * Math.Sin(theta * Math.PI / 180)), 0);
}
}
}
}