我想使用 ImageMagick 将图片转换为4位RGBI格式。事实证明,我可以通过将调色板作为单独的步骤来做到这一点。然而结果却很不理想。
例如,这是上述 15 色调色板的输入图像:
这是 RGBI4 调色板的输出:
相比之下,仅使用 8 种颜色和
-posterize 2
,即不允许中等强度,我得到的图片实际上更好:
请注意,第二次转换中使用的所有颜色也可用于第一次转换;它们只是没有被 ImageMagick 选择。
对我来说,这表明问题在于 ImageMagick 用于选择颜色的颜色空间度量。我如何控制它以获得至少与
-posterize 2
一样好的东西?
AFAIK,ImageMagick always 在 RGB 颜色空间中进行重新映射,这不会为您产生很好的结果。因此,我尝试转换为不同的色彩空间,但将其从ImageMagick隐藏,因此它认为图像和调色板是RGB,然后,当重新映射完成时,我承认数据是HSV并转换回RGB。对于您的示例图像来说,它似乎工作正常:
#!/bin/bash
# Make RGBI palette and save in HSV colourspace as three separate PGM images (no colourspace specified so IM treats as RGB)
magick $(for l in 128 255
do
for r in 0 $l
do
for g in 0 $l
do
for b in 0 $l
do
echo "xc:rgb($r,$g,$b)"
done
done
done
done) +append -unique-colors -colorspace HSV -separate pal-%d.pgm
# Split input image into HSV too and hide colourspace using PGM
magick input.png -colorspace HSV -separate chan-%d.pgm
# Recombine image and palette (pretending RGB), remap, admit HSV colourspace
magick chan-[012].pgm -combine \
\( pal-[012].pgm -combine -write MPR:palette +delete \) \
+dither -remap mpr:palette -set colorspace HSV result.png
我们可能必须处理黄色...事实上,这让我想到了另一个想法...我们可以分别采用输入图像中的几种单独颜色,并强制每个颜色都为新的输出颜色,one-at-一次...
是的,这就是解决方案,我可以在输入图像中选择单独的颜色(在任何容差范围内,或“模糊”)并将其更改为我想要的任何颜色。所以在这里,我会让黄色变成红色,绿色变成洋红色:
magick input.png -fuzz 10% \
-fill red -opaque "rgb(201,215,112)" \
-fill magenta -opaque "rgb(95,159,67)" result.png
我将让您对输入颜色进行精确映射...
仅供参考,这是我在 Python 和 OpenCV 中编写的丑陋的概念验证代码 - 顺便说一句,它可以工作:
#!/usr/bin/env python3
import numpy as np
import cv2 as cv
from functools import lru_cache
def show(name, na):
print(f'{name}: {na.shape}, {na.dtype}, {na.min()}, {na.max()}')
# Load image and convert to Luv
im = cv.imread('input.png')
imLab = cv.cvtColor(np.float32(im/255.), cv.COLOR_BGR2Luv)
rows, cols, ch = im.shape
show('imLab', imLab)
# Load palette and convert to Luv
pal = cv.imread('palette.png')
pLab = cv.cvtColor(np.float32(pal/255.), cv.COLOR_BGR2Luv)
pEntries = 15
show('pLab', pLab)
def labnearest(i0,i1,i2):
dNearest = 1_000_000
for pEntry in range(pEntries):
p0, p1, p2 = pLab[0,pEntry,0], pLab[0,pEntry,1], pLab[0,pEntry,2]
# Work out distance between this pixel and this palette entry
d = (p0 - i0)*(p0 - i0) + (p1 - i1)*(p1 -i1) + (p2 - i2)*(p2 - i2)
if d < dNearest:
dNearest = d
result = (p0, p1, p2)
return result
for x in range(rows):
for y in range(cols):
px = imLab[x,y]
imLab[x,y] = labnearest(px[0], px[1], px[2])
show('imLab', imLab)
res = (cv.cvtColor(imLab, cv.COLOR_Luv2BGR)*255).astype(np.uint8)
show('res', res)
cv.imwrite('result.png', res)