目录
引言:数字图像处理中,对图像的像素进行更改,则涉及到一些运算问题,这篇文章的重点在位逻辑运算中,通过位逻辑运算,我们可以发现一些神奇之处,利用位运算我们可以实现水印的添加,图像的加密解密,还可以通过为逻辑运算提取出我们感兴趣的图像的局部图像。下面进入正题。
(一)、图像加法运算
图像的加法运算可以用‘+’号进行操作,也可以用opencv库中的cv2.add()函数进行操作,但是通过这两种方法得到的图像的运算结果却有不同之处。除此之外再介绍一下cv2.addWeighted()函数的用法。
假如我们操作的图像是8比特位。也就是每个像素的取值在[0, 255]之间。
如果用‘+’号进行图像像素的加法操作,如果相加之后的结果大于255,则从0重新开始计算,也就是对255进行取余运算。
如果用cv2.add()函数进行加法操作,如果相加之后的结果大于255,则统一赋值为255。
我们通过程序来看一下。
import cv2
import numpy as np
# 声明两个随机数组,大小为4*4
img1 = np.random.randint(0, 255, (4, 4), dtype=np.uint8)
img2 = np.random.randint(0, 255, (4, 4), dtype=np.uint8)
# 打印一下生成的随机数组
print('img1=\n', img1)
print('img2=\n', img2)
# 利用运算符+进行像素的加法运算
img3 = img1 + img2
print('img1 + img2 = \n', img3)
img4 = cv2.add(img1, img2)
print('cv2.add(img1, img2)=\n', img4)
运行及结果如下所示,如果有像素和大于255,则两种方法得到的结果就不同:
建议:最好使用cv2.add()这个函数,因为如果用‘+’号的话,假如像素和为256,则得到的结果为1,从最亮变到了最暗,这是我们不想得到的,而用cv2.add(),超过255,那就默认为最大值255。不会出现从最亮的像素点变为最暗的像素点。
我们前面的加法操作,是直接对像素简单粗暴的进行相加。cv2.addWeighted()函数则可以实现,将图像的像素值乘以一个数,即以一定的比例参加加法运算。具体用法如下
cv2.addWeighted(img1, x1, img2, x2, b)
img1和img2代表两个图像数组,x1是图像img1各个像素要乘的数,x2是图像img2各个像素要乘的数,b是img1和img2进行加法运算之后的结果再加上b这个值。
我们通过举例来看一下,首先声明两个数组,一个全为10, 一个全为100, 分别乘以不同的系数0.6, 0.4,再加上一个b=1。
import numpy as np
import cv2
# 利用加权值对像素进行处理cv2.addWeighted(x, a, y, b, c)
img5 = np.ones((4, 4), dtype=np.uint8) * 10
img6 = np.ones((4, 4), dtype=np.uint8) * 100
img7 = cv2.addWeighted(img5, 0.6, img6, 0.4, 1)
print('img5=\n', img5)
print('img6=\n', img6)
print('cv2.addWeighted(img5, 0.6, img6, 0.4)=\n', img7)
运行结果如下:
(二)、位逻辑运算
我们在数学中以及计算机导论中都学过二进制,位逻辑运算,就是基于二进制进行的一种运算。主要有与、或、非、异或运算。这几个位运算,都有它们的优势,它们所对应的opencv的函数如下表所示。
函数名 | 基本含义 |
---|---|
cv2.bitwise_and() | 位与 |
cv2.bitwise_or() | 位或 |
cv2.bitwise_xor() | 异或 |
cv2.bitwise_not() | 取反(非) |
cv2.bitwise_and(img1, img2, mask=)
上面四个函数除了位非,其他的函数的参数是一样的,这里我们以其中一个进行说明
img1和img2:这两个都是数组矩阵,可以是图像对应的数组矩阵也可以是自己定义的数组矩阵。
mask:表示的是一个掩膜,是可选参数,不是必选参数,根据需求进行设定,后面有关于掩膜的详细讲解。
接下来我们先看一下理论上这些位运算的规定。
位与:全是1,则为1,否则为0。
位或:全是0,则为0,否则为1。
位异或:相同则为0,不相同为1。
位非:就是取相反。为0则取1,为1则取0。
如下图所示:
位与运算的应用
我们前面说过位与运算的原理,同为1则为1,否则都为零。那我们思考一下,假如现在有一个像素值为6,它对应的二进制为0000 0110,我们现在设另外一个像素值为255,对应的二进制为: 1111 1111,将6和255的二进制进行位与运算结果为:0000 0110。即结果还是6。大家可以尝试一下用其他的值x与255进行二进制位与运算,其结果还是x。
我么可以得出如下结论:
我们将任何数值x与数值255进行二进制位运算,其结果还是位本身。
同样,我们将任何数值x与数值0进行二进制位运算,其结果都是0。
利用以上结论我们可以对图像提取出我们感兴趣的部分。比如我们有一张图像,想提取出脸部信息。首先读取一张灰色图像,然后我们生成一个和图像同样大小的数组矩阵,然后其中的某些值我们设置为255可以得到原图像信息,某些值设置为0可以将不需要的地方显示为黑色。用如下代码进行实现。
import numpy as np
import cv2
# 读取一幅图像
filename = r'C:\Users\LBS\Desktop\lena.jpg'
f = cv2.imread(filename, 0)
cv2.imshow('01', f)
# 用位与操作,获取我们感兴趣的部分,利用全1则为1,否则为0的特点
Mat = np.zeros(np.shape(f), dtype=np.uint8)
Mat[100:400, 100:400] = 255
Mat[400: 500, 100:200] = 255
# 将定义的矩阵Mat和图像f进行位与操作
img8 = cv2.bitwise_and(f, Mat)
cv2.imshow('02', Mat)
cv2.imshow('03', img8)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下图:矩阵像素值为255的地方对应的图像的像素的位置显示为原图像内容, 其他地方则是显示为黑色。
(三)、掩膜
掩膜也是一个矩阵数组,由一系列的值组成。它可以限制位运算操作的区域,即在掩膜的数值不等于0的位置进行操作,掩膜等于零的位置,则把进行操作的图像对应的掩膜为0的位置处全部赋值为0,0即黑色。例如,假设我们现在构造两个矩阵数组Mat1,Mat2,再构造一个掩膜Mat3,然后对两个矩阵数组进行加法操作(位运算也一样,这里用加法是为了展示掩膜的用法),则加法操作的区域是在Mat3不等于0的位置上进行的,其他的位置都赋值为0。
代码如下:
import numpy as np
import cv2
# 掩膜,控制计算的范围,只在掩膜不等于1的位置进行操作,其余位置置为0。
Mat1 = np.ones((4, 4), dtype=np.uint8) * 6
Mat2 = np.ones((4, 4), dtype=np.uint8) * 4
# 定义一个掩膜,操作只会在掩膜值为非空的像素点上进行,将其它的像素点的值置为0
Mat3 = np.zeros((4, 4), dtype=np.uint8)
Mat3[2:4, 2:4] = 1
print('Mat1=\n', Mat1)
print('Mat2=\n', Mat2)
print('Mat3=\n', Mat3)
#在掩膜规定的非0位置处执行加法操作,也适用执行位逻辑操作。
Mat4 = cv2.add(Mat1, Mat2, mask=Mat3)
print('Mat4=\n', Mat4)
结果如下:
掩膜在图像中应用如下:
import numpy as np
import cv2
# 读取彩色图像
filename = r'C:\Users\LBS\Desktop\lena.jpg'
f1 = cv2.imread(filename, 1)
cv2.imshow('f1', f1)
# 这里需要注意的是要提取出彩色图像的长宽,来构造掩膜的大小
w, h, c = np.shape(f1)
# 定义一个掩膜mask,操作只会在掩膜值为非空的像素点上进行,将其它的像素点的值置为0
mask = np.zeros((w, h), dtype=np.uint8)
mask[100:400, 100:400] = 255
mask[400:500, 100:200] = 255
# 执行位与运算
#f1 和 f1进行运算的结果还是f1,只不过是在mask指定的非0的区域上进行的操作
f2 = cv2.bitwise_and(f1, f1, mask=mask)
cv2.imshow('f2', f2)
cv2.waitKey()
cv2.destroyAllWindows()
运行结果如下所示:
(四)、其他位逻辑运算
位或运算和位异或运算,我们在开头处已经给出了它的计算规则,其操作和位与运算都相同,其位异或运算可以实现图像的加密和解密操作。位与运算还可以实现位层次的水印添加和提取。由于篇幅有限,后面的文章,会对这些进行详细的实现和说明。
(五)、图像处理中的位图分解
这里我们主要拿灰色图像进行说明,如果我们有一个图像像素是8比特位,意思就是我们现在有一个像素值6,它可以用二进制来表示,这个二进制有8位,为0000 0110。那我们想一下是不是可以把这八位给拆开,那么一个图像由若干个像素值组成,然后每个像素值又可以转换为8位二进制,那我们将每一个像素的二进制的每一位分别提取出来是不是就可以提取出来8幅图像?比如我将现在图像看成是二进制组成的数组,对于现有的图像,我对每个像素的二进制我都提取它的第7位,那么各个像素对应的第七位的值被提取出来组成一幅图像。后面的文章也会详细总结关于位图分解的笔记。
编写不易,转载请注明出处。