使用 Python 和 OpenCV 进行图像混合





5.00/5 (1投票)
基于图像蒙版的图像混合的 Python 代码。
引言
Photoshop 和其他图像处理程序(例如,GIMP 或 Paint.net 等免费替代品)提供了广泛的可能性来编辑和处理图像和照片。然而,有时人们希望执行标准软件中未包含的操作,或者只是想深入了解特定的照片编辑过程是如何工作的。这两种目标都可以通过尝试将照片编辑过程转化为编程代码来实现。在这里,我将这样做,并提供将一个图像的部分(或整个图像)与另一个图像混合的代码。
要理解本教程的内容,您应该具备 Python(Python 3)的基本知识,以及图像在计算机上的表示方式(即 RGB 或 BGR 模型)。然而,由于代码并不算过于复杂,因此其他编程语言背景的人员也应该能够理解。如果您打算执行或试用我上传的代码,您需要在您的机器上安装 OpenCv 和 NumPy(使用 pip 命令或 Anaconda Navigator 进行安装),当然还有 Python。OpenCV 是一个免费的图像处理库(提供 C++、Python 等接口),包含许多有助于处理视频和图像等视觉输入的特性。在这里,我只使用了一些基本方法。我使用命令在屏幕上加载和显示图像,并设置了一个 while 循环来实现与程序的某些交互(但这也很基础)。如前所述,NumPy 也是必需的。NumPy 是 Python 处理数字、数组和矩阵的首选库,而 OpenCV 的图像数据可以使用 NumPy 方法进行操作和修改,这一点非常方便。
背景
图像混合
关于如何使用 OpenCV 将一个图像中的信息与第二个图像中的信息合并的示例代码在网络上非常普遍(例如,参见此链接)。例如,`cv2.addWeighted(image1, weighting_factor1, image2, weighting_factor2)` 这个 OpenCV 函数可以一行代码以直观的方式完成图像混合。如果为两个权重因子都选择 0.5,则该函数返回两个图像的等权重(或平均)混合。选择 0.7 和 0.3 作为输入权重,将得到一个由第一个图像的像素颜色占 70%、第二个图像的像素颜色占 30% 组成的图像。例如,选择 1 和 0 将返回未改变的第一个图像,而选择 0 和 1 将返回未改变的第二个图像。我提到这一点是因为它在下面我深入分析我编写的代码时很重要。
使用遮罩进行图像插入
当组合两个大小相同的图像(或更精确地说,感兴趣的区域)并且对所有像素应用相同的权重因子时,`cv2.addWeighted()` 函数特别有用。然而,有时人们计划将一个图像的特定区域转移到另一个图像。这可以通过多种方式完成,但一种常见的方法是使用遮罩。遮罩通常是一个二值图像(只包含黑白像素),它充当一个模板,围绕着需要插入或混合的区域。为了说清楚,我举个例子。如果想用第二个图像的背景替换第一个图像的天空,那么遮罩(或二值图像)需要有一个黑色区域代表第一个图像的天空,这样程序就能“知道”在哪里放置第二个图像的背景。换句话说,在遮罩中遇到黑色像素颜色(8 位图像中为 0;24 位图像中为 [0,0,0])的地方,第一个图像的像素将被第二个图像的像素替换。
使用权重因子矩阵进行图像叠加

