before candybox editor update
This commit is contained in:
@@ -3,7 +3,7 @@ using System.Drawing;
|
||||
using System.Threading.Channels;
|
||||
using Ninject;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.UI.Sources.Emulation
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using CameraSettings = Inspectron.HawkEye.Protocol.CameraSettings;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
@@ -22,6 +22,12 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public CameraSettings Settings
|
||||
{
|
||||
get => _cameraSettings;
|
||||
set => _cameraSettings = value;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using uEye.Defines;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using Point = System.Drawing.Point;
|
||||
|
||||
namespace Inspectron.Camera.UEye
|
||||
|
||||
@@ -43,7 +43,6 @@ public class OnnxModelOperation:BaseOperation
|
||||
}
|
||||
|
||||
var opts = new SessionOptions();
|
||||
//opts.AppendExecutionProvider_DML(0);
|
||||
_AIsession = new InferenceSession(ModelFilePath?.Path, opts);
|
||||
_inputName = _AIsession.InputMetadata.First().Key;
|
||||
_dimensions = _AIsession.InputMetadata.First().Value.Dimensions;
|
||||
|
||||
@@ -81,17 +81,6 @@ public class RawModelAIOperation: BaseOperation
|
||||
outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var currentImage = context.ActiveImage;
|
||||
|
||||
var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Drawing;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Text.Json;
|
||||
using System.Xml;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Workflow;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Extensions;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Features.ImagesStrip
|
||||
|
||||
@@ -11,18 +11,81 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" >
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.Gpu" Version="1.22.1">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.6.0.20220608">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<ProjectReference Include="..\..\framework\Inspectron.HawkEye\Inspectron.HawkEye.csproj">
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="..\..\framework\Inspectron.Settings\Inspectron.Settings.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="..\..\framework\Inspectron.Statistics\Inspectron.Statistics.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\framework\MaterialSkin.Core\MaterialSkin.Core.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Hawkeye.VisionBuilder.UI.Sources.Hawkeye\Hawkeye.VisionBuilder.UI.Sources.Hawkeye.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj">
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\VisionBuilder.UI.Statistics\VisionBuilder.UI.Statistics.csproj" >
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\VisionBuilder.UI.Windows\VisionBuilder.UI.Windows.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="models\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="models\blisterresnet.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="models\model_transparent_blister_resnet.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="models\model_transparent_blister_resnet_navy.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="models\model_transparent_candy_resnet.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace CandyboxPlugin
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
107
Plugins/CandyboxPlugin/Detectors/BlisterDetector.cs
Normal file
107
Plugins/CandyboxPlugin/Detectors/BlisterDetector.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.ML.OnnxRuntime;
|
||||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace CandyboxPlugin.Detectors
|
||||
{
|
||||
public class BlisterDetector
|
||||
{
|
||||
private readonly InferenceSession _AIsession;
|
||||
private readonly string _inputName;
|
||||
private readonly int[] _dimensions;
|
||||
static bool _initialized = false;
|
||||
private const string MODEL_NAME = "model_transparent_blister_resnet_navy.onnx";
|
||||
public BlisterDetector()
|
||||
{
|
||||
|
||||
var opts = new SessionOptions();
|
||||
// get this dll path
|
||||
var path=Assembly.GetCallingAssembly().Location;
|
||||
var dir=System.IO.Path.GetDirectoryName(path);
|
||||
var modelPath = Path.Combine(dir,"models\\" + MODEL_NAME);
|
||||
_AIsession = new InferenceSession(modelPath, opts);
|
||||
_inputName = _AIsession.InputMetadata.First().Key;
|
||||
_dimensions = _AIsession.InputMetadata.First().Value.Dimensions;
|
||||
// inputs
|
||||
foreach (var input in _AIsession.InputMetadata)
|
||||
{
|
||||
Console.WriteLine($"Name: {input.Key}");
|
||||
Console.WriteLine($" Type: {input.Value.ElementType}");
|
||||
Console.WriteLine($" Shape: [{string.Join(", ", input.Value.Dimensions)}]");
|
||||
}
|
||||
|
||||
// outputs
|
||||
foreach (var output in _AIsession.OutputMetadata)
|
||||
{
|
||||
Console.WriteLine($"Name: {output.Key}");
|
||||
Console.WriteLine($" Type: {output.Value.ElementType}");
|
||||
Console.WriteLine($" Shape: [{string.Join(", ", output.Value.Dimensions)}]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Mat Eval(Mat bmpTest)
|
||||
{
|
||||
var currentImage = bmpTest;
|
||||
|
||||
var rightColor = currentImage.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||
|
||||
|
||||
|
||||
// convert to float32
|
||||
var floatImage = new Mat();
|
||||
rightColor.ConvertTo(floatImage, MatType.CV_32FC3, 1.0 / 255);
|
||||
|
||||
// to array
|
||||
|
||||
var width = _dimensions[1];
|
||||
var height = _dimensions[2];
|
||||
|
||||
// check if need resize
|
||||
if (floatImage.Width != width || floatImage.Height != height)
|
||||
Cv2.Resize(floatImage, floatImage, new OpenCvSharp.Size(width, height));
|
||||
|
||||
|
||||
float[] data = new float[3 * width * height];
|
||||
int idx = 0;
|
||||
var rows = floatImage.Rows;
|
||||
var cols = floatImage.Cols;
|
||||
for (int y = 0; y < rows; y++)
|
||||
{
|
||||
for (int x = 0; x < cols; x++)
|
||||
{
|
||||
Vec3f pixel = floatImage.At<Vec3f>(y, x);
|
||||
for (int c = 0; c < 3; c++) // channels
|
||||
{
|
||||
data[idx++] = pixel[c]; // width*height*channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var inputTensor = new DenseTensor<float>(
|
||||
data,
|
||||
new int[] { 1, floatImage.Width, floatImage.Height, 3 }
|
||||
);
|
||||
|
||||
var inputs = new List<NamedOnnxValue> {
|
||||
NamedOnnxValue.CreateFromTensor("input", inputTensor)
|
||||
};
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
using var results = _AIsession.Run(inputs);
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Inference time: {sw.ElapsedMilliseconds} ms");
|
||||
var output = results.First().AsEnumerable<float>().ToArray();
|
||||
|
||||
// convert output to Mat and normalize to 0-255
|
||||
var outputMat = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_32FC1);
|
||||
outputMat.SetArray<float>(output);
|
||||
|
||||
outputMat.ConvertTo(outputMat, MatType.CV_8UC1, 255);
|
||||
|
||||
// resize back to original size
|
||||
Cv2.Resize(outputMat, outputMat, new OpenCvSharp.Size(currentImage.Width, currentImage.Height));
|
||||
return outputMat;
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Plugins/CandyboxPlugin/Detectors/CandyDetector.cs
Normal file
94
Plugins/CandyboxPlugin/Detectors/CandyDetector.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.ML.OnnxRuntime;
|
||||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace CandyboxPlugin.Detectors
|
||||
{
|
||||
public class CandyDetector
|
||||
{
|
||||
|
||||
private const string MODEL_NAME = "model_transparent_candy_resnet.onnx";
|
||||
private readonly InferenceSession _AIsession;
|
||||
private readonly string _inputName;
|
||||
private readonly int[] _dimensions;
|
||||
public CandyDetector()
|
||||
{
|
||||
var opts = new SessionOptions();
|
||||
// get this dll path
|
||||
var path = Assembly.GetCallingAssembly().Location;
|
||||
var dir = System.IO.Path.GetDirectoryName(path);
|
||||
var modelPath = Path.Combine(dir, "models\\" + MODEL_NAME);
|
||||
_AIsession = new InferenceSession(modelPath, opts);
|
||||
_inputName = _AIsession.InputMetadata.First().Key;
|
||||
_dimensions = _AIsession.InputMetadata.First().Value.Dimensions;
|
||||
foreach (var input in _AIsession.InputMetadata)
|
||||
{
|
||||
Console.WriteLine($"Name: {input.Key}");
|
||||
Console.WriteLine($" Type: {input.Value.ElementType}");
|
||||
Console.WriteLine($" Shape: [{string.Join(", ", input.Value.Dimensions)}]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Mat Eval(Mat bmpTest)
|
||||
{
|
||||
var currentImage = bmpTest;
|
||||
|
||||
var rightColor = currentImage.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||
|
||||
// convert to float32
|
||||
var floatImage = new Mat();
|
||||
rightColor.ConvertTo(floatImage, MatType.CV_32FC3, 1.0 / 255);
|
||||
// to array
|
||||
|
||||
var width = _dimensions[1];
|
||||
var height = _dimensions[2];
|
||||
|
||||
// check if need resize
|
||||
if (floatImage.Width != width || floatImage.Height != height)
|
||||
Cv2.Resize(floatImage, floatImage, new OpenCvSharp.Size(width, height));
|
||||
|
||||
|
||||
float[] data = new float[3 * width * height];
|
||||
int idx = 0;
|
||||
var rows = floatImage.Rows;
|
||||
var cols = floatImage.Cols;
|
||||
for (int y = 0; y < rows; y++)
|
||||
{
|
||||
for (int x = 0; x < cols; x++)
|
||||
{
|
||||
Vec3f pixel = floatImage.At<Vec3f>(y, x);
|
||||
for (int c = 0; c < 3; c++) // channels
|
||||
{
|
||||
data[idx++] = pixel[c]; // width*height*channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var inputTensor = new DenseTensor<float>(
|
||||
data,
|
||||
new int[] { 1, floatImage.Width, floatImage.Height, 3 }
|
||||
);
|
||||
|
||||
var inputs = new List<NamedOnnxValue> {
|
||||
NamedOnnxValue.CreateFromTensor("input", inputTensor)
|
||||
};
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
using var results = _AIsession.Run(inputs);
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Inference time: {sw.ElapsedMilliseconds} ms");
|
||||
var output = results.First().AsEnumerable<float>().ToArray();
|
||||
|
||||
// convert output to Mat and normalize to 0-255
|
||||
var outputMat = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_32FC1);
|
||||
outputMat.SetArray<float>(output);
|
||||
|
||||
outputMat.ConvertTo(outputMat, MatType.CV_8UC1, 255);
|
||||
|
||||
// resize back to original size
|
||||
Cv2.Resize(outputMat, outputMat, new OpenCvSharp.Size(currentImage.Width, currentImage.Height));
|
||||
return outputMat;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
Plugins/CandyboxPlugin/Forms/FAUFInput.Designer.cs
generated
Normal file
138
Plugins/CandyboxPlugin/Forms/FAUFInput.Designer.cs
generated
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
namespace Lindt.Candybox.RecipeSelector
|
||||
{
|
||||
partial class FAUFInput
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.btnStart = new MaterialSkin.Controls.MaterialRaisedButton();
|
||||
this.lblBlisterName = new System.Windows.Forms.Label();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// pictureBox1
|
||||
//
|
||||
this.pictureBox1.Location = new System.Drawing.Point(88, 72);
|
||||
this.pictureBox1.Name = "pictureBox1";
|
||||
this.pictureBox1.Size = new System.Drawing.Size(256, 256);
|
||||
this.pictureBox1.TabIndex = 0;
|
||||
this.pictureBox1.TabStop = false;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.BackColor = System.Drawing.Color.White;
|
||||
this.label1.Location = new System.Drawing.Point(88, 48);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(89, 15);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "Selected blister:";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.BackColor = System.Drawing.Color.White;
|
||||
this.label2.Location = new System.Drawing.Point(88, 368);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(84, 15);
|
||||
this.label2.TabIndex = 2;
|
||||
this.label2.Text = "FAUF Number:";
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.textBox1.Location = new System.Drawing.Point(88, 392);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(256, 39);
|
||||
this.textBox1.TabIndex = 3;
|
||||
this.textBox1.Text = "0";
|
||||
this.textBox1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBox1_KeyDown);
|
||||
//
|
||||
// btnStart
|
||||
//
|
||||
this.btnStart.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnStart.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.btnStart.Depth = 0;
|
||||
this.btnStart.DrawBorder = true;
|
||||
this.btnStart.Icon = null;
|
||||
this.btnStart.Location = new System.Drawing.Point(88, 472);
|
||||
this.btnStart.MouseState = MaterialSkin.MouseState.HOVER;
|
||||
this.btnStart.Name = "btnStart";
|
||||
this.btnStart.Primary = true;
|
||||
this.btnStart.Size = new System.Drawing.Size(256, 64);
|
||||
this.btnStart.TabIndex = 5;
|
||||
this.btnStart.Text = "Start";
|
||||
this.btnStart.UseVisualStyleBackColor = true;
|
||||
this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
|
||||
//
|
||||
// lblBlisterName
|
||||
//
|
||||
this.lblBlisterName.AutoSize = true;
|
||||
this.lblBlisterName.BackColor = System.Drawing.Color.White;
|
||||
this.lblBlisterName.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||
this.lblBlisterName.Location = new System.Drawing.Point(304, 48);
|
||||
this.lblBlisterName.Name = "lblBlisterName";
|
||||
this.lblBlisterName.Size = new System.Drawing.Size(39, 15);
|
||||
this.lblBlisterName.TabIndex = 7;
|
||||
this.lblBlisterName.Text = "label3";
|
||||
this.lblBlisterName.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
//
|
||||
// FAUFInput
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(440, 567);
|
||||
this.Controls.Add(this.lblBlisterName);
|
||||
this.Controls.Add(this.btnStart);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.pictureBox1);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FAUFInput";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox pictureBox1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private MaterialSkin.Controls.MaterialRaisedButton btnStart;
|
||||
private System.Windows.Forms.Label lblBlisterName;
|
||||
}
|
||||
}
|
||||
73
Plugins/CandyboxPlugin/Forms/FAUFInput.cs
Normal file
73
Plugins/CandyboxPlugin/Forms/FAUFInput.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using MaterialSkin.Controls;
|
||||
|
||||
namespace Lindt.Candybox.RecipeSelector
|
||||
{
|
||||
public partial class FAUFInput : MaterialForm
|
||||
{
|
||||
|
||||
|
||||
public FAUFInput(string recipe)
|
||||
{
|
||||
InitializeComponent();
|
||||
lblBlisterName.Text = recipe.Replace("R", "");
|
||||
var path = @"..\Data\Samples\\" +recipe + ".bmp";
|
||||
Bitmap sample;
|
||||
if (File.Exists(path))
|
||||
{
|
||||
sample = new Bitmap(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
sample = new Bitmap(256,256);
|
||||
}
|
||||
|
||||
|
||||
var img = new Bitmap(256,256);
|
||||
using (Graphics g = Graphics.FromImage(img))
|
||||
{
|
||||
g.Clear(Color.White);
|
||||
var offsetX = (img.Width - sample.Width) / 2;
|
||||
var offsetY = (img.Height - sample.Height) / 2;
|
||||
g.DrawImage(sample,offsetX,offsetY,sample.Width,sample.Height);
|
||||
}
|
||||
sample.Dispose();
|
||||
pictureBox1.Image = img;
|
||||
textBox1.Focus();
|
||||
textBox1.SelectAll();
|
||||
}
|
||||
|
||||
public string Value { get; set; } = "";
|
||||
|
||||
private void btnSkip_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void btnStart_Click(object sender, EventArgs e)
|
||||
{
|
||||
Value = textBox1.Text;
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void textBox1_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Return)
|
||||
{
|
||||
Value = textBox1.Text;
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Plugins/CandyboxPlugin/Forms/FAUFInput.resx
Normal file
60
Plugins/CandyboxPlugin/Forms/FAUFInput.resx
Normal file
@@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
130
Plugins/CandyboxPlugin/Forms/MapEditor.Designer.cs
generated
Normal file
130
Plugins/CandyboxPlugin/Forms/MapEditor.Designer.cs
generated
Normal file
@@ -0,0 +1,130 @@
|
||||
|
||||
namespace Lindt.Candybox.Demo
|
||||
{
|
||||
partial class MapEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapEditor));
|
||||
this.maskControl1 = new Lindt.Candybox.Demo.MaskControl();
|
||||
this.materialRaisedButton1 = new MaterialSkin.Controls.MaterialRaisedButton();
|
||||
this.materialRaisedButton3 = new MaterialSkin.Controls.MaterialRaisedButton();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.udMemoryBuffer = new System.Windows.Forms.NumericUpDown();
|
||||
((System.ComponentModel.ISupportInitialize)(this.udMemoryBuffer)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// maskControl1
|
||||
//
|
||||
this.maskControl1.Location = new System.Drawing.Point(8, 32);
|
||||
this.maskControl1.Mask = ((System.Drawing.Bitmap)(resources.GetObject("maskControl1.Mask")));
|
||||
this.maskControl1.Name = "maskControl1";
|
||||
this.maskControl1.Original = ((System.Drawing.Bitmap)(resources.GetObject("maskControl1.Original")));
|
||||
this.maskControl1.Size = new System.Drawing.Size(900, 704);
|
||||
this.maskControl1.TabIndex = 0;
|
||||
//
|
||||
// materialRaisedButton1
|
||||
//
|
||||
this.materialRaisedButton1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.materialRaisedButton1.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.materialRaisedButton1.Depth = 0;
|
||||
this.materialRaisedButton1.DrawBorder = true;
|
||||
this.materialRaisedButton1.Icon = null;
|
||||
this.materialRaisedButton1.Location = new System.Drawing.Point(928, 104);
|
||||
this.materialRaisedButton1.MouseState = MaterialSkin.MouseState.HOVER;
|
||||
this.materialRaisedButton1.Name = "materialRaisedButton1";
|
||||
this.materialRaisedButton1.Primary = false;
|
||||
this.materialRaisedButton1.Size = new System.Drawing.Size(128, 64);
|
||||
this.materialRaisedButton1.TabIndex = 1;
|
||||
this.materialRaisedButton1.Text = "Cancel";
|
||||
this.materialRaisedButton1.UseVisualStyleBackColor = true;
|
||||
this.materialRaisedButton1.Click += new System.EventHandler(this.materialRaisedButton1_Click);
|
||||
//
|
||||
// materialRaisedButton3
|
||||
//
|
||||
this.materialRaisedButton3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.materialRaisedButton3.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.materialRaisedButton3.Depth = 0;
|
||||
this.materialRaisedButton3.DrawBorder = true;
|
||||
this.materialRaisedButton3.Icon = null;
|
||||
this.materialRaisedButton3.Location = new System.Drawing.Point(928, 32);
|
||||
this.materialRaisedButton3.MouseState = MaterialSkin.MouseState.HOVER;
|
||||
this.materialRaisedButton3.Name = "materialRaisedButton3";
|
||||
this.materialRaisedButton3.Primary = true;
|
||||
this.materialRaisedButton3.Size = new System.Drawing.Size(128, 64);
|
||||
this.materialRaisedButton3.TabIndex = 3;
|
||||
this.materialRaisedButton3.Text = "Learn";
|
||||
this.materialRaisedButton3.UseVisualStyleBackColor = true;
|
||||
this.materialRaisedButton3.Click += new System.EventHandler(this.materialRaisedButton3_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.BackColor = System.Drawing.Color.White;
|
||||
this.label1.Location = new System.Drawing.Point(928, 184);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(90, 15);
|
||||
this.label1.TabIndex = 4;
|
||||
this.label1.Text = "Memory buffer:";
|
||||
//
|
||||
// udMemoryBuffer
|
||||
//
|
||||
this.udMemoryBuffer.Location = new System.Drawing.Point(928, 208);
|
||||
this.udMemoryBuffer.Name = "udMemoryBuffer";
|
||||
this.udMemoryBuffer.Size = new System.Drawing.Size(120, 23);
|
||||
this.udMemoryBuffer.TabIndex = 5;
|
||||
//
|
||||
// MapEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1066, 824);
|
||||
this.Controls.Add(this.udMemoryBuffer);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.materialRaisedButton3);
|
||||
this.Controls.Add(this.materialRaisedButton1);
|
||||
this.Controls.Add(this.maskControl1);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "MapEditor";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "MapEditor";
|
||||
((System.ComponentModel.ISupportInitialize)(this.udMemoryBuffer)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private MaskControl maskControl1;
|
||||
private MaterialSkin.Controls.MaterialRaisedButton materialRaisedButton1;
|
||||
private MaterialSkin.Controls.MaterialRaisedButton materialRaisedButton3;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.NumericUpDown udMemoryBuffer;
|
||||
}
|
||||
}
|
||||
76
Plugins/CandyboxPlugin/Forms/MapEditor.cs
Normal file
76
Plugins/CandyboxPlugin/Forms/MapEditor.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using CandyboxPlugin.Recipe;
|
||||
using MaterialSkin.Controls;
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Extensions;
|
||||
using Point = OpenCvSharp.Point;
|
||||
|
||||
namespace Lindt.Candybox.Demo
|
||||
{
|
||||
public partial class MapEditor : MaterialForm
|
||||
{
|
||||
private readonly Mat _original;
|
||||
private HistoRecipe _recipe;
|
||||
|
||||
public MapEditor(Mat original, HistoRecipe currentRecipe)
|
||||
{
|
||||
_original = original;
|
||||
InitializeComponent();
|
||||
|
||||
_recipe = currentRecipe.Clone();
|
||||
|
||||
_recipe.ProcessImage(original);
|
||||
|
||||
Mat matContours2;
|
||||
if (_recipe.LearnedParameters.CandyParameters.Count==0)
|
||||
{
|
||||
Mat matContours = new Mat(new OpenCvSharp.Size(512, 512), MatType.CV_8UC3, Scalar.Black);
|
||||
Cv2.DrawContours(matContours, _recipe.LastContours, -1, Scalar.White, -1);
|
||||
|
||||
matContours2 = matContours.Resize(new OpenCvSharp.Size(1800, 1408));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Mat matContours = new Mat(new OpenCvSharp.Size(512, 512), MatType.CV_8UC3, Scalar.Black);
|
||||
Cv2.DrawContours(matContours, _recipe.LastContours, -1, Scalar.White, -1);
|
||||
|
||||
matContours2 = matContours.Resize(new OpenCvSharp.Size(1800, 1408));
|
||||
|
||||
}
|
||||
|
||||
udMemoryBuffer.Value = _recipe.LearnedParameters.Configuration.MemoryBuffer;
|
||||
|
||||
|
||||
maskControl1.Mask = matContours2.ToBitmap();
|
||||
maskControl1.Original = original.Clone().ToBitmap();
|
||||
}
|
||||
|
||||
private void materialRaisedButton3_Click(object sender, EventArgs e)
|
||||
{
|
||||
List<Point[]> contours = new List<Point[]>();
|
||||
foreach (Point[] contour in maskControl1.LastContours)
|
||||
{
|
||||
contours.Add(contour.Select(x => new Point(x.X/1800f*512f, x.Y / 1408f * 512f)).ToArray());
|
||||
}
|
||||
|
||||
_recipe.LearnedParameters.Configuration.MemoryBuffer = (int)udMemoryBuffer.Value;
|
||||
_recipe.LearnContours(contours.ToArray(), _original);
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void materialRaisedButton1_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
}
|
||||
401
Plugins/CandyboxPlugin/Forms/MapEditor.resx
Normal file
401
Plugins/CandyboxPlugin/Forms/MapEditor.resx
Normal file
@@ -0,0 +1,401 @@
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="maskControl1.Mask" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAABwgAAAWACAYAAABN7xIaAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
wQAADsEBuJFr7QAAJoVJREFUeF7swQEBAAAAgJD+r+4IAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAABuD44JAAAAEAatf2pj+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADATQ2+hgAB2UBGuQAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="maskControl1.Original" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAABwgAAAWACAYAAABN7xIaAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
wQAADsEBuJFr7QAAJoVJREFUeF7swQEBAAAAgJD+r+4IAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAABuD44JAAAAEAatf2pj+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADATQ2+hgAB2UBGuQAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
38
Plugins/CandyboxPlugin/Forms/MaskControl.Designer.cs
generated
Normal file
38
Plugins/CandyboxPlugin/Forms/MaskControl.Designer.cs
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
namespace Lindt.Candybox.Demo
|
||||
{
|
||||
partial class MaskControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
119
Plugins/CandyboxPlugin/Forms/MaskControl.cs
Normal file
119
Plugins/CandyboxPlugin/Forms/MaskControl.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Inspectron.Hawkeye.Vision.Geometry;
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Extensions;
|
||||
using Point = System.Drawing.Point;
|
||||
|
||||
|
||||
namespace Lindt.Candybox.Demo
|
||||
{
|
||||
public partial class MaskControl : UserControl
|
||||
{
|
||||
private Bitmap _backBuffer;
|
||||
private GrahamConvexHull _hull;
|
||||
|
||||
public MaskControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
_backBuffer = new Bitmap(1800, 1408);
|
||||
_hull = new GrahamConvexHull();
|
||||
}
|
||||
|
||||
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Bitmap Mask { get; set; } = new Bitmap(1800, 1408);
|
||||
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public Bitmap Original { get; set; } = new Bitmap(1800, 1408);
|
||||
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
//base.OnPaintBackground(e);
|
||||
}
|
||||
|
||||
private bool _isDown = false;
|
||||
private Point _lastMouse;
|
||||
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
_isDown = true;
|
||||
_lastMouse = ConvertCoordinates(new Point(e.X, e.Y));
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseEventArgs e)
|
||||
{
|
||||
_isDown = false;
|
||||
}
|
||||
|
||||
private Pen _pen = new Pen(Color.White, 30);
|
||||
private Pen _penBlack = new Pen(Color.Black, 30);
|
||||
protected override void OnMouseMove(MouseEventArgs e)
|
||||
{
|
||||
|
||||
if (_isDown&&e.Button==MouseButtons.Left)
|
||||
{
|
||||
var curPoint = ConvertCoordinates(new Point(e.X, e.Y));
|
||||
using (Graphics g = Graphics.FromImage(Mask))
|
||||
{
|
||||
g.DrawLine(_pen, curPoint.X, curPoint.Y,_lastMouse.X,_lastMouse.Y);
|
||||
g.FillEllipse(Brushes.White, _lastMouse.X - 15, _lastMouse.Y - 15, 30,30);
|
||||
}
|
||||
}
|
||||
if (_isDown && e.Button == MouseButtons.Right)
|
||||
{
|
||||
var curPoint = ConvertCoordinates(new Point(e.X, e.Y));
|
||||
using (Graphics g = Graphics.FromImage(Mask))
|
||||
{
|
||||
g.DrawLine(_penBlack, curPoint.X, curPoint.Y, _lastMouse.X, _lastMouse.Y);
|
||||
g.FillEllipse(Brushes.Black, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30);
|
||||
}
|
||||
}
|
||||
_lastMouse = ConvertCoordinates(new Point(e.X, e.Y));
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private Pen _contourPen = new Pen(Color.GreenYellow, 3);
|
||||
protected override void OnPaint(PaintEventArgs e)
|
||||
{
|
||||
var mat = Mask.ToMat().CvtColor(ColorConversionCodes.BGR2GRAY);
|
||||
var contours = mat.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone);
|
||||
LastContours = contours;
|
||||
using (Graphics g = Graphics.FromImage(_backBuffer))
|
||||
{
|
||||
g.DrawImage(Original, 0, 0);
|
||||
|
||||
for (int i = 0; i < contours.Length; i++)
|
||||
{
|
||||
if (contours[i].Length > 1)
|
||||
{
|
||||
var points = contours[i].Select(x => new System.Drawing.Point(x.X, x.Y)).ToArray();
|
||||
g.DrawPolygon(_contourPen, points);
|
||||
}
|
||||
}
|
||||
g.DrawEllipse(Pens.Gray,_lastMouse.X-15, _lastMouse.Y - 15,30,30);
|
||||
}
|
||||
e.Graphics.DrawImage(_backBuffer,0, 0,Width,Height);
|
||||
}
|
||||
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public OpenCvSharp.Point[][] LastContours { get; set; }
|
||||
|
||||
Point ConvertCoordinates(Point coords)
|
||||
{
|
||||
return new Point(coords.X * 2, coords.Y * 2);
|
||||
}
|
||||
|
||||
protected override void OnResize(EventArgs e)
|
||||
{
|
||||
base.OnResize(e);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
120
Plugins/CandyboxPlugin/Forms/MaskControl.resx
Normal file
120
Plugins/CandyboxPlugin/Forms/MaskControl.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
120
Plugins/CandyboxPlugin/Geometry/DefaultRectangle2Algorithm.cs
Normal file
120
Plugins/CandyboxPlugin/Geometry/DefaultRectangle2Algorithm.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
namespace CandyboxPlugin.Geometry
|
||||
{
|
||||
public class DefaultRectangle2Algorithm
|
||||
{
|
||||
public List<IntPoint> FindRectangle2(List<IntPoint> hullPoints)
|
||||
{
|
||||
//check if no bounding box available
|
||||
if (hullPoints.Count <= 1)
|
||||
return hullPoints;
|
||||
|
||||
Rectangle2d minBox = null;
|
||||
var minAngle = 0d;
|
||||
|
||||
//foreach edge of the convex hull
|
||||
for (var i = 0; i < hullPoints.Count; i++)
|
||||
{
|
||||
var nextIndex = i + 1;
|
||||
|
||||
var current = hullPoints[i];
|
||||
var next = hullPoints[nextIndex % hullPoints.Count];
|
||||
|
||||
|
||||
|
||||
//min / max points
|
||||
var top = double.MinValue;
|
||||
var bottom = double.MaxValue;
|
||||
var left = double.MaxValue;
|
||||
var right = double.MinValue;
|
||||
|
||||
//get angle of segment to x axis
|
||||
var angle = AngleToXAxis(current,next);
|
||||
|
||||
//rotate every point and get min and max values for each direction
|
||||
foreach (var p in hullPoints)
|
||||
{
|
||||
var rotatedPoint = RotateToXAxis(p, angle);
|
||||
|
||||
top = Math.Max(top, rotatedPoint.Y);
|
||||
bottom = Math.Min(bottom, rotatedPoint.Y);
|
||||
|
||||
left = Math.Min(left, rotatedPoint.X);
|
||||
right = Math.Max(right, rotatedPoint.X);
|
||||
}
|
||||
|
||||
//create axis aligned bounding box
|
||||
var box = new Rectangle2d(new IntPoint((int)left, (int)bottom), new IntPoint((int)right, (int)top));
|
||||
|
||||
if (minBox == null || minBox.Area() > box.Area())
|
||||
{
|
||||
minBox = box;
|
||||
minAngle = angle;
|
||||
}
|
||||
}
|
||||
|
||||
//rotate axis algined box back
|
||||
var minimalBoundingBox = minBox.Points.Select(p => RotateToXAxis(p, -minAngle)).ToList();
|
||||
return minimalBoundingBox;
|
||||
}
|
||||
/// <summary>
|
||||
/// Calculates the angle to the X axis.
|
||||
/// </summary>
|
||||
/// <returns>The angle to the X axis.</returns>
|
||||
/// <param name="s">The segment to get the angle from.</param>
|
||||
static double AngleToXAxis(IntPoint a, IntPoint b)
|
||||
{
|
||||
var delta = a-b;
|
||||
return -Math.Atan((float)delta.Y / (float)delta.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates vector by an angle to the x-Axis
|
||||
/// </summary>
|
||||
/// <returns>Rotated vector.</returns>
|
||||
/// <param name="v">Vector to rotate.</param>
|
||||
/// <param name="angle">Angle to trun by.</param>
|
||||
static IntPoint RotateToXAxis(IntPoint v, double angle)
|
||||
{
|
||||
var newX = v.X * Math.Cos(angle) - v.Y * Math.Sin(angle);
|
||||
var newY = v.X * Math.Sin(angle) + v.Y * Math.Cos(angle);
|
||||
|
||||
return new IntPoint((int)newX, (int)newY);
|
||||
}
|
||||
}
|
||||
public class Rectangle2d
|
||||
{
|
||||
public IntPoint Location { get; set; }
|
||||
|
||||
public IntPoint Size { get; set; }
|
||||
|
||||
public Rectangle2d()
|
||||
{
|
||||
}
|
||||
|
||||
public Rectangle2d(IntPoint a, IntPoint c) : this()
|
||||
{
|
||||
Location = a;
|
||||
Size = c - a;
|
||||
}
|
||||
|
||||
public double Area()
|
||||
{
|
||||
return Size.X * Size.Y;
|
||||
}
|
||||
|
||||
public IntPoint[] Points
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[] {
|
||||
new IntPoint (Location.X, Location.Y),
|
||||
new IntPoint (Location.X + Size.X, Location.Y),
|
||||
new IntPoint (Location.X + Size.X, Location.Y + Size.Y),
|
||||
new IntPoint (Location.X, Location.Y + Size.Y)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
156
Plugins/CandyboxPlugin/Geometry/GrahamConvexHull.cs
Normal file
156
Plugins/CandyboxPlugin/Geometry/GrahamConvexHull.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CandyboxPlugin.Geometry;
|
||||
|
||||
|
||||
namespace Inspectron.Hawkeye.Vision.Geometry
|
||||
{
|
||||
public class GrahamConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Find convex hull for the given set of points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="points">Set of points to search convex hull for.</param>
|
||||
///
|
||||
/// <returns>Returns set of points, which form a convex hull for the given <paramref name="points"/>.
|
||||
/// The first point in the list is the point with lowest X coordinate (and with lowest Y if there are
|
||||
/// several points with the same X value). Points are provided in counter clockwise order
|
||||
/// (<a href="http://en.wikipedia.org/wiki/Cartesian_coordinate_system">Cartesian
|
||||
/// coordinate system</a>).</returns>
|
||||
///
|
||||
public List<IntPoint> FindHull(List<IntPoint> points)
|
||||
{
|
||||
// do nothing if there 3 points or less
|
||||
if (points.Count <= 3)
|
||||
{
|
||||
return new List<IntPoint>(points);
|
||||
}
|
||||
|
||||
// find a point, with lowest X and lowest Y
|
||||
int firstCornerIndex = 0;
|
||||
IntPoint pointFirstCorner = points[0];
|
||||
|
||||
for (int i = 1, n = points.Count; i < n; i++)
|
||||
{
|
||||
if ((points[i].X < pointFirstCorner.X) ||
|
||||
((points[i].X == pointFirstCorner.X) && (points[i].Y < pointFirstCorner.Y)))
|
||||
{
|
||||
pointFirstCorner = points[i];
|
||||
firstCornerIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// convert input points to points we can process
|
||||
PointToProcess firstCorner = new PointToProcess(pointFirstCorner);
|
||||
// Points to process must exclude the first corner that we've already found
|
||||
PointToProcess[] arrPointsToProcess = new PointToProcess[points.Count - 1];
|
||||
for (int i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
IntPoint point = points[i >= firstCornerIndex ? i + 1 : i];
|
||||
arrPointsToProcess[i] = new PointToProcess(point);
|
||||
}
|
||||
|
||||
// find K (tangent of line's angle) and distance to the first corner
|
||||
for (int i = 0, n = arrPointsToProcess.Length; i < n; i++)
|
||||
{
|
||||
int dx = arrPointsToProcess[i].X - firstCorner.X;
|
||||
int dy = arrPointsToProcess[i].Y - firstCorner.Y;
|
||||
|
||||
// don't need square root, since it is not important in our case
|
||||
arrPointsToProcess[i].Distance = dx * dx + dy * dy;
|
||||
// tangent of lines angle
|
||||
arrPointsToProcess[i].K = (dx == 0) ? float.PositiveInfinity : (float)dy / dx;
|
||||
}
|
||||
|
||||
// sort points by angle and distance
|
||||
Array.Sort(arrPointsToProcess);
|
||||
|
||||
// Convert points to process to a queue. Continually removing the first item of an array list
|
||||
// is highly inefficient
|
||||
Queue<PointToProcess> queuePointsToProcess = new Queue<PointToProcess>(arrPointsToProcess);
|
||||
|
||||
LinkedList<PointToProcess> convexHullTemp = new LinkedList<PointToProcess>();
|
||||
|
||||
// add first corner, which is always on the hull
|
||||
PointToProcess prevPoint = convexHullTemp.AddLast(firstCorner).Value;
|
||||
// add another point, which forms a line with lowest slope
|
||||
PointToProcess lastPoint = convexHullTemp.AddLast(queuePointsToProcess.Dequeue()).Value;
|
||||
|
||||
while (queuePointsToProcess.Count != 0)
|
||||
{
|
||||
PointToProcess newPoint = queuePointsToProcess.Peek();
|
||||
|
||||
// skip any point, which has the same slope as the last one or
|
||||
// has 0 distance to the first point
|
||||
if ((newPoint.K == lastPoint.K) || (newPoint.Distance == 0))
|
||||
{
|
||||
queuePointsToProcess.Dequeue();
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if current point is on the left side from two last points
|
||||
if ((newPoint.X - prevPoint.X) * (lastPoint.Y - newPoint.Y) - (lastPoint.X - newPoint.X) * (newPoint.Y - prevPoint.Y) < 0)
|
||||
{
|
||||
// add the point to the hull
|
||||
convexHullTemp.AddLast(newPoint);
|
||||
// and remove it from the list of points to process
|
||||
queuePointsToProcess.Dequeue();
|
||||
|
||||
prevPoint = lastPoint;
|
||||
lastPoint = newPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove the last point from the hull
|
||||
convexHullTemp.RemoveLast();
|
||||
|
||||
lastPoint = prevPoint;
|
||||
prevPoint = convexHullTemp.Last.Previous.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// convert points back
|
||||
List<IntPoint> convexHull = new List<IntPoint>();
|
||||
|
||||
foreach (PointToProcess pt in convexHullTemp)
|
||||
{
|
||||
convexHull.Add(pt.ToPoint());
|
||||
}
|
||||
|
||||
return convexHull;
|
||||
}
|
||||
|
||||
// Internal comparer for sorting points
|
||||
private class PointToProcess : IComparable
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
public float K;
|
||||
public float Distance;
|
||||
|
||||
public PointToProcess(IntPoint point)
|
||||
{
|
||||
X = point.X;
|
||||
Y = point.Y;
|
||||
|
||||
K = 0;
|
||||
Distance = 0;
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
PointToProcess another = (PointToProcess)obj;
|
||||
|
||||
return (K < another.K) ? -1 : (K > another.K) ? 1 :
|
||||
((Distance > another.Distance) ? -1 : (Distance < another.Distance) ? 1 : 0);
|
||||
}
|
||||
|
||||
public IntPoint ToPoint()
|
||||
{
|
||||
return new IntPoint(X, Y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
378
Plugins/CandyboxPlugin/Geometry/IntPoint.cs
Normal file
378
Plugins/CandyboxPlugin/Geometry/IntPoint.cs
Normal file
@@ -0,0 +1,378 @@
|
||||
// AForge Core Library
|
||||
// AForge.NET framework
|
||||
// http://www.aforgenet.com/framework/
|
||||
//
|
||||
// Copyright © AForge.NET, 2007-2011
|
||||
// contacts@aforgenet.com
|
||||
//
|
||||
|
||||
namespace CandyboxPlugin.Geometry
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure for representing a pair of coordinates of integer type.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks><para>The structure is used to store a pair of integer coordinates.</para>
|
||||
///
|
||||
/// <para>Sample usage:</para>
|
||||
/// <code>
|
||||
/// // assigning coordinates in the constructor
|
||||
/// IntPoint p1 = new IntPoint( 10, 20 );
|
||||
/// // creating a point and assigning coordinates later
|
||||
/// IntPoint p2;
|
||||
/// p2.X = 30;
|
||||
/// p2.Y = 40;
|
||||
/// // calculating distance between two points
|
||||
/// float distance = p1.DistanceTo( p2 );
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
///
|
||||
[Serializable]
|
||||
public struct IntPoint : IComparable<IntPoint>
|
||||
{
|
||||
/// <summary>
|
||||
/// X coordinate.
|
||||
/// </summary>
|
||||
///
|
||||
public int X;
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate.
|
||||
/// </summary>
|
||||
///
|
||||
public int Y;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IntPoint"/> structure.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="x">X axis coordinate.</param>
|
||||
/// <param name="y">Y axis coordinate.</param>
|
||||
///
|
||||
public IntPoint(int x, int y)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate Euclidean distance between two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="anotherPoint">Point to calculate distance to.</param>
|
||||
///
|
||||
/// <returns>Returns Euclidean distance between this point and
|
||||
/// <paramref name="anotherPoint"/> points.</returns>
|
||||
///
|
||||
public float DistanceTo(IntPoint anotherPoint)
|
||||
{
|
||||
int dx = X - anotherPoint.X;
|
||||
int dy = Y - anotherPoint.Y;
|
||||
|
||||
return (float)System.Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate squared Euclidean distance between two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="anotherPoint">Point to calculate distance to.</param>
|
||||
///
|
||||
/// <returns>Returns squared Euclidean distance between this point and
|
||||
/// <paramref name="anotherPoint"/> points.</returns>
|
||||
///
|
||||
public float SquaredDistanceTo(Point anotherPoint)
|
||||
{
|
||||
float dx = X - anotherPoint.X;
|
||||
float dy = Y - anotherPoint.Y;
|
||||
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addition operator - adds values of two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">First point for addition.</param>
|
||||
/// <param name="point2">Second point for addition.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to sum of corresponding
|
||||
/// coordinates of specified points.</returns>
|
||||
///
|
||||
public static IntPoint operator +(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return new IntPoint(point1.X + point2.X, point1.Y + point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addition operator - adds values of two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">First point for addition.</param>
|
||||
/// <param name="point2">Second point for addition.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to sum of corresponding
|
||||
/// coordinates of specified points.</returns>
|
||||
///
|
||||
public static IntPoint Add(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return new IntPoint(point1.X + point2.X, point1.Y + point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtraction operator - subtracts values of two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">Point to subtract from.</param>
|
||||
/// <param name="point2">Point to subtract.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to difference of corresponding
|
||||
/// coordinates of specified points.</returns>
|
||||
///
|
||||
public static IntPoint operator -(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return new IntPoint(point1.X - point2.X, point1.Y - point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtraction operator - subtracts values of two points.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">Point to subtract from.</param>
|
||||
/// <param name="point2">Point to subtract.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to difference of corresponding
|
||||
/// coordinates of specified points.</returns>
|
||||
///
|
||||
public static IntPoint Subtract(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return new IntPoint(point1.X - point2.X, point1.Y - point2.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addition operator - adds scalar to the specified point.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to increase coordinates of.</param>
|
||||
/// <param name="valueToAdd">Value to add to coordinates of the specified point.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point increased by specified value.</returns>
|
||||
///
|
||||
public static IntPoint operator +(IntPoint point, int valueToAdd)
|
||||
{
|
||||
return new IntPoint(point.X + valueToAdd, point.Y + valueToAdd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addition operator - adds scalar to the specified point.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to increase coordinates of.</param>
|
||||
/// <param name="valueToAdd">Value to add to coordinates of the specified point.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point increased by specified value.</returns>
|
||||
///
|
||||
public static IntPoint Add(IntPoint point, int valueToAdd)
|
||||
{
|
||||
return new IntPoint(point.X + valueToAdd, point.Y + valueToAdd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtraction operator - subtracts scalar from the specified point.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to decrease coordinates of.</param>
|
||||
/// <param name="valueToSubtract">Value to subtract from coordinates of the specified point.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point decreased by specified value.</returns>
|
||||
///
|
||||
public static IntPoint operator -(IntPoint point, int valueToSubtract)
|
||||
{
|
||||
return new IntPoint(point.X - valueToSubtract, point.Y - valueToSubtract);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtraction operator - subtracts scalar from the specified point.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to decrease coordinates of.</param>
|
||||
/// <param name="valueToSubtract">Value to subtract from coordinates of the specified point.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point decreased by specified value.</returns>
|
||||
///
|
||||
public static IntPoint Subtract(IntPoint point, int valueToSubtract)
|
||||
{
|
||||
return new IntPoint(point.X - valueToSubtract, point.Y - valueToSubtract);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplication operator - multiplies coordinates of the specified point by scalar value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to multiply coordinates of.</param>
|
||||
/// <param name="factor">Multiplication factor.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point multiplied by specified value.</returns>
|
||||
///
|
||||
public static IntPoint operator *(IntPoint point, int factor)
|
||||
{
|
||||
return new IntPoint(point.X * factor, point.Y * factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplication operator - multiplies coordinates of the specified point by scalar value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to multiply coordinates of.</param>
|
||||
/// <param name="factor">Multiplication factor.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point multiplied by specified value.</returns>
|
||||
///
|
||||
public static IntPoint Multiply(IntPoint point, int factor)
|
||||
{
|
||||
return new IntPoint(point.X * factor, point.Y * factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Division operator - divides coordinates of the specified point by scalar value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to divide coordinates of.</param>
|
||||
/// <param name="factor">Division factor.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point divided by specified value.</returns>
|
||||
///
|
||||
public static IntPoint operator /(IntPoint point, int factor)
|
||||
{
|
||||
return new IntPoint(point.X / factor, point.Y / factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Division operator - divides coordinates of the specified point by scalar value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Point to divide coordinates of.</param>
|
||||
/// <param name="factor">Division factor.</param>
|
||||
///
|
||||
/// <returns>Returns new point which coordinates equal to coordinates of
|
||||
/// the specified point divided by specified value.</returns>
|
||||
///
|
||||
public static IntPoint Divide(IntPoint point, int factor)
|
||||
{
|
||||
return new IntPoint(point.X / factor, point.Y / factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator - checks if two points have equal coordinates.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">First point to check.</param>
|
||||
/// <param name="point2">Second point to check.</param>
|
||||
///
|
||||
/// <returns>Returns <see langword="true"/> if coordinates of specified
|
||||
/// points are equal.</returns>
|
||||
///
|
||||
public static bool operator ==(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return ((point1.X == point2.X) && (point1.Y == point2.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inequality operator - checks if two points have different coordinates.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point1">First point to check.</param>
|
||||
/// <param name="point2">Second point to check.</param>
|
||||
///
|
||||
/// <returns>Returns <see langword="true"/> if coordinates of specified
|
||||
/// points are not equal.</returns>
|
||||
///
|
||||
public static bool operator !=(IntPoint point1, IntPoint point2)
|
||||
{
|
||||
return ((point1.X != point2.X) || (point1.Y != point2.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this instance of <see cref="IntPoint"/> equal to the specified one.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="obj">Another point to check equalty to.</param>
|
||||
///
|
||||
/// <returns>Return <see langword="true"/> if objects are equal.</returns>
|
||||
///
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return (obj is IntPoint) ? (this == (IntPoint)obj) : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hash code for this instance.
|
||||
/// </summary>
|
||||
///
|
||||
/// <returns>Returns the hash code for this instance.</returns>
|
||||
///
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X.GetHashCode() + Y.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion to <see cref="Point"/>.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="point">Integer point to convert to single precision point.</param>
|
||||
///
|
||||
/// <returns>Returns new single precision point which coordinates are implicitly converted
|
||||
/// to floats from coordinates of the specified integer point.</returns>
|
||||
///
|
||||
public static implicit operator Point(IntPoint point)
|
||||
{
|
||||
return new Point(point.X, point.Y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get string representation of the class.
|
||||
/// </summary>
|
||||
///
|
||||
/// <returns>Returns string, which contains values of the point in readable form.</returns>
|
||||
///
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}, {1}", X, Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate Euclidean norm of the vector comprised of the point's
|
||||
/// coordinates - distance from (0, 0) in other words.
|
||||
/// </summary>
|
||||
///
|
||||
/// <returns>Returns point's distance from (0, 0) point.</returns>
|
||||
///
|
||||
public float EuclideanNorm()
|
||||
{
|
||||
return (float)System.Math.Sqrt(X * X + Y * Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
|
||||
/// </summary>
|
||||
/// <param name="other">An object to compare with this instance.</param>
|
||||
/// <returns>A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="other" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="other" />. Greater than zero This instance follows <paramref name="other" /> in the sort order.</returns>
|
||||
public int CompareTo(IntPoint other)
|
||||
{
|
||||
int line = this.Y.CompareTo(other.Y);
|
||||
if (line == 0)
|
||||
return this.X.CompareTo(other.X);
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
108
Plugins/CandyboxPlugin/Modules/Barcode/CandyboxBarcodeReader.cs
Normal file
108
Plugins/CandyboxPlugin/Modules/Barcode/CandyboxBarcodeReader.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.IO.Ports;
|
||||
using System.Text;
|
||||
using MaterialSkin.Controls;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Statistics;
|
||||
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public class CandyboxBarcodeReader : IVisionBuilderModule
|
||||
{
|
||||
private readonly ILoadingService _loadingService;
|
||||
|
||||
private readonly CandyboxBarcodeReaderSettings _settings;
|
||||
private readonly IRecognitionControl _recognitionControl;
|
||||
private readonly VisionBuilderStatistics _statistics;
|
||||
private SerialPort _port;
|
||||
public EReaderState _readerState = EReaderState.ReadingRecipe;
|
||||
private MaterialCancellableLoader? _loader;
|
||||
|
||||
public CandyboxBarcodeReader(CandyboxBarcodeReaderSettings settings, IRecognitionControl recognitionControl, VisionBuilderStatistics statistics)
|
||||
{
|
||||
_settings = settings;
|
||||
_recognitionControl = recognitionControl;
|
||||
_statistics = statistics;
|
||||
}
|
||||
|
||||
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
if(_readerState == EReaderState.ReadingRecipe)
|
||||
ReadRecipe();
|
||||
if (_readerState == EReaderState.ReadingAUF)
|
||||
ReadAUF();
|
||||
}
|
||||
|
||||
private void ReadRecipe()
|
||||
{
|
||||
if (_recognitionControl.IsRunning) return;
|
||||
|
||||
byte[] buffer = new byte[_port.BytesToRead];
|
||||
_port.Read(buffer, 0, _port.BytesToRead);
|
||||
var data = Encoding.ASCII.GetString(buffer).Trim();
|
||||
var recipes = _recognitionControl.GetRecipesData();
|
||||
|
||||
string searchString = data;
|
||||
if (_settings.BarcodeRecipeMappings.Any(x => x.Barcode == searchString))
|
||||
{
|
||||
searchString = _settings.BarcodeRecipeMappings.First(x => x.Barcode == searchString).RecipeName;
|
||||
}
|
||||
|
||||
var existingRecipe = recipes.FirstOrDefault(x => x.RecipeName == searchString);
|
||||
if (existingRecipe == null)
|
||||
{
|
||||
Log.Warning("No matching recipe found for data: {Data}", data);
|
||||
return;
|
||||
}
|
||||
|
||||
_recognitionControl.SetRecipe(existingRecipe);
|
||||
_readerState = EReaderState.ReadingAUF;
|
||||
_loader = new MaterialCancellableLoader();
|
||||
_loader.StartLoading("Waiting for AUF...");
|
||||
_loader.LoadingCancelled += CancelWaiting;
|
||||
}
|
||||
|
||||
private void CancelWaiting(object? sender, string e)
|
||||
{
|
||||
_readerState = EReaderState.ReadingRecipe;
|
||||
_loader.Close();
|
||||
_loader.LoadingCancelled -= CancelWaiting;
|
||||
_loader = null;
|
||||
}
|
||||
|
||||
private void ReadAUF()
|
||||
{
|
||||
byte[] buffer = new byte[_port.BytesToRead];
|
||||
_port.Read(buffer, 0, _port.BytesToRead);
|
||||
var data = Encoding.ASCII.GetString(buffer).Trim();
|
||||
if (_loader != null)
|
||||
{
|
||||
_loader.Close();
|
||||
_loader.LoadingCancelled -= CancelWaiting;
|
||||
_loader = null;
|
||||
}
|
||||
|
||||
|
||||
_statistics.Metadata = data;
|
||||
_recognitionControl.Start();
|
||||
_readerState = EReaderState.ReadingAUF;
|
||||
}
|
||||
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
try
|
||||
{
|
||||
_port = new SerialPort(_settings.ComPort, 9600);
|
||||
_port.Open();
|
||||
_port.DataReceived += _port_DataReceived;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, "Failed to open com-port");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Inspectron.Settings;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public class CandyboxBarcodeReaderSettings(string CameraName) : ISettings
|
||||
{
|
||||
public string ComPort { get; set; } = "COM3";
|
||||
|
||||
public List<BarcodeRecipeMapping> BarcodeRecipeMappings { get; set; } = new List<BarcodeRecipeMapping>();
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => ComPort, CameraName + "/Barcode reader", nameof(ComPort));
|
||||
settings.RegisterSimple(this, () => BarcodeRecipeMappings, CameraName + "/Barcode reader", nameof(BarcodeRecipeMappings));
|
||||
}
|
||||
}
|
||||
|
||||
public class BarcodeRecipeMapping
|
||||
{
|
||||
public string Barcode { get; set; }
|
||||
public string RecipeName { get; set; }
|
||||
}
|
||||
|
||||
public class ListBarcodeRecipeMappingConverter : ITypeConverter
|
||||
{
|
||||
public object ConvertFrom(object value)
|
||||
{
|
||||
if (value is string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<BarcodeRecipeMapping>>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize BarcodeRecipeMapping list from JSON.");
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a JSON string.");
|
||||
}
|
||||
|
||||
public object ConvertTo(object value, Type destinationType)
|
||||
{
|
||||
if (value is List<BarcodeRecipeMapping> list && destinationType == typeof(string))
|
||||
{
|
||||
return JsonSerializer.Serialize(list, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a List<BarcodeRecipeMapping> and destinationType must be string.");
|
||||
|
||||
}
|
||||
}
|
||||
7
Plugins/CandyboxPlugin/Modules/Barcode/EReaderState.cs
Normal file
7
Plugins/CandyboxPlugin/Modules/Barcode/EReaderState.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public enum EReaderState
|
||||
{
|
||||
ReadingRecipe,
|
||||
ReadingAUF
|
||||
}
|
||||
105
Plugins/CandyboxPlugin/Modules/CandyboxImageProcessingControl.cs
Normal file
105
Plugins/CandyboxPlugin/Modules/CandyboxImageProcessingControl.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using CandyboxPlugin.Recipe;
|
||||
using Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||
using Newtonsoft.Json;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxImageProcessingControl : BaseRecognitionControl
|
||||
{
|
||||
private readonly IImageSource _imageSource;
|
||||
private HistoRecipe _histoRecipe;
|
||||
|
||||
public CandyboxImageProcessingControl(CandyboxRecognitionControlSettings settings, IImageSource imageSource, ILoadingService loadingService) : base(settings, loadingService)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
}
|
||||
|
||||
private const string RECIPES_DIR = @"..\Data\Recipes";
|
||||
private const string SAMPLES_DIR = @"..\Data\Samples";
|
||||
|
||||
public override List<RecipeData> GetRecipesData()
|
||||
{
|
||||
var recipes = new List<RecipeData>();
|
||||
var recipeFiles = Directory.GetFiles(RECIPES_DIR, "*.json");
|
||||
foreach (var file in recipeFiles)
|
||||
{
|
||||
var name = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
Mat? image = null;
|
||||
var filePatterns = new[]
|
||||
{
|
||||
name + ".bmp",
|
||||
"r" + name + ".bmp",
|
||||
name.Replace("recipe", "") + ".bmp"
|
||||
};
|
||||
|
||||
foreach (var pattern in filePatterns)
|
||||
{
|
||||
var filePath = Path.Combine(SAMPLES_DIR, pattern);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
image = Cv2.ImRead(filePath);
|
||||
break; // Exit loop after finding the first matching file
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug($"File not found: {filePath}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
recipes.Add(new RecipeData
|
||||
{
|
||||
RecipeName = name,
|
||||
Image = image
|
||||
});
|
||||
}
|
||||
return recipes;
|
||||
}
|
||||
|
||||
protected override void Initialize(RecipeData currentRecipe)
|
||||
{
|
||||
_histoRecipe = new HistoRecipe(currentRecipe.RecipeName);
|
||||
if (_imageSource is HawkeyeCameraImageSource hawkeye)
|
||||
{
|
||||
var settings = hawkeye.Settings;
|
||||
settings.ImageSettings = settings.ImageSettings with {Lines = _histoRecipe.GetCameraWidth()};
|
||||
hawkeye.ApplySettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WarmUp()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override (Mat originalImage, Mat analysisImage, TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)?
|
||||
ProcessImage(CancellationToken token)
|
||||
{
|
||||
var swTotal = System.Diagnostics.Stopwatch.StartNew();
|
||||
var swAcquision = System.Diagnostics.Stopwatch.StartNew();
|
||||
var image = _imageSource.GetImage(token).Result;
|
||||
swAcquision.Stop();
|
||||
if (image==null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var res=_histoRecipe.ProcessImage(image);
|
||||
swTotal.Stop();
|
||||
|
||||
var allErrors =
|
||||
res.ErrorPoints.Select(x => x.ToString())
|
||||
.Concat(
|
||||
res.ErrorReason.Where(x=>x != EErrorReason.Good).Select(x=>x.ToString())
|
||||
).ToArray();
|
||||
|
||||
return (image, res.ProcessedImage, swTotal.Elapsed, swAcquision.Elapsed, allErrors);
|
||||
|
||||
}
|
||||
}
|
||||
30
Plugins/CandyboxPlugin/Modules/CandyboxLearningTool.cs
Normal file
30
Plugins/CandyboxPlugin/Modules/CandyboxLearningTool.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using CandyboxPlugin.Recipe;
|
||||
using Inspectron.HawkEye.View;
|
||||
using Lindt.Candybox.Demo;
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
using VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxLearningTool: ILearningTool
|
||||
{
|
||||
|
||||
private ImagePreview _activeWindow;
|
||||
public CandyboxLearningTool(IImagePreviewService imagePreviewService)
|
||||
{
|
||||
_activeWindow = (imagePreviewService as WindowsImagePreviewService)!.CurrentWindow!;
|
||||
}
|
||||
|
||||
public bool IsLearningEnabled(string recipeName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Learn(string recipeName, Mat image)
|
||||
{
|
||||
var editor = new MapEditor(image, new HistoRecipe(recipeName));
|
||||
editor.ShowDialog(_activeWindow);
|
||||
}
|
||||
}
|
||||
13
Plugins/CandyboxPlugin/Modules/CandyboxRecipeCreationTool.cs
Normal file
13
Plugins/CandyboxPlugin/Modules/CandyboxRecipeCreationTool.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using MaterialSkin.Core.Controls;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxRecipeCreationTool:IRecipeCreationTool
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public bool CreateRecipe(out string recipeName)
|
||||
{
|
||||
return MaterialInputBox.Prompt("Recipe name","", out recipeName)==DialogResult.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxRecognitionControlSettings : BaseRecognitionControlSettings
|
||||
{
|
||||
public CandyboxRecognitionControlSettings(string cameraName) : base(cameraName)
|
||||
{
|
||||
}
|
||||
}
|
||||
36
Plugins/CandyboxPlugin/Plugin.cs
Normal file
36
Plugins/CandyboxPlugin/Plugin.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using CandyboxPlugin.Module;
|
||||
using Inspectron.Settings;
|
||||
using Ninject;
|
||||
using System.Reflection;
|
||||
using CandyboxPlugin.Modules.Barcode;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Plugins;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
|
||||
namespace CandyboxPlugin
|
||||
{
|
||||
public class Plugin: IPlugin
|
||||
{
|
||||
public void RegisterGlobalModules(IKernel kernel)
|
||||
{
|
||||
TypeConverterRegistry.Register<List<BarcodeRecipeMapping>>(new ListBarcodeRecipeMappingConverter());
|
||||
|
||||
kernel.Rebind<IRecipeCreationTool>().To<CandyboxRecipeCreationTool>().InSingletonScope();
|
||||
}
|
||||
|
||||
public void RegisterCameraModules(IKernel kernel, string cameraName)
|
||||
{
|
||||
kernel.Bind<CandyboxRecognitionControlSettings, ISettings>().ToConstant(new CandyboxRecognitionControlSettings(cameraName));
|
||||
|
||||
kernel.Rebind<IRecognitionControl>().To<CandyboxImageProcessingControl>().InSingletonScope();
|
||||
|
||||
kernel.Rebind<ILearningTool>().To<CandyboxLearningTool>().InSingletonScope();
|
||||
|
||||
kernel.Bind<CandyboxBarcodeReaderSettings, ISettings>()
|
||||
.ToConstant(new CandyboxBarcodeReaderSettings(cameraName));
|
||||
kernel.RegisterModule<CandyboxBarcodeReader>();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Plugins/CandyboxPlugin/Recipe/CandyParameter.cs
Normal file
42
Plugins/CandyboxPlugin/Recipe/CandyParameter.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public class CandyParameter
|
||||
{
|
||||
private Point? _originalPoint;
|
||||
public Queue<float[]> Histogram { get; set; } = new Queue<float[]>();
|
||||
|
||||
public Point Position { get; set; }
|
||||
public int OuterRadius { get; set; }
|
||||
public int InnerRadius { get; set; }
|
||||
public float Weight { get; set; } = 1;
|
||||
|
||||
|
||||
public void ResetPoint()
|
||||
{
|
||||
if (_originalPoint != null)
|
||||
{
|
||||
Position = _originalPoint.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void RotatePoint(Point centerPoint, double angleInRadians)
|
||||
{
|
||||
_originalPoint = Position;
|
||||
double cosTheta = Math.Cos(angleInRadians);
|
||||
double sinTheta = Math.Sin(angleInRadians);
|
||||
Position = new Point
|
||||
{
|
||||
X =
|
||||
(int)
|
||||
(cosTheta * (Position.X - centerPoint.X) -
|
||||
sinTheta * (Position.Y - centerPoint.Y) + centerPoint.X),
|
||||
Y =
|
||||
(int)
|
||||
(sinTheta * (Position.X - centerPoint.X) +
|
||||
cosTheta * (Position.Y - centerPoint.Y) + centerPoint.Y)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
63
Plugins/CandyboxPlugin/Recipe/ColorHeatMap.cs
Normal file
63
Plugins/CandyboxPlugin/Recipe/ColorHeatMap.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public class ColorHeatMap
|
||||
{
|
||||
public ColorHeatMap()
|
||||
{
|
||||
initColorsBlocks();
|
||||
}
|
||||
|
||||
public ColorHeatMap(byte alpha)
|
||||
{
|
||||
Alpha = alpha;
|
||||
initColorsBlocks();
|
||||
}
|
||||
|
||||
private void initColorsBlocks()
|
||||
{
|
||||
ColorsOfMap.AddRange(new Color[]
|
||||
{
|
||||
Color.FromArgb(Alpha, Color.PaleVioletRed),
|
||||
Color.FromArgb(Alpha, Color.Yellow),
|
||||
Color.FromArgb(Alpha, Color.GreenYellow),
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public Color GetColorForValue(double val, double maxVal)
|
||||
{
|
||||
double valPerc = val / maxVal; // value%
|
||||
double colorPerc = 1d / (ColorsOfMap.Count-1); // % of each block of color. the last is the "100% Color"
|
||||
double blockOfColor = valPerc / colorPerc; // the integer part repersents how many block to skip
|
||||
int blockIdx = (int) Math.Truncate(blockOfColor); // Idx of
|
||||
double valPercResidual = valPerc - blockIdx * colorPerc; //remove the part represented of block
|
||||
double percOfColor = valPercResidual / colorPerc; // % of color of this block that will be filled
|
||||
|
||||
Color cTarget = ColorsOfMap[blockIdx];
|
||||
if (blockIdx == ColorsOfMap.Count-1) return cTarget;
|
||||
Color cNext = cNext = ColorsOfMap[blockIdx + 1];
|
||||
|
||||
var deltaR = cNext.R - cTarget.R;
|
||||
var deltaG = cNext.G - cTarget.G;
|
||||
var deltaB = cNext.B - cTarget.B;
|
||||
|
||||
var R = cTarget.R + deltaR * valPercResidual;
|
||||
var G = cTarget.G + deltaG * valPercResidual;
|
||||
var B = cTarget.B + deltaB * valPercResidual;
|
||||
|
||||
Color c = ColorsOfMap[0];
|
||||
try
|
||||
{
|
||||
c = Color.FromArgb(Alpha, (byte) R, (byte) G, (byte) B);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public byte Alpha = 0xff;
|
||||
public List<Color> ColorsOfMap = new List<Color>();
|
||||
}
|
||||
}
|
||||
13
Plugins/CandyboxPlugin/Recipe/EErrorReason.cs
Normal file
13
Plugins/CandyboxPlugin/Recipe/EErrorReason.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public enum EErrorReason
|
||||
{
|
||||
Unknown,
|
||||
Missing,
|
||||
WrongType,
|
||||
Misposition,
|
||||
WrongSize,
|
||||
NotLearned,
|
||||
Good
|
||||
}
|
||||
}
|
||||
41
Plugins/CandyboxPlugin/Recipe/ErrorTextHelper.cs
Normal file
41
Plugins/CandyboxPlugin/Recipe/ErrorTextHelper.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using OpenCvSharp;
|
||||
using Point = System.Drawing.Point;
|
||||
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public static class ErrorTextHelper
|
||||
{
|
||||
private static Font _font = new Font(FontFamily.GenericMonospace, 8);
|
||||
public static void DrawErrorText(Graphics g,Point point, EErrorReason errorReason)
|
||||
{
|
||||
string errorText = errorReason.ToString();
|
||||
|
||||
var measured=g.MeasureString(errorText, _font);
|
||||
var mRect = new RectangleF(new PointF(point.X-measured.Width/2,point.Y), measured);
|
||||
g.FillRectangle(Brushes.Bisque,mRect);
|
||||
g.DrawRectangle(Pens.Coral,mRect.X,mRect.Y,mRect.Width,mRect.Height);
|
||||
g.DrawString(errorReason.ToString(),_font,Brushes.Black,mRect.X,mRect.Y);
|
||||
}
|
||||
|
||||
public static void DrawErrorText(Mat mat,OpenCvSharp.Point point, EErrorReason errorReason)
|
||||
{
|
||||
string errorText = errorReason.ToString();
|
||||
Cv2.Rectangle(mat,point,new OpenCvSharp.Point(point.X+80,point.Y+20),OpaqueScalar(Scalar.Bisque),thickness:Cv2.FILLED);
|
||||
Cv2.Rectangle(mat,point,new OpenCvSharp.Point(point.X+80,point.Y+20),OpaqueScalar(Scalar.Coral));
|
||||
Cv2.PutText(mat,errorText,new OpenCvSharp.Point(point.X,point.Y+15),HersheyFonts.HersheySimplex,0.4,OpaqueScalar(Scalar.Black));
|
||||
|
||||
}
|
||||
public static void DrawErrorText(Mat mat, OpenCvSharp.Point point, string errorReason)
|
||||
{
|
||||
string errorText = errorReason.ToString();
|
||||
Cv2.Rectangle(mat, point, new OpenCvSharp.Point(point.X + 80, point.Y + 20), OpaqueScalar(Scalar.Bisque), thickness: Cv2.FILLED);
|
||||
Cv2.Rectangle(mat, point, new OpenCvSharp.Point(point.X + 80, point.Y + 20), OpaqueScalar(Scalar.Coral));
|
||||
Cv2.PutText(mat, errorText, new OpenCvSharp.Point(point.X, point.Y + 15), HersheyFonts.HersheySimplex, 0.4, OpaqueScalar(Scalar.Black));
|
||||
|
||||
}
|
||||
private static Scalar OpaqueScalar(Scalar scalar)
|
||||
{
|
||||
return new Scalar(scalar.Val0, scalar.Val1, scalar.Val2, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
672
Plugins/CandyboxPlugin/Recipe/HistoRecipe.cs
Normal file
672
Plugins/CandyboxPlugin/Recipe/HistoRecipe.cs
Normal file
@@ -0,0 +1,672 @@
|
||||
using CandyboxPlugin.Detectors;
|
||||
using CandyboxPlugin.Geometry;
|
||||
using CandyboxPlugin.Voronoi;
|
||||
using OpenCvSharp;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Text.Json;
|
||||
using VisionBuilder.UI.Common.NullClasses;
|
||||
using Point = System.Drawing.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
//todo: update camera settings according to learned parameters
|
||||
public class HistoRecipe
|
||||
{
|
||||
private readonly string _recipeName;
|
||||
|
||||
private CandyDetector _learner;
|
||||
private BlisterDetector _blister;
|
||||
|
||||
private DefaultRectangle2Algorithm _rectangle2;
|
||||
private LearningParameters _learnedParameters;
|
||||
private ColorHeatMap _colorHeatMap;
|
||||
|
||||
|
||||
private Size AnalysisSize { get; }= new Size(512, 512);
|
||||
Size ThumbnailSize { get; } = new Size(256, 200);
|
||||
|
||||
public const string RECIPES_DIRECTORY = "..\\Data\\Recipes";
|
||||
|
||||
public HistoRecipe(string recipeName)
|
||||
{
|
||||
_recipeName = recipeName;
|
||||
_configurationPath = Path.GetFullPath(Path.Combine(RECIPES_DIRECTORY, _recipeName + ".json"));
|
||||
|
||||
_learner = new CandyDetector();
|
||||
_blister = new BlisterDetector();
|
||||
|
||||
_rectangle2 = new DefaultRectangle2Algorithm();
|
||||
_learnedParameters = new LearningParameters();
|
||||
_colorHeatMap = new ColorHeatMap(255);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if (File.Exists(_configurationPath))
|
||||
{
|
||||
_learnedParameters =
|
||||
JsonSerializer.Deserialize<LearningParameters>(File.ReadAllText(_configurationPath));
|
||||
}
|
||||
}
|
||||
|
||||
public void LearnContours(OpenCvSharp.Point[][] contours, Mat image)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
|
||||
|
||||
if (_learnedParameters.CandyParameters.Count == 0)//initial learning
|
||||
{
|
||||
_learnedParameters.BlisterAngle = _blisterAngle;
|
||||
_learnedParameters.BlisterPosition = _blisterCenter;
|
||||
_learnedParameters.ImageWidth = (int)(_blisterEdge / 512f * 1800f);
|
||||
for (int i = 0; i < contours.Length; i++)
|
||||
{
|
||||
var hist = CalcHist(contours, i, _resizedMat, out var outerRadius, out var innerRadius);
|
||||
var moments = Cv2.Moments(contours[i]);
|
||||
var cX = (int)(moments.M10 / moments.M00);
|
||||
var cY = (int)(moments.M01 / moments.M00);
|
||||
hist.GetArray(out float[] histData);
|
||||
var p = new CandyParameter()
|
||||
{
|
||||
Position = new Point(cX, cY),
|
||||
Histogram = new Queue<float[]>(),
|
||||
OuterRadius = (int)outerRadius,
|
||||
InnerRadius = (int)innerRadius
|
||||
};
|
||||
p.Histogram.Enqueue(histData);
|
||||
_learnedParameters.CandyParameters.Add(p);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (CandyParameter parameter in _learnedParameters.CandyParameters)
|
||||
{
|
||||
|
||||
parameter.ResetPoint();
|
||||
parameter.RotatePoint(Point.Empty, _blisterAngle - _learnedParameters.BlisterAngle);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < contours.Length; i++)
|
||||
{
|
||||
var hist = CalcHist(contours, i, _resizedMat, out var outerRadius, out var innerRadius);
|
||||
var moments = Cv2.Moments(contours[i]);
|
||||
var cX = (int)(moments.M10 / moments.M00);
|
||||
var cY = (int)(moments.M01 / moments.M00);
|
||||
hist.GetArray(out float[] histData);
|
||||
var point = ClosestPoint(_blisterCenter, cX, cY, (int)outerRadius, (int)innerRadius);
|
||||
point.Histogram.Enqueue(histData);
|
||||
if (point.Histogram.Count > _learnedParameters.Configuration.MemoryBuffer)
|
||||
{
|
||||
point.Histogram.Dequeue();
|
||||
}
|
||||
|
||||
}
|
||||
foreach (CandyParameter parameter in _learnedParameters.CandyParameters)
|
||||
{
|
||||
|
||||
parameter.ResetPoint();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(_configurationPath, JsonSerializer.Serialize(_learnedParameters, new JsonSerializerOptions() { WriteIndented = true }));
|
||||
image.Resize(ThumbnailSize).SaveImage(Path.Combine("..\\Data\\Samples", Path.GetFileNameWithoutExtension(_recipeName) + ".bmp"));
|
||||
}
|
||||
}
|
||||
|
||||
public HistoRecipe Clone()
|
||||
{
|
||||
var clone = new HistoRecipe(_recipeName);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public ProcessingResult ProcessImage(Mat image)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
{
|
||||
var framedImage = new Mat(new Size(1800, 1408),MatType.CV_8UC3,Scalar.FromRgb(36, 67, 182));
|
||||
// draw the image in top left corner of framed image
|
||||
// this does not actually create new Mat. it just creates a header for the region of interest
|
||||
image.CopyTo(framedImage[0, image.Height, 0, image.Width]);
|
||||
|
||||
// add alpha channel
|
||||
var rgb = framedImage;
|
||||
framedImage = framedImage.CvtColor(ColorConversionCodes.BGR2BGRA);
|
||||
|
||||
|
||||
var resized = rgb.Resize(AnalysisSize);
|
||||
|
||||
|
||||
_resizedMat = resized;
|
||||
var sw1 = Stopwatch.StartNew();
|
||||
|
||||
|
||||
Mat res = _learner.Eval(resized);
|
||||
Mat resBlister = _blister.Eval(resized);
|
||||
|
||||
sw1.Stop();
|
||||
Console.WriteLine("AI Time:" + sw1.ElapsedMilliseconds);
|
||||
Mat inverted = null;
|
||||
Mat overlayMat = new Mat(res.Width, res.Height, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
|
||||
bool wasError = false;
|
||||
List<int> errorPoints = new List<int>();
|
||||
OpenCvSharp.Point[] blisterRectangle;
|
||||
Mat numbersOverlay = new Mat(512, 512, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
|
||||
List<EErrorReason> errorReason = new List<EErrorReason>();
|
||||
using (var tracker = new ResourcesTracker())
|
||||
{
|
||||
|
||||
blisterRectangle = FindBlister(resBlister, out _blisterAngle, out _blisterCenter,
|
||||
out _blisterEdge);
|
||||
|
||||
|
||||
|
||||
|
||||
if (_learnedParameters.CandyParameters.Count > 0)
|
||||
{
|
||||
|
||||
DelaunayTriangulator triangulator = new DelaunayTriangulator();
|
||||
triangulator.GenerateBorder(512, 512);
|
||||
|
||||
var points=_learnedParameters.CandyParameters
|
||||
.Select((x, i) =>
|
||||
new VoronoiPoint(
|
||||
x.Position.X + _blisterCenter.X - _learnedParameters.BlisterPosition.X,
|
||||
x.Position.Y + _blisterCenter.Y - _learnedParameters.BlisterPosition.Y
|
||||
))
|
||||
.ToList();
|
||||
var triangles = triangulator.BowyerWatson(_learnedParameters.CandyParameters
|
||||
.Select((x,i) =>
|
||||
new VoronoiPoint(
|
||||
x.Position.X + _blisterCenter.X - _learnedParameters.BlisterPosition.X,
|
||||
x.Position.Y + _blisterCenter.Y - _learnedParameters.BlisterPosition.Y
|
||||
))
|
||||
.ToList());
|
||||
|
||||
var dbg=triangles.Where(x => x.IsInside(points[15])).ToList();
|
||||
var dbgEdges = Voronoi.Voronoi.GenerateEdgesFromDelaunay(dbg);
|
||||
foreach (var triangle in dbg)
|
||||
{
|
||||
Cv2.Line(
|
||||
numbersOverlay,
|
||||
new OpenCvSharp.Point(triangle.Vertices[0].X, triangle.Vertices[0].Y),
|
||||
new OpenCvSharp.Point(triangle.Vertices[1].X, triangle.Vertices[1].Y),
|
||||
new Scalar(255, 255, 0, 255), // Aqua color in BGR
|
||||
1 // Thickness
|
||||
);
|
||||
Cv2.Line(
|
||||
numbersOverlay,
|
||||
new OpenCvSharp.Point(triangle.Vertices[1].X, triangle.Vertices[1].Y),
|
||||
new OpenCvSharp.Point(triangle.Vertices[2].X, triangle.Vertices[2].Y),
|
||||
new Scalar(255, 255, 0, 255), // Aqua color in BGR
|
||||
1 // Thickness
|
||||
);
|
||||
Cv2.Line(
|
||||
numbersOverlay,
|
||||
new OpenCvSharp.Point(triangle.Vertices[2].X, triangle.Vertices[2].Y),
|
||||
new OpenCvSharp.Point(triangle.Vertices[0].X, triangle.Vertices[0].Y),
|
||||
new Scalar(255, 255, 0, 255), // Aqua color in BGR
|
||||
1 // Thickness
|
||||
);
|
||||
}
|
||||
//foreach (Edge edge in dbgEdges)
|
||||
//{
|
||||
// Cv2.Line(
|
||||
// numbersOverlay,
|
||||
// new OpenCvSharp.Point(edge.Point1.X, edge.Point1.Y),
|
||||
// new OpenCvSharp.Point(edge.Point2.X, edge.Point2.Y),
|
||||
// new Scalar(255, 255, 255, 255),
|
||||
// 3 // Thickness
|
||||
// );
|
||||
//}
|
||||
|
||||
|
||||
var edges = Voronoi.Voronoi.GenerateEdgesFromDelaunay(triangles);
|
||||
foreach (Edge edge in edges)
|
||||
{
|
||||
Cv2.Line(
|
||||
res,
|
||||
new OpenCvSharp.Point(edge.Point1.X, edge.Point1.Y),
|
||||
new OpenCvSharp.Point(edge.Point2.X, edge.Point2.Y),
|
||||
new Scalar(0, 0, 0), // Black color in BGR
|
||||
3 // Thickness
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//foreach (Edge edge in edges)
|
||||
//{
|
||||
// Cv2.Line(
|
||||
// numbersOverlay,
|
||||
// new OpenCvSharp.Point(edge.Point1.X, edge.Point1.Y),
|
||||
// new OpenCvSharp.Point(edge.Point2.X, edge.Point2.Y),
|
||||
// new Scalar(255, 255, 255, 255),
|
||||
// 3 // Thickness
|
||||
// );
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
var mat = res;
|
||||
Cv2.Threshold(mat, mat, 128, 255, ThresholdTypes.Binary);
|
||||
|
||||
OpenCvSharp.Point[][] contours;
|
||||
contours = mat.FindContoursAsArray(RetrievalModes.External,
|
||||
ContourApproximationModes.ApproxNone);
|
||||
|
||||
contours = contours.Where(x => Cv2.ContourArea(x) > 500).ToArray();
|
||||
|
||||
|
||||
|
||||
var sw2 = Stopwatch.StartNew();
|
||||
LastContours = contours;
|
||||
|
||||
int c = 0;
|
||||
foreach (CandyParameter parameter in _learnedParameters.CandyParameters)
|
||||
{
|
||||
|
||||
parameter.ResetPoint();
|
||||
parameter.RotatePoint(Point.Empty, _blisterAngle - _learnedParameters.BlisterAngle);
|
||||
var coordX = parameter.Position.X +
|
||||
(_blisterCenter.X - _learnedParameters.BlisterPosition.X);
|
||||
var coordY = parameter.Position.Y +
|
||||
(_blisterCenter.Y - _learnedParameters.BlisterPosition.Y);
|
||||
|
||||
// Draw filled white circle (ellipse)
|
||||
Cv2.Circle(numbersOverlay, new OpenCvSharp.Point(coordX, coordY), 10, new Scalar(255, 255, 255, 255), thickness: -1);
|
||||
|
||||
// Draw black ellipse outline
|
||||
Cv2.Ellipse(numbersOverlay, new OpenCvSharp.Point(coordX, coordY), new Size(10, 10), 0, 0, 360, new Scalar(0, 0, 0, 255), thickness: 1);
|
||||
|
||||
// Draw the number as text (centered, adjust offset as needed)
|
||||
Cv2.PutText(
|
||||
numbersOverlay,
|
||||
c.ToString(),
|
||||
new OpenCvSharp.Point(coordX - 7, coordY + 7), // Y offset for baseline alignment
|
||||
HersheyFonts.HersheySimplex,
|
||||
0.4, // Font scale
|
||||
new Scalar(0,0,0,255),
|
||||
1,
|
||||
LineTypes.AntiAlias
|
||||
);
|
||||
|
||||
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < contours.Length; i++)
|
||||
{
|
||||
if (Cv2.PointPolygonTest(contours[i],
|
||||
new Point2f(coordX, coordY), false) > 0)
|
||||
{
|
||||
var hist = CalcHist(contours, i, _resizedMat, out var outerRadius,
|
||||
out var innerRadius);
|
||||
var correl = 0;
|
||||
foreach (float[] h in parameter.Histogram)
|
||||
{
|
||||
Mat histC = new Mat(new Size(1, 125), MatType.CV_32FC1);
|
||||
histC.SetArray(h);
|
||||
|
||||
var correl_0 = Cv2.CompareHist(histC, hist, HistCompMethods.Correl) * 100;
|
||||
if (correl_0 > correl)
|
||||
{
|
||||
correl = (int) correl_0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_learnedParameters.Configuration.IgnoreCorrelation.Contains(c))
|
||||
correl = 100;
|
||||
if (correl > _learnedParameters.Configuration.SimilarityTolerance)
|
||||
{
|
||||
var min = _learnedParameters.Configuration.SimilarityTolerance;
|
||||
var max = 100;
|
||||
var value = correl;
|
||||
value = value < min ? min : value;
|
||||
|
||||
|
||||
var value2 = (int) Map(value, min, max, 0, 255);
|
||||
|
||||
var color = _colorHeatMap.GetColorForValue(value2, 255);
|
||||
Cv2.DrawContours(overlayMat, contours, i,
|
||||
new Scalar(color.B, color.G, color.R, color.A));
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cv2.DrawContours(overlayMat, contours, i, OpaqueScalar(Scalar.Red));
|
||||
Console.WriteLine($"{c} wrong type. similarity is {correl}%");
|
||||
errorReason.Add(EErrorReason.WrongType);
|
||||
ErrorTextHelper.DrawErrorText(overlayMat, new OpenCvSharp.Point(coordX, coordY),
|
||||
EErrorReason.WrongType);
|
||||
errorPoints.Add(c);
|
||||
found = true;
|
||||
wasError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var outerDiff = parameter.OuterRadius - outerRadius;
|
||||
|
||||
if (Math.Abs(outerDiff / parameter.OuterRadius) * 100 >
|
||||
_learnedParameters.Configuration.OuterTolerance && outerDiff < 0)
|
||||
{
|
||||
Cv2.DrawContours(overlayMat, contours, i, OpaqueScalar(Scalar.Red));
|
||||
errorReason.Add(EErrorReason.WrongSize);
|
||||
ErrorTextHelper.DrawErrorText(overlayMat, new OpenCvSharp.Point(coordX, coordY),
|
||||
EErrorReason.WrongSize);
|
||||
Console.WriteLine(
|
||||
$"{c} wrong outer size. expected {parameter.OuterRadius}, got {outerRadius} ({Math.Abs(outerDiff / parameter.OuterRadius) * 100f})");
|
||||
errorPoints.Add(c);
|
||||
|
||||
wasError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var innerDiff = parameter.InnerRadius - innerRadius;
|
||||
|
||||
|
||||
if (Math.Abs(innerDiff / parameter.InnerRadius) * 100 >
|
||||
_learnedParameters.Configuration.InnerTolerance && innerDiff < 0)
|
||||
{
|
||||
Cv2.DrawContours(overlayMat, contours, i, OpaqueScalar(Scalar.Red));
|
||||
Console.WriteLine(
|
||||
$"{c} wrong inner size. expected {parameter.InnerRadius}, got {innerRadius} ({Math.Abs(innerDiff / parameter.InnerRadius) * 100f})");
|
||||
errorReason.Add(EErrorReason.WrongSize);
|
||||
ErrorTextHelper.DrawErrorText(overlayMat, new OpenCvSharp.Point(coordX, coordY),
|
||||
EErrorReason.WrongSize);
|
||||
|
||||
errorPoints.Add(c);
|
||||
|
||||
wasError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
wasError = true;
|
||||
errorReason.Add(EErrorReason.Missing);
|
||||
|
||||
errorPoints.Add(c);
|
||||
Cv2.Circle(overlayMat, coordX, coordY, 10, OpaqueScalar(Scalar.Red), Cv2.FILLED);
|
||||
ErrorTextHelper.DrawErrorText(overlayMat, new OpenCvSharp.Point(coordX, coordY),
|
||||
EErrorReason.Missing);
|
||||
}
|
||||
|
||||
c++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (wasError)
|
||||
{
|
||||
Cv2.DrawContours(overlayMat, new[] {blisterRectangle}, 0, OpaqueScalar(Scalar.Red),
|
||||
thickness: 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cv2.DrawContours(overlayMat, new[] {blisterRectangle}, 0, OpaqueScalar(Scalar.Pink),
|
||||
thickness: 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
inverted = overlayMat;
|
||||
sw2.Stop();
|
||||
Console.WriteLine("CVTime:" + sw2.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sw.Stop();
|
||||
Console.WriteLine("Time:" + sw.ElapsedMilliseconds);
|
||||
|
||||
inverted = Overlap(numbersOverlay, inverted);
|
||||
var final = inverted.Resize(framedImage.Size());
|
||||
final = Overlap(final,framedImage);
|
||||
|
||||
if (_learnedParameters.ImageWidth > 0)
|
||||
{
|
||||
float cut = _learnedParameters.ImageWidth;
|
||||
Cv2.Line(final, new OpenCvSharp.Point(cut, 0), new OpenCvSharp.Point(cut, 1408), Scalar.Red, 1);
|
||||
}
|
||||
|
||||
if(_learnedParameters.CandyParameters.Count==0)errorReason.Add(EErrorReason.NotLearned);
|
||||
|
||||
return new ProcessingResult(framedImage, final,
|
||||
_learnedParameters.CandyParameters.Count == 0 || wasError, (int) sw.ElapsedMilliseconds,
|
||||
errorReason)
|
||||
{
|
||||
BlisterRectangle = blisterRectangle.Select(x => new Point(x.X, x.Y)).ToList(),
|
||||
ErrorPoints = errorPoints
|
||||
};
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return new ProcessingResult(image, image,
|
||||
true, (int)sw.ElapsedMilliseconds,
|
||||
new List<EErrorReason>() {EErrorReason.Unknown})
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OpenCvSharp.Point[][] LastContours { get; set; }
|
||||
|
||||
public LearningParameters LearnedParameters => _learnedParameters;
|
||||
|
||||
private CandyParameter ClosestPoint(Point blisterCenter, int cX, int cY, int outerRadius,
|
||||
int innerRadius)
|
||||
{
|
||||
CandyParameter closest = null;
|
||||
double minDistance = double.MaxValue;
|
||||
foreach (CandyParameter parameter in _learnedParameters.CandyParameters)
|
||||
{
|
||||
var coordX = parameter.Position.X +
|
||||
(blisterCenter.X - _learnedParameters.BlisterPosition.X);
|
||||
var coordY = parameter.Position.Y +
|
||||
(blisterCenter.Y - _learnedParameters.BlisterPosition.Y);
|
||||
var distance = Distance(coordX - cX, coordY - cY);
|
||||
if ( distance< minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
closest = parameter;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
|
||||
}
|
||||
|
||||
private static double Distance(int x, int y)
|
||||
{
|
||||
return Math.Sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void ShowHistogram(Mat hist)
|
||||
{
|
||||
Mat render = new Mat(new Size(125, 125), MatType.CV_8UC3, Scalar.All(255));
|
||||
double minVal, maxVal;
|
||||
Cv2.MinMaxLoc(hist, out minVal, out maxVal);
|
||||
Scalar color = Scalar.All(100);
|
||||
// Scales and draws histogram
|
||||
hist = hist * (maxVal != 0 ? 125 / maxVal : 0.0);
|
||||
hist.GetArray(out float[] histArr);
|
||||
for (int j = 0; j < 125; ++j)
|
||||
{
|
||||
render.Rectangle(
|
||||
new OpenCvSharp.Point(j , render.Rows - (int)histArr[j]),
|
||||
new OpenCvSharp.Point(j + 1 , render.Rows),
|
||||
color,
|
||||
-1);
|
||||
}
|
||||
Cv2.ImShow("hist",render);
|
||||
Cv2.WaitKey(1);
|
||||
}
|
||||
|
||||
|
||||
public static double Map (double value, double fromSource, double toSource, double fromTarget, double toTarget)
|
||||
{
|
||||
return (value - fromSource) / (toSource - fromSource) * (toTarget - fromTarget) + fromTarget;
|
||||
}
|
||||
private static object _lockObject = new object();
|
||||
|
||||
private float _blisterAngle;
|
||||
private Point _blisterCenter;
|
||||
private int _blisterEdge;
|
||||
private Mat _resizedMat;
|
||||
private readonly string _configurationPath;
|
||||
|
||||
|
||||
private static Scalar OpaqueScalar(Scalar scalar)
|
||||
{
|
||||
return new Scalar(scalar.Val0, scalar.Val1, scalar.Val2, 255);
|
||||
}
|
||||
private static Mat CalcHist(OpenCvSharp.Point[][] contours, int i, Mat resizedMat,out float outerRadius,out float innerRadius)
|
||||
{
|
||||
var c = new Mat(new Size(512, 512), MatType.CV_8UC1,Scalar.Black);
|
||||
|
||||
Cv2.DrawContours(c, contours, i, Scalar.White, thickness: Cv2.FILLED);
|
||||
|
||||
|
||||
Cv2.MinEnclosingCircle(contours[i],out var center,out outerRadius);
|
||||
|
||||
var distance = c.DistanceTransform(DistanceTypes.L2, DistanceTransformMasks.Mask5);
|
||||
distance.MinMaxIdx(out _,out var innerRadiusD);
|
||||
innerRadius = (float) innerRadiusD;
|
||||
|
||||
|
||||
var hist = new Mat();
|
||||
Cv2.CalcHist(new[] {resizedMat}, new[] {0, 1, 2}, c, hist, 3, new[] {5, 5, 5},
|
||||
new[] {new Rangef(0, 256), new Rangef(0, 256), new Rangef(0, 256)});
|
||||
Cv2.Normalize(hist, hist);
|
||||
hist = hist.Reshape(1, 125, 1);
|
||||
|
||||
|
||||
//if (i == 22)
|
||||
//{
|
||||
// ShowHistogram(hist);
|
||||
//}
|
||||
|
||||
|
||||
return hist;
|
||||
}
|
||||
|
||||
private OpenCvSharp.Point[] FindBlister(Mat resBlister,out float blisterAngle,out Point center, out int blisterEdge)
|
||||
{
|
||||
var blisterMat = resBlister.Threshold(128, 255, ThresholdTypes.Binary);
|
||||
|
||||
var blisterContours = blisterMat.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxNone);
|
||||
var cnt = blisterContours.Length;
|
||||
int? blisterArea = null;
|
||||
blisterEdge = 0;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
|
||||
var area = Cv2.ContourArea(blisterContours[i]);
|
||||
if (area>5000)
|
||||
{
|
||||
var max= blisterContours[i].Max(x => x.X);
|
||||
if (max>blisterEdge)
|
||||
{
|
||||
blisterEdge = max;
|
||||
}
|
||||
var rect2 = Cv2.MinAreaRect(blisterContours[i]);
|
||||
var tmp = rect2.Angle;
|
||||
if (tmp > 15)
|
||||
{
|
||||
tmp = -(90 - tmp);
|
||||
}
|
||||
blisterAngle = tmp/180f*3.14f;
|
||||
|
||||
center = new Point((int)rect2.Center.X, (int)rect2.Center.Y);
|
||||
Console.WriteLine("blister angle:"+blisterAngle);
|
||||
//var c = blisterMat.CvtColor(ColorConversionCodes.GRAY2BGR);
|
||||
//Cv2.Rectangle(c,rect2.BoundingRect(),Scalar.Red);
|
||||
//Cv2.ImShow("test",c);
|
||||
//Cv2.WaitKey(0);
|
||||
return rect2.Points().Select(x=>new OpenCvSharp.Point(x.X,x.Y)).ToArray();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Blister not found");
|
||||
}
|
||||
|
||||
public static Bitmap SetImageOpacity(Bitmap image, float opacity)
|
||||
{
|
||||
try
|
||||
{
|
||||
//create a Bitmap the size of the image provided
|
||||
var bmp = new Bitmap(image.Width, image.Height);
|
||||
|
||||
//create a graphics object from the image
|
||||
using (var gfx = Graphics.FromImage(bmp))
|
||||
{
|
||||
//create a color matrix object
|
||||
var matrix = new ColorMatrix();
|
||||
|
||||
//set the opacity
|
||||
matrix.Matrix33 = opacity;
|
||||
|
||||
//create image attributes
|
||||
var attributes = new ImageAttributes();
|
||||
|
||||
//set the color(opacity) of the image
|
||||
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
|
||||
|
||||
//now draw the image
|
||||
gfx.DrawImage(image, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, image.Width, image.Height,
|
||||
GraphicsUnit.Pixel, attributes);
|
||||
}
|
||||
|
||||
return bmp;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//MessageBox.Show(ex.Message);
|
||||
throw ex;
|
||||
//return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Mat Overlap(Mat source1, Mat source2)
|
||||
{
|
||||
var target = new Mat(source2.Size(),source2.Type());
|
||||
Cv2.Split(source1, out Mat[] src2Channels);
|
||||
var alpha = src2Channels[3];
|
||||
source2.CopyTo(target);
|
||||
source1.CopyTo(target,alpha);
|
||||
return target;
|
||||
}
|
||||
|
||||
public int GetCameraWidth()
|
||||
{
|
||||
return _learnedParameters.ImageWidth;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public class HistogramRecipeConfiguration
|
||||
{
|
||||
public int SimilarityTolerance { get; set; } = 80;
|
||||
public int InnerTolerance { get; set; } = 40;
|
||||
public int OuterTolerance { get; set; } = 40;
|
||||
|
||||
public List<int> IgnoreCorrelation { get; set; } = new List<int>();
|
||||
public int MemoryBuffer { get; set; } = 5;
|
||||
}
|
||||
}
|
||||
13
Plugins/CandyboxPlugin/Recipe/LearningParameters.cs
Normal file
13
Plugins/CandyboxPlugin/Recipe/LearningParameters.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Point = System.Drawing.Point;
|
||||
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public class LearningParameters
|
||||
{
|
||||
public HistogramRecipeConfiguration Configuration { get; set; } = new HistogramRecipeConfiguration();
|
||||
public float BlisterAngle { get; set; } = 0f;
|
||||
public Point BlisterPosition { get; set; }
|
||||
public List<CandyParameter> CandyParameters { get; set; } = new List<CandyParameter>();
|
||||
public int ImageWidth { get; set; } = 1800;
|
||||
}
|
||||
}
|
||||
42
Plugins/CandyboxPlugin/Recipe/ProcessingResult.cs
Normal file
42
Plugins/CandyboxPlugin/Recipe/ProcessingResult.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using OpenCvSharp;
|
||||
using Point = System.Drawing.Point;
|
||||
|
||||
namespace CandyboxPlugin.Recipe
|
||||
{
|
||||
public class ProcessingResult
|
||||
{
|
||||
private readonly Mat _processedImage;
|
||||
public List<EErrorReason> ErrorReason { get; } =new List<EErrorReason>();
|
||||
|
||||
public ProcessingResult(Mat image, Mat processedImage, bool error, int elapsedMilliseconds)
|
||||
{
|
||||
Image = image;
|
||||
_processedImage = processedImage;
|
||||
Error = error;
|
||||
ErrorReason = new List<EErrorReason>();
|
||||
ElapsedMilliseconds = elapsedMilliseconds;
|
||||
}
|
||||
public ProcessingResult(Mat image, Mat processedImage, bool error, int elapsedMilliseconds,List<EErrorReason> errorReason):this(image, processedImage, error, elapsedMilliseconds)
|
||||
{
|
||||
ErrorReason = errorReason;
|
||||
}
|
||||
public Mat Image { get; }
|
||||
|
||||
public Mat ProcessedImage
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
return _processedImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Error { get; }
|
||||
|
||||
public int ElapsedMilliseconds { get; }
|
||||
public List<Point> BlisterRectangle { get; set; }
|
||||
public List<int> ErrorPoints { get; set; } = new List<int>();
|
||||
}
|
||||
}
|
||||
79
Plugins/CandyboxPlugin/Voronoi/Delaunay.cs
Normal file
79
Plugins/CandyboxPlugin/Voronoi/Delaunay.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
namespace CandyboxPlugin.Voronoi
|
||||
{
|
||||
public class DelaunayTriangulator
|
||||
{
|
||||
private double MaxX { get; set; }
|
||||
private double MaxY { get; set; }
|
||||
private IEnumerable<Triangle> border;
|
||||
public void GenerateBorder(double maxX, double maxY)
|
||||
{
|
||||
MaxX = maxX;
|
||||
MaxY = maxY;
|
||||
|
||||
// TODO make more beautiful
|
||||
var point0 = new VoronoiPoint(0, 0);
|
||||
var point1 = new VoronoiPoint(0, MaxY);
|
||||
var point2 = new VoronoiPoint(MaxX, MaxY);
|
||||
var point3 = new VoronoiPoint(MaxX, 0);
|
||||
var points = new List<VoronoiPoint>() { point0, point1, point2, point3 };
|
||||
var tri1 = new Triangle(point0, point1, point2);
|
||||
var tri2 = new Triangle(point0, point2, point3);
|
||||
border = new List<Triangle>() { tri1, tri2 };
|
||||
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<Triangle> BowyerWatson(IEnumerable<VoronoiPoint> points)
|
||||
{
|
||||
//var supraTriangle = GenerateSupraTriangle();
|
||||
var triangulation = new HashSet<Triangle>(border);
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
var badTriangles = FindBadTriangles(point, triangulation);
|
||||
var polygon = FindHoleBoundaries(badTriangles);
|
||||
|
||||
foreach (var triangle in badTriangles)
|
||||
{
|
||||
foreach (var vertex in triangle.Vertices)
|
||||
{
|
||||
vertex.AdjacentTriangles.Remove(triangle);
|
||||
}
|
||||
}
|
||||
triangulation.RemoveWhere(o => badTriangles.Contains(o));
|
||||
|
||||
foreach (var edge in polygon.Where(possibleEdge => possibleEdge.Point1 != point && possibleEdge.Point2 != point))
|
||||
{
|
||||
|
||||
var triangle = new Triangle(point, edge.Point1, edge.Point2);
|
||||
triangulation.Add(triangle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return triangulation;
|
||||
}
|
||||
|
||||
private List<Edge> FindHoleBoundaries(ISet<Triangle> badTriangles)
|
||||
{
|
||||
var edges = new List<Edge>();
|
||||
foreach (var triangle in badTriangles)
|
||||
{
|
||||
edges.Add(new Edge(triangle.Vertices[0], triangle.Vertices[1]));
|
||||
edges.Add(new Edge(triangle.Vertices[1], triangle.Vertices[2]));
|
||||
edges.Add(new Edge(triangle.Vertices[2], triangle.Vertices[0]));
|
||||
}
|
||||
var grouped = edges.GroupBy(o => o);
|
||||
var boundaryEdges = edges.GroupBy(o => o).Where(o => o.Count() == 1).Select(o => o.First());
|
||||
return boundaryEdges.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ISet<Triangle> FindBadTriangles(VoronoiPoint point, HashSet<Triangle> triangles)
|
||||
{
|
||||
var badTriangles = triangles.Where(o => o.IsPointInsideCircumcircle(point));
|
||||
return new HashSet<Triangle>(badTriangles);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Plugins/CandyboxPlugin/Voronoi/Edge.cs
Normal file
31
Plugins/CandyboxPlugin/Voronoi/Edge.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace CandyboxPlugin.Voronoi
|
||||
{
|
||||
public class Edge
|
||||
{
|
||||
public VoronoiPoint Point1 { get; }
|
||||
public VoronoiPoint Point2 { get; }
|
||||
|
||||
public Edge(VoronoiPoint point1, VoronoiPoint point2)
|
||||
{
|
||||
Point1 = point1;
|
||||
Point2 = point2;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
var edge = obj as Edge;
|
||||
|
||||
var samePoints = Point1 == edge.Point1 && Point2 == edge.Point2;
|
||||
var samePointsReversed = Point1 == edge.Point2 && Point2 == edge.Point1;
|
||||
return samePoints || samePointsReversed;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hCode = (int)Point1.X ^ (int)Point1.Y ^ (int)Point2.X ^ (int)Point2.Y;
|
||||
return hCode.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Plugins/CandyboxPlugin/Voronoi/Triangle.cs
Normal file
113
Plugins/CandyboxPlugin/Voronoi/Triangle.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
namespace CandyboxPlugin.Voronoi
|
||||
{
|
||||
public class Triangle
|
||||
{
|
||||
|
||||
public VoronoiPoint[] Vertices { get; } = new VoronoiPoint[3];
|
||||
public VoronoiPoint Circumcenter { get; private set; }
|
||||
public double RadiusSquared;
|
||||
|
||||
public IEnumerable<Triangle> TrianglesWithSharedEdge {
|
||||
get {
|
||||
var neighbors = new HashSet<Triangle>();
|
||||
foreach (var vertex in Vertices)
|
||||
{
|
||||
var trianglesWithSharedEdge = vertex.AdjacentTriangles.Where(o =>
|
||||
{
|
||||
return o != this && SharesEdgeWith(o);
|
||||
});
|
||||
neighbors.UnionWith(trianglesWithSharedEdge);
|
||||
}
|
||||
|
||||
return neighbors;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Triangle(VoronoiPoint point1, VoronoiPoint point2, VoronoiPoint point3)
|
||||
{
|
||||
// In theory this shouldn't happen, but it was at one point so this at least makes sure we're getting a
|
||||
// relatively easily-recognised error message, and provides a handy breakpoint for debugging.
|
||||
if (point1 == point2 || point1 == point3 || point2 == point3)
|
||||
{
|
||||
throw new ArgumentException("Must be 3 distinct points");
|
||||
}
|
||||
|
||||
if (!IsCounterClockwise(point1, point2, point3))
|
||||
{
|
||||
Vertices[0] = point1;
|
||||
Vertices[1] = point3;
|
||||
Vertices[2] = point2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vertices[0] = point1;
|
||||
Vertices[1] = point2;
|
||||
Vertices[2] = point3;
|
||||
}
|
||||
|
||||
Vertices[0].AdjacentTriangles.Add(this);
|
||||
Vertices[1].AdjacentTriangles.Add(this);
|
||||
Vertices[2].AdjacentTriangles.Add(this);
|
||||
UpdateCircumcircle();
|
||||
}
|
||||
|
||||
private void UpdateCircumcircle()
|
||||
{
|
||||
// https://codefound.wordpress.com/2013/02/21/how-to-compute-a-circumcircle/#more-58
|
||||
// https://en.wikipedia.org/wiki/Circumscribed_circle
|
||||
var p0 = Vertices[0];
|
||||
var p1 = Vertices[1];
|
||||
var p2 = Vertices[2];
|
||||
var dA = p0.X * p0.X + p0.Y * p0.Y;
|
||||
var dB = p1.X * p1.X + p1.Y * p1.Y;
|
||||
var dC = p2.X * p2.X + p2.Y * p2.Y;
|
||||
|
||||
var aux1 = (dA * (p2.Y - p1.Y) + dB * (p0.Y - p2.Y) + dC * (p1.Y - p0.Y));
|
||||
var aux2 = -(dA * (p2.X - p1.X) + dB * (p0.X - p2.X) + dC * (p1.X - p0.X));
|
||||
var div = (2 * (p0.X * (p2.Y - p1.Y) + p1.X * (p0.Y - p2.Y) + p2.X * (p1.Y - p0.Y)));
|
||||
|
||||
if (div == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
var center = new VoronoiPoint(aux1 / div, aux2 / div);
|
||||
Circumcenter = center;
|
||||
RadiusSquared = (center.X - p0.X) * (center.X - p0.X) + (center.Y - p0.Y) * (center.Y - p0.Y);
|
||||
}
|
||||
|
||||
private bool IsCounterClockwise(VoronoiPoint point1, VoronoiPoint point2, VoronoiPoint point3)
|
||||
{
|
||||
var result = (point2.X - point1.X) * (point3.Y - point1.Y) -
|
||||
(point3.X - point1.X) * (point2.Y - point1.Y);
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
public bool SharesEdgeWith(Triangle triangle)
|
||||
{
|
||||
var sharedVertices = Vertices.Where(o => triangle.Vertices.Contains(o)).Count();
|
||||
return sharedVertices == 2;
|
||||
}
|
||||
|
||||
public bool IsPointInsideCircumcircle(VoronoiPoint point)
|
||||
{
|
||||
var d_squared = (point.X - Circumcenter.X) * (point.X - Circumcenter.X) +
|
||||
(point.Y - Circumcenter.Y) * (point.Y - Circumcenter.Y);
|
||||
return d_squared < RadiusSquared;
|
||||
}
|
||||
|
||||
public bool IsInside(VoronoiPoint point)
|
||||
{
|
||||
var b1 = Sign(point, Vertices[0], Vertices[1]) < 0.0;
|
||||
var b2 = Sign(point, Vertices[1], Vertices[2]) < 0.0;
|
||||
var b3 = Sign(point, Vertices[2], Vertices[0]) < 0.0;
|
||||
return ((b1 == b2) && (b2 == b3));
|
||||
}
|
||||
|
||||
private double Sign(VoronoiPoint p1, VoronoiPoint p2, VoronoiPoint p3)
|
||||
{
|
||||
return (p1.X - p3.X) * (p2.Y - p3.Y) - (p2.X - p3.X) * (p1.Y - p3.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Plugins/CandyboxPlugin/Voronoi/Voronoi.cs
Normal file
23
Plugins/CandyboxPlugin/Voronoi/Voronoi.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace CandyboxPlugin.Voronoi
|
||||
{
|
||||
public class Voronoi
|
||||
{
|
||||
public static List<Edge> GenerateEdgesFromDelaunay(IEnumerable<Triangle> triangulation)
|
||||
{
|
||||
var voronoiEdges = new HashSet<Edge>();
|
||||
foreach (var triangle in triangulation)
|
||||
{
|
||||
foreach (var neighbor in triangle.TrianglesWithSharedEdge)
|
||||
{
|
||||
|
||||
var edge = new Edge(triangle.Circumcenter, neighbor.Circumcenter);
|
||||
voronoiEdges.Add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
return voronoiEdges.ToList();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
32
Plugins/CandyboxPlugin/Voronoi/VoronoiPoint.cs
Normal file
32
Plugins/CandyboxPlugin/Voronoi/VoronoiPoint.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace CandyboxPlugin.Voronoi
|
||||
{
|
||||
public class VoronoiPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Used only for generating a unique ID for each instance of this class that gets generated
|
||||
/// </summary>
|
||||
private static int _counter;
|
||||
|
||||
/// <summary>
|
||||
/// Used for identifying an instance of a class; can be useful in troubleshooting when geometry goes weird
|
||||
/// (e.g. when trying to identify when Triangle objects are being created with the same Point object twice)
|
||||
/// </summary>
|
||||
private readonly int _instanceId = _counter++;
|
||||
|
||||
public double X { get; }
|
||||
public double Y { get; }
|
||||
public HashSet<Triangle> AdjacentTriangles { get; } = new HashSet<Triangle>();
|
||||
|
||||
public VoronoiPoint(double x, double y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Simple way of seeing what's going on in the debugger when investigating weirdness
|
||||
return $"{nameof(VoronoiPoint)} {_instanceId} {X:0.##}@{Y:0.##}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 MiB |
BIN
Plugins/CandyboxPlugin/models/blisterresnet.h5
Normal file
BIN
Plugins/CandyboxPlugin/models/blisterresnet.h5
Normal file
Binary file not shown.
BIN
Plugins/CandyboxPlugin/models/blisterresnet.onnx
Normal file
BIN
Plugins/CandyboxPlugin/models/blisterresnet.onnx
Normal file
Binary file not shown.
BIN
Plugins/CandyboxPlugin/models/candyresnet.h5
Normal file
BIN
Plugins/CandyboxPlugin/models/candyresnet.h5
Normal file
Binary file not shown.
5
Plugins/CandyboxPlugin/models/convert.py
Normal file
5
Plugins/CandyboxPlugin/models/convert.py
Normal file
@@ -0,0 +1,5 @@
|
||||
import tensorflow as tf, tf2onnx
|
||||
model = tf.keras.models.load_model("model_transparent_blister_resnet_navy.h5", compile=False) # or build in code
|
||||
spec = (tf.TensorSpec([None, 128, 128, 3], tf.float32, name="input"),)
|
||||
onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=18)
|
||||
with open("model_transparent_blister_resnet_navy.onnx","wb") as f: f.write(onnx_model.SerializeToString())
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/CandyboxPlugin/models/model_transparent_candy_resnet.h5
Normal file
BIN
Plugins/CandyboxPlugin/models/model_transparent_candy_resnet.h5
Normal file
Binary file not shown.
Binary file not shown.
5053
Plugins/CandyboxPlugin/recipes/recipeR162729.json
Normal file
5053
Plugins/CandyboxPlugin/recipes/recipeR162729.json
Normal file
File diff suppressed because it is too large
Load Diff
15698
Plugins/CandyboxPlugin/recipes/recipeR162732.json
Normal file
15698
Plugins/CandyboxPlugin/recipes/recipeR162732.json
Normal file
File diff suppressed because it is too large
Load Diff
3728
Plugins/CandyboxPlugin/recipes/recipeR162819.json
Normal file
3728
Plugins/CandyboxPlugin/recipes/recipeR162819.json
Normal file
File diff suppressed because it is too large
Load Diff
3728
Plugins/CandyboxPlugin/recipes/recipeR162820.json
Normal file
3728
Plugins/CandyboxPlugin/recipes/recipeR162820.json
Normal file
File diff suppressed because it is too large
Load Diff
14876
Plugins/CandyboxPlugin/recipes/recipeR162822.json
Normal file
14876
Plugins/CandyboxPlugin/recipes/recipeR162822.json
Normal file
File diff suppressed because it is too large
Load Diff
39618
Plugins/CandyboxPlugin/recipes/recipeR162824.json
Normal file
39618
Plugins/CandyboxPlugin/recipes/recipeR162824.json
Normal file
File diff suppressed because it is too large
Load Diff
6113
Plugins/CandyboxPlugin/recipes/recipeR162825.json
Normal file
6113
Plugins/CandyboxPlugin/recipes/recipeR162825.json
Normal file
File diff suppressed because it is too large
Load Diff
3198
Plugins/CandyboxPlugin/recipes/recipeR162876.json
Normal file
3198
Plugins/CandyboxPlugin/recipes/recipeR162876.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace PackstrasseBarcodeReader
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace PralinenPLC;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
|
||||
namespace B24SiemensPlugin;
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using Ninject;
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Plugins;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace TestPlugin
|
||||
{
|
||||
@@ -31,6 +34,58 @@ namespace TestPlugin
|
||||
public void RegisterCameraModules(IKernel kernel, string cameraName)
|
||||
{
|
||||
kernel.RegisterModule<TestCameraPlugin>();
|
||||
|
||||
kernel.Bind<TestRecognitionControlSettings, ISettings>().ToConstant(new TestRecognitionControlSettings(cameraName));
|
||||
kernel.Rebind<IRecognitionControl>().To<TestImageProcessingControl>().InSingletonScope();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestRecognitionControlSettings : BaseRecognitionControlSettings
|
||||
{
|
||||
public TestRecognitionControlSettings(string cameraName) : base(cameraName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class TestImageProcessingControl : BaseRecognitionControl
|
||||
{
|
||||
public TestImageProcessingControl(TestRecognitionControlSettings settings, ILoadingService loadingService) : base(settings, loadingService)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<RecipeData> GetRecipesData()
|
||||
{
|
||||
return new List<RecipeData>()
|
||||
{
|
||||
new RecipeData()
|
||||
{
|
||||
Image = new Mat(100, 100, MatType.CV_8UC3, new Scalar(0, 0, 255)),
|
||||
RecipeName = "Hello"
|
||||
},
|
||||
new RecipeData()
|
||||
{
|
||||
Image = new Mat(100, 100, MatType.CV_8UC3, new Scalar(0, 0, 255)),
|
||||
RecipeName = "World"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Initialize(RecipeData currentRecipe)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void WarmUp()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override (Mat originalImage, Mat analysisImage, TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)?
|
||||
ProcessImage(CancellationToken token)
|
||||
{
|
||||
var img = new Mat(100, 100, MatType.CV_8UC3, new Scalar(0, 0, 255));
|
||||
return (img, img, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(5), new string[] { "TestError" });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
PythonModelAPI/converting to onnx.txt
Normal file
1
PythonModelAPI/converting to onnx.txt
Normal file
@@ -0,0 +1 @@
|
||||
https://chatgpt.com/share/68baf669-9cb4-8011-a10a-30607499f6a3
|
||||
@@ -4,7 +4,7 @@ using Inspectron.Camera.UEye;
|
||||
using Ninject;
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace Lindt.Colorballs.Duo.Utils;
|
||||
|
||||
|
||||
@@ -64,14 +64,13 @@ public static class Extensions
|
||||
|
||||
public static IKernel UsePlugins(this IKernel self)
|
||||
{
|
||||
TypeDescriptor.AddAttributes(typeof(List<PluginSettings.PluginFlag>), new TypeConverterAttribute(typeof(PluginFlagConverter)));
|
||||
self.Bind<PluginSettings, ISettings>().ToConstant(new PluginSettings());
|
||||
self.RegisterModule<PluginLoader>();
|
||||
self.Get<PluginLoader>().InitializeModule();
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
public static IKernel InitializeGlobalPlugins(this IKernel self)
|
||||
public static IKernel RegisterGlobalPlugins(this IKernel self)
|
||||
{
|
||||
var plugins = self.Get<PluginLoader>().Plugins;
|
||||
foreach (var plugin in plugins)
|
||||
@@ -89,7 +88,7 @@ public static class Extensions
|
||||
return self;
|
||||
}
|
||||
|
||||
public static IKernel InitializeCameraPlugins(this IChildKernel self, string cameraName)
|
||||
public static IKernel RegisterCameraPlugins(this IChildKernel self, string cameraName)
|
||||
{
|
||||
var plugins = self.Get<PluginLoader>().Plugins;
|
||||
foreach (var plugin in plugins)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace VisionBuilder.UI.Common.NullClasses;
|
||||
public class NoRecipeCreation: IRecipeCreationTool
|
||||
{
|
||||
public bool Enabled { get; set; } = false;
|
||||
public void CreateRecipe(string recipeName)
|
||||
public bool CreateRecipe(out string recipeName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -9,14 +9,9 @@ namespace VisionBuilder.UI.Common.Plugins;
|
||||
[ModulePriority(1000)]
|
||||
public class PluginLoader:IVisionBuilderModule
|
||||
{
|
||||
private readonly PluginSettings _settings;
|
||||
|
||||
|
||||
const string PLUGINS_DIRECTORY = "Plugins";
|
||||
public PluginLoader(PluginSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public List<IPlugin> Plugins { get; } = new List<IPlugin>();
|
||||
|
||||
@@ -31,6 +26,15 @@ public class PluginLoader:IVisionBuilderModule
|
||||
_isInitialized = true;
|
||||
Log.Information("Loading plugins...");
|
||||
var pluginsPath = Path.Combine("..", "Data", PLUGINS_DIRECTORY);
|
||||
|
||||
var enabledPluginsPath = Path.Combine(pluginsPath, "enabled_plugins.txt");
|
||||
if (!File.Exists(enabledPluginsPath))
|
||||
{
|
||||
Log.Warning("Enabled plugins file not found: {EnabledPluginsPath}. All discovered plugins will be disabled by default.", enabledPluginsPath);
|
||||
File.WriteAllText(enabledPluginsPath, "");
|
||||
}
|
||||
|
||||
List<string> enabledPlugins = File.ReadAllLines(enabledPluginsPath).ToList();
|
||||
Directory.CreateDirectory(pluginsPath);
|
||||
var pluginFolders = Directory.GetDirectories(pluginsPath);
|
||||
|
||||
@@ -38,13 +42,8 @@ public class PluginLoader:IVisionBuilderModule
|
||||
{
|
||||
var pluginName = Path.GetFileNameWithoutExtension(folder);
|
||||
|
||||
if (_settings.Plugins.All(p => p.Name != pluginName))
|
||||
{
|
||||
Log.Warning("Discovered new plugin:{PluginName}. Adding to settings.", pluginName);
|
||||
_settings.AddPlugin(new PluginSettings.PluginFlag { Name = pluginName, Enabled = false });
|
||||
}
|
||||
|
||||
if (_settings.Plugins.Where(p => p.Name == pluginName).All(x=>!x.Enabled))
|
||||
if (!enabledPlugins.Contains(pluginName))
|
||||
{
|
||||
Log.Information("Skipping plugin {PluginName} as it is not enabled in settings.", pluginName);
|
||||
continue;
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Inspectron.Settings;
|
||||
using VisionBuilder.UI.Common.Utils;
|
||||
|
||||
namespace VisionBuilder.UI.Common.Plugins;
|
||||
|
||||
public class PluginSettings:ISettings
|
||||
{
|
||||
private List<PluginFlag> _plugins = new List<PluginFlag>();
|
||||
|
||||
public class PluginFlag
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{(Enabled ? "" : "(Disabled)")}{Name}";
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPlugin(PluginFlag plugin)
|
||||
{
|
||||
if (_plugins.All(p => p.Name != plugin.Name))
|
||||
{
|
||||
_plugins.Add(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
public List<PluginFlag> Plugins
|
||||
{
|
||||
get => _plugins;
|
||||
set
|
||||
{
|
||||
// add unique only
|
||||
foreach (var plugin in value)
|
||||
{
|
||||
AddPlugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => this.Plugins,"Plugins",nameof(Plugins));
|
||||
}
|
||||
}
|
||||
|
||||
public class PluginFlagConverter : GeneralListTypeConverter<PluginSettings.PluginFlag>
|
||||
{
|
||||
protected override PluginSettings.PluginFlag ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string stringValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
return null;
|
||||
var parts = stringValue.Split(',');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
return new PluginSettings.PluginFlag
|
||||
{
|
||||
Name = parts[0].Trim(),
|
||||
Enabled = bool.Parse(parts[1].Trim())
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, PluginSettings.PluginFlag errorShortName)
|
||||
{
|
||||
return $"{errorShortName.Name}, {errorShortName.Enabled}";
|
||||
}
|
||||
}
|
||||
191
VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs
Normal file
191
VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using System.Diagnostics;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace VisionBuilder.UI.Common.Processing;
|
||||
|
||||
public abstract class BaseRecognitionControl: IRecognitionControl
|
||||
{
|
||||
private readonly BaseRecognitionControlSettings _settings;
|
||||
private readonly ILoadingService _loadingService;
|
||||
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _loopTask;
|
||||
|
||||
public BaseRecognitionControl(BaseRecognitionControlSettings settings, ILoadingService loadingService)
|
||||
{
|
||||
_settings = settings;
|
||||
_loadingService = loadingService;
|
||||
}
|
||||
|
||||
public abstract List<RecipeData> GetRecipesData();
|
||||
|
||||
|
||||
RecipeData _currentRecipe;
|
||||
|
||||
public void SetRecipe(RecipeData recipe)
|
||||
{
|
||||
// error if running
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot change recipe while recognition is running.");
|
||||
}
|
||||
_currentRecipe = recipe;
|
||||
}
|
||||
|
||||
public bool IsRunning { get; set; }
|
||||
public bool IsPaused { get; set; }
|
||||
|
||||
public RecipeData CurrentRecipe => _currentRecipe;
|
||||
|
||||
private DateTime _startTime;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
|
||||
if (_currentRecipe == null)
|
||||
{
|
||||
throw new InvalidOperationException("No recipe set.");
|
||||
}
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Recognition is already running.");
|
||||
}
|
||||
_loadingService.StartLoading($"Starting recipe {_currentRecipe.RecipeName} on {_settings.CameraName}...");
|
||||
Initialize(_currentRecipe);
|
||||
_startTime = DateTime.Now;
|
||||
IsRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
Log.Information($"Warming up");
|
||||
try
|
||||
{
|
||||
var swWarmup = Stopwatch.StartNew();
|
||||
WarmUp();
|
||||
swWarmup.Stop();
|
||||
Log.Information($"Warmup run took {swWarmup.ElapsedMilliseconds} ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error during warmup run");
|
||||
}
|
||||
|
||||
_loopTask = Task.Run(() => Loop(_cts.Token), _cts.Token);
|
||||
|
||||
SessionStarted?.Invoke(new SessionStartedEvent
|
||||
{
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
SessionStarted = _startTime,
|
||||
|
||||
});
|
||||
_loadingService.StopLoading($"Starting recipe {_currentRecipe.RecipeName} on {_settings.CameraName}...");
|
||||
|
||||
}
|
||||
|
||||
protected abstract void Initialize(RecipeData currentRecipe);
|
||||
|
||||
protected abstract void WarmUp();
|
||||
|
||||
protected abstract (Mat originalImage, Mat analysisImage, TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)? ProcessImage(CancellationToken token);
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsRunning)
|
||||
return;
|
||||
_loadingService.StartLoading($"Stopping {_settings.CameraName}");
|
||||
_cts?.Cancel();
|
||||
try
|
||||
{
|
||||
_loopTask?.Wait();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Ignore cancellation exceptions
|
||||
}
|
||||
catch (AggregateException ex) when (ex.Flatten().InnerExceptions.Any(x=>x is TaskCanceledException))
|
||||
{
|
||||
// Ignore cancellation exceptions
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error while stopping recognition loop");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRunning = false;
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
_loopTask = null;
|
||||
}
|
||||
|
||||
SessionEnded?.Invoke(new SessionEndedEvent
|
||||
{
|
||||
SessionEnded = DateTime.Now
|
||||
});
|
||||
_loadingService.StopLoading($"Stopping {_settings.CameraName}");
|
||||
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
IsPaused = true;
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
IsPaused = false;
|
||||
}
|
||||
|
||||
private async Task Loop(CancellationToken token)
|
||||
{
|
||||
// Start the recognition process using the Hawkeye library
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
if (IsPaused)
|
||||
{
|
||||
await Task.Delay(100, token);
|
||||
continue;
|
||||
}
|
||||
|
||||
var processed = ProcessImage(token);
|
||||
|
||||
if (processed == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (processed.Value.processingTime.Milliseconds < _settings.MinimumProcessingTime)
|
||||
{
|
||||
Thread.Sleep(_settings.MinimumProcessingTime - (int)processed.Value.processingTime.Milliseconds);
|
||||
}
|
||||
|
||||
|
||||
var processingResult = new ImageProcessedEvent()
|
||||
{
|
||||
SessionStart = _startTime,
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
AnalysisTime = processed.Value.processingTime - processed.Value.acquisitionTime,
|
||||
ErrorNames = processed.Value.errorNames.ToList(),
|
||||
HasError = processed.Value.errorNames.Length > 0,
|
||||
ImageSource = _settings.CameraName,
|
||||
ImageOriginal = processed.Value.originalImage,
|
||||
ImageAnalysis = processed.Value.analysisImage
|
||||
};
|
||||
|
||||
var swProcessed = Stopwatch.StartNew();
|
||||
ImageProcessed(processingResult);
|
||||
swProcessed.Stop();
|
||||
Log.Information($"ImageProcessed event handled in {swProcessed.ElapsedMilliseconds} ms");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<ImageProcessedEvent> ImageProcessed = delegate { };
|
||||
public event Action<SessionStartedEvent>? SessionStarted = delegate { };
|
||||
public event Action<SessionEndedEvent>? SessionEnded = delegate { };
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Inspectron.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.Common.Processing;
|
||||
|
||||
public abstract class BaseRecognitionControlSettings(string cameraName) : ISettings
|
||||
{
|
||||
public string CameraName { get; } = cameraName;
|
||||
|
||||
public int MinimumProcessingTime { get; set; }
|
||||
public string RecipeNameFilter { get; set; } = "*";
|
||||
|
||||
public virtual void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName + "/Hawkeye recognition", "Minimum processing time (ms)");
|
||||
settings.RegisterSimple(this, () => this.RecipeNameFilter, CameraName + "/Hawkeye recognition", nameof(RecipeNameFilter));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace VisionBuilder.UI.Common.RecipeProcessing;
|
||||
namespace VisionBuilder.UI.Common.Processing;
|
||||
|
||||
public interface IImageSource
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace VisionBuilder.UI.Common.RecipeProcessing;
|
||||
namespace VisionBuilder.UI.Common.Processing;
|
||||
|
||||
public interface ILoadingService
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace VisionBuilder.UI.Common.RecipeProcessing;
|
||||
namespace VisionBuilder.UI.Common.Processing;
|
||||
|
||||
public interface IRecognitionControl
|
||||
{
|
||||
|
||||
public bool IsRunning { get; }
|
||||
List<RecipeData> GetRecipesData();
|
||||
void SetRecipe(RecipeData recipe);
|
||||
void Start();
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
public interface IRecipeCreationTool
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public void CreateRecipe(string recipeName);
|
||||
public bool CreateRecipe(out string recipeName);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.Commands.Interfaces;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Runtime;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.IOCommander.Interfaces;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@ using System.Threading.Tasks;
|
||||
using Hawkeye.VisionBuilder.Workflow;
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
||||
using Hawkeye.VisionBuilder.Workflow.Operations;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
{
|
||||
public class HawkeyeRecognitionControl: IRecognitionControl
|
||||
public class HawkeyeRecognitionControl: BaseRecognitionControl
|
||||
{
|
||||
private readonly IImageSource _imageSource;
|
||||
private readonly HawkeyeRecognitionSettings _recognitionConfiguration;
|
||||
@@ -21,14 +22,14 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _loopTask;
|
||||
|
||||
public HawkeyeRecognitionControl(IImageSource imageSource, HawkeyeRecognitionSettings recognitionConfiguration, ILoadingService loadingService)
|
||||
public HawkeyeRecognitionControl(IImageSource imageSource, HawkeyeRecognitionSettings recognitionConfiguration, ILoadingService loadingService): base(recognitionConfiguration,loadingService)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
_recognitionConfiguration = recognitionConfiguration;
|
||||
_loadingService = loadingService;
|
||||
|
||||
}
|
||||
|
||||
public List<RecipeData> GetRecipesData()
|
||||
public override List<RecipeData> GetRecipesData()
|
||||
{
|
||||
_loadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
|
||||
try
|
||||
@@ -55,178 +56,58 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
}
|
||||
}
|
||||
|
||||
RecipeData _currentRecipe;
|
||||
|
||||
public void SetRecipe(RecipeData recipe)
|
||||
{
|
||||
// error if running
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot change recipe while recognition is running.");
|
||||
}
|
||||
_currentRecipe = recipe;
|
||||
}
|
||||
|
||||
public bool IsRunning { get; set; }
|
||||
public bool IsPaused { get; set; }
|
||||
|
||||
private DateTime _startTime;
|
||||
private WorkflowList _workflow;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
|
||||
if (_currentRecipe == null)
|
||||
|
||||
protected override void Initialize(RecipeData currentRecipe)
|
||||
{
|
||||
throw new InvalidOperationException("No recipe set.");
|
||||
_workflow = WorkflowList.LoadJSONFromFile("..\\Data\\Recipes\\" + currentRecipe.RecipeName + ".jhrcp");
|
||||
}
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Recognition is already running.");
|
||||
}
|
||||
_loadingService.StartLoading($"Starting recipe {_currentRecipe.RecipeName} on {_recognitionConfiguration.CameraName}...");
|
||||
_workflow = WorkflowList.LoadJSONFromFile("..\\Data\\Recipes\\" + _currentRecipe.RecipeName + ".jhrcp");
|
||||
_startTime = DateTime.Now;
|
||||
IsRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
Log.Information($"Warming up");
|
||||
try
|
||||
protected override void WarmUp()
|
||||
{
|
||||
var swWarmup = Stopwatch.StartNew();
|
||||
// warmup run
|
||||
_workflow.ImageSource = new EmptyImageSource();
|
||||
_workflow.Context = new Context();
|
||||
_workflow.Context.CancellationToken = CancellationToken.None;
|
||||
_workflow.Execute(); // Warmup run
|
||||
swWarmup.Stop();
|
||||
Log.Information($"Warmup run took {swWarmup.ElapsedMilliseconds} ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error during warmup run");
|
||||
_workflow.Execute();
|
||||
}
|
||||
|
||||
_loopTask = Task.Run(() => Loop(_cts.Token), _cts.Token);
|
||||
|
||||
SessionStarted?.Invoke(new SessionStartedEvent
|
||||
protected override (Mat originalImage, Mat analysisImage,TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)? ProcessImage(CancellationToken token)
|
||||
{
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
SessionStarted = _startTime,
|
||||
|
||||
});
|
||||
_loadingService.StopLoading($"Starting recipe {_currentRecipe.RecipeName} on {_recognitionConfiguration.CameraName}...");
|
||||
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsRunning)
|
||||
return ;
|
||||
_loadingService.StartLoading($"Stopping {_recognitionConfiguration.CameraName}");
|
||||
_cts?.Cancel();
|
||||
try
|
||||
{
|
||||
_loopTask?.Wait();
|
||||
}
|
||||
catch (AggregateException ex) when (ex.InnerExceptions.All(e => e is TaskCanceledException))
|
||||
{
|
||||
// Ignore cancellation exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRunning = false;
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
_loopTask = null;
|
||||
}
|
||||
|
||||
SessionEnded?.Invoke(new SessionEndedEvent
|
||||
{
|
||||
SessionEnded = DateTime.Now
|
||||
});
|
||||
_loadingService.StopLoading($"Stopping {_recognitionConfiguration.CameraName}");
|
||||
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
IsPaused= true;
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
IsPaused = false;
|
||||
}
|
||||
|
||||
private async Task Loop(CancellationToken token)
|
||||
{
|
||||
// Start the recognition process using the Hawkeye library
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
if (IsPaused)
|
||||
{
|
||||
await Task.Delay(100, token);
|
||||
continue;
|
||||
}
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
_workflow.Context.CancellationToken= token;
|
||||
_workflow.ImageSource= _imageSource;
|
||||
_workflow.Context.CancellationToken = token;
|
||||
_workflow.ImageSource = _imageSource;
|
||||
try
|
||||
{
|
||||
_workflow.Execute();
|
||||
}
|
||||
catch (Exception e) when(token.IsCancellationRequested)
|
||||
catch (Exception e) when (token.IsCancellationRequested)
|
||||
{
|
||||
sw.Stop();
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
|
||||
if (sw.ElapsedMilliseconds < _recognitionConfiguration.MinimumProcessingTime)
|
||||
{
|
||||
Thread.Sleep(_recognitionConfiguration.MinimumProcessingTime - (int)sw.ElapsedMilliseconds);
|
||||
}
|
||||
string[] errorNames = _workflow.Operations.Where(x => !x.Result).Select(x => x.Label).ToArray();
|
||||
|
||||
var annotated = _workflow.Context.ActiveImage.ImageData.Clone();
|
||||
var canvas = new OpenCVCanvas(annotated);
|
||||
_workflow.Context.GraphicsElements.ForEach(x => x.Draw(canvas));
|
||||
|
||||
TimeSpan cameraTime = TimeSpan.FromMilliseconds(0);
|
||||
var originalImage = _workflow.Context.LastCameraImage.ImageData;
|
||||
var analysisImage = _workflow.Context.ActiveImage.ImageData;
|
||||
|
||||
var cameraOperation = _workflow.Operations.FirstOrDefault(x => x is GetImageOperation);
|
||||
TimeSpan cameraTime = TimeSpan.FromMilliseconds(0);
|
||||
if (cameraOperation != null)
|
||||
{
|
||||
cameraTime=cameraOperation.ExecutionTime;
|
||||
cameraTime = cameraOperation.ExecutionTime;
|
||||
}
|
||||
|
||||
var processingResult = new ImageProcessedEvent()
|
||||
{
|
||||
SessionStart = _startTime,
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
AnalysisTime = sw.Elapsed- cameraTime,
|
||||
ErrorNames = errorNames.ToList(),
|
||||
HasError = errorNames.Length > 0,
|
||||
ImageSource = _recognitionConfiguration.CameraName,
|
||||
ImageOriginal = _workflow.Context.LastCameraImage.ImageData,
|
||||
ImageAnalysis = annotated
|
||||
};
|
||||
string[] errorNames = _workflow.Operations.Where(x => !x.Result).Select(x => x.Label).ToArray();
|
||||
|
||||
var swProcessed = Stopwatch.StartNew();
|
||||
ImageProcessed(processingResult);
|
||||
swProcessed.Stop();
|
||||
Log.Information($"ImageProcessed event handled in {swProcessed.ElapsedMilliseconds} ms");
|
||||
|
||||
|
||||
}
|
||||
return (originalImage, analysisImage, sw.Elapsed, cameraTime, errorNames);
|
||||
}
|
||||
|
||||
public event Action<ImageProcessedEvent> ImageProcessed = delegate{};
|
||||
public event Action<SessionStartedEvent>? SessionStarted = delegate { };
|
||||
public event Action<SessionEndedEvent>? SessionEnded = delegate { };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
using Inspectron.Settings;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
public class HawkeyeRecognitionSettings: ISettings
|
||||
public class HawkeyeRecognitionSettings(string cameraName) : BaseRecognitionControlSettings(cameraName)
|
||||
{
|
||||
public string CameraName { get; }
|
||||
|
||||
public HawkeyeRecognitionSettings(string cameraName)
|
||||
{
|
||||
CameraName = cameraName;
|
||||
}
|
||||
|
||||
public int MinimumProcessingTime { get; set; }
|
||||
public string RecipeNameFilter { get; set; } = "*";
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName+"/Hawkeye recognition", "Minimum processing time (ms)");
|
||||
settings.RegisterSimple(this, () => this.RecipeNameFilter, CameraName + "/Hawkeye recognition", nameof(RecipeNameFilter));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Ringbuffer;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using Ninject;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Statistics
|
||||
{
|
||||
@@ -41,6 +41,8 @@ namespace VisionBuilder.UI.Statistics
|
||||
|
||||
}
|
||||
|
||||
public string Metadata { get; set; } = "";
|
||||
|
||||
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
|
||||
{
|
||||
var endedAt = obj.SessionEnded;
|
||||
@@ -81,7 +83,7 @@ namespace VisionBuilder.UI.Statistics
|
||||
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, _startTime, _startTime,
|
||||
obj.RecipeName, cameraName: _settings.CameraName);
|
||||
_filePath = Path.Combine(_directoryPath, _fileName);
|
||||
var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName };
|
||||
var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName, _settings.MetadataColumnName };
|
||||
if (!Directory.Exists(Path.GetDirectoryName(_filePath)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
|
||||
@@ -99,7 +101,7 @@ namespace VisionBuilder.UI.Statistics
|
||||
|
||||
_csvStatistics.WriteLine(
|
||||
|
||||
[_startTime.ToString("d"), _startTime.ToString("T"), EMPTY_DATE, EMPTY_TIME, DateTime.Now.ToString("T"), _currentRecipe, defectName]
|
||||
[_startTime.ToString("d"), _startTime.ToString("T"), EMPTY_DATE, EMPTY_TIME, DateTime.Now.ToString("T"), _currentRecipe, defectName, Metadata]
|
||||
|
||||
);
|
||||
|
||||
|
||||
@@ -29,8 +29,9 @@ Specifies directory, in which MaxBad and MaxGood are applied.
|
||||
public string ErrorTimeColumnName { get; set; } = "Error time";
|
||||
public string ProductColumnName { get; set; } = "Product";
|
||||
public string ErrorColumnName { get; set; } = "Error";
|
||||
public string MetadataColumnName { get; set; } = "Meta";
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
public virtual void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this,()=>this.PathTemplate,CameraName+"/"+"Statistics",nameof(PathTemplate));
|
||||
settings.RegisterSimple(this,()=>this.StartDateColumnName,CameraName+"/"+"Statistics",nameof(StartDateColumnName));
|
||||
@@ -40,6 +41,7 @@ Specifies directory, in which MaxBad and MaxGood are applied.
|
||||
settings.RegisterSimple(this,()=>this.ErrorTimeColumnName,CameraName+"/"+"Statistics",nameof(ErrorTimeColumnName));
|
||||
settings.RegisterSimple(this,()=>this.ProductColumnName,CameraName+"/"+"Statistics",nameof(ProductColumnName));
|
||||
settings.RegisterSimple(this,()=>this.ErrorColumnName,CameraName+"/"+"Statistics",nameof(ErrorColumnName));
|
||||
settings.RegisterSimple(this,()=>this.MetadataColumnName,CameraName+"/"+"Statistics",nameof(MetadataColumnName));
|
||||
}
|
||||
|
||||
public static string PathTemplatePreview(object template)
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
using VisionBuilder.UI.Statistics;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
|
||||
public Form1(MainWindowVM mainWindowVm, IPasswordInputService passwordInputService, UIConfiguration uiConfiguration)
|
||||
{
|
||||
|
||||
_mainWindowVm = mainWindowVm;
|
||||
_passwordInputService = passwordInputService;
|
||||
_uiConfiguration = uiConfiguration;
|
||||
@@ -25,6 +26,7 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
singleCameraControl1.SetViewModel(mainWindowVm.SingleCameraVms[0]);
|
||||
Load += Form1_Load;
|
||||
_mainWindowVm.PropertyChanged += _mainWindowVm_PropertyChanged;
|
||||
|
||||
}
|
||||
|
||||
private void _mainWindowVm_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
@@ -76,5 +78,10 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
{
|
||||
_mainWindowVm.ToggleTestModeCommand.Execute(null);
|
||||
}
|
||||
|
||||
public object GetHandle()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Plugins;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
@@ -41,14 +42,17 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
InspectronSettings settings = new InspectronSettings("..\\Data\\Config");
|
||||
|
||||
var mainKernel = Common.VisionBuilder.CreateMainKernel(settings);
|
||||
|
||||
mainKernel.UsePlugins();
|
||||
|
||||
|
||||
mainKernel
|
||||
.UseConsole()
|
||||
.UseIOCommander([CAMERA1])
|
||||
.UseIOCommanderWindowsDebug()
|
||||
.UseIOCommanderVirtualInput()
|
||||
.UseWindowsServices()
|
||||
.UsePlugins()
|
||||
|
||||
.RegisterGlobalPlugins()
|
||||
;
|
||||
|
||||
mainKernel.Get<ILoadingService>().StartLoading("Camera initialization");
|
||||
@@ -65,6 +69,7 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
.UseRingbuffer(CAMERA1)
|
||||
.UseHawkeyeRecipes(CAMERA1)
|
||||
.UseCameraIOCommander(CAMERA1)
|
||||
.RegisterCameraPlugins(CAMERA1)
|
||||
;
|
||||
|
||||
|
||||
@@ -81,17 +86,10 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
mainKernel.InitializeModules();
|
||||
kernelCamera1.InitializeModules();
|
||||
|
||||
|
||||
settings.LoadSettings(); // load settings for plugins as they are loaded after modules
|
||||
mainKernel.InitializeGlobalPlugins();
|
||||
kernelCamera1.InitializeCameraPlugins(CAMERA1);
|
||||
|
||||
// initialize plugins the same way as modules(loaded modules are skipped)
|
||||
mainKernel.InitializeModules();
|
||||
kernelCamera1.InitializeModules();
|
||||
|
||||
mainKernel.Get<ILoadingService>().StopLoading("Camera initialization");
|
||||
|
||||
|
||||
|
||||
var mainWindowVm = mainKernel.Get<MainWindowVM>();
|
||||
mainWindowVm.SingleCameraVms = [
|
||||
kernelCamera1.Get<SingleCameraVM>()
|
||||
|
||||
@@ -103,7 +103,6 @@ namespace Inspectron.HawkEye.View
|
||||
btnNext.TabIndex = 4;
|
||||
btnNext.Text = "Next image";
|
||||
btnNext.UseVisualStyleBackColor = true;
|
||||
btnNext.Visible = false;
|
||||
btnNext.Click += btnNext_Click;
|
||||
//
|
||||
// btnPrev
|
||||
@@ -122,7 +121,6 @@ namespace Inspectron.HawkEye.View
|
||||
btnPrev.TabIndex = 5;
|
||||
btnPrev.Text = "Previous image";
|
||||
btnPrev.UseVisualStyleBackColor = true;
|
||||
btnPrev.Visible = false;
|
||||
btnPrev.Click += btnPrev_Click;
|
||||
//
|
||||
// lblErrName
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace Inspectron.HawkEye.View
|
||||
|
||||
_previewVm.PropertyChanged += PreviewVmOnPropertyChanged;
|
||||
btnLearnThis.Visible = _previewVm.LearnButtonVisible;
|
||||
btnPrev.Visible = _previewVm.IsPreviousImageEnabled;
|
||||
btnNext.Visible = _previewVm.IsNextImageEnabled;
|
||||
btnPrev.Enabled = _previewVm.IsPreviousImageEnabled;
|
||||
btnNext.Enabled = _previewVm.IsNextImageEnabled;
|
||||
pictureBox1.Image = _previewVm.ImageAnalysis?.ToBitmap();
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace Inspectron.HawkEye.View
|
||||
btnLearnThis.Visible = _previewVm.LearnButtonVisible;
|
||||
break;
|
||||
case nameof(ErrorPreviewVM.IsPreviousImageEnabled):
|
||||
btnPrev.Visible = _previewVm.IsPreviousImageEnabled;
|
||||
btnPrev.Enabled = _previewVm.IsPreviousImageEnabled;
|
||||
break;
|
||||
case nameof(ErrorPreviewVM.IsNextImageEnabled):
|
||||
btnNext.Visible = _previewVm.IsNextImageEnabled;
|
||||
btnNext.Enabled = _previewVm.IsNextImageEnabled;
|
||||
break;
|
||||
case nameof(ErrorPreviewVM.ImageName):
|
||||
this.lblErrName.Text = _previewVm.ImageName;
|
||||
@@ -50,7 +50,6 @@ namespace Inspectron.HawkEye.View
|
||||
public ImagePreview()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
|
||||
private void materialRaisedButton1_Click(object sender, EventArgs e)
|
||||
@@ -61,7 +60,6 @@ namespace Inspectron.HawkEye.View
|
||||
private void btnLearnThis_Click(object sender, EventArgs e)
|
||||
{
|
||||
_previewVm.Learn();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnPrev_Click(object sender, EventArgs e)
|
||||
@@ -73,5 +71,7 @@ namespace Inspectron.HawkEye.View
|
||||
{
|
||||
_previewVm.NextImage();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +85,12 @@ namespace VisionBuilder.UI.Windows.Dialogs
|
||||
|
||||
private void btnCreateNewRecipe_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MaterialInputBox.Prompt("Create new recipe", "Recipe name", out var recipeName)==DialogResult.OK)
|
||||
if (_recipeCreationTool.CreateRecipe(out var recipeName))
|
||||
{
|
||||
_recipeCreationTool.CreateRecipe(recipeName);
|
||||
_recipeSelectionVm.SelectedRecipe = new RecipeData() { RecipeName = recipeName };
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Ninject;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.Services;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
using VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
@@ -13,10 +13,13 @@ public class WindowsImagePreviewService: IImagePreviewService
|
||||
|
||||
}
|
||||
|
||||
public ImagePreview? CurrentWindow { get; private set; }
|
||||
public void ShowImagePreview(ErrorPreviewVM errorPreviewVm)
|
||||
{
|
||||
var window = new ImagePreview();
|
||||
window.SetViewModel(errorPreviewVm);
|
||||
CurrentWindow = window;
|
||||
window.ShowDialog();
|
||||
CurrentWindow = null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using MaterialSkin.Controls;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaterialSkin.Core", "framework\MaterialSkin.Core\MaterialSkin.Core.csproj", "{D5FD2E9D-DA4F-1343-47E0-FBC473A149BC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Windows.Uno", "VisionBuilder.UI.Windows.Test\VisionBuilder.UI.Windows.Uno.csproj", "{7697A266-A721-4D74-9C5C-0D4F2F6BBF68}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1} = {31C47198-DEC2-4073-A0C2-220549502CD1}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inspectron.Settings", "framework\Inspectron.Settings\Inspectron.Settings.csproj", "{E286CE4C-B68A-94B6-F477-0DBF42358009}"
|
||||
EndProject
|
||||
@@ -263,7 +266,7 @@ Global
|
||||
{AD1A176C-B19F-4E60-9935-728722BAE3C1} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}
|
||||
{DB20082B-60E8-D623-3F3A-04612A929CD6} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{52D6A9AE-D0F1-4C52-B688-E0219169E179} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{F7D39916-489A-3583-09A0-175AE82D08B7} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}
|
||||
{F7D39916-489A-3583-09A0-175AE82D08B7} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F} = {D05689E3-04C6-4E3B-ACA7-3F4507CED4CC}
|
||||
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||
{B4949493-C2CD-4786-860D-DAACD39A662D} = {9FA47F4A-8F44-4F55-B6F7-99C962E9F18F}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user