2010-09-15 110 views
9

如何反转彩色映射图像?如何将彩色地图图像反转为标量值

我有一个二维图像绘制在色彩地图上的数据。我想要读取图像并“反转”颜色映射,即查找特定的RGB值并将其变为浮点。

例如: 使用该图片:http://matplotlib.sourceforge.net/_images/mri_demo.png

我应该能够得到一个440x360矩阵花车,知道颜色表被cm.jet

from pylab import imread 
import matplotlib.cm as cm 
a=imread('mri_demo.png') 
b=colormap2float(a,cm.jet) #<-tricky part 

回答

8

可能有更好的方法来做到这个;我不确定。 如果您阅读help(cm.jet),您将看到用于将区间[0,1]中的值映射到RGB 3元组的算法。你可以用一些纸和铅笔制定公式来反演定义映射的分段线性函数。

不过,也有一些这使纸张和铅笔的解决方案有些不吸引人的问题:

  1. 这是一个很大费力的代数和 该解决方案是具体的cm.jet。 如果更改颜色映射表,则必须再次执行所有这些工作 。如何自动化解这些代数方程是有趣的,但不是我知道如何解决的问题。

  2. 一般而言,颜色映射可能不是 可逆(可能有多于一个值可能会映射到相同颜色的 )。例如,在cm.jet的 的情况下,0.11 和0.125之间的值全都被映射到三元组(0,0,1)的RGB 。因此,如果您的图片包含纯蓝色 像素,则确实无法通过 判断它是来自0.11 的值还是0.125的值。

  3. 从[0,1]到 三元组的映射是三维空间中的一条曲线。在这条曲线上,图像中的 颜色可能不会完美地包含 。例如,可能有 是舍入错误。所以任何实际的解决方案都必须能够插入或以某种方式将三维空间中的点投影到曲线上。

由于非唯一性问题以及投影/插值问题,可能会有很多可能的解决方案来解决您提出的问题。以下只是一种可能性。

这是解决的独特性和投影/插值问题的一种方法:

创建gradient充当“码书”。 gradient是cm.jet彩色贴图中的一组RGBA四元组。 gradient的颜色对应于从0到1的值。使用scipy的矢量量化函数scipy.cluster.vq.vq将图像中的所有颜色mri_demo.png映射到gradient的最近颜色。 由于彩色贴图可能对许多值使用相同的颜色,因此渐变可能包含重复的颜色。我把它留到scipy.cluster.vq.vq来决定哪个(可能)非唯一的代码簿索引与特定的颜色关联。

import matplotlib.pyplot as plt 
import matplotlib.cm as cm 
import numpy as np 
import scipy.cluster.vq as scv 

def colormap2arr(arr,cmap):  
    # http://stackoverflow.com/questions/3720840/how-to-reverse-color-map-image-to-scalar-values/3722674#3722674 
    gradient=cmap(np.linspace(0.0,1.0,100)) 

    # Reshape arr to something like (240*240, 4), all the 4-tuples in a long list... 
    arr2=arr.reshape((arr.shape[0]*arr.shape[1],arr.shape[2])) 

    # Use vector quantization to shift the values in arr2 to the nearest point in 
    # the code book (gradient). 
    code,dist=scv.vq(arr2,gradient) 

    # code is an array of length arr2 (240*240), holding the code book index for 
    # each observation. (arr2 are the "observations".) 
    # Scale the values so they are from 0 to 1. 
    values=code.astype('float')/gradient.shape[0] 

    # Reshape values back to (240,240) 
    values=values.reshape(arr.shape[0],arr.shape[1]) 
    values=values[::-1] 
    return values 

arr=plt.imread('mri_demo.png') 
values=colormap2arr(arr,cm.jet)  
# Proof that it works: 
plt.imshow(values,interpolation='bilinear', cmap=cm.jet, 
      origin='lower', extent=[-3,3,-3,3]) 
plt.show() 

你看到的应该是接近再生mri_demo.png图像:

alt text

(原mri_demo.png有白色边框由于白色是不是在cm.jet的颜色。请注意,以scipy.cluster.vq.vq白色映射到最接近的点在gradient码书,这恰好是淡绿色。)

+0

是的,这基本上是我认为是可能的。您的初始解决方案包括从具有相同颜色图的图像中读取一条线,这可能对那些说,扫描一个数字并希望进行自己的数字分析的人有所帮助。我陷入了矢量量化的问题 - 最初,似乎最好的选择是循环遍历每个可能的颜色,并计算出距离实际像素值的3d距离 - 我无法看到如何快速执行循环。谢谢! – user448764 2010-09-16 20:33:19

0

海兰unutbu,

感谢您的回复,我了解您解释的过程并将其复制。它工作得很好,我用它来反转温度网格中的红外相机镜头,因为使用GIMP可以很容易地修改/重塑图片以实现我的目的。

我能够从相机镜头创建标量网格,这对我的任务来说非常有用。

我使用一个调色板文件,我可以使用GIMP + Sample a Gradient Along a Path创建。 我选择我的原始图片的颜色条,将其转换为调色板,然后以十六进制颜色序列输出。 我读了这个调色板文件,以创建一个由温度采样标准化的颜色图,用作代码簿。 我读了原始图像并使用矢量量化将颜色转换为值。 I 通过使用代码簿索引作为温度样本数组中的索引过滤器,稍微改进代码的pythonic风格,并应用一些过滤器通过来平滑我的结果。

from numpy import linspace, savetxt 
from matplotlib.colors import Normalize, LinearSegmentedColormap 
from scipy.cluster.vq import vq 

# sample the values to find from colorbar extremums 
vmin = -20. 
vmax = 120. 
precision = 1. 

resolution = 1 + vmax-vmin/precision 
sample = linspace(vmin,vmax,resolution) 

# create code_book from sample 
cmap = LinearSegmentedColormap.from_list('Custom', hex_color_list) 
norm = Normalize() 
code_book = cmap(norm(sample)) 

# quantize colors 
indices = vq(flat_image,code_book)[0] 
# filter sample from quantization results **(improved)** 
values = sample[indices] 

savetxt(image_file_name[:-3]+'.csv',values ,delimiter=',',fmt='%-8.1f') 

结果最后导出为.csv

最重要的是要建立一个很好的代表调色板文件,以获得良好的精度。我开始使用12种颜色和更多的颜色获得良好的渐变(代码簿)。 这个过程很有用,因为有时相机镜头不能轻松地和线性地转换为灰度。

感谢所有贡献者unutbu,抢劫,SciPy的社区;)

0

的LinearSegmentedColormap不给我相同的插值,如果我在我的测试过程中手动是不是,所以我宁愿用我自己的:

作为一个优势,matplotlib不再是必需的,因为我将我的代码集成到现有软件中。

def codeBook(color_list, N=256): 
    """ 
    return N colors interpolated from rgb color list 
    !!! workaround to matplotlib colormap to avoid dependency !!! 
    """ 
    # seperate r g b channel 
    rgb = np.array(color_list).T 
    # normalize data points sets 
    new_x = np.linspace(0., 1., N) 
    x = np.linspace(0., 1., len(color_list)) 
    # interpolate each color channel 
    rgb = [np.interp(new_x, x, channel) for channel in rgb] 
    # round elements of the array to the nearest integer. 
    return np.rint(np.column_stack(rgb)).astype('int') 
相关问题