OpenCV 4计算机视觉:Python语言实现(原书第3版)
上QQ阅读APP看书,第一时间看更新

2.2.3 基于numpy.array访问图像数据

我们已经知道在OpenCV中加载图像最简单(也是最常见)的方法是使用imread函数。我们还知道这将返回一幅图像,它实际上是一个数组(是二维还是三维取决于传递给imread的参数)。

numpy.array类对数组操作进行极大的优化,它允许某些类型的批量操作,而这些操作在普通Python列表中是不可用的。这些类型的numpy.array都是OpenCV中特定于数组类型的操作,对于图像操作来说很方便。但是,我们还是从一个基本的例子开始,逐步探讨图像操作。假设你想操作BGR图像的(0,0)坐标处的像素,并将其转换成白色像素:

如果将修改后的图像保存到文件后再查看该图像,你会在图像的左上角看到一个白点。当然,这种修改并不是很有用,但是它显示了某种修改的可能性。现在,我们利用numpy.array的功能在数组上执行变换的速度比普通的Python列表要快得多。

假设你想更改某一特定像素的蓝色值,例如(150,120)坐标处的像素。numpy.array类型提供了一个方便的方法item,它有三个参数:x(或者left)位置、y(或者top)位置以及数组中(x,y)位置的索引(请记住,在BGR图像中,某个特定位置处的数据是一个三元数组,包含按照B、G和R顺序排列的值),并返回索引位置的值。另一个方法itemset可以将某一特定像素的特定通道的值设置为指定的值。itemset有两个参数:三元组(x、y和索引)以及新值。

在下面的例子中,我们将(150,120)处的蓝色通道值从其当前值更改为255:

对于修改数组中的单个元素,itemset方法比我们在本节第一个例子中看到的索引语法要快一些。

同样,修改数组的一个元素本身并没有太大意义,但是它确实打开了一个充满可能性的世界。然而,就性能而言,这只适合于感兴趣的小区域。当需要操作整个图像或者感兴趣的大区域时,建议使用OpenCV的函数或者NumPy的数组切片。NumPy的数组切片允许指定索引的范围。我们来考虑使用数组切片来操作颜色通道的一个例子。将一幅图像的所有G(绿色)值都设置为0非常简单,如下面的代码所示:

这段代码执行了一个相当重要的操作,而且很容易理解。相关的代码行是最后一行,它指示程序从所有行和列中获取所有像素,并把绿色值(在三元BGR数组的一个索引处)设置为0。如果显示此图像,你会注意到绿色完全消失了。

通过使用NumPy的数组切片访问原始像素,我们可以做一些有趣的事情,其中之一是定义感兴趣区域(Region Of Interest,ROI)。一旦定义了感兴趣区域,就可以执行一系列的操作了。例如,可以把这个区域绑定到一个变量,定义第二个区域,将第一个区域的值赋给第二个区域(从而将图像的一部分复制到图像的另一个位置):

确保两个区域在大小上一致很重要。如果大小不一致,NumPy会(立刻)控诉这两个形状不匹配。

最后,我们可以访问numpy.array的属性,如下列代码所示:

这三个属性的定义如下:

·shape:描述数组形状的一个元组。对于图像,它(依次)包括高度、宽度、通道数(如果是彩色图像的话)。shape元组的长度是确定图像是灰度的还是彩色的一种有用方法。对于灰度图像,len(shape)==2,对于彩色图像,len(shape)==3。

·size:数组中的元素数。对于灰度图像,这和像素数是一样的。对于BGR图像,它是像素数的3倍,因为每个像素都由3个元素(B、G和R)表示。

·dtype:数组元素的数据类型。对于每个通道8位的图像,数据类型是numpy.uint8。

总之,强烈建议你在使用OpenCV时,了解NumPy的一般情况以及numpy.array的特殊情况。这个类是Python中使用OpenCV进行所有图像处理的基础。