From ebcc1d53f14112363bbf539bcaf0202aadcdc9d7 Mon Sep 17 00:00:00 2001
From: C3032 <1057644574@qq.com>
Date: 星期一, 13 四月 2026 12:58:58 +0800
Subject: [PATCH] 2D取图计数功能完成

---
 LB_SmartVision/ProcessRun/ProcessRunBll.cs                                  |   44 +
 LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.cs          |  161 +++++
 LB_VisionProcesses/Cameras/CameraConfig.cs                                  |    5 
 LB_SmartVision/ProcessRun/ProcessContext.cs                                 |   80 ++
 LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcess.cs              |  413 ++++++++++++++
 LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.Designer.cs |  567 ++++++++++++++++++++
 LB_VisionProcesses/Processes/TyreCounter/TyreStatistics.cs                  |  266 +++++++++
 LB_SmartVision/CSV/CsvRecordProductData.cs                                  |   20 
 LB_SmartVision/SQL/RecordProductData.cs                                     |   32 +
 LB_VisionProcesses/Cameras/2DCameraForm.cs                                  |    2 
 LB_VisionProcesses/Cameras/ICamera.cs                                       |   88 +++
 11 files changed, 1,677 insertions(+), 1 deletions(-)

diff --git a/LB_SmartVision/CSV/CsvRecordProductData.cs b/LB_SmartVision/CSV/CsvRecordProductData.cs
index 6648f12..93e9875 100644
--- a/LB_SmartVision/CSV/CsvRecordProductData.cs
+++ b/LB_SmartVision/CSV/CsvRecordProductData.cs
@@ -47,6 +47,26 @@
         /// 妫�娴嬬浉鏈�
         /// </summary>
         public string CameraInspection { get; set; }
+
+        /// <summary>
+        /// 杞儙ID
+        /// </summary>
+        public int TyreID { get; set; }
+
+        /// <summary>
+        /// 鍥惧儚搴忓彿锛�1~n锛�
+        /// </summary>
+        public int ImageIndex { get; set; }
+
+        /// <summary>
+        /// 杞儙绾ф娴嬬粨鏋�
+        /// </summary>
+        public string TyreResult { get; set; }
+
+        /// <summary>
+        /// 姣忓紶杞儙鍥惧儚鏁�
+        /// </summary>
+        public int ImagesPerTyre { get; set; }
     }
 
 }
diff --git a/LB_SmartVision/ProcessRun/ProcessContext.cs b/LB_SmartVision/ProcessRun/ProcessContext.cs
index e46967c..c680c4f 100644
--- a/LB_SmartVision/ProcessRun/ProcessContext.cs
+++ b/LB_SmartVision/ProcessRun/ProcessContext.cs
@@ -6,6 +6,7 @@
 using LB_VisionProcesses.Alogrithms;
 using LB_VisionProcesses.Cameras;
 using LB_VisionProcesses.Communicators;
+using LB_VisionProcesses.Processes;
 using LB_VisionProcesses.Processes.ScriptTool;
 using OpenCvSharp;
 using System.Collections.Concurrent;
@@ -585,7 +586,86 @@
         [Node("Halcon2D鍥惧儚婊ゆ尝", "Halcon2D宸ュ叿", "Basic", "Halcon2D鍥惧儚婊ゆ尝")]
         public void Halcon2D鍥惧儚婊ゆ尝(FlowNode node) { RunNodeAsync(node); }
 
+        [Node("杞儙璁℃暟", "鎺у埗", "Logic", "杞儙璁℃暟鑺傜偣")]
+        public void 杞儙璁℃暟(FlowNode node)
+        {
+            string ProcessName = node.Text;
+            try
+            {
+                if (dicContext.TryGetValue(ProcessName, out IProcess obj)
+                    && obj is TyreCounterProcess counter)
+                {
+                    UpdateInputs(counter);
 
+                    if (node.Break)
+                    {
+                        node.BranchIndex = "0";
+                        node.Result = true;
+                        counter.Result = true;
+                        counter.Msg = "杞儙璁℃暟鑺傜偣宸茶烦杩�";
+                        return;
+                    }
+
+                    // 浠庡墠缃妭鐐硅幏鍙栨娴嬬粨鏋滐紙閫氳繃杈撳叆鏄犲皠锛�
+                    bool imageResult = GetPreviousResult(node);
+
+                    // 璁剧疆杈撳叆缁撴灉
+                    counter.InputImage = imageResult;
+
+                    // 杩愯杞儙璁℃暟
+                    if (!counter.Run())
+                    {
+                        node.Result = false;
+                        Result &= false;
+                        Msg += $"[{ProcessName}]{counter.Msg}";
+                    }
+                    else
+                    {
+                        node.Result = true;
+
+                        // 濡傛灉瀹屾垚涓�涓疆鑳庯紝杈撳嚭瀹屾垚淇″彿
+                        if (counter.IsTyreComplete)
+                        {
+                            Msg += $"[{ProcessName}]瀹屾垚杞儙 #{counter.CurrentTyreID - 1}锛岀粨鏋�: {(counter.TyreResult ? "OK" : "NG")}";
+                        }
+                    }
+                }
+                else
+                {
+                    node.Result = false;
+                    Result &= false;
+                    Msg += $"[{ProcessName}]鏈壘鍒拌疆鑳庤鏁板伐鍏峰疄渚�";
+                }
+            }
+            catch (Exception ex)
+            {
+                node.Result = false;
+                Result &= false;
+                Msg += $"[{ProcessName}]杩愯鍙戠敓鎰忓,{ex.Message}";
+            }
+        }
+
+        /// <summary>
+        /// 鑾峰彇鍓嶇疆鑺傜偣鐨勬娴嬬粨鏋�
+        /// </summary>
+        private bool GetPreviousResult(FlowNode node)
+        {
+            try
+            {
+                // 鏌ユ壘鍓嶇疆鑺傜偣
+                var prevNode = dicContext.Values
+                    .OfType<IProcess>()
+                    .FirstOrDefault(p => p.Result == true || p.Result == false);
+
+                if (prevNode != null)
+                {
+                    return prevNode.Result;
+                }
+            }
+            catch { }
+
+            return true; // 榛樿杩斿洖OK
+        }
 
         #endregion
 
diff --git a/LB_SmartVision/ProcessRun/ProcessRunBll.cs b/LB_SmartVision/ProcessRun/ProcessRunBll.cs
index 014464b..09c7798 100644
--- a/LB_SmartVision/ProcessRun/ProcessRunBll.cs
+++ b/LB_SmartVision/ProcessRun/ProcessRunBll.cs
@@ -875,9 +875,26 @@
         double total_OK;
         double total_NG;
 
+        /// <summary>
+        /// 杞儙绾х粺璁� - OK璁℃暟
+        /// </summary>
+        double tyre_OK;
+        /// <summary>
+        /// 杞儙绾х粺璁� - NG璁℃暟
+        /// </summary>
+        double tyre_NG;
+
         public double total
         {
             get { return total_OK + total_NG; }
+        }
+
+        /// <summary>
+        /// 杞儙鎬绘暟
+        /// </summary>
+        public double tyreTotal
+        {
+            get { return tyre_OK + tyre_NG; }
         }
 
         public double Rate_OK
@@ -890,10 +907,37 @@
             }
         }
 
+        /// <summary>
+        /// 杞儙鑹搧鐜�
+        /// </summary>
+        public double TyreRate_OK
+        {
+            get
+            {
+                if (tyreTotal == 0)
+                    return 100;
+                return (tyre_OK / tyreTotal) * 100;
+            }
+        }
+
         public void ClearTotal()
         {
             total_OK = 0;
             total_NG = 0;
+            tyre_OK = 0;
+            tyre_NG = 0;
+        }
+
+        /// <summary>
+        /// 娣诲姞杞儙缁熻
+        /// </summary>
+        /// <param name="isOK">杞儙鏄惁OK</param>
+        public void AddTyreResult(bool isOK)
+        {
+            if (isOK)
+                tyre_OK++;
+            else
+                tyre_NG++;
         }
 
         /// <summary>
diff --git a/LB_SmartVision/SQL/RecordProductData.cs b/LB_SmartVision/SQL/RecordProductData.cs
index 0020776..fbe0589 100644
--- a/LB_SmartVision/SQL/RecordProductData.cs
+++ b/LB_SmartVision/SQL/RecordProductData.cs
@@ -71,5 +71,37 @@
         [DisplayName("妫�娴嬬浉鏈�")]
         [Browsable(true)]
         public string CameraInspection { get; set; }
