图像的读写与显示

读取图像

使用函数 cv.imread() 来读取图像,该函数共两个参数

  1. 图像的路径
  2. 图像的读取方式
    1. cv.IMREAD_COLOR 加载彩色图像,图像的任何透明度都将被忽略。这是默认标志
    2. cv.IMREAD_GRAYSCALE 以灰度模式加载图像
    3. cv.IMREAD_UNCHANGED 加载图像,包括 alpha 通道

例:

1
2
3
import cv2 as cv

img = cv.imread("1.jpg", cv.IMREAD_COLOR)

警告:如果图像路径错误,它将不会引发任何错误,但是会返回一个空值

显示图像

使用函数 cv.imshow() 在窗口显示图像,窗口自动适合图像尺寸。该函数共两个参数。

  1. 窗口名称
  2. 要显示的图像

可以根据需求创建任意多个窗口,但需要使用不同的窗口名称

例:

1
2
3
cv.imshow('image', img)
cv.waitkey()
cv.destoryAllWindows()

cv.waitkey()等待按键按下功能,其参数是时间(以毫秒为单位),默认为0,即永远等待。

cv.destoryAllWindows() 用来回收我们创建的所有窗口。

保存图像

使用函数cv.imwrite()来保存图像,该函数共两个参数。

  1. 文件名
  2. 需要保存的图像
1
cv.imwrite('2.jpg', img)

图像的基本操作

来源:https://docs.opencv.org/4.1.2/d3/df2/tutorial_py_basic_ops.html

本节中的大部分操作都与numpy相关,因为要操作图像数据,而图像数据就是一个np.ndarray的数组。

注意:在OpenCV中图像都处于GBR模式,使用这种方式的是因为历史遗留问题,在早年大部分的照相机制造商和软件供应商中流行的就是BGR模式, OpenCV为了迎合当时开发者的习惯就是用了BGR模式,然而在当今这个时代RGB又流行了起来,OpenCV已经无法在兼容RGB模式了,所以只能这样将就着使用GBR模式了

访问和修改像素值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
import cv2 as cv

img = cv.imread('1.jpg')
px = img[100, 100]
# [2 6 26]

# 也可以仅接收 rgb的任意一个值
blue = img[100, 100, 0]
# 或
blue, green, red = img[100, 100] # 不想接收的可以用 _ 代替

# 修改像素值
img[100, 100] = 255, 255, 255

警告:Numpy是用于快速数组计算的优化库。因此,简单的访问每个元素并对其进行修改是非常缓慢的,不建议这样使用。

更好的访问像素值和修改像素值的方法

1
2
3
4
5
# 访问 RED 的值
img.item(100, 100, 2)

# 修改 RED 的值
img.itemset((100, 100, 2), 100)

访问图像属性

图像的属性包括:行、列、通道数、图像的数据类型、像素数等等

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

1
2
print(img.shape)
# (1227 690 3)

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

像素总数可以通过img.size进行获取:

1
2
print(img.size)
# 846630

图像数据类型可以通过img.dtype进行获取

1
2
print(img.dtype)
# uint8

注意img.dtype在调试的时候非常重要,因为OpenCV-Python代码中大部分的错误可能都是因为无效数据类型引起的。

图像ROI(Region Of Internest

有时候我们需要使用图像的某个区域,这是可以通过NumPy索引的方式获取ROI

1
2
# 获取图像第220行到500行,250行到620行的图像
img[220:500, 250:620]

注意:如果对裁剪区域内容进行修改,这个值也会反馈裁剪之前的图像数组,因为在NumPy中默认使用的是引用拷贝的方式,所以在需要对特定的区域修改的时候,我们可以将其裁剪出来,然后在更小的区域做处理,这样可以提高我们修改的速度和准确性。

分割和合并图像通道

有的时候需要分别处理图像的G、B、R通道。可以使用以下方法做到这一点:

1
2
3
4
5
6
7
8
# 拆分
b, g, r = cv.split(img)
# 或
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
# 合并
img = cv.merge((b, g, r))

警告cv.split()是一项非常耗时的操作,相比于numpy的索引而言,实测速度大约相差百倍,数据量大的情况下可以到几百倍。

为图像设置边框(填充)

如果要在图像周围创建边框,则可以使用 cv.copyMakeBorder()。但是它在卷积运算,零填充等方便有更多的运用。该函数共7个参数:

  1. src 输入的图像
  2. top 上边界宽度
  3. bottom 下边界宽度
  4. left 左边界宽度
  5. right 右边界宽度
  6. borderType 边界类型
    1. cv.BORDER_CONSTANT 添加恒定的色彩边框
    2. cv.BORDER_REFLECT 边框是边框元素的镜像,例如:fedcba | abcdefgh |hgfedcb
    3. cv.BORDER_REFLECT_101 cv.BORDER_DEFAULT ,与上述基本相同,但是略有变化。例如:gfedcb | abcdefgh | gfedcba
    4. cv.BORDER_REPLICATE 最后一个元素被复制,例如:aaaaaa | abcdefg | gggggg
    5. cv.BORDER_WRAP 例如:cdefgh | abcdefgh | abcdefg
  7. value 颜色值,类型为cv.BORDER_CONSTANT有效

图像上的算术运算

来源:https://docs.opencv.org/4.1.2/d0/d86/tutorial_py_image_arithmetics.html

图像加法

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

注意:OpenCV的加法是饱和运算,而NumPy的加法是模运算,简单的讲就是,当相加的结果超过255的时候,OpenCV的相加的值为255,而NumPy相加的值为其取模的值

图像融合

这也类似于图像的加法,但是对图像赋予的权重不同,以使其具有融合或者透明的感觉。根据以下等式添加图像:
$$
g(x)=(1-\alpha)f_{0}(x)+(\alpha)f_{1}(x)
$$
在 OpenCV 中,我们可以使用cv.addWeighted()函数来达到图像融合的效果,该函数有7个参数:

  1. src1:第一个输入的数组
  2. alpha:第一个数组元素的权重
  3. src2:第二个输入的数组
  4. beta:第二个数组元素的权重
  5. gamma:标量,加到每个元素中。
  6. dst:输出数组
  7. dtype:输出的数据类型

大致可以表示为如下方程式:
$$
dst = \alpha \cdot img_{1} + \beta \cdot img_{2} + \gamma
$$

评论