ソースを参照

20260205001 添加相机回调取图,增加有批处理的图像处理线程,注释掉一些当前情况下会报错的引用。

向羽 孟 2 週間 前
コミット
c02e8b6dbd

+ 62 - 1
MvvmScaffoldFrame48.DLL/CameraTools/HikCamera.cs

@@ -1,5 +1,6 @@
 // 海康相机实例类
 using MvCameraControl;
+using MvFGCtrlC.NET;
 using MvvmScaffoldFrame48.Model.StorageModel.HikVisionCamera;
 using System;
 using System.Collections.Generic;
@@ -26,6 +27,12 @@ namespace MvvmScaffoldFrame48.DLL.CameraTools
                 return _device;
             }
         }
+
+        /// <summary>
+        /// 设备事件委托
+        /// </summary>
+        public event EventHandler<FrameGrabbedEventArgs> FrameGrabbedEvent;
+
         #endregion
 
         #region 变量
@@ -198,6 +205,31 @@ namespace MvvmScaffoldFrame48.DLL.CameraTools
             return result;
         }
 
+        public bool StartReceiveFuntionSetFrameGrabedEvent()
+        {
+            bool result = false;
+            try
+            {
+                if (_device == null) return result;
+                _device.StreamGrabber.SetImageNodeNum(30);
+
+                // 注册回调函数
+                _device.StreamGrabber.FrameGrabedEvent += FrameGrabbedEventHandler;
+
+
+                int ret = _device.StreamGrabber.StartGrabbing();
+                if (ret == MvError.MV_OK)
+                {
+                    result = true;
+                }
+            }
+            catch
+            {
+                result = false;
+            }
+            return result;
+        }
+
         /// <summary>
         /// 关闭采集
         /// </summary>
@@ -220,6 +252,28 @@ namespace MvvmScaffoldFrame48.DLL.CameraTools
             return result;
         }
 
+        public bool StopReceiveFuntionSetFrameGrabedEvent()
+        {
+            bool result = false;
+            try
+            {
+                if (_device == null) return result;
+
+                _device.StreamGrabber.FrameGrabedEvent -= FrameGrabbedEventHandler;
+
+                int ret = _device.StreamGrabber.StopGrabbing();
+                if (ret == MvError.MV_OK)
+                {
+                    result = true;
+                }
+            }
+            catch
+            {
+                result = false;
+            }
+            return result;
+        }
+
         /// <summary>
         /// 获取一次图片,需开启采集
         /// </summary>
@@ -278,7 +332,14 @@ namespace MvvmScaffoldFrame48.DLL.CameraTools
         #endregion
 
         #region 私有方法
-
+        /// <summary>
+        /// 图像抓取事件处理方法
+        /// </summary>
+        private void FrameGrabbedEventHandler(object sender,FrameGrabbedEventArgs frameData)
+        {
+            // 触发外部注册的事件
+            FrameGrabbedEvent?.Invoke(this, frameData);
+        }
         #endregion
     }
 }

+ 1 - 1
MvvmScaffoldFrame48.DLL/ImageAlgorithm/ProcessingAlgorithm_CCDShuLi.cs

@@ -93,7 +93,7 @@ namespace MvvmScaffoldFrame48.DLL.ImageAlgorithm
                     }
                     //转为历史物体,添加缺少的参数
                     item.Num = Interlocked.Increment(ref ObjectNum) + 1;
-                    item.ChannelNO = ActiveChannel(item);
+                    //item.ChannelNO = ActiveChannel(item);
                     item.EndCheckTime = DateTime.Now;
                     item.MaxLength = GetActionMaxLength(item.RowsData);
                     if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)

+ 153 - 6
MvvmScaffoldFrame48.DLL/ThreadManager/CameraGroup.cs

@@ -5,6 +5,7 @@ using MvvmScaffoldFrame48.Model.ResultModel;
 using MvvmScaffoldFrame48.Model.StorageModel.SystemConfig;
 using System;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -70,18 +71,22 @@ namespace MvvmScaffoldFrame48.DLL.ThreadManager
             // 根据配置加载算法
             LoadAlgorithmFromConfiguration();
 
-            Camera.StartReceiveFuntion();
+            Camera.FrameGrabbedEvent += CameraCaptureLoop;
+
+            //Camera.StartReceiveFuntion();
+            Camera.StartReceiveFuntionSetFrameGrabedEvent();
 
             // 重置取消令牌
             CancellationTokenSource = new CancellationTokenSource();
             var token = CancellationTokenSource.Token;
 
             // 启动相机采集线程
-            CameraTask = Task.Factory.StartNew(() => CameraCaptureLoop(token),
-                token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+            //CameraTask = Task.Factory.StartNew(() => CameraCaptureLoop(token),
+            //    token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+
 
             // 启动图像处理线程
-            ProcessingTask = Task.Factory.StartNew(() => ImageProcessingLoop(token),
+            ProcessingTask = Task.Factory.StartNew(() => ImageProcessingbatchLoop(token),
                 token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
 
             IsRunning = true;
@@ -114,7 +119,7 @@ namespace MvvmScaffoldFrame48.DLL.ThreadManager
             }
 
             IsRunning = false;
-            Camera.StopReceiveFuntion();
+            Camera.StopReceiveFuntionSetFrameGrabedEvent();
             Console.WriteLine($"相机组 {CameraId} 已停止");
         }
 
@@ -271,7 +276,12 @@ namespace MvvmScaffoldFrame48.DLL.ThreadManager
                     {
                         // 将图像放入队列
                         ImageQueue.Enqueue(imageData.Image.Clone() as IImage);
-                        Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}");
+                        double QuTuYanshi = (DateTime.Now - FromUnixTimestamp((long)imageData.HostTimeStamp)).TotalMilliseconds;
+                        //Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}");
+                        if(QuTuYanshi > 12)
+                        {
+                            Console.WriteLine($"识别落后时间{QuTuYanshi}");
+                        }
                         QueueSemaphore.Release(); // 通知处理线程有新数据
                         imageData.Dispose();
                     }
