图像上的算术运算

图像融合

  • 图像融合即也是图像加法,但它是对每个图像乘以相应的权重,使其具有融合或透明的感觉。根据以下等式进行运算。

  • f(x)可作为输入的图像,则上式可变为:

  • 在OpenCV中进行图像融合的函数为cv.addWeighted(img1,$\beta$,img2,$\alpha,\gamma$),其有四个参数,img1与img2分别为输入的图像,$\beta,\alpha$分别为各自图像融合时所占的权重,一般情况下$\gamma$为0。

    以下为测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import  cv2 as cv
    import numpy as np
    import matplotlib.pyplot as plt
    img1=cv.imread(r'D:\Python\image_processing\exercise\save.png')
    img2=cv.imread(r'D:\Python\image_processing\exercise\imori.jpg')

    dst1=cv.addWeighted(img1,0.8,img2,0.2,0)
    dst2=cv.addWeighted(img1,0.2,img2,0.8,0)
    images=[dst1,dst2]
    titles=['dst1','dst2']


    for i in range(2):
    images[i]=cv.cvtColor(images[i],cv.COLOR_BGR2RGB)
    plt.subplot(1,2,i+1)
    plt.imshow(images[i],'jet')
    plt.title(titles[i],fontsize=16)
    plt.xticks([]),plt.yticks([])

    plt.show()

    代码解读:

  • 在这里要注意到,使用Matplotlib显示图片时,要先将图片转换为RGB形式,在OpenCV中,图像是以BGR通道存储的,直接显示会出现问题。

  • 上述测试代码里的img1为img2的灰度图,可以发现当$\beta$取0.8,$\alpha$取0.2时,img1融合时占比较高,当$\beta$取0.2,$\alpha$取0.8时,二者的对比结果如下图所示:

    4

    可以发现前者更加偏灰度,后者更加偏向原图。

按位运算

  • 有时候,我们不想将图像相加或者融合,例如,下面这张图片:

    8

    它就是由两张图片——(西电LOGO和人物图)进行“掩盖”合成的。

  • 按位操作分为以下四种:AND,OR,NOT,XOR,一一介绍它们的运算:

    1. AND:即“与”运算,(1&1=1,1&0=0,0&1=0,0&0=0),其具体意义可理解为,只有两值都大于0,其为真。而在像素中,其取值在0~255之间,0为黑色,255为白色,而这里大于0的像素值都可以看作1。在两个都大于0的像素值之间进行AND运算,其结果取较小的像素值!

    2. NOT:即“非”运算,(~1=0,~0=1),其具体意义可理解为,取像素的相反值,则当图像为二值图时,原图的黑色变为白色,白色变为黑色。

    3. OR:即“与”运算,(1|1=1,1|0=1,0|1=1,0|0=0),其具体意义可理解为,只有当两值都为0,其为假。而对于两像素值,其先进行二进制转换,后对其进行运算,例如3|5:00000011|00000101=00000111,因此3|5=7。

    4. XOR:即“异或”运算,(1^1=0,1^0=1,0^1=1,0^0=0),其具体意义可理解为,只有当两值不相同时,其为真。对于两两像素值,先进行二进制转换,后对其进行运算,例如3^5:00000011^00000101=00000110,因此3^5=6。

  • OpenCV中,进行按位运算的有以下四个函数:cv.bitwise_not,cv.bitwise_and,cv.bitwise_or,cv.bitwise_xor,这四个函数即对应上面的运算规则。下面将以下两张图片合成为上图:

    6

    7

    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import cv2 as cv
    import numpy as np
    import matplotlib.pyplot as plt

    img1=cv.imread(r'D:\Python\image_processing\exercise\xidian1.jpg')
    img2=cv.imread(r'D:\Python\image_processing\exercise\girl.jpg')
    row,cols,channels=img1.shape
    roi=img2[0:row,0:cols]

    img1_gray=cv.cvtColor(img1,cv.COLOR_BGR2GRAY)
    ret,dst1=cv.threshold(img1_gray,200,255,cv.THRESH_BINARY_INV)
    dst1_inv=cv.bitwise_not(dst1)

    img2_bg=cv.bitwise_and(roi,roi,mask=dst1_inv)
    img1_bg=cv.bitwise_and(img1,img1,mask=dst1)

    dst=cv.add(img1_bg,img2_bg)
    img2[0:row,0:cols]=dst

    cv.imshow('images',img2)

    cv.waitKey(0)
    cv.destroyAllWindows()

    代码解读:

    • 首先由shape得到img1(LOGO)图像的大小,即行数和列数。由此,可以在img2图像上,通过numpy直接划分出与img1图像一样大小的 roi图像(为原图的左上角)。

    • 通过threshold函数可将img1图像二值化,而在这之前需要将img1图像转换为灰度图,才能将其传入。使用 THRESH_BINARY_INV方法将其二值化,二值化图像如下:

      8

      将得到的二值化图像(dst1)利用“非”运算,得到的图像如下:

      9

      可以发现,进行“非”运算后,对二值图来说,其实即进行了颜色反转。


阈值分割

固定阈值分割

  • 阈值分割简单来说,即大于阈值的变成一种值,小于阈值的为另一种值

  • 在python的cv2库中实现固定阈值分割的为cv2.threshold()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

img = cv.imread(r'D:\Python\image_processing\exercise\imori.jpg',0)
ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret,th2=cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
ret,th3=cv.threshold(img,127,255,cv.THRESH_TOZERO)
ret,th4=cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
ret,th5=cv.threshold(img,127,255,cv.THRESH_TRUNC)

titles=['Org','Binary','Binary_inv','Tozero','Tozero_inv','Trunc']
images=[img,th1,th2,th3,th4,th5]

for i in range(6):
plt.subplot(2,3,i+1)
plt.imshow(images[i],'gray')
plt.title(titles[i],fontsize=16)
plt.xticks([]),plt.yticks([])

plt.show()

运行结果如图所示:

1

  • cv2.threshold()函数由4个参数组成,img为传入函数的图像,127为设置的阈值大小(threshold),255为阈值设定方式里的阈值最大值(maxval),THRESH_BINARY为阈值设定的方式。

  • ret代表当前的阈值。

  • matplotlib.pyplot中subplot(2,3,i+1)即为将窗口分为2行3列,i+1表示当前的第i+1个子图,imshow()则是对图像进行处理,它不会让图片进行显示,图像显示需要show()

  • 阈值设定有5种方法,分别为:

    1.THRESH_BINARY:

当原像素值大于阈值,原像素值变为maxval,除此之外为0。

2.THRESH_BINARY_INV:

该阈值方法与上述阈值方法相反,从图像也可以观察得到。

3.THRESH_TOZERO:

​ 当原像素值大于阈值时,原像素值保持不变,除此之外,原像素值变为0。

4.THRESH_TOZERO_INV:

​ 该阈值方法与上述阈值方法相反。

5.THRESH_TRUNC:

​ 放原像素值大于阈值,则原像素值变为阈值,除此之外,原像素值保持不变。