Files
HawkeyeVision/Hawkeye.VisionBuilder/MainWindow.cs
2025-08-19 12:45:50 +02:00

454 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);
OrganizeWindows(null, null);
}
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.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);
}
}
}