从零开始详解OpenCV条形码区域分割

前言

在识别二维码之前,首先要划分出二维码的区域,在本篇文章中将从零开始实现二维码分割的功能,并详细介绍用到的方法。

我们需要处理的图像如下:
在这里插入图片描述

完整代码

首先我们先放出完整代码,然后根据整个分割流程介绍用到的函数和方法,完整代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as pltclass barCodes:def __init__(self, img_path, col_scale=800):self.image = cv2.imread(img_path)  # BGR图像self.gray_image = cv2.cvtColor(self.image.copy(), cv2.COLOR_RGB2GRAY)  # 灰度图self.col_scale = col_scale  # 根据列等比缩放图片self.scale = col_scale / self.image.shape[1]  # 缩放后图像宽与当前实际图像的宽的比值self.unscale = 1.0 / self.scale  # self.scale的倒值,用于还原缩放self.boxs = []  # 用于存储分割出来的框def scaleImage(self, image):# 根据比值来缩放图像im = image.copy()scale=self.scaleim = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))return imdef blackHat(self, image, kernel = np.ones((1, 3), np.uint8)):# 黑帽操作im = image.copy()im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel=kernel, anchor=(1, 0))return imdef threshold(self, image, low=10, heigh=255):# 根据阈值二值化im = image.copy()thresh, im = cv2.threshold(im, low, heigh, cv2.THRESH_BINARY)return imdef morph_close(self, image, kernel=np.ones((1, 5), np.uint8), iterations=2):# 闭操作im = image.copy()im = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel, iterations=iterations)return imdef dilate(self, image, kernel = np.ones((1,3),np.uint8)):# 腐蚀操作im = image.copy()im = cv2.dilate(im, kernel)return imdef getContours(self, image):# 获取边框im = image.copy()im_out = self.image.copy()unscale = self.unscalecontours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)if contours != None:for contour in contours:if cv2.contourArea(contour) < 2000:continuerect = cv2.minAreaRect(contour)rect = ((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \(int(rect[1][0] * unscale), int(rect[1][1] * unscale)),rect[2])box = np.int0(cv2.boxPoints(rect))self.boxs.append(box)cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness=2)return im_outdef forward(self, image, process_lst):im = image.copy()for i, process in enumerate(process_lst):im = process(im)self.plot_show(im, cmap=plt.cm.gray)return imdef plot_show(self, image, cmap=None):# 显示图像if cmap is None:plt.imshow(image)else:plt.imshow(image, cmap)plt.show()def getBarRect(self):self.plot_show(self.image[:,:,::-1])process_lst = [self.scaleImage, self.blackHat, self.threshold, self.morph_close, self.dilate, self.getContours]image = self.forward(self.gray_image, process_lst)image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)self.plot_show(image)barcode = barCodes('test.png')
barcode.getBarRect()

函数详解

初始化

def __init__(self, img_path, col_scale=800):self.image = cv2.imread(img_path)  # BGR图像self.gray_image = cv2.cvtColor(self.image.copy(), cv2.COLOR_RGB2GRAY)  # 灰度图self.col_scale = col_scale  # 根据列等比缩放图片self.scale = col_scale / self.image.shape[1]  # 缩放后图像宽与当前实际图像的宽的比值self.unscale = 1.0 / self.scale  # self.scale的倒值,用于还原缩放self.boxs = []  # 用于存储分割出来的框

在OpenCV中使用cv2.imread从文件中读入图像

语法:img = cv2.imread(filename[, flags])
参数:filename:要读取的图像的文件名或文件路径。flags(可选):读取图像时的标志。常用的标志有:	cv2.IMREAD_COLOR:以彩色模式加载图像。这是默认值。cv2.IMREAD_GRAYSCALE:以灰度模式加载图像。cv2.IMREAD_UNCHANGED:包括图像的 alpha 通道(如果存在)

