从零开始的Python图像识别-Week3 图像处理 - Go语言中文社区

从零开始的Python图像识别-Week3 图像处理


学校开设了Python图像识别的课程,想在这里给大家分享一下学习的内容与经历。
本周主要讲解了对图片的一些操作以及一个车牌识别的小练习。

核心操作

图像的基本操作

访问和修改像素值

首先引入我们所需要的库

>>> import cv2 as cv
>>> import numpy as np
>>> from matplotlib import pyplot as plt
>>> %matplotlib inline

然后加载彩色图片

>>> img = cv.imread("img1.jpg");
>>> plt.imshow(img)
>>> plt.show()


(ps:pyplot展示的图片会有亿点点色差(应该是因为cv中图片默认是BGR,而pyplot默认是RGB),建议还是使用cv.imshow()比较好。这里使用pyplot为了方便观察效果。)

我们可以通过行和列坐标来访问像素值。对于 BGR 图像,它返回一个由蓝色、绿色和红色值组成的数组。对于灰度图像,只返回相应的灰度。

>>> px = img[100,100]
>>> print( px )
[157 166 200]
# 仅访问蓝色像素
>>> blue = img[100,100,0]
>>> print( blue )
157

我们可以用相同的方式修改像素值。

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

注意 上面面的方法通常用于选择数组的区域,例如前5行和后3列。对于单个像素访问,Numpy数组方法array.item()array.itemset()被认为更好,但是它们始终返回标量。如果要访问所有B,G,R值,则需要分别调用所有的array.item()

更好的像素访问和编辑方法:

# 访问 RED 值
>>> img.item(10,10,2)
59
# 修改 RED 值
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100

访问图像属性

图像属性包括行数,列数和通道数,图像数据类型,像素数等。

图像的形状可通过img.shape访问。它返回行,列和通道数的元组(如果图像是彩色的):

>>> print( img.shape )
(342, 548, 3)

注意 如果图像是灰度的,则返回的元组仅包含行数和列数,因此这是检查加载的图像是灰度还是彩色的好方法。

像素总数可通过访问img.size获得:

>>> print( img.size )
562248

图像数据类型通过img.dtype获得:

>>> print( img.dtype )
uint8

图像感兴趣区域(ROI)

有时候,我们不得不处理一些特定区域的图像。对于图像中的眼睛检测,首先对整个图像进行人脸检测。在获取人脸图像时,我们只选择人脸区域,搜索其中的眼睛,而不是搜索整个图像。它提高了准确性(因为眼睛总是在面部上:D )和性能(因为我们搜索的区域很小)。

使用Numpy索引再次获得ROI。在这里,我要选择杯子并将其显示出来:

>>> bottle = img[100:300, 360:500]
>>> plt.imshow(bottle)
>>> plt.show()

拆分和合并图像通道

有时我们需要分别处理图像的B,G,R通道。在这种情况下,我们需要将BGR图像拆分为单个通道。在其他情况下,我们可能需要将这些单独的频道加入BGR图片。我们可以通过以下方式简单地做到这一点:

>>> b, g, r = cv.split(img)
>>> plt.imshow(r)
>>> plt.show()


你看看这色差:P

为图像设置边框(填充)

如果要在图像周围创建边框(如相框),则可以使用cv.copyMakeBorder()。但是它在卷积运算,零填充等方面有更多应用。此函数采用以下参数:

  • src - 输入图像

  • topbottomleftright 边界宽度(以相应方向上的像素数为单位)

  • borderType - 定义要添加哪种边框的标志。它可以是以下类型:

  • cv.BORDER_CONSTANT - 添加恒定的彩色边框。该值应作为下一个参数给出。常用

  • cv.BORDER_REFLECT - 边框将是边框元素的镜像,如下所示: fedcba | abcdefgh | hgfedcb

  • cv.BORDER_REFLECT_101cv.BORDER_DEFAULT与上述相同,但略有变化,例如: gfedcb | abcdefgh | gfedcba

  • cv.BORDER_REPLICATE最后一个元素被复制,像这样: aaaaaa | abcdefgh | hhhhhhh

  • cv.BORDER_WRAP难以解释,它看起来像这样: cdefgh | abcdefgh | abcdefg

  • value -边框的颜色,如果边框类型为cv.BORDER_CONSTANT

