소스 검색

20251031001 历史数据界面优化,最小外接矩形算法实现

向羽 孟 6 달 전
부모
커밋
e73aeb3c66

+ 2 - 2
CCDCountWpf/WpfPage/AuditTrailPage.xaml

@@ -26,7 +26,7 @@
             <Button x:Name="ErrorRecordBtn" Content="异常记录" Grid.Column="3" Click="ErrorRecordBtn_Click"></Button>
         </Grid>
         <Grid Grid.Row="1">
-            <Grid x:Name="BatchRecordGrid" Visibility="Collapsed">
+            <Grid x:Name="BatchRecordGrid" Visibility="Visible">
                 <Grid.RowDefinitions>
                     <RowDefinition Height="50" />
                     <RowDefinition Height="*" />
@@ -63,7 +63,7 @@
                 <ComboBox Grid.Row="0" x:Name="ValueChangeDateComBox" ItemsSource="{Binding ValueChangeItems}" MaxDropDownHeight="100" Background="{x:Null}" HorizontalAlignment="Right" VerticalAlignment="Center" Height="30" Width="180" FontSize="12" HorizontalContentAlignment="Center" Padding="6,6,5,3" Margin="0,0,10,0" SelectionChanged="ValueChangeRecordComBox_SelectionChanged"/>
                 <wpf:WebView2 x:Name="ValueChangeRecordWebBrowser" Grid.Row="1" />
             </Grid>
-            <Grid x:Name="ErrorRecordGrid" Visibility="Visible">
+            <Grid x:Name="ErrorRecordGrid" Visibility="Collapsed">
                 <Grid.RowDefinitions>
                     <RowDefinition Height="50" />
                     <RowDefinition Height="*" />

+ 49 - 13
CCDCountWpf/WpfPage/HistoryDataPage.xaml

@@ -10,25 +10,61 @@
 
     <Grid>
         <Grid.RowDefinitions>
+            <RowDefinition Height="60"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="60"/>
         </Grid.RowDefinitions>
-        <Border Grid.Row="0" BorderBrush="Black" BorderThickness="1" Margin="2,2,2,2">
+        <Grid Grid.Row="0">
+            <Label Content="日期筛选:" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0" />
+            <DatePicker x:Name="BatchNumRecordMinTime" Width="120" Height="25"  HorizontalAlignment="Left" VerticalAlignment="Center" SelectedDateChanged="BatchNumRecordMinTime_SelectedDateChanged" Margin="65,0,0,0"/>
+            <Label Content="-" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="190,0,0,0" />
+            <DatePicker x:Name="BatchNumRecordMaxTime" Width="120" Height="25"  HorizontalAlignment="Left" Margin="210,0,0,0" VerticalAlignment="Center" SelectedDateChanged="BatchNumRecordMaxTime_SelectedDateChanged"/>
+            <Label Content="批次选择:" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,200,0" />
+            <ComboBox Grid.Row="0" x:Name="BatchNumComBox" ItemsSource="{Binding BatchItems}" MaxDropDownHeight="100" Background="{x:Null}" HorizontalAlignment="Right" VerticalAlignment="Center" Height="30" Width="180" FontSize="12" HorizontalContentAlignment="Center" Padding="6,6,5,3" Margin="0,0,10,0" SelectionChanged="BatchNumRecordComBox_SelectionChanged"/>
+        </Grid>
+        <Border Grid.Row="1" BorderBrush="Black" BorderThickness="1" Margin="2,2,2,2">
             <Image x:Name="ShowBox" Source="{Binding HistoryImage}" Margin="0,0,0,0"/>
         </Border>
-        <Border Grid.Row="1" Margin="2,2,2,2" VerticalAlignment="Center" HorizontalAlignment="Center">
+        <Border x:Name="PageChangeBorder" Grid.Row="2" Margin="2,2,2,2" VerticalAlignment="Center" HorizontalAlignment="Center" Background="#FF0087FF" 
+                    CornerRadius="8"
+                    BorderBrush="Gray"
+                    BorderThickness="1">
             <StackPanel Orientation="Horizontal">
