check point
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hawkeye.VisionBuilder.Workflow;
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
{
|
||||
public class HawkeyeRecognitionControl: IRecognitionControl
|
||||
{
|
||||
private readonly IImageSource _imageSource;
|
||||
private readonly HawkeyeRecognitionSettings _recognitionConfiguration;
|
||||
private readonly ILoadingService _loadingService;
|
||||
|
||||
private CancellationTokenSource _cts;
|
||||
private Task _loopTask;
|
||||
|
||||
public HawkeyeRecognitionControl(IImageSource imageSource, HawkeyeRecognitionSettings recognitionConfiguration, ILoadingService loadingService)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
_recognitionConfiguration = recognitionConfiguration;
|
||||
_loadingService = loadingService;
|
||||
}
|
||||
|
||||
public string GetCameraName()
|
||||
{
|
||||
return _imageSource.GetName();
|
||||
}
|
||||
|
||||
public List<RecipeData> GetRecipesData()
|
||||
{
|
||||
_loadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
|
||||
try
|
||||
{
|
||||
var files = WorkflowRecipeHelper.ListRecipeFiles();
|
||||
List<RecipeData> recipes = new List<RecipeData>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
var workFlow = WorkflowList.LoadFromFile(file);
|
||||
|
||||
var recipe = new RecipeData
|
||||
{
|
||||
RecipeName = Path.GetFileNameWithoutExtension(file),
|
||||
Image = workFlow.RecipeImage
|
||||
};
|
||||
recipes.Add(recipe);
|
||||
}
|
||||
|
||||
return recipes;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loadingService.StopLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
|
||||
}
|
||||
}
|
||||
|
||||
RecipeData _currentRecipe;
|
||||
|
||||
public void SetRecipe(RecipeData recipe)
|
||||
{
|
||||
// error if running
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot change recipe while recognition is running.");
|
||||
}
|
||||
_currentRecipe = recipe;
|
||||
}
|
||||
|
||||
public bool IsRunning { get; set; }
|
||||
|
||||
private DateTime _startTime;
|
||||
private WorkflowList _workflow;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
|
||||
if (_currentRecipe == null)
|
||||
{
|
||||
throw new InvalidOperationException("No recipe set.");
|
||||
}
|
||||
if (IsRunning)
|
||||
{
|
||||
throw new InvalidOperationException("Recognition is already running.");
|
||||
}
|
||||
_loadingService.StartLoading($"Starting recipe {_currentRecipe.RecipeName} on {_recognitionConfiguration.CameraName}...");
|
||||
_workflow = WorkflowList.LoadFromFile("..\\Data\\Recipes\\" + _currentRecipe.RecipeName + ".hrcp");
|
||||
_startTime = DateTime.Now;
|
||||
IsRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
_loopTask = Task.Run(() => Loop(_cts.Token), _cts.Token);
|
||||
|
||||
SessionStarted?.Invoke(new SessionStartedEvent
|
||||
{
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
SessionStarted = _startTime,
|
||||
|
||||
});
|
||||
_loadingService.StopLoading($"Starting recipe {_currentRecipe.RecipeName} on {_recognitionConfiguration.CameraName}...");
|
||||
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsRunning)
|
||||
return ;
|
||||
_loadingService.StartLoading($"Stopping {_recognitionConfiguration.CameraName}");
|
||||
_cts?.Cancel();
|
||||
try
|
||||
{
|
||||
_loopTask?.Wait();
|
||||
}
|
||||
catch (AggregateException ex) when (ex.InnerExceptions.All(e => e is TaskCanceledException))
|
||||
{
|
||||
// Ignore cancellation exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRunning = false;
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
_loopTask = null;
|
||||
}
|
||||
|
||||
SessionEnded?.Invoke(new SessionEndedEvent
|
||||
{
|
||||
SessionEnded = DateTime.Now
|
||||
});
|
||||
_loadingService.StopLoading($"Stopping {_recognitionConfiguration.CameraName}");
|
||||
|
||||
}
|
||||
|
||||
private void Loop(CancellationToken token)
|
||||
{
|
||||
// Start the recognition process using the Hawkeye library
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
_workflow.ImageSource= _imageSource;
|
||||
_workflow.Execute();
|
||||
sw.Stop();
|
||||
|
||||
if (sw.ElapsedMilliseconds < _recognitionConfiguration.MinimumProcessingTime)
|
||||
{
|
||||
Thread.Sleep(_recognitionConfiguration.MinimumProcessingTime - (int)sw.ElapsedMilliseconds);
|
||||
}
|
||||
string[] errorNames = _workflow.Operations.Where(x => !x.Result).Select(x => x.Label).ToArray();
|
||||
|
||||
var annotated = _workflow.Context.ActiveImage.ImageData.Clone();
|
||||
var canvas = new OpenCVCanvas(annotated);
|
||||
_workflow.Context.GraphicsElements.ForEach(x => x.Draw(canvas));
|
||||
var processingResult = new ImageProcessedEvent()
|
||||
{
|
||||
SessionStart = _startTime,
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
AnalysisTime = sw.Elapsed,
|
||||
ErrorNames = errorNames.ToList(),
|
||||
HasError = errorNames.Length > 0,
|
||||
ImageSource = _imageSource.GetName(),
|
||||
ImageOriginal = _workflow.Context.LastCameraImage.ImageData,
|
||||
ImageAnalysis = annotated
|
||||
};
|
||||
|
||||
ImageProcessed(processingResult);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<ImageProcessedEvent> ImageProcessed = delegate{};
|
||||
public event Action<SessionStartedEvent>? SessionStarted = delegate { };
|
||||
public event Action<SessionEndedEvent>? SessionEnded = delegate { };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Inspectron.Settings;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
public class HawkeyeRecognitionSettings: ISettings
|
||||
{
|
||||
public string CameraName { get; }
|
||||
|
||||
public HawkeyeRecognitionSettings(string cameraName)
|
||||
{
|
||||
CameraName = cameraName;
|
||||
}
|
||||
|
||||
public int MinimumProcessingTime { get; set; }
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName+"/Hawkeye recognition", "Minimum processing time (ms)");
|
||||
}
|
||||
}
|
||||
63
VisionBuilder.UI.Recipes.HawkeyeRecipe/OpenCVCanvas.cs
Normal file
63
VisionBuilder.UI.Recipes.HawkeyeRecipe/OpenCVCanvas.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Hawkeye.VisionBuilder.Workflow;
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
public class OpenCVCanvas:ICanvas
|
||||
{
|
||||
private readonly Mat _image;
|
||||
|
||||
public OpenCVCanvas(Mat image)
|
||||
{
|
||||
_image = image;
|
||||
}
|
||||
|
||||
public void DrawRectangle(Vector2 start, Vector2 size, bool isGood)
|
||||
{
|
||||
_image.Rectangle(
|
||||
new Point(start.X, start.Y),
|
||||
new Point(start.X + size.X, start.Y + size.Y),
|
||||
isGood ? Scalar.Green : Scalar.Red,
|
||||
2);
|
||||
}
|
||||
|
||||
public void DrawLine(Vector2 start, Vector2 end, LineType type)
|
||||
{
|
||||
var color = type switch
|
||||
{
|
||||
LineType.Good => Scalar.Green,
|
||||
LineType.Bad => Scalar.Red,
|
||||
_ => Scalar.White
|
||||
};
|
||||
_image.Line(
|
||||
new Point(start.X, start.Y),
|
||||
new Point(end.X, end.Y),
|
||||
color,
|
||||
2);
|
||||
|
||||
}
|
||||
|
||||
public void DrawPoly(Vector2[] points, bool isGood)
|
||||
{
|
||||
var color = isGood ? Scalar.Green : Scalar.Red;
|
||||
var cvPoints = points.Select(p => new Point(p.X, p.Y)).ToArray();
|
||||
_image.Polylines([cvPoints], true, color, 2);
|
||||
|
||||
}
|
||||
|
||||
public void DrawCross(Vector2 location)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawNode(Vector2 location)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DrawArrow(Vector2 location, Vector2 direction)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Hawkeye.VisionBuilder.Workflow\Hawkeye.VisionBuilder.Workflow.csproj" />
|
||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Hawkeye.VisionBuilder.Workflow;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
public static class WorkflowRecipeHelper
|
||||
{
|
||||
|
||||
|
||||
private const string RECIPES_DIR = "..\\Data\\Recipes";
|
||||
public static List<string> ListRecipeFiles()
|
||||
{
|
||||
Directory.CreateDirectory(RECIPES_DIR);
|
||||
var recipes = Directory.GetFiles(RECIPES_DIR, "*.hrcp");
|
||||
return recipes.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user