OpenCV轻松入门:面向Python
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第3章 图像运算

针对图像的加法运算、位运算都是比较基础的运算。但是,很多复杂的图像处理功能正是借助这些基础的运算来完成的。所以,牢固掌握基础操作,对于更好地实现图像处理是非常有帮助的。本章简单介绍了加法运算、位运算,并使用它们实现了位平面分解、图像异或加密、数字水印、脸部打码/解码等实例。

3.1 图像加法运算

在图像处理过程中,经常需要对图像进行加法运算。可以通过加号运算符“+”对图像进行加法运算,也可以通过cv2.add()函数对图像进行加法运算。

通常情况下,在灰度图像中,像素用8个比特位(一个字节)来表示,像素值的范围是[0,255]。两个像素值在进行加法运算时,求得的和很可能超过255。上述两种不同的加法运算方式,对超过255的数值的处理方式是不一样的。

3.1.1 加号运算符

使用加号运算符“+”对图像a(像素值为a)和图像b(像素值为b)进行求和运算时,遵循以下规则:

式中,mod()是取模运算,“mod(a+b, 256)”表示计算“a+b的和除以256取余数”。

根据上述规则,两个点进行加法运算时:

● 如果两个图像对应像素值的和小于或等于255,则直接相加得到运算结果。例如,像素值28和像素值36相加,得到计算结果64。

● 如果两个图像对应像素值的和大于255,则将运算结果对256取模。例如255+58=313,大于255,则计算(255+58)% 256=57,得到计算结果57。

当然,上述公式也可以简化为a+b=mod(a+b,256),在运算时无论相加的和是否大于255,都对数值256取模。

例3.1】使用随机数数组模拟灰度图像,观察使用“+”对像素值求和的结果。

分析:通过将数组的数值类型定义为dtype=np.uint8,可以保证数组值的范围在[0,255]之间。

根据题目要求及分析,编写程序如下:

        import numpy as np
        img1=np.random.randint(0,256, size=[3,3], dtype=np.uint8)
        img2=np.random.randint(0,256, size=[3,3], dtype=np.uint8)
        print("img1=\n", img1)
        print("img2=\n", img2)
        print("img1+img2=\n", img1+img2)

运行程序,得到如下计算结果:

        img1=
         [[178  83  29]
         [202200158]
         [ 27177162]]
        img2=
         [[ 26  48  57]
         [ 52153   8]
         [ 10232   7]]
        img1+img2=
         [[204131  86]
         [254  97166]
         [ 37153169]]

从上述程序可以看到,使用“+”计算两个256级灰度图像内像素值的和时,运算结果会对256取模。

需要注意,本例题中的加法要进行取模,这是由数组的类型dtype=np.uint8所规定的。

3.1.2 cv2.add()函数

函数cv2.add()可以用来计算图像像素值相加的和,其语法格式为:

        计算结果=cv2.add(像素值a,像素值b)

使用函数cv2.add()对像素值a和像素值b进行求和运算时,会得到像素值对应图像的饱和值(最大值)。例如,8位灰度图像的饱和值为255,因此,在对8位灰度图的像素值求和时,遵循以下规则:

根据上述规则,在256级的灰度图像(8位灰度图)中的两个像素点进行加法运算时:

● 如果两个像素值的和小于或等于255,则直接相加得到运算结果。例如,像素值28和像素值36相加,得到计算结果64。

● 如果两个像素值的和大于255,则将运算结果处理为饱和值255。例如255+58=313,大于255,则得到计算结果255。

需要注意,函数cv2.add()中的参数可能有如下三种形式。

● 形式1:计算结果=cv2.add(图像1,图像2),两个参数都是图像,此时参与运算的图像大小和类型必须保持一致。

● 形式2:计算结果=cv2.add(数值,图像),第1个参数是数值,第2个参数是图像,此时将超过图像饱和值的数值处理为饱和值(最大值)。

● 形式3:计算结果=cv2.add(图像,数值),第1个参数是图像,第2个参数是数值,此时将超过图像饱和值的数值处理为饱和值(最大值)。

上述三种形式将在本章的后续小节中进一步介绍。

例3.2】使用随机数组模拟灰度图像,观察函数cv2.add()对像素值求和的结果。

根据题目要求,编写程序如下:

        import numpy as np
        import cv2
        img1=np.random.randint(0,256, size=[3,3], dtype=np.uint8)
        img2=np.random.randint(0,256, size=[3,3], dtype=np.uint8)
        print("img1=\n", img1)
        print("img2=\n", img2)
        img3=cv2.add(img1, img2)
        print("cv2.add(img1, img2)=\n", img3)

运行程序,得到如下计算结果:

        img1=
         [[136212   1]
         [ 47234  85]
         [197107169]]
        img2=
         [[109212  62]
         [ 19218245]
         [ 19103137]]
        cv2.add(img1, img2)=
         [[245255  63]
         [ 66255255]
         [216210255]]

例3.3】分别使用加号运算符和函数cv2.add()计算两幅灰度图像的像素值之和,观察处理结果。

根据题目要求,编写程序如下:

        import cv2
        a=cv2.imread("lena.bmp",0)
        b=a
        result1=a+b
        result2=cv2.add(a, b)
        cv2.imshow("original", a)
        cv2.imshow("result1", result1)
        cv2.imshow("result2", result2)
        cv2.waitKey()
        cv2.destroyAllWindows()

在本例中,首先读取了图像lena并将其标记为变量a;接下来,使用语句“b=a”将图像lena复制到变量b内;最后,分别使用“+”和函数cv2.add()计算a和b之和。

运行程序,得到如图3-1所示的运行结果,其中:

图3-1 【例3.3】程序的运行结果

● 左图是原始图像lena。

● 中间的图是使用加号运算符将图像lena自身相加的结果。

● 右图是使用函数cv2.add()将图像lena自身相加的结果。

从上述运算结果可以看出:

● 使用加号运算符计算图像像素值的和时,将和大于255的值进行了取模处理,取模后大于255的这部分值变得更小了,导致本来应该更亮的像素点变得更暗了,相加所得的图像看起来并不自然。

● 使用函数cv2.add()计算图像像素值的和时,将和大于255的值处理为饱和值255。图像像素值相加后让图像的像素值增大了,图像整体变亮。