bottle = img[100:300, 360:500]
replicate = cv.copyMakeBorder(bottle,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(bottle,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(bottle,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(bottle,10,10,10,10,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(bottle,10,10,10,10,cv.BORDER_CONSTANT,value=[255,0,0])
plt.subplot(231),plt.imshow(bottle,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()

效果


图像的算法操作

图像加法

我们可以通过OpenCV函数cv.add()或仅通过numpy操作res = img1 + img2添加两个图像。两个图像应具有相同的深度和类型,或者第二个图像可以只是一个标量值。

注意 OpenCV加法和Numpy加法之间有区别。OpenCV加法是饱和运算,而Numpy加法是模运算

>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]
>>> print( x+y )          # 250+10 = 260 % 256 = 4
[4]

图像融合

这也是图像加法,但是对图像赋予不同的权重,以使其具有融合或透明的感觉。根据以下等式添加图像:

G ( x ) = ( 1 − α ) f 0 ( x ) + α f 1 ( x ) G(x)= (1 - alpha)f_0(x)+ alpha f_1(x) G(x)=(1α)f0(x)+αf1(x)

通过从 α alpha α 0 → 1 0rightarrow1 01 更改,我们可以在一个图像到另一个图像之间执行很酷的过渡。

在这里,我找了两个图像,将它们融合在一起。第一幅图像的权重为0.7,第二幅图像的权重为0.3。cv.addWeighted()在图像上应用以下公式。

d s t = α ⋅ i m g 1 + β ⋅ i m g 2 + γ dst=alpha cdot img1+beta cdot img2 + gamma dst=αimg1+βimg2+γ

在这里, γ gamma γ 被视为零。

img1 = cv.imread('img1.jpg')
img2 = cv.imread('img2.jpg')
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

效果如下:

图片融合

按位运算

这包括按位 ANDORNOTXOR 操作。它们在提取图像的任何部分(我们将在后面的章节中看到)、定义和处理非矩形 ROI 等方面非常有用。

# 加载两张图片
img1 = cv.imread('img1.jpg')
img2 = cv.imread('bottle.jpg')
# 创建ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]

plt.subplot(222),plt.imshow(roi),plt.title("ROI"),plt.xticks([]),plt.yticks([])

# 创建bottle的掩码,并同时创建其相反掩码
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# 现在将ROI中bottle的区域涂黑(扣除)
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# 仅从bottle图像中提取bottle区域
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# 将bottle放入ROI并修改主图像
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
# 预览
plt.subplot(221),plt.imshow(img2),plt.title("BOTTLE"),plt.xticks([]),plt.yticks([])
plt.subplot(223),plt.imshow(mask),plt.title("MASK"),plt.xticks([]),plt.yticks([])
plt.subplot(224),plt.imshow(mask_inv),plt.title("MASK_INV"),plt.xticks([]),plt.yticks([])
plt.show()
# 显示结果
cv.imread("res", img1)
cv.waitKey(0)
cv.destroyAllWindows()

效果:


OpenCV中的图像处理

改变颜色空间

OpenCV中有超过150种颜色空间转换方法。但是我们将研究只有两个最广泛使用的,BGR↔灰色BGR↔HSV

对于颜色转换,我们使用cv函数。cvtColor(input_image, flag),其中flag决定转换的类型。

对于BGR→灰度转换,我们使用标志cv.COLOR_BGR2GRAY。类似地,对于BGR→HSV,我们使用标志cv.COLOR_BGR2HSV。要获取其他标记,只需在Python终端中运行以下命令:

>>> import cv2 as cv
>>> flags = [i for i in dir(cv) if i.startswith('COLOR_')]
>>> print( flags )

注意 HSV的色相范围为[0,179],饱和度范围为[0,255],值范围为[0,255]。不同的软件使用不同的规模。因此,如果你要将OpenCV值和它们比较,你需要将这些范围标准化。

对象追踪

现在我们知道了如何将BGR图像转换成HSV,我们可以使用它来提取一个有颜色的对象。在HSV中比在BGR颜色空间中更容易表示颜色。在我们的应用程序中,我们将尝试提取一个蓝色的对象。方法如下: - 取视频的每一帧 - 转换从BGR到HSV颜色空间 - 我们对HSV图像设置蓝色范围的阈值 - 现在单独提取蓝色对象,我们可以对图像做任何我们想做的事情。

cap = cv.VideoCapture(0)
while(1):
    # 读取帧
    _, frame = cap.read()
    # 转换颜色空间 BGR 到 HSV
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
    # 定义HSV中蓝色的范围
    lower_blue = np.array([110,50,50])
    upper_blue = np.array([130,255,255])
    # 设置HSV的阈值使得只取蓝色
    mask = cv.inRange(hsv, lower_blue, upper_blue)
    # 将掩膜和图像逐像素相加
    res = cv.bitwise_and(frame,frame, mask= mask)
    cv.imshow('frame',frame)
    cv.imshow('mask',mask)
    cv.imshow('res',res)
    k = cv.waitKey(5) & 0xFF
    if k == 27:
        break
cv.destroyAllWindows()

效果:
物体追踪

图像几何变换

变换

OpenCV提供了两个转换函数cv.warpAffine()cv.warpPerspective(),您可以使用它们进行各种转换。cv.warpAffine采用2x3转换矩阵,而cv.warpPerspective采用3x3转换矩阵作为输入。

缩放

缩放只是调整图像的大小。为此,OpenCV带有一个函数cv.resize()。图像的大小可以手动指定,也可以指定缩放比例。也可使用不同的插值方法。首选的插值方法是cv.INTER_AREA用于缩小,cv.INTER_CUBIC(慢)cv.INTER_LINEAR用于缩放。默认情况下,出于所有调整大小的目的,使用的插值方法为cv.INTER_LINEAR。您可以使用以下方法调整输入图像的大小:

img = cv.imread('img1.jpg')
res = cv.resize(img,None,fx=2, fy=2, interpolation = cv.INTER_CUBIC)
#或者
height, width = img.shape[:2]
res = cv.resize(img,(2*width, 2*height), interpolation = cv.INTER_CUBIC)

平移

平移是物体位置的移动。如果我们知道在(x,y)方向上的位移,则将其设为( t x t_x tx, t y t_y ty),我们可以创建转换矩阵 M mathbf{M} M,如下所示:

M = [ 1 0 t x   0 1 t y ] M = begin{bmatrix} 1 & 0 & t_x 0 & 1 & t_y end{bmatrix} M=[10tx 01ty]

我们可以将其放入np.float32类型的Numpy数组中,并将其传递给cv.warpAffine()函数。参见下面偏移为(100, 50)的示例:

img = cv.imread('img1.jpg',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])
dst = cv.warpAffine(img,M,(cols,rows))
cv.imshow('img',dst)
cv.waitKey(0)
cv.destroyAllWindows()

效果:
平移
警告 cv.warpAffine()函数的第三个参数是输出图像的大小,其形式应为(width,height)。记住width =列数,height =行数。

旋转

像旋转角度为 θ

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_44605962/article/details/105784742
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