为了方便显示,我们首先选择cv2.IMREAD_COLOR读取彩色BGR图片,因为随后的处理过程需要用到灰度图,所以我们需要将彩色图片转换成灰度图。

cv2.cvtColor 用于颜色空间转换。将图像从一个颜色空间转换到另一个颜色空间。

语法:dst = cv2.cvtColor(src, code[, dst[, dstCn]])
参数:src:输入图像,即要进行颜色空间转换的原始图像。code:转换类型。OpenCV 提供了多种预定义的颜色空间转换类型cv2.COLOR_BGR2GRAY:BGR 到灰度图。cv2.COLOR_BGR2HSV:BGR 到 HSV(色相、饱和度、亮度)。cv2.COLOR_BGR2HLS:BGR 到 HLS(色相、亮度、饱和度,与 HSV 类似,但计算方式不同)。cv2.COLOR_HSV2BGR:HSV 到 BGR。cv2.COLOR_GRAY2BGR:灰度图到 BGR(结果图像的每个通道都将具有相同的灰度值)dst(可选):输出图像,即颜色空间转换后的图像。dstCn(可选):目标图像的通道。

有时我们需要处理的图像过大,会影响到处理的速度,所以我们一般需要对图像进行缩放。但是如果直接对图像reshape,会影响到图像的比例导致图像失真,所以我们最好根据比例进行缩放。

我们设置了一个名为scale的变量,来保存我们需要的宽度与图像实际宽度的比,后续可以通过scale与图像的实际宽高相乘获取到等比例缩小后的长和宽。

显示图像

def plot_show(self, image, cmap=None):# 显示图像if cmap is None:plt.imshow(image)else:plt.imshow(image, cmap)plt.show()

在本篇中使用matplotlib显示图像。

按比例缩放

def scaleImage(self, image):# 根据比值来缩放图像im = image.copy()scale=self.scaleim = cv2.resize(im, (int(im.shape[1] * scale), int(im.shape[0] * scale)))return im

这个功能还是很简单的,获取到缩小后的长和宽之后调用cv2.resize对图像比例缩放。

语法:dst = cv2.resize(src, dsize[, fx[, fy[, interpolation]]])
参数:src:输入图像。dsize:目标图像的大小(宽度,高度)。fx(可选):水平轴(宽度)的缩放因子。当 dsize 为(0, 0)时,它才有效。fy(可选):垂直轴(高度)的缩放因子。当 dsize 为(0, 0) 时,它才有效。通常设置为与 fx 相同,以保持图像的纵横比。interpolation(可选):插值方法。常用的插值方法有:cv2.INTER_LINEAR:线性插值(默认)。cv2.INTER_NEAREST:最近邻插值。cv2.INTER_AREA:区域插值(当缩小图像时使用)。cv2.INTER_CUBIC:三次样条插值(当放大图像时使用)。cv2.INTER_LANCZOS4:Lanczos 插值。

黑帽操作

def blackHat(self, image, kernel = np.ones((1, 3), np.uint8)):# 黑帽操作im = image.copy()im = cv2.morphologyEx(im, cv2.MORPH_BLACKHAT, kernel=kernel, anchor=(1, 0))return im
黑帽操作(Black Hat)是形态学操作中的一种常见操作。
原理:	闭运算结果与原始图像之间的差别。闭运算:先膨胀,再腐蚀。膨胀:白色像素占据黑色像素腐蚀:黑色像素占据白色像素
作用:黑帽操作可以得到图像中较亮的小区域,而不考虑更暗的大区域。通常用于提取图像中的大型结构、背景或者光照不均匀造成的影响。

在黑帽操作中,第一步是先膨胀再腐蚀然后与原始图像相减,这个步骤使得处理后的图像白色区域比黑色区域更多,即黑色区域被白色区域侵蚀;第二步是与原本图像相减,经过这个步骤,处理的结果中就只剩下了边缘轮廓的线条,而条形码区域的边缘轮廓线条较为密集,经过这一步骤可以除去图像中的大部分背景。

