check point

This commit is contained in:
meelstorm
2025-07-14 12:03:59 +02:00
commit d3cb790bd9
431 changed files with 44078 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
using Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses;
public class CategoryItem:IFilterParent
{
public string Name { get; set; }
public List<IFilter> Filters { get; set; } = new List<IFilter>();
public void Save(BinaryWriter writer)
{
writer.Write(Name);
writer.Write(Filters.Count);
foreach (var filter in Filters)
{
var type = filter.GetType();
writer.Write(type.FullName);
filter.Save(writer);
}
}
public static CategoryItem Load(BinaryReader reader)
{
var category = new CategoryItem();
category.Name = reader.ReadString();
var count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var typeName = reader.ReadString();
var type = Type.GetType(typeName);
var filter = (IFilter)Activator.CreateInstance(type);
filter.Load(reader);
filter.Parent = category;
category.Filters.Add(filter);
}
return category;
}
public override string ToString()
{
return Name;
}
}

View File

@@ -0,0 +1,33 @@
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses;
public class CategoryTree
{
public List<CategoryItem> CategoryItems { get; set; }=new List<CategoryItem>();
public void Save(BinaryWriter writer)
{
writer.Write(CategoryItems.Count);
foreach (var category in CategoryItems)
{
category.Save(writer);
}
}
public static CategoryTree Load(BinaryReader reader)
{
var tree = new CategoryTree();
var count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
tree.CategoryItems.Add(CategoryItem.Load(reader));
}
return tree;
}
public static CategoryTree LoadFromFile(string path)
{
using var file = File.OpenRead(path);
using var reader = new BinaryReader(file);
return Load(reader);
}
}

View File

