458 lines
17 KiB
C#
458 lines
17 KiB
C#
using Compunet.YoloV8;
|
|
using Hawkeye.VisionBuilder.Features.ColorProgileGenerator;
|
|
using Hawkeye.VisionBuilder.Features.ImagePreview;
|
|
using Hawkeye.VisionBuilder.Features.ImagesStrip;
|
|
using Hawkeye.VisionBuilder.Features.ToolEditor;
|
|
using Hawkeye.VisionBuilder.Features.VisionSteps;
|
|
using Hawkeye.VisionBuilder.Features.Yolo;
|
|
using Hawkeye.VisionBuilder.Panels;
|
|
using Hawkeye.VisionBuilder.Workflow;
|
|
using Hawkeye.VisionBuilder.Workflow.ColorStudioClasses;
|
|
using Hawkeye.VisionBuilder.Workflow.Configuration;
|
|
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
|
using Hawkeye.VisionBuilder.Workflow.Operations.AI;
|
|
using Hawkeye.VisionBuilder.Workflow.Operations.Filters;
|
|
using Hawkeye.VisionBuilder.Workflow.Operations.Memory;
|
|
|
|
using Inspectron.Settings.WindowsWizard;
|
|
using Inspectron.Settings.WindowsWizard.Controls;
|
|
using Microsoft.WindowsAPICodePack.Dialogs;
|
|
using OpenCvSharp;
|
|
using OpenCvSharp.Extensions;
|
|
using System.Windows.Forms;
|
|
using Hawkeye.VisionBuilder.Features.ImageStatistics;
|
|
using Size = OpenCvSharp.Size;
|
|
|
|
namespace Hawkeye.VisionBuilder
|
|
{
|
|
public partial class MainWindow : Form
|
|
{
|
|
private readonly WorkflowList _workflowList;
|
|
private readonly OperationDiscoveryService _discoveryService;
|
|
private readonly ToolConfigurationPanel _toolConfigurationPanel;
|
|
private readonly ImagePreviewPanel _previewPanel;
|
|
private readonly VisionStepsPanel _visionStepsPanel;
|
|
private ImageStripPanel _imagesStripPanel;
|
|
public MainWindow(WorkflowList workflowList, OperationDiscoveryService discoveryService)
|
|
{
|
|
_workflowList = workflowList;
|
|
_discoveryService = discoveryService;
|
|
InitializeComponent();
|
|
|
|
|
|
{
|
|
_previewPanel = new ImagePreviewPanel();
|
|
_previewPanel.MdiParent = this;
|
|
_previewPanel.Show();
|
|
}
|
|
{
|
|
_visionStepsPanel = new VisionStepsPanel(workflowList, discoveryService, _previewPanel);
|
|
_visionStepsPanel.MdiParent = this;
|
|
_visionStepsPanel.Show();
|
|
}
|
|
{
|
|
_toolConfigurationPanel = new ToolConfigurationPanel(workflowList, _visionStepsPanel);
|
|
_toolConfigurationPanel.MdiParent = this;
|
|
_toolConfigurationPanel.Show();
|
|
}
|
|
{
|
|
ReinitCameraPanel(workflowList);
|
|
}
|
|
_visionStepsPanel.OperationSelected += operation =>
|
|
{
|
|
_toolConfigurationPanel.SetOperation(operation);
|
|
|
|
};
|
|
|
|
//var codeEditor = new ScriptEditorPanel();
|
|
//codeEditor.MdiParent = this;
|
|
//codeEditor.Show();
|
|
|
|
|
|
}
|
|
|
|
private void ReinitCameraPanel(WorkflowList workflowList)
|
|
{
|
|
if (_imagesStripPanel != null)
|
|
{
|
|
_imagesStripPanel.Close();
|
|
_imagesStripPanel.Dispose();
|
|
_imagesStripPanel = null;
|
|
}
|
|
|
|
|
|
|
|
|
|
_imagesStripPanel = new ImageStripPanel();
|
|
|
|
_workflowList.ImageSource= _imagesStripPanel;
|
|
|
|
_imagesStripPanel.ImageSelected += data =>
|
|
{
|
|
if (_imagesStripPanel is ImageStripPanel imagesStripPanel)
|
|
{
|
|
if (imagesStripPanel.IsExecutingWorkflow)
|
|
{
|
|
_visionStepsPanel.ExecuteWorkflow();
|
|
var result = _visionStepsPanel.WorkflowList.Operations.All(x => x.Result == true);
|
|
ProcessResult(result);
|
|
(_imagesStripPanel as ImageStripPanel).InformImageProcessed(result);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_visionStepsPanel.ProcessRowSelection();
|
|
var selectedOperation = _visionStepsPanel.GetSelectedOperation();
|
|
if (selectedOperation != null)
|
|
{
|
|
ProcessResult(selectedOperation.Result);
|
|
(_imagesStripPanel as ImageStripPanel).InformImageProcessed(selectedOperation.Result);
|
|
}
|
|
|
|
};
|
|
if(Directory.Exists(_workflowList.Configuration.EmulationPath))
|
|
_imagesStripPanel.LoadImages(_workflowList.Configuration.EmulationPath);
|
|
|
|
_imagesStripPanel.MdiParent = this;
|
|
_imagesStripPanel.Show();
|
|
}
|
|
|
|
string _saveErrorDirectory = null;
|
|
void ProcessResult(bool result)
|
|
{
|
|
if (_statisticsDialog != null)
|
|
{
|
|
if (result)
|
|
{
|
|
_statisticsDialog.AddGoodImage((_imagesStripPanel as ImageStripPanel).GetImagePath());
|
|
}
|
|
else
|
|
{
|
|
_statisticsDialog.AddBadImage((_imagesStripPanel as ImageStripPanel).GetImagePath(), _visionStepsPanel.WorkflowList.Operations.Where(x=>x.Result==false).Select(x=>x.Label).ToArray());
|
|
}
|
|
}
|
|
|
|
if (!copyOnStopToolStripMenuItem.Checked) return;
|
|
var shouldSaveOnError = (_imagesStripPanel as ImageStripPanel).StopOnErrors;
|
|
var shouldSaveOnNoError = (_imagesStripPanel as ImageStripPanel).StopOnNoErrors;
|
|
if ((!result && shouldSaveOnError) || (result && shouldSaveOnNoError))
|
|
{
|
|
var resultFile = Path.Combine(_saveErrorDirectory, Path.GetFileName((_imagesStripPanel as ImageStripPanel).GetImagePath()));
|
|
if (!File.Exists(resultFile))
|
|
{
|
|
File.Copy((_imagesStripPanel as ImageStripPanel).GetImagePath(), Path.Combine(_saveErrorDirectory, Path.GetFileName((_imagesStripPanel as ImageStripPanel).GetImagePath())));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
protected override void OnShown(EventArgs e)
|
|
{
|
|
base.OnShown(e);
|
|
WindowState= FormWindowState.Maximized;
|
|
Application.DoEvents();
|
|
OrganizeWindows(null, null);
|
|
_previewPanel.Fit();
|
|
}
|
|
|
|
private void OrganizeWindows(object sender, EventArgs e)
|
|
{
|
|
_previewPanel.Left = 0;
|
|
_previewPanel.Top = 0;
|
|
_previewPanel.Width = this.ClientSize.Width / 2;
|
|
_previewPanel.Height = (this.ClientSize.Height - 40) - 30 - _imagesStripPanel.Height;
|
|
|
|
_imagesStripPanel.Top = _previewPanel.Height;
|
|
_imagesStripPanel.Left = 0;
|
|
_imagesStripPanel.Width = _previewPanel.Width;
|
|
|
|
|
|
_toolConfigurationPanel.Top = 0;
|
|
_toolConfigurationPanel.Left = this.ClientSize.Width / 2;
|
|
_toolConfigurationPanel.Width = this.ClientSize.Width / 2 - 10;
|
|
_toolConfigurationPanel.Height = (this.ClientSize.Height - 40) / 2;
|
|
|
|
_visionStepsPanel.Top = (this.ClientSize.Height - 40) / 2;
|
|
_visionStepsPanel.Left = this.ClientSize.Width / 2;
|
|
_visionStepsPanel.Width = this.ClientSize.Width / 2 - 10;
|
|
_visionStepsPanel.Height = (this.ClientSize.Height - 40) / 2 - 30;
|
|
|
|
}
|
|
|
|
private void loadRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
OpenFileDialog dialog = new OpenFileDialog();
|
|
dialog.Filter = "Suported types|*.jhrcp;*.hrcp";
|
|
PrepareDialog(dialog);
|
|
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
var path = dialog.FileName;
|
|
var ext = Path.GetExtension(path);
|
|
if (ext == ".jhrcp")
|
|
{
|
|
LoadRecipeJSON(path);
|
|
}
|
|
else if (ext == ".hrcp")
|
|
{
|
|
LoadRecipeBinary(path);
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("Unsupported file format. Please use .hrcp or .jhrcp files.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void PrepareDialog(FileDialog dialog)
|
|
{
|
|
if (_loadedPath != null)
|
|
{
|
|
dialog.InitialDirectory = Path.GetDirectoryName(Path.GetFullPath(_loadedPath));
|
|
dialog.FileName = Path.GetFileNameWithoutExtension(_loadedPath);
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory(Path.GetFullPath(@"..\Data\Recipes"));
|
|
dialog.InitialDirectory = Path.GetFullPath(@"..\Data\Recipes");
|
|
}
|
|
}
|
|
|
|
private string _loadedPath = null;
|
|
|
|
|
|
public void LoadRecipeBinary(string path)
|
|
{
|
|
_loadedPath = path;
|
|
var bytes = File.ReadAllBytes(path);
|
|
MemoryStream stream = new MemoryStream(bytes);
|
|
BinaryReader reader = new BinaryReader(stream);
|
|
|
|
|
|
|
|
_workflowList.Load(reader, _discoveryService);
|
|
ReinitCameraPanel(_workflowList);
|
|
OrganizeWindows(null, null);
|
|
_visionStepsPanel.RefreshWorkflow();
|
|
}
|
|
|
|
public void LoadRecipeJSON(string path)
|
|
{
|
|
_loadedPath = path;
|
|
_workflowList.LoadJSON(path,_discoveryService);
|
|
ReinitCameraPanel(_workflowList);
|
|
OrganizeWindows(null, null);
|
|
_visionStepsPanel.RefreshWorkflow();
|
|
|
|
}
|
|
|
|
private void _workflowList_PythonMissing()
|
|
{
|
|
SetupConfig();
|
|
|
|
}
|
|
|
|
private void saveRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFileDialog dialog = new SaveFileDialog();
|
|
dialog.Filter = "*.jhrcp|*.jhrcp|*.hrcp|*.hrcp";
|
|
PrepareDialog(dialog);
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
var ext = Path.GetExtension(dialog.FileName);
|
|
if (ext == ".jhrcp")
|
|
{
|
|
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage?.ImageData;
|
|
_workflowList.SaveJSON(dialog.FileName);
|
|
}
|
|
else
|
|
{
|
|
SaveBinary(dialog.FileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SaveBinary(string path)
|
|
{
|
|
MemoryStream ms = new MemoryStream();
|
|
BinaryWriter bw = new BinaryWriter(ms);
|
|
|
|
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage.ImageData;
|
|
_workflowList.Save(bw);
|
|
ms.Close();
|
|
File.WriteAllBytes(path, ms.ToArray());
|
|
}
|
|
|
|
private void toolBtnConfiguration_Click(object sender, EventArgs e)
|
|
{
|
|
SetupConfig();
|
|
ReinitCameraPanel(_workflowList);
|
|
OrganizeWindows(null, null);
|
|
}
|
|
|
|
private void SetupConfig()
|
|
{
|
|
SetupPage.Begin("Camera", "Configuration")
|
|
.AddItem<EnumEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.RuntimeCameraType))
|
|
.AddItem<EnumEditor>(_workflowList.Configuration,
|
|
nameof(_workflowList.Configuration.DevelopmentCameraType))
|
|
.AddItem<PathEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.EmulationPath),
|
|
editor => editor.FolderPick = true)
|
|
.Next("Recipe selection")
|
|
.AddItem<CheckEditor>(_workflowList.Configuration.RecipeSelectionConfig, nameof(RecipeSelectionConfig.SerialEnabled))
|
|
.AddItem<StringEditor>(_workflowList.Configuration.RecipeSelectionConfig, nameof(RecipeSelectionConfig.ComPort))
|
|
.Next("Outputs")
|
|
.AddItem<EnumEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.Outputs))
|
|
.AddItem<NumberEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.ResultPin))
|
|
.AddItem<StringEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.SerialPort))
|
|
.AddItem<NumberEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.Delay), editor => editor.Max = 1000)
|
|
.Next("Python")
|
|
.AddItem<PathEditor>(_workflowList.Configuration, nameof(_workflowList.Configuration.PythonPath),
|
|
editor => editor.FolderPick = false)
|
|
.Next("Bad tweak")
|
|
.AddItem<CheckEditor>(_workflowList.Configuration.BadTweakLearning, nameof(BadTweakLearning.Enabled))
|
|
.AddItem<NumberEditor>(_workflowList.Configuration.BadTweakLearning, nameof(BadTweakLearning.ThresholdLevel),
|
|
editor =>
|
|
{
|
|
editor.Min = 0;
|
|
editor.Max = 255;
|
|
})
|
|
.Next("Statistics")
|
|
.ShowSetupDialog();
|
|
}
|
|
|
|
private void generateFromColorProfileToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
var tool = new ColorProfileSelection(_workflowList);
|
|
tool.ShowDialog();
|
|
_visionStepsPanel.RefreshWorkflow();
|
|
}
|
|
|
|
private void updateAllToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
var dialog = new OpenFileDialog();
|
|
dialog.Filter = "*.colorprofile|*.colorprofile";
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
var path = dialog.FileName;
|
|
var operations = _workflowList.Operations;
|
|
foreach (var operation in operations)
|
|
{
|
|
if (operation is ColorProfileOperation colorProfileOperation)
|
|
{
|
|
colorProfileOperation.ColorProfilePath.Path = path;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void copyPathToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (_imagesStripPanel is not ImageStripPanel panel) return;
|
|
var imagePath = panel.GetImagePath();
|
|
if (imagePath == null) return;
|
|
Clipboard.SetText(imagePath);
|
|
}
|
|
|
|
private void copyOnErrorToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
FolderBrowserDialog dialog = new FolderBrowserDialog();
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
_saveErrorDirectory = dialog.SelectedPath;
|
|
return;
|
|
}
|
|
copyOnStopToolStripMenuItem.Checked = false;
|
|
}
|
|
|
|
private void generateFromYoloToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
// pick yolo file
|
|
OpenFileDialog dialog = new OpenFileDialog();
|
|
dialog.Filter = "*.onnx|*.onnx";
|
|
dialog.InitialDirectory = Path.GetFullPath(@"..\Data\Models");
|
|
if (dialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
var path = dialog.FileName;
|
|
var predictor = YoloV8Predictor.Create(path);
|
|
var classes = predictor.Metadata.Names;
|
|
foreach (var cls in classes)
|
|
{
|
|
{
|
|
var operation = new YoloPickDetected();
|
|
operation.Label = cls.Name;
|
|
operation.TypeId = cls.Id + 1;
|
|
_workflowList.Operations.Add(operation);
|
|
}
|
|
}
|
|
_visionStepsPanel.RefreshWorkflow();
|
|
}
|
|
}
|
|
|
|
private void massSetAttributeToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
AttributeSetter setter = new AttributeSetter(_workflowList);
|
|
if (setter.ShowDialog() == DialogResult.OK)
|
|
{
|
|
_visionStepsPanel.RefreshWorkflow();
|
|
}
|
|
}
|
|
|
|
private string? _saveAsPath = null;
|
|
|
|
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (_imagesStripPanel is not ImageStripPanel panel) return;
|
|
|
|
var path = GetSaveAsPath();
|
|
if (path == null) return;
|
|
var imagePath = panel.GetImagePath();
|
|
if (imagePath == null) return;
|
|
var bytes = File.ReadAllBytes(imagePath);
|
|
File.WriteAllBytes(Path.Combine(path, Path.GetFileName(imagePath)), bytes);
|
|
}
|
|
|
|
string GetSaveAsPath()
|
|
{
|
|
if (_saveAsPath == null)
|
|
{
|
|
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
|
|
dialog.IsFolderPicker = true;
|
|
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
|
|
{
|
|
_saveAsPath = dialog.FileName;
|
|
}
|
|
}
|
|
return _saveAsPath;
|
|
}
|
|
|
|
ImageStatisticsDialog _statisticsDialog = null;
|
|
private void imageStatisticsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (_statisticsDialog!=null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_statisticsDialog = new ImageStatisticsDialog();
|
|
_statisticsDialog.Show(this);
|
|
_statisticsDialog.FormClosed +=
|
|
(s, args) =>
|
|
{
|
|
_statisticsDialog = null;
|
|
};
|
|
_statisticsDialog.FileSelected += _statisticsDialog_FileSelected;
|
|
}
|
|
|
|
private void _statisticsDialog_FileSelected(string file)
|
|
{
|
|
(_imagesStripPanel as ImageStripPanel).SelectImage(file);
|
|
}
|
|
}
|
|
} |