wsl support
This commit is contained in:
@@ -8,6 +8,7 @@ public class CSharpDataTransferHTTP
|
|||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
private readonly CancellationTokenSource _pingCts = new();
|
private readonly CancellationTokenSource _pingCts = new();
|
||||||
private readonly Thread _pingThread;
|
private readonly Thread _pingThread;
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
public CSharpDataTransferHTTP()
|
public CSharpDataTransferHTTP()
|
||||||
{
|
{
|
||||||
@@ -15,22 +16,17 @@ public class CSharpDataTransferHTTP
|
|||||||
{
|
{
|
||||||
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(10),
|
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(10),
|
||||||
MaxConnectionsPerServer = 100
|
MaxConnectionsPerServer = 100
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
_client = new HttpClient(handler)
|
_client = new HttpClient(handler)
|
||||||
{
|
{
|
||||||
// i've added this
|
|
||||||
DefaultRequestHeaders =
|
DefaultRequestHeaders =
|
||||||
{
|
{
|
||||||
ExpectContinue = false
|
ExpectContinue = false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
_api = new PythonModelAPI("http://localhost:8000",_client);
|
_api = new PythonModelAPI("http://localhost:8000", _client);
|
||||||
_pingThread = new Thread(PingLoop) { IsBackground = true };
|
_pingThread = new Thread(PingLoop) { IsBackground = true };
|
||||||
_pingThread.Start();
|
_pingThread.Start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PingLoop()
|
private void PingLoop()
|
||||||
@@ -51,38 +47,40 @@ public class CSharpDataTransferHTTP
|
|||||||
|
|
||||||
public void LoadModel(string path, string modelName)
|
public void LoadModel(string path, string modelName)
|
||||||
{
|
{
|
||||||
_api.LoadModelAsync(new LoadModelIn()
|
lock (_lock)
|
||||||
{
|
{
|
||||||
Name = modelName,
|
_api.LoadModelAsync(new LoadModelIn()
|
||||||
Path = Path.GetFileName(path)
|
{
|
||||||
}).Wait();
|
Name = modelName,
|
||||||
|
Path = Path.GetFileName(path)
|
||||||
|
}).Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ActivateModel(string modelName)
|
public void ActivateModel(string modelName)
|
||||||
{
|
{
|
||||||
_api.ActivateModelAsync(new ActivateModelIn(){Name = modelName}).Wait();
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_api.ActivateModelAsync(new ActivateModelIn() { Name = modelName }).Wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] GetAcceptSize()
|
public int[] GetAcceptSize()
|
||||||
{
|
{
|
||||||
var response = _api.GetAcceptSizeAsync().Result;
|
var response = _api.GetAcceptSizeAsync().Result;
|
||||||
|
|
||||||
return response.Accept_size.ToArray();
|
return response.Accept_size.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] GetOutputSize()
|
public int[] GetOutputSize()
|
||||||
{
|
{
|
||||||
var response = _api.GetOutputSizeAsync().Result;
|
var response = _api.GetOutputSizeAsync().Result;
|
||||||
|
|
||||||
return response.Accept_size.ToArray();
|
return response.Accept_size.ToArray();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] PredictRaw(byte[] byteArray)
|
public byte[] PredictRaw(byte[] byteArray)
|
||||||
{
|
{
|
||||||
var response = _api.PredictRawAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
var response = _api.PredictRawAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
||||||
var stream = response.Stream;
|
var stream = response.Stream;
|
||||||
// read the stream to byte array
|
|
||||||
using var memoryStream = new MemoryStream();
|
using var memoryStream = new MemoryStream();
|
||||||
stream.CopyTo(memoryStream);
|
stream.CopyTo(memoryStream);
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
@@ -92,16 +90,15 @@ public class CSharpDataTransferHTTP
|
|||||||
|
|
||||||
public byte[] Predict(byte[] byteArray)
|
public byte[] Predict(byte[] byteArray)
|
||||||
{
|
{
|
||||||
var response = _api.PredictAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
lock (_lock)
|
||||||
var stream = response.Stream;
|
{
|
||||||
// read the stream to byte array
|
var response = _api.PredictAsync(new FileParameter(new MemoryStream(byteArray))).Result;
|
||||||
using var memoryStream = new MemoryStream();
|
var stream = response.Stream;
|
||||||
stream.CopyTo(memoryStream);
|
using var memoryStream = new MemoryStream();
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
stream.CopyTo(memoryStream);
|
||||||
var responseBytes = memoryStream.ToArray();
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
return responseBytes;
|
var responseBytes = memoryStream.ToArray();
|
||||||
|
return responseBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using static IronPython.Modules._ast;
|
||||||
|
|
||||||
|
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
|
||||||
|
|
||||||
|
public class DockerScriptRunner
|
||||||
|
{
|
||||||
|
private Process process;
|
||||||
|
private readonly string _workingDirectory;
|
||||||
|
|
||||||
|
public DockerScriptRunner(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()
|
||||||
|
{
|
||||||
|
|
||||||
|
var killContainerArg = @"-d ubuntu -u root -e sh -c ""docker rm -f cv || true""";
|
||||||
|
var killProcess = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo("wsl", killContainerArg)
|
||||||
|
{
|
||||||
|
UseShellExecute = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
killProcess.Start();
|
||||||
|
killProcess.WaitForExit();
|
||||||
|
killProcess.Dispose();
|
||||||
|
|
||||||
|
//startInfo.CreateNoWindow = true;
|
||||||
|
// create and start the process
|
||||||
|
process = new Process();
|
||||||
|
var convertedWd = ConvertToWslPath(_workingDirectory);
|
||||||
|
var arg =
|
||||||
|
$@"-d ubuntu -u root -e sh -c ""cd \""{convertedWd}\"" && docker run --rm --name cv --gpus all -it -p 8000:8000 -v \""$(pwd)\"":/app -w /app cv uvicorn --host 0.0.0.0 PythonModelAPI:app """;
|
||||||
|
|
||||||
|
Console.WriteLine("args:"+arg);
|
||||||
|
process.StartInfo = new ProcessStartInfo("wsl", arg)
|
||||||
|
{
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,53 @@
|
|||||||
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Hawkeye.VisionBuilder.Workflow.DataTransfer;
|
||||||
|
|
||||||
public class PythonModelProxyHTTP
|
public class PythonModelProxyHTTP
|
||||||
{
|
{
|
||||||
private static bool _isInitialized = false;
|
private static bool _isInitialized = false;
|
||||||
private static UvicornScriptRunner _pythonScriptRunner;
|
private static DockerScriptRunner _pythonScriptRunner;
|
||||||
private static CSharpDataTransferHTTP _cSharpDataTransferHTTP;
|
private static CSharpDataTransferHTTP _cSharpDataTransferHTTP;
|
||||||
|
|
||||||
|
static object _lock = new object();
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
{
|
{
|
||||||
if (_isInitialized) return;
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_isInitialized) return;
|
||||||
|
_isInitialized = true;
|
||||||
|
|
||||||
var workDirAbsPath = Path.GetFullPath(@"..\Data\Models");
|
var workDirAbsPath = Path.GetFullPath(@"..\Data\Models");
|
||||||
_pythonScriptRunner = new UvicornScriptRunner(workDirAbsPath);
|
_pythonScriptRunner = new DockerScriptRunner(workDirAbsPath);
|
||||||
_cSharpDataTransferHTTP = new CSharpDataTransferHTTP();
|
|
||||||
|
|
||||||
_pythonScriptRunner.Start();
|
|
||||||
Thread.Sleep(1000); // Wait for the server to start
|
_pythonScriptRunner.Start();
|
||||||
|
// Wait for the server to start by checking if port 8000 is open
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpClient http = new HttpClient();
|
||||||
|
// check if 404 occurs on localhost:8000
|
||||||
|
var response = http.GetAsync("http://127.0.0.1:8000/").Result;
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.NotFound ||
|
||||||
|
response.StatusCode == System.Net.HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
break; // Port is open
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Log.Debug("Waiting for port to open");
|
||||||
|
// Ignore exceptions and retry
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(1000); // Wait before retrying
|
||||||
|
}
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
_cSharpDataTransferHTTP = new CSharpDataTransferHTTP();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CSharpDataTransferHTTP GetInterface()
|
public static CSharpDataTransferHTTP GetInterface()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class RawModelAIOperation: BaseOperation
|
|||||||
public RawModelAIOperation()
|
public RawModelAIOperation()
|
||||||
{
|
{
|
||||||
|
|
||||||
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
|
|
||||||
CanHaveProcessingError = false;
|
CanHaveProcessingError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +46,7 @@ public class RawModelAIOperation: BaseOperation
|
|||||||
|
|
||||||
protected override void InterpretInternal(Context context)
|
protected override void InterpretInternal(Context context)
|
||||||
{
|
{
|
||||||
|
_cSharpDataTransferMQRPC = PythonModelProxyHTTP.GetInterface();
|
||||||
if (!CheckImageExists(context)) return;
|
if (!CheckImageExists(context)) return;
|
||||||
if (!CheckColorful(context)) return;
|
if (!CheckColorful(context)) return;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ public class RawModelAIOperation: BaseOperation
|
|||||||
{
|
{
|
||||||
if (!File.Exists(ModelFilePath?.Path))
|
if (!File.Exists(ModelFilePath?.Path))
|
||||||
{
|
{
|
||||||
this.SetError("Model file not found");
|
this.SetError($"Model file \"{ModelFilePath?.Path}\" not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,31 +75,47 @@ public class RawModelAIOperation: BaseOperation
|
|||||||
_cSharpDataTransferMQRPC.ActivateModel(_modelName);
|
_cSharpDataTransferMQRPC.ActivateModel(_modelName);
|
||||||
|
|
||||||
|
|
||||||
var sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize();
|
int[] sizeEncoded=[];
|
||||||
var size = sizeEncoded.Select(Convert.ToInt32).ToArray();
|
int[] outputSize = [];
|
||||||
_imageWidth = size[0];
|
sizeEncoded = _cSharpDataTransferMQRPC.GetAcceptSize();
|
||||||
_imageHeight = size[1];
|
outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
|
||||||
|
|
||||||
var outputSize = _cSharpDataTransferMQRPC.GetOutputSize();
|
|
||||||
var outputSizeArray = outputSize.Select(Convert.ToInt32).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var currentImage = context.ActiveImage;
|
var currentImage = context.ActiveImage;
|
||||||
|
|
||||||
var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB);
|
var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||||
|
|
||||||
|
|
||||||
|
var size = sizeEncoded.Select(Convert.ToInt32).ToArray();
|
||||||
|
_imageWidth = size[0];
|
||||||
|
_imageHeight = size[1];
|
||||||
|
|
||||||
var resized = rightColor.Resize(new Size(_imageHeight, _imageWidth));
|
var resized = rightColor.Resize(new Size(_imageHeight, _imageWidth));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int dataSize = resized.Rows * resized.Cols * resized.ElemSize();
|
int dataSize = resized.Rows * resized.Cols * resized.ElemSize();
|
||||||
var byteArray = new byte[dataSize];
|
var byteArray = new byte[dataSize];
|
||||||
Marshal.Copy(resized.Data, byteArray, 0, dataSize);
|
Marshal.Copy(resized.Data, byteArray, 0, dataSize);
|
||||||
|
|
||||||
var allowed = FilterClasses.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x)).ToArray();
|
|
||||||
|
|
||||||
var result = _cSharpDataTransferMQRPC.PredictRaw(byteArray);
|
var result = _cSharpDataTransferMQRPC.PredictRaw(byteArray);
|
||||||
|
|
||||||
byte[] resultColor;
|
byte[] resultColor;
|
||||||
resultColor = result.Select(x => (byte)(x) ).ToArray();
|
resultColor = result.Select(x => (byte)(x) ).ToArray();
|
||||||
|
|
||||||
// black and white mask
|
|
||||||
|
var outputSizeArray = outputSize.Select(Convert.ToInt32).ToArray();
|
||||||
var mask = new Mat(outputSizeArray[0], outputSizeArray[1], MatType.CV_8UC1, resultColor);
|
var mask = new Mat(outputSizeArray[0], outputSizeArray[1], MatType.CV_8UC1, resultColor);
|
||||||
|
|
||||||
var maskResized = mask.Resize(new Size(currentImage.ImageData.Width, currentImage.ImageData.Height));
|
var maskResized = mask.Resize(new Size(currentImage.ImageData.Width, currentImage.ImageData.Height));
|
||||||
|
|||||||
Reference in New Issue
Block a user