使用的方法:imread()
cv.imread(filename[,flags])
参数说明:
filename:要读取文件的名称
flages:
注意 除了这三个标志,你可以分别简单地传递整数1、0或-1。
import cv2 as cv
image_1 = cv.imread('1.png', 1)
image_2 = cv.imread("D:\\learn\\1.png", 1)
3、4行代码效果相同,只不过第四行代码可以读取别的路径下的图片。
使用的方法:imshow()
、waitkey()
、destroyAllWindows()
imshow():显示图像
cv.imshow(winname,mat)
参数说明:
winname:显示图像窗口的名称
mat:要显示的图像
waitKey():等待用户按下键盘按键的时间。当用户按下键盘的任意按键时,将执行waitKey() 方法,并且获取此方法的返回值。
retval = cv.waitKey(delay)
参数说明:
retval:与被按下键相对应的ASCII码。例如ESC键的ACSII码为27,当用户按下ESC键时,waitKey()方法返回值时27,如果不是就返回-1。
delay:等待用户按下键盘上按键的时间。单位为ms,如果值为负数,0或者为空时表示无限等待。
destroyAllWindows():销毁正在显示图像的窗口
cv.destroyAllWindows()
实例:
import cv2 as cv
image = cv.imread('1.png', 1)
cv.imshow("taylor", image)
retval = cv.waitKey()
cv.destroyAllWindows()
print(retval)
运行结果:按下ESC
27
注意:
1️⃣窗口的名称不能为中文,否则会出现乱码。
2️⃣为了能够正常显示图像,
cv.waitKey()
不能省略,否则图像会一闪而过。
补充:
1️⃣cv.destroyWindow()
:销毁指定名称的窗口。
import cv2 as cv
image = cv.imread('1.png', 1)
cv.imshow("taylor", image)
retval = cv.waitKey()
cv.destroyWindow("taylor")
print(retval)
2️⃣在特殊情况下,用户可以先创建一个空窗口,然后再将图像传入窗口。这种情况下可以指定窗口的大小。这是通过cv.nameWindow()
完成的。默认情况下,该标志为cv.WINDOW_AUTOSIZE
,但是如果将标志指定为cv.WINDOW_NORMAL
就可以调整窗口的大小。
实例:
import cv2 as cv
image = cv.imread("1.png")
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.imshow("taylor", image)
cv.waitKey()
cv.destroyAllWindows()
运行结果:窗口是可以用鼠标调整大小的
注意:
代码的第3,4行要显示的窗口名称必须保持一致,否则会出现两个窗口。
使用方法:imwrite()
:可用于按照指定路径保存图像。
cv.imwrite(filename,img)
参数说明:
filename:保存图像的完整路径。
img:要保存的图像。
实例:
import cv2 as cv
image = cv.imread("1.png", 0)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.imshow("taylor", image)
cv.imwrite("D:\\learn\\2.png", image)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
使用方法:shape、size、dtype
1️⃣shape:如果图像是彩色图像,那么获取的是一个由图像的像素列数,行数和通道数组成的数组;
如果图像是灰度图像,那么获取的是一个由图像的像素列数,行数组成的数组。
2️⃣size:获取的是一个包含图像的像素个数,值大小等于像素列数×像素行数×通道数(如果是灰度图像,通道数就是1)。
3️⃣dtype:获取的是图像的数据类型。
import cv2 as cv
image = cv.imread("1.png")
print("图像的色彩属性:")
print("shape = ", image.shape)
print("size = ", image.size)
print("dtype = ", image.dtype)
运行结果:
图像的色彩属性:
shape = (1080, 1920, 3)
size = 6220800
dtype = uint8
图像数字化指的是用数字表示图像。计算机通常会把像素值处理为处理为256个灰度级别,这256个灰度级别分别用区间[0,255]中的数值表示。其中0表示纯黑色,255表示纯白色。
像素是构成数字图像的基本单位。
确定像素的位置
首先要确定此图像水平方向和垂直方向上的像素个数。
在OpenCV中正确表示某个像素坐标的表示方法是(y,x)。
实例:
import cv2 as cv
image = cv.imread("1.png")
px = image[1079, 1919]
获取像素的BGR值
上一小节已经获取了坐标为[1079,1919]上的像素px,那么使用print()
方法打印这个像素得到的就是这个像素BGR值。
import cv2 as cv
image = cv.imread("1.png")
px = image[1079, 1919]
print("这个像素的BGR值为", px)
运行 结果为:
这个像素的BGR值为 [179 177 177]
在了解这三组数字代表的含义之前,先了解一下何为三基色。
人眼能感受到红色、绿色和蓝色三种颜色,把这三种颜色称为三基色,同时将三基色以不同比例混合就可以得到不同的颜色。
那么在计算机中要对颜色进行编码,就要使用到色彩空间。
其中较为常用的就是RGB色彩空间。以此为例,在RGB色彩空间中,有三个颜色通道,分别是:红色R、绿色G和蓝色B。并且每个颜色都在[0,255]之间取值。
不过需要注意的是,OpenCV在输出通道顺序的时候是相反的,是按照BGR输出。
在OpenCV中获取像素的BGR值方法有两种:
import cv2 as cv
image = cv.imread("1.png")
px = image[1079, 1919]
print("这个像素的BGR值为", px)
import cv2 as cv
image = cv.imread("1.png")
print("这个像素的B值为", image[1079, 1919, 0])
print("这个像素的G值为", image[1079, 1919, 1])
print("这个像素的R值为", image[1079, 1919, 2])
运行结果:
参考前文程序输出。
修改像素的BGR值
前一节获得了坐标为[1079,1919]的像素的BGR值,如果要修改BGR值,代码为:
import cv2 as cv
image = cv.imread("1.png")
px = image[1079, 1919]
print("这个像素修改前的BGR值为", px)
px = [255, 255, 255]
print("这个像素修改后的BGR值为", px)
运行结果:
这个像素修改前的BGR值为 [179 177 177]
这个像素修改后的BGR值为 [255, 255, 255]
注意:
如果将每个像素的R、G、B值改为相等大小时,就可以得到灰度图像。全为0时就是纯黑色,全为255时就是纯白色。
如果要修改指定区域中的所有像素就可以使用2层for循环嵌套:
import cv2 as cv
image = cv.imread("1.png", 1)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
for i in range(0, 79):
for j in range(0, 19):
image[i, j] = [0, 0, 0]
cv.imshow("taylor", image)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
⚠️ 警告
文章刚开头介绍了imread()
方法,前一节介绍了如何修改指定区域的元素。
如果图片是以彩色图像 加载的时候,那么修改指定区域像素的颜色,可以直接使用列表[0,0,0]
如果图像不是以彩色图像加载的时候,那么修改指定区域像素的颜色,不能直接使用列表[0,0,0],否则会报错:ValueError: setting an array element with a sequence.
必须要使用数组。
Numpy是用于快速数组计算的优化库。因此,简单地访问每个像素值并对其进行修改将非常缓慢,因此不建议使用。
注意 上面的方法通常用于选择数组的区域,例如前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
除了前文介绍的RGB和BGR色彩空间,还有两个比较常见的色彩空间:GRAY和HSV色彩空间。
1.GRAY色彩空间
GRAY色彩空间通常指的是灰度图像,每个像素都是从黑到白([0,255]).0表示纯黑色,255表示纯白色。
2.从RGB/BGR色彩空间到GRAY色彩空间
使用方法:dst = cv.cvtColor(src,code)
参数说明:
dst:转换后的图像;
src:转换前的初始图像
code:色彩空间转换码
色彩空间转换码 | 含义 |
---|---|
cv.COLOR_BGR2GRAY | 从BGR色彩空间转换到GRAY色彩空间 |
cv.COLOR_RGB2GRAY | 从RGB色彩空间转换到GRAY色彩空间 |
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
image_GRAY = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
cv.imshow("taylor", image_GRAY)
cv.waitKey()
cv.destroyAllWindows()
1️⃣色彩空间转换码还有很多,读者请自行查阅。
2️⃣虽然色彩空间转换是双向的,但是灰度图像是无法转换成彩色图像的。因为彩色图像在转换成灰度图像的时候丢失了颜色比例,并且无法找回。
3.HSV色彩空间
HSV色彩空间是基于色调、饱和度、亮度而言的。
色调 H 指的是光的颜色。在OpenCV中,色调在[0,180]内取值。红、黄、绿和蓝分别是:0、30、60、120。
饱和度 S 指的是色彩的深浅。在OpenCV中,饱和度在[0,255]内取值。饱和度为0时,图像将变为灰度图像。
亮度 V 指的是光的明暗。在OpenCV中,亮度在[0,255]内取值。
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
image_GRAY = cv.cvtColor(image, cv.COLOR_BGR2HSV)
cv.imshow("taylor", image_GRAY)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWW2H1Si-1649246389207)(D:\learn\opencv入门.assets\image-20220406101443885.png)]
1.拆分通道
使用方法:split()
b,g,r = cv.split(bgr_image)
参数说明:
b:B通道图像
g:G通道图像
R:R通道图像
bgr_image:一幅RGB图像
拆分一幅BGR图像通道的顺序是B→G→R,因此等号左边必须是“b, g, r”。
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
b, g, r = cv.split(image)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_B", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_G", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_R", cv.WINDOW_NORMAL)
cv.imshow("taylor", image)
cv.imshow("taylor_B", b)
cv.imshow("taylor_G", g)
cv.imshow("taylor_R", r)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
有的读者会发现得到的B、G、R通道图像是3幅不同灰度的图像。这是因为在cv.imshow("taylor_B",b)
时候,BGR这三通道的值都为B通道的值。其余两个同理。所以只要 B=G=R 就可以得到灰度图像。
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
b, g, r = cv.split(image)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_B", cv.WINDOW_NORMAL)
for i in range(0, 1079):
for j in range(0, 1919):
a = image[i, j]
image[i, j] = a[0]
cv.imshow("taylor", image)
cv.imshow("taylor_B", b)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
拆分一幅图像的HSV通道。只需先将图像先转换为HSV图像即可,后面步骤一致。
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)
h, s, v = cv.split(image_HSV)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_H", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_S", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_V", cv.WINDOW_NORMAL)
cv.imshow("taylor", image_HSV)
cv.imshow("taylor_H", h)
cv.imshow("taylor_S", s)
cv.imshow("taylor_V", v)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7fPJp6E-1649246389208)(D:\learn\opencv入门.assets\image-20220406183300394.png)]
2.合并通道
合并通道是拆分通道的逆过程。
使用方法:bgr = cv.merge([b,g,r])
参数说明:
bgr:按照B→G→R的顺序合并后得到的图像。
b:B通道图像
g:G通道图像
r:R通道图像
如果用户要按照R→G→B顺序合并,调换顺序即可。
实例:
import cv2 as cv
image = cv.imread("1.png", 1)
b, g, r = cv.split(image)
cv.namedWindow("taylor_original", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_merge_BGR", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_merge_RGB", cv.WINDOW_NORMAL)
cv.imshow("taylor_original", image)
image_merge_BGR = cv.merge([b, g, r])
image_merge_RGB = cv.merge([r, g, b])
cv.imshow("taylor_merge_BGR", image_merge_BGR)
cv.imshow("taylor_merge_RGB", image_merge_RGB)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
相信读者已经发现,按照不同的合并顺序得到的图像也是不一样的。
HSV通道合并同理。不多赘述。
3.合理运用拆分通道和合并通道
在HSV色彩空间内,如果保持其中两个通道的值不变,调整第三个通道,会得到相应的艺术效果。
**实例:**只把H通道的值改为180
import cv2 as cv
cv.namedWindow("taylor_H180", cv.WINDOW_NORMAL)
image = cv.imread("1.png", 1)
image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)
h, s, v = cv.split(image_HSV)
h[:] = 180
image_HSV_merge = cv.merge([h, s, v])
image_HSV2BGR = cv.cvtColor(image_HSV_merge, cv.COLOR_HSV2BGR)
cv.imshow("taylor_H180", image_HSV2BGR)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
⁉️疑问:
前一段程序中的第十行做出以下变动:
代码变换前后,输出图片的效果是一样的???
使用
type()
查看 h 数据类型的时候发现其类型是,不是list。
4.alpha通道
前文说明了BGR色彩空间包含了3个通道,在此基础上再加上一个A通道就构成了alpha色彩空间(BRGA色彩空间)。A用于设置图像的透明度。A在区间[0,255]内取值。0表示纯透明。
实例:
import cv2 as cv
cv.namedWindow("taylor_BGRA_A200", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_BGRA", cv.WINDOW_NORMAL)
image = cv.imread("1.png", 1)
image_BRGA = cv.cvtColor(image, cv.COLOR_BGR2BGRA)
b, g, r, a = cv.split(image_BRGA)
a[:] = 200
image_BRGA_A200 = cv.merge([b, g, r, a])
cv.imshow("taylor_BGRA_A200", image_BRGA_A200)
cv.imshow("taylor_BGRA", image_BRGA)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
虽然从输出的图片看不出区别,但是将这两幅图使用imwrite()
就可以看出区别:
实例:
import cv2 as cv
cv.namedWindow("taylor_BGRA_A200", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_BGRA", cv.WINDOW_NORMAL)
image = cv.imread("1.png", 1)
image_BGRA = cv.cvtColor(image, cv.COLOR_BGR2BGRA)
b, g, r, a = cv.split(image_BGRA)
a[:] = 200
image_BGRA_A200 = cv.merge([b, g, r, a])
cv.imshow("taylor_BGRA_A200", image_BGRA_A200)
cv.imshow("taylor_BGRA", image_BGRA)
cv.imwrite("D:/learn/taylor_BGRA_A200.png", image_BGRA_A200)
cv.imwrite("D:/learn/taylor_BGRA.png", image_BGRA)
cv.waitKey()
cv.destroyAllWindows()
运行结果:
array属性:
名称 | 类型 |
---|---|
bool_ | 布尔数据类型 |
int_ | 默认的整数类型,类似C语言中long,int32、int64 |
intc | 和C语言中的int类型一样,一般是int32或者int64 |
intp | 用于索引的整数类型,类似于C中色ssize_t,一般还是int32或int64 |
int8 | 一字节相同的8位整数,-128~127 |
int16 | 两字节(16位),-32768~32767 |
int32、int64 | 不多赘述 |
uint8 | 无符号整数 |
uint16.unit32、uint64 | 不多赘述 |
float_ | float64的简写 |
float16 | 半精度浮点数,包括1个符号位,5个指数位,10个尾数位 |
float32 | 单精度浮点数,包括1个符号位,8个指数位,23个尾数位 |
float64 | 双精度浮点数,包括1个符号位,11个指数位,52个尾数位 |
complex_ | 复数类型,与complex128相同 |
complex64 | 实部和虚部共享32位的数据类型 |
complex128 | 同理 |
str_ | 字符串类型 |
string_ | 字节串类型 |
datatime64 | 日期时间类型 |
timedelta64 | 两个时间之间的间隔 |
每一种数据类型否有对应的数据转换方法
np.int8(3.14)
np.float64(1)
np.float(True)
np.array(object,dtype,copy,order,subok,ndim)
# 创建一维和二维数组
import numpy as np
n1 = np.array([1, 2, 3])
n2 = np.array([[1, 2, 3], [4, 5, 6]])
# 创建浮点类型数组
import numpy as np
list1 = [1, 2, 3, 4, 5]
n1 = np.array(list1, dtype=np.float_)
print(n1)
print(n1.dtype)
print(type(n1), type(n1[0]))
******************************
运行结果:
[1. 2. 3. 4. 5.]
float64
<class 'numpy.ndarray'> <class 'numpy.float64'>
# 创建三维数组
import numpy as np
nd1 = [1, 2, 3, 4, 5]
np = np.array(nd1,ndmin=3)
print(np)
print(np.shape)
print(np.ndim)
*********************
[[[1 2 3 4 5]]]
(1, 1, 5)
3
shape
将是 (n,m)
。因此,shape
元组的长度就是rank或维度的个数 ndim
。# 创建指定维度和数据类型的未初始化数组
import numpy as np
n = np.empty([3, 2], dtype=int)
print(n)
*******************
[[1 2]
[3 4]
[5 6]]
zeros( )
import numpy as np
n = np.zeros([3, 2], dtype=int)
print(n)
import numpy as np
n = np.ones([3, 2], dtype=int)
print(n)
n = np.random.randint(low,high,size)
import numpy as np
n = np.random.randint(1, 10, size=(3, 2))
n
1.加减乘除运算
import numpy as np
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = np.array([[1, 2, 3], [4, 5, 6]])
print(n1 + n2)
print(n1 - n2)
print(n1 * n2)
print(n1 / n2)
************************
[[ 2 4 6]
[ 8 10 12]]
[[0 0 0]
[0 0 0]]
[[ 1 4 9]
[16 25 36]]
[[1. 1. 1.]
[1. 1. 1.]]
2.比较运算
import numpy as np
n1 = np.array([4,5,6])
n2 = np.array([1,2,3])
print(n1 >= n2)
print(n1 == n2)
print(n1 <= n2)
print(n1 != n2)
************************
[ True True True]
[False False False]
[False False False]
[ True True True]
3.复制数组
numpy提供的array( )方法里有copy参数,但是更常用的是n2 = n1.copy()
。修改n2不会影响n1.
import numpy as np
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = n1.copy()
print(n1 == n2)
n2[0][1] = 9
print(n2)
********************
[[ True True True]
[ True True True]]
[[1 9 3]
[4 5 6]]
所谓数组的索引,就是用于标记数组对应元素的唯一数字。从0开始,即数组中的第一个元素索引是0,以此类推。Numpy支持可以使用标准Python语法x[obj]
的语法对数组进行索引,其中x是数组,obj是选择方式。
import numpy as np
n1 = np.array([1, 2, 3])
print(n1[1])
********************
2
和Python列表的索引类似。
import numpy as np
nd1 = [1, 2, 3, 4, 5]
n1 = np.array(nd1)
print(n1)
print(n1[1])
**************
2
二维数组的索引可以使用array[n,m],表示第n个数组的第m个元素。
import numpy as np
nd1 = [1, 2, 3, 4, 5]
n1 = np.array([nd1, nd1])
print(n1, end="\t")
print(n1[1], end="\n")
print(n1[1, 2])
print(n1[1][2])
*************
[[1 2 3 4 5]
[1 2 3 4 5]] [1 2 3 4 5]
3
3
# 二维数组切片式索引
在OpenCV中黑白图像是二维数组,彩色图像是三维数组。数组中每个元素就是图像对应位置的像素值。因此修改图像像素其实就是修改数组。
数组索引、像素索引、像素坐标关系:
数组行索引 = 像素所在行数 - 1 = 像素纵坐标
数组列索引 = 像素所在列数 - 1 = 像素横坐标