如何使用numpy对图像进行模式遮罩?

27 投票
5 回答
52837 浏览
提问于 2025-04-15 18:40

我正在使用numpy来构建像素数组。一个800x600的图像是一个三维数组,大小是800x600x3。这里的3代表每个像素有三个颜色通道(红、绿、蓝)。我还有一个类似的数组,里面有固定的图案(像棋盘格,具体可以查看这里)。另外,我还有一个800x600的数组,里面存的是掩码值。在掩码值为零的地方,我想把图案的像素复制到图像的像素上。而在掩码值不为零的地方,我想保持图像的像素不变。

>>> image.shape
(800, 600, 3)
>>> chex.shape
(800, 600, 3)
>>> mask.shape
(800, 600)

我觉得这样做应该可以:

image[mask == 0,...] = chex

但是却出现了“ValueError: array is not broadcastable to correct shape”的错误。

我该用什么方法在掩码为零的地方把棋盘格的像素复制到图像的像素上呢?

5 个回答

4

我发现最简单的方法是创建一个“遮罩”,其中1代表“要保留的像素”,而0代表“要去掉的像素”。

然后我用这个遮罩去乘我的图像,这样就能去掉不需要的像素。比如说,如果只想保留人像的边框(外面部分),可以这样做:

from scipy.misc import imread
import matplotlib.pyplot as plt
import numpy as np

im = imread('portrait.jpg', mode='L') # read in image
plt.imshow(im) # show the original image

enter image description here

mask = np.ones(im.shape) # create a mask with the image's shape
bw = 0.1 # identify border width and height as fraction of image size
bx = int(im.shape[1] * bw) # get the x dimension border width
by = int(im.shape[0] * bw) # get the y dimension border height
mask[bx:-bx,by:-by] = 0 # create a mask with 1 for border and 0 for inside

masked = im * mask # multiply `im` by the mask to zero out non-border pixels
plt.imshow(masked) # show the result of the masking operation

enter image description here

5

我想用@unutbu的回答来举个例子。在这个例子中,我有一张猫的图片,我把它旋转了一下。旋转后,图片的边缘出现了一些黑色的部分,这些黑边看起来很难看,尤其是当我把它放到不是黑色的背景上时。

import matplotlib.pyplot as plt
from scipy.ndimage import rotate


cat = plt.imread('cat.jpeg')
bg = plt.imread('background.jpeg')


rotcat = rotate(cat, angle=8, reshape=True) ## rotating creates some black edges
height, width, _ = rotcat.shape

bgcopy = bg.copy() ## create a copy of the background; paste on copy

x, y = 40, 50 
bgcopy[x:x+height, y:y+width] = rotcat
plt.imsave('cat-on-bg-mask.jpg', bgcopy)

不好的粘贴效果

所以,我找到了这些黑边的区域,并把这些区域的颜色换成了原来的背景颜色。

mask_ind = (bgcopy == 0)
bgcopy[mask_ind] = bg[mask_ind]
plt.imsave('cat-on-bg.jpg', bgcopy)

好的粘贴效果

我还想提一下,PIL.Image(来自Pillow库)可以用更少的步骤把一张图片粘贴到另一张图片上。

35
idx=(mask==0)
image[idx]=chex[idx]

注意,image的形状是(800,600,3),而idx的形状是(800,600)。关于索引的规则可以参考这里

如果选择的元组比需要的维度小,那么就会在选择元组的末尾添加必要的对象,以使修改后的选择元组长度达到N。

因此,索引数组有自己的一种广播能力。idx的形状会被提升到(800,600,:)

撰写回答