2011-05-25 11 views
5

Estoy trazando una imagen PGM: enter image description here Aquí está el data que estoy usando.Apagado por un error en imshow?

El problema es que algunos de los píxeles que se muestran son incorrectos. Por ejemplo:

  • las tres cajas grises cerca de la cima de la imagen tienen un valor de 11 (por lo que deben ser de color rojo, no rojo)
  • los dos píxeles de color amarillo en la fila superior - que son de valor 8, entonces deberían ser de color amarillo-verde, no amarillo

¿Alguien puede explicar las discrepancias y cómo solucionarlas?

Aquí es mi fuente:

from pylab import * 
import numpy  
LABELS = range(13) 
NUM_MODES = len(LABELS) 
def read_ascii_pgm(fname): 
    """ 
    Very fragile PGM reader. It's OK since this is only for reading files 
    output by my own app. 
    """ 
    lines = open(fname).read().strip().split('\n') 
    assert lines[0] == 'P2' 
    width, height = map(int, lines[1].split(' ')) 
    assert lines[2] == '13' 
    pgm = numpy.zeros((height, width), dtype=numpy.uint8) 
    for i in range(height): 
     cols = lines[3+i].split(' ') 
     for j in range(width): 
      pgm[i,j] = int(cols[j]) 
    return pgm 
def main(): 
    import sys 
    assert len(sys.argv) > 1 
    fname = sys.argv[1] 
    pgm = read_ascii_pgm(fname) 
    # EDIT: HACK! 
    pgm[0,0] = 12 
    cmap = cm.get_cmap('spectral', NUM_MODES) 
    imshow(pgm, cmap=cmap, interpolation='nearest') 
    edit = True 
    if edit: 
     cb = colorbar() 
    else: 
     ticks = [ (i*11./NUM_MODES + 6./NUM_MODES) for i in range(NUM_MODES) ] 
     cb = colorbar(ticks=ticks) 
     cb.ax.set_yticklabels(map(str, LABELS)) 
    savefig('imshow.png') 
if __name__ == '__main__': 
    main() 

EDITAR

veo lo que está pasando aquí ahora. Básicamente, imshow parece estar haciendo esto:

  • determinar el rango dinámico (como [ min(image), max(image) ]
  • representan esta usando el número de colores que se especifican en el mapa de color (13 colores)

lo que quiero que hay que hacer es:

  • uso del rango dinámico que he especificado al crear el mapa de colores (13)
  • representan esta usando los 13 colores en el mapa de color

puedo verificar esto forzando el rango dinámico de la imagen a ser 13 (véase la línea marcada HACK). ¿Hay una mejor manera de hacer esto?

Aquí es una imagen actualizada: enter image description here

+1

+1 - esto parece ser un problema que mucha gente podría llegar a, conseguir "casi corregir "resultados". Odiaría atrapar este _después_ de la publicación. – samplebias

Respuesta

5

la solución es establecer im.set_clim(vmin, vmax). Básicamente, los valores en la imagen se traducían para cubrir todo el rango de color. Por ejemplo, si 3 fuera el valor más grande en sus datos, se le asignaría el valor de color máximo.

En su lugar, debe decir que max_nodes es el valor más alto (13 en su caso), aunque no aparezca en los datos, p. im.set_clim(0, 13).

que cambió su código un poco para trabajar con otros archivos de datos con diferentes valores para num_modes:

import numpy 
from pylab import * 

def read_ascii_pgm(fname): 
    lines = open(fname).read().strip().split('\n') 
    assert lines[0] == 'P2' 
    width, height = map(int, lines[1].split(' ')) 
    num_modes = int(lines[2]) 
    pgm = numpy.zeros((height, width), dtype=numpy.uint8) 
    for i in range(height): 
     cols = lines[3+i].split(' ') 
     for j in range(width): 
      pgm[i,j] = int(cols[j]) 
    return pgm, num_modes + 1 

if __name__ == '__main__': 
    import sys 
    assert len(sys.argv) > 1 
    fname = sys.argv[1] 
    pgm, num_modes = read_ascii_pgm(fname) 
    labels = range(num_modes) 
    cmap = cm.get_cmap('spectral', num_modes) 
    im = imshow(pgm, cmap=cmap, interpolation='nearest') 
    im.set_clim(0, num_modes) 
    ticks = [(i + 0.5) for i in range(num_modes)] 
    cb = colorbar(ticks=ticks) 
    cb.ax.set_yticklabels(map(str, labels)) 
    savefig('imshow_new.png') 

Algunos datos de las pruebas más simples para ilustrar. Observe que el valor num_modes es 10, pero ningún punto de datos alcanza ese nivel. Esto muestra cómo el índice de valores en el mapa de colores 1: 1:

P2 
5 3 
10 
0 1 0 2 0 
3 0 2 0 1 
0 1 0 2 0 

Salida:

enter image description here

+0

Sí, entendí esto bien antes de volver aquí para ver su respuesta. En realidad estoy pasando el mínimo y el máximo como argumentos para 'imshow' pero es esencialmente lo mismo.¡Gracias por la redacción fácil de entender! – misha

3

No hay discrepancia, sólo está configurando manualmente las garrapatas a ser etiquetados con valores que no son lo que realmente son.

cuenta de que su LABELS es sólo range(13), mientras que las ubicaciones reales de garrapatas (ticks) no van de 0 a 12.

es así, estás etiquetando manualmente la garrapata superior, que tiene una posición de 10.6 , como 12!

Trate de sacar la línea cb.ax.set_yticklabels(map(str, LABELS)), y verá lo que quiero decir (Además, matplotlib automáticamente los convertirá en cadenas. No hay ninguna razón para llamar al map(str, LABELS)).

¿Quizás en lugar de utilizar un conjunto estático de números como etiquetas, simplemente convierta las ubicaciones reales de las marcas en etiquetas? Algo así como [round(tick) for tick in ticks]?

Editar: Lo siento, sonó más malvado de lo que pretendía ... ¡No quise que sonara de esa manera!:)

