先上代码:
#!/usr/bin/env python
#
import cv2 as cv
import sys
import numpy as np
import matplotlib.pyplot as plt
if __name__ == '__main__':
# 读取图像并判断是否读取成功
img = cv.imread('../images/food-01.jpg')
if img is None:
print('Failed to read food-01.jpg.')
sys.exit()
else:
# 使用cv.merge()函数添加alpha通道
zeros = np.ones(img.shape[:2], dtype=img.dtype) * 100
result_BGR_alpha = cv.merge([img, zeros])
print('原图的通道数为:{}'.format(img.shape[2]))
print('处理后的通道数为:{}'.format(result_BGR_alpha.shape[2]))
# 图像保存到硬盘
cv.imwrite('../results/food-01.png', result_BGR_alpha)
# 以下代码为图像展示
# 因为opencv的颜色通道顺序为[B,G,R],而matplotlib的颜色通道顺序为[R,G,B],
# 所以作图前要先进行通道顺序的调整。
result_RGB_alpha = result_BGR_alpha[:, :, (2, 1, 0, 3)]
img_RGB = img[:, :, (2, 1, 0)]
# 以下开始绘制图形并显示
plt.figure()
plt.subplot(1, 2, 1) # 将画板分为一行两列,接下来要绘的图位于第一个位置
plt.title('Original image')
plt.imshow(img_RGB)
plt.subplot(1, 2, 2) # 将画板分为一行两列,接下来要绘的图位于第二个位置
plt.title('Add alpha channel image')
plt.imshow(result_RGB_alpha)
plt.show()
运行结果如下图所示:
下面对程序进行说明。
1 图像的alpha通道有什么用?
答:一个图像的每个像素都有 BGR 三个通道,后来有个名叫Alvy Ray Smith 提出每个像素再增加一个 Alpha 通道,取值为0到1(当然,在咱们程序中是从0到255),用来储存这个像素是否对图片有「贡献」,0代表透明、1或255代表不透明。也就是说,「Alpha 通道」储存一个值,这个值与原图像中的值作了运算后,其外在表现是「透明度」。 当然,简单的理解就是Alpha 通道可以控制图像的透明度。
2 opencv中的merge()函数用于合并若干个相同尺寸(size)和深度(depth)的矩阵。
merge()函数的原型有两种,分别介绍如下:
第一种原型:
C++
void cv::merge (const Mat * mv, size_t count, OutputArray dst)
Python:
dst = cv.merge(mv[, dst])
参数意义如下:
mv—input array of matrices to be merged; all the matrices in mv must have the same size and the same depth.(翻译:将要被合并的矩阵数组,矩阵数组中的每个矩阵都要相同的尺寸和深度,什么叫矩阵数组?下面代码中的“channels”就是一个矩阵数组,它由3个矩阵元素组成,这3个矩阵元素分别为m1、m2、m3)
Mat m1 = (Mat_<uchar>(2,2) << 1,4,7,10);
Mat m2 = (Mat_<uchar>(2,2) << 2,5,8,11);
Mat m3 = (Mat_<uchar>(2,2) << 3,6,9,12);
Mat channels[3] = {m1, m2, m3};
count—number of input matrices when mv is a plain C array; it must be greater than zero.(翻译:需要合并的矩阵数目,它必须是大于0的数)
dst—output array of the same size and the same depth as mv[0]; The number of channels will be equal to the parameter count.(翻译:合并之后的矩阵,它的尺寸和深度与mv[0]矩阵的尺寸和深度一致,它的通道数就是参数count)
第二种原型:
C++
void cv::merge (InputArrayOfArrays mv, OutputArray dst)
Python:
dst = cv.merge(mv[, dst])
参数mv和dst的意义和第一个原型基本相同,这里就不多赘述了。只是要注意这里的mv的类型为InputArrayOfArrays,直译为用于输入的矩阵数组的数组,即它是一个二维数组。
我们这里的程序是用Python写成的,两种原型对于Python编程来说都没有区别,所以不用去区分到底是第一种原型还是第二种原型。
3 为什么不用opencv的imshow()函数显示图像?
答:因为opencv的imshow()函数并不能把带图片的alpha通道的效果显示出来。
文末再附一个利用merge()函数和图像alpha通道的例子:
该例子利用图像的轮廓图生成alpha通道,然后利用merge()函数将alpha通道融合到原BGR图像中,最终得到带透明度信息的png图像。在效果上体现为将手绘的图形从纸上抠出(即手绘图的抠图实现)
原BGR图像如下(名字“jing_ling_300_400.jpg”):
其带填充效果的轮廓如下(名字“JL_Contours_01.bmp”):
可用下面的代码生成名字为JL_image_BGR_alpha.png的图像,该图像把原图中的背景部分全部置为透明状态了。
# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也请联系博主
# 图像处理技术交流QQ群 271891601
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1
import numpy as np
import cv2 as cv
import sys
image_contours1 = cv.imread('F:/material/images/2022/2022-06/JL_Contours_01.bmp', 0)
image_src = cv.imread('F:/material/images/2022/2022-06/jing_ling_300_400.jpg')
if image_contours1 is None:
print('Error: Could not load image')
sys.exit()
if image_src is None:
print('Error: Could not load image')
sys.exit()
alpha_channel = np.zeros(image_src.shape[0:2], image_src.dtype)
img_row = image_contours1.shape[0]
img_col = image_contours1.shape[1]
for i in range(img_row):
for j in range(img_col):
temp1 = image_contours1[i, j]
if temp1 > 200:
alpha_channel[i, j] = 255
image_BGR_alpha = cv.merge([image_src, alpha_channel])
# cv.imshow(' alpha_channel', alpha_channel)
cv.imwrite('F:/material/images/2022/2022-06/JL_image_BGR_alpha.png', image_BGR_alpha)
运行生成的JL_image_BGR_alpha.png图像如下图所示:
可见,输出的png图像把原图中的背景部分全部置为透明状态了,这是一次比较成功的抠图。