before candybox editor update

This commit is contained in:
meelstorm
2025-09-16 10:42:43 +02:00
parent e5746ef766
commit dc12b3e51a
103 changed files with 96379 additions and 343 deletions

View File

@@ -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>

View File

@@ -1,7 +0,0 @@
namespace CandyboxPlugin
{
public class Class1
{
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}
}
}

View 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>

View 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;
}
}

View 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;
}
}
}

View 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>

View 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
}
}

View 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);
}
}
}

View 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>

View 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)
};
}
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

View 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");
}
}
}

View File

@@ -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.");
}
}

View File

@@ -0,0 +1,7 @@
namespace CandyboxPlugin.Modules.Barcode;
public enum EReaderState
{
ReadingRecipe,
ReadingAUF
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View File

@@ -0,0 +1,10 @@
using VisionBuilder.UI.Common.Processing;
namespace CandyboxPlugin.Module;
public class CandyboxRecognitionControlSettings : BaseRecognitionControlSettings
{
public CandyboxRecognitionControlSettings(string cameraName) : base(cameraName)
{
}
}

View 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>();
}
}
}

View 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)
};
}
}
}

View 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>();
}
}

View File

@@ -0,0 +1,13 @@
namespace CandyboxPlugin.Recipe
{
public enum EErrorReason
{
Unknown,
Missing,
WrongType,
Misposition,
WrongSize,
NotLearned,
Good
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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>();
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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())

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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
{

View File

@@ -1,5 +1,5 @@
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.RecipeProcessing;
using VisionBuilder.UI.Common.Processing;
namespace PralinenPLC;

View File

@@ -1,5 +1,4 @@
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.RecipeProcessing;
namespace B24SiemensPlugin;

View File

@@ -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" });
}
}
}