2010-09-15 106 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') 
相關問題