-                <Button x:Name="PreviousBtn" Content="上一张" Height="40" Width="100" Margin="0,0,30,0" Click="PreviousBtn_Click"/>
-                <Label Content="第" FontSize="20" />
-                <Label Content="{Binding HistoryImageNum}" FontSize="20" />
-                <Label Content="张/共" FontSize="20" />
-                <Label Content="{Binding HistoryImageCount}" FontSize="20" />
-                <Label Content="张" FontSize="20" Margin="0,0,20,0" />
-                <Label Content="跳转至" FontSize="20" />
-                <TextBox x:Name="NowPageNumTbx" Text="{Binding HistoryImageNum}"  Height="40" Width="50" FontSize="20" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
-                <Label Content="张" FontSize="20" Margin="0,0,5,0" />
-                <Button x:Name="SkipBtn" Content="跳转" Height="30" Width="50" Margin="0,0,0,0" Click="SkipBtn_Click" />
-                <Button x:Name="NextBtn" Content="下一张" Height="40" Width="100" Margin="30,0,0,0" Click="NextBtn_Click" />
+                <Button x:Name="PreviousBtn" Content="上一张" Height="40" Width="100" Margin="30,5,30,5" Click="PreviousBtn_Click" Padding="1,1,1,1" VerticalAlignment="Center" BorderThickness="1" Background="#FF0123FF" Foreground="White" Cursor="Hand">
+                    <Button.Template>
+                        <ControlTemplate TargetType="Button">
+                            <Border CornerRadius="5"  Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"  BorderThickness="{TemplateBinding BorderThickness}">
+                                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            </Border>
+                        </ControlTemplate>
+                    </Button.Template>
+                </Button>              
+                <Label Content="第" FontSize="20" VerticalAlignment="Center" Foreground="White" />
+                <Label Content="{Binding HistoryImageNum}" FontSize="20" VerticalAlignment="Center" Foreground="White" />
+                <Label Content="张/共" FontSize="20" VerticalAlignment="Center" Foreground="White" />
+                <Label Content="{Binding HistoryImageCount}" FontSize="20" VerticalAlignment="Center" Foreground="White" />
+                <Label Content="张" FontSize="20" Margin="0,0,20,0" VerticalAlignment="Center" Foreground="White" />
+                <Label Content="跳转至" FontSize="20" VerticalAlignment="Center" Foreground="White" />
+                <TextBox x:Name="NowPageNumTbx" Text="{Binding HistoryImageNum}"  Height="40" Width="50" FontSize="20" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Center" Background="{x:Null}" Foreground="White" />
+                <Label Content="张" FontSize="20" Margin="0,0,5,0" VerticalAlignment="Center" Foreground="White" />
+                <Button x:Name="SkipBtn" Content="跳转" Height="30" Width="50" Margin="0,0,0,0" Click="SkipBtn_Click" VerticalAlignment="Center" BorderThickness="1" Background="#FF0123FF" Foreground="White" Cursor="Hand">
+                    <Button.Template>
+                        <ControlTemplate TargetType="Button">
+                            <Border CornerRadius="5"  Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"  BorderThickness="{TemplateBinding BorderThickness}">
+                                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            </Border>
+                        </ControlTemplate>
+                    </Button.Template>
+                </Button>
+                <Button x:Name="NextBtn" Content="下一张" Height="40" Width="100" Margin="30,0,30,0" Click="NextBtn_Click" VerticalAlignment="Center" BorderThickness="1" Background="#FF0123FF" Foreground="White" Cursor="Hand">
+                    <Button.Template>
+                        <ControlTemplate TargetType="Button">
+                            <Border CornerRadius="5"  Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"  BorderThickness="{TemplateBinding BorderThickness}">
+                                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            </Border>
+                        </ControlTemplate>
+                    </Button.Template>
+                </Button>
             </StackPanel>
         </Border>
     </Grid>

+ 183 - 10
CCDCountWpf/WpfPage/HistoryDataPage.xaml.cs

@@ -30,17 +30,13 @@ namespace CCDCountWpf.WpfPage
     {
         private static ActionMesSqliteDataClass actionMesSqliteDataClass = null;
         int DataStartLine = 0;
-        private int PageHeight = 1000;
+        private int PageHeight = 2000;
         public HistoryDataPage()
         {
             InitializeComponent();
             DataContext = ShowMessageBus.ShowBinding;
-            actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{MessageBus.MainThreadS[0].cameraConfig.CamerNo}\\ActiveObjectData_{MessageBus.MainThreadS[0].BatchNumber}.db");
-            //actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{MessageBus.MainThreadS[0].cameraConfig.CamerNo}\\ActiveObjectData20250827.db");
-            actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine);
-            ShowMessageBus.ShowBinding.HistoryImageCount = (EndLine - StartLine)/PageHeight;
-            DataStartLine = StartLine;
-            UpDateShowBinding();
+            InitBatchItems();
+            //UpDateShowBinding();
         }
 
         private void PreviousBtn_Click(object sender, RoutedEventArgs e)