Edit2: En respuesta a la pregunta actualizado, sí, imshow determina el rango automáticamente desde el min y max de la entrada. (Estoy confundido ... ¿Qué más haría?)

Si desea una asignación de color directa sin interpolación, utilice uno de los mapas de colores discretos, no un LinearSegmentedColormap. Sin embargo, es más fácil establecer los límites manualmente en uno de los LinearSegmentedColormap s de matplotlib (que es lo que matplotlib.cm.spectral es).

Si desea establecer manualmente el rango de la asignación de color utilizada, simplemente llame al set_clim([0,12]) en el objeto de coloraxis que devuelve imshow.

E.g.

import matplotlib.pyplot as plt 
import matplotlib as mpl 
import numpy as np 

with open('temp.pgm') as infile: 
    header, nrows, ncols = [infile.readline().strip() for _ in range(3)] 
    data = np.loadtxt(infile).astype(np.uint8) 

cmap = mpl.cm.get_cmap('spectral', 13) 
cax = plt.imshow(data, cmap, interpolation='nearest') 
cax.set_clim([0,13]) 
cbar = plt.colorbar(cax, ticks=np.arange(0.5, 13, 1.0)) 
cbar.ax.set_yticklabels(range(13)) 
plt.show() 

enter image description here

+0

Gracias por su respuesta. Antes que nada, no estoy de acuerdo contigo. Creo que ** hay ** una discrepancia. La barra de colores tiene 13 colores, de los cuales el gris es el color de mayor valor, el rojo oscuro el segundo más alto, el rojo el tercero, etc. La imagen tiene un máximo de 13 intensidades (como lo especifiqué al crear el mapa de colores), 12 es el valor más alto. ¿Me equivoco al suponer que 12 debería representarse como gris y 11 como rojo oscuro? – misha

+0

No se preocupe por sonar sarcástico. Me acabas de dar una idea de cuál es el problema en realidad. Por favor mira mi pregunta actualizada ¡Y gracias! – misha

+1

Woops, no notaron la respuesta de @ samplebias, que dice exactamente lo mismo que la mía. ¡Me tomó demasiado tiempo editar! :) Sin embargo, dejaré la respuesta como está. –

Cuestiones relacionadas