如何从颜色字典中快速更改图像中的像素?
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。该教程将这种方法列为解决此类问题的最快方法。
【讨论】:
以上是关于如何从颜色字典中快速更改图像中的像素?的主要内容,如果未能解决你的问题,请参考以下文章