c# script support
python server support
This commit is contained in:
1092
Hawkeye.VisionBuilder.Workflow/API/PythonAPI.cs
Normal file
1092
Hawkeye.VisionBuilder.Workflow/API/PythonAPI.cs
Normal file
File diff suppressed because it is too large
Load Diff
103
Hawkeye.VisionBuilder.Workflow/API/generator.nswag
Normal file
103
Hawkeye.VisionBuilder.Workflow/API/generator.nswag
Normal file
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"runtime": "Net80",
|
||||
"defaultVariables": null,
|
||||
"documentGenerator": {
|
||||
"fromDocument": {
|
||||
"json": "",
|
||||
"url": "http://localhost:8000/openapi.json",
|
||||
"output": null,
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
},
|
||||
"codeGenerators": {
|
||||
"openApiToCSharpClient": {
|
||||
"clientBaseClass": null,
|
||||
"configurationClass": null,
|
||||
"generateClientClasses": true,
|
||||
"suppressClientClassesOutput": false,
|
||||
"generateClientInterfaces": false,
|
||||
"suppressClientInterfacesOutput": false,
|
||||
"clientBaseInterface": null,
|
||||
"injectHttpClient": true,
|
||||
"disposeHttpClient": true,
|
||||
"protectedMethods": [],
|
||||
"generateExceptionClasses": true,
|
||||
"exceptionClass": "ApiException",
|
||||
"wrapDtoExceptions": true,
|
||||
"useHttpClientCreationMethod": false,
|
||||
"httpClientType": "System.Net.Http.HttpClient",
|
||||
"useHttpRequestMessageCreationMethod": false,
|
||||
"useBaseUrl": true,
|
||||
"generateBaseUrlProperty": true,
|
||||
"generateSyncMethods": false,
|
||||
"generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
|
||||
"exposeJsonSerializerSettings": false,
|
||||
"clientClassAccessModifier": "public",
|
||||
"typeAccessModifier": "public",
|
||||
"propertySetterAccessModifier": "",
|
||||
"generateNativeRecords": false,
|
||||
"useRequiredKeyword": false,
|
||||
"generateContractsOutput": false,
|
||||
"contractsNamespace": null,
|
||||
"contractsOutputFilePath": null,
|
||||
"parameterDateTimeFormat": "s",
|
||||
"parameterDateFormat": "yyyy-MM-dd",
|
||||
"generateUpdateJsonSerializerSettingsMethod": true,
|
||||
"useRequestAndResponseSerializationSettings": false,
|
||||
"serializeTypeInformation": false,
|
||||
"queryNullValue": "",
|
||||
"className": "PythonModelAPI",
|
||||
"operationGenerationMode": "SingleClientFromPathSegments",
|
||||
"additionalNamespaceUsages": [],
|
||||
"additionalContractNamespaceUsages": [],
|
||||
"generateOptionalParameters": false,
|
||||
"generateJsonMethods": false,
|
||||
"enforceFlagEnums": false,
|
||||
"parameterArrayType": "System.Collections.Generic.IEnumerable",
|
||||
"parameterDictionaryType": "System.Collections.Generic.IDictionary",
|
||||
"responseArrayType": "System.Collections.Generic.ICollection",
|
||||
"responseDictionaryType": "System.Collections.Generic.IDictionary",
|
||||
"wrapResponses": false,
|
||||
"wrapResponseMethods": [],
|
||||
"generateResponseClasses": true,
|
||||
"responseClass": "SwaggerResponse",
|
||||
"namespace": "Hawkeye.VisionBuilder.Workflow",
|
||||
"requiredPropertiesMustBeDefined": true,
|
||||
"dateType": "System.DateTimeOffset",
|
||||
"jsonConverters": null,
|
||||
"anyType": "object",
|
||||
"dateTimeType": "System.DateTimeOffset",
|
||||
"timeType": "System.TimeSpan",
|
||||
"timeSpanType": "System.TimeSpan",
|
||||
"arrayType": "System.Collections.Generic.ICollection",
|
||||
"arrayInstanceType": "System.Collections.ObjectModel.Collection",
|
||||
"dictionaryType": "System.Collections.Generic.IDictionary",
|
||||
"dictionaryInstanceType": "System.Collections.Generic.Dictionary",
|
||||
"arrayBaseType": "System.Collections.ObjectModel.Collection",
|
||||
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
|
||||
"classStyle": "Poco",
|
||||
"jsonLibrary": "NewtonsoftJson",
|
||||
"jsonPolymorphicSerializationStyle": "NJsonSchema",
|
||||
"generateDefaultValues": true,
|
||||
"generateDataAnnotations": true,
|
||||
"excludedTypeNames": [],
|
||||
"excludedParameterNames": [],
|
||||
"handleReferences": false,
|
||||
"generateImmutableArrayProperties": false,
|
||||
"generateImmutableDictionaryProperties": false,
|
||||
"jsonSerializerSettingsTransformationMethod": null,
|
||||
"inlineNamedArrays": false,
|
||||
"inlineNamedDictionaries": false,
|
||||
"inlineNamedTuples": true,
|
||||
"inlineNamedAny": false,
|
||||
"generateDtoTypes": true,
|
||||
"generateOptionalPropertiesAsNullable": false,
|
||||
"generateNullableReferenceTypes": false,
|
||||
"templateDirectory": null,
|
||||
"serviceHost": null,
|
||||
"serviceSchemes": null,
|
||||
"output": "PythonAPI.cs",
|
||||
"newLineBehavior": "Auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using static Community.CsharpSqlite.Sqlite3;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
|
||||
|
||||
public class CSharpDataTransferHTTP
|
||||
{
|
||||
private readonly PythonModelAPI _api;
|
||||
private readonly HttpClient _client;
|
||||
private readonly CancellationTokenSource _pingCts = new();
|
||||
private readonly Thread _pingThread;
|
||||
|
||||
public CSharpDataTransferHTTP()
|
||||
{
|
||||
var handler = new SocketsHttpHandler
|
||||
{
|
||||
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(10),
|
||||
MaxConnectionsPerServer = 100
|
||||
|
||||
|
||||
|
||||
};
|
||||
_client = new HttpClient(handler)
|
||||
{
|
||||
// i've added this
|
||||
DefaultRequestHeaders =
|
||||
{
|
||||
ExpectContinue = false
|
||||
}
|
||||
};
|
||||
_api = new PythonModelAPI("http://localhost:8000",_client);
|
||||
_pingThread = new Thread(PingLoop) { IsBackground = true };
|
||||
_pingThread.Start();
|
||||
|
||||
}
|
||||
|
||||
private void PingLoop()
|
||||
{
|
||||
while (!_pingCts.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
_client.GetAsync("http://localhost:8000/").Wait();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors, optionally log
|
||||
}
|
||||
Thread.Sleep(3000); // Ping every 3 seconds
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadModel(string path, string modelName)
|
||||
{
|
||||
_api.LoadModelAsync(new LoadModelIn()
|
||||
{
|
||||
Name = modelName,
|
||||
Path = Path.GetFileName(path)
|
||||
}).Wait();
|
||||
}
|
||||
|
||||
public void ActivateModel(string modelName)
|
||||
{
|
||||
_api.ActivateModelAsync(new ActivateModelIn(){Name = modelName}).Wait();
|
||||
}
|
||||
|
||||
public int[] GetAcceptSize()
|
||||
{
|
||||
var response = _api.GetAcceptSizeAsync().Result;
|
||||
|
||||
return response.Accept_size.ToArray();
|
||||
}
|
||||
|
||||
public int[] GetOutputSize()
|
||||
{
|
||||
var response = _api.GetOutputSizeAsync().Result;
|
||||
|
||||
return response.Accept_size.ToArray();
|
||||
|
||||
}
|
||||
|
||||
public byte[] PredictRaw(byte[] byteArray)
|
||||
{
|
||||
var response = _api.PredictRawAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
||||
var stream = response.Stream;
|
||||
// read the stream to byte array
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
var responseBytes = memoryStream.ToArray();
|
||||
return responseBytes;
|
||||
}
|
||||
|
||||
public byte[] Predict(byte[] byteArray)
|
||||
{
|
||||
var response = _api.PredictAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
||||
var stream = response.Stream;
|
||||
// read the stream to byte array
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
var responseBytes = memoryStream.ToArray();
|
||||
return responseBytes;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,5 +2,26 @@
|
||||
|
||||
public class PythonModelProxyHTTP
|
||||
{
|
||||
|
||||
private static bool _isInitialized = false;
|
||||
private static UvicornScriptRunner _pythonScriptRunner;
|
||||
private static CSharpDataTransferHTTP _cSharpDataTransferHTTP;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (_isInitialized) return;
|
||||
|
||||
var workDirAbsPath = Path.GetFullPath(@"..\Data\Models");
|
||||
_pythonScriptRunner = new UvicornScriptRunner(workDirAbsPath);
|
||||
_cSharpDataTransferHTTP = new CSharpDataTransferHTTP();
|
||||
|
||||
_pythonScriptRunner.Start();
|
||||
Thread.Sleep(1000); // Wait for the server to start
|
||||
}
|
||||
|
||||
public static CSharpDataTransferHTTP GetInterface()
|
||||
{
|
||||
if (!_isInitialized) Initialize();
|
||||
|
||||
return _cSharpDataTransferHTTP;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
|
||||
|
||||
public class UvicornScriptRunner
|
||||
{
|
||||
private Process process;
|
||||
private readonly string _workingDirectory;
|
||||
|
||||
public UvicornScriptRunner(string workingDirectory)
|
||||
{
|
||||
_workingDirectory = workingDirectory;
|
||||
}
|
||||
public static string ConvertToWslPath(string windowsPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(windowsPath))
|
||||
throw new ArgumentException("Path cannot be null or empty.", nameof(windowsPath));
|
||||
|
||||
// Replace backslashes with forward slashes
|
||||
string wslPath = windowsPath.Replace('\\', '/');
|
||||
|
||||
// Extract the drive letter and convert it to WSL format
|
||||
if (wslPath.Length > 1 && wslPath[1] == ':')
|
||||
{
|
||||
char driveLetter = char.ToLower(wslPath[0]);
|
||||
wslPath = $"/mnt/{driveLetter}{wslPath.Substring(2)}";
|
||||
}
|
||||
|
||||
return wslPath;
|
||||
}
|
||||
public void Start()
|
||||
{
|
||||
|
||||
|
||||
|
||||
//startInfo.CreateNoWindow = true;
|
||||
// create and start the process
|
||||
process = new Process();
|
||||
var convertedWd = ConvertToWslPath(_workingDirectory);
|
||||
var arg =
|
||||
$@"--distribution ubuntu --user root --cd ""{convertedWd}"" -- uvicorn PythonModelAPI:app";
|
||||
process.StartInfo = new ProcessStartInfo("wsl", arg)
|
||||
{
|
||||
UseShellExecute = false
|
||||
};
|
||||
process.Start();
|
||||
// on windows only
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
ChildProcessTracker.AddProcess(process);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (process != null && !process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
process.Dispose();
|
||||
process = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,29 @@
|
||||
<Configurations>Debug;Release;CPU</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="OpenAPIs\**" />
|
||||
<EmbeddedResource Remove="OpenAPIs\**" />
|
||||
<None Remove="OpenAPIs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILGPU" Version="1.5.1" />
|
||||
<PackageReference Include="IronPython" Version="3.4.0" />
|
||||
<PackageReference Include="MessagePack" Version="2.5.108" />
|
||||
<PackageReference Include="Microsoft.Extensions.ApiDescription.Client" Version="7.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="NetMQ" Version="4.0.1.11" />
|
||||
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Ninject" Version="3.3.6" />
|
||||
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.18.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
|
||||
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.6.0.20220608" />
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ public abstract class ColorAIOperation:BaseOperation
|
||||
public ColorAIOperation(string model_name)
|
||||
{
|
||||
_modelName = model_name;
|
||||
_cSharpDataTransferMQRPC = PythonModelProxyRPC.GetInterface();
|
||||
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
|
||||
CanHaveProcessingError = false;
|
||||
}
|
||||
|
||||
private readonly CSharpDataTransferMQRPC _cSharpDataTransferMQRPC;
|
||||
private readonly CSharpDataTransferHTTP _cSharpDataTransferMQRPC;
|
||||
private int _imageWidth;
|
||||
private int _imageHeight;
|
||||
|
||||
@@ -52,15 +52,16 @@ public abstract class ColorAIOperation:BaseOperation
|
||||
this.SetError("Model file not found");
|
||||
return;
|
||||
}
|
||||
_cSharpDataTransferMQRPC.TransferData<object>(new MethodCall("load_model", ModelFilePath.Path, _modelName));
|
||||
|
||||
_cSharpDataTransferMQRPC.LoadModel(ModelFilePath.Path, _modelName);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
_cSharpDataTransferMQRPC.ActivateModel(_modelName);
|
||||
|
||||
var sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize();
|
||||
|
||||
|
||||
_cSharpDataTransferMQRPC.TransferData<object>(new MethodCall("activate_model", _modelName));
|
||||
|
||||
var sizeEncoded = _cSharpDataTransferMQRPC.TransferData<object[]>(new MethodCall("get_accept_size"));
|
||||
var size = sizeEncoded.Select(Convert.ToInt32).ToArray();
|
||||
_imageWidth = size[1];
|
||||
_imageHeight = size[2];
|
||||
@@ -82,22 +83,30 @@ public abstract class ColorAIOperation:BaseOperation
|
||||
allowedFlags[i] = 1;
|
||||
}
|
||||
|
||||
var resultEncoded = _cSharpDataTransferMQRPC.TransferData<object[]>(new MethodCall(IsRaw?"predict_raw": "predict", byteArray));
|
||||
|
||||
var result = resultEncoded.Select(Convert.ToByte).ToArray();
|
||||
byte[] result;
|
||||
if (IsRaw)
|
||||
{
|
||||
result = _cSharpDataTransferMQRPC.PredictRaw(byteArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _cSharpDataTransferMQRPC.Predict(byteArray);
|
||||
}
|
||||
|
||||
|
||||
byte[] resultColor;
|
||||
|
||||
if (IsRaw)
|
||||
{
|
||||
resultColor = result.Chunk(25).Select(x =>
|
||||
{
|
||||
return x.Select((x, i) => allowedFlags[i] == 1 ? (byte) (x) : (byte) 0).Max();
|
||||
}).ToArray();
|
||||
//resultColor = result.Select(x => (byte)(x)).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
resultColor = result.Select(x => allowedFlags[x]==1? (byte)(x * 255) : (byte)0).ToArray();
|
||||
resultColor = result.Select(x => allowedFlags[x]==1? (byte)(x * 255.0) : (byte)0).ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ public class RawModelAIOperation: BaseOperation
|
||||
public RawModelAIOperation()
|
||||
{
|
||||
|
||||
_cSharpDataTransferMQRPC = PythonModelProxyRPC.GetInterface();
|
||||
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
|
||||
CanHaveProcessingError = false;
|
||||
}
|
||||
|
||||
private CSharpDataTransferMQRPC _cSharpDataTransferMQRPC;
|
||||
private CSharpDataTransferHTTP _cSharpDataTransferMQRPC;
|
||||
private int _imageWidth;
|
||||
private int _imageHeight;
|
||||
|
||||
@@ -66,34 +66,22 @@ public class RawModelAIOperation: BaseOperation
|
||||
var absPath = Path.GetFullPath(Path.Combine(dir, ModelFilePath.Path));
|
||||
ModelFilePath.Path = absPath;
|
||||
}
|
||||
|
||||
_cSharpDataTransferMQRPC.TransferData<object>(new MethodCall("load_model", ModelFilePath.Path, _modelName));
|
||||
_cSharpDataTransferMQRPC.LoadModel(ModelFilePath.Path, _modelName);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
_cSharpDataTransferMQRPC.ActivateModel(_modelName);
|
||||
|
||||
_cSharpDataTransferMQRPC.TransferData<object>(new MethodCall("activate_model", _modelName));
|
||||
|
||||
var sizeEncoded = _cSharpDataTransferMQRPC.TransferData<object[]>(new MethodCall("get_accept_size"));
|
||||
var sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize();
|
||||
var size = sizeEncoded.Select(Convert.ToInt32).ToArray();
|
||||
_imageWidth = size[1];
|
||||
_imageHeight = size[2];
|
||||
_imageWidth = size[0];
|
||||
_imageHeight = size[1];
|
||||
|
||||
var outputSize = _cSharpDataTransferMQRPC.TransferData<object[]>(new MethodCall("get_output_size"));
|
||||
var outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
|
||||
var outputSizeArray = outputSize.Select(Convert.ToInt32).ToArray();
|
||||
|
||||
int classes;
|
||||
if (outputSizeArray.Length<3)
|
||||
{
|
||||
classes=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
classes = outputSizeArray[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
var currentImage = context.ActiveImage;
|
||||
|
||||
@@ -105,20 +93,11 @@ public class RawModelAIOperation: BaseOperation
|
||||
|
||||
var allowed = FilterClasses.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x)).ToArray();
|
||||
|
||||
var allowedFlags = new byte[128];
|
||||
foreach (var i in allowed)
|
||||
{
|
||||
allowedFlags[i] = 1;
|
||||
}
|
||||
|
||||
var resultEncoded = _cSharpDataTransferMQRPC.TransferData<object[]>(new MethodCall("predict_raw", byteArray));
|
||||
|
||||
var result = resultEncoded.Select(Convert.ToByte).ToArray();
|
||||
var result = _cSharpDataTransferMQRPC.PredictRaw(byteArray);
|
||||
|
||||
byte[] resultColor;
|
||||
resultColor = result.Select(x => (byte)(x) ).ToArray();
|
||||
|
||||
|
||||
// black and white mask
|
||||
var mask = new Mat(outputSizeArray[0], outputSizeArray[1], MatType.CV_8UC1, resultColor);
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public class YoloDetectionOperation:BaseOperation
|
||||
}
|
||||
protected override void InterpretInternal(Context context)
|
||||
{
|
||||
CheckImageExists(context);
|
||||
CheckColorful(context);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Reflection;
|
||||
using Compunet.YoloV8;
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
||||
using Hawkeye.VisionBuilder.Workflow.Operations.Attributes;
|
||||
using Microsoft.Scripting.Runtime;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Workflow.Operations.Scripts;
|
||||
|
||||
[Category("Script")]
|
||||
public class CSharpProcessOperation:BaseOperation
|
||||
{
|
||||
private FilePath _scriptFilePath = new FilePath() { Format = "C# dll(*.dll)|*.dll" };
|
||||
private bool _initialized;
|
||||
private object _processor;
|
||||
private MethodInfo? _method;
|
||||
|
||||
|
||||
public FilePath ScriptFilePath
|
||||
{
|
||||
get => _scriptFilePath;
|
||||
set
|
||||
{
|
||||
_scriptFilePath = value;
|
||||
_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EnsureInitialized()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (!File.Exists(ScriptFilePath?.Path))
|
||||
{
|
||||
this.SetError("Script file not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var absPath = Path.GetFullPath(ScriptFilePath?.Path);
|
||||
var loadContext = new PluginLoadContext(absPath);
|
||||
var assembly = loadContext.LoadFromAssemblyPath(absPath);
|
||||
|
||||
// find class that has Process method
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var method = type.GetMethod("ProcessImage", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (method != null)
|
||||
{
|
||||
_processor = Activator.CreateInstance(type);
|
||||
_method = method;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
Label = Path.GetFileNameWithoutExtension(ScriptFilePath?.Path);
|
||||
_initialized = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void InterpretInternal(Context context)
|
||||
{
|
||||
CheckImageExists(context);
|
||||
if (!EnsureInitialized()) return;
|
||||
|
||||
var image = context.ActiveImage.ImageData;
|
||||
var result = (Mat)_method.Invoke(_processor, new []{ image });
|
||||
|
||||
context.ActiveImage = new HawkeyeImage()
|
||||
{
|
||||
ImageData = result
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.Workflow.Operations.Scripts;
|
||||
|
||||
class PluginLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private AssemblyDependencyResolver _resolver;
|
||||
|
||||
public PluginLoadContext(string pluginPath)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(pluginPath);
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (assemblyPath != null)
|
||||
{
|
||||
return LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
||||
if (libraryPath != null)
|
||||
{
|
||||
return LoadUnmanagedDllFromPath(libraryPath);
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user