如何批量抠图绿幕照片(使用色度键)

4

目标

我有数百张与这张类似的图片:

源照片 我只想使用绿屏为每个图像创建一个遮罩,看起来像这张图片(边框最好稍微平滑一些):

mask

如果您想进行测试,这是原始图像链接: https://mega.nz/#!0YJnzAJR!GRYI4oNWcsKztHGoK7e4uIv_GvXBjMvyry7cPmyRpRA


我尝试过的方法

我找到了this篇帖子,其中用户使用Imagemagick实现了色度键控。

for i in *; do convert $i -colorspace HSV -separate +channel \
  \( -clone 0 -background none -fuzz 3% +transparent grey43 \) \
  \( -clone 1 -background none -fuzz 10% -transparent grey100 \) \
  -delete 0,1 -alpha extract -compose Multiply -composite \
  -negate mask_$i; done;

但无论我如何调整数字,结果都不完美: result


我感觉很蠢,自己找不到这么简单的问题的解决方案。请注意,我正在使用Linux,所以没有Photoshop或After Effects!但是我相信一定有解决这个问题的方法。
更新1
我刚刚尝试使用this greenscreen script,作者是fmw42,通过运行./greenscreen infile.jpg outfile.png,我对结果感到非常满意。但是,每张图像处理需要大约40秒,这意味着处理所有图片需要8小时(虽然我有一台相当强大的工作站,见下面的规格)。也许这与处理过程中出现的那些错误有关?
convert-im6.q16: width or height exceeds limit `black' @ error/cache.c/OpenPixelCache/3911.
convert-im6.q16: ImageSequenceRequired `-composite' @ error/mogrify.c/MogrifyImageList/7995.
convert-im6.q16: no images defined `./GREENSCREEN.6799/lut.png' @ error/convert.c/ConvertImageCommand/3258.
convert-im6.q16: unable to open image `./GREENSCREEN.6799/lut.png': No such file or directory @ error/blob.c/OpenBlob/2874.
convert-im6.q16: ImageSequenceRequired `-clut' @ error/mogrify.c/MogrifyImageList/7870.
convert-im6.q16: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG `mask.png' @ warning/png.c/MagickPNGWarningHandler/1667.

工作站规格

  • 内存: 125.8 GiB
  • 处理器: AMD® Ryzen 9 3900x 12核心处理器 × 24
  • 显卡: GeForce GTX 970/PCIe/SSE2 (两个)

你试过这个吗:http://www.fmwconcepts.com/imagemagick/greenscreen/index.php?如果这是商业项目,你需要付费使用它。 - dlemstra
是的,它的效果相当不错,但处理图像的时间太长了(请参见我的更新问题)。 - Florian Ludewig
关于您从我的脚本中收到的错误消息,您的ImageMagick版本是多少? - fmw42
3个回答

5

我们知道背景是绿色的,并且通过它的颜色可以与物体区分开来,因此我建议使用颜色阈值处理。为此,我编写了一个简单的OpenCV Python代码以演示结果。

首先,我们需要安装OpenCV。

sudo apt update
pip3 install opencv-python
# verify installation
python3 -c "import cv2; print(cv2.__version__)"

接下来,我们在与图片相同的目录中创建一个名为skull.py的脚本。

import cv2
import numpy as np

def show_result(winname, img, wait_time):
    scale = 0.2
    disp_img = cv2.resize(img, None, fx=scale, fy=scale)
    cv2.imshow(winname, disp_img)
    cv2.waitKey(wait_time)

