<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      【Talk is cheap. Show me the code.】 公眾號如有回復不及時的,麻煩點擊聯系關于我-聯系博主,微信我。謝謝!
      老帥哥

      Stephen-kzx的博客

      【Talk is cheap. Show me the code.】【公眾號如有回復不及時的,麻煩點擊聯系關于我-聯系博主,微信我。謝謝!】

      OpenCvSharp基于顏色反差規避FBA面單貼標2

      第一版的劣勢

              原理同上一邊博客記錄,在基礎上改造的更加細致些,100*100的貼標區域,很容易讓原本就不大的FBA紙箱,留下更多空白區域,并且空白區域和原廠標簽空隙不足貼下一張新的標簽,導致東一張西一張,雖然能夠滿足規避原廠標簽的初衷,但是如果客戶需要貼多張標簽,就會捉襟見肘

      解決辦法-提升精度

            既然100*100的匹配,容易造成可貼標區域浪費,那么何不把精度提升到100倍呢?

            把原來100*100的網格,細分為由10個10*10的網格組成,每次匹配可貼標區域,偏移一個10*10網格的網格,然后根據占用的這個10*10的網格,按照偏移的方向,向左向上分別獲取相鄰的10個網格,那不就組成了一個100*100的可貼標區域了嗎?(當然如果需要可貼標區域利用率更高,可以縮小100倍,比如1*1的網格,獲取相鄰橫向和縱向100個這樣1*1網格,也可以組成100*100的可貼標區域,本文已經把網格大小提取出來,可用作擴展配置,本文拋磚引玉,有更好的想法可以一起交流完善)。

            無圖言屌,用一張粗糙的動態圖,來說明第二版本提升精度的慢動作(最下面紅色區域是硬件的物理鈑金,已經根據上篇博客當作原廠標簽標記了,所以標記為干擾區域)

      網格

      先看最終效果

      避免文字無趣,先看下實際的定位效果(紅色標記原廠標簽,黃色標記可貼區域坐標)

      20250919194626109

      下面是模擬效果(紅色區域是人工制造的FBA原廠標簽),旋轉紙箱不同方向的貼標效果

      wechat_2025-09-24_173404_373

      可以看到,無論紙箱如何旋轉,新帖的標簽,都可以完全避開.

      廢話少說,上源碼

      116076-20250310125032345-1793233350[1]

      大部分源碼在上個博文已經分享出來,以下附上改動點。(文章最后會附上不同紙箱的定位效果)

         // 裁剪圖像(從右下角開始保留指定尺寸)
         var croppedImage = AvoidFactoryLabelSDK.CropImageFromBottomRight(originalImage, boxWidthMm, boxHeightMm);
      
         if (croppedImage.Empty())
         {
             Console.WriteLine("裁剪后的圖像為空");
             return;
         }
      
         // 檢測所有原廠面單位置
         var labelPositions = AvoidFactoryLabelSDK.DetectOriginalLabelPositions(croppedImage);
         Console.WriteLine($"檢測到 {labelPositions.Count} 個原廠面單:");
         foreach (var pos in labelPositions)
         {
             Console.WriteLine($"位置: {pos.GridCoordinate}, 尺寸: {pos.WidthMm:F1}mm × {pos.HeightMm:F1}mm");
         }
      
         // 查找可貼標簽的位置
         string availablePosition = AvoidFactoryLabelSDK.FindAvailableLabelPosition(croppedImage, labelPositions);
         Console.WriteLine($"可貼標簽的位置: {availablePosition}");
      
         // 可視化結果(可選)
         Bitmap resultbm = AvoidFactoryLabelSDK.VisualizeResults(croppedImage, labelPositions, availablePosition);
         lblStatus.Text = availablePosition;
         pictureBox1.Image = resultbm;
      
       /// <summary>
       /// 原廠標簽規避算法
       /// </summary>
       /// <param name="bmSource">原箱標簽</param>
       /// <param name="x">返回坐標X</param>
       /// <param name="y">返回坐標y</param>
       /// <param name="message">異常信息</param>
       /// <param name="dpi">電腦DPI</param>
       /// <returns></returns>
       public static Bitmap AvoidFactoryLabelAlgorithm(string imagepath, double boxWidthMm, double boxHeightMm, out int x, out int y, out string message, double sizeF = 1.7, double dpi = 300)
       {
           message = string.Empty;
           x = y = 1;
      
           // 加載圖像 
           var originalImage = Cv2.ImRead(imagepath, OpenCvSharp.ImreadModes.Grayscale);
           //計算每毫米像素數 (基于300 DPI)
           double PixelsPerMm = dpi / 25.4; // 約等于 11.811 
                                            // 計算面單灰度范圍
           CalculateLabelGrayRange();
           ShellLine.WriteLine($"計算出的面單灰度范圍: {MinLabelGray}-{MaxLabelGray}");
      
           // 裁剪圖像(從右下角開始保留指定尺寸)
           var croppedImage = CropImageFromBottomRight(originalImage, boxWidthMm, boxHeightMm);
      
           if (croppedImage.Empty())
           {
               ShellLine.WriteLine("裁剪后的圖像為空");
               return croppedImage.ToBitmap();
           }
            
           // 檢測所有原廠面單位置
           var labelPositions = DetectOriginalLabelPositions(croppedImage);
           ShellLine.WriteLine($"檢測到 {labelPositions.Count} 個原廠面單:");
           foreach (var pos in labelPositions)
           {
               ShellLine.WriteLine($"位置: {pos.GridCoordinate}, 尺寸: {pos.WidthMm:F1}mm × {pos.HeightMm:F1}mm");
           } 
           // 查找可貼標簽的位置
           string availablePosition = FindAvailableLabelPosition(croppedImage, labelPositions);
           ShellLine.WriteLine($"可貼標簽的位置: {availablePosition}"); 
           x = availablePosition.Split('-')[0].ToIntExt();
           y = availablePosition.Split('-')[1].ToIntExt();
           // 可視化結果(可選)
           Bitmap resultMap =  VisualizeResults(croppedImage, labelPositions, availablePosition);
           
           return resultMap; 
       }
      

        

        // 根據顏色列表計算面單灰度范圍
        public static void CalculateLabelGrayRange()
        {
            var grayValues = new List<int>();
      
            foreach (var colorHex in LabelColors)
            {
                // 將十六進制顏色轉換為RGB
                System.Drawing.Color color = ColorTranslator.FromHtml(colorHex);
      
                // 計算灰度值 (使用標準公式: 0.299*R + 0.587*G + 0.114*B)
                int grayValue = (int)(0.299 * color.R + 0.587 * color.G + 0.114 * color.B);
                grayValues.Add(grayValue);
      
                Console.WriteLine($"顏色 {colorHex} 的灰度值: {grayValue}");
            }
      
            // 計算最小和最大灰度值,并擴展范圍以容納類似顏色
            MinLabelGray = grayValues.Min() - 10;
            MaxLabelGray = grayValues.Max() + 10;
      
            // 確保范圍在0-255之間
            MinLabelGray = Math.Max(0, MinLabelGray);
            MaxLabelGray = Math.Min(255, MaxLabelGray);
        }
      

        

       // 檢測所有原廠面單位置
       public static List<LabelPosition> DetectOriginalLabelPositions(OpenCvSharp.Mat image)
       {
           var labelPositions = new List<LabelPosition>();
      
           // 二值化圖像以分離面單區域
           var binary = new OpenCvSharp.Mat();
           Cv2.Threshold(image, binary, MinLabelGray, 255, ThresholdTypes.Binary);
      
           // 形態學操作去除噪聲
           var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(5, 5));
           Cv2.MorphologyEx(binary, binary, MorphTypes.Open, kernel);
      
           // 查找輪廓
           Cv2.FindContours(binary, out var contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
      
           // 過濾輪廓(按面積)
           var filteredContours = contours.Where(c => Cv2.ContourArea(c) > 1000).ToList();
      
           // 處理每個輪廓
           foreach (var contour in filteredContours)
           {
               // 獲取輪廓的邊界矩形
               var rect = Cv2.BoundingRect(contour);
      
               // 轉換為網格坐標
               string gridCoordinate = ConvertToGridCoordinate(rect, image.Rows, image.Cols);
      
               // 計算實際尺寸(毫米)
               double widthMm = rect.Width / PixelsPerMm;
               double heightMm = rect.Height / PixelsPerMm;
      
               // 添加到結果列表
               labelPositions.Add(new LabelPosition
               {
                   Rect = rect,
                   GridCoordinate = gridCoordinate,
                   WidthMm = widthMm,
                   HeightMm = heightMm
               });
           }
      
           return labelPositions;
       }
      

        

         // 查找可貼標簽的位置(使用10mm×10mm基礎網格)
         public static string FindAvailableLabelPosition(OpenCvSharp.Mat image, List<LabelPosition> labelPositions)
         {
             // 獲取圖像尺寸
             int rows = image.Rows;
             int cols = image.Cols;
      
             // 計算基礎網格行列數
             int baseGridCols = (int)Math.Ceiling((double)cols / BaseGridSizePixels);
             int baseGridRows = (int)Math.Ceiling((double)rows / BaseGridSizePixels);
      
             // 從右下角開始查找(先橫向,再縱向)
             for (int baseRow = 0; baseRow < baseGridRows; baseRow++)
             {
                 for (int baseCol = 0; baseCol < baseGridCols; baseCol++)
                 {
                     // 計算當前基礎網格的像素坐標(右下角)
                     int baseX = cols - baseCol * BaseGridSizePixels;
                     int baseY = rows - baseRow * BaseGridSizePixels;
      
                     // 計算100mm×100mm區域的像素坐標
                     int labelX = baseX - LabelSizePixels;
                     int labelY = baseY - LabelSizePixels;
      
                     // 檢查區域是否在圖像范圍內
                     if (labelX < 0 || labelY < 0)
                         continue;
      
                     // 創建100mm×100mm區域矩形
                     Rect labelRect = new Rect(labelX, labelY, LabelSizePixels, LabelSizePixels);
      
                     // 檢查區域是否與任何原廠標簽相交
                     bool intersects = false;
                     foreach (var labelPos in labelPositions)
                     {
                         if (labelRect.IntersectsWith(labelPos.Rect))
                         {
                             intersects = true;
                             break;
                         }
                     }
      
                     // 如果不相交,則返回當前位置
                     if (!intersects)
                     {
                         // 轉換為網格坐標 (baseRow+1, baseCol+1)
                         return $"{baseRow + 1}-{baseCol + 1}";
                     }
                 }
             }
      
             // 如果沒有找到可用位置,返回默認位置
             return "1-1";
         }
      

        

          // 可視化結果
          public static Bitmap VisualizeResults(OpenCvSharp.Mat image, List<LabelPosition> labelPositions, string availablePosition)
          {
              var colorImage = new OpenCvSharp.Mat();
              Cv2.CvtColor(image, colorImage, ColorConversionCodes.GRAY2BGR);
      
              int rows = image.Rows;
              int cols = image.Cols;
              // 繪制網格
              for (int x = 0; x < cols; x += BaseGridSizePixels)
              {
                  Cv2.Line(colorImage, new OpenCvSharp.Point(x, 0), new OpenCvSharp.Point(x, rows), Scalar.Green, 5);
              }
              for (int y = 0; y < rows; y += BaseGridSizePixels)
              {
                  Cv2.Line(colorImage, new OpenCvSharp.Point(0, y), new OpenCvSharp.Point(cols, y), Scalar.Green, 5);
              }
              // 標記所有原廠面單位置(紅色)
              foreach (var labelPos in labelPositions)
              {
                  Cv2.Rectangle(colorImage,
                               labelPos.Rect.TopLeft,
                               labelPos.Rect.BottomRight,
                               Scalar.Red, 3);
      
                  // 添加標簽文本
                  Cv2.PutText(colorImage,
                             labelPos.GridCoordinate,
                             new OpenCvSharp.Point(labelPos.Rect.X, labelPos.Rect.Y - 5),
                             HersheyFonts.HersheySimplex,
                             0.5,
                             Scalar.Red,
                             3);
              }
      
              // 標記可貼標簽位置(黃色)
              if (!string.IsNullOrEmpty(availablePosition) && availablePosition != "1-1")
              {
                  var parts = availablePosition.Split('-');
                  if (parts.Length == 2)
                  {
                      int row = int.Parse(parts[0]);
                      int col = int.Parse(parts[1]);
      
                      // 計算100mm×100mm區域的像素坐標
                      int x = cols - col * BaseGridSizePixels - LabelSizePixels;
                      int y = rows - row * BaseGridSizePixels - LabelSizePixels;
      
                      // 確保區域在圖像范圍內
                      if (x < 0) x = 0;
                      if (y < 0) y = 0;
      
                      int width = Math.Min(LabelSizePixels, cols - x);
                      int height = Math.Min(LabelSizePixels, rows - y);
      
                      if (width > 0 && height > 0)
                      {
                          Cv2.Rectangle(colorImage,
                                       new OpenCvSharp.Point(x, y),
                                       new OpenCvSharp.Point(x + width, y + height),
                                       Scalar.Yellow, 3);
      
                          // 添加標簽文本
                          Cv2.PutText(colorImage,
                                     availablePosition,
                                     new OpenCvSharp.Point(x + 10, y + 30),
                                     HersheyFonts.HersheySimplex,
                                     1,
                                     Scalar.Yellow,
                                     3);
                      }
                  }
              }
      
              return colorImage.ToBitmap();
          }
      }
      

        demo展示效果

      網格1

      結束語

              感謝各位耐心查閱!  如果您有更好的想法歡迎一起交流,有不懂的也可以微信公眾號聯系博主,作者公眾號會經常發一些實用的小工具和demo源碼,需要的可以去看看!另外,如果覺得本篇博文對您或者身邊朋友有幫助的,麻煩點個關注!贈人玫瑰,手留余香,您的支持就是我寫作最大的動力,感謝您的關注,期待和您一起探討!再會!

      640

       

      posted @ 2025-09-24 18:09  何以解憂唯有*碼  閱讀(210)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 免费国产一区二区不卡| 成人欧美日韩一区二区三区| 999精品视频在线| 乱码精品一区二区亚洲区| 欧美激情综合色综合啪啪五月| 久热久精久品这里在线观看| 人人爽人人爽人人片av东京热| 中国少妇人妻xxxxx| 特黄 做受又硬又粗又大视频| 国产综合av一区二区三区 | 热久久这里只有精品国产| 国产av无码专区亚洲av软件| 日韩精品久久一区二区三| 国产99青青成人A在线| 精品无人乱码一区二区三区 | 污污网站18禁在线永久免费观看| 日产日韩亚洲欧美综合下载| 亚洲精品久久麻豆蜜桃| 亚洲综合区激情国产精品| 久久午夜无码鲁丝片直播午夜精品| 97视频精品全国免费观看| 亚洲国产午夜精品福利| 十九岁的日本电影免费观看| 国产女同一区二区在线| 国产成人无码aa片免费看| 精品一区二区成人码动漫| 一区二区三区激情都市| 欧美人与动人物牲交免费观看 | 亚洲色大成网站www久久九| 中文在线天堂中文在线天堂| 男女性高爱潮免费网站| 乌克兰丰满女人a级毛片右手影院 人妻中文字幕不卡精品 | 亚洲精品日韩中文字幕| 老鸭窝在线视频| 无码国产成人午夜电影在线观看| 一区二区三区精品自拍视频| 精品无码国产一区二区三区51安| 国产一区二区三区激情视频| 中文字幕国产精品二区| 欧美日韩一区二区三区视频播放 | 国产av最新一区二区|