2010-09-15 771 views
9

¿Cómo invierto una imagen mapeada en color?cómo invertir la imagen del mapa de color a valores escalares

Tengo una imagen en 2D que traza los datos en un mapa de colores. Me gustaría leer la imagen y "invertir" el mapa de color, es decir, buscar un valor RGB específico y convertirlo en un flotante.

Por ejemplo: utilizando esta imagen: http://matplotlib.sourceforge.net/_images/mri_demo.png

yo debería ser capaz de obtener una matriz de 440x360 flotadores, conociendo el mapa de colores se cm.jet

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

Respuesta

8

Puede haber mejores maneras de hacer esta; No estoy seguro. Si lee help(cm.jet), verá el algoritmo utilizado para mapear valores en el intervalo [0,1] a RGB 3-tuplas. Podrías, con un poco de papel y lápiz, elaborar fórmulas para invertir las funciones lineales por tramos que definen el mapeo.

Sin embargo, hay una serie de cuestiones que hacen que la solución de papel y lápiz algo poco atractivo:

  1. Es mucho más laboriosa del álgebra, y la solución es específico para cm.jet. Tendría que hacer todo este trabajo nuevamente si cambia el mapa de colores. Cómo automatizar la resolución de estas ecuaciones algebraicas es interesante, pero no es un problema que sepa cómo resolverlo.

  2. En general, el mapa de color puede no ser invertible (se puede asignar más de un valor al del mismo color). En el caso de cm.jet, los valores entre 0.11 y 0.125 se asignan todos al RGB 3-tuple (0,0,1), por ejemplo. Por lo tanto, si su imagen contiene un píleo azul puro , realmente no hay forma de que diga si proviene de un valor de 0.11 o un valor de, digamos, 0.125.

  3. La asignación de [0,1] a 3-tuplas es una curva en 3 espacios. Los colores en su imagen pueden no estar perfectamente en esta curva. Es posible que sea un error de redondeo, por ejemplo. Entonces, cualquier solución práctica tiene que poder interpolar o de alguna manera proyectar puntos en 3 espacios en la curva.

Debido a la falta de exclusividad y al problema de proyección/interpolación, puede haber muchas soluciones posibles al problema que plantea. A continuación hay solo una posibilidad.

Aquí es una forma de resolver los problemas de singularidad y proyección/interpolación:

Crear una gradient que actúa como un "libro de códigos". El gradient es una matriz de 4-tuplas RGBA en el mapa de colores cm.jet. Los colores de gradient corresponden a valores de 0 a 1. Utilice la función de cuantificación de vector de scipy scipy.cluster.vq.vq para asignar todos los colores en su imagen, mri_demo.png, al color más cercano en gradient. Dado que un mapa de color puede usar el mismo color para muchos valores, el degradado puede contener colores duplicados. Lo dejo en scipy.cluster.vq.vq para decidir qué (posiblemente) índice de libro de código no único para asociar con un color en particular.

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

La imagen que se ve debe estar cerca de reproducción mri_demo.png:

alt text

(El mri_demo.png original tenía un borde blanco Puesto que el blanco no es un color en cm.jet. , tenga en cuenta que scipy.cluster.vq.vq mapas en blanco a al punto más cercano en el libro gradient código, que pasa a ser de un color verde pálido.)

+0

Sí, esto es esencialmente lo que pensé que era posible. Su solución inicial incluía leer una línea de una imagen con el mismo mapa de color, lo que puede ser útil para las personas que dicen, escanean una figura y quieren hacer su propio análisis numérico. Me estaba quedando atrapado en la cuantificación del vector: inicialmente, parecía que la mejor opción era recorrer cada color posible en el lut y calcular una distancia en 3D del valor real del píxel, que no podía ver cómo hacerlo rápidamente sin bucle ¡Gracias! – user448764

0

Hy unutbu,

Gracias por su respuesta, entiendo el proceso que explica y lo reproduce. Funciona muy bien, lo uso para invertir tomas de cámara IR en cuadrículas de temperatura, ya que una imagen puede ser fácilmente retrabajada/remodelada para cumplir mi propósito usando GIMP.

Soy capaz de crear grillas de escalar desde tomas de cámara que son realmente útiles en mis tareas.

Utilizo un archivo de paleta que puedo crear usando GIMP + Sample a Gradient Along a Path. Escojo la barra de color de mi imagen original, la convierto en paleta y luego la exporto como secuencia de color hexadecimal. Leí este archivo de paleta para crear un mapa de color normalizado por una muestra de temperatura para usar como libro de códigos. Leo la imagen original y uso la cuantización vectorial para invertir el color en valores. I mejore ligeramente el estilo pythonic del código usando índices de libro de códigos como filtro de índice en la matriz de muestra de temperatura y aplique algunos filtros para suavizar mis resultados.

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

Los resultados son finalmente exportar en CSV

Lo más importante es crear un archivo de paletas representante para obtener una buena precisión. Comienzo a obtener una buena gradiente (libro de códigos) usando 12 colores y más. Este proceso es útil ya que a veces las tomas de cámara no se pueden traducir a escala de grises de forma fácil y lineal.

Gracias a todos los contribuyentes unutbu, Rob A, scipy comunidad;)

0

El LinearSegmentedColormap no me da la misma interpolación si no lo hago de forma manual durante mi prueba, por lo que prefieren utilizar mi propia:

Como una ventaja, matplotlib no es más necesario ya que integro mi código dentro de un software existente.

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') 
Cuestiones relacionadas