+
+        /// <summary>
+        /// 杞儙ID
+        /// </summary>
+        [Category("RecordProductData"), PropertyOrder(7)]
+        [DisplayName("杞儙ID")]
+        [Browsable(true)]
+        public int TyreID { get; set; }
+
+        /// <summary>
+        /// 鍥惧儚搴忓彿锛�1~n锛�
+        /// </summary>
+        [Category("RecordProductData"), PropertyOrder(8)]
+        [DisplayName("鍥惧儚搴忓彿")]
+        [Browsable(true)]
+        public int ImageIndex { get; set; }
+
+        /// <summary>
+        /// 杞儙绾ф娴嬬粨鏋�
+        /// </summary>
+        [Category("RecordProductData"), PropertyOrder(9)]
+        [DisplayName("杞儙缁撴灉")]
+        [Browsable(true)]
+        public string TyreResult { get; set; }
+
+        /// <summary>
+        /// 姣忓紶杞儙鍥惧儚鏁�
+        /// </summary>
+        [Category("RecordProductData"), PropertyOrder(10)]
+        [DisplayName("杞儙鍥惧儚鏁�")]
+        [Browsable(true)]
+        public int ImagesPerTyre { get; set; }
     }
 }
diff --git a/LB_VisionProcesses/Cameras/2DCameraForm.cs b/LB_VisionProcesses/Cameras/2DCameraForm.cs
index 5aabcf7..2409faf 100644
--- a/LB_VisionProcesses/Cameras/2DCameraForm.cs
+++ b/LB_VisionProcesses/Cameras/2DCameraForm.cs
@@ -78,7 +78,7 @@
 
         UserPictureBox onlinePictureBox { get; set; }
         private System.Windows.Forms.Timer updateTimer;
-        Total total = new Total { iImageCount = 0, iScanCount = 0 };
+        Total total = new Total { iImageCount = 0, iScanCount = 0, ImagesPerTyre = 1 };
         DateTime startGrabtime = DateTime.Now;
         
         // 鍔ㄦ�佹坊鍔犵殑澧炵泭涓嬫媺妗�
diff --git a/LB_VisionProcesses/Cameras/CameraConfig.cs b/LB_VisionProcesses/Cameras/CameraConfig.cs
index 4bec6f7..3b619c9 100644
--- a/LB_VisionProcesses/Cameras/CameraConfig.cs
+++ b/LB_VisionProcesses/Cameras/CameraConfig.cs
@@ -42,6 +42,11 @@
 
             Params.Inputs.Add("瓒呮椂鏃堕棿", 1000);
             Params.Inputs.Add("鏄惁澶辫触閲嶆柊鍙栧浘", false);
+
+            // 2D杞儙璁℃暟閰嶇疆锛堥粯璁や负1锛岃〃绀�3D妯″紡锛�2D鐩告満璁剧疆涓簄锛�
+            Params.Inputs.Add("ImagesPerTyre", 1);
+            Params.Inputs.Add("褰撳墠杞儙ID", 1);
+            Params.Inputs.Add("褰撳墠鍥惧儚搴忓彿", 0);
         }
 
         /// <summary>
