数字化一副色彩地图

5
考虑以下图像: Source: https://fr.mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/34863/versions/15/screenshot.jpg 我想将其打印为灰度图像。我可以使用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)

我收到了如下图像:

enter image description here

但显然这不是我想要的。

我的问题是:有没有一种方法,可以使用scikit-image或原始的numpy和/或mathplotlib来数字化图像,以便我得到一个三维数组(第一维:X索引,第二维:Y索引,第三维:根据颜色映射的值)。然后我就可以轻松地将颜色映射更改为在灰度打印时效果更好的内容了?


1
最简单的方法是在生成图像时使用更好的颜色映射表。第二种最简单的方法是告诉制作图像的人使用更好的颜色映射表。最后,如果你真的卡在这张图片上,并且你确切地知道使用了哪个颜色映射表(看起来像jet),那么可能可以创建一个从RGB到cmap值的反向查找表。 - MB-F
@kazemakase 你说得对,但是我卡在了图像上。有没有简单的方法来创建反向查找表? - cjorssen
是的,有一个相对简单的方法...但它是有限制的。 - MB-F
2个回答

5
下面的示例演示了一种简单的方法来撤消色图的“值 -> 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) 最后返回通过色彩映射大小归一化的索引,使结果在0-1范围内。

enter image description here

这种方法有一些注意事项:

  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()

你能解释一下你的unmap_nearest函数中关于di的技巧吗? - cjorssen
在我的使用情境中,我认为我需要摆脱世界地图。 - cjorssen
@cjorssen 我编辑了问题,逐步解释了函数。如果还不清楚,请告诉我。 - MB-F
如果快速而粗略的方法不足以满足需求,那么绝对值得深入研究。 - MB-F
1
这种暴力方法对我来说非常有效。有关Jupyter笔记本实现,请查看https://github.com/svaberg/imageslurper - Svaberg
显示剩余3条评论

1

在 @kazemakmakase 的回答基础上,如果你正在数字化一张图片,那么你可能处理的是原始图片的副本,这些副本可能已经被转换过,甚至在某些时候可能已经被打印和扫描过了。这些操作都可能会使得颜色偏离最初使用的“真实”色图。

你可以通过使用图形色条中的一个切片作为要匹配的“模式”(RGB)来解决这个问题。具体地说,将图形裁剪到仅包含彩色渐变(在此示例中为横向),然后用 @kazemakmakase 的示例中的 rgb 变量替换:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接