@@ -67,6 +63,10 @@ namespace CCDCountWpf.WpfPage
 
         private void UpDateShowBinding()
         {
+            if (actionMesSqliteDataClass == null)
+            {
+                return;
+            }
             Stopwatch stopwatch = Stopwatch.StartNew();
             BitmapImage ShowBitMap = null;
             Bitmap BitmapImage = null;
@@ -74,7 +74,7 @@ namespace CCDCountWpf.WpfPage
             if (ShowResult.Count > 0)
             {
                 int ThisImageStartLine = (int)ShowResult.Min(o => o.StartLine);
-                int ImageHeight = (int)(ShowResult.Max(o => o.LastSeenLine) - ShowResult.Min(o => o.StartLine));
+                int ImageHeight = (int)(ShowResult.Max(o => o.LastSeenLine) - ShowResult.Min(o => o.StartLine))+50;
                 int ImageWidth = ShowResult.Max(o => o.ImageWidth);
                 BitmapImage = new Bitmap(ImageWidth, ImageHeight <= PageHeight ? PageHeight : ImageHeight);
 
@@ -89,7 +89,7 @@ namespace CCDCountWpf.WpfPage
                     foreach (var item in ShowResult)
                     {
                         int roix = item.MinStartCol - 5;
-                        int roiy = (int)(item.StartLine - ThisImageStartLine) - 5;
+                        int roiy = (int)(item.StartLine - ThisImageStartLine) + 15;
                         int roiheight = (int)(item.LastSeenLine - (long)item.StartLine) + 10;
                         int roiwidth = item.MaxEndCol - item.MinStartCol + 10;
                         g.DrawRectangle(GreenPen, new System.Drawing.Rectangle(roix, roiy, roiwidth, roiheight));
@@ -99,7 +99,7 @@ namespace CCDCountWpf.WpfPage
 
                         foreach (var item1 in item.RowsData)
                         {
-                            int yPos = (int)(item1.RowsCol - ThisImageStartLine);
+                            int yPos = (int)(item1.RowsCol - ThisImageStartLine + 20);
                             g.DrawLine(item.StateCode == 0 ? bluePen : redPen, new System.Drawing.Point(item1.StartCol, yPos), new System.Drawing.Point(item1.EndCol, yPos));
                         }
                     }
@@ -142,5 +142,178 @@ namespace CCDCountWpf.WpfPage
         {
             UpDateShowBinding();
         }
+
+        /// <summary>
+        /// 初始化批次列表
+        /// </summary>
+        private void InitBatchItems()
+        {
+            string folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{MessageBus.MainThreadS[0].cameraConfig.CamerNo}";
+            if (!Directory.Exists(folderPath))
+            {
+                // 创建文件夹
+                Directory.CreateDirectory(folderPath);
+            }
+            try
+            {
+                // 使用 DirectoryInfo 获取文件并按修改时间排序
+                DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
+                FileInfo[] files = dirInfo.GetFiles("*.db", SearchOption.AllDirectories);
+
+                // 按照修改时间排序(最新的在前)
+                var sortedFiles = files.OrderByDescending(f => f.LastWriteTime).ToArray();
+                ShowMessageBus.ShowBinding.BatchItems.Clear();
+                foreach (FileInfo file in sortedFiles)
+                {
+                    ShowMessageBus.ShowBinding.BatchItems.Add(System.IO.Path.GetFileNameWithoutExtension(file.Name).Split('_')[1]);
+                }
+                BatchNumComBox.SelectedIndex = 0;
+            }
+            catch
+            { }
+        }
+
+        /// <summary>
+        /// 初始化批号列表
+        /// </summary>
+        private void InitBatchItems(DateTime Mintime, DateTime MaxTime)
+        {
+            string folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}DATA\\BatchData";
+            if (!Directory.Exists(folderPath))
+            {
+                // 创建文件夹
+                Directory.CreateDirectory(folderPath);
+            }
+            try
+            {
+                // 使用 DirectoryInfo 获取文件并按修改时间排序
+                DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
+                FileInfo[] files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
+
+                // 按照修改时间排序(最新的在前)
+                var sortedFiles = files.Where(f => f.CreationTime > Mintime && f.CreationTime < MaxTime.AddDays(1)).OrderByDescending(f => f.LastWriteTime).ToArray();
+                ShowMessageBus.ShowBinding.BatchItems.Clear();
+                foreach (FileInfo file in sortedFiles)
+                {
+                    ShowMessageBus.ShowBinding.BatchItems.Add(System.IO.Path.GetFileNameWithoutExtension(file.Name).Split('_')[1]);
+                }
+                BatchNumComBox.SelectedIndex = 0;
+            }
+            catch
+            { }
+        }
+
+        /// <summary>
+        /// 初始化批号列表
+        /// </summary>
+        private void InitBatchItemsByMinTime(DateTime Mintime)
+        {
+            string folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}DATA\\BatchData";
+            if (!Directory.Exists(folderPath))
+            {
+                // 创建文件夹
+                Directory.CreateDirectory(folderPath);
+            }
+            try
+            {
+                // 使用 DirectoryInfo 获取文件并按修改时间排序
+                DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
+                FileInfo[] files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
+
+                // 按照修改时间排序(最新的在前)
+                var sortedFiles = files.Where(f => f.CreationTime > Mintime).OrderByDescending(f => f.LastWriteTime).ToArray();
+                ShowMessageBus.ShowBinding.BatchItems.Clear();
+                foreach (FileInfo file in sortedFiles)
+                {
+                    ShowMessageBus.ShowBinding.BatchItems.Add(System.IO.Path.GetFileNameWithoutExtension(file.Name).Split('_')[1]);
+                }
+                BatchNumComBox.SelectedIndex = 0;
+            }
+            catch
+            { }
+        }
+
+        /// <summary>
+        /// 初始化批号列表
+        /// </summary>
+        private void InitBatchItemsByMaxTime(DateTime MaxTime)
+        {
+            string folderPath = $"{AppDomain.CurrentDomain.BaseDirectory}DATA\\BatchData";
+            if (!Directory.Exists(folderPath))
+            {
+                // 创建文件夹
+                Directory.CreateDirectory(folderPath);
+            }
+            try
+            {
+                // 使用 DirectoryInfo 获取文件并按修改时间排序
+                DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
+                FileInfo[] files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
+
+                // 按照修改时间排序(最新的在前)
+                var sortedFiles = files.Where(f => f.CreationTime < MaxTime.AddDays(1)).OrderByDescending(f => f.LastWriteTime).ToArray();
+                ShowMessageBus.ShowBinding.BatchItems.Clear();
+                foreach (FileInfo file in sortedFiles)
+                {
+                    ShowMessageBus.ShowBinding.BatchItems.Add(System.IO.Path.GetFileNameWithoutExtension(file.Name).Split('_')[1]);
+                }
+                BatchNumComBox.SelectedIndex = 0;
+            }
+            catch
+            { }
+        }
+
+        private void BatchNumRecordMinTime_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (BatchNumRecordMinTime.SelectedDate == null)
+            {
+                return;
+            }
+            else
+            {
+                if (BatchNumRecordMaxTime.SelectedDate == null)
+                {
+                    InitBatchItemsByMinTime((DateTime)BatchNumRecordMinTime.SelectedDate);
+                }
+                else
+                {
+                    InitBatchItems((DateTime)BatchNumRecordMinTime.SelectedDate, (DateTime)BatchNumRecordMaxTime.SelectedDate);
+                }
+            }
+        }
+
+        private void BatchNumRecordMaxTime_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (BatchNumRecordMaxTime.SelectedDate == null)
+            {
+                return;
+            }
+            else
+            {
+                if (BatchNumRecordMinTime.SelectedDate == null)
+                {
+                    InitBatchItemsByMaxTime((DateTime)BatchNumRecordMaxTime.SelectedDate);
+                }
+                else
+                {
+                    InitBatchItems((DateTime)BatchNumRecordMinTime.SelectedDate, (DateTime)BatchNumRecordMaxTime.SelectedDate);
+                }
+            }
+        }
+
+        private void BatchNumRecordComBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            if (BatchNumComBox.SelectedItem != null && BatchNumComBox.SelectedItem.ToString() != string.Empty)
+            {
+                string BatchNumber = BatchNumComBox.SelectedItem.ToString();
+                actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{MessageBus.MainThreadS[0].cameraConfig.CamerNo}\\ActiveObjectData_{BatchNumber}.db");
+                //actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{MessageBus.MainThreadS[0].cameraConfig.CamerNo}\\ActiveObjectData_20250908.db");
+                actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine);
+                ShowMessageBus.ShowBinding.HistoryImageCount = (EndLine - StartLine) / PageHeight;
+                DataStartLine = StartLine;
+                ShowMessageBus.ShowBinding.HistoryImageNum = 1;
+                UpDateShowBinding();
+            }
+        }
     }
 }