在OpenCV中cv2.morphologyEx用于执行形态学操作,我们可以通过cv2.morphologyEx实现黑帽操作

语法:dst = cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])参数src:源图像,必须是单通道的灰度图像。op:形态学操作的类型,可以是以下几种之一:cv2.MORPH_ERODE:腐蚀操作。cv2.MORPH_DILATE:膨胀操作。cv2.MORPH_OPEN:开运算(先腐蚀后膨胀)。cv2.MORPH_CLOSE:闭运算(先膨胀后腐蚀)。cv2.MORPH_GRADIENT:形态学梯度(膨胀后减去腐蚀)。cv2.MORPH_TOPHAT:顶帽运算(原图像减去开运算结果)。cv2.MORPH_BLACKHAT:黑帽运算(闭运算结果减去原图像)。kernel:结构元素或卷积核,用于定义形态学操作的结构和大小。它通常是一个奇数大小的二维数组(矩阵)。dst(可选):输出图像,如果未指定,则函数会创建一个新的输出图像。anchor(可选):锚点位置,指定了卷积核的基准点,默认为(-1, -1),表示位于卷积核的中心。iterations(可选):操作迭代的次数,默认为1。对于腐蚀和膨胀操作,增加迭代次数可以加大效果。borderType(可选):像素外推法选择标志,默认为cv2.BORDER_CONSTANT。borderValue(可选):当使用cv2.BORDER_CONSTANT时,此值指定了边界像素的值。

经过这一过程后图像变为
在这里插入图片描述

阈值二值化

def threshold(self, image, low=10, heigh=255):# 根据阈值二值化im = image.copy()thresh, im = cv2.threshold(im, low, heigh, cv2.THRESH_BINARY)return im

经过黑帽操作后的图像显然还不够清晰,我们可以通过阈值二值化操作将图像的像素点变为纯黑和纯白两种颜色。

在opencv中可以使用cv2.threshold完成这一操作

语法:retval, dst = cv2.threshold(src, thresh, maxval, type[, dst])
参数:src:源图像(必须是灰度图像)。thresh:阈值。maxval:用于替换超过阈值的像素的最大值。type:阈值处理的类型。OpenCV 提供了几种不同的类型,其中最常见的有:cv2.THRESH_BINARY:二值化阈值处理(0~maxval)cv2.THRESH_BINARY_INV:反二值化阈值处理(0~maxval)cv2.THRESH_TRUNC:截断阈值处理,如果像素值大于阈值,则像素值设置为阈值。cv2.THRESH_TOZERO:低阈值归零处理,如果像素值小于或等于阈值,则像素值设置为0。cv2.THRESH_TOZERO_INV:高阈值归零处理,如果像素值大于阈值,则像素值设置为0。cv2.THRESH_OTSU(结合此值,函数会计算并选择一个最佳的全局阈值)cv2.THRESH_TRIANGLE(结合此值,函数会计算并选择一个基于三角形方法的最佳全局阈值)dst(可选):输出图像。如果省略,则使用一个新的图像。
返回值:retval:分割的阈值dst:阈值处理后的图像。

经过这一操作,图像变为
在这里插入图片描述

闭操作

def morph_close(self, image, kernel=np.ones((1, 5), np.uint8), iterations=2):# 闭操作im = image.copy()im = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel, iterations=iterations)return im

通过闭操作,先膨胀(白色像素吞噬黑色像素)后腐蚀(黑色像素吞噬白色像素),可以使得条形码分散的各部分合并在一起。

经过操作后图像效果如下
在这里插入图片描述

腐蚀操作

def dilate(self, image, kernel = np.ones((1,3),np.uint8)):# 腐蚀操作im = image.copy()im = cv2.dilate(im, kernel)return im

通过腐蚀操作(黑色像素吞噬白色像素)可以进一步去除非条形码的线条干扰。

经过操作后图像效果如下,在这里效果并不明显,可以再多尝试一些参数组合。
在这里插入图片描述

