使用 Grabcut 从图像中删除黑色背景 - Python

Posted

技术标签:

【中文标题】使用 Grabcut 从图像中删除黑色背景 - Python【英文标题】:Remove Black Background from Image with Grabcut - Python 【发布时间】:2021-06-05 06:37:37 【问题描述】:

这是之前发布的关于从 .png 图像中删除黑色背景的问题 (Remove Background from Image - Python) 的后续内容。我在 Q&A 中使用的脚本运行良好,但在完全去除黑色背景的同时保留图像轮廓内的所有内容时,我仍然会遇到一些细微的不一致。我使用 OpenCV 抓取算法尝试了一个不同的脚本,但我仍然遇到同样的问题。我希望能够在不删除图像轮廓内的像素的情况下从图像中删除黑色背景,这是我在运行脚本时使用的脚本以及示例输入/输出图像。

import numpy as np
import cv2
img = cv2.imread("C:\\users\\mdl518\\Desktop\\overhead_image.png") # Input 8-bit 3-channel image
      
mask = np.zeros(img.shape[:2], np.uint8) # img.shape[:2] = first, second values of img.shape
bgdModel = np.zeros((1,65), np.float64) # temporary array for background
fgdModel = np.zeros((1,65), np.float64) # temporary array for foreground
rect = (1,0,img.shape[1],img.shape[0]) # specify w,h beyond bounds of img.shape

# Grabcut algorithm to extract foreground (5 iterations) within region of interest
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

# Split RGB channels and create new alpha channel
r_channel, g_channel, b_channel = cv2.split(img) 
a_channel = np.where((mask==2)|(mask==0), 0, 255).astype('uint8')  
        
img_RGBA = cv2.merge((r_channel, g_channel, b_channel, a_channel)) # add alpha channel to image
cv2.imwrite(".//output_image.png", img_RGBA)  ## write new image to file

输入图像:

输出图像(使用上面的脚本): 输出图像(使用“轮廓”方法):

我正在尝试在 0 到 255 之间调整我的“矩形”边界和蒙版设置,但我仍然无法解决这个问题 - 当前设置似乎在不移除额外像素的情况下最大限度地保留了图像,但它仍然没有保留整个图像。非常感谢任何帮助!

【问题讨论】:

上次编辑的一些东西破坏了输入图像,你能解决这个问题吗?我通过下载您提供的输入图像并在其上运行得到了我的结果。我用于轮廓的蒙版期望背景完全是黑色。这种块状看起来像是来自某种压缩或抗锯齿,导致图像周围的像素不完全是黑色。尝试下载您的输入图像并在其上运行,看看您是否得到不同的结果。 感谢您解决输入图像问题,我已修复它,现在可以再次查看。关于建议的解决方案,我在两台不同的机器上使用原始输入图像重新运行了您的代码,并在两个输出图像中获得了相同的块状。我将研究抗锯齿的可能性,否则尝试调整您的代码以使其正常工作,再次感谢! 您是否尝试下载输入图像并运行它?我的意思是,即使你有原始图像,也可以从堆栈溢出中下载它并通过代码运行它。这应该使它与我在计算机上使用的图像完全相同。这将帮助我们了解这是压缩问题还是发生了其他奇怪的事情。 Ian - 我认为你在做某事。我重新下载了提供的输入图像并在您的脚本中重新运行,它可以无缝运行! :) 值得一提的是,提供的输入图像是 3D 模型的原始 2D 图像预览 (~3MB) 的屏幕截图 (~750KB)。也许将 3D 模型处理为显示为 2D 图像预览是无法保留轮廓的原因? 【参考方案1】:

我们可以制作一个包含图像中所有非黑色区域的蒙版,并使用 findContours 来获取每个黑色区域的形状。通过选择最大的轮廓,我们可以屏蔽掉彩色区域之外的所有内容。

import cv2
import numpy as np

# load image
img = cv2.imread("terrain.png");
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);

# mask
mask = cv2.inRange(gray, 1, 255);

# get contours # OpenCV 3.4, in OpenCV 2* or 4* it returns (contours, _)
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

# get biggest contour
biggest = None;
biggest_size = -1;
for con in contours:
    area = cv2.contourArea(con);
    if area > biggest_size:
        biggest_size = area;
        biggest = con;

# make mask with biggest contour
new_mask = np.zeros_like(mask);
cv2.drawContours(new_mask, [biggest], -1, (255), -1);

# redraw with white
img[new_mask == 0] = (255,255,255);

# show image
cv2.imshow("Image", img);
cv2.imshow("Mask", mask);
cv2.imshow("New Mask", new_mask);
cv2.waitKey(0);

【讨论】:

Ian - 您发布的嵌入图像是脚本的结果吗?我复制/粘贴了代码并在我的最后运行它,但我在图像的直接周边得到块状边缘,即使图像保留在轮廓内。你还做了什么来获得上面的图像吗?再次感谢! 是的,我保存了运行程序后得到的图像。我使用了您发布的输入图像。 您能否在运行代码后发布您的结果,以便我可以看到您在说什么? Ian - 有没有办法在评论中插入图片(比如这个),或者我可以将它嵌入到您的初始帖子中吗? 我认为没有办法将图像添加到 cmets。

以上是关于使用 Grabcut 从图像中删除黑色背景 - Python的主要内容,如果未能解决你的问题,请参考以下文章

Python使用OpenCV去除图像背景自定义前景区域矩形框(Draw Rectangle Around Foreground)使用OpenCV的GrabCut算法去除图像的背景

32opencv入门GrabCut & FloodFill图像分割

OpenCV背景分割/从单个图像中减去

上传和调整 Png 大小会产生黑色背景图像

从图像拼接中删除黑色虚线

从警报对话框中删除黑色背景 - Android