简体   繁体   English

使用 OpenCV/Python 从图像中删除绿色背景屏幕

[英]Remove green background screen from image using OpenCV/Python

So I have this image below:所以我在下面有这张图片: 在此处输入图像描述

As you can see, there is some noisy green outline in the image.如您所见,图像中有一些嘈杂的绿色轮廓。 This is by far, my latest output with OpenCV with Python.到目前为止,这是我使用 Python 使用 OpenCV 的最新输出。 And I'm still a beginner with this so I need your help.我还是个初学者,所以我需要你的帮助。

Assuming I were to create a completely new script and feed this image and 'clean' the image, how will I do it?假设我要创建一个全新的脚本并提供此图像并“清理”该图像,我将如何做?

EDIT: This is the original image:编辑:这是原始图像: 在此处输入图像描述

Use Canny Edge Detection on your image.在您的图像上使用Canny 边缘检测 Then depending how it looks like use Morphological Dilation ( cv.dilate() ) to make the edges a bit thicker.然后根据它看起来如何使用形态膨胀( cv.dilate() )使边缘更厚一点。 Afterwards remove the edges from the green channel of the image or reduce the brightness of green channel.然后从图像的绿色通道中去除边缘或降低绿色通道的亮度。 Or make the pixels transparent.或者使像素透明。

This post states the following approach: 这篇文章说明了以下方法:

1.) Convert green pixels to transparency. 1.) 将绿色像素转换为透明度。 Basically uses a filtering rule in the HSV color space.基本上使用 HSV 颜色空间中的过滤规则。

2.) On the hair and some boundary pixels colors are mixed with green. 2.) 在头发和一些边界像素颜色上混有绿色。 To reduce this problem these pixels are filtered and balanced to reduce their green proportion.为了减少这个问题,这些像素被过滤和平衡以减少它们的绿色比例。

3.) Apply a gradient transparency to all boundary pixels. 3.) 对所有边界像素应用渐变透明度。

Here is a rather simple approach.这是一个相当简单的方法。

Background:背景:

Dominant colors like red, green, blue and yellow can be segmented pretty easily when analyzed in LAB color space .LAB 颜色空间中进行分析时,可以很容易地分割主要颜色,如红色、绿色、蓝色和黄色。 LAB space has 3 channels, out of which only 2 are color channels while 1 is a brightness channel: LAB空间有3个通道,其中只有2个是颜色通道,1个是亮度通道:

  • L-channel: represents the brightness L-channel:代表亮度
  • A-channel: represents the amount of red/green color A通道:代表红/绿颜色的量
  • B-channel: represents the amount of blue/yellow color B通道:代表蓝/黄颜色的量

在此处输入图像描述

Observing the plot above:观察上图:

  1. a-axis clearly shows red/green color on its extremes a 轴在其极端处清楚地显示红色/绿色
  2. b-axis shows blue/yellow color on its extremes b 轴在其极端处显示蓝色/黄色

Applying a suitable threshold in the corresponding channels makes it easy for us to segment these colors.在相应的通道中应用合适的阈值可以让我们轻松地分割这些颜色。

Approach:方法:

We will use the above info as a base to approach the problem at hand:我们将使用上述信息作为解决手头问题的基础:

1. Perform basic masking: 1. 执行基本掩蔽:

-> Convert image to LAB space -> Threshold a-channel to isolate green background -> Masking original image with binary mask ->将图像转换为 LAB 空间->阈值 a 通道​​以隔离绿色背景->使用二进制掩码屏蔽原始图像