获取边框

 def getContours(self, image):# 获取边框im = image.copy()im_out = self.image.copy()unscale = self.unscalecontours, hierarchy = cv2.findContours(im, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)if contours != None:for contour in contours:if cv2.contourArea(contour) < 2000:continuerect = cv2.minAreaRect(contour)rect = ((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \(int(rect[1][0] * unscale), int(rect[1][1] * unscale)),rect[2])box = np.int0(cv2.boxPoints(rect))self.boxs.append(box)cv2.drawContours(im_out, [box], 0, (0, 255, 0), thickness=2)return im_out

根据之前的处理结果已经可以看到一些雏形了,接下来我们将直接使用 cv2.findContours直接获取条形码轮廓区域。
cv2.findContours的用法:

语法:contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
参数:image: 源图像,必须是8位单通道二值图像。mode: 轮廓检索模式。可选参数:cv2.RETR_EXTERNAL: 只检索最外层的轮廓。cv2.RETR_LIST: 检索所有的轮廓,但不建立父子关系。cv2.RETR_CCOMP: 检索所有的轮廓,并建立两个级别的关系(即,谁是谁的外部轮廓,谁是谁的孔)。cv2.RETR_TREE: 检索所有的轮廓,并重新建立嵌套的轮廓关系。method: 轮廓近似方法。可以是以下之一:cv2.CHAIN_APPROX_NONE: 存储轮廓的每个点。cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角线段,只留下它们的端点。cv2.CHAIN_APPROX_TC89_L1, cv2.CHAIN_APPROX_TC89_KCOS: 使用 Teh-Chinl 的近似算法。contours: 检测到的轮廓。每个轮廓都是图像边界点的一个点集。hierarchy: 可选的输出向量,其中包含了有关图像拓扑的信息。
返回值:contours和hierarchy。

上述代码中首先获取了条形码所有的轮廓区域,随后遍历所有轮廓区域,通过cv2.contourArea检测轮廓面积大小,当轮廓面积过小时,很显然并不是我们所需的条形码区域,所以我们直接跳过处理。如果大于设定的轮廓面积,使用cv2.minAreaRect获取符合轮廓形状的最小矩形,获取的矩形就是条形码的区域。随后就可以画图了。

cv2.drawContours用法

语法:cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
参数:image:源图像,可以是二值图像或彩色图像。注意,cv2.drawContours 会在图像上直接绘制轮廓。contours:要绘制的轮廓。contourIdx:要绘制的轮廓的索引。如果为 -1,则绘制所有轮廓。color:轮廓的颜色,是一个三元组,表示 BGR颜色。thickness:线条的粗细。如果为负数,则轮廓将被填充。lineType:线条类型。默认cv2.LINE_8(8连通线)。hierarchy:轮廓的层次关系,是一个 NumPy 数组。通常这个参数可以省略。maxLevel:最多绘制的轮廓层次级别。如果未指定,则绘制所有级别。offset:轮廓点相对于图像原点的偏移量。通常这个参数可以省略。

这一步骤后图像效果是蓝色的,因为opencv是BGR格式而matplotlib是RGB格式的
在这里插入图片描述
如果BGR转RGB,结果如下
在这里插入图片描述

流程化

根据之前的步骤我们可以看出,执行的过程是线性的,所以我们可以将代码流程化

def forward(self, image, process_lst):im = image.copy()for i, process in enumerate(process_lst):im = process(im)self.plot_show(im, cmap=plt.cm.gray)return im

其中process_lst是执行过程组成的列表,image是图像,每步执行后调用plot_show显示结果。

def getBarRect(self):self.plot_show(self.image[:,:,::-1])process_lst = [self.scaleImage, self.blackHat, self.threshold, self.morph_close, self.dilate, self.getContours]image = self.forward(self.gray_image, process_lst)image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)self.plot_show(image)

在使用时传入图像路径后调用getBarRect即可完成条码区域区分功能

