|
|
@@ -27,10 +27,7 @@ namespace CCDCount.DLL
|
|
|
public event EventHandler<ActiveObjectEventArgsClass> WorkCompleted;
|
|
|
private List<ActiveObjectClass> activeObjects = new List<ActiveObjectClass>(); // 当前跟踪中的物体
|
|
|
private List<ActiveObjectClass> historyActiveObjects = new List<ActiveObjectClass>(); // 历史物体
|
|
|
- private ConcurrentQueue<IFrameOut> IFrameDatas = new ConcurrentQueue<IFrameOut>(); //图像数据队列
|
|
|
private LockFreeRingBuffer<IFrameOut> RingBuffer_IFrameDatas = new LockFreeRingBuffer<IFrameOut>(300);
|
|
|
- private Thread IdentifyImageProcessThread = null; // 识别线程
|
|
|
- private bool IsIdentify = false; //线程是否开始识别的标志
|
|
|
private long currentLine = 0; //行数记录
|
|
|
private ShuLiConfigClass shuLiConfig = null;// 数粒参数配置文件
|
|
|
public List<int> ChannelsRoi { get { return _ChannelsRoi; } }
|
|
|
@@ -38,10 +35,10 @@ namespace CCDCount.DLL
|
|
|
private int ChannelWidth = 0;//每个区域的宽度
|
|
|
private int IdentifyImageWidth = -1;
|
|
|
private int ObjectNum = 0;
|
|
|
- public int ImageNum { get { return IFrameDatas.Count; } }
|
|
|
+ public int ImageNum { get { return RingBuffer_IFrameDatas.Count; } }
|
|
|
private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
|
|
|
- private double XCoefficient = 1;
|
|
|
- private double YCoefficient = 2;
|
|
|
+ private double XCoefficient = 0.1;
|
|
|
+ private double YCoefficient = 0.2;
|
|
|
private DateTime[] ChannelIntervalTime = new DateTime[8];
|
|
|
|
|
|
private int _HistoryActiveNum = 0;
|
|
|
@@ -106,13 +103,10 @@ namespace CCDCount.DLL
|
|
|
return ObjectNum;
|
|
|
}
|
|
|
|
|
|
- List<ActiveObjectClass> lostObjects = new List<ActiveObjectClass>();
|
|
|
/// <summary>
|
|
|
/// 处理图像序列的主入口
|
|
|
/// </summary>
|
|
|
/// <param name="image">图像像素数据</param>
|
|
|
- /// <param name="ImageWidth">图像宽</param>
|
|
|
- /// <param name="currentLine">当前行数</param>
|
|
|
/// <returns>检测到的物体总数</returns>
|
|
|
public bool ProcessImageSequence(IFrameOut image)
|
|
|
{
|
|
|
@@ -120,177 +114,7 @@ namespace CCDCount.DLL
|
|
|
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;
|
|
|
- }
|
|
|
-
|
|
|
- //识别到结果并输出
|
|
|
-
|
|
|
- _rwLock.EnterReadLock();
|
|
|
- // 清理超时未更新的物体
|
|
|
- lostObjects = activeObjects
|
|
|
- .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
- .ToList();
|
|
|
- _rwLock.ExitReadLock();
|
|
|
- _rwLock.EnterWriteLock();
|
|
|
- lostObjects.ForEach(o => activeObjects.Remove(o));
|
|
|
- _rwLock.ExitWriteLock();
|
|
|
-
|
|
|
-
|
|
|
- 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);
|
|
|
- var QutuYanshiTime = (DateTime.Now - item.PictureEndReadTime).TotalMilliseconds;
|
|
|
- if (QutuYanshiTime > 20)
|
|
|
- {
|
|
|
- //LOG.error($"结果输出延时超过了20ms,耗时{QutuYanshiTime}");
|
|
|
- Console.WriteLine($"结果输出延时超过了20ms,耗时{QutuYanshiTime}");
|
|
|
- }
|
|
|
- //噪点判定
|
|
|
- //if (item.Area < shuLiConfig.NoiseFilter_Threshold)
|
|
|
- //{
|
|
|
- // item.StateCode = 9;
|
|
|
- // //LOG.log(string.Format("噪点过滤,噪点面积:{0}", item.Area), 6);
|
|
|
- // continue;
|
|
|
- //}
|
|
|
- //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;
|
|
|
- // }
|
|
|
- // //item.hasSignificantConcavity = CalculationResult.hasSignificantConcavity;
|
|
|
- // //item.concavityRatio = CalculationResult.concavityRatio;
|
|
|
- // //if(CalculationResult.hasSignificantConcavity)
|
|
|
- // //{
|
|
|
- // // if(CalculationResult.concavityRatio>0.3)
|
|
|
- // // {
|
|
|
- // // item.StateCode= 11;
|
|
|
- // // }
|
|
|
- // //}
|
|
|
- // }
|
|
|
- // else
|
|
|
- // {
|
|
|
- // item.StateCode = 7;
|
|
|
- // }
|
|
|
- //}
|
|
|
- //if (shuLiConfig.PandingCode != -1)
|
|
|
- //{
|
|
|
- // 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));
|
|
|
- // //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);
|
|
|
- // }
|
|
|
- // 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);
|
|
|
- // }
|
|
|
- // 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);
|
|
|
- // }
|
|
|
- // else
|
|
|
- // {
|
|
|
- // item.StateCode = 0;
|
|
|
- // //LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
|
|
|
- // //Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
|
|
|
- // }
|
|
|
- //}
|
|
|
- if(item.StateCode != 7&&item.StateCode!=9)
|
|
|
- {
|
|
|
- //转为历史物体,添加缺少的参数
|
|
|
- item.Num = Interlocked.Increment(ref ObjectNum);
|
|
|
- item.ChannelNO = ActiveChannel(item);
|
|
|
- }
|
|
|
- item.EndCheckTime = DateTime.Now;
|
|
|
- //if(item.StateCode != 9)
|
|
|
- OneActive.Add(item);
|
|
|
- }
|
|
|
- //stopwatch.Stop();
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // Console.WriteLine($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- // //FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
- //stopwatch.Restart();
|
|
|
- if (OneActive.Count > 0)
|
|
|
- {
|
|
|
- //ThreadPool.QueueUserWorkItem(_ =>
|
|
|
- //{
|
|
|
- //触发回调事件
|
|
|
- OnWorkCompleted(OneActive);
|
|
|
- //});
|
|
|
- }
|
|
|
- // stopwatch.Stop();
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果完成回调函数,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- OneActive.Clear();
|
|
|
- }
|
|
|
- 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);
|
|
|
- result = ProcessLine(image, i);
|
|
|
+ ProcessLine(image, i);
|
|
|
currentLine += 1;
|
|
|
}
|
|
|
|
|
|
@@ -391,6 +215,7 @@ namespace CCDCount.DLL
|
|
|
item.MaxLength = item.MaxEndCol - item.MinStartCol;
|
|
|
item.StateCode = 10;
|
|
|
LOG.log(string.Format("颗粒{0}的有效高度仅一行", item.Num));
|
|
|
+ return;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -405,14 +230,15 @@ namespace CCDCount.DLL
|
|
|
else
|
|
|
{
|
|
|
item.StateCode = 7;
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 分类处理
|
|
|
- ApplyClassificationRules(item);
|
|
|
+ ApplyClassificationRules(ref item);
|
|
|
|
|
|
// 添加到历史记录
|
|
|
- if (item.StateCode != 7 && item.StateCode != 9)
|
|
|
+ if (item.StateCode != 7 && item.StateCode != 9 && item.StateCode != 10)
|
|
|
{
|
|
|
item.Num = Interlocked.Increment(ref ObjectNum);
|
|
|
item.ChannelNO = ActiveChannel(item);
|
|
|
@@ -420,14 +246,17 @@ namespace CCDCount.DLL
|
|
|
|
|
|
item.EndCheckTime = endTime;
|
|
|
|
|
|
- if (item.StateCode != 9)
|
|
|
+ if (item.StateCode != 7 && item.StateCode != 9 && item.StateCode != 10)
|
|
|
{
|
|
|
processedObjects.Add(item);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 抽取分类规则,便于维护
|
|
|
- private void ApplyClassificationRules(ActiveObjectClass item)
|
|
|
+ /// <summary>
|
|
|
+ /// 分类规则
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="item"></param>
|
|
|
+ private void ApplyClassificationRules(ref ActiveObjectClass item)
|
|
|
{
|
|
|
if (shuLiConfig.PandingCode == -1) return;
|
|
|
|
|
|
@@ -439,7 +268,11 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
else if (item.StateCode == 7)
|
|
|
{
|
|
|
- LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
|
|
|
+ LOG.log("视野被遮挡");
|
|
|
+ }
|
|
|
+ else if (item.StateCode == 9)
|
|
|
+ {
|
|
|
+ LOG.log(string.Format("噪点", item.Num));
|
|
|
}
|
|
|
}
|
|
|
else if (item.Area < shuLiConfig.MinArea &&
|
|
|
@@ -518,41 +351,11 @@ namespace CCDCount.DLL
|
|
|
//}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 开启识别
|
|
|
- /// </summary>
|
|
|
- public void StartIdentifyFuntion(int ImaageWidth)
|
|
|
- {
|
|
|
- UpdateIdentifyImageWidth(ImaageWidth);
|
|
|
- InitChannel();
|
|
|
- try
|
|
|
- {
|
|
|
- // 标志位置位true
|
|
|
- IsIdentify = true;
|
|
|
- // 打开识别线程
|
|
|
- IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
|
|
|
- {
|
|
|
- Priority = ThreadPriority.AboveNormal
|
|
|
- };
|
|
|
- IdentifyImageProcessThread.Start();
|
|
|
- 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>
|
|
|
/// <param name="ImaageWidth"></param>
|
|
|
- public void StartIdentifyFuntion2(int ImaageWidth)
|
|
|
+ public void StartIdentifyFuntion(int ImaageWidth)
|
|
|
{
|
|
|
UpdateIdentifyImageWidth(ImaageWidth);
|
|
|
InitChannel();
|
|
|
@@ -560,9 +363,9 @@ namespace CCDCount.DLL
|
|
|
{
|
|
|
CancellationTokenSource = new CancellationTokenSource();
|
|
|
var token = CancellationTokenSource.Token;
|
|
|
-
|
|
|
+ LOG.log(string.Format("X系数:{0},Y系数:{1}", XCoefficient, YCoefficient), 6);
|
|
|
// 启动图像处理线程
|
|
|
- ProcessingTask = Task.Factory.StartNew(() => IdentifyImageProcessTask2(token),
|
|
|
+ ProcessingTask = Task.Factory.StartNew(() => IdentifyImageProcessTask(token),
|
|
|
token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
|
|
SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
|
|
|
}
|
|
|
@@ -577,30 +380,7 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 关闭识别
|
|
|
- /// </summary>
|
|
|
- public void StopIdentifyFuntion()
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- // 标志位设为false
|
|
|
- IsIdentify = false;
|
|
|
- if (IdentifyImageProcessThread != null && IdentifyImageProcessThread.IsAlive)
|
|
|
- IdentifyImageProcessThread.Join();
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
- public async void StopIdentifyFuntion2()
|
|
|
+ public async void StopIdentifyFuntion()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
@@ -630,24 +410,14 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 向识别队列添加一个数据
|
|
|
- /// </summary>
|
|
|
- /// <param name="items"></param>
|
|
|
- public void SetOnceIdentifyImageData(IFrameOut items)
|
|
|
- {
|
|
|
- IFrameDatas.Enqueue(items.Clone() as IFrameOut);
|
|
|
- //IFrameDatas.Enqueue(items );
|
|
|
- }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 向识别队列添加一个数据
|
|
|
/// </summary>
|
|
|
/// <param name="items"></param>
|
|
|
- public void SetOnceIdentifyImageData2(IFrameOut items)
|
|
|
+ public void SetOnceIdentifyImageData(IFrameOut items)
|
|
|
{
|
|
|
RingBuffer_IFrameDatas.TryEnqueue(items.Clone() as IFrameOut);
|
|
|
- //IFrameDatas.Enqueue(items );
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -963,15 +733,12 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
IsPrintLightOnError = false;
|
|
|
- //stopwatch.Stop();
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像连通域检测超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
- //stopwatch.Restart();
|
|
|
foreach (var region in currentRegions)
|
|
|
{
|
|
|
-
|
|
|
+ if(region.End-region.Start<=3)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
// 查找全部可合并的活跃物体(有重叠+在允许间隔内)
|
|
|
_rwLock.EnterReadLock();
|
|
|
var matcheds = activeObjects.Where(o =>
|
|
|
@@ -991,14 +758,14 @@ namespace CCDCount.DLL
|
|
|
MaxEndCol = matcheds.Max(o => o.MaxEndCol),
|
|
|
StartLine = matcheds.Min(o => o.StartLine),
|
|
|
LastSeenLine = matcheds.Max(o => o.LastSeenLine),
|
|
|
- LastSeenLineStartCol = matcheds.Min(o => o.LastSeenLineStartCol),
|
|
|
- LastSeenLineEndCol = matcheds.Max(o => o.LastSeenLineEndCol),
|
|
|
+ PreSeenLineStartCol = matcheds.Min(o => o.PreSeenLineStartCol),
|
|
|
+ PreSeenLineEndCol = matcheds.Max(o => o.PreSeenLineEndCol),
|
|
|
StartCheckTime = matcheds.Min(o => o.StartCheckTime),
|
|
|
EndCheckTime = matcheds.Max(o => o.EndCheckTime),
|
|
|
Area = matcheds.Sum(o => o.Area),
|
|
|
RowsData = CopeRowsData,
|
|
|
ImageWidth = matcheds.FirstOrDefault().ImageWidth,
|
|
|
- StateCode = 8
|
|
|
+ //StateCode = 8
|
|
|
};
|
|
|
_rwLock.EnterWriteLock();
|
|
|
// 从活跃区域中删除被合并的区域
|
|
|
@@ -1026,8 +793,22 @@ namespace CCDCount.DLL
|
|
|
EndCol = region.End,
|
|
|
RowsCol = currentLine,
|
|
|
});
|
|
|
- matched.LastSeenLineStartCol = region.Start;
|
|
|
- matched.LastSeenLineEndCol = region.End;
|
|
|
+ if(matched.LastSeenLineEndCol==-1)
|
|
|
+ {
|
|
|
+ matched.LastSeenLineEndCol = region.End;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ matched.LastSeenLineEndCol = Math.Max(matched.LastSeenLineEndCol, region.End);
|
|
|
+ }
|
|
|
+ if (matched.LastSeenLineStartCol == -1)
|
|
|
+ {
|
|
|
+ matched.LastSeenLineStartCol = region.Start;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ matched.LastSeenLineStartCol = Math.Min(matched.LastSeenLineStartCol, region.Start);
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -1039,8 +820,8 @@ namespace CCDCount.DLL
|
|
|
MaxEndCol = region.End,
|
|
|
StartLine = currentLine,
|
|
|
LastSeenLine = currentLine,
|
|
|
- LastSeenLineStartCol = region.Start,
|
|
|
- LastSeenLineEndCol = region.End,
|
|
|
+ PreSeenLineStartCol = region.Start,
|
|
|
+ PreSeenLineEndCol = region.End,
|
|
|
StartCheckTime = DateTime.Now,
|
|
|
PictureStartReadTime = FromUnixTimestamp((long)imagedata.HostTimeStamp),
|
|
|
Area = region.End - region.Start + 1,
|
|
|
@@ -1057,195 +838,25 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //stopwatch.Stop();
|
|
|
- //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
- //{
|
|
|
- // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像区域合并超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
- //}
|
|
|
- return result;
|
|
|
- }
|
|
|
- private bool ProcessLine2(ReadOnlySpan<byte> image, ulong ImageHostTime, uint imageWidth, int RowNo)
|
|
|
- {
|
|
|
- // 缓存配置值
|
|
|
- int maxGap = shuLiConfig.MAX_GAP;
|
|
|
- long currentTimeLine = currentLine;
|
|
|
-
|
|
|
- // 步骤1:检测当前行的有效区域
|
|
|
- //currentRegions = FindValidRegions(image, (int)imageWidth, RowNo);
|
|
|
-
|
|
|
- if (currentRegions.Count == 0|| currentRegions == null) return false;
|
|
|
-
|
|
|
- if (currentRegions.Count == 1)
|
|
|
+ foreach (var obj in activeObjects)
|
|
|
{
|
|
|
- if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imageWidth)
|
|
|
+ if(obj.LastSeenLineEndCol!=-1)
|
|
|
{
|
|
|
- if (!IsPrintLightOnError)
|
|
|
- {
|
|
|
- FaultLog.RecordLogMessage("The current effective area is the entire row. Check the field of view and light source", 6);
|
|
|
- IsPrintLightOnError = true;
|
|
|
- }
|
|
|
- return false;
|
|
|
+ obj.PreSeenLineEndCol = obj.LastSeenLineEndCol;
|
|
|
+ obj.LastSeenLineEndCol = -1;
|
|
|
}
|
|
|
- }
|
|
|
- IsPrintLightOnError = false;
|
|
|
-
|
|
|
- // 批量处理操作列表
|
|
|
- var operations = new List<OperationItem>();
|
|
|
- var currentActiveObjects = new List<ActiveObjectClass>();
|
|
|
-
|
|
|
- // 一次性获取当前活跃物体快照
|
|
|
- _rwLock.EnterReadLock();
|
|
|
- try
|
|
|
- {
|
|
|
- currentActiveObjects = new List<ActiveObjectClass>(activeObjects);
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- _rwLock.ExitReadLock();
|
|
|
- }
|
|
|
-
|
|
|
- foreach (var region in currentRegions)
|
|
|
- {
|
|
|
- // 查找所有可合并的活跃物体(重叠+在允许间隔内)
|
|
|
- var matcheds = currentActiveObjects.Where(o =>
|
|
|
- IsOverlapping(o, region) &&
|
|
|
- (currentTimeLine - o.LastSeenLine - 1) <= maxGap).ToList();
|
|
|
-
|
|
|
- // 当有多个可合并的活跃物体时,将多个物体合并
|
|
|
- if (matcheds.Count >= 2)
|
|
|
- {
|
|
|
- // 计算聚合值(单次遍历)
|
|
|
- var aggregateResult = CalculateAggregates(matcheds);
|
|
|
-
|
|
|
- var mergedObject = new ActiveObjectClass
|
|
|
- {
|
|
|
- MinStartCol = aggregateResult.minStartCol,
|
|
|
- MaxEndCol = aggregateResult.maxEndCol,
|
|
|
- StartLine = aggregateResult.minStartLine,
|
|
|
- LastSeenLine = aggregateResult.maxLastSeenLine,
|
|
|
- LastSeenLineStartCol = aggregateResult.minLastSeenStartCol,
|
|
|
- LastSeenLineEndCol = aggregateResult.maxLastSeenEndCol,
|
|
|
- StartCheckTime = aggregateResult.minStartTime,
|
|
|
- EndCheckTime = aggregateResult.maxEndTime,
|
|
|
- PictureStartReadTime = aggregateResult.pictureMinReadTime,
|
|
|
- Area = aggregateResult.totalArea,
|
|
|
- RowsData = aggregateResult.mergedRowsData,
|
|
|
- ImageWidth = aggregateResult.imageWidth,
|
|
|
- StateCode = 8
|
|
|
- };
|
|
|
-
|
|
|
- operations.Add(new OperationItem
|
|
|
- {
|
|
|
- Type = OperationType.MergeMultiple,
|
|
|
- ObjectsToRemove = matcheds,
|
|
|
- ObjectToAdd = mergedObject
|
|
|
- });
|
|
|
-
|
|
|
- // 从currentActiveObjects中移除已处理的物体,避免重复处理
|
|
|
- foreach (var obj in matcheds)
|
|
|
- {
|
|
|
- currentActiveObjects.Remove(obj);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
+ if (obj.LastSeenLineStartCol != -1)
|
|
|
{
|
|
|
- // 查找单一匹配物体
|
|
|
- var matched = matcheds.FirstOrDefault();
|
|
|
- if (matched != null)
|
|
|
- {
|
|
|
- operations.Add(new OperationItem
|
|
|
- {
|
|
|
- Type = OperationType.Update,
|
|
|
- ObjectToUpdate = matched,
|
|
|
- Region = region
|
|
|
- });
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // 创建新物体
|
|
|
- var newObject = new ActiveObjectClass
|
|
|
- {
|
|
|
- MinStartCol = region.Start,
|
|
|
- MaxEndCol = region.End,
|
|
|
- StartLine = currentTimeLine,
|
|
|
- LastSeenLine = currentTimeLine,
|
|
|
- LastSeenLineStartCol = region.Start,
|
|
|
- LastSeenLineEndCol = region.End,
|
|
|
- StartCheckTime = DateTime.Now,
|
|
|
- PictureStartReadTime = FromUnixTimestamp((long)ImageHostTime),
|
|
|
- Area = region.End - region.Start + 1,
|
|
|
- ImageWidth = IdentifyImageWidth,
|
|
|
- RowsData = new List<RowStartEndCol> {
|
|
|
- new RowStartEndCol {
|
|
|
- StartCol = region.Start,
|
|
|
- EndCol = region.End,
|
|
|
- RowsCol = currentTimeLine,
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- operations.Add(new OperationItem
|
|
|
- {
|
|
|
- Type = OperationType.Add,
|
|
|
- ObjectToAdd = newObject
|
|
|
- });
|
|
|
- }
|
|
|
+ obj.PreSeenLineStartCol = obj.LastSeenLineStartCol;
|
|
|
+ obj.LastSeenLineStartCol = -1;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- currentRegions.Clear();
|
|
|
- // 执行所有操作(最小化锁持有时间)
|
|
|
- bool hasChanges = false;
|
|
|
- if (operations.Count > 0)
|
|
|
- {
|
|
|
- _rwLock.EnterWriteLock();
|
|
|
- try
|
|
|
- {
|
|
|
- foreach (var op in operations)
|
|
|
- {
|
|
|
- switch (op.Type)
|
|
|
- {
|
|
|
- case OperationType.MergeMultiple:
|
|
|
- // 移除被合并的物体
|
|
|
- foreach (var objItem in op.ObjectsToRemove)
|
|
|
- {
|
|
|
- activeObjects.Remove(objItem);
|
|
|
- }
|
|
|
- // 添加合并后的物体
|
|
|
- activeObjects.Add(op.ObjectToAdd);
|
|
|
- break;
|
|
|
-
|
|
|
- case OperationType.Update:
|
|
|
- // 更新现有物体
|
|
|
- var obj = op.ObjectToUpdate;
|
|
|
- obj.MinStartCol = Math.Min(obj.MinStartCol, op.Region.Start);
|
|
|
- obj.MaxEndCol = Math.Max(obj.MaxEndCol, op.Region.End);
|
|
|
- obj.Area += op.Region.End - op.Region.Start + 1;
|
|
|
- obj.LastSeenLine = currentTimeLine;
|
|
|
- obj.RowsData.Add(new RowStartEndCol
|
|
|
- {
|
|
|
- StartCol = op.Region.Start,
|
|
|
- EndCol = op.Region.End,
|
|
|
- RowsCol = currentTimeLine,
|
|
|
- });
|
|
|
- obj.LastSeenLineStartCol = op.Region.Start;
|
|
|
- obj.LastSeenLineEndCol = op.Region.End;
|
|
|
- break;
|
|
|
-
|
|
|
- case OperationType.Add:
|
|
|
- activeObjects.Add(op.ObjectToAdd);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- hasChanges = true;
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- _rwLock.ExitWriteLock();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return hasChanges;
|
|
|
+ //stopwatch.Stop();
|
|
|
+ //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
+ //{
|
|
|
+ // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像区域合并超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
+ //}
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
// 辅助类定义
|
|
|
@@ -1265,48 +876,6 @@ namespace CCDCount.DLL
|
|
|
public ValidRegionModelClass Region { get; set; }
|
|
|
}
|
|
|
|
|
|
- // 计算聚合值的方法
|
|
|
- private (int minStartCol, int maxEndCol, long minStartLine, long maxLastSeenLine,
|
|
|
- int minLastSeenStartCol, int maxLastSeenEndCol, DateTime minStartTime,
|
|
|
- DateTime maxEndTime,DateTime pictureMinReadTime,int totalArea, List<RowStartEndCol> mergedRowsData, int imageWidth)
|
|
|
- CalculateAggregates(List<ActiveObjectClass> objects)
|
|
|
- {
|
|
|
- int minStartCol = int.MaxValue;
|
|
|
- int maxEndCol = int.MinValue;
|
|
|
- long minStartLine = int.MaxValue;
|
|
|
- long maxLastSeenLine = int.MinValue;
|
|
|
- int minLastSeenStartCol = int.MaxValue;
|
|
|
- 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;
|
|
|
-
|
|
|
- var mergedRowsData = new List<RowStartEndCol>();
|
|
|
-
|
|
|
- foreach (var obj in objects)
|
|
|
- {
|
|
|
- minStartCol = Math.Min(minStartCol, obj.MinStartCol);
|
|
|
- maxEndCol = Math.Max(maxEndCol, obj.MaxEndCol);
|
|
|
- minStartLine = Math.Min(minStartLine, obj.StartLine);
|
|
|
- maxLastSeenLine = Math.Max(maxLastSeenLine, obj.LastSeenLine);
|
|
|
- minLastSeenStartCol = Math.Min(minLastSeenStartCol, obj.LastSeenLineStartCol);
|
|
|
- maxLastSeenEndCol = Math.Max(maxLastSeenEndCol, obj.LastSeenLineEndCol);
|
|
|
-
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- return (minStartCol, maxEndCol, minStartLine, maxLastSeenLine,
|
|
|
- minLastSeenStartCol, maxLastSeenEndCol, minStartTime,
|
|
|
- maxEndTime,pictureMinReadTime,totalArea, mergedRowsData, imageWidth);
|
|
|
- }
|
|
|
-
|
|
|
List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
|
|
|
/// <summary>
|
|
|
/// 检测有效物体区域(横向连续黑色像素段)
|
|
|
@@ -1379,48 +948,6 @@ 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>
|
|
|
@@ -1430,7 +957,7 @@ namespace CCDCount.DLL
|
|
|
private bool IsOverlapping(ActiveObjectClass obj, ValidRegionModelClass region)
|
|
|
{
|
|
|
// 判断区域是否不相交的逆条件
|
|
|
- return !(region.End < obj.LastSeenLineStartCol || region.Start > obj.LastSeenLineEndCol);
|
|
|
+ return !(region.End < obj.PreSeenLineStartCol || region.Start > obj.PreSeenLineEndCol);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -1901,14 +1428,17 @@ namespace CCDCount.DLL
|
|
|
private bool TryAdd(List<ActiveObjectClass> list, ActiveObjectClass item, int maxSize)
|
|
|
{
|
|
|
list.Add(item);
|
|
|
- Interlocked.Increment(ref _HistoryActiveNum);
|
|
|
- if(item.StateCode == 0)
|
|
|
+ if(item.StateCode!=9&& item.StateCode != 10)
|
|
|
{
|
|
|
- Interlocked.Increment(ref _OkHistoryNum);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Interlocked.Increment(ref _NgHistoryNum);
|
|
|
+ Interlocked.Increment(ref _HistoryActiveNum);
|
|
|
+ if (item.StateCode == 0)
|
|
|
+ {
|
|
|
+ Interlocked.Increment(ref _OkHistoryNum);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Interlocked.Increment(ref _NgHistoryNum);
|
|
|
+ }
|
|
|
}
|
|
|
if (list.Count > maxSize)
|
|
|
{
|
|
|
@@ -1926,117 +1456,10 @@ namespace CCDCount.DLL
|
|
|
//图像处理线程
|
|
|
public Task ProcessingTask { get; set; }
|
|
|
/// <summary>
|
|
|
- /// 识别图像线程
|
|
|
+ /// 图像处理线程运行方法
|
|
|
/// </summary>
|
|
|
- private void IdentifyImageProcess()
|
|
|
- {
|
|
|
- Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
- while (IsIdentify)
|
|
|
- {
|
|
|
- //判断队列中是否有数据
|
|
|
- if (IFrameDatas.Count() > 0)
|
|
|
- {
|
|
|
- stopwatch.Restart();
|
|
|
- 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.待识别队列数据堆积);
|
|
|
- }
|
|
|
- }
|
|
|
- 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}");
|
|
|
- }
|
|
|
- if (IframeData != null)
|
|
|
- {
|
|
|
- //识别
|
|
|
- ProcessImageSequence2(IframeData);
|
|
|
- IframeData.Dispose();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Thread.Sleep(1);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ /// <param name="token"></param>
|
|
|
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();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private async void IdentifyImageProcessTask2(CancellationToken token)
|
|
|
{
|
|
|
const int batchSize = 5; // 每批处理的最大图像数量
|
|
|
var batch = new List<IFrameOut>(batchSize); // 存储当前批次的图像
|
|
|
@@ -2050,10 +1473,6 @@ namespace CCDCount.DLL
|
|
|
while (batch.Count < batchSize && RingBuffer_IFrameDatas.TryDequeue(out IFrameOut imageData))
|
|
|
{
|
|
|
batch.Add(imageData);
|
|
|
- if (RingBuffer_IFrameDatas.Count > 0)
|
|
|
- {
|
|
|
- QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程
|
|
|
- }
|
|
|
}
|
|
|
// 顺序处理当前批次的所有图像
|
|
|
foreach (var image in batch)
|
|
|
@@ -2061,7 +1480,7 @@ namespace CCDCount.DLL
|
|
|
if (image != null)
|
|
|
{
|
|
|
//识别
|
|
|
- ProcessImageSequence2(image);
|
|
|
+ ProcessImageSequence(image);
|
|
|
image.Dispose();
|
|
|
}
|
|
|
else
|
|
|
@@ -2073,9 +1492,10 @@ namespace CCDCount.DLL
|
|
|
}
|
|
|
catch(Exception ex)
|
|
|
{
|
|
|
-
|
|
|
+ LOG.error("ShuLiClass-IdentifyImageProcessTask:" + ex.Message);
|
|
|
}
|
|
|
}
|
|
|
+ LOG.log("图像处理线程运行结束", 6);
|
|
|
}
|
|
|
#endregion
|
|
|
}
|