+ 182 - 13
TestWork.DLL/ShuLiClass.cs

@@ -421,8 +421,8 @@ namespace CCDCount.DLL
                     }
                     return false;
                 }
-                IsPrintLightOnError = false;
             }
+            IsPrintLightOnError = false;
             lock (_lockObj)
             {
                 foreach (var region in currentRegions)
@@ -519,6 +519,7 @@ namespace CCDCount.DLL
         {
             List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
             int start = -1; // 当前区域起始标记
+            int end = -1;
             // 遍历所有像素列
             if (shuLiConfig.IsIdentifyRoiOpen)
             {
@@ -530,16 +531,18 @@ namespace CCDCount.DLL
                     }
                     else if (start != -1) // 遇到白色像素且存在进行中的区域
                     {
+                        end = (i - 1) % (int)image.Width;
                         // 检查区域宽度是否达标
-                        if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
+                        if (end - start >= shuLiConfig.NoiseFilter_Threshold)
                         {
                             regions.Add(new ValidRegionModelClass()
                             {
                                 Start = start,
-                                End = (i - 1) % (int)image.Width
+                                End = end
                             }); // 记录有效区域
                         }
                         start = -1; // 重置区域标记
+                        end = -1;
                     }
                 }
             }
@@ -553,23 +556,25 @@ namespace CCDCount.DLL
                     }
                     else if (start != -1) // 遇到白色像素且存在进行中的区域
                     {
+                        end = (i - 1) % (int)image.Width;
                         // 检查区域宽度是否达标
-                        if (i - start >= shuLiConfig.MIN_OBJECT_WIDTH)
+                        if (end - start >= shuLiConfig.NoiseFilter_Threshold)
                         {
                             regions.Add(new ValidRegionModelClass()
                             {
                                 Start = start,
-                                End = (i - 1) % (int)image.Width
+                                End = end
                             }); // 记录有效区域
                         }
                         start = -1; // 重置区域标记
+                        end = -1;
                     }
                 }
             }
 
 
             // 处理行尾未闭合的区域
