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

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

@@ -12,13 +12,15 @@ using System.Text;
using System.Threading.Tasks;
using YoloDotNet.Extensions;
using OpenCvSharp;
using Check.Main.Infer; // 引入 IDetector, DetectionResult
namespace Check.Main.Camera
{
public class CameraProcessor : IDisposable
{
private readonly int _cameraIndex;
private readonly int _modeId;
// private readonly ModelSettings _model;
private readonly IDetector _detector; // 替换为接口
private readonly ModelSettings _modelSettings; // 保留模型设置以获取更多参数
private readonly BlockingCollection<ImageData> _imageQueue = new BlockingCollection<ImageData>();
private readonly Thread _workerThread;
private volatile bool _isRunning = false;
@@ -28,11 +30,12 @@ namespace Check.Main.Camera
public event EventHandler<ProcessingCompletedEventArgs> OnProcessingCompleted;
public CameraProcessor(int cameraIndex, int modelId)//, ModelSettings model
// 构造函数现在接受 IDetector 实例和 ModelSettings
public CameraProcessor(int cameraIndex, IDetector detector, ModelSettings modelSettings)
{
_cameraIndex = cameraIndex;
_modeId = modelId;
//_model = model;
_detector = detector ?? throw new ArgumentNullException(nameof(detector));
_modelSettings = modelSettings ?? throw new ArgumentNullException(nameof(modelSettings));
_workerThread = new Thread(ProcessQueue) { IsBackground = true, Name = $"Cam_{_cameraIndex}_Processor" };
}
@@ -42,15 +45,16 @@ namespace Check.Main.Camera
_workerThread.Start();
}
public void EnqueueImage(Bitmap bmp)
public void EnqueueImage(Bitmap bmp, long productId) // 接收产品ID
{
if (!_isRunning)
{
bmp?.Dispose();
return;
}
_imageCounter++;
_imageQueue.Add(new ImageData(_imageCounter, _cameraIndex, bmp));
// _imageCounter 在此用于内部跟踪不是产品ID
// 产品ID现在由 DetectionCoordinator 生成并传递
_imageQueue.Add(new ImageData(productId, _cameraIndex, bmp));
}
/// <summary>
@@ -58,42 +62,38 @@ namespace Check.Main.Camera
/// </summary>
private void ProcessQueue()
{
//// 从模型管理器获取此线程专属的YOLO模型
//var yoloModel = YoloModelManager.GetModel(_modeId);
//if (yoloModel == null)
ThreadSafeLogger.Log($"相机#{_cameraIndex} 启动处理线程,算法类型:{_modelSettings.M_AType}");
////训练HALCON模型
////训练阶段相机2
//var trainer = new LogoTemplateTrainer();
//trainer.TrainAndSaveTemplates(
// new List<string>
// {
// @"D:\HalconTemplateMatch\train2\logo1.bmp",
// @"D:\HalconTemplateMatch\train2\logo2.bmp",
// @"D:\HalconTemplateMatch\train2\logo3.bmp"
// },
// @"D:\HalconTemplateMatch\model_2");
////训练阶段相机39.25修改!!
//trainer.TrainAndSaveTemplates(
// new List<string>
// {
// @"D:\HalconTemplateMatch\train3\3C_1.bmp",
// @"D:\HalconTemplateMatch\train3\3C_2.bmp",
// @"D:\HalconTemplateMatch\train3\3C_3.bmp"
// },
// @"D:\HalconTemplateMatch\model_3");
//if (trainer == null)
//{
// ThreadSafeLogger.Log($"[错误] 相机 #{_modeId} 无法获取对应的YOLO模型,处理线程已中止。");
// return; // 如果没有模型,此线程无法工作
// ThreadSafeLogger.Log($"[错误] 相机 #{_modeId} 未加载模板,处理线程已中止。");
// return;
//}
//训练阶段相机2
var trainer = new LogoTemplateTrainer();
trainer.TrainAndSaveTemplates(
new List<string>
{
@"D:\HalconTemplateMatch\train2\logo1.bmp",
@"D:\HalconTemplateMatch\train2\logo2.bmp",
@"D:\HalconTemplateMatch\train2\logo3.bmp"
},
@"D:\HalconTemplateMatch\model_2");
//训练阶段相机39.25修改!!
trainer.TrainAndSaveTemplates(
new List<string>
{
@"D:\HalconTemplateMatch\train3\3C_1.bmp",
@"D:\HalconTemplateMatch\train3\3C_2.bmp",
@"D:\HalconTemplateMatch\train3\3C_3.bmp"
},
@"D:\HalconTemplateMatch\model_3");
if (trainer == null)
{
ThreadSafeLogger.Log($"[错误] 相机 #{_modeId} 未加载模板,处理线程已中止。");
return;
}
while (_isRunning)
{
@@ -103,116 +103,23 @@ namespace Check.Main.Camera
ImageData data = _imageQueue.Take();
using (data)
{
//using (var skImage = ConvertBitmapToSKImage(data.Image)) // 转换图像格式并管理其生命周期
//{
// if (skImage == null) continue;
// var predictions = yoloModel.RunObjectDetection(skImage);
// string result = predictions.Any() ? "NG" : "OK";
// ThreadSafeLogger.Log($"相机 #{_cameraIndex} 处理产品 #{data.ProductId},检测到 {predictions.Count} 个目标,结果: {result}");
// // 将处理结果交给协调器进行组装
// DetectionCoordinator.AssembleProduct(data, result);
// if (OnProcessingCompleted != null)
// {
// using (var resultSkImage = skImage.Draw(predictions))
// {
// // 4. 触发事件,将绘制好的 resultSkImage 传递出去
// // 所有权在这里被转移
// OnProcessingCompleted?.Invoke(this, new ProcessingCompletedEventArgs(
// _cameraIndex,
// data.ProductId,
// resultSkImage
// ));
// }
// }
//}
//***********************************使用Halcon模板匹配进行检测****************************************************
if (data.Image == null) continue;
// 统一定义预测结果
var matcher = new LogoMatcher();
//9.25(增加一根据不同的相机编号调用不同的模型!)
string filepath = "";
if (_cameraIndex == 2)
{
matcher.LoadTemplates(@"D:\HalconTemplateMatch\model_2");
filepath = "D:\\HalconTemplateMatch\\train2";
}
else if (_cameraIndex == 3)
{
matcher.LoadTemplates(@"D:\HalconTemplateMatch\model_3");
filepath = "D:\\HalconTemplateMatch\\train3";
}
////原bool返回的处理
//bool found = matcher.FindLogo(data.Image);
//string result = found ? "OK":"NG";
//double返回的处理
double score = matcher.FindLogo(data.Image);
//Mat cam = ProcessImg.BitmapToMat(data.Image);
//score= ProcessImg.ProcessImagesInFolder(filepath,cam);
string result = (score > 0.5) ? "OK" : "NG";
ThreadSafeLogger.Log($"相机 #{_cameraIndex} 处理产品 #{data.ProductId},结果: {result},得分: {score}");
DetectionResult detectionResult = _detector.Detect(data.Image);
ThreadSafeLogger.Log($"相机 #{_cameraIndex} 处理产品 #{data.ProductId},结果: {(detectionResult.IsOk ? "OK" : "NG")}, 信息: {detectionResult.Message}, 得分: {detectionResult.Score:F2}");
// 将处理结果交给协调器进行组装
DetectionCoordinator.AssembleProduct(data, result);
DetectionCoordinator.AssembleProduct(data.ProductId, data.CameraIndex, detectionResult.IsOk, detectionResult.ResultImage);
//给PLC的M90、M91写值10.10
if (FrmMain.PlcClient != null)
{
if (result == "OK")
{
//吹气到合格框
FrmMain.PlcClient.WriteBool("90", true); // 写入M90为1
// 延时复位
Task.Run(async () =>
{
//await Task.Delay(300); // 延时300毫秒可根据实际气动时间调整
//await FrmMain.PlcClient.WriteAsync("M90", 0);
});
}
else
{
//吹气到不合格框
FrmMain.PlcClient.WriteBool("91", true);// 写入M91为1
// 延时复位
Task.Run(async () =>
{
//await Task.Delay(300);
//await FrmMain.PlcClient.WriteAsync("M91", 0);
});
}
//完成一次检测进行刷新
Thread.Sleep(2000);
FrmMain.PlcClient.WriteBool("90", false); //
FrmMain.PlcClient.WriteBool("91", false); // 写入M90为1
}
else
{
ThreadSafeLogger.Log("6跳过写入。");
}
// ③ 外部订阅事件
// 外部订阅事件,传递结果图像
OnProcessingCompleted?.Invoke(
this,
new ProcessingCompletedEventArgs
(
_cameraIndex,
data.ProductId,
data.Image // 原图传出去
detectionResult.ResultImage // 传递带有绘制结果的图像
)
);
}
@@ -252,6 +159,7 @@ namespace Check.Main.Camera
}
public static SKBitmap ToSKBitmapFast(Bitmap bitmap)
{
if (bitmap == null) return null;
// 确保是 32bppArgbBGRA 内存布局)
Bitmap src = bitmap;
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
@@ -298,13 +206,15 @@ namespace Check.Main.Camera
}
}
}
public void Stop()
{
_isRunning = false;
// 解除阻塞,让线程可以检查 _isRunning 标志并退出
_imageQueue.CompleteAdding();
_workerThread.Join(500); // 等待线程结束
_workerThread.Join(5000); // 等待线程结束。10.22修改原来是500
}
/// <summary>
/// 线程安全地重置该相机的图像计数器。
/// </summary>
@@ -322,6 +232,27 @@ namespace Check.Main.Camera
{
Stop();
_imageQueue.Dispose();
_detector?.Dispose(); // 释放检测器资源
}
}
public class ProcessingCompletedEventArgs : EventArgs, IDisposable
{
public int CameraIndex { get; }
public long ProductId { get; }
public Bitmap ResultImage { get; } // 新增:带有检测结果的图像
public ProcessingCompletedEventArgs(int cameraIndex, long productId, Bitmap resultImage)
{
CameraIndex = cameraIndex;
ProductId = productId;
ResultImage = resultImage;
}
public void Dispose()
{
ResultImage?.Dispose();
}
}