ShuLiClass.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. using CCDCount.MODEL.ConfigModel;
  2. using CCDCount.MODEL.ShuLiClass;
  3. using LogClass;
  4. using MvCameraControl;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Threading;
  12. namespace CCDCount.DLL
  13. {
  14. public class ShuLiClass
  15. {
  16. #region 变量
  17. /// <summary>
  18. /// 当活跃物体转变为历史物体时的回调事件
  19. /// </summary>
  20. public event EventHandler<ActiveObjectEventArgsClass> WorkCompleted;
  21. private List<ActiveObjectClass> activeObjects = new List<ActiveObjectClass>(); // 当前跟踪中的物体
  22. private List<ActiveObjectClass> historyActiveObjects = new List<ActiveObjectClass>(); // 历史物体
  23. private ConcurrentQueue<IImage> IFrameDatas = new ConcurrentQueue<IImage>(); //图像数据队列
  24. private Thread IdentifyImageProcessThread = null; // 识别线程
  25. private bool IsIdentify = false; //线程是否开始识别的标志
  26. private long currentLine = 0; //行数记录
  27. private ShuLiConfigClass shuLiConfig = null;// 数粒参数配置文件
  28. public List<int> ChannelsRoi{get{ return _ChannelsRoi; }}
  29. private List<int> _ChannelsRoi = new List<int>();
  30. private int ChannelWidth = 0;//每个区域的宽度
  31. private int IdentifyImageWidth = -1;
  32. private static readonly object _lockObj = new object(); // 专用锁对象\
  33. private int ObjectNum = 0;
  34. public int ImageNum { get { return IFrameDatas.Count; } }
  35. #endregion
  36. #region 公共方法
  37. /// <summary>
  38. /// 初始化构造方法
  39. /// </summary>
  40. public ShuLiClass()
  41. {
  42. // 加载默认参数
  43. shuLiConfig = new ShuLiConfigClass()
  44. {
  45. Channel = 8,
  46. PandingCode = 2
  47. };
  48. }
  49. public ShuLiClass(ShuLiConfigClass config)
  50. {
  51. if(config.IsLoadCanfig)
  52. {
  53. // 加载传出的参数
  54. shuLiConfig = config;
  55. }
  56. else
  57. {
  58. // 加载默认参数
  59. shuLiConfig = new ShuLiConfigClass()
  60. {
  61. Channel = 8,
  62. PandingCode = 2
  63. };
  64. }
  65. InitChannel();
  66. }
  67. /// <summary>
  68. /// 处理图像序列的主入口
  69. /// </summary>
  70. /// <param name="image">图像像素数据</param>
  71. /// <param name="ImageWidth">图像宽</param>
  72. /// <param name="currentLine">当前行数</param>
  73. /// <returns>检测到的物体总数</returns>
  74. public bool ProcessImageSequence(IImage image)
  75. {
  76. bool result = false;
  77. for (int i = 0;i<image.Height;i++)
  78. {
  79. result = ProcessLine(image,i);
  80. currentLine += 1;
  81. }
  82. return result;
  83. }
  84. /// <summary>
  85. /// 返回最后一个历史物品
  86. /// </summary>
  87. /// <returns></returns>
  88. public ActiveObjectClass GetLastActive()
  89. {
  90. if (historyActiveObjects.Count() == 0)
  91. return null;
  92. return historyActiveObjects.Last();
  93. }
  94. /// <summary>
  95. /// 返回历史物品
  96. /// </summary>
  97. /// <returns></returns>
  98. public List<ActiveObjectClass> GetHistoryActive()
  99. {
  100. lock (_lockObj) // 加锁
  101. {
  102. return historyActiveObjects.ToList();
  103. }
  104. }
  105. /// <summary>
  106. /// 返回缓存在内存的历史物品的总数量
  107. /// </summary>
  108. /// <returns></returns>
  109. public int GetHistoryActiveNum()
  110. {
  111. lock (_lockObj) // 加锁
  112. return historyActiveObjects.Count();
  113. }
  114. /// <summary>
  115. /// 获取历史数据中,正常数据数量
  116. /// </summary>
  117. /// <returns></returns>
  118. public int GetOkHistoryNum()
  119. {
  120. lock (_lockObj)
  121. return historyActiveObjects.Where(o=>o.StateCode == 0).Count();
  122. }
  123. /// <summary>
  124. /// 获取历史数据中,异常数据数量
  125. /// </summary>
  126. /// <returns></returns>
  127. public int GetNgHistoryNum()
  128. {
  129. lock (_lockObj)
  130. return historyActiveObjects.Where(o=>o.StateCode != 0).Count();
  131. }
  132. /// <summary>
  133. /// 清除历史数据
  134. /// </summary>
  135. /// <returns></returns>
  136. public bool ClearHistoryActive()
  137. {
  138. lock (_lockObj)
  139. {
  140. historyActiveObjects.Clear();
  141. return true;
  142. }
  143. }
  144. /// <summary>
  145. /// 开启识别
  146. /// </summary>
  147. public void StartIdentifyFuntion(int ImaageWidth)
  148. {
  149. UpdateIdentifyImageWidth(ImaageWidth);
  150. InitChannel();
  151. try
  152. {
  153. // 标志位置位true
  154. IsIdentify = true;
  155. // 打开识别线程
  156. IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
  157. {
  158. Priority = ThreadPriority.Highest
  159. };
  160. IdentifyImageProcessThread.Start();
  161. }
  162. catch (Exception ex)
  163. {
  164. FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message);
  165. throw;
  166. }
  167. }
  168. /// <summary>
  169. /// 关闭识别
  170. /// </summary>
  171. public void StopIdentifyFuntion()
  172. {
  173. try
  174. {
  175. // 标志位设为false
  176. IsIdentify = false;
  177. if(IdentifyImageProcessThread!=null&& IdentifyImageProcessThread.IsAlive)
  178. IdentifyImageProcessThread.Join();
  179. }
  180. catch (Exception ex)
  181. {
  182. FaultLog.RecordErrorMessage("Stop thread failed!, " + ex.Message);
  183. throw;
  184. }
  185. }
  186. /// <summary>
  187. /// 向识别队列添加一个数据
  188. /// </summary>
  189. /// <param name="items"></param>
  190. public void SetOnceIdentifyImageData(IImage items)
  191. {
  192. IFrameDatas.Enqueue(items.Clone() as IImage);
  193. }
  194. /// <summary>
  195. /// 保存参数
  196. /// </summary>
  197. public void SaveConfig()
  198. {
  199. if(!Directory.Exists(".\\Config\\")) Directory.CreateDirectory(".\\Config\\");
  200. XmlStorage.SerializeToXml(shuLiConfig, ".\\Config\\ShuLiConfig.xml");
  201. }
  202. /// <summary>
  203. /// 更新检测宽度信息
  204. /// </summary>
  205. /// <param name="Width"></param>
  206. public void UpdateIdentifyImageWidth(int Width)
  207. {
  208. IdentifyImageWidth = Width;
  209. }
  210. /// <summary>
  211. /// 初始化通道划分
  212. /// </summary>
  213. /// <param name="ImageWidth"></param>
  214. public void InitChannel()
  215. {
  216. _ChannelsRoi.Clear();
  217. shuLiConfig.ImageWidth = IdentifyImageWidth==-1? shuLiConfig.ImageWidth: IdentifyImageWidth;
  218. if (shuLiConfig.Channel > 0)
  219. {
  220. if (shuLiConfig.IsIdentifyRoiOpen)
  221. {
  222. ChannelWidth = (shuLiConfig.IdentifyStopX - shuLiConfig.IdentifyStartX) / shuLiConfig.Channel;
  223. }
  224. else
  225. {
  226. ChannelWidth = shuLiConfig.ImageWidth / shuLiConfig.Channel;
  227. }
  228. for (int i = 0; i < shuLiConfig.Channel; i++)
  229. {
  230. _ChannelsRoi.Add(ChannelWidth + i * ChannelWidth);
  231. }
  232. }
  233. }
  234. /// <summary>
  235. /// 获取配置信息
  236. /// </summary>
  237. /// <returns></returns>
  238. public ShuLiConfigClass GetConfigValue()
  239. {
  240. ShuLiConfigClass result = shuLiConfig;
  241. return result;
  242. }
  243. public int GetConfigImageWidth()
  244. {
  245. int result = -1;
  246. if(shuLiConfig != null)
  247. result = shuLiConfig.ImageWidth;
  248. return result;
  249. }
  250. #endregion
  251. #region 私有方法
  252. /// <summary>
  253. /// 对外通知事件
  254. /// </summary>
  255. private void OnWorkCompleted(List<ActiveObjectClass> activeObject)
  256. {
  257. ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
  258. // 触发事件
  259. WorkCompleted?.Invoke(this, activeObjectEventArgs);
  260. }
  261. private bool IsPrintLightOnError = false;
  262. private bool IsObstructedViewError = false;
  263. /// <summary>
  264. /// 处理单行像素数据
  265. /// 返回值为false的时候无活跃物体转变为历史物体
  266. /// 返回值为true的时候有活跃物体转变为历史物体
  267. /// </summary>
  268. /// <param name="image">当前行像素数组</param>
  269. private bool ProcessLine(IImage imagedata,int RowNo)
  270. {
  271. bool result = false;
  272. // 步骤1:检测当前行的有效区域
  273. var currentRegions = FindValidRegions(imagedata,RowNo);
  274. if (currentRegions.Count == 1)
  275. {
  276. if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imagedata.Width)
  277. {
  278. if(!IsPrintLightOnError)
  279. {
  280. FaultLog.RecordLogMessage("当前行有效区域为整行,检查视野和光源", 5);
  281. IsPrintLightOnError = true;
  282. }
  283. return false;
  284. }
  285. IsPrintLightOnError = false;
  286. }
  287. // 步骤2:处理当前行每个区域
  288. for (int i = 0; i < currentRegions.Count; i++)
  289. {
  290. var region = currentRegions[i];
  291. // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
  292. var matcheds = activeObjects.Where(o =>
  293. IsOverlapping(o, region) &&
  294. (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP).ToList();
  295. //当有多个可合并的活跃物体时,将多个物体合并
  296. if (matcheds.Count >= 2)
  297. {
  298. // 合并有效区域队列
  299. var CopeRowsData = new List<RowStartEndCol>();
  300. matcheds.ForEach(o => CopeRowsData = CopeRowsData.Concat(o.RowsData).ToList());
  301. // 合并有效区域并保存在新的区域中
  302. var MergeMatched = new ActiveObjectClass
  303. {
  304. MinStartCol = matcheds.Min(o => o.MinStartCol),
  305. MaxEndCol = matcheds.Max(o => o.MaxEndCol),
  306. StartLine = matcheds.Min(o => o.StartLine),
  307. LastSeenLine = matcheds.Max(o => o.LastSeenLine),
  308. LastSeenLineStartCol = matcheds.Min(o => o.LastSeenLineStartCol),
  309. LastSeenLineEndCol = matcheds.Max(o => o.LastSeenLineEndCol),
  310. StartCheckTime = matcheds.Min(o => o.StartCheckTime),
  311. EndCheckTime = matcheds.Max(o => o.EndCheckTime),
  312. Area = matcheds.Sum(o => o.Area),
  313. RowsData = CopeRowsData,
  314. ImageWidth = matcheds.FirstOrDefault().ImageWidth,
  315. };
  316. // 从活跃区域中删除被合并的区域
  317. matcheds.ForEach(o => activeObjects.Remove(o));
  318. // 保存新的区域到活跃区域中
  319. activeObjects.Add(MergeMatched);
  320. }
  321. // 搜获可用且可合并的活跃区域
  322. var matched = activeObjects.FirstOrDefault(o =>
  323. IsOverlapping(o, region) &&
  324. (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP);
  325. if (matched != null)
  326. {
  327. // 合并区域:扩展物体边界并更新状态
  328. matched.MinStartCol = Math.Min(matched.MinStartCol, region.Start);
  329. matched.MaxEndCol = Math.Max(matched.MaxEndCol, region.End);
  330. matched.Area += region.End - region.Start + 1;
  331. matched.LastSeenLine = currentLine;
  332. matched.RowsData.Add(new RowStartEndCol
  333. {
  334. StartCol = region.Start,
  335. EndCol = region.End,
  336. RowsCol = currentLine,
  337. });
  338. }
  339. else
  340. {
  341. // 创建新物体(首次出现的区域)
  342. activeObjects.Add(new ActiveObjectClass
  343. {
  344. MinStartCol = region.Start,
  345. MaxEndCol = region.End,
  346. StartLine = currentLine,
  347. LastSeenLine = currentLine,
  348. LastSeenLineStartCol = region.Start,
  349. LastSeenLineEndCol = region.End,
  350. StartCheckTime = DateTime.Now,
  351. Area = region.End - region.Start + 1,
  352. ImageWidth = IdentifyImageWidth,
  353. RowsData = new List<RowStartEndCol> {
  354. new RowStartEndCol {
  355. StartCol = region.Start,
  356. EndCol = region.End,
  357. RowsCol = currentLine,
  358. }
  359. }
  360. });
  361. }
  362. }
  363. currentRegions.Clear();
  364. // 更新有效物体的最后一行的起始点
  365. activeObjects.Where(o => o.LastSeenLine == currentLine).ToList().ForEach(o => o.LastSeenLineStartCol = o.RowsData.Where(p => p.RowsCol == currentLine).Min(p => p.StartCol));
  366. activeObjects.Where(o => o.LastSeenLine == currentLine).ToList().ForEach(o => o.LastSeenLineEndCol = o.RowsData.Where(p => p.RowsCol == currentLine).Max(p => p.EndCol));
  367. // 步骤3:清理超时未更新的物体
  368. var lostObjects = activeObjects
  369. .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
  370. .ToList();
  371. List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
  372. // 有物体转变为活跃物体,返回值转为true
  373. if (lostObjects.Count() > 0)
  374. {
  375. result = true;
  376. foreach (var item in lostObjects)
  377. {
  378. //噪点判定
  379. if (item.LastSeenLine - item.StartLine < shuLiConfig.NoiseFilter_Threshold ||
  380. item.RowsData.Max(o => o.EndCol - o.StartCol) < shuLiConfig.NoiseFilter_Threshold)
  381. continue;
  382. //转为历史物体,添加缺少的参数
  383. item.Num = ObjectNum += 1;
  384. item.ChannelNO = ActiveChannel(item);
  385. item.EndCheckTime = DateTime.Now;
  386. OneActive.Add(item);
  387. if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)
  388. {
  389. item.StateCode = 7;
  390. FaultLog.RecordLogMessage("ShuLiClass-ProcessLine:非颗粒,视野异常",3);
  391. LOG.log(string.Format("ShuLiClass-ProcessLine:非颗粒,视野异常"), 6);
  392. Console.WriteLine("ShuLiClass-ProcessLine:非颗粒,视野异常");
  393. }
  394. else if (shuLiConfig.PandingCode != -1)
  395. {
  396. if (item.Area < shuLiConfig.MinArea
  397. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
  398. {
  399. item.StateCode = 5;
  400. LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
  401. Console.WriteLine("颗粒编号{0}:面积过小", item.Num);
  402. }
  403. else if (item.Area > shuLiConfig.MaxArea
  404. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
  405. {
  406. item.StateCode = 6;
  407. LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
  408. Console.WriteLine("颗粒编号{0}:面积过大", item.Num);
  409. }
  410. else if (item.LastSeenLine - item.StartLine < shuLiConfig.MIN_OBJECT_HEIGHT
  411. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
  412. {
  413. item.StateCode = 2;
  414. LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
  415. Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
  416. }
  417. else if (item.LastSeenLine - item.StartLine > shuLiConfig.MAX_OBJECT_HEIGHT
  418. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
  419. {
  420. item.StateCode = 1;
  421. LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
  422. Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
  423. }
  424. else if (item.RowsData.Max(o => o.EndCol - o.StartCol) > shuLiConfig.MAX_OBJECT_WIDTH
  425. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
  426. {
  427. item.StateCode = 3;
  428. LOG.log(string.Format("颗粒编号{0}:超宽粒", item.Num));
  429. Console.WriteLine("颗粒编号{0}:超宽粒", item.Num);
  430. }
  431. else if (item.RowsData.Max(o => o.EndCol - o.StartCol) < shuLiConfig.MIN_OBJECT_WIDTH
  432. && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
  433. {
  434. item.StateCode = 4;
  435. LOG.log(string.Format("颗粒编号{0}:超窄粒", item.Num));
  436. Console.WriteLine("颗粒编号{0}:超窄粒", item.Num);
  437. }
  438. else
  439. {
  440. item.StateCode = 0;
  441. LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
  442. Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
  443. }
  444. }
  445. }
  446. if (OneActive.Count > 0)
  447. //触发回调事件
  448. OnWorkCompleted(OneActive);
  449. }
  450. else
  451. {
  452. OneActive = null;
  453. }
  454. lock (_lockObj)
  455. {
  456. // 累加到总数并从活跃物体转移到历史物体
  457. lostObjects.Where(o => o.LastSeenLine - o.StartLine >= shuLiConfig.NoiseFilter_Threshold && o.StateCode != 7).ToList().ForEach(o => historyActiveObjects.Add(o));
  458. lostObjects.ForEach(o => activeObjects.Remove(o));
  459. lostObjects.ForEach(o => historyActiveObjects.Where(P => P.Num == o.Num - 100).ToList().ForEach(P => P.RowsData.Clear()));
  460. }
  461. return result;
  462. }
  463. /// <summary>
  464. /// 检测有效物体区域(横向连续黑色像素段)
  465. /// </summary>
  466. /// <param name="line">当前行像素数组</param>
  467. /// <returns>有效区域列表(起始/结束位置)</returns>
  468. private List<(int Start, int End)> FindValidRegions(IImage image,int RowNo)
  469. {
  470. List<(int Start, int End)> regions = new List<(int Start, int End)>();
  471. int start = -1; // 当前区域起始标记
  472. // 遍历所有像素列
  473. if (shuLiConfig.IsIdentifyRoiOpen)
  474. {
  475. for (int i = (int)image.Width*RowNo + shuLiConfig.IdentifyStartX; i < (int)image.Width * RowNo + shuLiConfig.IdentifyStopX; i++)
  476. {
  477. if (image.PixelData[i] < shuLiConfig.RegionThreshold) // 发现黑色像素
  478. {
  479. if (start == -1) start = i%(int)image.Width; // 开始新区域
  480. }
  481. else if (start != -1) // 遇到白色像素且存在进行中的区域
  482. {
  483. // 检查区域宽度是否达标
  484. if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
  485. {
  486. regions.Add((start, (i - 1)% (int)image.Width)); // 记录有效区域
  487. }
  488. start = -1; // 重置区域标记
  489. }
  490. }
  491. }
  492. else
  493. {
  494. for (int i = (int)image.Width * RowNo; i < (int)image.Width * (RowNo+1); i++)
  495. {
  496. if (image.PixelData[i] < shuLiConfig.RegionThreshold) // 发现黑色像素
  497. {
  498. if (start == -1) start = i % (int)image.Width; // 开始新区域
  499. }
  500. else if (start != -1) // 遇到白色像素且存在进行中的区域
  501. {
  502. // 检查区域宽度是否达标
  503. if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
  504. {
  505. regions.Add((start, (i - 1) % (int)image.Width)); // 记录有效区域
  506. }
  507. start = -1; // 重置区域标记
  508. }
  509. }
  510. }
  511. // 处理行尾未闭合的区域
  512. if (start != -1 && image.Width - start >= shuLiConfig.MIN_OBJECT_WIDTH)
  513. {
  514. regions.Add((start, (int)image.Width - 1));
  515. }
  516. return regions;
  517. }
  518. /// <summary>
  519. /// 判断区域重叠(与活跃物体的横向坐标重叠检测)
  520. /// </summary>
  521. /// <param name="obj">活跃物体</param>
  522. /// <param name="region">当前区域</param>
  523. /// <returns>是否发生重叠</returns>
  524. private bool IsOverlapping(ActiveObjectClass obj, (int Start, int End) region)
  525. {
  526. // 判断区域是否不相交的逆条件
  527. return !(region.End < obj.LastSeenLineStartCol || region.Start > obj.LastSeenLineEndCol);
  528. }
  529. /// <summary>
  530. /// 通道区域判定
  531. /// </summary>
  532. /// <param name="activeObject"></param>
  533. /// <returns></returns>
  534. private int ActiveChannel(ActiveObjectClass activeObject)
  535. {
  536. int result = -1;
  537. int StartChannel = activeObject.MinStartCol / ChannelWidth;
  538. int EndChannel = activeObject.MaxEndCol / ChannelWidth;
  539. if (StartChannel == EndChannel)
  540. {
  541. result = StartChannel;
  542. }
  543. else if (EndChannel - StartChannel>1)
  544. {
  545. Console.WriteLine("ActiveChannel-Error");
  546. //error
  547. }
  548. else
  549. {
  550. result = _ChannelsRoi[StartChannel] - activeObject.MinStartCol > activeObject.MaxEndCol - _ChannelsRoi[StartChannel]? StartChannel: EndChannel;
  551. }
  552. return result;
  553. }
  554. #endregion
  555. #region 线程方法
  556. /// <summary>
  557. /// 识别图像线程
  558. /// </summary>
  559. private void IdentifyImageProcess()
  560. {
  561. //Stopwatch stopwatch = Stopwatch.StartNew();
  562. IImage IframeData = null;
  563. while (IsIdentify)
  564. {
  565. //判断队列中是否有数据
  566. if (IFrameDatas.Count() > 0)
  567. {
  568. //stopwatch.Restart();
  569. IFrameDatas.TryDequeue(out IframeData);
  570. //是否成功取得数据
  571. if (IframeData != null)
  572. {
  573. //识别
  574. ProcessImageSequence(IframeData);
  575. }
  576. else
  577. {
  578. Console.WriteLine("识别数据为空");
  579. continue;
  580. }
  581. //输出耗时
  582. //stopwatch.Stop();
  583. ///Console.WriteLine("识别线程单次运行耗时:" + stopwatch.Elapsed.ToString());
  584. }
  585. else
  586. {
  587. Thread.Sleep(1);
  588. }
  589. }
  590. }
  591. #endregion
  592. }
  593. }