文章目录
- 1. 图像矩阵按位操作
- 1.1 按位取反
- 1.1.1 函数简述和原型
- 1.1.2 参数
- 1.1.3 返回值
- 1.1.4 用法举例
- 1.1.5 实例展示
- 1.2 按位与
- 1.2.1 函数简述和原型
- 1.2.2 参数
- 1.2.3 返回值
- 1.2.4 用法举例
- 1.2.5 实例展示
- 1.3 按位异或
- 1.3.1 函数简述和原型
- 1.3.2 参数
- 1.3.3 返回值
- 1.3.4 用法举例
- 1.3.5 实例展示
- 1.4 按位或
- 1.4.1 函数简述和原型
- 1.4.2 参数
- 1.4.3 返回值
- 1.4.4 用法举例
- 1.4.5 实例展示
- 2. 图像矩阵绝对差值
- 2.1 函数简述和原型
- 2.2 参数
- 2.3 返回值
- 2.4 用法举例
- 2.5 实例展示
- 3. 图像叠加
- 3.1 简单叠加
- 3.1.1 函数简述和原型
- 3.1.2 参数
- 3.1.3 返回值
- 3.1.4 用法举例
- 3.2 加权叠加
- 3.2.1 函数简述和原型
- 3.2.2 参数
- 3.2.3 返回值
- 3.2.4 用法举例
- 3.2.5 实例展示
- 4. 图像拼接
- 4.1 水平拼接
- 4.1.1 函数简述和原型
- 4.1.2 参数
- 4.1.3 返回值
- 4.1.4 用法举例
- 4.2 垂直拼接
- 4.2.1 函数简述和原型
- 4.2.2 参数
- 4.2.3 返回值
- 4.2.4 用法举例
- 5. 综合实例
- 5.1 目的
- 5.2 代码实现
- 5.3 结果展示
1. 图像矩阵按位操作
1.1 按位取反
1.1.1 函数简述和原型
该函数用于对图片矩阵数据中每个元素进行按位取反操作,往往对二值化的图像进行该操作,使得黑色变成白色,白色变成黑色。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga0002cf8b418479f4cb49a75442baee2f
dst = cv2.bitwise_not(src[, mask])
1.1.2 参数
- src:要按位取反的图片数据;
- mask:掩模,需要是与图片大小一致的一个ndarray,元素为uint8类型。一般来说是一个二值化的矩阵,即元素值不是0(黑色)就是255(白色),如果设置了该参数,则掩模中值为255(白色)的像素才会进行按位取反的操作,掩模中值为0(黑色)的像素则直接忽略,该部分的结果也是黑色;
1.1.3 返回值
- dst:按位取反后的图像数据;
1.1.4 用法举例
img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('origin', img)
# 先使用自适应均值——高斯法将图像二值化
th = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 17, 6)
cv2.imshow('binarized', th)
# 创建一个掩模,仅右半部分为白色,即仅关注图像的右半部分
mask = np.zeros_like(th)
h, w = mask.shape
mask[:, int(w/2):] = 255
cv2.imshow('mask', mask)
th_inv = cv2.bitwise_not(th, mask=mask)
cv2.imshow('binary inverse', th_inv)
1.1.5 实例展示
1.2 按位与
1.2.1 函数简述和原型
该函数用于对两个一样大小的图片矩阵数据中的每个像素值进行按位与操作。实际应用中往往将某张图片与自身进行按位与操作,再与mask
参数结合,提取出掩模中不为0的部分在图像中对应的位置。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga60b4d04b251ba5eb1392c34425497e14
dst = cv2.bitwise_and(src1, src2[, mask])
1.2.2 参数
- src1:按位与操作的第一张图片数据;
- src2:按位与操作的第二张图片数据,大小需要与第一张图片一致;
- mask:掩模,需要是与按位与的两张图片大小一致的一个ndarray,元素为uint8类型。一般来说是一个二值化的矩阵,即元素值不是0(黑色)就是255(白色),如果设置了该参数,则掩模中值为255(白色)的像素才会进行按位与操作,掩模中值为0(黑色)的像素则直接忽略,该部分的结果也是黑色;
1.2.3 返回值
- dst:按位与操作后的图像数据;
1.2.4 用法举例
img = cv2.imread(r'.\image.jpg', cv2.IMREAD_GRAYSCALE)
cv2.imshow('Origin', img)
# 创建一个掩模,仅右半部分为白色,即仅关注图像的右半部分
mask = np.zeros_like(img)
h, w = img.shape
mask[:, int(w/2):] = 255
cv2.imshow('Mask', mask)
# 将图像与自身按位与,结合mask参数仅提取图像的右半部分
bitAnd = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('BitwiseAnd', bitAnd)
1.2.5 实例展示
1.3 按位异或
1.3.1 函数简述和原型
该函数用于对两个一样大小的图片矩阵数据中的每个像素值进行按位异或操作,主要目的即为寻找出两个图像中不同的部分。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga84b2d8188ce506593dcc3f8cd00e8e2c
dst = cv2.bitwise_xor(src1, src2[, mask])
1.3.2 参数
- src1:按位异或操作的第一张图片数据;
- src2:按位异或操作的第二张图片数据,大小需要与第一张图片一致;
- mask:掩模,需要是与按位异或的两张图片大小一致的一个ndarray,元素为uint8类型。一般来说是一个二值化的矩阵,即元素值不是0(黑色)就是255(白色),如果设置了该参数,则掩模中值为255(白色)的像素才会进行按位异或操作,掩模中值为0(黑色)的像素则直接忽略,该部分的结果也是黑色;
1.3.3 返回值
- dst:按位异或操作后的图像数据,两张图像相同的部分为0(黑色),不同的部分为255(白色);
1.3.4 用法举例
img1 = cv2.imread(r'.\image1.jpg', cv2.IMREAD_COLOR)
img2 = cv2.imread(r'.\image2.jpg', cv2.IMREAD_COLOR)
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
bitXor = cv2.bitwise_xor(img1, img2)
cv2.imshow('bitXOR', bitXor)
1.3.5 实例展示
1.4 按位或
1.4.1 函数简述和原型
该函数用于对两个一样大小的图片矩阵数据中的每个像素值进行按位或操作。实际应用中往往针对二值化图像进行该操作,目的在于取两个二值化图像中的白色部分的并集。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#gab85523db362a4e26ff0c703793a719b4
dst = cv2.bitwise_or(src1, src2[, mask])
1.4.2 参数
- src1:按位或操作的第一张图片数据;
- src2:按位或操作的第二张图片数据,大小需要与第一张图片一致;
- mask:掩模,需要是与按位或的两张图片大小一致的一个ndarray,元素为uint8类型。一般来说是一个二值化的矩阵,即元素值不是0(黑色)就是255(白色),如果设置了该参数,则掩模中值为255(白色)的像素才会进行按位或操作,掩模中值为0(黑色)的像素则直接忽略,该部分的结果也是黑色;
1.4.3 返回值
- dst:按位或操作后的图像数据;
1.4.4 用法举例
# 创建两个黑色画布
canvas1 = np.zeros((300, 300), dtype=np.uint8)
canvas2 = np.zeros((300, 300), dtype=np.uint8)
# 在两个画布的不同位置分别绘制白色的实心矩形
cv2.rectangle(canvas1, (50, 50), (150, 150), color=255, thickness=cv2.FILLED)
cv2.rectangle(canvas2, (80, 80), (250, 250), color=255, thickness=cv2.FILLED)
cv2.imshow('image1', canvas1)
cv2.imshow('image2', canvas2)
# 按位或操作,合并两个白色矩形
bitOr = cv2.bitwise_or(canvas1, canvas2)
cv2.imshow('bitwiseOr', bitOr)
1.4.5 实例展示
2. 图像矩阵绝对差值
2.1 函数简述和原型
该函数用于计算两个一样大小的图片矩阵数据中每个像素值之间差值的绝对值,类似按位异或操作,该函数主要目的也为寻找出两个图像中不同的部分。然而,两张图像中某个像素的值只要有不同,按位异或在该位置计算出的值均为255,因此按位异或操作的输出值是一个二值化矩阵(不是0就是255)。而该函数的输出则有大有小,值越大表示该位置两张输入图像的像素差异越大。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga6fef31bc8c4071cbc114a758a2b79c14
dst = cv2.absdiff(src1, src2)
2.2 参数
- src1:计算差异的第一张图像数据;
- src1:计算差异的第二张图像数据,大小需要与第一张图像一致;
2.3 返回值
- dst:计算出的两张图像在每个像素点处差值的绝对值;
2.4 用法举例
img1 = cv2.imread(r'.\image1.jpg', cv2.IMREAD_COLOR)
img2 = cv2.imread(r'.\image2.jpg', cv2.IMREAD_COLOR)
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
# 计算两张图片的差异绝对值
diff = cv2.absdiff(img1, img2)
cv2.imshow('difference', diff)
2.5 实例展示
下图比较了一下按位异或与绝对差值,可见由于按位异或无法区分出差异大小,导致差异较小的地方输出也较大,因此噪点较多:
3. 图像叠加
3.1 简单叠加
3.1.1 函数简述和原型
该函数用于加和两个一样大小的图片矩阵数据中的每个像素值,加和结果溢出时则取最大值,如对于单字节类型(uint8)即为255。实际应用中对该函数的应用较少。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga0002cf8b418479f4cb49a75442baee2f
dst = cv2.add(src1, src2[, mask[, dtype]])
3.1.2 参数
- src1:要叠加的第一幅图像数据;
- src2:要叠加的第二幅图像数据,大小需要与第一幅图像一致;
- mask:掩模,需要是与图片大小一致的一个ndarray,元素为uint8类型。一般来说是一个二值化的矩阵,即元素值不是0(黑色)就是255(白色),如果设置了该参数,则掩模中值为255(白色)的像素才会进行叠加操作,掩模中值为0(黑色)的像素则直接忽略,该部分的结果也是黑色;
- dtype:输出的数据类型,默认值为-1,表示与第一幅图像一致。实际应用中图像的数据类型往往都是单字节类型(uint8),故不对该参数进行设置;
3.1.3 返回值
- dst:叠加后的图像;
3.1.4 用法举例
img1 = cv2.imread(r".\image1.jpeg", cv2.IMREAD_COLOR)
img2 = cv2.imread(r".\image2.jpg", cv2.IMREAD_COLOR)
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
# 简单叠加两张图片,仅叠加右半部分
h, w, c = img1.shape
mask = np.zeros((h, w), dtype=np.uint8)
mask[:,int(w/2):] = 255
imgAdd = cv2.add(img1, img2, mask=mask)
cv2.imshow('Simple Add', imgAdd)
3.2 加权叠加
3.2.1 函数简述和原型
该函数用于对两个一样大小的图片矩阵数据中的每个像素值进行加权加和,加和结果溢出时则取最大值,如对于单字节类型(uint8)即为255。相比于简单叠加,该函数更为灵活,且如果参数设置合理,加和结果也不那么容易溢出(输出即为白色)。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#gafafb2513349db3bcff51f54ee5592a19
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dtype])
3.2.2 参数
- src1:要叠加的第一幅图像数据;
- alpha:double类型,第一幅图像的数据的权重值;
- src2:要叠加的第二幅图像数据,大小需要与第一幅图像一致;
- beta:double类型,第二幅图像的数据的权重值;
- gamma:double类型,最后加权结果的偏移量;
- dtype:输出的数据类型,默认值为-1,表示与第一幅图像一致。实际应用中图像的数据类型往往都是单字节类型(uint8),故不对该参数进行设置;
3.2.3 返回值
- dst:加权叠加后的图像,每个像素的值为
$dst[i]=alpha*src1[i]+beta*src2[i]+gamma$
;
3.2.4 用法举例
img1 = cv2.imread(r".\image1.jpeg", cv2.IMREAD_COLOR)
img2 = cv2.imread(r".\image2.jpg", cv2.IMREAD_COLOR)
cv2.imshow('image1', img1)
cv2.imshow('image2', img2)
# 加权叠加两张图片,两张图片各占一半,最后输出值再减去40
imgAddW = cv2.addWeighted(img1, 0.5, img2, 0.5, -40)
3.2.5 实例展示
下图对比了一下简单叠加和加权叠加,很明显简单叠加很容易溢出,而加权叠加则可以自由地控制输出:
4. 图像拼接
4.1 水平拼接
4.1.1 函数简述和原型
该函数用于在水平方向上拼接多个高度一致(即行数一致)的图像。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#gaf9771c991763233866bf76b5b5d1776f
dst = cv2.hconcat(src)
4.1.2 参数
- src:一个包含图像矩阵的列表,其中每个元素为想要拼接的图像数据,这些图像矩阵的行数必须一致,即图像高度一致;
4.1.3 返回值
- dst:水平拼接后的图像;
4.1.4 用法举例
img1 = cv2.imread(r".\image1.jpeg", cv2.IMREAD_COLOR)
img2 = cv2.imread(r".\image2.jpg", cv2.IMREAD_COLOR)
# 水平方向上拼接图像后展示
cv2.imshow('image', cv2.hconcat([img1,img2]))
4.2 垂直拼接
4.2.1 函数简述和原型
该函数用于在垂直方向上拼接多个宽度一致(即列数一致)的图像。官方文档:https://docs.opencv.org/3.4.2/d2/de8/group__core__array.html#ga744f53b69f6e4f12156cdde4e76aed27
dst = cv2.vconcat(src)
4.2.2 参数
- src:一个包含图像矩阵的列表,其中每个元素为想要拼接的图像数据,这些图像矩阵的列数必须一致,即图像宽度一致;
4.2.3 返回值
- dst:垂直拼接后的图像;
4.2.4 用法举例
img1 = cv2.imread(r".\image1.jpeg", cv2.IMREAD_COLOR)
img2 = cv2.imread(r".\image2.jpg", cv2.IMREAD_COLOR)
# 垂直方向上拼接图像后展示
cv2.imshow('image', cv2.vconcat([img1,img2]))
5. 综合实例
此处演示一个综合应用上述几种API的例子。
5.1 目的
目前有一张路飞的图片和一张叶伊布的图片,现在希望将叶伊布放在路飞的掌心。主要思路如下:首先将关心的区域(ROI)取出,本例中即为路飞的手掌部分;将伊布的图片缩放至合适的大小;之后将伊布的图像转为灰度图像,再进行二值化,可以获得一个掩模,该掩模中图像的白色背景部分为0(黑色)。利用该掩模将伊布的图像中白色的背景除去,也就仅将有颜色的部分取出,即只提取出了伊布;之后再将该掩模按位取反,则掩模中白色背景为255(白色),利用该掩模可以将路飞手掌中伊布的部分抹黑,从而将伊布的位置腾出来;最后把除去背景的伊布与伊布位置为黑色的路飞手掌叠加,再将叠加后的区域替换路飞的原始图像,则就实现了把伊布放在路飞的手掌中的效果。
5.2 代码实现
luffy = cv2.imread(r".\resources\Luffy1.jpeg", cv2.IMREAD_COLOR)
eevee = cv2.imread(r".\resources\cartoon1.jpg", cv2.IMREAD_COLOR)
# 先取出关心区域(ROI),即路飞的手掌部分
luffy_hand = luffy[366:494, 792:972]
# 将伊布的图像缩小至适合路飞手掌的大小
eevee_shrink = cv2.resize(eevee, None, fx=1/6, fy=1/6, interpolation=cv2.INTER_CUBIC)
# 制作掩模,将伊布图像中白色背景除去
eevee_gray = cv2.cvtColor(eevee_shrink, cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(eevee_gray, 250, 255, cv2.THRESH_BINARY_INV)
eevee_extract = cv2.bitwise_and(eevee_shrink, eevee_shrink, mask=mask)
# 掩模取反,将路飞手掌中伊布的位置抹黑
mask_inv = cv2.bitwise_not(mask)
luffy_hand = cv2.bitwise_and(luffy_hand, luffy_hand, mask=mask_inv)
# 融合路飞手掌和伊布
luffy_hand = cv2.add(luffy_hand, eevee_extract)
# 将融合后的ROI放回原图对应位置
luffy[366:494, 792:972] = luffy_hand
cv2.imshow('Eevee in Luffy\'s Hand', luffy)
5.3 结果展示