diff --git a/LB_VisionProcesses/Cameras/ICamera.cs b/LB_VisionProcesses/Cameras/ICamera.cs
index 27c6c3a..ef64a79 100644
--- a/LB_VisionProcesses/Cameras/ICamera.cs
+++ b/LB_VisionProcesses/Cameras/ICamera.cs
@@ -8,8 +8,45 @@
 {
     public class Total
     {
+        /// <summary>
+        /// 鍥惧儚璁℃暟
+        /// </summary>
         public int iImageCount { get; set; } = 0;
+
+        /// <summary>
+        /// 鎵弿璁℃暟
+        /// </summary>
         public int iScanCount { get; set; } = 0;
+
+        /// <summary>
+        /// 瀹屾暣杞儙璁℃暟
+        /// </summary>
+        public int TyreCount { get; set; } = 0;
+
+        /// <summary>
+        /// 姣忓紶杞儙鎵�闇�鍥惧儚鏁帮紙2D鐩告満浣跨敤锛岄粯璁�1琛ㄧず3D鐩告満锛�
+        /// </summary>
+        public int ImagesPerTyre { get; set; } = 1;
+
+        /// <summary>
+        /// 褰撳墠杞儙鐨勫浘鍍忓簭鍙凤紙0~ImagesPerTyre-1锛�
+        /// </summary>
+        public int CurrentImageIndex { get; set; } = 0;
+
+        /// <summary>
+        /// 杞儙OK璁℃暟
+        /// </summary>
+        public int TyreOK { get; set; } = 0;
+
+        /// <summary>
+        /// 杞儙NG璁℃暟
+        /// </summary>
+        public int TyreNG { get; set; } = 0;
+
+        /// <summary>
+        /// 褰撳墠杞儙ID
+        /// </summary>
+        public int CurrentTyreID { get; set; } = 1;
 
         public Total() { }
 
@@ -19,10 +56,61 @@
             this.iScanCount = iScanCount;
         }
 
+        /// <summary>
+        /// 娣诲姞鍥惧儚骞舵洿鏂拌疆鑳庤鏁�
+        /// </summary>
+        /// <param name="imageResult">鍥惧儚妫�娴嬬粨鏋�</param>
+        /// <returns>鏄惁瀹屾垚涓�涓疆鑳�</returns>
+        public bool AddImageResult(bool imageResult)
+        {
+            iImageCount++;
+            CurrentImageIndex++;
+
+            // 妫�鏌ユ槸鍚﹀畬鎴愪竴涓疆鑳�
+            if (CurrentImageIndex >= ImagesPerTyre)
+            {
+                TyreCount++;
+                CurrentImageIndex = 0;
+
+                // 鏇存柊杞儙OK/NG璁℃暟锛坣寮犱腑浠讳竴NG鍒欐暣鑳嶯G锛�
+                if (imageResult)
+                    TyreOK++;
+                else
+                    TyreNG++;
+
+                CurrentTyreID++;
+                return true;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// 鑾峰彇杞儙鑹搧鐜�
+        /// </summary>
+        public double TyreRateOK => TyreCount > 0 ? (TyreOK / (double)TyreCount) * 100 : 0;
+
         public void Clear()
         {
             iImageCount = 0;
             iScanCount = 0;
+            TyreCount = 0;
+            CurrentImageIndex = 0;
+            TyreOK = 0;
+            TyreNG = 0;
+            CurrentTyreID = 1;
+        }
+
+        /// <summary>
+        /// 閲嶇疆杞儙璁℃暟锛堜繚鐣橧magesPerTyre閰嶇疆锛�
+        /// </summary>
+        public void ResetTyreCount()
+        {
+            TyreCount = 0;
+            CurrentImageIndex = 0;
+            TyreOK = 0;
+            TyreNG = 0;
+            CurrentTyreID = 1;
         }
     }
     public interface ICamera : IDisposable
diff --git a/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcess.cs b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcess.cs
new file mode 100644
index 0000000..3704425
--- /dev/null
+++ b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcess.cs
@@ -0,0 +1,413 @@
+using HalconDotNet;
+using LB_VisionProcesses.Alogrithms;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LB_VisionProcesses.Processes
+{
+    /// <summary>
+    /// 杞儙璁℃暟宸ュ叿 - 鐢ㄤ簬娴佺▼鑺傜偣涓粺璁¤疆鑳庢暟閲�
+    /// </summary>
+    [Process("杞儙璁℃暟", Category = "鎺у埗", Description = "鐢ㄤ簬2D宸ヤ綅杞儙璁℃暟锛岄厤缃畁寮犲浘/杞儙")]
+    public class TyreCounterProcess : BaseProcess
+    {
+        #region 閰嶇疆灞炴��
+
+        /// <summary>
+        /// 姣忓紶杞儙鎵�闇�鍥惧儚鏁�
+        /// </summary>
+        public int ImagesPerTyre
+        {
+            get
+            {
+                if (Params.Inputs.ContainsKey("ImagesPerTyre"))
+                    return Convert.ToInt32(Params.Inputs["ImagesPerTyre"]);
+                return 8;
+            }
+            set
+            {
+                Params.Inputs["ImagesPerTyre"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 鍒ゅ畾瑙勫垯
+        /// </summary>
+        public TyreJudgeRule JudgeRule
+        {
+            get
+            {
+                if (Params.Inputs.ContainsKey("JudgeRule"))
+                    return (TyreJudgeRule)Enum.Parse(typeof(TyreJudgeRule), Params.Inputs["JudgeRule"].ToString());
+                return TyreJudgeRule.AnyNG;
+            }
+            set
+            {
+                Params.Inputs["JudgeRule"] = value.ToString();
+            }
+        }
+
+        /// <summary>
+        /// 鏄惁鑷姩閲嶇疆锛堝畬鎴愪竴涓疆鑳庡悗鑷姩閲嶇疆璁℃暟锛�
+        /// </summary>
+        public bool AutoReset
+        {
+            get
+            {
+                if (Params.Inputs.ContainsKey("AutoReset"))
+                    return Convert.ToBoolean(Params.Inputs["AutoReset"]);
+                return true;
+            }
+            set
+            {
+                Params.Inputs["AutoReset"] = value;
+            }
+        }
+
+        #endregion
+
+        #region 杈撳嚭灞炴��
+
+        /// <summary>
+        /// 褰撳墠杞儙ID
+        /// </summary>
+        public int CurrentTyreID
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("CurrentTyreID"))
+                    return Convert.ToInt32(Params.Outputs["CurrentTyreID"]);
+                return 1;
+            }
+            private set
+            {
+                Params.Outputs["CurrentTyreID"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 褰撳墠鍥惧儚搴忓彿锛�1~n锛�
+        /// </summary>
+        public int CurrentImageIndex
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("CurrentImageIndex"))
+                    return Convert.ToInt32(Params.Outputs["CurrentImageIndex"]);
+                return 0;
+            }
+            private set
+            {
+                Params.Outputs["CurrentImageIndex"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 鏄惁瀹屾垚杞儙
+        /// </summary>
+        public bool IsTyreComplete
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("IsTyreComplete"))
+                    return Convert.ToBoolean(Params.Outputs["IsTyreComplete"]);
+                return false;
+            }
+            private set
+            {
+                Params.Outputs["IsTyreComplete"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 杞儙缁撴灉
+        /// </summary>
+        public bool TyreResult
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("TyreResult"))
+                    return Convert.ToBoolean(Params.Outputs["TyreResult"]);
+                return false;
+            }
+            private set
+            {
+                Params.Outputs["TyreResult"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 杞儙鎬绘暟
+        /// </summary>
+        public int TyreTotal
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("TyreTotal"))
+                    return Convert.ToInt32(Params.Outputs["TyreTotal"]);
+                return 0;
+            }
+            private set
+            {
+                Params.Outputs["TyreTotal"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 杞儙OK鏁�
+        /// </summary>
+        public int TyreOK
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("TyreOK"))
+                    return Convert.ToInt32(Params.Outputs["TyreOK"]);
+                return 0;
+            }
+            private set
+            {
+                Params.Outputs["TyreOK"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 杞儙NG鏁�
+        /// </summary>
+        public int TyreNG
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("TyreNG"))
+                    return Convert.ToInt32(Params.Outputs["TyreNG"]);
+                return 0;
+            }
+            private set
+            {
+                Params.Outputs["TyreNG"] = value;
+            }
+        }
+
+        /// <summary>
+        /// 鑹搧鐜�
+        /// </summary>
+        public double TyreRateOK
+        {
+            get
+            {
+                if (Params.Outputs.ContainsKey("TyreRateOK"))
+                    return Convert.ToDouble(Params.Outputs["TyreRateOK"]);
+                return 0;
+            }
+            private set
+            {
+                Params.Outputs["TyreRateOK"] = value;
+            }
+        }
+
+        #endregion
+
+        #region 绉佹湁瀛楁
+
+        /// <summary>
+        /// 杞儙缁熻鍣ㄥ疄渚�
+        /// </summary>
+        private TyreStatistics? _tyreStatistics;
+
+        /// <summary>
+        /// 杈撳叆鐨勫浘鍍忕粨鏋滐紙浠庡墠缃妭鐐硅幏鍙栵級
+        /// </summary>
+        private bool _inputImageResult = true;
+
+        #endregion
+
+        #region 鏋勯�犳柟娉�
+
+        public TyreCounterProcess()
+        {
+            strProcessClass = "LB_VisionProcesses.Processes.TyreCounterProcess";
+            _tyreStatistics = new TyreStatistics();
+            InitParams();
+        }
+
+        /// <summary>
+        /// 鍒濆鍖栧弬鏁�
+        /// </summary>
+        private void InitParams()
+        {
+            // 杈撳叆鍙傛暟
+            if (!Params.Inputs.ContainsKey("ImagesPerTyre"))
+                Params.Inputs.Add("ImagesPerTyre", 8);
+            if (!Params.Inputs.ContainsKey("JudgeRule"))
+                Params.Inputs.Add("JudgeRule", TyreJudgeRule.AnyNG.ToString());
+            if (!Params.Inputs.ContainsKey("AutoReset"))
+                Params.Inputs.Add("AutoReset", true);
+
+            // 杈撳嚭鍙傛暟
+            if (!Params.Outputs.ContainsKey("CurrentTyreID"))
+                Params.Outputs.Add("CurrentTyreID", 1);
+            if (!Params.Outputs.ContainsKey("CurrentImageIndex"))
+                Params.Outputs.Add("CurrentImageIndex", 0);
+            if (!Params.Outputs.ContainsKey("IsTyreComplete"))
+                Params.Outputs.Add("IsTyreComplete", false);
+            if (!Params.Outputs.ContainsKey("TyreResult"))
+                Params.Outputs.Add("TyreResult", false);
+            if (!Params.Outputs.ContainsKey("TyreTotal"))
+                Params.Outputs.Add("TyreTotal", 0);
+            if (!Params.Outputs.ContainsKey("TyreOK"))
+                Params.Outputs.Add("TyreOK", 0);
+            if (!Params.Outputs.ContainsKey("TyreNG"))
+                Params.Outputs.Add("TyreNG", 0);
+            if (!Params.Outputs.ContainsKey("TyreRateOK"))
+                Params.Outputs.Add("TyreRateOK", 0.0);
+        }
+
+        #endregion
+
+        #region 鏍稿績鏂规硶
+
+        /// <summary>
+        /// 杩愯杞儙璁℃暟閫昏緫
+        /// </summary>
+        public override bool Run()
+        {
+            DateTime startTime = DateTime.Now;
+
+            try
+            {
+                // 鑾峰彇杈撳叆缁撴灉锛堜粠鍓嶇疆鑺傜偣鐨凴esult鍙傛暟锛�
+                GetInputResult();
+
+                // 鏇存柊缁熻鍣ㄩ厤缃�
+                _tyreStatistics.ImagesPerTyre = ImagesPerTyre;
+                _tyreStatistics.JudgeRule = JudgeRule;
+
+                // 娣诲姞鍥惧儚缁撴灉
+                var result = _tyreStatistics.AddImageResult(_inputImageResult);
+
+                // 鏇存柊杈撳嚭鍙傛暟
+                UpdateOutputs(result);
+
+                // 璁剧疆缁撴灉
+                Result = true;
+                Msg = result.Message;
+
+                RunTime = (DateTime.Now - startTime).TotalMilliseconds;
+                return true;
+            }
+            catch (Exception ex)
+            {
+                Result = false;
+                Msg = $"杞儙璁℃暟杩愯澶辫触: {ex.Message}";
+                RunTime = (DateTime.Now - startTime).TotalMilliseconds;
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 鑾峰彇杈撳叆缁撴灉锛堜粠鍓嶇疆鑺傜偣浼犻�掔殑Result锛�
+        /// </summary>
+        private void GetInputResult()
+        {
+            // 濡傛灉InputImage鏄痓ool绫诲瀷锛岃鏄庡墠缃妭鐐逛紶閫掍簡缁撴灉
+            if (InputImage is bool boolResult)
+            {
+                _inputImageResult = boolResult;
+            }
+            else
+            {
+                // 榛樿璁や负OK
+                _inputImageResult = true;
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊杈撳嚭鍙傛暟
+        /// </summary>
+        private void UpdateOutputs(TyreCountResult result)
+        {
+            CurrentTyreID = result.TyreID > 0 ? result.TyreID : _tyreStatistics.CurrentTyreID;
+            CurrentImageIndex = result.CurrentImageIndex;
+            IsTyreComplete = result.IsTyreComplete;
+            TyreResult = result.TyreResult;
+            TyreTotal = _tyreStatistics.TyreTotal;
+            TyreOK = _tyreStatistics.TyreOK;
+            TyreNG = _tyreStatistics.TyreNG;
+            TyreRateOK = _tyreStatistics.TyreRateOK;
+        }
+
+        /// <summary>
+        /// 鍒濆鍖栬繍琛屽弬鏁�
+        /// </summary>
+        public override void InitRunParams()
+        {
+            base.InitRunParams();
+            _inputImageResult = true;
+        }
+
+        /// <summary>
+        /// 閲嶇疆杞儙璁℃暟
+        /// </summary>
+        public void ResetTyreCount()
+        {
+            _tyreStatistics?.Reset();
+            UpdateOutputs(new TyreCountResult());
+        }
+
+        /// <summary>
+        /// 鑾峰彇缁熻淇℃伅
+        /// </summary>
+        public string GetStatisticsInfo()
+        {
+            return _tyreStatistics?.GetStatisticsInfo() ?? "缁熻鍣ㄦ湭鍒濆鍖�";
+        }
+
+        /// <summary>
+        /// 鑾峰彇杩涘害淇℃伅
+        /// </summary>
+        public string GetProgressInfo()
+        {
+            return _tyreStatistics?.GetProgressInfo() ?? "缁熻鍣ㄦ湭鍒濆鍖�";
+        }
+
+        #endregion
+
+        #region 閲嶅啓鏂规硶
+
+        public override bool Load(string fullPath = null)
+        {
+            bool result = base.Load(fullPath);
+            if (result)
+            {
+                // 鍔犺浇閰嶇疆鍚庨噸鏂板垵濮嬪寲缁熻鍣�
+                _tyreStatistics = new TyreStatistics(ImagesPerTyre, JudgeRule);
+            }
+            return result;
+        }
+
+        public override bool Save(string filePath = null)
+        {
+            return base.Save(filePath);
+        }
+
+        public override object Clone()
+        {
+            TyreCounterProcess clone = (TyreCounterProcess)MemberwiseClone();
+            clone._tyreStatistics = new TyreStatistics(ImagesPerTyre, JudgeRule);
+            return clone;
+        }
+
+        public override void Dispose()
+        {
+            _tyreStatistics = null;
+            base.Dispose();
+        }
+
+        #endregion
+    }
+}
diff --git a/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.Designer.cs b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.Designer.cs
new file mode 100644
index 0000000..da63386
--- /dev/null
+++ b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.Designer.cs
@@ -0,0 +1,567 @@
+namespace LB_VisionProcesses.Processes
+{
+    partial class TyreCounterProcessEdit
+    {
+        /// <summary>
+        /// 蹇呴渶鐨勮璁″櫒鍙橀噺銆�
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// 娓呯悊鎵�鏈夋鍦ㄤ娇鐢ㄧ殑璧勬簮銆�
+        /// </summary>
+        /// <param name="disposing">濡傛灉搴旈噴鏀炬墭绠¤祫婧愶紝涓� true锛涘惁鍒欎负 false銆�</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region 缁勪欢璁捐鍣ㄧ敓鎴愮殑浠g爜
+
+        /// <summary>
+        /// 璁捐鍣ㄦ敮鎸佹墍闇�鐨勬柟娉� - 涓嶈淇敼
+        /// 浣跨敤浠g爜缂栬緫鍣ㄤ慨鏀规鏂规硶鐨勫唴瀹广��
+        /// </summary>
+        private void InitializeComponent()
+        {
+            splitContainer1 = new SplitContainer();
+            groupBoxInputs = new GroupBox();
+            dataGridInputs = new DataGridView();
+            groupBoxConfig = new GroupBox();
+            tableLayoutPanelConfig = new TableLayoutPanel();
+            lblImagesPerTyre = new Label();
+            numericUpDownImagesPerTyre = new NumericUpDown();
+            lblJudgeRule = new Label();
+            comboBoxJudgeRule = new ComboBox();
+            checkBoxAutoReset = new CheckBox();
+            groupBoxStatistics = new GroupBox();
+            tableLayoutPanelStats = new TableLayoutPanel();
+            lblCurrentTyreID = new Label();
+            txtCurrentTyreID = new TextBox();
+            lblCurrentImageIndex = new Label();
+            txtCurrentImageIndex = new TextBox();
+            lblTyreTotal = new Label();
+            txtTyreTotal = new TextBox();
+            lblTyreOK = new Label();
+            txtTyreOK = new TextBox();
+            lblTyreNG = new Label();
+            txtTyreNG = new TextBox();
+            lblTyreRateOK = new Label();
+            txtTyreRateOK = new TextBox();
+            groupBoxOutputs = new GroupBox();
+            dataGridOutputs = new DataGridView();
+            topToolStrip = new ToolStrip();
+            btnRun = new ToolStripButton();
+            btnSave = new ToolStripButton();
+            btnReset = new ToolStripButton();
+            statusStrip = new StatusStrip();
+            lblResult = new ToolStripStatusLabel();
+            lblMsg = new ToolStripStatusLabel();
+            ((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
+            splitContainer1.Panel1.SuspendLayout();
+            splitContainer1.Panel2.SuspendLayout();
+            splitContainer1.SuspendLayout();
+            groupBoxInputs.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)dataGridInputs).BeginInit();
+            groupBoxConfig.SuspendLayout();
+            tableLayoutPanelConfig.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)numericUpDownImagesPerTyre).BeginInit();
+            groupBoxStatistics.SuspendLayout();
+            tableLayoutPanelStats.SuspendLayout();
+            groupBoxOutputs.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)dataGridOutputs).BeginInit();
+            topToolStrip.SuspendLayout();
+            statusStrip.SuspendLayout();
+            SuspendLayout();
+            //
+            // splitContainer1
+            //
+            splitContainer1.BackColor = Color.FromArgb(32, 41, 50);
+            splitContainer1.Dock = DockStyle.Fill;
+            splitContainer1.Location = new Point(0, 31);
+            splitContainer1.Name = "splitContainer1";
+            //
+            // splitContainer1.Panel1
+            //
+            splitContainer1.Panel1.Controls.Add(groupBoxInputs);
+            //
+            // splitContainer1.Panel2
+            //
+            splitContainer1.Panel2.Controls.Add(groupBoxOutputs);
+            splitContainer1.Size = new Size(884, 458);
+            splitContainer1.SplitterDistance = 294;
+            splitContainer1.TabIndex = 0;
+            //
+            // groupBoxInputs
+            //
+            groupBoxInputs.BackColor = Color.FromArgb(32, 41, 50);
+            groupBoxInputs.Controls.Add(dataGridInputs);
+            groupBoxInputs.Dock = DockStyle.Fill;
+            groupBoxInputs.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            groupBoxInputs.ForeColor = SystemColors.Control;
+            groupBoxInputs.Location = new Point(0, 0);
+            groupBoxInputs.Name = "groupBoxInputs";
+            groupBoxInputs.Size = new Size(294, 458);
+            groupBoxInputs.TabIndex = 0;
+            groupBoxInputs.TabStop = false;
+            groupBoxInputs.Text = "杈撳叆鍙傛暟";
+            //
+            // dataGridInputs
+            //
+            dataGridInputs.BackgroundColor = Color.FromArgb(32, 46, 50);
+            dataGridInputs.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            dataGridInputs.Dock = DockStyle.Fill;
+            dataGridInputs.GridColor = SystemColors.Control;
+            dataGridInputs.Location = new Point(3, 24);
+            dataGridInputs.Name = "dataGridInputs";
+            dataGridInputs.Size = new Size(288, 431);
+            dataGridInputs.TabIndex = 0;
+            //
+            // groupBoxConfig
+            //
+            groupBoxConfig.BackColor = Color.FromArgb(32, 41, 50);
+            groupBoxConfig.Controls.Add(tableLayoutPanelConfig);
+            groupBoxConfig.Dock = DockStyle.Top;
+            groupBoxConfig.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            groupBoxConfig.ForeColor = SystemColors.Control;
+            groupBoxConfig.Location = new Point(0, 0);
+            groupBoxConfig.Name = "groupBoxConfig";
+            groupBoxConfig.Size = new Size(586, 180);
+            groupBoxConfig.TabIndex = 1;
+            groupBoxConfig.TabStop = false;
+            groupBoxConfig.Text = "閰嶇疆";
+            //
+            // tableLayoutPanelConfig
+            //
+            tableLayoutPanelConfig.ColumnCount = 2;
+            tableLayoutPanelConfig.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 150F));
+            tableLayoutPanelConfig.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+            tableLayoutPanelConfig.Controls.Add(lblImagesPerTyre, 0, 0);
+            tableLayoutPanelConfig.Controls.Add(numericUpDownImagesPerTyre, 1, 0);
+            tableLayoutPanelConfig.Controls.Add(lblJudgeRule, 0, 1);
+            tableLayoutPanelConfig.Controls.Add(comboBoxJudgeRule, 1, 1);
+            tableLayoutPanelConfig.Controls.Add(checkBoxAutoReset, 1, 2);
+            tableLayoutPanelConfig.Dock = DockStyle.Fill;
+            tableLayoutPanelConfig.Location = new Point(3, 24);
+            tableLayoutPanelConfig.Name = "tableLayoutPanelConfig";
+            tableLayoutPanelConfig.RowCount = 3;
+            tableLayoutPanelConfig.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F));
+            tableLayoutPanelConfig.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F));
+            tableLayoutPanelConfig.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F));
+            tableLayoutPanelConfig.Size = new Size(580, 153);
+            tableLayoutPanelConfig.TabIndex = 0;
+            //
+            // lblImagesPerTyre
+            //
+            lblImagesPerTyre.AutoSize = true;
+            lblImagesPerTyre.Dock = DockStyle.Fill;
+            lblImagesPerTyre.Location = new Point(3, 0);
+            lblImagesPerTyre.Name = "lblImagesPerTyre";
+            lblImagesPerTyre.Size = new Size(144, 40);
+            lblImagesPerTyre.TabIndex = 0;
+            lblImagesPerTyre.Text = "姣忓紶杞儙鍥惧儚鏁�:";
+            lblImagesPerTyre.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // numericUpDownImagesPerTyre
+            //
+            numericUpDownImagesPerTyre.Location = new Point(153, 3);
+            numericUpDownImagesPerTyre.Maximum = new decimal(new int[] { 64, 0, 0, 0 });
+            numericUpDownImagesPerTyre.Minimum = new decimal(new int[] { 1, 0, 0, 0 });
+            numericUpDownImagesPerTyre.Name = "numericUpDownImagesPerTyre";
+            numericUpDownImagesPerTyre.Size = new Size(100, 29);
+            numericUpDownImagesPerTyre.TabIndex = 1;
+            numericUpDownImagesPerTyre.Value = new decimal(new int[] { 8, 0, 0, 0 });
+            //
+            // lblJudgeRule
+            //
+            lblJudgeRule.AutoSize = true;
+            lblJudgeRule.Dock = DockStyle.Fill;
+            lblJudgeRule.Location = new Point(3, 40);
+            lblJudgeRule.Name = "lblJudgeRule";
+            lblJudgeRule.Size = new Size(144, 40);
+            lblJudgeRule.TabIndex = 2;
+            lblJudgeRule.Text = "鍒ゅ畾瑙勫垯:";
+            lblJudgeRule.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // comboBoxJudgeRule
+            //
+            comboBoxJudgeRule.DropDownStyle = ComboBoxStyle.DropDownList;
+            comboBoxJudgeRule.FormattingEnabled = true;
+            comboBoxJudgeRule.Items.AddRange(new object[] { "AnyNG", "AllOK", "MajorityVote" });
+            comboBoxJudgeRule.Location = new Point(153, 43);
+            comboBoxJudgeRule.Name = "comboBoxJudgeRule";
+            comboBoxJudgeRule.Size = new Size(160, 29);
+            comboBoxJudgeRule.TabIndex = 3;
+            //
+            // checkBoxAutoReset
+            //
+            checkBoxAutoReset.AutoSize = true;
+            checkBoxAutoReset.Checked = true;
+            checkBoxAutoReset.CheckState = CheckState.Checked;
+            checkBoxAutoReset.Location = new Point(153, 83);
+            checkBoxAutoReset.Name = "checkBoxAutoReset";
+            checkBoxAutoReset.Size = new Size(161, 25);
+            checkBoxAutoReset.TabIndex = 4;
+            checkBoxAutoReset.Text = "瀹屾垚杞儙鑷姩閲嶇疆";
+            checkBoxAutoReset.UseVisualStyleBackColor = true;
+            //
+            // groupBoxStatistics
+            //
+            groupBoxStatistics.BackColor = Color.FromArgb(32, 41, 50);
+            groupBoxStatistics.Controls.Add(tableLayoutPanelStats);
+            groupBoxStatistics.Dock = DockStyle.Fill;
+            groupBoxStatistics.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            groupBoxStatistics.ForeColor = SystemColors.Control;
+            groupBoxStatistics.Location = new Point(0, 180);
+            groupBoxStatistics.Name = "groupBoxStatistics";
+            groupBoxStatistics.Size = new Size(586, 278);
+            groupBoxStatistics.TabIndex = 2;
+            groupBoxStatistics.TabStop = false;
+            groupBoxStatistics.Text = "缁熻淇℃伅";
+            //
+            // tableLayoutPanelStats
+            //
+            tableLayoutPanelStats.ColumnCount = 2;
+            tableLayoutPanelStats.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 150F));
+            tableLayoutPanelStats.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+            tableLayoutPanelStats.Controls.Add(lblCurrentTyreID, 0, 0);
+            tableLayoutPanelStats.Controls.Add(txtCurrentTyreID, 1, 0);
+            tableLayoutPanelStats.Controls.Add(lblCurrentImageIndex, 0, 1);
+            tableLayoutPanelStats.Controls.Add(txtCurrentImageIndex, 1, 1);
+            tableLayoutPanelStats.Controls.Add(lblTyreTotal, 0, 2);
+            tableLayoutPanelStats.Controls.Add(txtTyreTotal, 1, 2);
+            tableLayoutPanelStats.Controls.Add(lblTyreOK, 0, 3);
+            tableLayoutPanelStats.Controls.Add(txtTyreOK, 1, 3);
+            tableLayoutPanelStats.Controls.Add(lblTyreNG, 0, 4);
+            tableLayoutPanelStats.Controls.Add(txtTyreNG, 1, 4);
+            tableLayoutPanelStats.Controls.Add(lblTyreRateOK, 0, 5);
+            tableLayoutPanelStats.Controls.Add(txtTyreRateOK, 1, 5);
+            tableLayoutPanelStats.Dock = DockStyle.Fill;
+            tableLayoutPanelStats.Location = new Point(3, 24);
+            tableLayoutPanelStats.Name = "tableLayoutPanelStats";
+            tableLayoutPanelStats.RowCount = 6;
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.RowStyles.Add(new RowStyle(SizeType.Absolute, 35F));
+            tableLayoutPanelStats.Size = new Size(580, 251);
+            tableLayoutPanelStats.TabIndex = 0;
+            //
+            // lblCurrentTyreID
+            //
+            lblCurrentTyreID.AutoSize = true;
+            lblCurrentTyreID.Dock = DockStyle.Fill;
+            lblCurrentTyreID.Location = new Point(3, 0);
+            lblCurrentTyreID.Name = "lblCurrentTyreID";
+            lblCurrentTyreID.Size = new Size(144, 35);
+            lblCurrentTyreID.TabIndex = 0;
+            lblCurrentTyreID.Text = "褰撳墠杞儙ID:";
+            lblCurrentTyreID.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtCurrentTyreID
+            //
+            txtCurrentTyreID.BackColor = Color.FromArgb(32, 46, 50);
+            txtCurrentTyreID.BorderStyle = BorderStyle.FixedSingle;
+            txtCurrentTyreID.ForeColor = SystemColors.Control;
+            txtCurrentTyreID.Location = new Point(153, 3);
+            txtCurrentTyreID.Name = "txtCurrentTyreID";
+            txtCurrentTyreID.ReadOnly = true;
+            txtCurrentTyreID.Size = new Size(100, 29);
+            txtCurrentTyreID.TabIndex = 1;
+            txtCurrentTyreID.Text = "1";
+            //
+            // lblCurrentImageIndex
+            //
+            lblCurrentImageIndex.AutoSize = true;
+            lblCurrentImageIndex.Dock = DockStyle.Fill;
+            lblCurrentImageIndex.Location = new Point(3, 35);
+            lblCurrentImageIndex.Name = "lblCurrentImageIndex";
+            lblCurrentImageIndex.Size = new Size(144, 35);
+            lblCurrentImageIndex.TabIndex = 2;
+            lblCurrentImageIndex.Text = "褰撳墠鍥惧儚搴忓彿:";
+            lblCurrentImageIndex.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtCurrentImageIndex
+            //
+            txtCurrentImageIndex.BackColor = Color.FromArgb(32, 46, 50);
+            txtCurrentImageIndex.BorderStyle = BorderStyle.FixedSingle;
+            txtCurrentImageIndex.ForeColor = SystemColors.Control;
+            txtCurrentImageIndex.Location = new Point(153, 38);
+            txtCurrentImageIndex.Name = "txtCurrentImageIndex";
+            txtCurrentImageIndex.ReadOnly = true;
+            txtCurrentImageIndex.Size = new Size(100, 29);
+            txtCurrentImageIndex.TabIndex = 3;
+            txtCurrentImageIndex.Text = "0/8";
+            //
+            // lblTyreTotal
+            //
+            lblTyreTotal.AutoSize = true;
+            lblTyreTotal.Dock = DockStyle.Fill;
+            lblTyreTotal.Location = new Point(3, 70);
+            lblTyreTotal.Name = "lblTyreTotal";
+            lblTyreTotal.Size = new Size(144, 35);
+            lblTyreTotal.TabIndex = 4;
+            lblTyreTotal.Text = "杞儙鎬绘暟:";
+            lblTyreTotal.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtTyreTotal
+            //
+            txtTyreTotal.BackColor = Color.FromArgb(32, 46, 50);
+            txtTyreTotal.BorderStyle = BorderStyle.FixedSingle;
+            txtTyreTotal.ForeColor = SystemColors.Control;
+            txtTyreTotal.Location = new Point(153, 73);
+            txtTyreTotal.Name = "txtTyreTotal";
+            txtTyreTotal.ReadOnly = true;
+            txtTyreTotal.Size = new Size(100, 29);
+            txtTyreTotal.TabIndex = 5;
+            txtTyreTotal.Text = "0";
+            //
+            // lblTyreOK
+            //
+            lblTyreOK.AutoSize = true;
+            lblTyreOK.Dock = DockStyle.Fill;
+            lblTyreOK.ForeColor = Color.LightGreen;
+            lblTyreOK.Location = new Point(3, 105);
+            lblTyreOK.Name = "lblTyreOK";
+            lblTyreOK.Size = new Size(144, 35);
+            lblTyreOK.TabIndex = 6;
+            lblTyreOK.Text = "杞儙OK:";
+            lblTyreOK.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtTyreOK
+            //
+            txtTyreOK.BackColor = Color.FromArgb(32, 46, 50);
+            txtTyreOK.BorderStyle = BorderStyle.FixedSingle;
+            txtTyreOK.ForeColor = Color.LightGreen;
+            txtTyreOK.Location = new Point(153, 108);
+            txtTyreOK.Name = "txtTyreOK";
+            txtTyreOK.ReadOnly = true;
+            txtTyreOK.Size = new Size(100, 29);
+            txtTyreOK.TabIndex = 7;
+            txtTyreOK.Text = "0";
+            //
+            // lblTyreNG
+            //
+            lblTyreNG.AutoSize = true;
+            lblTyreNG.Dock = DockStyle.Fill;
+            lblTyreNG.ForeColor = Color.LightCoral;
+            lblTyreNG.Location = new Point(3, 140);
+            lblTyreNG.Name = "lblTyreNG";
+            lblTyreNG.Size = new Size(144, 35);
+            lblTyreNG.TabIndex = 8;
+            lblTyreNG.Text = "杞儙NG:";
+            lblTyreNG.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtTyreNG
+            //
+            txtTyreNG.BackColor = Color.FromArgb(32, 46, 50);
+            txtTyreNG.BorderStyle = BorderStyle.FixedSingle;
+            txtTyreNG.ForeColor = Color.LightCoral;
+            txtTyreNG.Location = new Point(153, 143);
+            txtTyreNG.Name = "txtTyreNG";
+            txtTyreNG.ReadOnly = true;
+            txtTyreNG.Size = new Size(100, 29);
+            txtTyreNG.TabIndex = 9;
+            txtTyreNG.Text = "0";
+            //
+            // lblTyreRateOK
+            //
+            lblTyreRateOK.AutoSize = true;
+            lblTyreRateOK.Dock = DockStyle.Fill;
+            lblTyreRateOK.Location = new Point(3, 175);
+            lblTyreRateOK.Name = "lblTyreRateOK";
+            lblTyreRateOK.Size = new Size(144, 35);
+            lblTyreRateOK.TabIndex = 10;
+            lblTyreRateOK.Text = "鑹搧鐜�:";
+            lblTyreRateOK.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // txtTyreRateOK
+            //
+            txtTyreRateOK.BackColor = Color.FromArgb(32, 46, 50);
+            txtTyreRateOK.BorderStyle = BorderStyle.FixedSingle;
+            txtTyreRateOK.ForeColor = SystemColors.Control;
+            txtTyreRateOK.Location = new Point(153, 178);
+            txtTyreRateOK.Name = "txtTyreRateOK";
+            txtTyreRateOK.ReadOnly = true;
+            txtTyreRateOK.Size = new Size(100, 29);
+            txtTyreRateOK.TabIndex = 11;
+            txtTyreRateOK.Text = "0.00%";
+            //
+            // groupBoxOutputs
+            //
+            groupBoxOutputs.BackColor = Color.FromArgb(32, 41, 50);
+            groupBoxOutputs.Controls.Add(groupBoxStatistics);
+            groupBoxOutputs.Controls.Add(groupBoxConfig);
+            groupBoxOutputs.Dock = DockStyle.Fill;
+            groupBoxOutputs.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            groupBoxOutputs.ForeColor = SystemColors.Control;
+            groupBoxOutputs.Location = new Point(0, 0);
+            groupBoxOutputs.Name = "groupBoxOutputs";
+            groupBoxOutputs.Size = new Size(586, 458);
+            groupBoxOutputs.TabIndex = 0;
+            groupBoxOutputs.TabStop = false;
+            groupBoxOutputs.Text = "杈撳嚭涓庨厤缃�";
+            //
+            // dataGridOutputs
+            //
+            dataGridOutputs.BackgroundColor = Color.FromArgb(32, 46, 50);
+            dataGridOutputs.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            dataGridOutputs.Dock = DockStyle.Fill;
+            dataGridOutputs.GridColor = SystemColors.Control;
+            dataGridOutputs.Location = new Point(3, 24);
+            dataGridOutputs.Name = "dataGridOutputs";
+            dataGridOutputs.Size = new Size(580, 431);
+            dataGridOutputs.TabIndex = 0;
+            //
+            // topToolStrip
+            //
+            topToolStrip.BackColor = Color.FromArgb(32, 41, 50);
+            topToolStrip.GripStyle = ToolStripGripStyle.Hidden;
+            topToolStrip.Items.AddRange(new ToolStripItem[] { btnRun, btnSave, btnReset });
+            topToolStrip.Location = new Point(0, 0);
+            topToolStrip.Name = "topToolStrip";
+            topToolStrip.Size = new Size(884, 31);
+            topToolStrip.TabIndex = 1;
+            topToolStrip.Text = "toolStrip1";
+            //
+            // btnRun
+            //
+            btnRun.DisplayStyle = ToolStripItemDisplayStyle.Text;
+            btnRun.Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            btnRun.ForeColor = SystemColors.Control;
+            btnRun.ImageTransparentColor = Color.Magenta;
+            btnRun.Name = "btnRun";
+            btnRun.Size = new Size(41, 28);
+            btnRun.Text = "杩愯";
+            btnRun.Click += btnRun_Click;
+            //
+            // btnSave
+            //
+            btnSave.DisplayStyle = ToolStripItemDisplayStyle.Text;
+            btnSave.Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            btnSave.ForeColor = SystemColors.Control;
+            btnSave.ImageTransparentColor = Color.Magenta;
+            btnSave.Name = "btnSave";
+            btnSave.Size = new Size(41, 28);
+            btnSave.Text = "淇濆瓨";
+            btnSave.Click += btnSave_Click;
+            //
+            // btnReset
+            //
+            btnReset.DisplayStyle = ToolStripItemDisplayStyle.Text;
+            btnReset.Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            btnReset.ForeColor = SystemColors.Control;
+            btnReset.ImageTransparentColor = Color.Magenta;
+            btnReset.Name = "btnReset";
+            btnReset.Size = new Size(73, 28);
+            btnReset.Text = "閲嶇疆璁℃暟";
+            btnReset.Click += btnReset_Click;
+            //
+            // statusStrip
+            //
+            statusStrip.BackColor = Color.FromArgb(32, 41, 50);
+            statusStrip.Items.AddRange(new ToolStripItem[] { lblResult, lblMsg });
+            statusStrip.Location = new Point(0, 489);
+            statusStrip.Name = "statusStrip";
+            statusStrip.Size = new Size(884, 30);
+            statusStrip.TabIndex = 2;
+            statusStrip.Text = "statusStrip1";
+            //
+            // lblResult
+            //
+            lblResult.AutoSize = false;
+            lblResult.BackColor = Color.FromArgb(32, 41, 50);
+            lblResult.Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            lblResult.ForeColor = SystemColors.Control;
+            lblResult.Name = "lblResult";
+            lblResult.Size = new Size(100, 25);
+            lblResult.Text = "缁撴灉: --";
+            //
+            // lblMsg
+            //
+            lblMsg.BackColor = Color.FromArgb(32, 41, 50);
+            lblMsg.Font = new Font("Microsoft YaHei UI", 10F, FontStyle.Regular, GraphicsUnit.Point, 134);
+            lblMsg.ForeColor = SystemColors.Control;
+            lblMsg.Name = "lblMsg";
+            lblMsg.Size = new Size(769, 25);
+            lblMsg.Spring = true;
+            lblMsg.Text = "灏辩华";
+            lblMsg.TextAlign = ContentAlignment.MiddleLeft;
+            //
+            // TyreCounterProcessEdit
+            //
+            AutoScaleDimensions = new SizeF(9F, 20F);
+            AutoScaleMode = AutoScaleMode.Font;
+            BackColor = Color.FromArgb(32, 41, 50);
+            Controls.Add(splitContainer1);
+            Controls.Add(topToolStrip);
+            Controls.Add(statusStrip);
+            Name = "TyreCounterProcessEdit";
+            Size = new Size(884, 519);
+            Load += TyreCounterProcessEdit_Load;
+            splitContainer1.Panel1.ResumeLayout(false);
+            splitContainer1.Panel2.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)splitContainer1).EndInit();
+            splitContainer1.ResumeLayout(false);
+            groupBoxInputs.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)dataGridInputs).EndInit();
+            groupBoxConfig.ResumeLayout(false);
+            tableLayoutPanelConfig.ResumeLayout(false);
+            tableLayoutPanelConfig.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)numericUpDownImagesPerTyre).EndInit();
+            groupBoxStatistics.ResumeLayout(false);
+            tableLayoutPanelStats.ResumeLayout(false);
+            tableLayoutPanelStats.PerformLayout();
+            groupBoxOutputs.ResumeLayout(false);
+            ((System.ComponentModel.ISupportInitialize)dataGridOutputs).EndInit();
+            topToolStrip.ResumeLayout(false);
+            topToolStrip.PerformLayout();
+            statusStrip.ResumeLayout(false);
+            statusStrip.PerformLayout();
+            ResumeLayout(false);
+            statusStrip.PerformLayout();
+        }
+
+        #endregion
+
+        private SplitContainer splitContainer1;
+        private GroupBox groupBoxInputs;
+        private DataGridView dataGridInputs;
+        private GroupBox groupBoxOutputs;
+        private DataGridView dataGridOutputs;
+        private ToolStrip topToolStrip;
+        private ToolStripButton btnRun;
+        private ToolStripButton btnSave;
+        private ToolStripButton btnReset;
+        private StatusStrip statusStrip;
+        private ToolStripStatusLabel lblResult;
+        private ToolStripStatusLabel lblMsg;
+        private GroupBox groupBoxConfig;
+        private TableLayoutPanel tableLayoutPanelConfig;
+        private Label lblImagesPerTyre;
+        private NumericUpDown numericUpDownImagesPerTyre;
+        private Label lblJudgeRule;
+        private ComboBox comboBoxJudgeRule;
+        private CheckBox checkBoxAutoReset;
+        private GroupBox groupBoxStatistics;
+        private TableLayoutPanel tableLayoutPanelStats;
+        private Label lblCurrentTyreID;
+        private TextBox txtCurrentTyreID;
+        private Label lblCurrentImageIndex;
+        private TextBox txtCurrentImageIndex;
+        private Label lblTyreTotal;
+        private TextBox txtTyreTotal;
+        private Label lblTyreOK;
+        private TextBox txtTyreOK;
+        private Label lblTyreNG;
+        private TextBox txtTyreNG;
+        private Label lblTyreRateOK;
+        private TextBox txtTyreRateOK;
+    }
+}
diff --git a/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.cs b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.cs
new file mode 100644
index 0000000..e93c114
--- /dev/null
+++ b/LB_VisionProcesses/Processes/TyreCounter/TyreCounterProcessEdit.cs
@@ -0,0 +1,161 @@
+using LB_VisionProcesses.Alogrithms;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace LB_VisionProcesses.Processes
+{
+    /// <summary>
+    /// 杞儙璁℃暟宸ュ叿缂栬緫鍣�
+    /// </summary>
+    public partial class TyreCounterProcessEdit : TAlgorithmEdit
+    {
+        DataTable DataTableInputs = new DataTable();
+        DataTable DataTableOutputs = new DataTable();
+
+        public TyreCounterProcessEdit(TyreCounterProcess subject = null)
+        {
+            InitializeComponent();
+            if (subject == null)
+                subject = new TyreCounterProcess();
+
+            this.Subject = subject;
+        }
+
+        private void TyreCounterProcessEdit_Load(object sender, EventArgs e)
+        {
+            LoadParas();
+        }
+
+        /// <summary>
+        /// 鍔犺浇鍙傛暟
+        /// </summary>
+        public override void LoadParas()
+        {
+            if (Subject is TyreCounterProcess counter)
+            {
+                // 鍔犺浇閰嶇疆鍙傛暟
+                numericUpDownImagesPerTyre.Value = counter.ImagesPerTyre;
+                comboBoxJudgeRule.SelectedItem = counter.JudgeRule.ToString();
+                checkBoxAutoReset.Checked = counter.AutoReset;
+
+                // 鍔犺浇缁熻淇℃伅
+                UpdateStatistics();
+
+                // 缁戝畾杈撳叆鍙傛暟
+                DataTableInputs.Columns.Add("Key", typeof(string));
+                DataTableInputs.Columns.Add("Value", typeof(object));
+                DataTableInputs.Rows.Clear();
+
+                var inputs = new Dictionary<string, object>
+                {
+                    { "ImagesPerTyre", counter.ImagesPerTyre },
+                    { "JudgeRule", counter.JudgeRule.ToString() },
+                    { "AutoReset", counter.AutoReset }
+                };
+
+                foreach (var item in inputs)
+                    DataTableInputs.Rows.Add(item.Key, item.Value);
+
+                dataGridInputs.DataSource = DataTableInputs;
+
+                // 缁戝畾杈撳嚭鍙傛暟
+                DataTableOutputs.Columns.Add("Key", typeof(string));
+                DataTableOutputs.Columns.Add("Value", typeof(object));
+                DataTableOutputs.Rows.Clear();
+
+                var outputs = new Dictionary<string, object>
+                {
+                    { "CurrentTyreID", counter.CurrentTyreID },
+                    { "CurrentImageIndex", counter.CurrentImageIndex },
+                    { "IsTyreComplete", counter.IsTyreComplete },
+                    { "TyreResult", counter.TyreResult },
+                    { "TyreTotal", counter.TyreTotal },
+                    { "TyreOK", counter.TyreOK },
+                    { "TyreNG", counter.TyreNG },
+                    { "TyreRateOK", counter.TyreRateOK }
+                };
+
+                foreach (var item in outputs)
+                    DataTableOutputs.Rows.Add(item.Key, item.Value);
+
+                dataGridOutputs.DataSource = DataTableOutputs;
+
+                // 璁剧疆鐘舵�佹爮
+                lblResult.Text = $"缁撴灉: {(counter.Result ? "OK" : "NG")}";
+                lblResult.ForeColor = counter.Result ? Color.LightGreen : Color.LightCoral;
+                lblMsg.Text = counter.Msg;
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊缁熻淇℃伅鏄剧ず
+        /// </summary>
+        private void UpdateStatistics()
+        {
+            if (Subject is TyreCounterProcess counter)
+            {
+                txtCurrentTyreID.Text = counter.CurrentTyreID.ToString();
+                txtCurrentImageIndex.Text = $"{counter.CurrentImageIndex}/{counter.ImagesPerTyre}";
+                txtTyreTotal.Text = counter.TyreTotal.ToString();
+                txtTyreOK.Text = counter.TyreOK.ToString();
+                txtTyreNG.Text = counter.TyreNG.ToString();
+                txtTyreRateOK.Text = $"{counter.TyreRateOK:F2}%";
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊杈撳叆鍙傛暟
+        /// </summary>
+        public override void UpdataInputs()
+        {
+            if (Subject is TyreCounterProcess counter)
+            {
+                counter.ImagesPerTyre = (int)numericUpDownImagesPerTyre.Value;
+                counter.JudgeRule = (TyreJudgeRule)Enum.Parse(typeof(TyreJudgeRule), comboBoxJudgeRule.SelectedItem?.ToString() ?? "AnyNG");
+                counter.AutoReset = checkBoxAutoReset.Checked;
+
+                // 鏇存柊杈撳叆鍙傛暟琛�
+                Subject.Params.Inputs["ImagesPerTyre"] = counter.ImagesPerTyre;
+                Subject.Params.Inputs["JudgeRule"] = counter.JudgeRule.ToString();
+                Subject.Params.Inputs["AutoReset"] = counter.AutoReset;
+            }
+        }
+
+        private void btnSave_Click(object sender, EventArgs e)
+        {
+            UpdataInputs();
+            lblMsg.Text = "鍙傛暟宸蹭繚瀛�";
+            lblMsg.ForeColor = Color.LightGreen;
+        }
+
+        private void btnReset_Click(object sender, EventArgs e)
+        {
+            if (Subject is TyreCounterProcess counter)
+            {
+                counter.ResetTyreCount();
+                UpdateStatistics();
+                LoadParas();
+                lblMsg.Text = "璁℃暟鍣ㄥ凡閲嶇疆";
+                lblMsg.ForeColor = Color.LightGreen;
+            }
+        }
+
+        private void btnRun_Click(object sender, EventArgs e)
+        {
+            UpdataInputs();
+            Subject.Run();
+            UpdateStatistics();
+            LoadParas();
+            lblResult.Text = $"缁撴灉: {(Subject.Result ? "OK" : "NG")}";
+            lblResult.ForeColor = Subject.Result ? Color.LightGreen : Color.LightCoral;
+            lblMsg.Text = Subject.Msg;
+        }
+    }
+}
diff --git a/LB_VisionProcesses/Processes/TyreCounter/TyreStatistics.cs b/LB_VisionProcesses/Processes/TyreCounter/TyreStatistics.cs
new file mode 100644
index 0000000..6e1950c
--- /dev/null
+++ b/LB_VisionProcesses/Processes/TyreCounter/TyreStatistics.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LB_VisionProcesses.Processes
+{
+    /// <summary>
+    /// 杞儙鍒ゅ畾瑙勫垯
+    /// </summary>
+    public enum TyreJudgeRule
+    {
+        /// <summary>
+        /// 浠讳竴NG鍒欐暣鑳嶯G
+        /// </summary>
+        AnyNG,
+
+        /// <summary>
+        /// 鍏ㄩ儴OK鎵嶆暣鑳嶰K
+        /// </summary>
+        AllOK,
+
+        /// <summary>
+        /// 澶氭暟琛ㄥ喅锛堣秴杩囧崐鏁癘K鍒橭K锛�
+        /// </summary>
+        MajorityVote
+    }
+
+    /// <summary>
+    /// 杞儙璁℃暟缁撴灉
+    /// </summary>
+    public class TyreCountResult
+    {
+        /// <summary>
+        /// 鏄惁瀹屾垚涓�涓疆鑳�
+        /// </summary>
+        public bool IsTyreComplete { get; set; }
+
+        /// <summary>
+        /// 杞儙ID
+        /// </summary>
+        public int TyreID { get; set; }
+
+        /// <summary>
+        /// 杞儙鏁翠綋缁撴灉
+        /// </summary>
+        public bool TyreResult { get; set; }
+
+        /// <summary>
+        /// 鍚勫浘鍍忕粨鏋滃垪琛�
+        /// </summary>
+        public List<bool> ImageResults { get; set; } = new List<bool>();
+
+        /// <summary>
+        /// 褰撳墠鍥惧儚搴忓彿锛�1~n锛�
+        /// </summary>
+        public int CurrentImageIndex { get; set; }
+
+        /// <summary>
+        /// 娑堟伅
+        /// </summary>
+        public string Message { get; set; }
+    }
+
+    /// <summary>
+    /// 杞儙缁熻鍣� - 鐢ㄤ簬绠$悊杞儙绾у埆鐨勮鏁板拰缁熻
+    /// </summary>
+    public class TyreStatistics
+    {
+        #region 閰嶇疆鍙傛暟
+
+        /// <summary>
+        /// 姣忓紶杞儙鎵�闇�鍥惧儚鏁帮紙榛樿8寮狅級
+        /// </summary>
+        public int ImagesPerTyre { get; set; } = 8;
+
+        /// <summary>
+        /// 鍒ゅ畾瑙勫垯
+        /// </summary>
+        public TyreJudgeRule JudgeRule { get; set; } = TyreJudgeRule.AnyNG;
+
+        #endregion
+
+        #region 缁熻鏁版嵁
+
+        /// <summary>
+        /// 杞儙鎬绘暟
+        /// </summary>
+        public int TyreTotal => TyreOK + TyreNG;
+
+        /// <summary>
+        /// 杞儙OK璁℃暟
+        /// </summary>
+        public int TyreOK { get; set; }
+
+        /// <summary>
+        /// 杞儙NG璁℃暟
+        /// </summary>
+        public int TyreNG { get; set; }
+
+        /// <summary>
+        /// 褰撳墠杞儙ID锛堜粠1寮�濮嬶級
+        /// </summary>
+        public int CurrentTyreID { get; set; } = 1;
+
+        /// <summary>
+        /// 褰撳墠杞儙鐨勫浘鍍忓簭鍙凤紙0~ImagesPerTyre-1锛�
+        /// </summary>
+        public int CurrentImageIndex { get; set; } = 0;
+
+        /// <summary>
+        /// 褰撳墠杞儙鐨勫悇鍥惧儚缁撴灉
+        /// </summary>
+        public List<bool> CurrentTyreResults { get; set; } = new List<bool>();
+
+        /// <summary>
+        /// 褰撳墠杞儙鐨勫悇鍥惧儚SN
+        /// </summary>
+        public List<string> CurrentTyreImageSNs { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 杞儙鑹搧鐜�
+        /// </summary>
+        public double TyreRateOK => TyreTotal > 0 ? (TyreOK / (double)TyreTotal) * 100 : 0;
+
+        #endregion
+
+        #region 鏋勯�犳柟娉�
+
+        public TyreStatistics() { }
+
+        public TyreStatistics(int imagesPerTyre, TyreJudgeRule judgeRule = TyreJudgeRule.AnyNG)
+        {
+            ImagesPerTyre = imagesPerTyre;
+            JudgeRule = judgeRule;
+        }
+
+        #endregion
+
+        #region 鏍稿績鏂规硶
+
+        /// <summary>
+        /// 娣诲姞鍥惧儚妫�娴嬬粨鏋�
+        /// </summary>
+        /// <param name="isOK">鍥惧儚妫�娴嬬粨鏋�</param>
+        /// <param name="imageSN">鍥惧儚搴忓垪鍙凤紙鍙�夛級</param>
+        /// <returns>杞儙璁℃暟缁撴灉</returns>
+        public TyreCountResult AddImageResult(bool isOK, string imageSN = null)
+        {
+            // 娣诲姞鍒板綋鍓嶈疆鑳庣粨鏋滃垪琛�
+            CurrentTyreResults.Add(isOK);
+            CurrentTyreImageSNs.Add(imageSN);
+            CurrentImageIndex++;
+
+            var result = new TyreCountResult
+            {
+                CurrentImageIndex = CurrentImageIndex,
+                IsTyreComplete = false,
+                Message = $"褰撳墠杞儙绗� {CurrentImageIndex}/{ImagesPerTyre} 寮犲浘鍍�"
+            };
+
+            // 妫�鏌ユ槸鍚﹀畬鎴愪竴涓疆鑳�
+            if (CurrentImageIndex >= ImagesPerTyre)
+            {
+                // 璁$畻杞儙鏁翠綋缁撴灉
+                bool tyreResult = CalculateTyreResult();
+
+                // 鏇存柊缁熻
+                if (tyreResult)
+                    TyreOK++;
+                else
+                    TyreNG++;
+
+                // 鏋勫缓瀹屾垚缁撴灉
+                result.IsTyreComplete = true;
+                result.TyreID = CurrentTyreID;
+                result.TyreResult = tyreResult;
+                result.ImageResults = new List<bool>(CurrentTyreResults);
+                result.Message = $"瀹屾垚杞儙 #{CurrentTyreID}锛岀粨鏋�: {(tyreResult ? "OK" : "NG")}";
+
+                // 閲嶇疆褰撳墠杞儙鏁版嵁
+                ResetCurrentTyre();
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 璁$畻杞儙鏁翠綋缁撴灉
+        /// </summary>
+        private bool CalculateTyreResult()
+        {
+            if (CurrentTyreResults.Count == 0)
+                return true;
+
+            switch (JudgeRule)
+            {
+                case TyreJudgeRule.AnyNG:
+                    // n寮犱腑浠讳竴NG鍒欐暣鑳嶯G
+                    return !CurrentTyreResults.Any(r => !r);
+
+                case TyreJudgeRule.AllOK:
+                    // 鍏ㄩ儴OK鎵嶆暣鑳嶰K锛堜笌AnyNG鐩稿悓锛�
+                    return CurrentTyreResults.All(r => r);
+
+                case TyreJudgeRule.MajorityVote:
+                    // 澶氭暟琛ㄥ喅锛氳秴杩囧崐鏁癘K鍒橭K
+                    int okCount = CurrentTyreResults.Count(r => r);
+                    return okCount > (CurrentTyreResults.Count / 2.0);
+
+                default:
+                    return !CurrentTyreResults.Any(r => !r);
+            }
+        }
+
+        /// <summary>
+        /// 閲嶇疆褰撳墠杞儙鏁版嵁
+        /// </summary>
+        private void ResetCurrentTyre()
+        {
+            CurrentTyreID++;
+            CurrentImageIndex = 0;
+            CurrentTyreResults.Clear();
+            CurrentTyreImageSNs.Clear();
+        }
+
+        /// <summary>
+        /// 閲嶇疆鎵�鏈夌粺璁�
+        /// </summary>
+        public void Reset()
+        {
+            TyreOK = 0;
+            TyreNG = 0;
+            CurrentTyreID = 1;
+            ResetCurrentTyre();
+        }
+
+        /// <summary>
+        /// 娓呯┖褰撳墠杞儙锛堢敤浜庡紓甯告儏鍐碉級
+        /// </summary>
+        public void ClearCurrentTyre()
+        {
+            ResetCurrentTyre();
+        }
+
+        /// <summary>
+        /// 鑾峰彇褰撳墠杩涘害淇℃伅
+        /// </summary>
+        public string GetProgressInfo()
+        {
+            if (ImagesPerTyre <= 1)
+                return $"杞儙 #{CurrentTyreID} (3D妯″紡)";
+
+            return $"杞儙 #{CurrentTyreID} - 绗� {CurrentImageIndex}/{ImagesPerTyre} 寮�";
+        }
+
+        /// <summary>
+        /// 鑾峰彇缁熻淇℃伅瀛楃涓�
+        /// </summary>
+        public string GetStatisticsInfo()
+        {
+            return $"杞儙鎬绘暟: {TyreTotal}, OK: {TyreOK}, NG: {TyreNG}, 鑹搧鐜�: {TyreRateOK:F2}%";
+        }
+
+        #endregion
+    }
+}

--
Gitblit v1.9.3