修改框架(未完全完成)实现单个相机分开绑定算法
This commit is contained in:
		| @@ -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"); | ||||
|  | ||||
|             ////训练阶段(相机3)9.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"); | ||||
|  | ||||
|             //训练阶段(相机3)9.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; | ||||
|             // 确保是 32bppArgb(BGRA 内存布局) | ||||
|             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(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user