数字化颜色图

Digitize a colormap

考虑下图:

我想将其打印为灰度图像。我可以用 scikit-image:

进行转换
from skimage.io import imread
from matplotlib import pyplot as plt
from skimage.color import rgb2gray


img = imread('image.jpg')

plt.grid(which = 'both')
plt.imshow(rgb2gray(img), cmap=plt.cm.gray)

我得到:

obviously not what I want.

我的问题是: 有没有办法用 scikit-image 或原始 numpy and/or mathplotlib 来数字化图像,以便我得到一个 3D 数组(第一维:X 索引,第二维:Y 索引,第三维:根据颜色图的值)。然后我可以很容易地将颜色图更改为在灰度打印时结果更好的颜色图?

下面的示例演示了撤消颜色图 value -> RGB 映射的简单方法。

def unmap_nearest(img, rgb):
    """ img is an image of shape [n, m, 3], and rgb is a colormap of shape [k, 3]. """
    d = np.sum(np.abs(img[np.newaxis, ...] - rgb[:, np.newaxis, np.newaxis, :]), axis=-1)    
    i = np.argmin(d, axis=0)
    return i / (rgb.shape[0] - 1)

此函数的工作原理是获取每个像素的 RGB 值并在颜色图中查找最匹配颜色的索引。索引和广播的一些技巧可以实现高效的矢量化(以临时数组上花费的内存为代价):

  • img[np.newaxis, ...] 将图像从形状 [n, m, 3] 转换为 [1, n, m, 3]

  • rgb[:, np.newaxis, np.newaxis, :] 将颜色图从形状 [k, 3] 转换为 [k, 1, 1, 3]。

  • 减去结果数组会得到一个形状为 [k, n, m, 3] 的数组,其中包含每个颜色图索引 k 和每个颜色分量的像素 n, m 之间的差异。
  • sum(abs(..), axis=-1) 对所有颜色分量(最后一个维度)取差的绝对值并求和,得到所有像素和颜色图条目(形状数组 [k, n, m ]).
  • i = np.argmin(d, axis=0) 沿第一个维度查找最小元素的索引。结果是每个像素[n,m]的最佳匹配色图条目的索引。
  • return i / (rgb.shape[0] - 1) 最后 returns 索引按颜色图大小归一化,结果在 0-1 范围内。

这种方法有一个 faw 警告:

  1. 无法重建原始取值范围
  2. 它将所有像素视为彩色地图的一部分(即大陆等值线也将被映射)。
  3. 如果你使用了错误的颜色贴图,它会很滑稽地失败。

.

import numpy as np
import matplotlib.pyplot as plt
from skimage.color import rgb2gray


def unmap_nearest(img, rgb):
    """ img is an image of shape [n, m, 3], and rgb is a colormap of shape [k, 3]. """
    d = np.sum(np.abs(img[np.newaxis, ...] - rgb[:, np.newaxis, np.newaxis, :]), axis=-1)    
    i = np.argmin(d, axis=0)
    return i / (rgb.shape[0] - 1)


cmap = plt.cm.jet
rgb = cmap(np.linspace(0, 1, cmap.N))[:, :3]


original = (np.arange(10)[:, None] + np.arange(10)[None, :])

plt.subplot(2, 2, 1)
plt.imshow(original, cmap='gray')
plt.colorbar()
plt.title('original')


plt.subplot(2, 2, 2)
rgb_img = cmap(original / 18)[..., :-1]
plt.imshow(rgb_img)
plt.title('color-mapped')

plt.subplot(2, 2, 3)
wrong = rgb2gray(rgb_img)
plt.imshow(wrong, cmap='gray')
plt.title('rgb2gray')

plt.subplot(2, 2, 4)
reconstructed = unmap_nearest(rgb_img, rgb)
plt.imshow(reconstructed, cmap='gray')
plt.colorbar()
plt.title('reconstructed')

plt.show()

基于@kazemakmakase 的回答,如果您正在对图形进行数字化,您可能正在处理已转换的原件副本,或者甚至可能在某个时候打印和扫描。这些东西会扭曲最初使用的 "true" 颜色图中的颜色。

您可以通过使用图形颜色条的切片作为 'pattern' (rgb) 进行匹配来处理此问题。具体来说,将图形缩小到仅颜色渐变(在此示例中为横向),然后将@kazemakmakase 示例中的 rgb 变量替换为:

cmapimg = plt.imread('cropped_colorbar.png')
rgb = cmapimg[cmapimg.shape[0]/2,:,:3]