1: 应用场景
在实际应用中,OpenCV可以用于二维码检测的场景有很多,例如:
自动化测试:在UI自动化测试中,可以用OpenCV检测二维码图像,以确保二维码正确生成且无损坏。
物流跟踪:在物流环节中,可以用二维码跟踪包裹的位置,通过手机摄像头扫描二维码,OpenCV可以帮助识别二维码并获取其内容。
无人机或者机器人:在无人机或者机器人的视觉导航中,OpenCV可以帮助识别二维码并导航到正确的位置。
图像识别服务:可以将二维码识别作为一个图像识别服务,通过手机或其他设备的摄像头实时检测和识别二维码。
2: 基本流程步骤既原理
在OpenCV中,二维码检测可以通过以下步骤完成:
读取图像。
转换为灰度图像。
应用阈值操作或边缘检测以强化二维码的对比度。
使用一种二维码检测器(如QRCodeDetector
)。
检测并绘制二维码的位置。
3:掌握基本实现函数
首先完成cv_2环境配置,配置代码如下,终端输入安装即可
conda create --name cv_env python=3.8
conda activate cv_env
pip install opencv-python
接下来认识几个常用函数
IMREAD_READ:读取图像函数
IMREAD_COLOR:读取彩色图像cvtColor:转换颜色
COLOR_BGR2GRAY:转换为灰色图像
threshold:阈值函数
THRESH_BINARY:二值化处理
THRESH_BINARY_INV:反二值化处理
waitkey:设置等待时间
destroyAllwindows:设置销毁窗口
resize:改变图片大小
GaussianBlur:高斯过滤波函数
medianBlur:中间值过滤波函数
Blur:均值过滤波函数
Canny:算子边缘检测函数
sobel:算子边缘检测函数
addweighted:将两张图片进行权重混合实现图像的透明叠加和特效处理
findContours:轮廓检测算法查找边缘检测出的轮廓
contourArea:计算轮廓面积
drawContours:画出定位好的轮廓
morphologyEx:形态学变换
contourArea:计算轮廓面积
boundingRect:轮廓拟合
4: 代码实现
如下可以看到一个二维码,常出现于生活中的支付码,那接下来一起按流程代码实现
不妨我们先将如下需检测图片命名为QR_code.png
1:获取原图像
import cv2
img = cv2.imread('QR_code.png')
cv2.imshow('img',img)
2: 灰度化处理
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray_img',gray_img)
灰度化最直接的原因:减少计算量
包含色彩的图片,特征量,计算量会成指数倍数增加
比如一个点,灰度的话,就256个维度而已,但是如果算上RGB色彩的话,那就是1600万以上维度。然后再相互组合,或者说找梯度,可以想象看计算量有多大
但是好在就算是全色盲也可以分辨物体,对于一般的物体识别灰度图就够了,于是就先降维(灰度)来计算
不过一直觉得不是每个应用都需要把彩色变成灰度,毕竟颜色是一个比较有用的特征
首先 灰度化之后失去了什么,又得到了什么
灰度化之后颜色信息丢失,所以一般基于color-based算法就不能灰度化
但是很多简单的识别算法对于颜色的依赖性不强,hand-craft特征更多关注边缘梯度信息
灰度化之后矩阵维数下降,运算速度大幅度提高,并且梯度信息仍然保留
工程中很多应用加上color信息之后鲁棒性会下降
怎么选择就是在performance和efficiency之间做一个权衡罢了
是否选择灰度图主要需要权衡以下几点:
1. 梯度
我们识别物体,最关键的因素是梯度(现在很多的特征提取HOG,LBP,SIFT等等本质都是梯度的统计信息),梯度意味着边缘,这是最本质的部分,而计算梯度,最常用就是灰度图。颜色本身,非常容易受到光照等因素的影响,同类的物体颜色有很多变化,所以颜色本身难以提供关键信息
2.计算量
灰度化其实就是将图片降维,这样就能大大降低计算量
对于设备的计算能力和识别速度的需求是你取舍的一个关键点
有时候灰度图还是过大,再次降维使用二值化图像
3.颜色信息
现在大部分的彩色图像都是采用RGB颜色模式,处理图像的时候,要分别知对RGB三种分量进行处理,实际上RGB并不能反映图像的形态特征,只是从光学的原理上进行颜色的调配
对于一些场景,颜色所能提供的信息并不多
比如大多数的医学图像RGB提供的信息量很少(几乎没有),所以可以直接灰度图来进行后续计算
但是也并不绝对,颜色有时候提供的信息也很重要
比如分辨红绿灯的话,是绝对不能直接只用灰度图的
3: 使用滤波进行平滑和降噪
gauss_img = cv2.GaussianBlur(gray_img,ksize=(9,9),sigmaX=0)
median_img = cv2.medianBlur(gauss_img,ksize=5)
blur_img = cv2.blur(gauss_img,ksize=(5,5))
cv2.imshow('gauss_img',gauss_img)
cv2.imshow('median_img',median_img)
cv2.imshow('blur_img',blur_img)
1: 平滑,降噪和滤波三者之间的关系
平滑是效果,降噪是目的,滤波是手段。
通过滤波可以实现平滑的效果,比如高斯滤波,但是也可以用来增强图像,突出边缘,比如拉普拉斯滤波、张量扩散、引导图像滤波等。
除了滤波,平滑的效果还可以通过在频域中,对高频分量的衰减实现。
平滑的效果不一定是要达到去噪的目的,还可以用来磨皮,产生朦胧美等。
去噪的方法应该能够保持边缘、细节、纹理,使其不被平滑掉。
2: 下采样原理
1、什么是下采样
下采样也称降采样。对于一幅图像,进行缩小,称为下采样。
2、下采样的目的
(1)使得图像符合显示区域的大小
(2)获得需要的图像缩略图
3、下采样的原理
对于一副图像I,尺寸为MN,对其进行s倍下采样,即得到(M/s)(N/s)尺寸的分辨率图像,s是M和N的公约数。如果图像是矩阵的形式,那么就是把原始图像I中的s*s窗口内的图像变成一个像素值,这个像素值一般为窗口内所有像素的均值。
高斯金字塔的下采样原理,采用的是删除偶数列和行。
4、下采样的特点
(1)下采样是不可逆的,下采样丢失的信息无法通过上采样来恢复。
(2)下采样会使图像变得模糊。
3: 上采样原理
1、什么是上采样
上采样也称图像插值。对一副图像进行放大,即为上采样。
2、上采样的目的
(1)适应高分辨率
(2)获得需要的放大图
3、上采样的原理
几乎都是采用内差值的方法,在原有图像像素的基础上在像素点之间采用合适的差值算法插入新的元素。
拉普拉斯金字塔的获得过程中,对下采样的图像进行上采样,采用的是各方向放大后,补充0值,然后卷积获得该出的像素值。
4、上采样的特点
上采样后图像也会发生模糊,因为采样过程中丢失了信息。
4: 图像二值化
ret,thr = cv2.threshold(gray_img,50,255,cv2.THRESH_BINARY)
cv2.imshow('thr',thr)
1:二值化原理
图像二值化是指将指将256阶的灰度图通过合适的阈值,转换为黑白二值图。即像素或0和255。其目的通常为将图像的前后景进行分割,使图像变得简单,数据量减小,能凸显出感兴趣的目标的轮廓。主要用于目标物体检测,目标跟踪、物体分离等领域
5: 边缘查找
contours,hierarchy = cv2.findContours(thr,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
findContours
函数是OpenCV库中一个非常重要的函数,它用于检测图像中的轮廓。该函数的主要原理是通过查找图像中的一阶导数变化来识别轮廓。
参数说明:
-
image: 输入的二值图像,黑白色彩模式。
-
mode: 轮廓的检索模式。
-
method: 轮廓的近似方法。
返回值:
-
contours: 检测到的轮廓列表。
-
hierarchy: 每个轮廓的父轮廓、子轮廓及同级轮廓的索引。
-
6: 查找计算并挑选轮廓
接下来 6 7 为了大家方便理解,我将注释写入代码
'定义一个空列表用于储存筛选面积后的目标轮廓'
count_list = []
'循环枚举遍历所有查找出来的边缘轮廓'
cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel=())
for i,cont in enumerate(counts):
'筛选查找出来的边缘轮廓,挑出存在且符合:子类轮廓外,套用父类轮廓的边缘'
if hir[0][i][2] != -1 and hir[0][i][3] != -1:
'循环计算出边缘轮廓的类型'
area = cv2.contourArea(cont)
print(area)
'筛选出:符合面积大小的目标轮廓'
min_area = 30000
'判断条件:大于目标面积的轮廓加入'
if area>min_area:
count_list.append(cont)
'在原图上画出符合上述条件的轮廓'
cv2.drawContours(img,count_list,-1,(0,255,0),3)
7:利用子轮廓坐标定位父轮廓
'定义一个空列表:用于储存上述用面积筛选出轮廓的二次坐标轮廓筛选'
shape_list = []
'循环遍历枚举上述通过面积符合条件的轮廓'
for index,new_count in enumerate(count_list):
'定义x,y坐标点,宽度和高度,利用boundingRect计算并返回一个点集的边界矩形'
x,y,weight,high = cv2.boundingRect(new_count)
print(x,y)
'''
30 572
499 104
30 104
'''
'列表输出自动由下标从0排序,上述输出可得筛选出所有轮廓的坐标'
if index == 0:
'30,572可得轮廓在左下方'
shape_list.append((x,y+high))
if index == 1:
'499,104可得轮廓在右上方'
shape_list.append((x+weight,y))
if index == 2:
'30,104可得轮廓靠近原点在左上方'
shape_list.append((x,y))
'由条件可得定位最外层父类轮廓至少需两个对角坐标,故选对角左下和右上定位,在原图上画出符合上述条件的轮廓'
cv2.rectangle(img,shape_list[0],shape_list[1],(0,0,255),3)
8: 画出轮廓运行图片并设置等待时长后销毁窗口
'展示在原图上画出轮廓的图片'
cv2.imshow('img',img)
'设置等待时间'
cv2.waitKey(0)
'运行结束设置销毁窗口结束运行'
cv2.destroyAllWindows()
9:展示结果
那最后到这里,不知道大家对二维码检测又加深了多少印象呢,也许大家远超出自己预期都已经掌握,自己跃跃欲试了呢!
那今天的分享到此结束,我是MR.LEI,大家下期再见!