【问题标题】:How to separate human body from background in an image如何在图像中将人体与背景分开
【发布时间】:2021-04-07 16:27:38
【问题描述】:

我一直在尝试将图像中的人体与背景分开,但我所看到的所有方法似乎都不太适合我。

我收集了以下图片;

  • 背景图像
  • 人物在背景中的图像。

现在我想从背景中删除这个人。 我尝试从使用res = cv2.subtract(background, foreground) 的人的图像中减去背景图像(我是图像处理的新手)。

opencv 中的背景减法方法如cv2.BackgroundSubtractorMOG2()cv2.BackgroundSubtractorMOG2() 仅适用于视频或图像序列,而我见过的轮廓检测方法仅适用于实体形状。

grabCut 对我来说不太好用,因为我想自动化这个过程。

鉴于我拥有的图像(背景图像和背景图像以及其中的人),有没有一种方法可以将人从背景中剔除?

【问题讨论】:

  • 请张贴图片和源代码说明您的问题
  • 嗨@ChristophRackwitz 我已将图片添加到问题中
  • 你能关闭相机上的任何自动设置吗?尤其是自动白平衡使得进行简单的减法变得困难,因为当您在第二张照片中进入视野时,它会改变物体的实际颜色。

标签: matlab opencv image-processing image-segmentation scikit-image


【解决方案1】:

我不会为这个问题推荐神经网络。在你有已知背景的情况下,这需要做很多工作。我将逐步介绍在此图像上进行背景分割所采取的步骤。

首先,我切换到 LAB 色彩空间,以获得一些可以使用的抗光通道。我做了一个简单的前景和背景减法,并结合了 a 和 b 通道。

您可以看到,即使使用对光敏感度较低的颜色通道,背景中仍然存在显着的颜色变化。这可能是由于相机上的自动白平衡造成的,当您进入视野时,您会看到一些背景颜色发生了变化。

我采取的下一步是对该图像进行阈值处理。最佳阈值可能并不总是相同,您必须调整到适合您的照片集的范围。

我使用 openCV 的 findContours 函数来获取每个 blob 的分割点,并按大小过滤可用的轮廓。我设置的大小阈值为 15000。作为参考,图像中的人的像素区域为 27551。

那么就只需要裁剪出轮廓即可。

这种技术适用于任何好的阈值策略。如果您可以通过关闭自动设置来提高图片的一致性并确保人与墙壁之间的良好对比度,那么您可以使用更简单的阈值策略并获得良好的效果。

只是为了好玩:

编辑:

我忘记添加我使用的代码:

import cv2
import numpy as np

# rescale values
def rescale(img, orig, new):
    img = np.divide(img, orig);
    img = np.multiply(img, new);
    img = img.astype(np.uint8);
    return img;

# get abs(diff) of all hue values
def diff(bg, fg):
    # do both sides
    lh = bg - fg;
    rh = fg - bg;

    # pick minimum # this works because of uint wrapping
    low = np.minimum(lh, rh);
    return low;

# load image
bg = cv2.imread("back.jpg");
fg = cv2.imread("person.jpg");
fg_original = fg.copy();

# blur
bg = cv2.blur(bg,(5,5));
fg = cv2.blur(fg,(5,5));

# convert to lab
bg_lab = cv2.cvtColor(bg, cv2.COLOR_BGR2LAB);
fg_lab = cv2.cvtColor(fg, cv2.COLOR_BGR2LAB);
bl, ba, bb = cv2.split(bg_lab);
fl, fa, fb = cv2.split(fg_lab);

# subtract
d_b = diff(bb, fb);
d_a = diff(ba, fa);

# rescale for contrast
d_b = rescale(d_b, np.max(d_b), 255);
d_a = rescale(d_a, np.max(d_a), 255);

# combine
combined = np.maximum(d_b, d_a);

# threshold 
# check your threshold range, this will work for
# this image, but may not work for others
# in general: having a strong contrast with the wall makes this easier
thresh = cv2.inRange(combined, 70, 255);

# opening and closing
kernel = np.ones((3,3), np.uint8);

# closing
thresh = cv2.dilate(thresh, kernel, iterations = 2);
thresh = cv2.erode(thresh, kernel, iterations = 2);

# opening
thresh = cv2.erode(thresh, kernel, iterations = 2);
thresh = cv2.dilate(thresh, kernel, iterations = 3);

# contours
_, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

# filter contours by size
big_cntrs = [];
marked = fg_original.copy();
for contour in contours:
    area = cv2.contourArea(contour);
    if area > 15000:
        print(area);
        big_cntrs.append(contour);
cv2.drawContours(marked, big_cntrs, -1, (0, 255, 0), 3);

# create a mask of the contoured image
mask = np.zeros_like(fb);
mask = cv2.drawContours(mask, big_cntrs, -1, 255, -1);

# erode mask slightly (boundary pixels on wall get color shifted)
mask = cv2.erode(mask, kernel, iterations = 1);

# crop out
out = np.zeros_like(fg_original) # Extract out the object and place into output image
out[mask == 255] = fg_original[mask == 255];

# show
cv2.imshow("combined", combined);
cv2.imshow("thresh", thresh);
cv2.imshow("marked", marked);
# cv2.imshow("masked", mask);
cv2.imshow("out", out);
cv2.waitKey(0);

【讨论】:

    【解决方案2】:

    由于很容易找到包含大量人体的数据集,我建议您实施神经网络分割技术来完美提取人体。请查看this 链接以查看类似示例。

    【讨论】:

    • 感谢@Enes Uguroglu 的回复,我会检查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-21
    • 2020-03-03
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多