如何从颜色字典中快速更改图像中的像素?

Posted

技术标签:

【中文标题】如何从颜色字典中快速更改图像中的像素?【英文标题】:How can I quickly change pixels in a image from a color dictionary? 【发布时间】:2019-12-02 07:06:58 【问题描述】:

我有一张图片,我想从颜色图中更改图片中的所有颜色,例如。 (10,20,212) : (60,40,112)...

目前,我正在读取图像 OpenCV,然后遍历图像数组并更改每个像素,但这非常慢。

有什么方法可以让我更快地做到这一点?

【问题讨论】:

你的 hashmap 的大小是多少? 这是一个分段掩码,我的 hashmap 大小在 100 左右 你是什么意思“你想从颜色图中改变图像中的所有像素”?把它们从什么变成什么?请出示您的形象。还有你的颜色图。 我想用 BGR 值从像 (10,20,212) : (60,40,112)... 这样的字典中更改图像的像素,我必须遍历图像并使用 color_map 字典对其进行更改。出于保密考虑,我无法展示图片。 您的意思是将像素值rgb(10,20,212) 更改为rgb(60,40,112)?如果是,您要重新映射多少种这样的颜色? 【参考方案1】:

我为这个问题提供了两个答案。这个答案更多基于 PIL/Pillow,而另一个更多基于 OpenCV。结合我的其他答案阅读此答案,并可能混合搭配。

您可以使用调色板来完成。如果您不熟悉调色板图像,而不是在每个像素位置都有一个 RGB 值,您可以在最多 256 种颜色的调色板中使用一个简单的 8 位索引。

因此,我们可以做的是将您的图像加载为 PIL 图像,并将其量化为您拥有的输入颜色集。然后每个像素都会有你地图中颜色的索引。然后只需将调色板替换为您要映射到的颜色即可。

#!/usr/bin/env python3

import numpy as np
from PIL import Image

def QuantizeToGivenPalette(im, palette):
    """Quantize image to a given palette.

    The input image is expected to be a PIL Image.
    The palette is expected to be a list of no more than 256 R,G,B values."""

    e = len(palette)
    assert e>0,    "Palette unexpectedly short"
    assert e<=768, "Palette unexpectedly long"
    assert e%3==0, "Palette not multiple of 3, so not RGB"

    # Make tiny, 1x1 new palette image
    p = Image.new("P", (1,1))

    # Zero-pad the palette to 256 RGB colours, i.e. 768 values and apply to image
    palette += (768-e)*[0]
    p.putpalette(palette)

    # Now quantize input image to the same palette as our little image
    return im.convert("RGB").quantize(palette=p)

# Open input image and palettise to "inPalette" so each pixel is replaced by palette index
# ... so all black pixels become 0, all red pixels become 1, all green pixels become 2...
im = Image.open('image.png').convert('RGB')

inPalette = [
    0,0,0,    # black
    255,0,0,  # red
    0,255,0,  # green
    0,0,255,  # blue
    255,255,255 # white
    ]
r = QuantizeToGivenPalette(im,inPalette)

# Now simply replace the palette leaving the indices unchanged
newPalette = [
    255,255,255,  # white
    0,255,255,    # cyan
    255,0,255,    # magenta
    255,255,0,    # yellow
    0,0,0         # black
    ]

# Zero-pad the palette to 256 RGB colours, i.e. 768 values
newPalette += (768-len(newPalette))*[0]

# And finally replace the palette with the new one
r.putpalette(newPalette)

# Save result
r.save('result.png')

输入图像

输出图像

因此,要使用将旧颜色值映射到新颜色值的字典具体执行您所要求的操作,您需要将 oldPalette 初始化为字典的 keys 并将 newPalette 初始化为字典的

关键字:Python、PIL、Pillow、图像、图像处理、量化、量化、特定调色板、给定调色板、指定调色板、已知调色板、重映射、重映射、颜色映射、映射。

有一些关于调色图像here 和here 的有用词。

【讨论】:

【参考方案2】:

我为这个问题提供了两个答案。这个答案更多基于 OpenCV 而另一个更多基于 PIL/Pillow。结合我的其他答案阅读此答案,并可能混合搭配。

您可以使用 Numpy 的 linalg.norm() 查找颜色之间的距离,然后使用 argmin() 选择最近的。然后,您可以使用 LUT “查找表” 根据图像中的现有值查找新值。

#!/usr/bin/env python3

import numpy as np
import cv2

def QuantizeToGivenPalette(im, palette):
    """Quantize image to a given palette.
    
    The input image is expected to be a Numpy array.
    The palette is expected to be a list of R,G,B values."""

    # Calculate the distance to each palette entry from each pixel
    distance = np.linalg.norm(im[:,:,None] - palette[None,None,:], axis=3)

    # Now choose whichever one of the palette colours is nearest for each pixel
    palettised = np.argmin(distance, axis=2).astype(np.uint8)

    return palettised

# Open input image and palettise to "inPalette" so each pixel is replaced by palette index
# ... so all black pixels become 0, all red pixels become 1, all green pixels become 2...
im=cv2.imread("image.png",cv2.IMREAD_COLOR)

inPalette = np.array([
   [0,0,0],             # black
   [0,0,255],           # red
   [0,255,0],           # green
   [255,0,0],           # blue
   [255,255,255]],      # white
   )

r = QuantizeToGivenPalette(im,inPalette)

# Now make LUT (Look Up Table) with the 5 new colours
LUT = np.zeros((5,3),dtype=np.uint8)
LUT[0]=[255,255,255]  # white
LUT[1]=[255,255,0]    # cyan
LUT[2]=[255,0,255]    # magenta
LUT[3]=[0,255,255]    # yellow
LUT[4]=[0,0,0]        # black

# Look up each pixel in the LUT
result = LUT[r]

# Save result
cv2.imwrite('result.png', result)

输入图像

输出图像

关键字:Python、PIL、Pillow、图像、图像处理、量化、量化、特定调色板、给定调色板、指定调色板、已知调色板、重映射、重映射、颜色映射、映射、LUT , linalg.norm。

【讨论】:

很好的解决方案。但是,我似乎得到了错误的结果(可能是由于 uint8 溢出,在减法期间)。 inPalette 的类型不应该与 uint8 不同吗?【参考方案3】:

我认为您可能会发现使用 opencv 的内置 LUT 功能很有帮助,如 here 所述。

该函数已经有一个python绑定,它将原始矩阵和LUT作为输入,并将新矩阵作为输出返回。

没有在 python 中使用它的教程,但是有一个在 C++ 中使用它的教程,我想这会很有用,找到 here。该教程将这种方法列为解决此类问题的最快方法。

【讨论】:

以上是关于如何从颜色字典中快速更改图像中的像素?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改灰度图像的 cairo 上下文源颜色

更改 SKSpriteNode 图像的颜色?

在android显示中更改像素颜色

如何更改图像中的像素

如何从imageview中的png获取特定颜色的所有像素

如何更改UIImage / UIImageView的单个像素的颜色