S1:对图像进行读取和显示
使用函数cv2.imread(filepath,flags)读入一副图片
filepath:要读入图片的完整路径
flags:读入图片的标志
cv2.IMREAD_COLOR:默认参数,读入一副彩色图片,忽略alpha通道
cv2.IMREAD_GRAYSCALE:读入灰度图片
cv2.IMREAD_UNCHANGED:顾名思义,读入完整图片,包括alpha通道
plt.imshow()函数负责对图像进行处理,并显示其格式,而plt.show()则是将plt.imshow()处理后的函数显示出来。
import cv2 as cv
from matplotlib import pyplot as plt
# 对测试数据进行读取和显示
image = cv.imread("C:/Users/tzting/Desktop/BarcodesTutorial-master/BarcodesTutorial-master/img/barcodes.jpg", cv.IMREAD_GRAYSCALE)
plt.imshow(image,interpolation='nearest',cmap='Greys_r')# interpolation代表的是插值运算,'nearest'只是选取了其中的一种插值方式。cmap表示绘图时的样式,这里选择的是Greys_r主题。
plt.title("test_image")
plt.show()
彩色图像:
读入的直接是灰度图像时:
问题来了:
输入的图像红色的,而在python中为蓝色,为什么呢?
然后我又用cv.imshow()来验证
import cv2 as cv
# 对测试数据进行读取和显示
image = cv.imread("C:/Users/tzt/Desktop/BarcodesTutorial-master/BarcodesTutorial-master/img/barcodes.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", image)
cv.waitKey(0)
cv.destroyAllWindows()
发现输出结果为红色
出现上面这个原因是因为 opencv 的接口使用BGR模式,而 matplotlib.pyplot 接口使用的是RGB模式
最后修改完的代码如上图所示,这是中间的小插曲,让我们继续!
S2:二值化处理
通过阈值的设定来提取出我们感兴趣的部分。使用黑帽运算符,我们可以增加较暗的图像元素。首先使用简单的全局阈值安全地对图像进行二值化处理。黑帽运算符使我们可以使用非常低的阈值,而不必过多地关注噪声。
1、黑帽
dst = cv2.morphologyEx(src,op,kernel,anchor,iterations,borderType,borderValue)
例子:image = cv.morphologyEx(image,cv.MORPH_BLACKHAT,kernel,anchor=(1,0))
src: 输入图像对象矩阵,为二值化图像
op: 形态学操作类型
cv2.MORPH_OPEN 开运算:先进行腐蚀操作,后进行膨胀操作,主要用来去除一些较亮的部分,即先腐蚀掉不要的部分,再进行膨胀。
cv2.MORPH_CLOSE 闭运算:先进行膨胀操作,后进行腐蚀操作,主要用来去除一些较暗的部分。腐蚀高亮
cv2.MORPH_GRADIENT 形态梯度:膨胀运算结果减去腐蚀运算结果,可以拿到轮廓信息。
cv2.MORPH_TOPHAT 顶帽运算:原图像减去开运算结果。
cv2.MORPH_BLACKHAT 底帽运算:原图像减去开运算结果。
kernel:进行腐蚀操作的核,可以通过函数getStructuringElement()获得
anchor:锚点,默认为(-1,-1)
iterations:腐蚀操作的次数,默认为1
borderType: 边界种类
borderValue:边界值
参考1:https://www.cnblogs.com/silence-cho/p/11069903.html
2:https://www.jianshu.com/p/fc07d3065cf1
2、图像预处理
threshold()函数原型如下:
double cv::threshold ( InputArray src, OutputArray dst, double thresh, double maxval, int type )
例子:thresh, image = cv.threshold(image, 10, 255, cv.THRESH_BINARY)
InputArray src: 输入图像,可以是Mat类型,图像必须为单通道8位或32位浮点型图像
OutputArray dst: 输出图像,与输入图像尺寸和类型相同
double thresh: 设定的阈值
double maxval: 使用THRESH_BINARY和THRESH_BINARY_INV类型的最大值
int type: 阈值化类型
参考:https://blog.csdn.net/keith_bb/article/details/54617625?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
# 重新定义下图像的大小
scale = 800.0/image.shape[1]
image = cv.resize(image,(int(image.shape[1]*scale),int(image.shape[0]*scale)))
# 黑帽
kernel = np.ones((1,3),np.uint8)
image = cv.morphologyEx(image,cv.MORPH_BLACKHAT,kernel,anchor=(1,0))
# 阈值处理
thresh, image = cv.threshold(image, 10, 255, cv.THRESH_BINARY)
cv.imshow("then_image",image)
result:
先腐蚀再膨胀 其实就是开操作,先膨胀再腐蚀 其实就是闭操作,上面已经定义过!
# operazioni morfologiche 形态运算
kernel = np.ones((1, 5), np.uint8)
image = cv.morphologyEx(image, cv.MORPH_DILATE, kernel, anchor=(-1,-1), iterations=2) # dilatazione膨胀求局部最大值,腐蚀求局部最小值
image = cv.morphologyEx(image, cv.MORPH_CLOSE, kernel, anchor=(-1,-1), iterations=2) # chiusura关闭
cv.imshow("dilate_close_demo",image)
kernel = np.ones((21, 35), np.uint8)
image = cv.morphologyEx(image, cv.MORPH_OPEN, kernel, iterations=1) # 最后的预处理步骤是应用具有很大内核的开运算符,以删除太少而无法适合条形码形状的元素。
cv.imshow("last_demo",image)
S3:检索带有坐标和尺寸的条形码矩形
opencv中通过使用findContours函数,简单几个的步骤就可以检测出物体的轮廓
原型
findContours( InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode, int method, Point offset=Point());
第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
第二个参数:contours,定义为“vector<vector<Point>> contours”,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓有多少轮廓,向量contours就有多少元素。
第三个参数:hierarchy,定义为“vector<Vec4i> hierarchy”,先来看一下Vec4i的定义: typedef Vec<int, 4> Vec4i;
Vec4i是Vec<int,4>的别名,定义了一个“向量内每一个元素包含了4个int型变量”的向量。
所以从定义上看,hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。
向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。
hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第
i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个
轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为
默认值-1。
第四个参数:int型的mode,定义轮廓的检索模式:
取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关
系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,
所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围
内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内
层轮廓还可以继续包含内嵌轮廓。
第五个参数:int型的method,定义轮廓的近似方法:
取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours
向量内,拐点与拐点之间直线段上的信息点不予保留
取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
第六个参数:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值!
参考:https://blog.csdn.net/dcrmg/article/details/51987348#
在上面的代码中,我使用提取的矩形绘制它们,并将其覆盖在原始图像上
# estrazione dei componenti connessi 提取连接组件
image,contours , hierarchy= cv.findContours(image, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
unscale = 1.0 / scale
if contours != None:
for contour in contours:
# se l'area non è grande a sufficienza la salto如果面积不够大,则跳
if cv.contourArea(contour) <= 2000:
continue
# estraggo il rettangolo di area minima (in formato (centro_x, centro_y), (width, height), angolo)我提取最小面积的矩形(格式为(centro_x,centro_y),(宽度,高度),角)
rect = cv.minAreaRect(contour)
# l'effetto della riscalatura iniziale deve essere eliminato dalle coordinate rilevate必须从检测到的坐标中消除初始重新缩放的影响
rect = \
((int(rect[0][0] * unscale), int(rect[0][1] * unscale)), \
(int(rect[1][0] * unscale), int(rect[1][1] * unscale)), \
rect[2])
# disegno il tutto sull'immagine originale我将所有内容绘制在原始图像上
box = np.int0(cv.boxPoints(rect))
cv.drawContours(image_out, [box], 0, (0, 255, 0), thickness=2)
plt.imshow(image_out)
# scrittura dell' immagine finale写最后的形象
cv.imwrite(r'C:/Users/tzt/Desktop/out.png', image_out)
最后运行结果:
后面学的有点仓促,之后在研究!整篇论文是基于https://blog.csdn.net/qq_42722197/article/details/108971078上研究的,谢谢小白学视觉,感觉是个良心公众号哈哈哈!!