@@ -289,8 +299,36 @@ namespace MvvmScaffoldFrame48.DLL.ThreadManager
             }
         }
 
+        /// <summary>
+        /// 相机回调取图
+        /// 与循环取图二选一
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        private void CameraCaptureLoop(object sender, FrameGrabbedEventArgs e)
+        {
+            try
+            {
+                // 将图像放入队列
+                ImageQueue.Enqueue(e.FrameOut.Image.Clone() as IImage);
+                double QuTuYanshi = (DateTime.Now - FromUnixTimestamp((long)e.FrameOut.HostTimeStamp)).TotalMilliseconds;
+                //Console.WriteLine($"相机 {CameraId} 采集到图像,帧号{imageData.FrameNum}");
+                if (QuTuYanshi > 12)
+                {
+                    Console.WriteLine($"识别落后时间{QuTuYanshi}");
+                }
+                QueueSemaphore.Release(); // 通知处理线程有新数据
+                e.FrameOut.Dispose();
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"相机 {CameraId} 采集异常: {ex.Message}");
+            }
+        }
+
         /// <summary>
         /// 图像处理循环
+        /// 适用取图频率不高的处理,或者处理速度远快于取图速度的方法
         /// </summary>
         private async void ImageProcessingLoop(CancellationToken token)
         {
@@ -325,6 +363,115 @@ namespace MvvmScaffoldFrame48.DLL.ThreadManager
                 Console.WriteLine($"相机 {CameraId} 处理异常: {ex.Message}");
             }
         }
+
+        /// <summary>
+        /// 图像处理循环-引入批处理
+        /// 处理速度与取图速度的差不多的时候
+        /// </summary>
+        /// <param name="token"></param>
+        private async void ImageProcessingbatchLoop(CancellationToken token)
+        {
+            const int batchSize = 5; // 每批处理的最大图像数量
+            var batch = new List<IImage>(batchSize); // 存储当前批次的图像
+
+            try
+            {
+                while (!token.IsCancellationRequested)
+                {
+                    // 等待至少一张图像数据
+                    await QueueSemaphore.WaitAsync(token);
+
+                    // 收集一批图像(严格按队列顺序)
+                    while (batch.Count < batchSize && ImageQueue.TryDequeue(out IImage imageData))
+                    {
+                        batch.Add(imageData);
+                        if (ImageQueue.Count > 0)
+                        {
+                            QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程
+                        }
+                    }
+
+                    // 顺序处理当前批次的所有图像
+                    foreach (var image in batch)
+                    {
+                        ProcessImage(image, CameraId); // 严格按顺序执行处理逻辑
+                        image.Dispose(); // 处理完成后立即释放资源
+                    }
+
+                    // 清空当前批次,准备下一轮处理
+                    batch.Clear();
+
+                    // 如果队列为空,短暂休眠以避免过度占用CPU
+                    if (ImageQueue.Count == 0)
+                    {
+                        await Task.Delay(1, token); // 固定短时间休眠
+                    }
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                // 线程被取消,正常退出
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"相机 {CameraId} 处理异常: {ex.Message}");
+            }
+        }
         #endregion
+
+        public DateTime FromUnixTimestamp(long timestamp, bool isMilliseconds = false)
+        {
+            try
+            {
+                // 如果未指定单位,尝试自动检测
+                if (!isMilliseconds)
+                {
+                    // 如果时间戳看起来像毫秒级(13位数字)
+                    if (timestamp.ToString().Length > 10)
+                    {
+                        isMilliseconds = true;
+                    }
+                }
+
+                if (isMilliseconds)
+                {
+                    // 验证毫秒级时间戳范围
+                    const long minMilliTimestamp = -62135596800000; // 0001-01-01 00:00:00 UTC
+                    const long maxMilliTimestamp = 253402300799999; // 9999-12-31 23:59:59 UTC
+
+                    if (timestamp < minMilliTimestamp || timestamp > maxMilliTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "毫秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+                else
+                {
+                    // 验证秒级时间戳范围
+                    const long minTimestamp = -62135596800;
+                    const long maxTimestamp = 253402300799;
+
+                    if (timestamp < minTimestamp || timestamp > maxTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                throw new ArgumentException($"无法转换时间戳 {timestamp}: {ex.Message}", ex);
+            }
+        }
     }
 }

+ 1 - 1
MvvmScaffoldFrame48.MODEL/StorageModel/ImageAlgorithm/ShuLI/ShuLiConfigClassModel.cs

@@ -19,7 +19,7 @@ namespace MvvmScaffoldFrame48.Model.StorageModel.ImageAlgorithm.ShuLI
         /// <summary>
         /// 二值化阈值
         /// </summary>
-        public int RegionThreshold { get; set; } = 30;
+        public int RegionThreshold { get; set; } = 80;
         /// <summary>
         /// 允许物体中断的最大连续行数
         /// </summary>