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

0 1

规避原理

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

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

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

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

  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 LabelColors = new List
    {
    "#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个网格,最后两个网格超过原箱尺寸无效,默认返回第一个网格(视情况自定义)


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

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

相关推荐
yubo05096 小时前
计算机视觉第五课:给每个物体画 bounding box
人工智能·opencv·计算机视觉
AGV算法笔记8 小时前
OpenCV 二维码三维定位 普通摄像头也能测空间坐标
人工智能·数码相机·opencv·工业视觉· 机器人视觉
yubo05098 小时前
计算机视觉第四课:寻找轮廓(自动框出所有物体)
人工智能·opencv·计算机视觉
sali-tec9 小时前
C# 基于OpenCv的视觉工作流-章79-单位转换
图像处理·人工智能·opencv·算法·计算机视觉
_李小白20 小时前
【android opencv学习笔记】Day 26: 滤波算法之低通滤波与图像缩放插值
android·opencv·学习
Hello world.Joey1 天前
吴恩达深度学习基础
人工智能·深度学习·神经网络·opencv·算法·机器学习·计算机视觉
梦想三三1 天前
【OpenCV四大边缘检测算法】Sobel、Scharr、Laplacian、Canny 详解
人工智能·opencv·计算机视觉
半壶清水1 天前
用 Python 和 OpenCV 提取书法作品中的每一个单字
python·opencv·计算机视觉
梦想三三1 天前
【Open CV图像形态学处理技术】边界填充与形态学运算
人工智能·opencv·计算机视觉
梦想三三1 天前
【Open CV图像处理】修改运算与平滑处理
人工智能·opencv·计算机视觉