barcode = barCodes('test.png')
barcode.getBarRect()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/10192.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于fastapi sqladmin开发,实现可动态配置admin

1. 功能介绍&#xff1a; 1. 支持动态创建表、类&#xff0c;属性&#xff0c;唯一约束、外键&#xff0c;索引&#xff0c;关系&#xff0c;无需写代码&#xff0c;快速创建业务对象&#xff1b; 2. 支持配置admin显示参数&#xff0c;支持sqladmin原生参数设置&#xff0c;动…

Istio 使用 Apache SkyWalking 进行服务链路追踪、链路监控告警

一、Istio 使用 Apache SkyWalking 链路追踪和告警 SkyWalking是一个开源的观测平台&#xff0c;用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据&#xff0c;SkyWalking 提供了一种简便的方式来清晰地观测分布式系统&#xff0c;甚至可以观测横跨不同云的系统…

终端安全管理防护软件排行榜2024(四大终端监控软件推荐)

你的企业存在这些问题吗&#xff1f; 数字化转型的深入和远程办公模式的普及&#xff0c;企业对终端安全管理的需求日益凸显。 确保终端设备的安全性不仅关乎数据保护、业务连续性&#xff0c;更直接影响企业的声誉与合规性。 2024年终端安全防护软件排行榜&#xff0c;有谁荣…

【MySQL的内置函数】

文章目录 一、日期函数1.current_date()2.current_time()3.current_timestamp4. date_add 穿越未来5.date_sub 回到过去6.datediff案例 二、字符串函数2.1charset2.2 concat ——拼接字符串2.3 ucase——转化成大写2.4 lcase——转化成小写2.5 left&#xff08;&#xff09;2.6…

树与二叉树之间的转换

树转化成二叉树&#xff1a;兄弟相连留长子 1.加线&#xff1a;在兄弟之间加一条线 2.抹线&#xff1a;对每个结点&#xff0c;除了其左孩子外&#xff0c;去除其与其余孩子之间的关系 3.旋转&#xff1a;以树的根结点为轴心&#xff0c;将整树顺时针转45 二叉树转化成为树…

苹果 iPhone 15 Pro Max 称霸:智能手机市场势不可挡

苹果 iPhone 15 Pro Max 称霸&#xff1a;智能手机市场势不可挡 概述 在拥挤且竞争激烈的智能手机市场中&#xff0c;苹果的 iPhone 15 Pro Max 成为明显的赢家&#xff0c;在 2024 年第一季度最畅销智能手机排行榜上名列前茅。根据 Counterpoint Research 的数据&#xff0c…

将来会是Python、Java、Golang三足鼎立吗?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 Java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 软件工程里没有银弹&#xff…

uniapp、web网页跨站数据交互及通讯

来来来&#xff0c;说说你的创作灵感&#xff01;这就跟吃饭睡觉一样&#xff0c;饿了就找吃的&#xff0c;渴了就倒水张口灌。 最近一个多月实在是忙的没再更新日志&#xff0c;好多粉丝私信说之前的创作于他们而言非常有用&#xff01;受益菲浅&#xff0c;这里非常感谢粉丝…

前端nginx(windows操作系统)学习配置开发验证

Nginx概述 Nginx 作为负载均衡在 Linux 系统上具备很好的并发性能&#xff0c;并且占用极小的内存。但是在 Windows 系统上并不支撑较高并发&#xff0c;所以在Windows系统上选用Nginx作为负载均衡&#xff0c;需要考虑并发情况。 若并发需求低于 300&#xff0c;部署集群仅以…

使用 Valgrind 检测内存泄漏

Valgrind 是一个编程工具&#xff0c;用于内存调试、内存泄漏检测以及性能分析。Valgrind 工具集中的 Memcheck 是用于检测内存管理和线程错误的主要工具。 参考&#xff1a;https://blog.csdn.net/weixin_44046545/article/details/138417524 1、安装 Valgrind sudo apt-ge…

