Python OpenCV入门到精通学习日记:像素操作以及色彩空间与通道
前言
昨天以及学过了如何定位像素并获取修改像素的BGR值,还有一些Numpy的基本操作,今天继续学习如何创建图像
像素操作以及色彩空间与通道
1 使用Numpy模块操作像素
1.1 创建图像
在Opencv中,黑白图像实际上就是一个二维数组,彩色图像是一个三维数组。数组中的每个元素就是图像对应位置的像素值。所以,大家可以把修改像素的操作当成修改数组的操作。
1.1.1 创建黑白图像
在黑白图像中,像素值为0表示纯黑,像素值为255表示纯白。
创建一个100行、200列(即宽200、高100)的数组,数组元素格式为无符号8位整数,用0填充整个数组,将该数组当作图像显示出来,
width = 200
height = 100
img = np.zeros((height,width),np.uint8)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行如下:
创建纯白图像的方式有两种:
- 第一种 先纯黑图像,然后将所有的像素值全部都改为255;
- 第二种 使用Numpy提供的
ones()
方法创建一个像素值均为1的数组,然后让数组乘以255;
# 这里用的是第二种办法
img = np.ones((height,width),np.uint8)*255
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
除此之外,我们还可以使用切片式索引操作来修改图像中指定区域的像素,从而达到修改图像内容的效果。
先绘制纯黑图像作为背景,然后使用切片式索引操作将图像中横坐标为50到100、纵坐标为25到75到的矩形区域颜色改为纯白色。
img = np.zeros((height,width),np.uint8)
img[25:75,50:100] = 255
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
学到这里,我们又可以尝试将切片式索引操作引入循环,就可以有接下来的操作:
img = np.zeros((height,width),np.uint8)
for i in range(0,width,40): # 遍历图像的宽度,步长为40
img[:,i:i+20] = 255 # 在图像的每20个像素宽的区域填充白色(255表示白色)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
1.1.2 创建彩色图像
前面说的都是用二维数组表示的黑白图像,而引入三基色的概念后,已经无法用二维数组来表示了,这时,我们需要使用三维数组。而这第三个索引表示的就是B(蓝)G(绿)R(红)3种颜色的分量。
创建彩色图像数组时要将数组创建成三维数组,元素类型仍然为无符号8位整数。创建好表示纯黑图像的三维数组后,复制出3个副本,3个副本分别修改最后一个索引代表的元素值。根据BGR的顺序,索引0表示蓝色分量,索引1表示绿色分量,索引2表示红色分量,让3个副本分别显示纯蓝、纯绿和纯红:
img = np.zeros((height,width,3),np.uint8)
blue = img.copy()
blue[:,:,0] = 255 # 复制黑色图像,然后设置蓝色通道的像素值为255,生成蓝色图像
green = img.copy()
green[:,:,1] = 255 # 复制黑色图像,然后设置绿色通道的像素值为255,生成绿色图像
red = img.copy()
red[:,:,2] = 255 # 复制黑色图像,然后设置红色通道的像素值为255,生成红色图像
cv2.imshow("blue",blue)
cv2.imshow("green",green)
cv2.imshow("red",red)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
这里需要注意设置纯黑图像时np.zeros((height,width,3),np.uint8)
这里有个3,是代表图像的通道数。
1.1.3 创建随机图像
随机图像是指图像中每一个像素值都是随机生成的,因为像素之间不会组成有效的视觉信息,所以这样的图像看上去就像杂乱无章的沙子。虽然随机图像没有任何视觉信息,但对于图像处理技术仍然很重要,毫无规律的像素数组被称为干扰图像的噪声,可以当作图像加密的密钥。
我们可以使用NumPy
提供的random.randint()
方法就可以创建随机数组,将随机值的取值范围设定在0~256(即像素值范围),元素类型设定为无符号8位整数:
# 随机值设置在0到256,单通道的图像
img = np.random.randint(256,size=(height,width),dtype=np.uint8)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
如果想要获得随机的彩色图像,只需要创建三维数组就可以了,这是只需要修改size参数的维度参数:
img = np.random.randint(256,size=(height,width,3),dtype=np.uint8)
运行结果如下:
1.2 拼接图像
NumPy
提供了两种拼接数组的方法,分别是hstack()
方法和vstack()
方法。这两种拼接方法同样可用于拼接图像。
1.2.1 水平拼接数组
hstack()
方法可以对数组进行水平拼接(或叫横向拼接),其语法如下:
array = numpy.hstack(tup)
参数说明:
tup:要拼接的数组元组。返回值说明:
array:将参数元组中的数组水平拼接后生成的新数组。
hstack()
方法可以拼接多个数组,但是被拼接的数组必须在每一个维度都具有相同的长度,也就是数组“形状相同”,例如2行2列的数组只能拼接2行2列的数组,否则会出现错误。
如图所示:
创建3个一维数组,将这3个数组进行水平拼接:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])
result = np.hstack((a, b, c))
print(result)
运行结果如下:
[1 2 3 4 5 6 7 8 9]
1.2.2 垂直拼接数组
用法就不用多说了,和水平拼接数组的方法是一样的,函数不同而已。
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([7, 8, 9])
result = np.vstack((a, b, c))
print(result)
运行结果如下:
[[1 2 3]
[4 5 6]
[7 8 9]]
1.2.3 在图像处理中的应用
在OpenCV中,图像就是一个二维或三维的像素数组,这些数组同样可以被NumPy拼接。
读取一幅图像,让该图像拼接自身图像,分别用水平和垂直2种方式拼接:
img = cv2.imread("img.png")
img_h = np.hstack((img,img))
img_v = np.vstack((img,img))
cv2.imshow("img_h",img_h)
cv2.imshow("img_v",img_v)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
2 色彩空间与通道
色彩是人们对不同光线的主观感知,为了表示不同的光线的色彩,人类建立了多种的色彩模型,我们将和谐色彩模型成为色彩空间。OpenCV中的BGR色彩空间有3个通道,即表示蓝色的B通道、表示绿色的G通道和表示红色的R通道。我们接着学色彩空间和通道,以及二者之间的紧密联系。
上次的流程图捣鼓了半天还不好看,不想画流程图了,这里引用书上的内容:
2.1 色彩空间
在前文我们已经说过了大多数工具都把一副彩色图像的色彩空间默认为RGB,但是Opencv默认为BGR,原因不再赘述。在这里,我们继续介绍另外两种常见的色彩空间:GRAY色彩空间和HSV色彩空间
2.1.1 GRAY色彩空间
- 什么是GRAY色彩空间?
GRAY色彩空间通常指的是灰度空间,灰度空间是一种每个像素都是从黑到白,被处理为256个灰度级别的单色图像。这256个灰度级别分别用区间[0, 255]中的数值表示。其中,“0”表示纯黑色,“255”表示纯白色,0~255的数值表示不同亮度(即色彩的深浅程度)的深灰色或者浅灰色。因此,一幅灰度图像也能够展现丰富的细节信息。
- 从BGR色彩空间转换到GRAY色彩空间
OpenCV能够将同一幅图像从一个色彩空间转换到另一个色彩空间。在Opencv中有用于转换图像色彩空间的函数:cvtColor()
,语法如下:
dst = cv2.cvtColor(src, code)
参数说明:
dst:转换后的图像。
src:转换前的初始图像。
code:色彩空间转换码。
当图像从BGR色彩空间转换到GRAY色彩空间时,常用的色彩空间转换码是cv2.COLOR_BGR2GRAY。
import cv2
img = cv2.imread("img.png")
cv2.imshow("img",img)
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",gray_img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
注意:虽然色彩空间类型转换时双向的,而且Opencv也提供了转换码,但是灰度图像是无法转换为彩色图像的,这是因为在彩色图像转换为灰度图像的过程中,丢失了颜色比例。这些比例一旦丢失就再也找不回了。
2.1.2 HSV色彩空间
- 什么是HSV色彩空间?
BGR是基于三基色而言的,而HSV是基于色调,饱和度和亮度而言的。
其中,色调(H)是指光的颜色。色调在区间[0,180]内取值。代表红色、黄色、绿色和蓝色的色调值分别为0、30、60和120。饱和度(S)是指色彩的深浅,饱和度在区间[0,255]内取值。值得注意的是,当饱和度为0时,图像将变为灰度图像。亮度(V)是指光的明暗。与饱和度相同,在OpenCV中,亮度在区间[0, 255]内取值。亮度值越大,图像越亮;当亮度值为0时,图像呈纯黑色。
- 从BGR色彩空间转换到HSV色彩空间
OpenCV提供的cvtColor()
方法不仅能将图像从BGR色彩空间转换到GRAY色彩空间,还能将图像从BGR色彩空间转换到HSV色彩空间。当图像在BGR色彩空间和HSV色彩空间之间转换时,常用的色彩空间转换码是cv2.COLOR_BGR2HSV
和cv2.COLOR_HSV2BGR
。
hsv_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.imshow("HSV",hsv_img)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
2.2 通道
我们继续学习如何拆分和合并BGR通道。
2.2.1 拆分通道
为了拆分图像的通道,Opencv提供了split()
方法。
- 拆分BGR图像中的通道
split()
方法的语法如下:
b, g, r = cv2.split(bgr_image)
这参数应该比较简单,最后得到的也就是三个通道的图像。
b,g,r = cv2.split(img)
cv2.imshow("b",b)
cv2.imshow("g",g)
cv2.imshow("r",r)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
这里当时我有些疑问,为什么获得的是灰度图像呢?
原因是当程序执行到cv2.imshow("B", b)
时,原图像B、G、R这3个通道的值都会被修改为B通道图像的值,即(b, b, b),另外两个通道同理。前面我们说过,对于BGR图像,只要B、G、R这3个通道的值都相同,就可以得到灰度图像。
- 拆分一幅HSV图像中的通道
我们还是使用split()
,但是我们要获取的是HSV图像的通道,所以要获取的参数也就变成了h,s,v。
h,s,v = cv2.split(hsv_img)
cv2.imshow("h",h)
cv2.imshow("s",s)
cv2.imshow("v",v)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
2.2.2 合并通道
合并通道是拆分通道的逆过程。虽然拆分通道后可以得到不同亮度的灰度图像,但是将图像重新合并,是否能得到原来的图像呢?合并图像我们使用merge()
方法。
- 合并B通道图像、G通道图像和R通道图像
merge()
的语法如下:
bgr = cv2.merge([b, g, r])
参数不再赘述。
bgr = cv2.merge([b,g,r])
cv2.imshow("BGR",bgr)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
- 合并H通道图像、S通道图像和V通道图像
和合并B通道图像、G通道图像和R通道图像的方法一样,这里直接给出代码
hsv = cv2.merge([h,s,v])
cv2.imshow("HSV",hsv)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下:
根据上面的内容,我们可以发现,当将三幅不同亮度的灰度图像重新合并后,又可以重新的到原来的图像
2.2.3 alpha通道
BGR色彩空间包含了3个通道,即B通道、G通道和R通道。OpenCV在BGR色彩空间的基础上,又增加了一个用于设置图像透明度的A通道,即alpha通道。这样,形成一个由B通道、G通道、R通道和A通道4个通道构成的色彩空间,即BGRA色彩空间。在BGRA色彩空间中,alpha通道在区间[0, 255]内取值;其中,0表示透明,255表示不透明。
从BGR色彩空间转换到BGRA色彩空间;然后拆分BGRA图像中的通道;接着把BGRA图像的透明度调整为172后,合并拆分后的通道图像;再接着把BGRA图像的透明度调整为0后,合并拆分后的通道图像;最后分别显示BGRA图像、透明度为172的BGRA图像和透明度为0的BGRA图像。
bgra_img = cv2.cvtColor(img,cv2.COLOR_BGR2BGRA)
cv2.imwrite("BGRA.png",bgra_img)
b,g,r,a = cv2.split(bgra_img)
a[:,:] = 172
bgra_172 = cv2.merge([b,g,r,a])
a[:,:] = 0
bgra_0 = cv2.merge([b,g,r,a])
cv2.imwrite("172.png",bgra_172)
cv2.imwrite("0.png",bgra_0)
在这里要注意,这里不能直接展示,需要将图像保存为.png格式的图像文件,透明度才会有所变化,这是因为PNG图像是一种典型的4通道(即B通道、G通道、R通道和A通道)图像,因此被保存的3幅图像的格式均为.png,不可以使用.jpg,那样的话图像不会有所变化。
运行结果如下:
小结
今天把昨天没有学完的内容看完了,学习了如何拼接图像和处理图像,值得注意的是彩色灰度图像不可以转换,对于HSV色彩空间,如果保持其中两个通道值不变,调整第三个值会有不同效果,不过为了展示效果,需要将其中HSV转换为BGR才行,还有就是为了能直观看见透明效果,需要保存图像为.png图像文件。
明天开始学习绘制图像和文字!!!