|
@@ -34,9 +34,12 @@ namespace CCDCount.DLL
|
|
|
private List<int> _ChannelsRoi = new List<int>();
|
|
private List<int> _ChannelsRoi = new List<int>();
|
|
|
private int ChannelWidth = 0;//每个区域的宽度
|
|
private int ChannelWidth = 0;//每个区域的宽度
|
|
|
private int IdentifyImageWidth = -1;
|
|
private int IdentifyImageWidth = -1;
|
|
|
- private static readonly object _lockObj = new object(); // 专用锁对象
|
|
|
|
|
|
|
+ //private static readonly object _lockObj = new object(); // 专用锁对象
|
|
|
private int ObjectNum = 0;
|
|
private int ObjectNum = 0;
|
|
|
public int ImageNum { get { return IFrameDatas.Count; } }
|
|
public int ImageNum { get { return IFrameDatas.Count; } }
|
|
|
|
|
+ private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
|
|
|
|
|
+ private double XCoefficient = 1;
|
|
|
|
|
+ private double YCoefficient = 1;
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
|
#region 公共方法
|
|
#region 公共方法
|
|
@@ -83,6 +86,7 @@ namespace CCDCount.DLL
|
|
|
return ObjectNum;
|
|
return ObjectNum;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ List<ActiveObjectClass> lostObjects = new List<ActiveObjectClass>();
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 处理图像序列的主入口
|
|
/// 处理图像序列的主入口
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -99,42 +103,53 @@ namespace CCDCount.DLL
|
|
|
currentLine += 1;
|
|
currentLine += 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
//识别到结果并输出
|
|
//识别到结果并输出
|
|
|
- lock (_lockObj)
|
|
|
|
|
- {
|
|
|
|
|
- // 清理超时未更新的物体
|
|
|
|
|
- var lostObjects = activeObjects
|
|
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ //{
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ // 清理超时未更新的物体
|
|
|
|
|
+ lostObjects = activeObjects
|
|
|
.Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
.Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
.ToList();
|
|
.ToList();
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ //}
|
|
|
|
|
+ if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果筛选,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
|
|
|
|
|
|
|
+ List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
|
|
|
|
|
|
|
|
- // 有物体转变为活跃物体,返回值转为true
|
|
|
|
|
- if (lostObjects.Count() > 0)
|
|
|
|
|
|
|
+ // 有物体转变为活跃物体,返回值转为true
|
|
|
|
|
+ if (lostObjects.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ result = true;
|
|
|
|
|
+ stopwatch.Restart();
|
|
|
|
|
+ foreach (var item in lostObjects)
|
|
|
{
|
|
{
|
|
|
- result = true;
|
|
|
|
|
- foreach (var item in lostObjects)
|
|
|
|
|
|
|
+ //噪点判定
|
|
|
|
|
+ if (item.Area < shuLiConfig.NoiseFilter_Threshold)
|
|
|
{
|
|
{
|
|
|
- //噪点判定
|
|
|
|
|
- if (item.Area < shuLiConfig.NoiseFilter_Threshold)
|
|
|
|
|
- {
|
|
|
|
|
- item.StateCode = 9;
|
|
|
|
|
- //LOG.log(string.Format("噪点过滤,噪点面积:{0}", item.Area), 6);
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- //转为历史物体,添加缺少的参数
|
|
|
|
|
- item.Num = ObjectNum += 1;
|
|
|
|
|
- item.ChannelNO = ActiveChannel(item);
|
|
|
|
|
- item.EndCheckTime = DateTime.Now;
|
|
|
|
|
- item.MaxLength = GetActionMaxLength(item.RowsData);
|
|
|
|
|
- if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
|
|
- {
|
|
|
|
|
- item.StateCode = 7;
|
|
|
|
|
- FaultLog.RecordLogMessage("ShuLiClass-ProcessLine:非颗粒,视野异常", 3);
|
|
|
|
|
- LOG.log(string.Format("ShuLiClass-ProcessLine:非颗粒,视野异常"), 6);
|
|
|
|
|
- Console.WriteLine("ShuLiClass-ProcessLine:非颗粒,视野异常");
|
|
|
|
|
- }
|
|
|
|
|
- else if (shuLiConfig.PandingCode != -1)
|
|
|
|
|
|
|
+ item.StateCode = 9;
|
|
|
|
|
+ //LOG.log(string.Format("噪点过滤,噪点面积:{0}", item.Area), 6);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ //转为历史物体,添加缺少的参数
|
|
|
|
|
+ item.Num = Interlocked.Increment(ref ObjectNum) + 1;
|
|
|
|
|
+ item.ChannelNO = ActiveChannel(item);
|
|
|
|
|
+ item.EndCheckTime = DateTime.Now;
|
|
|
|
|
+ item.MaxLength = GetActionMaxLength(item.RowsData);
|
|
|
|
|
+ //item.MaxLength = 100;
|
|
|
|
|
+ if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)
|
|
|
|
|
+ {
|
|
|
|
|
+ item.StateCode = 7;
|
|
|
|
|
+ //FaultLog.RecordLogMessage("ShuLiClass-ProcessLine:非颗粒,视野异常", 3);
|
|
|
|
|
+ //LOG.log(string.Format("ShuLiClass-ProcessLine:非颗粒,视野异常"), 6);
|
|
|
|
|
+ //Console.WriteLine("ShuLiClass-ProcessLine:非颗粒,视野异常");
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (shuLiConfig.PandingCode != -1)
|
|
|
{
|
|
{
|
|
|
if (item.StateCode != -1)
|
|
if (item.StateCode != -1)
|
|
|
{
|
|
{
|
|
@@ -178,28 +193,44 @@ namespace CCDCount.DLL
|
|
|
Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
|
|
Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- OneActive.Add(item);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (OneActive.Count > 0)
|
|
|
|
|
- {
|
|
|
|
|
- LOG.log(string.Format("识别完成,首个颗粒编号:{0},颗粒数量:{1}", OneActive[0].Num, OneActive.Count), 6);
|
|
|
|
|
- //触发回调事件
|
|
|
|
|
- Task.Run(() =>
|
|
|
|
|
- {
|
|
|
|
|
- OnWorkCompleted(OneActive);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ OneActive.Add(item);
|
|
|
}
|
|
}
|
|
|
- else
|
|
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
|
|
+ //{
|
|
|
|
|
+ // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ //}
|
|
|
|
|
+ //stopwatch.Restart();
|
|
|
|
|
+ if (OneActive.Count > 0)
|
|
|
{
|
|
{
|
|
|
- OneActive = null;
|
|
|
|
|
|
|
+ //LOG.log(string.Format("识别完成,首个颗粒编号:{0},颗粒数量:{1}", OneActive[0].Num, OneActive.Count), 6);
|
|
|
|
|
+ //触发回调事件
|
|
|
|
|
+ //Task.Run(() =>
|
|
|
|
|
+ //{
|
|
|
|
|
+ // OnWorkCompleted(OneActive);
|
|
|
|
|
+ //});
|
|
|
|
|
+ ThreadPool.QueueUserWorkItem(_ =>OnWorkCompleted(OneActive));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 累加到总数并从活跃物体转移到历史物体
|
|
|
|
|
- lostObjects.Where(o => o.Area >= shuLiConfig.NoiseFilter_Threshold && o.StateCode != 7 && o.StateCode != 9).ToList().ForEach(o => TryAdd(historyActiveObjects, o, 2500));
|
|
|
|
|
- lostObjects.ForEach(o => activeObjects.Remove(o));
|
|
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ //if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
|
|
+ //{
|
|
|
|
|
+ // FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果完成回调函数,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ //}
|
|
|
}
|
|
}
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ OneActive.Clear();
|
|
|
|
|
+ }
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ //{
|
|
|
|
|
+ _rwLock.EnterWriteLock();
|
|
|
|
|
+ // 累加到总数并从活跃物体转移到历史物体
|
|
|
|
|
+ OneActive.ForEach(o => TryAdd(historyActiveObjects, o, 2500));
|
|
|
|
|
+ //lostObjects.Where(o => o.StateCode != 7 && o.StateCode != 9).ToList().ForEach(o => TryAdd(historyActiveObjects, o, 2500));
|
|
|
|
|
+ lostObjects.ForEach(o => activeObjects.Remove(o));
|
|
|
|
|
+ _rwLock.ExitWriteLock();
|
|
|
|
|
+ //}
|
|
|
|
|
+
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -220,10 +251,13 @@ namespace CCDCount.DLL
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
public List<ActiveObjectClass> GetHistoryActive()
|
|
public List<ActiveObjectClass> GetHistoryActive()
|
|
|
{
|
|
{
|
|
|
- lock (_lockObj) // 加锁
|
|
|
|
|
- {
|
|
|
|
|
- return historyActiveObjects.ToList();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ //lock (_lockObj) // 加锁
|
|
|
|
|
+ //{
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ var result = historyActiveObjects.ToList();
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ return result;
|
|
|
|
|
+ //}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -232,8 +266,11 @@ namespace CCDCount.DLL
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
public int GetHistoryActiveNum()
|
|
public int GetHistoryActiveNum()
|
|
|
{
|
|
{
|
|
|
- lock (_lockObj) // 加锁
|
|
|
|
|
- return historyActiveObjects.Count();
|
|
|
|
|
|
|
+ //lock (_lockObj) // 加锁
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ var result = historyActiveObjects.Count;
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -242,8 +279,11 @@ namespace CCDCount.DLL
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
public int GetOkHistoryNum()
|
|
public int GetOkHistoryNum()
|
|
|
{
|
|
{
|
|
|
- lock (_lockObj)
|
|
|
|
|
- return historyActiveObjects.Where(o => o.StateCode == 0).Count();
|
|
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ var result = historyActiveObjects.Where(o => o.StateCode == 0).ToList().Count;
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -252,8 +292,11 @@ namespace CCDCount.DLL
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
public int GetNgHistoryNum()
|
|
public int GetNgHistoryNum()
|
|
|
{
|
|
{
|
|
|
- lock (_lockObj)
|
|
|
|
|
- return historyActiveObjects.Where(o => o.StateCode != 0).Count();
|
|
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ var result = historyActiveObjects.Where(o => o.StateCode != 0).ToList().Count;
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -262,11 +305,13 @@ namespace CCDCount.DLL
|
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
|
public bool ClearHistoryActive()
|
|
public bool ClearHistoryActive()
|
|
|
{
|
|
{
|
|
|
- lock (_lockObj)
|
|
|
|
|
- {
|
|
|
|
|
- historyActiveObjects.Clear();
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ //{
|
|
|
|
|
+ _rwLock.EnterWriteLock();
|
|
|
|
|
+ historyActiveObjects.Clear();
|
|
|
|
|
+ _rwLock.ExitWriteLock();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ //}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -387,6 +432,34 @@ namespace CCDCount.DLL
|
|
|
result = shuLiConfig.ImageWidth;
|
|
result = shuLiConfig.ImageWidth;
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 获取坐标转换系数
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="XCoefficient"></param>
|
|
|
|
|
+ /// <param name="YCoefficient"></param>
|
|
|
|
|
+ public void GetXYCoefficient(out double XCoefficient, out double YCoefficient)
|
|
|
|
|
+ {
|
|
|
|
|
+ XCoefficient = this.XCoefficient;
|
|
|
|
|
+ YCoefficient = this.YCoefficient;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 设置坐标转换X系数
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="XCoefficient"></param>
|
|
|
|
|
+ public void SetXCoefficient(double XCoefficient)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.XCoefficient = XCoefficient;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 设置坐标转换Y系数
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public void SetYCoefficient(double YCoefficient)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.YCoefficient = YCoefficient;
|
|
|
|
|
+ }
|
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
|
#region 私有方法
|
|
#region 私有方法
|
|
@@ -396,6 +469,7 @@ namespace CCDCount.DLL
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
private void OnWorkCompleted(List<ActiveObjectClass> activeObject)
|
|
private void OnWorkCompleted(List<ActiveObjectClass> activeObject)
|
|
|
{
|
|
{
|
|
|
|
|
+ if(activeObject == null) return;
|
|
|
ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
|
|
ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
|
|
|
// 触发事件
|
|
// 触发事件
|
|
|
WorkCompleted?.Invoke(this, activeObjectEventArgs);
|
|
WorkCompleted?.Invoke(this, activeObjectEventArgs);
|
|
@@ -411,6 +485,7 @@ namespace CCDCount.DLL
|
|
|
/// <param name="image">当前行像素数组</param>
|
|
/// <param name="image">当前行像素数组</param>
|
|
|
private bool ProcessLine(IImage imagedata, int RowNo)
|
|
private bool ProcessLine(IImage imagedata, int RowNo)
|
|
|
{
|
|
{
|
|
|
|
|
+ Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
bool result = false;
|
|
bool result = false;
|
|
|
// 步骤1:检测当前行的有效区域
|
|
// 步骤1:检测当前行的有效区域
|
|
|
var currentRegions = FindValidRegions(imagedata, RowNo);
|
|
var currentRegions = FindValidRegions(imagedata, RowNo);
|
|
@@ -428,93 +503,112 @@ namespace CCDCount.DLL
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
IsPrintLightOnError = false;
|
|
IsPrintLightOnError = false;
|
|
|
- lock (_lockObj)
|
|
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
{
|
|
{
|
|
|
- foreach (var region in currentRegions)
|
|
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像连通域检测超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ }
|
|
|
|
|
+ stopwatch.Restart();
|
|
|
|
|
+ //lock (_lockObj)
|
|
|
|
|
+ //{
|
|
|
|
|
+ foreach (var region in currentRegions)
|
|
|
|
|
+ {
|
|
|
|
|
+
|
|
|
|
|
+ // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ var matcheds = activeObjects.Where(o =>
|
|
|
|
|
+ IsOverlapping(o, region) &&
|
|
|
|
|
+ (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP).ToList();
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ //当有多个可合并的活跃物体时,将多个物体合并
|
|
|
|
|
+ if (matcheds.Count >= 2)
|
|
|
{
|
|
{
|
|
|
- // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
|
|
|
|
|
- var matcheds = activeObjects.Where(o =>
|
|
|
|
|
- IsOverlapping(o, region) &&
|
|
|
|
|
- (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP).ToList();
|
|
|
|
|
- //当有多个可合并的活跃物体时,将多个物体合并
|
|
|
|
|
- if (matcheds.Count >= 2)
|
|
|
|
|
|
|
+ // 合并有效区域队列
|
|
|
|
|
+ var CopeRowsData = new List<RowStartEndCol>();
|
|
|
|
|
+ matcheds.ForEach(o => CopeRowsData = CopeRowsData.Concat(o.RowsData).ToList());
|
|
|
|
|
+ // 合并有效区域并保存在新的区域中
|
|
|
|
|
+ var MergeMatched = new ActiveObjectClass
|
|
|
{
|
|
{
|
|
|
- // 合并有效区域队列
|
|
|
|
|
- var CopeRowsData = new List<RowStartEndCol>();
|
|
|
|
|
- matcheds.ForEach(o => CopeRowsData = CopeRowsData.Concat(o.RowsData).ToList());
|
|
|
|
|
- // 合并有效区域并保存在新的区域中
|
|
|
|
|
- var MergeMatched = new ActiveObjectClass
|
|
|
|
|
- {
|
|
|
|
|
- MinStartCol = matcheds.Min(o => o.MinStartCol),
|
|
|
|
|
- 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),
|
|
|
|
|
- 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
|
|
|
|
|
- };
|
|
|
|
|
- // 从活跃区域中删除被合并的区域
|
|
|
|
|
- matcheds.ForEach(o => activeObjects.Remove(o));
|
|
|
|
|
- // 保存新的区域到活跃区域中
|
|
|
|
|
- activeObjects.Add(MergeMatched);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 搜获可用且可合并的活跃区域
|
|
|
|
|
- var matched = activeObjects.FirstOrDefault(o =>
|
|
|
|
|
- IsOverlapping(o, region) &&
|
|
|
|
|
- (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP);
|
|
|
|
|
- if (matched != null)
|
|
|
|
|
|
|
+ MinStartCol = matcheds.Min(o => o.MinStartCol),
|
|
|
|
|
+ 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),
|
|
|
|
|
+ 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
|
|
|
|
|
+ };
|
|
|
|
|
+ _rwLock.EnterWriteLock();
|
|
|
|
|
+ // 从活跃区域中删除被合并的区域
|
|
|
|
|
+ matcheds.ForEach(o => activeObjects.Remove(o));
|
|
|
|
|
+ // 保存新的区域到活跃区域中
|
|
|
|
|
+ activeObjects.Add(MergeMatched);
|
|
|
|
|
+ _rwLock.ExitWriteLock();
|
|
|
|
|
+ }
|
|
|
|
|
+ _rwLock.EnterReadLock();
|
|
|
|
|
+ // 搜获可用且可合并的活跃区域
|
|
|
|
|
+ var matched = activeObjects.FirstOrDefault(o =>
|
|
|
|
|
+ IsOverlapping(o, region) &&
|
|
|
|
|
+ (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP);
|
|
|
|
|
+ _rwLock.ExitReadLock();
|
|
|
|
|
+ if (matched != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 合并区域:扩展物体边界并更新状态
|
|
|
|
|
+ matched.MinStartCol = Math.Min(matched.MinStartCol, region.Start);
|
|
|
|
|
+ matched.MaxEndCol = Math.Max(matched.MaxEndCol, region.End);
|
|
|
|
|
+ matched.Area += region.End - region.Start + 1;
|
|
|
|
|
+ matched.LastSeenLine = currentLine;
|
|
|
|
|
+ matched.RowsData.Add(new RowStartEndCol
|
|
|
{
|
|
{
|
|
|
- // 合并区域:扩展物体边界并更新状态
|
|
|
|
|
- matched.MinStartCol = Math.Min(matched.MinStartCol, region.Start);
|
|
|
|
|
- matched.MaxEndCol = Math.Max(matched.MaxEndCol, region.End);
|
|
|
|
|
- matched.Area += region.End - region.Start + 1;
|
|
|
|
|
- matched.LastSeenLine = currentLine;
|
|
|
|
|
- matched.RowsData.Add(new RowStartEndCol
|
|
|
|
|
- {
|
|
|
|
|
- StartCol = region.Start,
|
|
|
|
|
- EndCol = region.End,
|
|
|
|
|
- RowsCol = currentLine,
|
|
|
|
|
- });
|
|
|
|
|
- matched.LastSeenLineStartCol = region.Start;
|
|
|
|
|
- matched.LastSeenLineEndCol = region.End;
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
|
|
+ StartCol = region.Start,
|
|
|
|
|
+ EndCol = region.End,
|
|
|
|
|
+ RowsCol = currentLine,
|
|
|
|
|
+ });
|
|
|
|
|
+ matched.LastSeenLineStartCol = region.Start;
|
|
|
|
|
+ matched.LastSeenLineEndCol = region.End;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ _rwLock.EnterWriteLock();
|
|
|
|
|
+ // 创建新物体(首次出现的区域)
|
|
|
|
|
+ activeObjects.Add(new ActiveObjectClass
|
|
|
{
|
|
{
|
|
|
- // 创建新物体(首次出现的区域)
|
|
|
|
|
- activeObjects.Add(new ActiveObjectClass
|
|
|
|
|
- {
|
|
|
|
|
- MinStartCol = region.Start,
|
|
|
|
|
- MaxEndCol = region.End,
|
|
|
|
|
- StartLine = currentLine,
|
|
|
|
|
- LastSeenLine = currentLine,
|
|
|
|
|
- LastSeenLineStartCol = region.Start,
|
|
|
|
|
- LastSeenLineEndCol = region.End,
|
|
|
|
|
- StartCheckTime = DateTime.Now,
|
|
|
|
|
- Area = region.End - region.Start + 1,
|
|
|
|
|
- ImageWidth = IdentifyImageWidth,
|
|
|
|
|
- RowsData = new List<RowStartEndCol> {
|
|
|
|
|
- new RowStartEndCol {
|
|
|
|
|
- StartCol = region.Start,
|
|
|
|
|
- EndCol = region.End,
|
|
|
|
|
- RowsCol = currentLine,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ MinStartCol = region.Start,
|
|
|
|
|
+ MaxEndCol = region.End,
|
|
|
|
|
+ StartLine = currentLine,
|
|
|
|
|
+ LastSeenLine = currentLine,
|
|
|
|
|
+ LastSeenLineStartCol = region.Start,
|
|
|
|
|
+ LastSeenLineEndCol = region.End,
|
|
|
|
|
+ StartCheckTime = DateTime.Now,
|
|
|
|
|
+ Area = region.End - region.Start + 1,
|
|
|
|
|
+ ImageWidth = IdentifyImageWidth,
|
|
|
|
|
+ RowsData = new List<RowStartEndCol> {
|
|
|
|
|
+ new RowStartEndCol {
|
|
|
|
|
+ StartCol = region.Start,
|
|
|
|
|
+ EndCol = region.End,
|
|
|
|
|
+ RowsCol = currentLine,
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ _rwLock.ExitWriteLock();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ //}
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ if (stopwatch.ElapsedMilliseconds > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像区域合并超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
currentRegions.Clear();
|
|
currentRegions.Clear();
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+ List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 检测有效物体区域(横向连续黑色像素段)
|
|
/// 检测有效物体区域(横向连续黑色像素段)
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -522,7 +616,7 @@ namespace CCDCount.DLL
|
|
|
/// <returns>有效区域列表(起始/结束位置)</returns>
|
|
/// <returns>有效区域列表(起始/结束位置)</returns>
|
|
|
private List<ValidRegionModelClass> FindValidRegions(IImage image, int RowNo)
|
|
private List<ValidRegionModelClass> FindValidRegions(IImage image, int RowNo)
|
|
|
{
|
|
{
|
|
|
- List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
|
|
|
|
|
|
|
+ regions.Clear();
|
|
|
int start = -1; // 当前区域起始标记
|
|
int start = -1; // 当前区域起始标记
|
|
|
int end = -1;
|
|
int end = -1;
|
|
|
// 遍历所有像素列
|
|
// 遍历所有像素列
|
|
@@ -728,7 +822,8 @@ namespace CCDCount.DLL
|
|
|
CenterPoint = new Point() { X = (hull[1].X + hull[0].X) / 2, Y = (hull[1].Y + hull[0].Y) / 2 },
|
|
CenterPoint = new Point() { X = (hull[1].X + hull[0].X) / 2, Y = (hull[1].Y + hull[0].Y) / 2 },
|
|
|
Width = 0,
|
|
Width = 0,
|
|
|
Height = Distance(hull[0], hull[1]),
|
|
Height = Distance(hull[0], hull[1]),
|
|
|
- Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X)
|
|
|
|
|
|
|
+ Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X),
|
|
|
|
|
+ MaxLength = Distance(hull[0], hull[1])
|
|
|
};
|
|
};
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
@@ -737,6 +832,8 @@ namespace CCDCount.DLL
|
|
|
int pointIndex1 = 0;
|
|
int pointIndex1 = 0;
|
|
|
int pointIndex2 = 0;
|
|
int pointIndex2 = 0;
|
|
|
|
|
|
|
|
|
|
+ maxDist = Distance(hull[pointIndex1], hull[pointIndex2], XCoefficient, YCoefficient);
|
|
|
|
|
+
|
|
|
for (int i = 0; i < n; i++)
|
|
for (int i = 0; i < n; i++)
|
|
|
{
|
|
{
|
|
|
for (int j = i + 1; j < n; j++)
|
|
for (int j = i + 1; j < n; j++)
|
|
@@ -751,6 +848,7 @@ namespace CCDCount.DLL
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
|
|
double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
|
|
|
var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
|
|
var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
|
|
|
// 找到包围盒
|
|
// 找到包围盒
|
|
@@ -779,7 +877,8 @@ namespace CCDCount.DLL
|
|
|
CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
|
|
CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
|
|
|
Width = width,
|
|
Width = width,
|
|
|
Height = height,
|
|
Height = height,
|
|
|
- Angle = angle
|
|
|
|
|
|
|
+ Angle = angle,
|
|
|
|
|
+ MaxLength = maxDist
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|
|
@@ -790,7 +889,7 @@ namespace CCDCount.DLL
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
|
|
/// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
|
|
|
/// <returns>最小外接矩形的四个顶点</returns>
|
|
/// <returns>最小外接矩形的四个顶点</returns>
|
|
|
- public List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
|
|
|
|
|
|
|
+ private List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
|
|
|
{
|
|
{
|
|
|
if (convexHull == null || convexHull.Count < 3)
|
|
if (convexHull == null || convexHull.Count < 3)
|
|
|
return null;
|
|
return null;
|
|
@@ -889,6 +988,13 @@ namespace CCDCount.DLL
|
|
|
return Math.Sqrt(dx * dx + dy * dy);
|
|
return Math.Sqrt(dx * dx + dy * dy);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public double Distance(Point a,Point b,double XCoefficient,double YCoefficient)
|
|
|
|
|
+ {
|
|
|
|
|
+ double dx = a.X - b.X;
|
|
|
|
|
+ double dy = a.Y - b.Y;
|
|
|
|
|
+ return Math.Sqrt(dx * dx * XCoefficient + dy * dy * YCoefficient);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private bool TryAdd(List<ActiveObjectClass> list, ActiveObjectClass item, int maxSize)
|
|
private bool TryAdd(List<ActiveObjectClass> list, ActiveObjectClass item, int maxSize)
|
|
|
{
|
|
{
|
|
|
list.Add(item);
|
|
list.Add(item);
|
|
@@ -906,19 +1012,25 @@ namespace CCDCount.DLL
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
private void IdentifyImageProcess()
|
|
private void IdentifyImageProcess()
|
|
|
{
|
|
{
|
|
|
- //Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
|
|
|
|
+ Stopwatch stopwatch = Stopwatch.StartNew();
|
|
|
while (IsIdentify)
|
|
while (IsIdentify)
|
|
|
{
|
|
{
|
|
|
//判断队列中是否有数据
|
|
//判断队列中是否有数据
|
|
|
if (IFrameDatas.Count() > 0)
|
|
if (IFrameDatas.Count() > 0)
|
|
|
{
|
|
{
|
|
|
- //stopwatch.Restart();
|
|
|
|
|
- if (IFrameDatas.Count() > 50)
|
|
|
|
|
- SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积, $"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}", "DLL:ShuLIClass-IdentifyImageProcess");
|
|
|
|
|
- //FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
|
|
|
|
|
- else
|
|
|
|
|
- SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
|
|
|
|
|
|
|
+ stopwatch.Restart();
|
|
|
|
|
+ if (IFrameDatas.Count() > 5)
|
|
|
|
|
+ //SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积, $"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}", "DLL:ShuLIClass-IdentifyImageProcess");
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
|
|
|
|
|
+ //else
|
|
|
|
|
+ //SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
|
|
|
IFrameDatas.TryDequeue(out IImage IframeData);
|
|
IFrameDatas.TryDequeue(out IImage IframeData);
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ if (stopwatch.ElapsedMilliseconds > 5)
|
|
|
|
|
+ {
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:图像读取超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ }
|
|
|
|
|
+ stopwatch.Restart();
|
|
|
//是否成功取得数据
|
|
//是否成功取得数据
|
|
|
if (IframeData != null)
|
|
if (IframeData != null)
|
|
|
{
|
|
{
|
|
@@ -931,7 +1043,11 @@ namespace CCDCount.DLL
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
//输出耗时
|
|
//输出耗时
|
|
|
- //stopwatch.Stop();
|
|
|
|
|
|
|
+ stopwatch.Stop();
|
|
|
|
|
+ if(stopwatch.ElapsedMilliseconds > 5)
|
|
|
|
|
+ {
|
|
|
|
|
+ FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:图像识别超时,此次识别耗时:{stopwatch.Elapsed}");
|
|
|
|
|
+ }
|
|
|
//Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed},待识别队列剩余数量{IFrameDatas.Count()}");
|
|
//Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed},待识别队列剩余数量{IFrameDatas.Count()}");
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|