当前位置: 首页 > news >正文

OpenCvSharp基于颜色反差规避FBA面单贴标

01

规避原理

1.抠图,根据色差或者根据固定包裹位置以及包裹尺寸抠出纸箱图片

2.色差,获取纸箱上所有背景色的灰度值

3.采图,采集大量视野相同,光源相同面单的色差灰度值,整理区间

4.取反,所有非面单灰度值区间的,都认为是纸箱背景色

02
  1. 根据DPI计算1mm对应像素点。

  2. 获取吸取的颜色,计算灰度值

 

// 300 DPI 计算:每毫米像素数 = 300 / 25.4 ≈ 11.81
private const double PixelsPerMm = 300.0 / 25.4 ;
private static int LabelSizePixels = Convert.ToInt32(Math.Ceiling((int)(95 * PixelsPerMm) / 1.7)); // 100mm × 100mm

// 面单颜色列表(十六进制格式)
private static readonly List<string> LabelColors = new List<string>
{
"#E2E2E0", "#DEDEDC", "#E0E0DE", "#CCCCCA", "#B2B2B0", "#C2C2C0","#FFFFFF","#FEFEFE","#FCFCFC" ,"#ADADAD"
};

// 计算出的面单灰度范围
private static int MinLabelGray;
private static int MaxLabelGray;
// 加载图像
var originalImage = Cv2.ImRead(@"D:\Users\steph\Pictures\1分4\Image_20250913210539498.jpg", OpenCvSharp.ImreadModes.Grayscale);

// 计算面单灰度范围
CalculateLabelGrayRange();
Console.WriteLine($"计算出的面单灰度范围: {MinLabelGray}-{MaxLabelGray}");


if (originalImage.Empty())
{
Console.WriteLine("无法加载图像");
return;
}

此处用第二种最简单方式演示,视野固定包裹位置,根据计算包裹的尺寸,扣除原箱外观

// 从右下角裁剪图像
private static OpenCvSharp.Mat CropImageFromBottomRight(OpenCvSharp.Mat image, double widthMm, double heightMm)
{
// 将毫米转换为像素
int widthPixels = (int)(widthMm * PixelsPerMm/1.7);
int heightPixels = (int)(heightMm * PixelsPerMm/1.7);

// 获取图像尺寸
int imgWidth = image.Cols;
int imgHeight = image.Rows;

// 计算裁剪区域的左上角坐标
int x = Math.Max(0, imgWidth - widthPixels);
int y = Math.Max(0, imgHeight - heightPixels);

// 确保裁剪区域不超出图像边界
widthPixels = Math.Min(widthPixels, imgWidth - x);
heightPixels = Math.Min(heightPixels, imgHeight - y);

// 检查裁剪区域是否有效
if (widthPixels <= 0 || heightPixels <= 0)
{
Console.WriteLine($"无效的裁剪区域: x={x}, y={y}, width={widthPixels}, height={heightPixels}");
return image.Clone(); // 返回原始图像的副本
}

Console.WriteLine($"裁剪区域: x={x}, y={y}, width={widthPixels}, height={heightPixels}");

// 裁剪图像
return new OpenCvSharp.Mat(image, new Rect(x, y, widthPixels, heightPixels));
}

根据裁切后的原箱外观,以及灰度值区间,定位原厂面单位置

// 检测所有原厂面单位置
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;
}

检查可贴标签位置是否与原厂标签有交集,检查可贴区域是否超过原箱尺寸,此处我们以新帖面单大小100mm*100mm为例。没有可贴标签位置默认选择1-1位置贴标

// 检测所有原厂面单位置
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;
}

可视化结果,以绿色网格铺满原箱,以红色区域标定原厂标签位置,以蓝色网格标定可贴标签位置,返回可视化结果

// 检测所有原厂面单位置
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;
}

看看效果1,运行看看效果.(如下图纸箱长400,高190)白色原厂面单占据了前面6个网格,最后两个网格超过原箱尺寸无效,默认返回第一个网格(视情况自定义)

image.png

保持期待 奔赴山海KEEP LOOKING FORWARD TO GOING

效果2.原厂标签占据第一个和中间4个网格,可贴标签区域蓝色网格标识,并返回可贴坐标

image.png

 

http://www.gsyq.cn/news/7703.html

相关文章:

  • Torrent File Editor 1.0.0
  • US$49 Multi-languages Smart Zed-Bull With Mini Type No Tokens Needed
  • AI CodeReview + Devops协同
  • 【API接口】最新可用IP地址查询接口
  • 磁盘分析工具推荐(Wiztree)
  • Markbook Day03
  • 数组,java学习第五天
  • U3D动作游戏开发读书笔记--3.1 物理系统详解(上)
  • US$198 Auxiliary Heater Diagnostic Unit for Eberspacher 12V/24V Systems
  • 20250918 之所思 - 人生如梦
  • 用 Go 语言与 Tesseract OCR 实现英文数字验证码识别
  • lc1031-两个非重叠子数组的最大和
  • 我对 WPF 动摇时的选择:.NET Framework 4.6.2+WPF+Islands+UWP+CompostionApi - 行人-
  • US$1198 Xhorse VVDI2 BMW Version With Basic+BMW OBD+BMW CAS4+BMW FEM/BDC
  • 什么情况下需要用到xargs
  • Office 2024安装包专业增强版超详细下载安装教程
  • 关于 pdfminer 的安装 - 指南
  • EF Core 与 MySQL:日志和调试详解
  • 使用镜像源解决github拉取代码问题 - GD
  • 类和面向对象
  • mac更新or安装homebrew失败
  • 微信小程序实现-单选-以及全选功能。 - 教程
  • Typescript中闭包的原理 - 教程
  • Hadoop本地库加载问题分析与解决方案
  • 专用通路方式
  • 2025.8 做题记录
  • 关于pytorch的读书报告
  • Emacs 折腾日记(三十)——打造C++ IDE 续
  • 完整教程:.NET驾驭Word之力:玩转文本与格式
  • 综合与实现流程【p3】--(DSP-存储)优化PS系统集成