|
|
@@ -4,9 +4,11 @@ using CCDCount.MODEL.ResultModel;
|
|
|
using CCDCount.MODEL.ShuLiModel;
|
|
|
using LogClass;
|
|
|
using MvCameraControl;
|
|
|
+using Org.BouncyCastle.Utilities;
|
|
|
using System;
|
|
|
using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
|
|
|
using System.Diagnostics;
|
|
|
using System.Drawing;
|
|
|
using System.IO;
|
|
|
@@ -14,6 +16,8 @@ using System.Linq;
|
|
|
using System.Runtime.InteropServices.WindowsRuntime;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
+using System.Windows.Media.Media3D;
|
|
|
+using static iTextSharp.text.pdf.AcroFields;
|
|
|
|
|
|
namespace CCDCount.DLL
|
|
|
{
|
|
|
@@ -109,39 +113,40 @@ namespace CCDCount.DLL
|
|
|
public bool ProcessImageSequence(IFrameOut image)
|
|
|
{
|
|
|
bool result = false;
|
|
|
+ ReadOnlySpan<byte> spanFromArr = image.Image.PixelData.AsSpan();
|
|
|
for (int i = 0; i < image.Image.Height; i++)
|
|
|
{
|
|
|
- result = ProcessLine(image, i);
|
|
|
+ result = ProcessLine2(spanFromArr,image.HostTimeStamp,image.Image.Width, i);
|
|
|
currentLine += 1;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- //Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
//识别到结果并输出
|
|
|
- //lock (_lockObj)
|
|
|
- //{
|
|
|
+
|
|
|
_rwLock.EnterReadLock();
|
|
|
// 清理超时未更新的物体
|
|
|
lostObjects = activeObjects
|
|
|
.Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
.ToList();
|
|
|
_rwLock.ExitReadLock();
|
|
|
- //}
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果筛选,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
+
|
|
|
|
|
|
List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
|
|
|
|
|
|
// 有物体转变为活跃物体,返回值转为true
|
|
|
if (lostObjects.Count > 0)
|
|
|
{
|
|
|
+ Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
result = true;
|
|
|
//stopwatch.Restart();
|
|
|
foreach (var item in lostObjects)
|
|
|
{
|
|
|
+
|
|
|
item.PictureEndReadTime = FromUnixTimestamp((long)image.HostTimeStamp);
|
|
|
+ DateTime NowTime = DateTime.Now;
|
|
|
+ var AllPictureUseTime = (item.PictureEndReadTime - item.PictureStartReadTime).TotalMilliseconds;
|
|
|
+ var PictureUsetime = (NowTime - item.PictureStartReadTime).TotalMilliseconds;
|
|
|
+ if (PictureUsetime - AllPictureUseTime > 20)
|
|
|
+ LOG.error($"算法耗时超过了20ms,相机取图总耗时:{AllPictureUseTime},从取第一张图到现在的耗时{PictureUsetime}");
|
|
|
//噪点判定
|
|
|
if (item.Area < shuLiConfig.NoiseFilter_Threshold)
|
|
|
{
|
|
|
@@ -159,8 +164,11 @@ namespace CCDCount.DLL
|
|
|
{
|
|
|
if((item.LastSeenLine - item.StartLine) < shuLiConfig.MAX_Idetify_Height)
|
|
|
{
|
|
|
- var CalculationResult = SizeCalculation2(item.RowsData);
|
|
|
- item.MaxLength = CalculationResult.MaxLength;
|
|
|
+ var CalculationResult = SizeCalculation(item.RowsData);
|
|
|
+ if(CalculationResult!=null)
|
|
|
+ {
|
|
|
+ item.MaxLength = CalculationResult.Height;
|
|
|
+ }
|
|
|
//item.hasSignificantConcavity = CalculationResult.hasSignificantConcavity;
|
|
|
//item.concavityRatio = CalculationResult.concavityRatio;
|
|
|
//if(CalculationResult.hasSignificantConcavity)
|
|
|
@@ -182,46 +190,46 @@ namespace CCDCount.DLL
|
|
|
{
|
|
|
if (item.StateCode == 8)
|
|
|
{
|
|
|
- LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
|
|
|
}
|
|
|
else if (item.StateCode == 7)
|
|
|
{
|
|
|
- LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
|
|
|
}
|
|
|
}
|
|
|
else if (item.Area < shuLiConfig.MinArea
|
|
|
&& (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
|
|
|
{
|
|
|
item.StateCode = 6;
|
|
|
- LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
|
|
|
- Console.WriteLine("颗粒编号{0}:面积过小", item.Num);
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
|
|
|
+ //Console.WriteLine("颗粒编号{0}:面积过小", item.Num);
|
|
|
}
|
|
|
else if (item.Area > shuLiConfig.MaxArea
|
|
|
&& (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
|
|
|
{
|
|
|
item.StateCode = 5;
|
|
|
- LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
|
|
|
- Console.WriteLine("颗粒编号{0}:面积过大", item.Num);
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
|
|
|
+ //Console.WriteLine("颗粒编号{0}:面积过大", item.Num);
|
|
|
}
|
|
|
else if (item.MaxLength < shuLiConfig.MIN_Object_LENGTH
|
|
|
&& (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
|
|
|
{
|
|
|
item.StateCode = 2;
|
|
|
- LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
|
|
|
- Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
|
|
|
+ //Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
|
|
|
}
|
|
|
else if (item.MaxLength > shuLiConfig.MAX_Object_LENGTH
|
|
|
&& (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
|
|
|
{
|
|
|
item.StateCode = 1;
|
|
|
- LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
|
|
|
- Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
|
|
|
+ //Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
item.StateCode = 0;
|
|
|
- LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
|
|
|
- Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
|
|
|
+ //LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
|
|
|
+ //Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
|
|
|
}
|
|
|
}
|
|
|
if(item.StateCode != 7&&item.StateCode!=9)
|
|
|
@@ -234,11 +242,12 @@ namespace CCDCount.DLL
|
|
|
if(item.StateCode != 9)
|
|
|
OneActive.Add(item);
|
|
|
}
|
|
|
- //stopwatch.Stop();
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
+ stopwatch.Stop();
|
|
|
+ if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
+ {
|
|
|
+ Console.WriteLine($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
+ //FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
+ }
|
|
|
//stopwatch.Restart();
|
|
|
if (OneActive.Count > 0)
|
|
|
{
|
|
|
@@ -266,6 +275,199 @@ namespace CCDCount.DLL
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 处理图像序列的主入口
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="image">图像像素数据</param>
|
|
|
+ /// <returns>检测到的物体总数</returns>
|
|
|
+ public bool ProcessImageSequence2(IFrameOut image)
|
|
|
+ {
|
|
|
+ bool result = false;
|
|
|
+ ReadOnlySpan<byte> spanFromArr = image.Image.PixelData.AsSpan();
|
|
|
+ for (int i = 0; i < image.Image.Height; i++)
|
|
|
+ {
|
|
|
+ result = ProcessLine2(spanFromArr, image.HostTimeStamp, image.Image.Width, i);
|
|
|
+ currentLine += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优化: 减少锁的使用频率,批量处理逻辑
|
|
|
+ List<ActiveObjectClass> objectsToProcess = null;
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 使用ToList()避免在锁内进行复杂查询
|
|
|
+ objectsToProcess = activeObjects
|
|
|
+ .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP ||
|
|
|
+ (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
+ .ToList();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优化: 提前返回,避免不必要的处理
|
|
|
+ if (objectsToProcess.Count == 0)
|
|
|
+ {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优化: 预分配容量,减少内存重新分配
|
|
|
+ var processedObjects = new List<ActiveObjectClass>(objectsToProcess.Count);
|
|
|
+
|
|
|
+ // 优化: 将耗时的计算移到循环外
|
|
|
+ var endTime = DateTime.Now;
|
|
|
+ var pictureEndTime = FromUnixTimestamp((long)image.HostTimeStamp);
|
|
|
+
|
|
|
+ foreach (var item in objectsToProcess)
|
|
|
+ {
|
|
|
+ // 优化: 将耗时的计算移到循环外
|
|
|
+ ProcessSingleObject(item, pictureEndTime, endTime, processedObjects);
|
|
|
+ }
|
|
|
+
|
|
|
+ result = true;
|
|
|
+
|
|
|
+ // 优化: 批量操作减少锁的持有时间
|
|
|
+ if (processedObjects.Count > 0)
|
|
|
+ {
|
|
|
+ _rwLock.EnterWriteLock();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 批量转移对象
|
|
|
+ foreach (var item in processedObjects)
|
|
|
+ {
|
|
|
+ TryAdd(historyActiveObjects, item, 2500);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量移除
|
|
|
+ foreach (var item in objectsToProcess)
|
|
|
+ {
|
|
|
+ activeObjects.Remove(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _rwLock.ExitWriteLock();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优化: 异步触发事件,避免阻塞主线程
|
|
|
+ ThreadPool.QueueUserWorkItem(_ =>
|
|
|
+ {
|
|
|
+ OnWorkCompleted(processedObjects);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 抽取的单个对象处理方法
|
|
|
+ private void ProcessSingleObject(ActiveObjectClass item, DateTime pictureEndTime,
|
|
|
+ DateTime endTime, List<ActiveObjectClass> processedObjects)
|
|
|
+ {
|
|
|
+ item.PictureEndReadTime = pictureEndTime;
|
|
|
+ var nowTime = DateTime.Now;
|
|
|
+ var ShiBieLuoHouTime = (nowTime - item.PictureEndReadTime).TotalMilliseconds;
|
|
|
+ // 优化: 将日志记录改为条件执行
|
|
|
+ if (ShiBieLuoHouTime > 30)
|
|
|
+ {
|
|
|
+ LOG.error($"算法落后了超过30ms,相机取图总耗时:{ShiBieLuoHouTime}");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 快速滤除噪点
|
|
|
+ if (item.Area < shuLiConfig.NoiseFilter_Threshold)
|
|
|
+ {
|
|
|
+ item.StateCode = 9;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算长度
|
|
|
+ if (item.StartLine == item.LastSeenLine)
|
|
|
+ {
|
|
|
+ item.MaxLength = item.MaxEndCol - item.MinStartCol;
|
|
|
+ item.StateCode = 10;
|
|
|
+ LOG.log(string.Format("颗粒{0}的有效高度仅一行", item.Num));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if ((item.LastSeenLine - item.StartLine) < shuLiConfig.MAX_Idetify_Height)
|
|
|
+ {
|
|
|
+ var calculationResult = SizeCalculation(item.RowsData);
|
|
|
+ if(calculationResult!=null)
|
|
|
+ {
|
|
|
+ item.MaxLength = calculationResult.Height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ item.StateCode = 7;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分类处理
|
|
|
+ ApplyClassificationRules(item);
|
|
|
+
|
|
|
+ // 添加到历史记录
|
|
|
+ if (item.StateCode != 7 && item.StateCode != 9)
|
|
|
+ {
|
|
|
+ item.Num = Interlocked.Increment(ref ObjectNum);
|
|
|
+ item.ChannelNO = ActiveChannel(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ item.EndCheckTime = endTime;
|
|
|
+
|
|
|
+ if (item.StateCode != 9)
|
|
|
+ {
|
|
|
+ processedObjects.Add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 抽取分类规则,便于维护
|
|
|
+ private void ApplyClassificationRules(ActiveObjectClass item)
|
|
|
+ {
|
|
|
+ if (shuLiConfig.PandingCode == -1) return;
|
|
|
+
|
|
|
+ if (item.StateCode != -1)
|
|
|
+ {
|
|
|
+ if (item.StateCode == 8)
|
|
|
+ {
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
|
|
|
+ }
|
|
|
+ else if (item.StateCode == 7)
|
|
|
+ {
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (item.Area < shuLiConfig.MinArea &&
|
|
|
+ (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
|
|
|
+ {
|
|
|
+ item.StateCode = 6;
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
|
|
|
+ }
|
|
|
+ else if (item.Area > shuLiConfig.MaxArea &&
|
|
|
+ (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
|
|
|
+ {
|
|
|
+ item.StateCode = 5;
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
|
|
|
+ }
|
|
|
+ else if (item.MaxLength < shuLiConfig.MIN_Object_LENGTH &&
|
|
|
+ (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
|
|
|
+ {
|
|
|
+ item.StateCode = 2;
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
|
|
|
+ }
|
|
|
+ else if (item.MaxLength > shuLiConfig.MAX_Object_LENGTH &&
|
|
|
+ (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
|
|
|
+ {
|
|
|
+ item.StateCode = 1;
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ item.StateCode = 0;
|
|
|
+ LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 返回最后一个历史物品
|
|
|
/// </summary>
|
|
|
@@ -360,7 +562,7 @@ namespace CCDCount.DLL
|
|
|
// 打开识别线程
|
|
|
IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
|
|
|
{
|
|
|
- Priority = ThreadPriority.Highest
|
|
|
+ Priority = ThreadPriority.AboveNormal
|
|
|
};
|
|
|
IdentifyImageProcessThread.Start();
|
|
|
SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
|
|
|
@@ -376,6 +578,31 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public void StartIdentifyFuntion2(int ImaageWidth)
|
|
|
+ {
|
|
|
+ UpdateIdentifyImageWidth(ImaageWidth);
|
|
|
+ InitChannel();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ CancellationTokenSource = new CancellationTokenSource();
|
|
|
+ var token = CancellationTokenSource.Token;
|
|
|
+
|
|
|
+ // 启动图像处理线程
|
|
|
+ ProcessingTask = Task.Factory.StartNew(() => IdentifyImageProcessTask(token),
|
|
|
+ token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
|
|
+ SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ SystemAlarm.AlarmAlert(AlarmMessageList.识别线程启动失败,
|
|
|
+ "Start thread failed!, " + ex.Message,
|
|
|
+ "识别线程启动失败, " + ex.Message,
|
|
|
+ "DLL:ShuLiClass-StartIdentifyFuntion");
|
|
|
+ //FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message);
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 关闭识别
|
|
|
/// </summary>
|
|
|
@@ -399,6 +626,35 @@ namespace CCDCount.DLL
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
+ public async void StopIdentifyFuntion2()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 发送取消信号
|
|
|
+ CancellationTokenSource.Cancel();
|
|
|
+
|
|
|
+ // 等待线程完成
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (ProcessingTask != null)
|
|
|
+ await ProcessingTask;
|
|
|
+ }
|
|
|
+ catch (OperationCanceledException)
|
|
|
+ {
|
|
|
+ // 正常取消,忽略异常
|
|
|
+ }
|
|
|
+ SystemAlarm.AlarmCancel(AlarmMessageList.识别线程停止失败);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ //FaultLog.RecordErrorMessage("Stop thread failed!, " + ex.Message);
|
|
|
+ SystemAlarm.AlarmAlert(AlarmMessageList.识别线程停止失败,
|
|
|
+ "Stop thread failed!, " + ex.Message,
|
|
|
+ "识别线程停止失败, " + ex.Message,
|
|
|
+ "DLL:ShuLiClass-StopIdentifyFuntion");
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 向识别队列添加一个数据
|
|
|
@@ -407,6 +663,7 @@ namespace CCDCount.DLL
|
|
|
public void SetOnceIdentifyImageData(IFrameOut items)
|
|
|
{
|
|
|
IFrameDatas.Enqueue(items.Clone() as IFrameOut);
|
|
|
+ //IFrameDatas.Enqueue(items );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -679,7 +936,7 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
|
|
|
private bool IsPrintLightOnError = false;
|
|
|
-
|
|
|
+ List<ValidRegionModelClass> currentRegions = null;
|
|
|
/// <summary>
|
|
|
/// 处理单行像素数据
|
|
|
/// 返回值为false的时候无活跃物体转变为历史物体
|
|
|
@@ -807,19 +1064,20 @@ namespace CCDCount.DLL
|
|
|
//}
|
|
|
return result;
|
|
|
}
|
|
|
- private bool ProcessLine2(IFrameOut imagedata, int RowNo)
|
|
|
+ private bool ProcessLine2(ReadOnlySpan<byte> image, ulong ImageHostTime, uint imageWidth, int RowNo)
|
|
|
{
|
|
|
// 缓存配置值
|
|
|
int maxGap = shuLiConfig.MAX_GAP;
|
|
|
long currentTimeLine = currentLine;
|
|
|
|
|
|
// 步骤1:检测当前行的有效区域
|
|
|
- var currentRegions = FindValidRegions(imagedata.Image, RowNo);
|
|
|
- if (currentRegions.Count == 0) return false;
|
|
|
+ currentRegions = FindValidRegions(image, (int)imageWidth, RowNo);
|
|
|
+
|
|
|
+ if (currentRegions.Count == 0|| currentRegions == null) return false;
|
|
|
|
|
|
if (currentRegions.Count == 1)
|
|
|
{
|
|
|
- if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imagedata.Image.Width)
|
|
|
+ if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imageWidth)
|
|
|
{
|
|
|
if (!IsPrintLightOnError)
|
|
|
{
|
|
|
@@ -869,6 +1127,7 @@ namespace CCDCount.DLL
|
|
|
LastSeenLineEndCol = aggregateResult.maxLastSeenEndCol,
|
|
|
StartCheckTime = aggregateResult.minStartTime,
|
|
|
EndCheckTime = aggregateResult.maxEndTime,
|
|
|
+ PictureStartReadTime = aggregateResult.pictureMinReadTime,
|
|
|
Area = aggregateResult.totalArea,
|
|
|
RowsData = aggregateResult.mergedRowsData,
|
|
|
ImageWidth = aggregateResult.imageWidth,
|
|
|
@@ -913,7 +1172,7 @@ namespace CCDCount.DLL
|
|
|
LastSeenLineStartCol = region.Start,
|
|
|
LastSeenLineEndCol = region.End,
|
|
|
StartCheckTime = DateTime.Now,
|
|
|
- PictureStartReadTime = FromUnixTimestamp((long)imagedata.HostTimeStamp),
|
|
|
+ PictureStartReadTime = FromUnixTimestamp((long)ImageHostTime),
|
|
|
Area = region.End - region.Start + 1,
|
|
|
ImageWidth = IdentifyImageWidth,
|
|
|
RowsData = new List<RowStartEndCol> {
|
|
|
@@ -933,6 +1192,7 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ currentRegions.Clear();
|
|
|
// 执行所有操作(最小化锁持有时间)
|
|
|
bool hasChanges = false;
|
|
|
if (operations.Count > 0)
|
|
|
@@ -1008,7 +1268,7 @@ namespace CCDCount.DLL
|
|
|
// 计算聚合值的方法
|
|
|
private (int minStartCol, int maxEndCol, long minStartLine, long maxLastSeenLine,
|
|
|
int minLastSeenStartCol, int maxLastSeenEndCol, DateTime minStartTime,
|
|
|
- DateTime maxEndTime, int totalArea, List<RowStartEndCol> mergedRowsData, int imageWidth)
|
|
|
+ DateTime maxEndTime,DateTime pictureMinReadTime,int totalArea, List<RowStartEndCol> mergedRowsData, int imageWidth)
|
|
|
CalculateAggregates(List<ActiveObjectClass> objects)
|
|
|
{
|
|
|
int minStartCol = int.MaxValue;
|
|
|
@@ -1019,6 +1279,7 @@ namespace CCDCount.DLL
|
|
|
int maxLastSeenEndCol = int.MinValue;
|
|
|
DateTime minStartTime = DateTime.MaxValue;
|
|
|
DateTime maxEndTime = DateTime.MinValue;
|
|
|
+ DateTime pictureMinReadTime = DateTime.MaxValue;
|
|
|
int totalArea = 0;
|
|
|
int imageWidth = objects.FirstOrDefault()?.ImageWidth ?? IdentifyImageWidth;
|
|
|
|
|
|
@@ -1035,6 +1296,7 @@ namespace CCDCount.DLL
|
|
|
|
|
|
if (obj.StartCheckTime < minStartTime) minStartTime = obj.StartCheckTime;
|
|
|
if (obj.EndCheckTime > maxEndTime) maxEndTime = obj.EndCheckTime;
|
|
|
+ if (obj.PictureStartReadTime<pictureMinReadTime) pictureMinReadTime = obj.PictureStartReadTime;
|
|
|
|
|
|
totalArea += obj.Area;
|
|
|
mergedRowsData.AddRange(obj.RowsData);
|
|
|
@@ -1042,7 +1304,7 @@ namespace CCDCount.DLL
|
|
|
|
|
|
return (minStartCol, maxEndCol, minStartLine, maxLastSeenLine,
|
|
|
minLastSeenStartCol, maxLastSeenEndCol, minStartTime,
|
|
|
- maxEndTime, totalArea, mergedRowsData, imageWidth);
|
|
|
+ maxEndTime,pictureMinReadTime,totalArea, mergedRowsData, imageWidth);
|
|
|
}
|
|
|
|
|
|
List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
|
|
|
@@ -1116,6 +1378,48 @@ namespace CCDCount.DLL
|
|
|
return new List<ValidRegionModelClass>(regions);
|
|
|
}
|
|
|
|
|
|
+ private List<ValidRegionModelClass> FindValidRegions(ReadOnlySpan<byte> image,int imageWidth, int RowNo)
|
|
|
+ {
|
|
|
+ regions.Clear();
|
|
|
+ regions.Capacity = 10;
|
|
|
+
|
|
|
+ ReadOnlySpan<byte> rowSpan;
|
|
|
+ if (shuLiConfig.IsIdentifyRoiOpen)
|
|
|
+ {
|
|
|
+ int offset = RowNo * imageWidth + shuLiConfig.IdentifyStartX;
|
|
|
+ int length = Math.Min(shuLiConfig.IdentifyStopX - shuLiConfig.IdentifyStartX,
|
|
|
+ imageWidth - shuLiConfig.IdentifyStartX);
|
|
|
+ rowSpan = image.Slice(offset, length);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ rowSpan = image.Slice(RowNo * imageWidth, imageWidth);
|
|
|
+ }
|
|
|
+
|
|
|
+ int start = -1;
|
|
|
+ byte threshold = (byte)shuLiConfig.RegionThreshold;
|
|
|
+
|
|
|
+ for (int i = 0; i < rowSpan.Length; i++)
|
|
|
+ {
|
|
|
+ if (rowSpan[i] < threshold)
|
|
|
+ {
|
|
|
+ if (start == -1) start = i;
|
|
|
+ }
|
|
|
+ else if (start != -1)
|
|
|
+ {
|
|
|
+ regions.Add(new ValidRegionModelClass { Start = start, End = i - 1 });
|
|
|
+ start = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (start != -1)
|
|
|
+ {
|
|
|
+ regions.Add(new ValidRegionModelClass { Start = start, End = rowSpan.Length - 1 });
|
|
|
+ }
|
|
|
+
|
|
|
+ return new List<ValidRegionModelClass>(regions);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 判断区域重叠(与活跃物体的横向坐标重叠检测)
|
|
|
/// </summary>
|
|
|
@@ -1605,6 +1909,12 @@ namespace CCDCount.DLL
|
|
|
#endregion
|
|
|
|
|
|
#region 线程方法
|
|
|
+ //信号量
|
|
|
+ public SemaphoreSlim QueueSemaphore { get; set; }
|
|
|
+ //取消令牌
|
|
|
+ public CancellationTokenSource CancellationTokenSource { get; set; }
|
|
|
+ //图像处理线程
|
|
|
+ public Task ProcessingTask { get; set; }
|
|
|
/// <summary>
|
|
|
/// 识别图像线程
|
|
|
/// </summary>
|
|
|
@@ -1635,6 +1945,12 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
IFrameDatas.TryDequeue(out IFrameOut IframeData);
|
|
|
stopwatch.Stop();
|
|
|
+ var ShiBieLuoHouTime = (DateTime.Now - FromUnixTimestamp((long)IframeData.HostTimeStamp)).TotalMilliseconds;
|
|
|
+ // 优化: 将日志记录改为条件执行
|
|
|
+ if (ShiBieLuoHouTime > 30)
|
|
|
+ {
|
|
|
+ LOG.error($"算法落后了超过30ms,相机取图总耗时:{ShiBieLuoHouTime}");
|
|
|
+ }
|
|
|
if (stopwatch.ElapsedMilliseconds > 5)
|
|
|
{
|
|
|
FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:Image reading timed out, this recognition took time:{stopwatch.Elapsed}");
|
|
|
@@ -1642,13 +1958,71 @@ namespace CCDCount.DLL
|
|
|
if (IframeData != null)
|
|
|
{
|
|
|
//识别
|
|
|
- ProcessImageSequence(IframeData);
|
|
|
+ ProcessImageSequence2(IframeData);
|
|
|
+ IframeData.Dispose();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Thread.Sleep(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async void IdentifyImageProcessTask(CancellationToken token)
|
|
|
+ {
|
|
|
+ Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
+ const int batchSize = 5; // 每批处理的最大图像数量
|
|
|
+ var batch = new List<IFrameOut>(batchSize); // 存储当前批次的图像
|
|
|
+ while (!token.IsCancellationRequested)
|
|
|
+ {
|
|
|
+ // 等待图像数据
|
|
|
+ await QueueSemaphore.WaitAsync(token);
|
|
|
+ //判断队列中是否有数据
|
|
|
+ if (IFrameDatas.Count() > 5)
|
|
|
+ {
|
|
|
+ //FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
|
|
|
+ FaultLog.RecordErrorMessage($"There is too much data in the image data queue. Please handle it in a timely manner! The current data quantity is:{IFrameDatas.Count()}");
|
|
|
+ if (IFrameDatas.Count() > 100)
|
|
|
+ {
|
|
|
+ SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积,
|
|
|
+ $"The image data queue is blocked. Please check the configuration and images",
|
|
|
+ "待识别队列数据堆积,请检查设置和图像",
|
|
|
+ "DLL:ShuLIClass-IdentifyImageProcess");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 收集一批图像(严格按队列顺序)
|
|
|
+ while (batch.Count < batchSize && IFrameDatas.TryDequeue(out IFrameOut imageData))
|
|
|
+ {
|
|
|
+ batch.Add(imageData);
|
|
|
+ if (IFrameDatas.Count > 0)
|
|
|
+ {
|
|
|
+ QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 顺序处理当前批次的所有图像
|
|
|
+ foreach (var image in batch)
|
|
|
+ {
|
|
|
+ if (image != null)
|
|
|
+ {
|
|
|
+ //识别
|
|
|
+ ProcessImageSequence2(image);
|
|
|
+ image.Dispose();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
|
+ batch.Clear();
|
|
|
}
|
|
|
}
|
|
|
#endregion
|