123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777 |
- using CCDCount.MODEL.ConfigModel;
- using CCDCount.MODEL.ShuLiModel;
- using LogClass;
- using MvCameraControl;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- namespace CCDCount.DLL
- {
- public class ShuLiClass
- {
- #region 变量
- /// <summary>
- /// 当活跃物体转变为历史物体时的回调事件
- /// </summary>
- public event EventHandler<ActiveObjectEventArgsClass> WorkCompleted;
- private List<ActiveObjectClass> activeObjects = new List<ActiveObjectClass>(); // 当前跟踪中的物体
- private List<ActiveObjectClass> historyActiveObjects = new List<ActiveObjectClass>(); // 历史物体
- private ConcurrentQueue<IImage> IFrameDatas = new ConcurrentQueue<IImage>(); //图像数据队列
- private Thread IdentifyImageProcessThread = null; // 识别线程
- private bool IsIdentify = false; //线程是否开始识别的标志
- private long currentLine = 0; //行数记录
- private ShuLiConfigClass shuLiConfig = null;// 数粒参数配置文件
- public List<int> ChannelsRoi { get { return _ChannelsRoi; } }
- private List<int> _ChannelsRoi = new List<int>();
- private int ChannelWidth = 0;//每个区域的宽度
- private int IdentifyImageWidth = -1;
- private static readonly object _lockObj = new object(); // 专用锁对象\
- private int ObjectNum = 0;
- public int ImageNum { get { return IFrameDatas.Count; } }
- #endregion
- #region 公共方法
- /// <summary>
- /// 初始化构造方法
- /// </summary>
- public ShuLiClass()
- {
- // 加载默认参数
- shuLiConfig = new ShuLiConfigClass()
- {
- Channel = 8,
- PandingCode = 2
- };
- }
- public ShuLiClass(ShuLiConfigClass config)
- {
- if (config.IsLoadCanfig)
- {
- // 加载传出的参数
- shuLiConfig = config;
- }
- else
- {
- // 加载默认参数
- shuLiConfig = new ShuLiConfigClass()
- {
- Channel = 8,
- PandingCode = 2
- };
- }
- InitChannel();
- }
- public long InitCurrentLine(int line)
- {
- currentLine = line;
- return currentLine;
- }
- public int InitNum(int num)
- {
- ObjectNum = num;
- return ObjectNum;
- }
- /// <summary>
- /// 处理图像序列的主入口
- /// </summary>
- /// <param name="image">图像像素数据</param>
- /// <param name="ImageWidth">图像宽</param>
- /// <param name="currentLine">当前行数</param>
- /// <returns>检测到的物体总数</returns>
- public bool ProcessImageSequence(IImage image)
- {
- bool result = false;
- for (int i = 0; i < image.Height; i++)
- {
- result = ProcessLine(image, i);
- currentLine += 1;
- }
- return result;
- }
- /// <summary>
- /// 返回最后一个历史物品
- /// </summary>
- /// <returns></returns>
- public ActiveObjectClass GetLastActive()
- {
- if (historyActiveObjects.Count() == 0)
- return null;
- return historyActiveObjects.Last();
- }
- /// <summary>
- /// 返回历史物品
- /// </summary>
- /// <returns></returns>
- public List<ActiveObjectClass> GetHistoryActive()
- {
- lock (_lockObj) // 加锁
- {
- return historyActiveObjects.ToList();
- }
- }
- /// <summary>
- /// 返回缓存在内存的历史物品的总数量
- /// </summary>
- /// <returns></returns>
- public int GetHistoryActiveNum()
- {
- lock (_lockObj) // 加锁
- return historyActiveObjects.Count();
- }
- /// <summary>
- /// 获取历史数据中,正常数据数量
- /// </summary>
- /// <returns></returns>
- public int GetOkHistoryNum()
- {
- lock (_lockObj)
- return historyActiveObjects.Where(o => o.StateCode == 0).Count();
- }
- /// <summary>
- /// 获取历史数据中,异常数据数量
- /// </summary>
- /// <returns></returns>
- public int GetNgHistoryNum()
- {
- lock (_lockObj)
- return historyActiveObjects.Where(o => o.StateCode != 0).Count();
- }
- /// <summary>
- /// 清除历史数据
- /// </summary>
- /// <returns></returns>
- public bool ClearHistoryActive()
- {
- lock (_lockObj)
- {
- historyActiveObjects.Clear();
- return true;
- }
- }
- /// <summary>
- /// 开启识别
- /// </summary>
- public void StartIdentifyFuntion(int ImaageWidth)
- {
- UpdateIdentifyImageWidth(ImaageWidth);
- InitChannel();
- try
- {
- // 标志位置位true
- IsIdentify = true;
- // 打开识别线程
- IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
- {
- Priority = ThreadPriority.Highest
- };
- IdentifyImageProcessThread.Start();
- }
- catch (Exception ex)
- {
- FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message);
- throw;
- }
- }
- /// <summary>
- /// 关闭识别
- /// </summary>
- public void StopIdentifyFuntion()
- {
- try
- {
- // 标志位设为false
- IsIdentify = false;
- if (IdentifyImageProcessThread != null && IdentifyImageProcessThread.IsAlive)
- IdentifyImageProcessThread.Join();
- }
- catch (Exception ex)
- {
- FaultLog.RecordErrorMessage("Stop thread failed!, " + ex.Message);
- throw;
- }
- }
- /// <summary>
- /// 向识别队列添加一个数据
- /// </summary>
- /// <param name="items"></param>
- public void SetOnceIdentifyImageData(IImage items)
- {
- IFrameDatas.Enqueue(items.Clone() as IImage);
- }
- /// <summary>
- /// 保存参数
- /// </summary>
- public void SaveConfig()
- {
- if (!Directory.Exists(".\\Config\\")) Directory.CreateDirectory(".\\Config\\");
- XmlStorage.SerializeToXml(shuLiConfig, ".\\Config\\ShuLiConfig.xml");
- }
- /// <summary>
- /// 更新检测宽度信息
- /// </summary>
- /// <param name="Width"></param>
- public void UpdateIdentifyImageWidth(int Width)
- {
- IdentifyImageWidth = Width;
- }
- /// <summary>
- /// 初始化通道划分
- /// </summary>
- /// <param name="ImageWidth"></param>
- public void InitChannel()
- {
- _ChannelsRoi.Clear();
- shuLiConfig.ImageWidth = IdentifyImageWidth == -1 ? shuLiConfig.ImageWidth : IdentifyImageWidth;
- if (shuLiConfig.Channel > 0)
- {
- if (shuLiConfig.IsIdentifyRoiOpen)
- {
- ChannelWidth = (shuLiConfig.IdentifyStopX - shuLiConfig.IdentifyStartX) / shuLiConfig.Channel;
- }
- else
- {
- ChannelWidth = shuLiConfig.ImageWidth / shuLiConfig.Channel;
- }
- for (int i = 0; i < shuLiConfig.Channel; i++)
- {
- _ChannelsRoi.Add(ChannelWidth + i * ChannelWidth);
- }
- }
- }
- /// <summary>
- /// 获取配置信息
- /// </summary>
- /// <returns></returns>
- public ShuLiConfigClass GetConfigValue()
- {
- ShuLiConfigClass result = shuLiConfig;
- return result;
- }
- public int GetConfigImageWidth()
- {
- int result = -1;
- if (shuLiConfig != null)
- result = shuLiConfig.ImageWidth;
- return result;
- }
- #endregion
- #region 私有方法
- /// <summary>
- /// 对外通知事件
- /// </summary>
- private void OnWorkCompleted(List<ActiveObjectClass> activeObject)
- {
- ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
- // 触发事件
- WorkCompleted?.Invoke(this, activeObjectEventArgs);
- }
- private bool IsPrintLightOnError = false;
- /// <summary>
- /// 处理单行像素数据
- /// 返回值为false的时候无活跃物体转变为历史物体
- /// 返回值为true的时候有活跃物体转变为历史物体
- /// </summary>
- /// <param name="image">当前行像素数组</param>
- private bool ProcessLine(IImage imagedata, int RowNo)
- {
- bool result = false;
- // 步骤1:检测当前行的有效区域
- var currentRegions = FindValidRegions(imagedata, RowNo);
- if (currentRegions.Count == 1)
- {
- if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imagedata.Width)
- {
- if (!IsPrintLightOnError)
- {
- FaultLog.RecordLogMessage("当前行有效区域为整行,检查视野和光源", 5);
- IsPrintLightOnError = true;
- }
- return false;
- }
- IsPrintLightOnError = false;
- }
- lock (_lockObj)
- {
- // 步骤2:处理当前行每个区域
- for (int i = 0; i < currentRegions.Count; i++)
- {
- var region = currentRegions[i];
- // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
- 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
- {
- 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)
- {
- // 合并区域:扩展物体边界并更新状态
- 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,
- });
- }
- else
- {
- // 创建新物体(首次出现的区域)
- 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,
- }
- }
- });
- }
- }
- currentRegions.Clear();
- // 更新有效物体的最后一行的起始点
- activeObjects.Where(o => o.LastSeenLine == currentLine).ToList().ForEach(o => o.LastSeenLineStartCol = o.RowsData.Where(p => p.RowsCol == currentLine).Min(p => p.StartCol));
- activeObjects.Where(o => o.LastSeenLine == currentLine).ToList().ForEach(o => o.LastSeenLineEndCol = o.RowsData.Where(p => p.RowsCol == currentLine).Max(p => p.EndCol));
- // 步骤3:清理超时未更新的物体
- var lostObjects = activeObjects
- .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
- .ToList();
- List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
- // 有物体转变为活跃物体,返回值转为true
- if (lostObjects.Count() > 0)
- {
- result = true;
- foreach (var item in lostObjects)
- {
- //噪点判定
- if (item.Area < shuLiConfig.NoiseFilter_Threshold)
- {
- item.StateCode = 9;
- continue;
- }
- //转为历史物体,添加缺少的参数
- item.Num = ObjectNum += 1;
- item.ChannelNO = ActiveChannel(item);
- item.EndCheckTime = DateTime.Now;
- OneActive.Add(item);
- 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)
- {
- if (item.StateCode != -1)
- {
- if (item.StateCode == 8)
- {
- LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
- }
- }
- else if (item.Area < shuLiConfig.MinArea
- && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
- {
- item.StateCode = 5;
- 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 = 6;
- 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 if (item.LastSeenLine - item.StartLine < shuLiConfig.MIN_OBJECT_HEIGHT
- // && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
- //{
- // item.StateCode = 2;
- // LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
- // Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
- //}
- //else if (item.LastSeenLine - item.StartLine > shuLiConfig.MAX_OBJECT_HEIGHT
- // && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
- //{
- // item.StateCode = 1;
- // LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
- // Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
- //}
- //else if (item.RowsData.Max(o => o.EndCol - o.StartCol) > shuLiConfig.MAX_OBJECT_WIDTH
- // && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
- //{
- // item.StateCode = 3;
- // LOG.log(string.Format("颗粒编号{0}:超宽粒", item.Num));
- // Console.WriteLine("颗粒编号{0}:超宽粒", item.Num);
- //}
- //else if (item.RowsData.Max(o => o.EndCol - o.StartCol) < shuLiConfig.MIN_OBJECT_WIDTH
- // && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
- //{
- // item.StateCode = 4;
- // 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 (OneActive.Count > 0)
- {
- //触发回调事件
- Task.Run(() =>
- {
- OnWorkCompleted(OneActive);
- });
- }
- }
- else
- {
- OneActive = null;
- }
- // 累加到总数并从活跃物体转移到历史物体
- lostObjects.Where(o => o.Area >= shuLiConfig.NoiseFilter_Threshold && o.StateCode != 7 && o.StateCode != 9).ToList().ForEach(o => TryAdd(historyActiveObjects, o, 2000));
- lostObjects.ForEach(o => activeObjects.Remove(o));
- }
- return result;
- }
- /// <summary>
- /// 检测有效物体区域(横向连续黑色像素段)
- /// </summary>
- /// <param name="line">当前行像素数组</param>
- /// <returns>有效区域列表(起始/结束位置)</returns>
- private List<(int Start, int End)> FindValidRegions(IImage image, int RowNo)
- {
- List<(int Start, int End)> regions = new List<(int Start, int End)>();
- int start = -1; // 当前区域起始标记
- // 遍历所有像素列
- if (shuLiConfig.IsIdentifyRoiOpen)
- {
- for (int i = (int)image.Width * RowNo + shuLiConfig.IdentifyStartX; i < (int)image.Width * RowNo + shuLiConfig.IdentifyStopX; i++)
- {
- if (image.PixelData[i] < shuLiConfig.RegionThreshold) // 发现黑色像素
- {
- if (start == -1) start = i % (int)image.Width; // 开始新区域
- }
- else if (start != -1) // 遇到白色像素且存在进行中的区域
- {
- // 检查区域宽度是否达标
- if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
- {
- regions.Add((start, (i - 1) % (int)image.Width)); // 记录有效区域
- }
- start = -1; // 重置区域标记
- }
- }
- }
- else
- {
- for (int i = (int)image.Width * RowNo; i < (int)image.Width * (RowNo + 1); i++)
- {
- if (image.PixelData[i] < shuLiConfig.RegionThreshold) // 发现黑色像素
- {
- if (start == -1) start = i % (int)image.Width; // 开始新区域
- }
- else if (start != -1) // 遇到白色像素且存在进行中的区域
- {
- // 检查区域宽度是否达标
- if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
- {
- regions.Add((start, (i - 1) % (int)image.Width)); // 记录有效区域
- }
- start = -1; // 重置区域标记
- }
- }
- }
- // 处理行尾未闭合的区域
- if (start != -1 && image.Width - start >= shuLiConfig.MIN_OBJECT_WIDTH)
- {
- regions.Add((start, (int)image.Width - 1));
- }
- return regions;
- }
- /// <summary>
- /// 判断区域重叠(与活跃物体的横向坐标重叠检测)
- /// </summary>
- /// <param name="obj">活跃物体</param>
- /// <param name="region">当前区域</param>
- /// <returns>是否发生重叠</returns>
- private bool IsOverlapping(ActiveObjectClass obj, (int Start, int End) region)
- {
- // 判断区域是否不相交的逆条件
- return !(region.End < obj.LastSeenLineStartCol || region.Start > obj.LastSeenLineEndCol);
- }
- /// <summary>
- /// 通道区域判定
- /// </summary>
- /// <param name="activeObject"></param>
- /// <returns></returns>
- private int ActiveChannel(ActiveObjectClass activeObject)
- {
- int result = -1;
- int StartChannel = activeObject.MinStartCol / ChannelWidth;
- int EndChannel = activeObject.MaxEndCol / ChannelWidth;
- if (StartChannel == EndChannel)
- {
- result = StartChannel;
- }
- else if (EndChannel - StartChannel > 1)
- {
- Console.WriteLine("ActiveChannel-Error");
- //error
- }
- else
- {
- result = _ChannelsRoi[StartChannel] - activeObject.MinStartCol > activeObject.MaxEndCol - _ChannelsRoi[StartChannel] ? StartChannel : EndChannel;
- }
- return result;
- }
- /// <summary>
- /// 获取结果最长长边
- /// </summary>
- /// <param name="Rows"></param>
- /// <returns></returns>
- private double GetActionMaxLength(List<RowStartEndCol> Rows)
- {
- List<Point> points = ConvexHull(Rows);
- return RotatingCalipers(points);
- }
- /// <summary>
- /// 凸包点集合获取
- /// </summary>
- /// <param name="Rows"></param>
- /// <returns></returns>
- private List<Point> ConvexHull(List<RowStartEndCol> Rows)
- {
- List<Point> points = Rows.Select(o => new Point(o.StartCol, (int)o.RowsCol)).ToList();
- points.AddRange(Rows.Select(o => new Point(o.EndCol, (int)o.RowsCol)).ToList());
- points = points.OrderBy(o => o.X).ThenBy(o => o.Y).ToList();
- var lower = new List<Point>();
- foreach (var p in points)
- {
- while (lower.Count >= 2 && Cross(lower[lower.Count - 2], lower[lower.Count - 1], p) <= 0)
- lower.RemoveAt(lower.Count - 1);
- lower.Add(p);
- }
- var upper = new List<Point>();
- for (int i = points.Count - 1; i >= 0; i--)
- {
- var p = points[i];
- while (upper.Count >= 2 && Cross(upper[upper.Count - 2], upper[upper.Count - 1], p) <= 0)
- upper.RemoveAt(upper.Count - 1);
- upper.Add(p);
- }
- lower.RemoveAt(lower.Count - 1);
- upper.RemoveAt(upper.Count - 1);
- lower.AddRange(upper);
- return lower;
- }
- /// <summary>
- /// 凸包最长边
- /// </summary>
- /// <param name="hull"></param>
- /// <returns></returns>
- private double RotatingCalipers(List<Point> hull)
- {
- int n = hull.Count;
- if (n == 1) return 0;
- if (n == 2) return Distance(hull[0], hull[1]);
- int k = 1;
- double maxDist = 0;
- for (int i = 0; i < n; i++)
- {
- while (Area2(hull[i], hull[(i + 1) % n], hull[(k + 1) % n]) >
- Area2(hull[i], hull[(i + 1) % n], hull[k]))
- {
- k = (k + 1) % n;
- }
- double currentDist = DistanceToLine(hull[i], hull[(i + 1) % n], hull[k]);
- maxDist = Math.Max(maxDist, currentDist);
- }
- return maxDist;
- }
- private double DistanceToLine(Point a, Point b, Point c)
- {
- double area = Math.Abs(Area2(a, b, c));
- double baseLength = Distance(a, b);
- return area / baseLength;
- }
- // 计算向量叉积
- private int Cross(Point o, Point a, Point b) =>
- (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X);
- // 计算三角形面积的两倍
- private int Area2(Point a, Point b, Point c) =>
- (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
- // 计算两点间距离
- private double Distance(Point a, Point b)
- {
- int dx = a.X - b.X;
- int dy = a.Y - b.Y;
- return Math.Sqrt(dx * dx + dy * dy);
- }
- private bool TryAdd(List<ActiveObjectClass> list, ActiveObjectClass item, int maxSize)
- {
- list.Add(item);
- if (list.Count > maxSize)
- {
- list[list.Count - 2000].RowsData.Clear();
- }
- return true;
- }
- #endregion
- #region 线程方法
- /// <summary>
- /// 识别图像线程
- /// </summary>
- private void IdentifyImageProcess()
- {
- //Stopwatch stopwatch = Stopwatch.StartNew();
- IImage IframeData = null;
- while (IsIdentify)
- {
- //判断队列中是否有数据
- if (IFrameDatas.Count() > 0)
- {
- //stopwatch.Restart();
- IFrameDatas.TryDequeue(out IframeData);
- //是否成功取得数据
- if (IframeData != null)
- {
- //识别
- ProcessImageSequence(IframeData);
- }
- else
- {
- Console.WriteLine("识别数据为空");
- continue;
- }
- //输出耗时
- //stopwatch.Stop();
- //Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed.ToString()},待识别队列剩余数量{IFrameDatas.Count()}");
- }
- else
- {
- Thread.Sleep(1);
- }
- }
- }
- #endregion
- }
- }
|