2.1. 图像的基本操作

2.1.1. 目标

学会:

  • 访问像素值并修改它们

  • 访问图像属性

  • 图像设置区域(ROI)

  • 分割和合并图像

本节中几乎所有的操作都主要与Numpy相关,而不是OpenCV。使用OpenCV编写更好的优化代码需要对Numpy有很好的了解。

(示例将在Python终端中显示,因为大多数示例只是单行代码)

2.1.2. 访问和修改像素值

Let’s load a color image first:

>>> import cv2
>>> import numpy as np
>>> img = cv2.imread('/cvdata/messi5.jpg')

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

>>> px = img[100,100]
>>> px
array([157, 166, 200], dtype=uint8)
>>> blue = img[100,100,0]
>>> blue
157

可以用同样的方法修改像素值。

>>> img[100,100] = [255,255,255]
>>> img[100,100]
array([255, 255, 255], dtype=uint8)

Warning

Numpy是一个用于快速数组计算的优化库。因此,简单地访问每个像素值并修改它将非常缓慢,这是不可取的。

Note

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

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

>>> # accessing RED value
>>> img.item(10,10,2)
59
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100

2.1.3. 访问图像属性

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

Shape of image is accessed by img.shape. It returns a tuple of number of rows, columns and channels (if image is color): :

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

Note

如果图像是灰度的,则返回的元组只包含行数和列数。因此,它是检测加载图像是灰度图像还是彩色图像的一种好方法。

Total number of pixels is accessed by img.size :

>>> img.size
562248

Image datatype is obtained by img.dtype :

>>> img.dtype
dtype('uint8')

Note

img.dtype 在调试时非常重要,因为OpenCV Python代码中的大量错误是由无效数据类型引起的。

2.1.4. 图像感兴趣区

有时,你会不得不获取图像的某些区域。对于图像中的眼睛检测,首先对图像执行直到面部找到才停止的面部检测,然后再在面部区域内搜索眼睛。这种方法提高了准确性(因为眼睛总在脸上:D)和性能(因为我们搜索的是一个小区域)。

使用Numpy索引再次获得ROI。在这里,我选择球并将其复制到图像中的另一个区域:

>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball
>>> %matplotlib inline
>>> import  matplotlib.pyplot as plt
>>> img_2 = img[:,:,[2,1,0]]
>>> plt.imshow(img)
<matplotlib.image.AxesImage at 0x7fc195f36a58>
../_images/sec01-basic-ops_22_1.png
>>> plt.imshow(img_2)
<matplotlib.image.AxesImage at 0x7fc195ed2b70>
../_images/sec01-basic-ops_23_1.png
>>> plt.imshow(img[:,:,[2,1,0]])
<matplotlib.image.AxesImage at 0x7fc194638470>
../_images/sec01-basic-ops_24_1.png
>>> img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 注意这行。
>>> plt.imshow(img)
<matplotlib.image.AxesImage at 0x7fc19461c5c0>
../_images/sec01-basic-ops_26_1.png
>>> cv2.imwrite('xx_lena_opencv_red.jpg', img)
True

image0

检查以下结果:

2.1.5. 分割和合并图像通道

在需要时,图像的B、G、R通道可以分割成各自的平面。然后,各个通道可以合并在一起,再次形成BGR图像。这可以通过以下方式执行:

>>> b,g,r = cv2.split(img)
>>> img = cv2.merge((b,g,r))

>>> b = img[:,:,0]

假设,你想把所有的红色像素设为零,你不需要单独分割出红色平面/数组,然后将其设为零。您只需使用速度更快的Numpy索引即可。

>>> img[:,:,2] = 0

Warning

cv2.split() 是一个昂贵的操作(就时间而言),所以只有在必要时才使用它。Numpy索引效率更高,应该尽可能使用。

2.1.6. 为图像制作边框(填充)

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

  • src -输入图像

  • topbottomleftright -相应方向上以像素数表示的边框宽度

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

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

    • cv2.BORDER_REFLECT - Border will be mirror reflection of the border elements, like this : fedcbahgfedcb

    • cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT - Same as above, but with a slight change, like this : gfedcbgfedcba

    • cv2.BORDER_REPLICATE - Last element is replicated throughout, like this: aaaaaahhhhhhh

    • cv2.BORDER_WRAP - Can’t explain, it will look like this : cdefghabcdefg

  • value -如果边框类型为 cv2.BORDER_CONSTANT

为了更好地理解,用下面的示例代码来演示所有这些边框类型:

>>> import cv2
>>> import numpy as np
>>> from matplotlib import pyplot as plt
>>>
>>> BLUE = [255,0,0]
>>>
>>> img1 = cv2.imread('/cvdata/opencv-logo.png')
>>>
>>> replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
>>> reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
>>> reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
>>> wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
>>> constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)
>>>
>>> plt.subplot(231),plt.imshow(img1,'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()
../_images/sec01-basic-ops_34_0.png

见下面的结果。(图像用matplotlib显示的,所以红色和蓝色的平面将会互换):

>>> image = plt.imread('/cvdata/opencv-logo.png')
>>> plt.imshow(image)
<matplotlib.image.AxesImage at 0x7fc18f50ee48>
../_images/sec01-basic-ops_37_1.png

2.1.7. 额外资源

2.1.8. 练习