-            if (start != -1 && image.Width - start >= shuLiConfig.MIN_OBJECT_WIDTH)
+            if (start != -1 && image.Width - start >= shuLiConfig.NoiseFilter_Threshold)
             {
                 regions.Add(new ValidRegionModelClass()
                 {
@@ -625,9 +630,20 @@ namespace CCDCount.DLL
         /// <returns></returns>
         private double GetActionMaxLength(List<RowStartEndCol> Rows)
         {
+            //Stopwatch stopwatch = Stopwatch.StartNew();
             List<Point> points = ConvexHull(Rows);
-            return RotatingCalipers(points);
-
+            //stopwatch.Stop();
+            //Console.WriteLine($"凸包计算耗时:{stopwatch.Elapsed}");
+            //var test = CalculateMinimumBoundingRectangle(points);
+            var result = RotatingCalipers(points);
+            if (result!= null)
+            {
+                return result.Height;
+            }
+            else
+            {
+                return 0;
+            }
         }
 
         /// <summary>
@@ -668,8 +684,9 @@ namespace CCDCount.DLL
         /// </summary>
         /// <param name="hull"></param>
         /// <returns></returns>
-        private double RotatingCalipers(List<Point> hull)
+        private BoundingRectangleMdoel RotatingCalipers(List<Point> hull)
         {
+            /* 老方法注释
             int n = hull.Count;
             if (n == 1) return 0;
             if (n == 2) return Distance(hull[0], hull[1]);
@@ -690,8 +707,160 @@ namespace CCDCount.DLL
             }
 
             return maxDist;
+            */
+
+            BoundingRectangleMdoel result = null;
+            int n = hull.Count;
+            if (n == 1)
+            {
+                return result;
+            }
+            if (n == 2)
+            {
+                result = new BoundingRectangleMdoel
+                {
+                    points = hull,
+                    CenterPoint = new Point() { X = (hull[1].X + hull[0].X) / 2, Y = (hull[1].Y + hull[0].Y) / 2 },
+                    Width = 0,
+                    Height = Distance(hull[0], hull[1]),
+                    Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X)
+                };
+                return result;
+            }
+
+            double maxDist = 0;
+            int pointIndex1 = 0;
+            int pointIndex2 = 0;
+
+            for (int i = 0; i < n; i++)
+            {
+                for (int j = i + 1; j < n; j++)
+                {
+                    if (Distance(hull[i], hull[j]) > maxDist)
+                    {
+                        maxDist = Distance(hull[i], hull[j]);
+                        pointIndex1 = i;
+                        pointIndex2 = j;
+                    }
+                }
+            }
+
+
+            double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
+            var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
+            // 找到包围盒
+            int minX = rotatedPoints.Min(p => p.X);
+            int maxX = rotatedPoints.Max(p => p.X);
+            int minY = rotatedPoints.Min(p => p.Y);
+            int maxY = rotatedPoints.Max(p => p.Y);
+            int centerX = (minX + maxX) / 2;
+            int centerY = (minY + maxY) / 2;
+
+            int height = maxX - minX;
+            int width = maxY - minY;
+
+            // 构造矩形的四个顶点并旋转回原来的角度
+            var rectangle = new List<Point>
+            {
+                 RotatePoint(new Point(minX, minY), angle),
+                 RotatePoint(new Point(maxX, minY), angle),
+                 RotatePoint(new Point(maxX, maxY), angle),
+                 RotatePoint(new Point(minX, maxY), angle)
+            };
+
+            result = new BoundingRectangleMdoel
+            {
+                points = rectangle,
+                CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
+                Width = width,
+                Height = height,
+                Angle = angle
+            };
+
+            return result;
         }
 
+        /// <summary>
+        /// 计算凸包的最小外接矩形
+        /// </summary>
+        /// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
+        /// <returns>最小外接矩形的四个顶点</returns>
+        public List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
+        {
+            if (convexHull == null || convexHull.Count < 3)
+                return null;
+
+            double minArea = double.MaxValue;
+            List<Point> minRectangle = null;
+
+            int n = convexHull.Count;
+
+            // 遍历每一条边作为基准边
+            for (int i = 0; i < n; i++)
+            {
+                Point edgeStart = convexHull[i];
+                Point edgeEnd = convexHull[(i + 1) % n];
+
+                // 计算当前边的角度
+                double angle = Math.Atan2(edgeEnd.Y - edgeStart.Y, edgeEnd.X - edgeStart.X);
+
+                // 将所有点绕原点旋转-angle角度,使当前边与x轴平行
+                var rotatedPoints = convexHull.Select(p => RotatePoint(p, -angle)).ToList();
+
+                // 找到包围盒
+                int minX = rotatedPoints.Min(p => p.X);
+                int maxX = rotatedPoints.Max(p => p.X);
+                int minY = rotatedPoints.Min(p => p.Y);
+                int maxY = rotatedPoints.Max(p => p.Y);
+
+                // 计算面积
+                double area = (maxX - minX) * (maxY - minY);
+
+                // 如果面积更小,则更新最小矩形
+                if (area < minArea)
+                {
+                    minArea = area;
+
+                    // 构造矩形的四个顶点并旋转回原来的角度
+                    var rectangle = new List<Point>
+                    {
+                         RotatePoint(new Point(minX, minY), angle),
+                         RotatePoint(new Point(maxX, minY), angle),
+                         RotatePoint(new Point(maxX, maxY), angle),
+                         RotatePoint(new Point(minX, maxY), angle)
+                    };
+
+                    minRectangle = rectangle;
+                }
+            }
+
+            return minRectangle;
+        }
+
+        /// <summary>
+        /// 绕原点旋转点
+        /// </summary>
+        /// <param name="point">待旋转的点</param>
+        /// <param name="angle">旋转角度(弧度)</param>
+        /// <returns>旋转后的点</returns>
+        private static Point RotatePoint(Point point, double angle)
+        {
+            double cos = Math.Cos(angle);
+            double sin = Math.Sin(angle);
+
+            return new Point(
+                (int)(point.X * cos - point.Y * sin),
+                (int)(point.X * sin + point.Y * cos)
+            );
+        }
+
+        /// <summary>
+        /// 计算点到线段的距离
+        /// </summary>
+        /// <param name="a">线段点1</param>
+        /// <param name="b">线段点2</param>
+        /// <param name="c">点三</param>
+        /// <returns></returns>
         private double DistanceToLine(Point a, Point b, Point c)
         {
             double area = Math.Abs(Area2(a, b, c));
@@ -732,13 +901,13 @@ namespace CCDCount.DLL
         /// </summary>
         private void IdentifyImageProcess()
         {
-            Stopwatch stopwatch = Stopwatch.StartNew();
+            //Stopwatch stopwatch = Stopwatch.StartNew();
             while (IsIdentify)
             {
                 //判断队列中是否有数据
                 if (IFrameDatas.Count() > 0)
                 {
-                    stopwatch.Restart();
+                    //stopwatch.Restart();
                     if (IFrameDatas.Count() > 50)
                         FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
                     IFrameDatas.TryDequeue(out IImage IframeData);
@@ -754,8 +923,8 @@ namespace CCDCount.DLL
                         continue;
                     }
                     //输出耗时
-                    stopwatch.Stop();
-                    Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed},待识别队列剩余数量{IFrameDatas.Count()}");
+                    //stopwatch.Stop();
+                    //Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed},待识别队列剩余数量{IFrameDatas.Count()}");
                 }
                 else
                 {

+ 64 - 50
TestWork.DLL/SqlDataClass/ActionMesSqliteDataClass.cs

@@ -309,10 +309,12 @@ namespace CCDCount.DLL.SqlDataClass
         public List<ActiveObjectClass> GetActiveObjectForPage(int StartLine, int EndLine)
         {
             List<ActiveObjectClass> activeObjects = new List<ActiveObjectClass>();
-            using (var conn = new SQLiteConnection(_connectionString))
+            try
             {
-                conn.Open();
-                const string query = @"
+                using (var conn = new SQLiteConnection(_connectionString))
+                {
+                    conn.Open();
+                    const string query = @"
                 SELECT 
                     ao.*,
                     rd.Id AS RowId, rd.RowsCol, rd.StartCol, rd.EndCol
@@ -320,68 +322,80 @@ namespace CCDCount.DLL.SqlDataClass
                 LEFT JOIN RowData rd ON ao.Id = rd.ActiveObjectId
                 WHERE ao.StartLine <= @StartLine AND LastSeenLine>=@LastSeenLine ORDER BY rd.Id";
 
-                using (var cmd = new SQLiteCommand(query, conn))
-                {
-                    cmd.Parameters.AddWithValue("@StartLine", EndLine);
-                    cmd.Parameters.AddWithValue("@LastSeenLine", StartLine);
-
-                    using (var reader = cmd.ExecuteReader())
+                    using (var cmd = new SQLiteCommand(query, conn))
                     {
-                        int NowId = -1;
-                        ActiveObjectClass activeObject = null;
-                        var rowsData = new List<RowStartEndCol>();
+                        cmd.Parameters.AddWithValue("@StartLine", EndLine);
+                        cmd.Parameters.AddWithValue("@LastSeenLine", StartLine);
 
-                        while (reader.Read())
+                        using (var reader = cmd.ExecuteReader())
                         {
-                            object value = reader["Id"];
-                            if (!Convert.IsDBNull(value))
+                            int NowId = -1;
+                            ActiveObjectClass activeObject = null;
+                            var rowsData = new List<RowStartEndCol>();
+
+                            while (reader.Read())
                             {
-                                if (NowId != Convert.ToInt32(reader["Id"]))
+                                object value = reader["Id"];
+                                if (!Convert.IsDBNull(value))
                                 {
-                                    if (activeObject != null)
+                                    if (NowId != Convert.ToInt32(reader["Id"]))
                                     {
-                                        activeObjects.Add(activeObject);
-                                        activeObject = null;
-                                        rowsData = new List<RowStartEndCol>();
+                                        if (activeObject != null)
+                                        {
+                                            activeObjects.Add(activeObject);
+                                            activeObject = null;
+                                            rowsData = new List<RowStartEndCol>();
+                                        }
+                                        NowId = Convert.ToInt32(reader["Id"]);
                                     }
-                                    NowId = Convert.ToInt32(reader["Id"]);
-                                }
-                                // 只初始化主对象一次
-                                if (activeObject == null)
-                                {
-                                    activeObject = new ActiveObjectClass
+                                    // 只初始化主对象一次
+                                    if (activeObject == null)
+                                    {
+                                        activeObject = new ActiveObjectClass
+                                        {
+                                            Num = Convert.ToInt32(reader["Num"]),
+                                            MinStartCol = Convert.ToInt32(reader["MinStartCol"]),
+                                            MaxEndCol = Convert.ToInt32(reader["MaxEndCol"]),
+                                            LastSeenLineStartCol = Convert.ToInt32(reader["LastSeenLineStartCol"]),
+                                            LastSeenLineEndCol = Convert.ToInt32(reader["LastSeenLineEndCol"]),
+                                            StartLine = Convert.ToInt64(reader["StartLine"]),
+                                            LastSeenLine = Convert.ToInt64(reader["LastSeenLine"]),
+                                            StartCheckTime = DateTime.Parse(reader["StartCheckTime"].ToString()),
+                                            EndCheckTime = DateTime.Parse(reader["EndCheckTime"].ToString()),
+                                            Area = Convert.ToInt32(reader["Area"]),
+                                            MaxLength = Convert.ToDouble(reader["MaxLength"]),
+                                            ChannelNO = Convert.ToInt32(reader["ChannelNO"]),
+                                            ImageWidth = Convert.ToInt32(reader["ImageWidth"]),
+                                            StateCode = Convert.ToInt32(reader["StateCode"]),
+                                            BatchNumber = reader["BatchNumber"].ToString(),
+                                            RowsData = rowsData
+                                        };
+                                    }
+
+                                    // 添加行数据(确保RowData记录存在)
+                                    rowsData.Add(new RowStartEndCol
                                     {
-                                        Num = Convert.ToInt32(reader["Num"]),
-                                        MinStartCol = Convert.ToInt32(reader["MinStartCol"]),
-                                        MaxEndCol = Convert.ToInt32(reader["MaxEndCol"]),
-                                        LastSeenLineStartCol = Convert.ToInt32(reader["LastSeenLineStartCol"]),
-                                        LastSeenLineEndCol = Convert.ToInt32(reader["LastSeenLineEndCol"]),
-                                        StartLine = Convert.ToInt64(reader["StartLine"]),
-                                        LastSeenLine = Convert.ToInt64(reader["LastSeenLine"]),
-                                        StartCheckTime = DateTime.Parse(reader["StartCheckTime"].ToString()),
-                                        EndCheckTime = DateTime.Parse(reader["EndCheckTime"].ToString()),
-                                        Area = Convert.ToInt32(reader["Area"]),
-                                        MaxLength = Convert.ToDouble(reader["MaxLength"]),
-                                        ChannelNO = Convert.ToInt32(reader["ChannelNO"]),
-                                        ImageWidth = Convert.ToInt32(reader["ImageWidth"]),
-                                        StateCode = Convert.ToInt32(reader["StateCode"]),
-                                        BatchNumber = reader["BatchNumber"].ToString(),
-                                        RowsData = rowsData
-                                    };
+                                        RowsCol = Convert.ToInt64(reader["RowsCol"]),
+                                        StartCol = Convert.ToInt32(reader["StartCol"]),
+                                        EndCol = Convert.ToInt32(reader["EndCol"])
+                                    });
                                 }
+                            }
 
-                                // 添加行数据(确保RowData记录存在)
-                                rowsData.Add(new RowStartEndCol
-                                {
-                                    RowsCol = Convert.ToInt64(reader["RowsCol"]),
-                                    StartCol = Convert.ToInt32(reader["StartCol"]),
-                                    EndCol = Convert.ToInt32(reader["EndCol"])
-                                });
+                            if (activeObject != null)
+                            {
+                                activeObjects.Add(activeObject);
+                                activeObject = null;
+                                rowsData = new List<RowStartEndCol>();
                             }
                         }
                     }
                 }
             }
+            catch(Exception ex)
+            {
+                Console.WriteLine($"GetActiveObjectForPage - Error:{ex.Message}");
+            }
             return activeObjects;
         }
 

+ 2 - 0
TestWork.MODEL/CCDCount.MODEL.csproj

@@ -52,6 +52,7 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -73,6 +74,7 @@
     <Compile Include="ShuLiModel\ActiveObjectClass.cs" />
     <Compile Include="ShuLiModel\ActiveObjectEventArgsClass.cs" />
     <Compile Include="ConfigModel\ShuLiConfigClass.cs" />
+    <Compile Include="ShuLiModel\BoundingRectangleMdoel.cs" />
     <Compile Include="ShuLiModel\ValidRegionModelClass.cs" />
     <Compile Include="SqlDataModel\ErroeMesDataModelClass.cs" />
     <Compile Include="SqlDataModel\ErrorMesDataEventModelClass.cs" />

+ 1 - 1
TestWork.MODEL/ConfigModel/ShuLiConfigClass.cs

@@ -20,7 +20,7 @@
         /// <summary>
         /// 允许物体中断的最大连续行数
         /// </summary>
-        public int MAX_GAP { get; set; } = 1;
+        public int MAX_GAP { get; set; } = 2;
         /// <summary>
         /// 单个物体允许的最大高度
         /// </summary>

+ 22 - 0
TestWork.MODEL/ShuLiModel/BoundingRectangleMdoel.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CCDCount.MODEL.ShuLiModel
+{
+    public class BoundingRectangleMdoel
+    {
+        public List<Point> points { get; set; }
+
+        public Point CenterPoint { get; set; }
+
+        public double Width { get; set; }
+
+        public double Height { get; set; }
+
+        public double Angle { get; set; }
+    }
+}