汇昌联信科技:做拼多多网店要押金吗?

做拼多多网店要押金吗?”这个问题&#xff0c;其实与拼多多的平台规则有关。在开店之前&#xff0c;商家需要详细了解平台的各项规定和费用构成&#xff0c;这样才能做好充足的准备。 一、明确回答问题 做拼多多网店&#xff0c;不需要支付押金。拼多多的入驻门槛相对较低&…

【本地部署及云化部署】

文章目录 本地部署及云化部署介绍 文章目录 文章目录一、本地部署模式二、云化部署模式总结 一、本地部署模式 需建设专业化机房&#xff0c;系统应用、前端软件全部安装到本地服务器上。需要专业的IT、网络安全、DBA、电气化工程师进行维护。近些年勒索病毒安全事件频发&am…

k8s设置在任意node里执行kubectl 命令

一、问题 正常来讲kubectl 只能在master node 里运行 当我们尝试在某个 node 节点来执行时&#xff0c; 通常会遇到下面错误 执行错误&#xff1a;The connection to the server localhost:8080 was refused - did you specify the 原因&#xff1a;因为k8s的各个组建&#xf…

安装配置pushgateway

环境 主机名 服务器IP 系统 说明 Ubuntu -1 192.168.1.144 Ubuntu.20.04 docker安装Prometheus docker 192.168.1.140 cent…

KAN核心团队震撼力作!MIT华人用AI首次发现物理学全新方程 | 最新快讯

新智元报道 编辑&#xff1a;Aeneas 好困 刚刚提出了 KAN 的 MIT 物理学家 Max Tegmark 和北大校友刘子鸣&#xff0c;又有一项重磅研究问世了&#xff01;团队发现&#xff0c;它们用 AI 发现了物理学中的新方程&#xff0c;从此&#xff0c;AI 很可能被引入物理学研究领域&am…

东芝移动硬盘是固态还是机械硬盘?数据丢失怎么办

东芝移动硬盘凭借出色的性能和稳定性&#xff0c;在市场上备受赞誉。那么&#xff0c;如何判断自己手中的东芝移动硬盘是固态硬盘还是机械硬盘呢&#xff1f;本文将指导您如何进行这一判断&#xff0c;并深入探讨固态硬盘与机械硬盘之间的区别。同时&#xff0c;针对数据丢失这…

RustDesk 自建服务器部署和使用教程

RustDesk 是一个强大的开源远程桌面软件&#xff0c;是中国开发者的作品&#xff0c;它使用 Rust 编程语言构建&#xff0c;提供安全、高效、跨平台的远程访问体验。可以说是目前全球最火的开源远程桌面软件了&#xff0c;GitHub 星星数量达到了惊人的 64k&#xff01; 与 Team…

【Linux】基础命令,文件处理,用户,vim编辑器,文件压缩

常用命令及参数&#xff1a;dir表示文件夹&#xff0c;file表示文件&#xff08;file可表示其他目录下的文件&#xff09; pwd命令&#xff1b;查看当前所属文件夹&#xff08;print working directory&#xff09; ls [选项] dir&#xff1b;查看当前、指定文件夹目录内容&am…

切换tomcat使用的jdk版本

改一下这俩地方 用这个启动时候 就可以使用对应的jdk版本了 java的classpath内容如下&#xff08;换成自己的&#xff09;&#xff1a; E:\A_code\environment\tomcat\Tomcat9.0\bin\bootstrap.jar;E:\A_code\environment\tomcat\Tomcat9.0\bin\tomcat-juli.jar

存储卡如何下载歌曲?

作为存储芯片及存储卡的原厂&#xff0c;我们了解客户关于如何在存储卡上下载歌曲的疑问。在这篇文章中&#xff0c;我们将详细解析存储卡的使用方法和歌曲下载步骤&#xff0c;帮助客户顺利完成歌曲下载并存储到存储卡中。 1. 选择合适的存储卡 首先&#xff0c;确保您选择的存…