Python 和 OpenCV 库使处理图像或视频等视觉输入变得非常容易。在本篇文章中,我将讨论有关图像混合的内容并提出一种合并两个图像的像素信息的方法。为了实现这一点,我使用了第三张图像(所谓的蒙版),作为混合过程的模板。因为这个模板是一个加权因子矩阵,所以有可能产生结果,这是通常在网上找到的混合方法无法获得的(或者至少我没有找到)。
简介
Photoshop 和其他图像处理程序(例如,GIMP 或 Paint.net 等免费软件替代品)提供了广泛的可能性来编辑和处理图像和照片。然而,有时,人们希望执行标准软件中没有的操作,或者只是想了解一下具体的照片编辑程序是如何工作的。这两个目标都可以通过尝试将照片编辑程序转换为编程代码来实现。在这里,在这段文字中,我将这样做并提供用于将图像的部分(或整个图像)与另一个图像合并的代码。
要理解本教程的内容,您应该具备 Python (Python 3) 的基本知识以及图像在计算机上的表示方式(即 RGB 或 BGR 模型)。但是,由于代码并不过分复杂,因此具有其他编程语言背景的人也应该可以访问它。如果您打算执行或使用我加载的代码,则需要在您的机器上安装 OpenCv 和 NumPy(使用pip
命令或Anaconda Navigator
这样做)当然还有 Python。OpenCV 是一个免费提供的图像处理库(接口可用于 C++、Python 等),包含许多有助于处理视频和图像等视觉输入的功能。这里,在这段代码中,我只使用了一些基本的方法。我使用命令在屏幕上加载和显示图像,并设置一个 while 循环来启用与程序的一些交互(但这也非常实际)。如前所述,还需要 NumPy。NumPy 是 Python 处理数字、数组和矩阵的第一大库,它非常方便,可以使用 NumPy 方法操作和修改 OpenCv 图像数据。
背景
混合图像
关于如何使用 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;[0,0,
使用加权因子矩阵进行图像叠加
在我在这里展示的示例代码中,我将图像混合方法与基于掩码的图像插入方法结合起来。与“标准”方法相比,我使用非二进制掩码(可以保存 0 到 255 之间的所有值),其中每个像素都用作单独的权重。因此,我没有定义图像一的整体加权因子(例如,0.7)和图像二的整体加权因子(例如,0.3),而是使用掩码的每个像素作为加权因子(参见下图)。例如,如果掩码中位于 [X, Y] 位置的像素的值为 0(即黑色),则图像 1 的像素将放置在该位置。如果掩码的一个像素保持值 255(白色),则图像 2 的相应像素将占据该位置。如果它介于两者之间(例如,170),则会混合图像一和图像二。使用掩码作为由权重因子组成的地图在叠加两个图像时提供了更大的灵活性。它可以产生过渡或“软”边界(甚至更多)。
使用代码
我将您可以在存储库中找到的代码示例分为四个部分。下面,我只展示了两个内部部分的代码,因为它们涵盖了本教程的主要主题。为完整起见,我在下面简要评论第 1 节和第 4 节,然后以要点的形式提供第 2 节和第 3 节的基本信息。
在第 1 节中,您可以找到用于加载所需图像(即cv2.imread(“X.jpg”,cv2.IMREAD_UNCHANGED)
)的OpenCV 代码。有三个图像: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的中间,它给出了一个50-50混合像素1和像素 2;另请参见混合图像部分)。当参数为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
如果您想要了解更多关于Python OpenCV的内容,推荐大家可以系统地学习一下OpenCV相关教程。