@@ -0,0 +1,47 @@
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses
{
public class ColorConfig
{
private int _intensityMaxGrey;
public ColorConfig()
{
MaxBlobSize = 2000;
HueHighSaturationMinGrey = 20;
HueHighSaturationMaxGrey = 50;
HighSaturationMinGrey = 100;
HighSaturationMaxGrey = 255;
ClosingCircleRadius = 8.0;
IntensityMaxGrey = 255;
IntensityMinGrey = 0;
MedianRadius = 10;
MedianMargin = 1;
UseMedianFilter = true;
}
public int MaxBlobSize { get; set; }
public int HueHighSaturationMinGrey { get; set; }
public int HueHighSaturationMaxGrey { get; set; }
public int HighSaturationMinGrey { get; set; }
public int HighSaturationMaxGrey { get; set; }
public int IntensityMaxGrey
{
get { return _intensityMaxGrey; }
set { _intensityMaxGrey = value; }
}
public int IntensityMinGrey { get; set; }
public double ClosingCircleRadius { get; set; }
public int MedianRadius { get; set; }
public int MedianMargin { get; set; }
public bool UseMedianFilter { get; set; }
public bool GroupColorEnabled { get; set; }
public List<HSIValue> GroupColorValues { get; set; }=new List<HSIValue>();
public HSIValue HSI { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class AddFilterItem : IFilter, IFilterParent
{
public IFilterParent Parent { get; set; }
public List<IFilter> Filters { get; set; } = new List<IFilter>();
public void Transform(ref Mat image, ref Mat res)
{
using var tracker = new ResourcesTracker();
var temp = tracker.NewMat(res.Size(), MatType.CV_8UC1, Scalar.Black);
foreach (var filter in Filters)
{
filter.Transform(ref image, ref temp);
}
res = res + temp;
temp.Dispose();
}
public void Save(BinaryWriter bw)
{
bw.Write(Filters.Count);
foreach (var filter in Filters)
{
bw.Write(filter.GetType().FullName);
filter.Save(bw);
}
}
public void Load(BinaryReader reader)
{
int count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var type = reader.ReadString();
var filter = (IFilter)Activator.CreateInstance(Type.GetType(type));
filter.Load(reader);
filter.Parent = this;
Filters.Add(filter);
}
}
public bool IsSelected { get; set; }
public override string ToString()
{
return "Add";
}
}

View File

@@ -0,0 +1,66 @@
using System.Text;
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class BlobsFilterItem:IFilter
{
public int MinArea { get; set; } = 0;
public int MaxArea { get; set; } = 999999;
public int MinRadius { get; set; } = 0;
public int MaxRadius { get; set; } = 999999;
public IFilterParent Parent { get; set; }
public void Transform(ref Mat image, ref Mat res)
{
var contours = res
.FindContoursAsArray(RetrievalModes.List, ContourApproximationModes.ApproxSimple);
Mat result = new Mat(res.Rows, res.Cols, MatType.CV_8U, new Scalar(0));
foreach (Point[] contour in contours)
{
var area = Cv2.ContourArea(contour);
if (area < MinArea || area > MaxArea)
continue;
var radius = GetContourRadius(contour);
if (radius < MinRadius || radius > MaxRadius)
continue;
Cv2.DrawContours(result, new[] { contour }, -1, new Scalar(255), -1);
}
res = result;
}
int GetContourRadius(Point[] contour)
{
Cv2.MinEnclosingCircle(contour, out var center, out var radius);
return (int)radius;
}
public void Save(BinaryWriter writer)
{
writer.Write(MinArea);
writer.Write(MaxArea);
writer.Write(MinRadius);
writer.Write(MaxRadius);
}
public void Load(BinaryReader reader)
{
MinArea = reader.ReadInt32();
MaxArea = reader.ReadInt32();
MinRadius = reader.ReadInt32();
MaxRadius = reader.ReadInt32();
}
public bool IsSelected { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Blobs: ");
sb.Append($"Area:{MinArea}-{MaxArea}, ");
sb.Append($"Radius:{MinRadius}-{MaxRadius}");
return sb.ToString();
}
}

View File

@@ -0,0 +1,30 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class BlurFilterItem:IFilter
{
public IFilterParent Parent { get; set; }
public int Radius { get; set; } = 15;
public void Transform(ref Mat image, ref Mat res)
{
image = image.Blur(new Size(Radius, Radius));
}
public void Save(BinaryWriter writer)
{
writer.Write(Radius);
}
public void Load(BinaryReader reader)
{
Radius = reader.ReadInt32();
}
public bool IsSelected { get; set; }
public override string ToString()
{
return "Blur "+Radius;
}
}

View File

@@ -0,0 +1,13 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public interface IFilter
{
public IFilterParent Parent { get; set; }
void Transform(ref Mat image, ref Mat res);
void Save(BinaryWriter writer);
void Load(BinaryReader reader);
public bool IsSelected { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public interface IFilterParent
{
public List<IFilter> Filters { get; set; }
}

View File

@@ -0,0 +1,52 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class IntersectionFilterItem : IFilter, IFilterParent
{
public IFilterParent Parent { get; set; }
public List<IFilter> Filters { get; set; } = new List<IFilter>();
public void Transform(ref Mat image, ref Mat res)
{
using var tracker = new ResourcesTracker();
var temp = tracker.NewMat(res.Size(), MatType.CV_8UC1, Scalar.Black);
foreach (var filter in Filters)
{
filter.Transform(ref image, ref temp);
}
res = res & temp;
temp.Dispose();
}
public void Save(BinaryWriter bw)
{
bw.Write(Filters.Count);
foreach (var filter in Filters)
{
bw.Write(filter.GetType().FullName);
filter.Save(bw);
}
}
public void Load(BinaryReader reader)
{
int count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var type = reader.ReadString();
var filter = (IFilter)Activator.CreateInstance(Type.GetType(type));
filter.Load(reader);
filter.Parent = this;
Filters.Add(filter);
}
}
public bool IsSelected { get; set; }
public override string ToString()
{
return "Intersection";
}
}

View File

@@ -0,0 +1,78 @@
using System.Diagnostics;
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class MorphologyFilterItem:IFilter
{
public enum EMorphologyType
{
Erode,
Dilate,
Close,
Open
}
public int Size { get; set; } = 1;
public EMorphologyType MorphologyType { get; set; }=EMorphologyType.Erode;
public void Load(BinaryReader reader)
{
Size = reader.ReadInt32();
MorphologyType = (EMorphologyType)reader.ReadInt32();
}
public bool IsSelected { get; set; }
public IFilterParent Parent { get; set; }
public MorphologyFilterItem()
{
}
public MorphologyFilterItem(CategoryItem parent)
{
Parent = parent;
}
public void Transform(ref Mat image, ref Mat res)
{
if(Size==0)
return;
var sw=Stopwatch.StartNew();
var kernel = Cv2.GetStructuringElement(MorphShapes.Ellipse,new Size((int)(Size), (int)(Size)));
switch (MorphologyType)
{
case EMorphologyType.Dilate:
//Cv2.Dilate(res, res,kernel);
Cv2.MorphologyEx(res, res, MorphTypes.Dilate, kernel);
break;
case EMorphologyType.Erode:
Cv2.Erode(res, res,kernel);
break;
case EMorphologyType.Close:
Cv2.MorphologyEx(res, res, MorphTypes.Close, kernel);
break;
case EMorphologyType.Open:
Cv2.MorphologyEx(res, res, MorphTypes.Open, kernel);
break;
}
sw.Stop();
}
public void Save(BinaryWriter writer)
{
writer.Write(Size);
writer.Write((int)MorphologyType);
}
public override string ToString()
{
return $"{MorphologyType} {Size}";
}
}

View File

@@ -0,0 +1,101 @@
using System.Windows.Markup;
using OpenCvSharp;
using Size = OpenCvSharp.Size;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class RangeFilterItem : IFilter
{
public IFilterParent Parent { get; set; }
public RangeFilterItem(CategoryItem parent)
{
Parent = parent;
}
public RangeFilterItem()
{
}
public int HMin { get; set; } = 0;
public int HMax { get; set; } = 100;
public int SMin { get; set; } = 0;
public int SMax { get; set; } = 255;
public int VMin { get; set; } = 0;
public int VMax { get; set; } = 255;
public int Dilation { get; set; } = 0;
public int Closing { get; set; } = 0;
public override string ToString()
{
return $"H: {HMin}-{HMax}, S: {SMin}-{SMax}, V: {VMin}-{VMax}";
}
public void Transform(ref Mat hsv, ref Mat res)
{
var lower = new Scalar(HMin, SMin, VMin);
var upper = new Scalar(HMax, SMax, VMax);
var mask = hsv.InRange(lower, upper);
if (Dilation > 0)
{
// generate circular kernel
var kernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(Dilation, Dilation));
mask = mask.Dilate(kernel);
kernel.Dispose();
}
if (Closing > 0)
{
// generate circular kernel
var kernel = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(Closing, Closing));
mask = mask.MorphologyEx(MorphTypes.Close, kernel);
kernel.Dispose();
}
res+=mask;
mask.Dispose();
}
public void Save(BinaryWriter writer)
{
writer.Write(HMin);
writer.Write(HMax);
writer.Write(SMin);
writer.Write(SMax);
writer.Write(VMin);
writer.Write(VMax);
writer.Write(Dilation);
writer.Write(Closing);
}
public void Load(BinaryReader reader)
{
HMin = reader.ReadInt32();
HMax = reader.ReadInt32();
SMin = reader.ReadInt32();
SMax = reader.ReadInt32();
VMin = reader.ReadInt32();
VMax = reader.ReadInt32();
Dilation = reader.ReadInt32();
Closing = reader.ReadInt32();
}
public bool IsSelected { get; set; }
public static RangeFilterItem Load(BinaryReader reader, CategoryItem parent)
{
var filter = new RangeFilterItem(parent);
filter.HMin = reader.ReadInt32();
filter.HMax = reader.ReadInt32();
filter.SMin = reader.ReadInt32();
filter.SMax = reader.ReadInt32();
filter.VMin = reader.ReadInt32();
filter.VMax = reader.ReadInt32();
filter.Dilation = reader.ReadInt32();
filter.Closing = reader.ReadInt32();
return filter;
}
}

View File

@@ -0,0 +1,25 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class SobelFilterItem:IFilter
{
public IFilterParent Parent { get; set; }
public void Transform(ref Mat image, ref Mat res)
{
var filtered=image.Sobel(MatType.CV_32F, 1, 1, 3);
filtered.ConvertTo(image, MatType.CV_8UC3);
}
public void Save(BinaryWriter writer)
{
}
public void Load(BinaryReader reader)
{
}
public bool IsSelected { get; set; }
}

View File

@@ -0,0 +1,176 @@
using System.Diagnostics;
using OpenCvSharp;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class SpatialFilterItem : IFilter
{
private Mat? _mask=null;
public IFilterParent Parent { get; set; }
public bool Subtract { get; set; }
public string? FilePath { get; set; }
public void ReloadMask()
{
if (!File.Exists(FilePath))
{
CreateMask();
}else
if (!string.IsNullOrEmpty(FilePath))
{
_mask = new Mat(FilePath,ImreadModes.Grayscale);
}
}
public SpatialFilterItem()
{
}
void CreateMask()
{
_mask = new Mat(13005 + 256, 256, MatType.CV_8UC1, (Scalar)0);
}
public SpatialFilterItem(CategoryItem parent) : this()
{
Parent = parent;
}
public Mat Mask
{
get
{
if (_mask == null)
{
if (FilePath != null)
{
ReloadMask();
}
else
{
CreateMask();
}
}
return _mask;
}
set
{
_mask = value;
}
}
struct PixelStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 65536*3)]
public byte[] Pixels;
}
struct PixelStructMask
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3394816)]
public byte[] Pixels;
}
public static Mat lastInput;
public unsafe void Transform(ref Mat image, ref Mat res)
{
//var pixelStruct = Marshal.PtrToStructure<PixelStruct>(new IntPtr(image.DataPointer));
var candyBytes = GetPixels<byte>(image);
var candidates = new Mat(image.Size(), MatType.CV_8UC1, (Scalar)0);
Mat tmp=new Mat();
image.CopyTo(tmp);
lastInput = tmp;
//var pixelStructMask = Marshal.PtrToStructure<PixelStructMask>(new IntPtr(Mask.DataPointer));
var maskBytes = GetPixels<byte>(Mask);
var len = candyBytes.Length;
var w = image.Width;
var mw = Mask.Width;
var lenDiv3 = len / 3;
for (var q = 0; q < lenDiv3; q++)
{
var i = q * 3;
var p = i / 3;
var y = p / w;
var x = p % w;
var yp = (int)candyBytes[i + 0];
var xp = candyBytes[i + 1];
var vp = ((byte) (candyBytes[i + 2] / 5f)) * 255;
var hp = yp + vp;
var maskColor = maskBytes[hp * mw + xp];
if (maskColor > 0)
candidates.Set<byte>(y, x, 255);
}
if (Subtract && !IsSelected)
{
res -= candidates;
}
else
{
res += candidates;
}
}
string Hash(Span<byte> bytes) {
using var sha = SHA256.Create();
var hash = sha.ComputeHash(bytes.ToArray());
return Convert.ToBase64String(hash);
}
unsafe Span<T> GetPixels<T>(Mat image) where T : unmanaged
{
return new Span<T>(image.DataPointer, image.Width * image.Height * image.Channels());
}
public void Save(BinaryWriter writer)
{
writer.Write(FilePath??"");
writer.Write(Subtract);
}
public void Load(BinaryReader reader)
{
FilePath = reader.ReadString();
Console.WriteLine("Loaded path:"+FilePath);
if(FilePath.StartsWith("..\\../"))
FilePath = FilePath.Replace("..\\../", "..\\");
Subtract = reader.ReadBoolean();
}
public bool IsSelected { get; set; }
public override string ToString()
{
var totalColors = 0;
if (Mask != null)
{
totalColors = Mask.CountNonZero();
}
return $"{(Subtract ? "-" : "+")}Spatial Filter({totalColors} colors)";
}
}