img = cv2.imread('skull.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of green color in HSV
lower_green = np.array([70, 200, 100])
upper_green = np.array([90, 255, 255])
# Threshold the HSV image to extract green color
mask = cv2.inRange(hsv, lower_green, upper_green)
mask = cv2.bitwise_not(mask)

#cv2.imwrite('mask.png', mask)
show_result('mask', mask, 0)
cv2.destroyAllWindows()

您可以轻松地找到使用OpenCV进行HSV颜色操作的教程。我不会介绍此处使用的函数,但其中一部分很重要。图像操作通常在RGB颜色空间中完成,其中包含红色、绿色和蓝色组件。然而,HSV更像人类视觉系统,其中包含色相、饱和度和值组件。您可以在此处找到转换方法。由于我们根据感知分离颜色,因此HSV更适合此任务。
关键是适当选择阈值。我通过检查选择了大约80作为色相(最大值为180),并且对于饱和度和值分别选择高于200和100(最大值为255)。您可以通过以下代码打印特定像素的值:
rows,cols,channels = hsv.shape
print(hsv[row, column])

请注意,原点位于左上角。
以下是结果:mask.png 可能需要两件事情。一件是对一组图像进行操作,使用for循环可以轻松完成。另一件是如果您不喜欢结果的某个部分,您可能想要知道像素位置并相应地更改阈值。这可以使用鼠标事件实现。
for i in range(1, 100):
    img = imread(str(i) + '.jpg')

def mouse_callback(event, x, y, flags, params):
    if event == cv2.EVENT_LBUTTONDOWN:
        row = y
        column = x
        print(row, column)

winname = 'img'
cv2.namedWindow(winname)
cv2.setMouseCallback(winname, mouse_callback)

请注意show_result函数会按比例因子调整图像大小。

如果您不想处理像素位置,而希望得到平滑的结果,可以应用形态学变换。特别是开运算和闭运算可以完成工作。

kernel = np.ones((11,11), np.uint8)
opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

使用开运算的结果 (核大小为 11x11): opened.png


哇,这正是我想要的,谢谢!我不确定为什么我没有想到使用OpenCV :D - Florian Ludewig
在skull.py脚本中,我应该把这3行用于形态学转换的代码放在哪里? - Wintermute
@Wintermute 在脚本末尾显示之前,这里使用形态转换进行最后的修饰。 - Burak

3

我无法将此内容放入评论中,因此将其作为答案。如果您想使用Fred的 greenscreen 脚本,您可以使用 GNU Parallel 以加快速度。

假设您使用以下命令:

mkdir out
greenscreen image.png out/image.png

如果你需要处理成千上万张图片,可以采用以下方法,让所有的CPU核心并行工作,直到所有图片都被处理完成:

mkdir out
parallel greenscreen {} out/{} ::: *.png

绝对是使用这个脚本的最佳方式!但我更喜欢使用@Burak的解决方案,因为它更快,代码也更简单。无论如何,还是谢谢你! - Florian Ludewig

2

如果您使用类Unix系统,可以尝试我的greenscreen脚本,它调用了ImageMagick,并以Bash Unix编写。例如:

输入:

enter image description here

greenscreen img.jpg result.png

结果(绿色变为透明):

enter image description here

该结果已经缩小了50%,以避免StackOverflow认为原始结果过大。但是,StackOverflow已将图像从透明PNG更改为白色背景JPG。

请注意,其他图像可能需要除默认值之外的参数值。您可以在http://www.fmwconcepts.com/imagemagick/获取我的脚本。请注意,对于商业用途,您需要与我联系以获取许可证。


这真的很好!但不幸的是,对我来说这是不可行的,因为处理一张图片需要40秒,这意味着处理所有图片将需要大约8个小时(尽管我有一台相当强大的工作站)。 - Florian Ludewig
我曾经在Mac OSX上运行IM 6.9.10.97 Q16。我测试了IM 6.9.10.23,它花费了19秒。我的Mac只有两个核心。你有多个核心吗?如果是这样,你是否启用了OpenMP?当你执行convert -version时,它会列出来。 - fmw42
是的,我有很多核心。当我运行 convert -version 时,它也告诉我:Features: Cipher DPC Modules OpenMP - Florian Ludewig
是的,这完全有道理,但老实说,我不知道如何在不打开24个终端窗口的情况下完成这个任务:D(尤其是我需要防止重复处理)。 - Florian Ludewig
我会尝试的!但说实话,肯定有更好的方法(我的意思是OBS能够在遮挡绿屏的情况下流畅地进行60帧的直播)。但我真的很感激你的努力,谢谢!这绝对是一个临时解决方案。 - Florian Ludewig
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接