img = cv2.imread('green_background.jpg')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_channel = lab[:,:,1]
th = cv2.threshold(a_channel,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
masked = cv2.bitwise_and(img, img, mask = th)    # contains dark background
m1 = masked.copy()
m1[th==0]=(255,255,255)                          # contains white background

在此处输入图像描述

2. Remove green shade along the border: 2.去除沿边界的绿色阴影:

-> Convert masked image LAB space -> Normalize the a-channel mlab[:,:,1] to use the entire intensity range between [0-255] -> Inverse binary threshold to select area with green borders ->转换蒙版图像 LAB 空间->归一化 a 通道mlab[:,:,1]以使用 [0-255] 之间的整个强度范围->反向二进制阈值以选择具有绿色边框的区域

mlab = cv2.cvtColor(masked, cv2.COLOR_BGR2LAB)
dst = cv2.normalize(mlab[:,:,1], dst=None, alpha=0, beta=255,norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)

在此处输入图像描述

Above image is the a-channel with dark pixels around the border.上图是边框周围有暗像素的 a 通道​​。

threshold_value = 100
dst_th = cv2.threshold(dst, threshold_value, 255, cv2.THRESH_BINARY_INV)[1]

Above is the threshold image to segment green shaded regions.以上是分割绿色阴影区域的阈值图像。

在此处输入图像描述

In the a-channel, having green color represents the lower end of the range [0-255], while red color represents the higher end.在 a 通道​​中,绿色代表范围 [0-255] 的下限,而红色代表上限。 We will set intensity value in the a-channel of the selected pixels to 127:我们将选定像素的 a 通道​​中的强度值设置为 127:

mlab2 = mlab.copy()
mlab[:,:,1][dst_th == 255] = 127

在此处输入图像描述

Now in the above image, we do not see the dark borders around the person.现在在上图中,我们看不到人周围的黑色边框。

Convert the image to BGR and the pixels that were dark (0) in the threshold image are set to white (255, 255, 255) in the color image将图像转换为 BGR 并将阈值图像中的暗 (0) 像素设置为彩色图像中的白色 (255, 255, 255)

img2 = cv2.cvtColor(mlab, cv2.COLOR_LAB2BGR)
img2[th==0]=(255,255,255)

在此处输入图像描述

The resulting image looks like nothing much has changed, so here is a zoomed in comparison:生成的图像看起来没有太大变化,所以这里是一个放大的比较:

Before Step 2:第 2 步之前:

在此处输入图像描述

After Step 2:步骤 2 之后:

在此处输入图像描述

Conclusion:结论:

The noisy green outline around the border has improved to a certain extent.边界周围嘈杂的绿色轮廓得到了一定程度的改善。 You can try varying threshold_value and experiment.您可以尝试改变threshold_value并进行实验。

The green shade present within the person's face still remains.人脸上的绿色阴影仍然存在。 Sophisticated methods will be needed to get rid of those.将需要复杂的方法来摆脱这些。 Hope this approach helps.希望这种方法有所帮助。

Here is a variation on @Jeru Luke's answer using Python/OpenCV.这是@Jeru Luke 使用 Python/OpenCV 回答的变体。 It does the same thresholding in LAB colorspace, but I simply anti-alias using a blur and a stretch of half the input range of gray values on the A channel.它在 LAB 颜色空间中执行相同的阈值处理,但我只是使用模糊和拉伸 A 通道上灰度值输入范围的一半来消除锯齿。 It has the benefit of reducing some green and smoothing the result so that it will blend smoothly into any background image.它的好处是减少了一些绿色并平滑了结果,以便它可以平滑地融入任何背景图像。

Adjust the blur sigma values and/or the input clip values in the rescale_intensity to adjust the smoothing and amount of green showing.在 rescale_intensity 中调整模糊 sigma 值和/或输入剪辑值,以调整平滑度和绿色显示量。

Input:输入:

在此处输入图像描述

import cv2
import numpy as np
import skimage.exposure

# load image
img = cv2.imread('greenscreen.jpg')

# convert to LAB
lab = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)

# extract A channel
A = lab[:,:,1]

# threshold A channel
thresh = cv2.threshold(A, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# blur threshold image
blur = cv2.GaussianBlur(thresh, (0,0), sigmaX=5, sigmaY=5, borderType = cv2.BORDER_DEFAULT)

# stretch so that 255 -> 255 and 127.5 -> 0
mask = skimage.exposure.rescale_intensity(blur, in_range=(127.5,255), out_range=(0,255)).astype(np.uint8)

# add mask to image as alpha channel
result = img.copy()
result = cv2.cvtColor(img,cv2.COLOR_BGR2BGRA)
result[:,:,3] = mask

# save output
cv2.imwrite('greenscreen_thresh.png', thresh)
cv2.imwrite('greenscreen_mask.png', mask)
cv2.imwrite('greenscreen_antialiased.png', result)

# Display various images to see the steps
cv2.imshow('A',A)
cv2.imshow('thresh', thresh)
cv2.imshow('blur', blur)
cv2.imshow('mask', mask)
cv2.imshow('result', result)

cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold Image:阈值图像:

在此处输入图像描述

Mask Image (for alpha channel):蒙版图像(用于 Alpha 通道):

在此处输入图像描述

Result:结果:

在此处输入图像描述

Try to make a custom threshould, like:尝试制作自定义阈值,例如:

def color_filter(img, r, g, b):
    colors = [b, g, r]
    result = np.zeros(img.shape, dtype=np.uint8)
    for i in range(3):
        result[:, :, i] = np.where(img[:, :, i] < colors[i], 0, 255)
    return result.astype(np.uint8)

UPDATE : here another solution https://codereview.stackexchange.com/a/184059/15056更新:这里是另一个解决方案https://codereview.stackexchange.com/a/184059/15056

To threshould every color channel with a different value.为每个颜色通道设置不同的值。

You can find the best configuration for you using您可以找到最适合您的配置

def test_colors(img):
    cv.imshow("test_colors", img)
    r = 100
    g = 100
    b = 100
    while True:
        k = chr(cv.waitKey(0))
        if k == 'a':
            r += 1
        elif k == 'q':
            r -= 1
        elif k == 's':
            g += 1
        elif k == 'w':
            g -= 1
        elif k == 'd':
            b += 1
        elif k == 'e':
            b -= 1
        elif k == 't':
            r += 1
            g += 1
            b += 1
        elif k == 'g':
            r -= 1
            g -= 1
            b -= 1
        elif k == 'r':
            r = 100
            g = 100
            b = 100
            cv.imshow("test_colors", img)
            continue
        elif k == 'x':
            cv.destroyAllWindows()   
            print("The RGB is ", (r, g, b))
            break
        else:
            continue
        cv.imshow("test_colors", color_filter(img, r, g, b))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM