Numpy.putmask 与图像

3 投票
3 回答
2118 浏览
提问于 2025-04-16 15:01

我有一张图片,已经转换成了一个包含RGBA值的数组。假设这个数组的大小是50 x 50 x 4。

我想把所有像素值为array([255, 255, 255, 255])的地方,替换成array([0, 0, 0, 0])。也就是说:

from numpy import *
from PIL import Image
def test(mask):
        mask = array(mask)
        find = array([255, 255, 255, 255])
        replace = array([0, 0, 0, 0])
        return putmask(mask, mask != find, replace)

mask = Image.open('test.png')
test(mask)

我哪里做错了?这让我遇到了一个ValueError: putmask: mask and data must be the same size的错误。不过如果我把数组改成数字(查找255,替换成0),那就可以正常工作。

3 个回答

2

这个解决方案使用了 putmask,我觉得它和提问者的代码最接近。提问者的原始代码中有两个错误需要注意:1) putmask 是一种就地操作,它不会返回任何值,而是返回 None。2) putmask 还需要两个数组的大小相同。可惜的是,它没有 axis 这个参数。

import numpy as np
from PIL import Image

img1 = Image.open('test.png')
arry = np.array(img1)
find = np.array([255, 255, 255, 255])
repl = np.array([  0,   0,   0,   0])
# this is the closest to the OPs code I could come up with that
# compares each pixel array with the 'find' array
mask = np.all(arry==find, axis=2)
# I iterate here just in case repl is not always the same value
for i,rep in enumerate(repl):
    # putmask works in-place - returns None
    np.putmask(arry[:,:,i], mask, rep)

img2 = Image.fromarray(arry, mode='RGBA')
img2.save('testx.png')
4

更简洁的做法是

img = Image.open('test.png')
a = numpy.array(img)
a[(a == 255).all(axis=-1)] = 0
img2 = Image.fromarray(a, mode='RGBA')

更一般来说,如果findrepl中的项目不完全相同,你也可以这样做

find = [1, 2, 3, 4]
repl = [5, 6, 7, 8]
a[(a == find).all(axis=-1)] = repl
2

一种实现这种通道遮罩的方法是将数组分成红色(r)、绿色(g)、蓝色(b)和透明度(a)四个通道,然后使用numpy的逻辑位运算来定义索引:

import numpy as np
import Image

def blackout(img):
    arr = np.array(img)
    r,g,b,a=arr.T
    idx = ((r==255) & (g==255) & (b==255) & (a==255)).T
    arr[idx]=0
    return arr

img = Image.open('test.png')
mask=blackout(img)
img2=Image.fromarray(mask,mode='RGBA')
img2.show()

撰写回答