OpenCV - 将掩膜应用于彩色图像

68
如何在最新的python绑定(cv2)中为彩色图像应用掩码?在以前的python绑定中,最简单的方法是使用cv.Copy,例如:

cv.Copy(dst, src, mask)

但是这个函数在cv2绑定中不可用。有没有不使用样板代码的解决方法?


你想要做什么?由于它支持numpy,你可以使用numpy函数。 - Abid Rahman K
我正在对原始图像进行HSV颜色空间的一些过滤。 - pzo
5个回答

107

如果您已经有掩模图像,可以使用 cv2.bitwise_and 函数。

要检查下面的代码:

img = cv2.imread('lena.jpg')
mask = cv2.imread('mask.png',0)
res = cv2.bitwise_and(img,img,mask = mask)

对于 lena 图像和矩形掩模,输出结果如下所示。

输入图像和掩模


1
我正在尝试做类似的事情。掩码应该是黑色还是白色?对于这个基础问题感到抱歉,我是opencv的新手。谢谢。 - Albert Vonpupp
8
没错,你说得对,口罩应该是黑白单通道的。如果你想处理图像中的某个区域,那么在口罩中应将该区域设为白色,其余部分应为黑色。不需要为提出这个问题而道歉,每个人在开始学习时都是新手,就连我也是。 - Abid Rahman K
非常感谢,Abid!我会在今晚回家后尝试它。我必须将一张图片分成几个区域(并将此过程重复到几个图像中)。问候! - Albert Vonpupp
我可以做到 =),现在我需要摆脱黑色区域... 你有什么办法吗? - Albert Vonpupp
2
你可以选择切割图像。ROI = img [y:y + h,x:x + w],其中 x,y 是区域左上角点,h,w 是区域的高度和宽度。 - Abid Rahman K
显示剩余3条评论

37

如果您想要背景颜色不是纯黑色,那么这里有一个解决方案。我们只需要反转遮罩并将其应用于与之大小相同的一张背景图片上,然后将背景和前景组合在一起即可。这种解决方案的优点是背景可以是任何东西(甚至是其他图像)。

此示例修改自霍夫圆变换。第一张图片是OpenCV标志,第二张是原始遮罩,第三张是背景+前景组合。

apply mask and get a customized background

# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghcircles/py_houghcircles.html
import cv2
import numpy as np

# load the image
img = cv2.imread('E:\\FOTOS\\opencv\\opencv_logo.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# detect circles
gray = cv2.medianBlur(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY), 5)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=50, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))

# draw mask
mask = np.full((img.shape[0], img.shape[1]), 0, dtype=np.uint8)  # mask is only 
for i in circles[0, :]:
    cv2.circle(mask, (i[0], i[1]), i[2], (255, 255, 255), -1)

# get first masked value (foreground)
fg = cv2.bitwise_or(img, img, mask=mask)

# get second masked value (background) mask must be inverted
mask = cv2.bitwise_not(mask)
background = np.full(img.shape, 255, dtype=np.uint8)
bk = cv2.bitwise_or(background, background, mask=mask)

# combine foreground+background
final = cv2.bitwise_or(fg, bk)

注意:最好使用OpenCV的方法,因为它们是经过优化的。


17
import cv2 as cv

im_color = cv.imread("lena.png", cv.IMREAD_COLOR)
im_gray = cv.cvtColor(im_color, cv.COLOR_BGR2GRAY)

在这一点上,您有一张彩色和一张灰度图像。我们正在处理8位、uint8图像。这意味着图像的像素值可以在[0,255]的范围内,并且值必须是整数。

left-color,right-gray

让我们进行二进制阈值操作。它创建一个黑白掩膜图像。黑色区域的值为0,白色区域的值为255

_, mask = cv.threshold(im_gray, thresh=180, maxval=255, type=cv.THRESH_BINARY)
im_thresh_gray = cv.bitwise_and(im_gray, mask)

左侧显示了掩模,其右侧的图像是在灰度图像和掩模之间应用位与操作的结果。发生的情况是,掩模中像素值为零(黑色)的空间位置在结果图像中变为像素值为零。掩模中像素值为255(白色)的位置,结果图像保留其原始灰度值。

left-mask,right-bitwise_and_with_mask

为了将这个掩膜应用到我们的原始彩色图像上,我们需要将掩膜转换成一个三通道图像,因为原始彩色图像是一个三通道图像。
mask3 = cv.cvtColor(mask, cv.COLOR_GRAY2BGR)  # 3 channel mask

然后,我们可以使用相同的bitwise_and函数将这个三通道掩码应用到我们的彩色图像上。

im_thresh_color = cv.bitwise_and(im_color, mask3)

mask3 是代码中左侧的图像,im_thresh_color 是其右侧的图像。

left-mask-3channel,right-bitwise_and_with_3channel-mask

您可以绘制结果并自行查看。

cv.imshow("original image", im_color)
cv.imshow("binary mask", mask)
cv.imshow("3 channel mask", mask3)
cv.imshow("im_thresh_gray", im_thresh_gray)
cv.imshow("im_thresh_color", im_thresh_color)
cv.waitKey(0)

我在这里找到的原始图像是lenacolor.png


3

Abid Rahman K提供的答案并不完全正确。我也尝试过,发现很有帮助,但仍然遇到了一些困难。

以下是如何使用给定遮罩复制图像的方法。

x, y = np.where(mask!=0)
pts = zip(x, y)
# Assuming dst and src are of same sizes
for pt in pts:
   dst[pt] = src[pt]

这个方法虽然有点慢,但能给出正确的结果。
编辑:
Pythonic方式。
idx = (mask!=0)
dst[idx] = src[idx]

4
在这里没有必要重新发明轮子,因为cv2中的“bitwise_and”方法完全符合要求。不仅如此,它的功能非常明显。 - Zoran Pavlovic

3

其他方法都假设二进制掩码。如果你想使用真实值的单通道灰度图像作为掩码(例如来自 alpha 通道),你可以将其扩展到三个通道,然后用于插值:

assert len(mask.shape) == 2 and issubclass(mask.dtype.type, np.floating)
assert len(foreground_rgb.shape) == 3
assert len(background_rgb.shape) == 3

alpha3 = np.stack([mask]*3, axis=2)
blended = alpha3 * foreground_rgb + (1. - alpha3) * background_rgb

请注意,操作成功需要 mask 在范围 0..1 内。同时,假定 1.0 表示仅保留前景,而 0.0 表示仅保留背景。
如果掩膜的形状可能为 (h, w, 1),那么这会有所帮助:
alpha3 = np.squeeze(np.stack([np.atleast_3d(mask)]*3, axis=2))

在这里,np.atleast_3d(mask)将掩膜变成(h, w, 1)格式,如果它原先是(h, w)格式。而np.squeeze(...)则将结果从(h, w, 3, 1)改变为(h, w, 3)格式。


感谢您的说明。我如何使用二进制掩码将RGB像素更改为特定值?例如,rgb_image [bin_msk] = [41,34,128]。我的图像大小为3000px x 3000px。 - Tin

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