Using the Code
我将您可以在仓库中找到的代码示例分为四个部分。下面,我只展示中间两个部分的代码,因为它们涵盖了本教程的主要主题。为了完整起见,我将在以下内容中简要评论第 1 部分和第 4 部分,然后以要点形式介绍第 2 部分和第 3 部分的关键信息。
在第 1 部分,您会找到用于加载所需图像的 OpenCV 代码(即 `cv2.imread(“X.jpg”,cv2.IMREAD_UNCHANGED)`)。有三个图像:`image 1` 是将 `image 2` 的内容粘贴到的表面,`image 3` 是定义混合操作条件的遮罩或模板。还有一些附加功能,但我留给您去发现它们的作用(我上传的示例代码中有信息丰富的注释)。
在第 4 部分,您会找到一个“`while` 循环”,它允许一种基本的用户交互。该循环等待按键事件。按下“`q`”键将退出程序,而按下“`s`”键将保存混合操作的结果然后退出程序。
第 2 部分和第 3 部分包含的代码是基于遮罩提供的权重因子对两个图像的信息进行混合。
- 函数 `mix_pixel(pix_1, pix_2, perc)` 的处理方式与 `cv2.addWeighted()` 类似。它从两个图像中获取像素信息,并根据函数第三个参数(即 `perc`)中给出的权重进行混合(例如,当 `perc` 在 0 和 255 之间时,它会得到像素 1 和像素 2 的 50/50 混合;另见“图像混合”部分)。当参数是 `NumPy` 数组时(**注意**:所有数组必须具有相同的维度),操作将应用于图像的所有相应像素。第三个参数是一个数组(遮罩),其中包含每个像素的单独权重因子。
- 函数 `blend_images_using_mask(img_orig, img_for_overlay,img_mask)` 非常简单。它接收三个图像,检查遮罩是否为 3 通道灰度图像(否则,它将与其它图像的维度不同),调用 `mix_pixel()` 函数,然后将结果作为 3 通道 8 位无符号整数返回(否则 OpenCV 显示图像的命令将失败)。
- 您在下面示例中找到的最终命令用于显示不同类型的图像,以便使操作结果可见并便于评估。还有一些代码行用于缩小图像,因为照片的原始尺寸通常对于标准屏幕来说太大。
# code skeleton
import numpy as np
import cv2
# .......omitted code
# functions for blending operations
# takes a pixel from image 1 (pix_1) and blends it with a pixel from image 2 (pix_2)
# depending on the value given in perc (percentage);
# if perc = 0 or 255 (or 0,0,0 or 255,255,255) it will perform no blending at all
# and return the value of image 1 or image 2;
# by contrast, all values in between (e.g., 140) will give a weighted blend of the two images
# function can be used with scalars or numpy arrays (perc will be greyscale numpy array then)
def mix_pixel(pix_1, pix_2, perc):
return (perc/255 * pix_1) + ((255 - perc)/255 * pix_2)
# function for blending images depending on values given in mask
def blend_images_using_mask(img_orig, img_for_overlay, img_mask):
# turn mask into 24 bit greyscale image if necessary
# because mix_pixel() requires numpy arrays having the same dimension
# if image is 24-bit BGR, the image has 3 dimensions, if 8 bit greyscale 2 dimensions
if len(img_mask.shape) != 3:
img_mask = cv2.cvtColor(img_mask, cv2.COLOR_GRAY2BGR)
# interpolate between two images (img_orig and img_to_insert)
# using the values in img_mask (each pixel serves as individual weight)
# as weighting factors (ranging from [0,0,0] to [255,255,255] or 0 to 100 percent);
# because all three images are numpy arrays standard operators
# for multiplication etc. will be applied to all values in arrays
img_res = mix_pixel(img_orig, img_for_overlay, img_mask)
return img_res.astype(np.uint8)
# blend images and display results
# call function above to perform blending with mask (containing weights for interpolation)
img_blended = blend_images_using_mask(img, img_insert, img_insert_mask)
#print(img_blended.shape)
# rf -> resizing factor; used to make images smaller, so they can be displayed on screen;
# blending operations, however, will be performed on original sized images
rf = 0.4
wi = img.shape[1] # width and
hi = img.shape[0] # height of images
# call OpenCV resize
img_sm = cv2.resize(img, (int(wi * rf), int(hi * rf)),
interpolation=cv2.INTER_CUBIC)
img_insert_sm = cv2.resize(
img_insert, (int(wi * rf), int(hi * rf)), interpolation=cv2.INTER_CUBIC)
img_blended_sm = cv2.resize(
img_blended, (int(wi * rf), int(hi * rf)), interpolation=cv2.INTER_CUBIC)
# display images
cv2.imshow("Original Image", img_sm)
cv2.imshow("Insert This Image", img_insert_sm)
cv2.imshow("Blended Images", img_blended_sm)
# .......omitted code
关注点
不保证代码没有错误并且能在每个平台上运行。运行代码时要小心,不要正确终止它(不按 'q' 或 's')。然后您应该重新启动 Kernel(如果使用 Jupyter Notebook),或者从系统中删除线程(Windows 任务管理器)。此外,所有文件必须放在同一个文件夹中,程序才能正确执行。欢迎提出改进建议。
历史
- 2020 年 12 月 23 日:初始版本