修改框架(未完全完成)实现单个相机分开绑定算法

This commit is contained in:
2025-10-20 17:47:48 +08:00
parent 31d9f8d6b6
commit 73249ee6c2
11 changed files with 1226 additions and 422 deletions

View File

@@ -25,6 +25,8 @@ namespace Check.Main.Infer
/// </summary>
private static int _enabledCameraCount = 0;
private static long _productCounter = 0; // 新增产品计数器10.22
public static event EventHandler<DetectionResultEventArgs> OnDetectionCompleted;
public static bool IsDetectionRunning { get; private set; } = false;
@@ -51,72 +53,220 @@ namespace Check.Main.Infer
public static void Initialize(List<CameraSettings> cameraSettings, List<ModelSettings> modelSettings)
{
Shutdown(); // 先关闭旧的
YoloModelManager.Initialize(modelSettings); // 确保 YOLO 模型在初始化协调器时加载。10.22新增
var enabledCameras = cameraSettings.Where(c => c.IsEnabled).ToList();
_enabledCameraCount = enabledCameras.Count;
if (_enabledCameraCount == 0) return;
//if (_enabledCameraCount == 0) return;
if (_enabledCameraCount == 0)
{
ThreadSafeLogger.Log("没有启用的相机,检测协调器未初始化。");
return;
}
foreach (var camSetting in enabledCameras)
{
// 找到与相机编号匹配的模型
var model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID);
ModelSettings model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID);
if (model == null)
{
ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像。");
//ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像");
ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 匹配的模型 (ID: {camSetting.ModelID})。该相机将无法处理图像。");
continue;
}
var processor = new CameraProcessor(camSetting.CameraIndex,camSetting.ModelID);
IDetector detector = null;
object detectorSettings = null; // 用于传递特定检测器的设置
// 根据相机的 CheckType 和模型的 AlgorithmType 决定使用哪个检测器
if (camSetting.CheckType == CheckType.Traditional && model.M_AType == AlgorithmType.Tradition)
{
detector = new HalconTemplateDetector();
detectorSettings = new HalconDetectionSettings { ScoreThreshold = model.HalconScoreThreshold };
ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 HALCON 传统算法。");
}
else if (camSetting.CheckType == CheckType.DeepLearning && model.M_AType == AlgorithmType.DeepLearning)
{
detector = new YoloDetector();
detectorSettings = new YoloDetectionSettings
{
ConfidenceThreshold = model.YoloConfidenceThreshold,
NmsThreshold = model.YoloNmsThreshold
};
ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 YOLO 深度学习算法。");
}
else
{
ThreadSafeLogger.Log($"[警告] 相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 的 CheckType ({camSetting.CheckType}) 与模型 (ID: {model.Id}, AlgorithmType: {model.M_AType}) 不匹配或不支持。跳过此相机。");
continue;
}
// 初始化检测器
try
{
// 对于YOLOmodelPath实际上传递的是ModelID
// 对于HALCONmodelPath是实际的模板目录
string initPath = (detector is YoloDetector) ? model.Id.ToString() : model.Path;
detector.Initialize(initPath, detectorSettings);
}
catch (Exception ex)
{
ThreadSafeLogger.Log($"[错误] 初始化相机 #{camSetting.CameraIndex} 的检测器失败: {ex.Message}");
detector?.Dispose();
continue;
}
var processor = new CameraProcessor(camSetting.CameraIndex, detector, model);
_processors.TryAdd(camSetting.CameraIndex, processor);
processor.Start();
}
ThreadSafeLogger.Log($"检测协调器已初始化,启动了 {_processors.Count} 个相机处理线程。");
}
//public static void EnqueueImage(int cameraIndex, Bitmap bmp)
//{
// if (_processors.TryGetValue(cameraIndex, out var processor))
// {
// processor.EnqueueImage(bmp);
// }
// else
// {
// // 如果找不到处理器必须释放Bitmap防止泄漏
// bmp?.Dispose();
// }
//}
public static void EnqueueImage(int cameraIndex, Bitmap bmp)
{
// 在图像进入队列之前生成一个新的产品ID
long currentProductId;
lock (_productAssemblies) // 同步访问产品计数器
{
_productCounter++;
currentProductId = _productCounter;
}
if (_processors.TryGetValue(cameraIndex, out var processor))
{
processor.EnqueueImage(bmp);
processor.EnqueueImage(bmp, currentProductId); // 传递产品ID
}
else
{
// 如果找不到处理器必须释放Bitmap防止泄漏
bmp?.Dispose();
bmp?.Dispose(); // 如果找不到处理器必须释放Bitmap防止泄漏
ThreadSafeLogger.Log($"[警告] 未能为相机 {cameraIndex} 找到处理器,产品 {currentProductId} 的图像被丢弃。");
// 如果没有处理器,不需要在 _productAssemblies 中添加,因为不会有结果返回
}
}
// 供 CameraProcessor 回调,用以组装产品
public static void AssembleProduct(ImageData data, string result)
{
var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount));
//// 供 CameraProcessor 回调,用以组装产品
//public static void AssembleProduct(ImageData data, string result)
//{
// var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount));
if (assembly.AddResult(data.CameraIndex, result))
// if (assembly.AddResult(data.CameraIndex, result))
// {
// string finalResult = assembly.GetFinalResult();
// ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}");
// // 只有在检测运行时,才触发事件
// if (IsDetectionRunning)
// {
// OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK"));
// }
// if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly))
// {
// finishedAssembly.Dispose();
// }
// }
//}
// CameraProcessor 回调,用以组装产品
public static void AssembleProduct(long productId, int cameraIndex, bool isOk, Bitmap resultImage)
{
// GetOrAdd 确保 ProductAssembly 只被创建一次
var assembly = _productAssemblies.GetOrAdd(productId, (id) => new ProductAssembly(id, _enabledCameraCount));
assembly.AddResult(cameraIndex, isOk, resultImage);
// 检查产品是否已完成所有相机的检测
if (assembly.IsComplete())
{
string finalResult = assembly.GetFinalResult();
string finalResult = assembly.GetFinalResult() ? "OK" : "NG";
ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}");
// 只有在检测运行时,才触发事件
// 触发事件 (例如更新主UI上的总OK/NG计数)
if (IsDetectionRunning)
{
OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK"));
OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(assembly.GetFinalResult()));
}
if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly))
// PLC 写入逻辑
if (FrmMain.PlcClient != null) // 假设 FrmMain.PlcClient 可访问
{
finishedAssembly.Dispose();
try
{
if (assembly.GetFinalResult()) // 最终结果 OK
{
FrmMain.PlcClient.WriteBool("M90", true); // 写入M90为1
Thread.Sleep(50); // 短暂延时
FrmMain.PlcClient.WriteBool("M90", false);
}
else // 最终结果 NG
{
FrmMain.PlcClient.WriteBool("M91", true); // 写入M91为1
Thread.Sleep(50); // 短暂延时
FrmMain.PlcClient.WriteBool("M91", false);
}
ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 最终结果 {finalResult} 已写入PLC。");
}
catch (Exception ex)
{
ThreadSafeLogger.Log($"[错误] 写入PLC失败{ex.Message}");
}
}
else
{
ThreadSafeLogger.Log($"[警告] 产品 #{assembly.ProductId} 检测结果未能写入PLCPLC客户端未连接。");
}
// 移除并释放 ProductAssembly
if (_productAssemblies.TryRemove(productId, out var finishedAssembly))
{
finishedAssembly.Dispose(); // 释放所有存储的 Bitmap
}
}
}
/// <summary>
/// 命令所有活动的相机处理器重置它们的内部计数器。
/// </summary>
///// <summary>
///// 命令所有活动的相机处理器重置它们的内部计数器。
///// </summary>
//public static void ResetAllCounters()
//{
// foreach (var processor in _processors.Values)
// {
// processor.ResetCounter();
// }
// ThreadSafeLogger.Log("所有相机处理器的产品计数器已重置。");
//}
public static void ResetAllCounters()
{
lock (_productAssemblies)
{
_productCounter = 0;
// 清空所有未完成的产品,并释放其资源
foreach (var assembly in _productAssemblies.Values)
{
assembly.Dispose();
}
_productAssemblies.Clear();
}
foreach (var processor in _processors.Values)
{
processor.ResetCounter();
}
ThreadSafeLogger.Log("所有相机处理器产品计数器已重置。");
ThreadSafeLogger.Log("所有相机处理器产品计数器已重置。");
}
public static CameraProcessor GetProcessor(int cameraIndex)
@@ -142,8 +292,88 @@ namespace Check.Main.Infer
{
assembly.Dispose();
}
_productAssemblies.Clear();
YoloModelManager.Shutdown(); // 确保YOLO模型也关闭
ThreadSafeLogger.Log("检测协调器已关闭。");
}
}
// 新增 ProductAssembly 类,用于集中管理一个产品的检测结果和图像
public class ProductAssembly : IDisposable
{
public long ProductId { get; }
private readonly int _expectedCameraCount;
private readonly ConcurrentDictionary<int, bool> _cameraResults = new ConcurrentDictionary<int, bool>();
private readonly ConcurrentDictionary<int, Bitmap> _resultImages = new ConcurrentDictionary<int, Bitmap>(); // 存储每个相机的结果图像
private readonly object _lock = new object();
public ProductAssembly(long productId, int expectedCameraCount)
{
ProductId = productId;
_expectedCameraCount = expectedCameraCount;
}
/// <summary>
/// 添加单个相机的检测结果。
/// </summary>
/// <param name="cameraIndex">相机编号。</param>
/// <param name="isOk">检测结果是否为OK。</param>
/// <param name="resultImage">带有检测结果的图像。</param>
public void AddResult(int cameraIndex, bool isOk, Bitmap resultImage)
{
lock (_lock)
{
_cameraResults.TryAdd(cameraIndex, isOk);
if (resultImage != null)
{
// 克隆图像,确保 ProductAssembly 拥有其所有权
_resultImages.TryAdd(cameraIndex, (Bitmap)resultImage.Clone());
resultImage.Dispose(); // 释放传入的原始图像副本
}
}
}
/// <summary>
/// 检查是否所有相机都已提交结果。
/// </summary>
public bool IsComplete()
{
lock (_lock)
{
return _cameraResults.Count == _expectedCameraCount;
}
}
/// <summary>
/// 获取最终产品检测结果所有相机都OK才为OK
/// </summary>
public bool GetFinalResult()
{
lock (_lock)
{
return _cameraResults.Values.All(r => r);
}
}
/// <summary>
/// 获取某个相机的结果图像。
/// </summary>
public Bitmap GetResultImage(int cameraIndex)
{
_resultImages.TryGetValue(cameraIndex, out var bmp);
return bmp;
}
public void Dispose()
{
lock (_lock)
{
foreach (var bmp in _resultImages.Values)
{
bmp?.Dispose();
}
_resultImages.Clear();
_cameraResults.Clear();
}
}
}
}