在本文中,将探讨如何开发一个应用程序,该程序能够移除或替换图像中的背景。将使用两个库来实现这一功能:首先是Mediapipe库,用于将人物从背景中分割出来;其次是cv2库,用于执行图像处理技术。
图像来源:Stack Overflow
在填写某些表格时,可能需要提供没有背景的护照照片,这时应用程序就能派上用场。此外,类似于Zoom这样的应用,也可以创建一个迷版的Zoom应用,其中包含更换背景的功能。在远程工作的场景中,这种应用也非常有用,因为在视频会议中,可能不希望同事看到背景。
现在,将加载所有必需的库来构建这个应用。
import cv2
import numpy as np
import mediapipe as mp
import matplotlib.pyplot as plt
首先,需要初始化自拍照分割模型。
图像来源:ProAndroidDev
第一步是初始化模型,这将是自拍照分割模型的预步骤。在这个模型中,将有两种类型的模型:
通用模型:如果将0作为自拍照分割模型的参数传递,那么将选择通用模型。
风景模型:如果将1作为上述模型的参数传递,那么将选择风景模型。
注意:如果没有指定任何模型,那么默认将选择0,即通用模型。
但是,等等!这两种模型之间有什么区别呢?让讨论一下:
当涉及到通用模型时,它特别适用于256X256x1,即256高度、256宽度、1通道作为输入,以及256x256x3作为输出。
当谈论风景模型时,它特别适用于144X256X1作为输入,并产生144x256x3输出张量,除此之外,通用模型和风景模型是相同的。
change_background_mp = mp.solutions.selfie_segmentation
change_bg_segment = change_background_mp.SelfieSegmentation()
代码解析:正如这里讨论的,将使用mp.solutions.selfie_segmentation初始化分割模型,如果分解它,可以看到正在从Mediapipe库中调用solutions类,然后从那个类中调用selfie_segmentation模型。
在模型初始化之后,将设置分割函数,即SelfieSegmentation()。
之前,已经初始化了分割模型,并创建了一个自拍照分割函数。现在,让读取样本图像,看看它是什么样子:
sample_img = cv2.imread('media/sample.jpg')
plt.figure(figsize = [10, 10])
plt.title("Sample Image");plt.axis('off');plt.imshow(sample_img[:,:,::-1]);plt.show()
代码解析:首先,从read()函数读取图像。然后,在绘制/显示图像之前,将使用figure函数设置显示的大小。最后,在显示图像之前,最好将图像格式从RGB转换为BGR,因为cv2在处理彩色图像时只以这种格式读取图像,然后将使用show函数显示图像。
现在已经准备好使用自拍照分割模型处理样本图像,首先移除或替换背景。但在此之前,正如所知,之前已经将样本图像转换为BGR格式,因为cv2库只能以这种方式正确读取图像,但对于Mediapipe库来说并非如此,所以将图像从BGR转换为RGB格式。
RGB_sample_img = cv2.cvtColor(sample_img, cv2.COLOR_BGR2RGB)
result = change_bg_segment.process(RGB_sample_img)
代码解析:正如讨论的,首先将BGR格式的图像转换为RGB格式图像。现在,将使用process函数处理自拍照分割模型在样本图像上。然后,正如在读取图像部分所做的那样,将使用figure函数设置图像大小。最后,将并排显示原始图像和分割图像(使用matplotlib的subplot函数)和imshow函数。
如果仔细观察输出(分割的子图,即主要处理输出),可以看到,有些区域既不是纯黑色也不是纯白色,它们有点灰色,这表明模型无法预测这些地方是背景还是人物,因此将使用阈值技术以获得更准确的分割区域。
binary_mask = result.segmentation_mask > 0.9
代码解析:在这里,使用二进制掩码的概念,它将为人物的像素值设置为1,为背景的像素值设置为0。同时,将设置阈值值为0.9,即90%的置信度,像素值大于这个值时将设置为1,否则为0。
到目前为止,已经通过执行一些图像预处理技术准确地分割了图像。现在,是时候直观地看看图像的背景将如何被移除,为此,将使用numpy.where()函数。这个函数将使用二进制掩码值,并返回白色区域为每个1像素值,然后替换每个区域为0像素值,即黑色区域与255,这意味着背景将只有白色。
binary_mask_3 = np.dstack((binary_mask,binary_mask,binary_mask))
output_image = np.where(binary_mask_3, sample_img, 255)
代码解析:正如讨论的,将使用Numpy的d-stack函数将图像从单通道转换为三通道。现在,将使用Numpy的函数将每个黑色区域转换为白色区域。最后,将使用figure函数设置图像大小。然后,将使用show函数显示原始图像和输出图像。
注意:到目前为止,为了获得白色背景,已经使用了255作为值,但也可以使用另一个背景图像作为输出,只需要在np.where函数中更改参数即可。
bg_img = cv2.imread('media/background.jpg')
output_image = np.where(binary_mask_3, sample_img, bg_img)
代码解析:这里是最后一部分,将替换图像的背景。首先,将使用imread函数读取那个背景图像。现在,将创建一个最终的输出图像。将使用np.where函数用其他背景图像替换黑色区域(二进制掩码)。最后,将显示原始图像、样本图像和最终分割结果。
最后,已经开发了一个应用程序,可以移除任何包含人物的图像的背景,尽管也可以创建实时功能,就像Zoom应用程序一样。逻辑将相同,只是不是图像处理,而是视频处理。
从本文中,学到了图像分割的工作原理及其在现实世界中的应用。有许多图像分割技术可供选择。但这是最简单的一种,正如所看到的,它是模块化的。还介绍了一些图像预处理技术,如阈值处理、腐蚀、堆栈。这些基本技术也涉及到构建一个完整的计算机视觉应用的管道。