View File

@@ -0,0 +1,177 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using ILGPU;
using ILGPU.Runtime;
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
[Obsolete]
public class SpatialFilterItemGPU:IFilter
{
private Mat? _mask = null;
private MemoryBuffer1D<byte, Stride1D.Dense> _maskGpu;
public IFilterParent Parent { get; set; }
public bool Subtract { get; set; }
public string? FilePath { get; set; }
public void ReloadMask()
{
if (!File.Exists(FilePath))
{
CreateMask();
}
else
if (!string.IsNullOrEmpty(FilePath))
{
_mask = new Mat(FilePath).CvtColor(ColorConversionCodes.BGR2GRAY);
}
LoadMaskToGpu();
}
private unsafe void LoadMaskToGpu()
{
var maskBytes = new Span<byte>(Mask.DataPointer, Mask.Width * Mask.Height).ToArray();
if (_maskGpu != null)
{
_maskGpu.Dispose();
}
_maskGpu = Kernels.Accelerator.Allocate1D<byte>(maskBytes);
}
private static MemoryBuffer1D<byte, Stride1D.Dense>? _imageMemory;
private static MemoryBuffer1D<byte, Stride1D.Dense>? _resMemory;
public SpatialFilterItemGPU()
{
}
void CreateMask()
{
_mask = new Mat(13005 + 256, 256, MatType.CV_8UC1, (Scalar)0);
}
public SpatialFilterItemGPU(CategoryItem parent) : this()
{
Parent = parent;
}
public Mat Mask
{
get
{
if (_mask == null)
{
if (FilePath != null)
{
ReloadMask();
}
else
{
CreateMask();
}
}
return _mask;
}
set
{
_mask = value;
}
}
private static Mat? _lastLoadedImage;
public unsafe void Transform(ref Mat image, ref Mat res)
{
var w = image.Width;
var mw = Mask.Width;
if (_lastLoadedImage != image)
{
var imageBytes = new ReadOnlySpan<byte>(image.DataPointer, image.Width * image.Height * 3);
_imageMemory ??= Kernels.Accelerator.Allocate1D<byte>(imageBytes.Length);
_imageMemory.View.BaseView.CopyFromCPU(imageBytes);
_lastLoadedImage = image;
}
var len = (int)_imageMemory.Length;
_resMemory ??= Kernels.Accelerator.Allocate1D<byte>(len / 3);
Kernels.SpatialKernel(len/3, w, mw, _imageMemory.View, _maskGpu.View, _resMemory.View);
var arr = _resMemory.GetAsArray1D();
var candidates=new Mat(new[]{image.Height,image.Width}, MatType.CV_8UC1);
candidates.SetArray(arr);
if (Subtract && !IsSelected)
{
res -= candidates;
}
else
{
res |= candidates;
}
candidates.Dispose();
}
public void Save(BinaryWriter writer)
{
writer.Write(FilePath ?? "");
writer.Write(Subtract);
}
public void Load(BinaryReader reader)
{
FilePath = reader.ReadString();
Console.WriteLine("Loaded path:" + FilePath);
if (FilePath.StartsWith("..\\../"))
FilePath = FilePath.Replace("..\\../", "..\\");
Subtract = reader.ReadBoolean();
}
public bool IsSelected { get; set; }
public override string ToString()
{
var totalColors = 0;
if (Mask != null)
{
totalColors = Mask.CountNonZero();
}
return $"{(Subtract ? "-" : "+")}Spatial Filter({totalColors} colors)";
}
}

