|
|
@@ -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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|