【问题标题】:Remove colors from an image if they are not in a list python如果图像不在列表 python 中,则从图像中删除颜色
【发布时间】:2019-11-21 14:02:52
【问题描述】:

我有一个相对较大的 RGBA 图像(转换为 numpy),我需要替换所有未出现在列表中的颜色。我怎么能以 pythonic 快速的方式做到这一点?

使用简单的迭代我有一个解决这个问题的方法,但是由于图像非常大(2500 x 2500),这个过程非常慢。

# Keep only these colors in the image, otherwise replace with (0,255,0,255)
palette = [[0,0,0,255],[0, 255, 0,255], [255, 0, 0,255], [128, 128, 128,255], [0, 0, 255,255], [255, 0, 255,255], [0, 255, 255,255], [255, 255, 255,255], [128, 128, 0,255], [0, 128, 128,255], [128, 0, 128,255]]

# Current slow solution with a 2500 x 2500 x 4 array (mask)
for z in range(mask.shape[0]):
    for y in range(mask.shape[1]):
        if (mask[z,y,:].tolist() not in palette):
            mask[z, y] = (0,255,0,255)

每张图像的预期操作时间:不到半分钟

当前时间:两分钟

【问题讨论】:

  • 请注意,你不是在做递归,你是在做迭代。
  • 已编辑,谢谢@Jmonsky

标签: python image list numpy colors


【解决方案1】:

使用此代码,我能够在 33 到 37 秒之间的任意位置替换随机生成的 2500 x 2500 图像。你让我的机器在 51 到 57 秒之间执行的方法。

mask = np.random.rand(2500,2500,4)
mask = np.floor(mask * 255)

palette = np.array([[0,0,0,255],[0, 255, 0,255], [255, 0, 0,255], [128, 128, 128,255], [0, 0, 255,255], [255, 0, 255,255], [0, 255, 255,255], [255, 255, 255,255], [128, 128, 0,255], [0, 128, 128,255], [128, 0, 128,255]])
default = np.array([0,255,0,255])
for z in range(mask.shape[0]):
    for y in range(mask.shape[1]):
        if not mask[z,y,:] in palette:
            mask[z,y,:] = default

【讨论】:

    【解决方案2】:

    这绝对不是您应该关注的时间窗口。这是broadcasting的一种方法:

    # palette.shape == (4,11)
    palette = np.array(palette).transpose()
    
    # sample a.shape == (2,2,4)
    a= np.array([[[ 28, 231, 203, 235],
             [255, 0, 0,255]],
    
           [[ 50, 152,  36, 151],
            [252,  43,  63,  25]]])
    
    # mask
    # all(2) force all channels to be equal
    # any(-1) matches any color
    mask = (a[:,:,:, None] == palette).all(2).any(-1)
    
    # replace color
    rep_color = np.array([0,255,0,255])
    
    # np.where to the rescue:
    ret = np.where(mask[:,:,None], a, rep_color[None,None,:])
    

    样本:

    变成

    对于a = np.random.randint(0,256, (2500,2500,4)),它需要:

    每个循环 5.26 秒 ± 179 毫秒(平均值 ± 标准偏差,7 次运行,每个循环 1 个)


    更新:如果您强制所有内容为 np.uint8,您可以将频道合并到 int32 并获得更快的速度:

    a = np.random.randint(0,256, (2500,2500,4), dtype=np.uint8)
    p = np.array(palette, dtype=np.uint8).transpose()
    
    # zip the data into 32 bits
    # could be even faster if we handle the memory directly
    aa = a[:,:,0] * (2**24) + a[:,:,1]*(2**16) + a[:,:,2]*(2**8) + a[:,:,3]
    pp = p[0]*(2**24) + p[1]*(2**16) + p[2]*(2**8) + p[3]
    mask = (aa[:,:,None]==pp).any(-1)
    ret = np.where(mask[:,:,None], a, rep_color[None,None,:])
    

    需要:

    每个循环 1.34 秒 ± 29.7 毫秒(平均值 ± 标准偏差,7 次运行,每个循环 1 个)

    【讨论】:

    • 非常好的答案?
    【解决方案3】:

    我尝试了pyvips。这是一个线程化的流式图像处理库,因此速度很快且不需要太多内存。

    import sys
    import pyvips
    from functools import reduce
    
    # Keep only these colors in the image, otherwise replace with (0,255,0,255)
    palette = [[0,0,0,255], [0, 255, 0,255], [255, 0, 0,255], [128, 128, 128,255], [0, 0, 255,255], [255, 0, 255,255], [0, 255, 255,255], [255, 255, 255,255], [128, 128, 0,255], [0, 128, 128,255], [128, 0, 128,255]]
    
    im = pyvips.Image.new_from_file(sys.argv[1], access="sequential")
    
    # test our image against each sample ... bandand() will AND all image bands
    # together, ie. we want pixels where they all match
    masks = [(im == colour).bandand() for colour in palette]
    
    # OR all the masks together to find pixels which are in the palette
    mask = reduce((lambda x, y: x | y), masks)
    
    # pixels not in the mask become [0, 255, 0, 255]
    im = mask.ifthenelse(im, [0, 255, 0, 255])
    
    im.write_to_file(sys.argv[2])
    

    在这台 2015 i5 笔记本电脑上使用 2500x 2500 像素的 PNG,我看到了:

    $ /usr/bin/time -f %M:%e ./replace-pyvips.py ~/pics/x.png y.png
    55184:0.92
    

    因此最大内存为 55mb,经过时间为 0.92 秒。

    我尝试了 Quang Hoang 的优秀 numpy 版本进行比较:

    p = np.array(palette).transpose()
    
    # mask
    # all(2) force all channels to be equal
    # any(-1) matches any color 
    mask = (a[:,:,:, None] == p).all(2).any(-1)
    
    # replace color
    rep_color = np.array([0,255,0,255])
    
    # np.where to the rescue:
    a = np.where(mask[:,:,None], a, rep_color[None,None,:])
    
    im = Image.fromarray(a.astype('uint8'))
    im.save(sys.argv[2])
    

    在相同的 2500 x 2500 像素图像上运行:

    $ /usr/bin/time -f %M:%e ./replace-broadcast.py ~/pics/x.png y.png
    413504:3.08
    

    410MB 内存峰值,经过 3.1 秒。

    正如 Hoang 所说,通过比较 uint32 可以进一步加快这两个版本的速度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-03
      • 2021-01-16
      • 2020-02-07
      • 2014-02-08
      • 2023-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多