View File

@@ -0,0 +1,52 @@
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses.Filters;
public class SubtractFilterItem : IFilter,IFilterParent
{
public IFilterParent Parent { get; set; }
public List<IFilter> Filters { get; set; } = new List<IFilter>();
public void Transform(ref Mat image, ref Mat res)
{
using var tracker = new ResourcesTracker();
var temp = tracker.NewMat(res.Size(),MatType.CV_8UC1,Scalar.Black);
foreach (var filter in Filters)
{
filter.Transform(ref image, ref temp);
}
res = res - temp;
temp.Dispose();
}
public void Save(BinaryWriter bw)
{
bw.Write(Filters.Count);
foreach (var filter in Filters)
{
bw.Write(filter.GetType().FullName);
filter.Save(bw);
}
}
public void Load(BinaryReader reader)
{
int count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
var type = reader.ReadString();
var filter = (IFilter)Activator.CreateInstance(Type.GetType(type));
filter.Load(reader);
filter.Parent = this;
Filters.Add(filter);
}
}
public bool IsSelected { get; set; }
public override string ToString()
{
return "Subtract";
}
}

View File

@@ -0,0 +1,125 @@
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Xml.Serialization;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses
{
public class HSIValue
{
public HSIValue()
{
}
public int MinSize { get; set; } = 1;
public int MinComponentSize { get; set; } = 1;
public int PreFilterId { get; set; } = 0;
public int PreFilterSize { get; set; } = 10;
public int Closing { get; set; }
public int Circle { get; set; } = 1;
public string Tag { get; set; }
private Range<int> _hue = new Range<int>() {Minimum = 0,Maximum = 10};
private Range<int> _saturation = new Range<int>() { Minimum = 0, Maximum = 10 };
private Range<int> _intensity = new Range<int>() { Minimum = 0, Maximum = 10 };
public string Name { get; set; }
public Range<int> Hue
{
get { return _hue; }
set
{
if (Equals(value, _hue)) return;
_hue = value;
OnPropertyChanged();
}
}
public Range<int> Saturation
{
get { return _saturation; }
set
{
if (Equals(value, _saturation)) return;
_saturation = value;
OnPropertyChanged();
}
}
public Range<int> Intensity
{
get { return _intensity; }
set
{
if (Equals(value, _intensity)) return;
_intensity = value;
OnPropertyChanged();
}
}
public bool Active { get; set; }
public List<HSIValue> SubColors { get; set; }=new List<HSIValue>();
public Rectangle GetRectangle()
{
Rectangle th = new Rectangle((int) (Hue.Minimum), (int) (Saturation.Minimum),
(int) (Hue.Maximum) - (int) (Hue.Minimum), (int) (Saturation.Maximum) - (int) (Saturation.Minimum));
return th;
}
[XmlIgnore]
public bool ShowStar { get; set; }
public int Dilation { get; set; }
public override string ToString()
{
return $"{Name} {Hue}:{Saturation}:{Intensity}{(ShowStar?"*":"")}";
}
public static explicit operator ColorConfig(HSIValue value)
{
var config = new ColorConfig()
{
ClosingCircleRadius = value.Circle,
HueHighSaturationMaxGrey = value.Hue.Maximum,
HueHighSaturationMinGrey = value.Hue.Minimum,
HighSaturationMaxGrey = value.Saturation.Maximum,
HighSaturationMinGrey = value.Saturation.Minimum,
IntensityMaxGrey = value.Intensity.Maximum,
IntensityMinGrey = value.Intensity.Minimum,
MaxBlobSize = value.MinSize,
MedianMargin = 1,
MedianRadius = 10,
UseMedianFilter = true,
};
return config;
}
public static implicit operator HSIValue(ColorConfig config)
{
return new HSIValue()
{
Hue = new Range<int>(config.HueHighSaturationMinGrey, config.HueHighSaturationMaxGrey),
Saturation = new Range<int>(config.HighSaturationMinGrey, config.HighSaturationMaxGrey),
Intensity = new Range<int>(config.IntensityMinGrey, config.IntensityMaxGrey),
Circle = (int)config.ClosingCircleRadius,
MinSize = config.MaxBlobSize
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,157 @@
using ILGPU;
using ILGPU.Runtime;
using OpenCvSharp;
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses;
public static class Kernels
{
static Kernels()
{
Init();
AppDomain.CurrentDomain.ProcessExit +=
Destruct;
}
private static void Destruct(object? sender, EventArgs e)
{
_accelerator.Dispose();
_gpuContext.Dispose();
}
private static object _lock = new object();
private static ILGPU.Context _gpuContext;
private static Accelerator _accelerator;
private static Action<Index1D, int, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>> _spatialKernel;
private static Action<Index1D, int, int, ArrayView<byte>, ArrayView<byte>> _dilationKernel;
public static Action<Index1D, int, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>> SpatialKernel => _spatialKernel;
public static Action<Index1D, int, int, ArrayView<byte>, ArrayView<byte>> Dilation => _dilationKernel;
public static Accelerator Accelerator
{
get => _accelerator;
set => _accelerator = value;
}
static Dictionary<Mat, MemoryBuffer1D<byte,Stride1D.Dense>> _imageCache = new ();
public static unsafe MemoryBuffer1D<byte, Stride1D.Dense> TransformImageCached(Mat image)
{
if (_imageCache.ContainsKey(image))
return _imageCache[image];
var imageBytes = new Span<byte>(image.DataPointer, image.Width * image.Height * 3);
var res=Kernels.Accelerator.Allocate1D<byte>(imageBytes.ToArray());
_imageCache[image] = res;
return res;
}
static void Init()
{
lock (_lock)
{
if (_gpuContext != null) return;
_gpuContext = ILGPU.Context.Create(builder => builder.AllAccelerators());
_accelerator = _gpuContext.Devices.First(x => x.AcceleratorType == AcceleratorType.Cuda)
.CreateAccelerator(_gpuContext);
Console.WriteLine("GPU acceleration initialized");
_spatialKernel = _accelerator
.LoadAutoGroupedStreamKernel<Index1D, int, int, ArrayView<byte>, ArrayView<byte>, ArrayView<byte>>(
Kernels.TransformSpatialKernel);
_dilationKernel = _accelerator
.LoadAutoGroupedStreamKernel<Index1D, int, int, ArrayView<byte>,ArrayView<byte>>(
Kernels.DilationKernel);
}
}
private static void TransformSpatialKernel(Index1D index, int w, int mw, ArrayView<byte> candyBytes, ArrayView<byte> maskBytes, ArrayView<byte> res)
{
var i = index;
var p = i * 3;
var y = p / w;
var x = p % w;
var yp = (int)candyBytes[p + 0];
var xp = candyBytes[p + 1];
var vp = (byte)(candyBytes[p + 2] / 5f) * 255;
var hp = yp + vp;
var maskColor = maskBytes[hp * mw + xp];
if (maskColor > 0)
res[i] = 255;
else
res[i] = 0;
}
private static void DilationKernel(Index1D index, int w, int size, ArrayView<byte> maskBytes,
ArrayView<byte> res)
{
var i = index;
var p = i ;
var y = p / w;
var x = p % w;
var halfSize = size-1 / 2;
if (maskBytes[p] > 0)
{
for (int j = -halfSize; j <= halfSize; j++)
{
for (int k = -halfSize; k <= halfSize; k++)
{
var yp = y + j;
var xp = x + k;
if (yp < 0 || yp >= w || xp < 0 || xp >= w)
continue;
res[yp * w + xp] = 255;
}
}
}
}
private static void ErosionKernel(Index1D index, int w, int size, ArrayView<byte> maskBytes,
ArrayView<byte> res)
{
var i = index;
var p = i ;
var y = p / w;
var x = p % w;
var halfSize = size-1 / 2;
if (maskBytes[p] <255)
{
for (int j = -halfSize; j <= halfSize; j++)
{
for (int k = -halfSize; k <= halfSize; k++)
{
var yp = y + j;
var xp = x + k;
if (yp < 0 || yp >= w || xp < 0 || xp >= w)
continue;
res[yp * w + xp] = 0;
}
}
}
}
}

View File

@@ -0,0 +1,64 @@
namespace Hawkeye.VisionBuilder.Workflow.ColorStudioClasses
{
/// <summary>The Range class.</summary>
/// <typeparam name="T">Generic parameter.</typeparam>
public class Range<T> where T : IComparable<T>
{
public Range()
{
}
public Range(T min,T max)
{
this.Maximum = max;
this.Minimum = min;
}
/// <summary>Minimum value of the range.</summary>
public T Minimum { get; set; }
/// <summary>Maximum value of the range.</summary>
public T Maximum { get; set; }
/// <summary>Presents the Range in readable format.</summary>
/// <returns>String representation of the Range</returns>
public override string ToString()
{
return string.Format("[{0} - {1}]", this.Minimum, this.Maximum);
}
/// <summary>Determines if the range is valid.</summary>
/// <returns>True if range is valid, else false</returns>
public bool IsValid()
{
return this.Minimum.CompareTo(this.Maximum) <= 0;
}
/// <summary>Determines if the provided value is inside the range.</summary>
/// <param name="value">The value to test</param>
/// <returns>True if the value is inside Range, else false</returns>
public bool ContainsValue(T value)
{
return (this.Minimum.CompareTo(value) <= 0) && (value.CompareTo(this.Maximum) <= 0);
}
/// <summary>Determines if this Range is inside the bounds of another range.</summary>
/// <param name="Range">The parent range to test on</param>
/// <returns>True if range is inclusive, else false</returns>
public bool IsInsideRange(Range<T> range)
{
return this.IsValid() && range.IsValid() && range.ContainsValue(this.Minimum) && range.ContainsValue(this.Maximum);
}
/// <summary>Determines if another range is inside the bounds of this range.</summary>
/// <param name="Range">The child range to test</param>
/// <returns>True if range is inside, else false</returns>
public bool ContainsRange(Range<T> range)
{
return this.IsValid() && range.IsValid() && this.ContainsValue(range.Minimum) && this.ContainsValue(range.Maximum);
}
}
}