Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Off by one error in imshow?

I'm plotting a PGM image: enter image description here Here's the data I'm using.

The problem is some of the shown pixels are wrong. For example:

  • the three grey boxes near the top of the image are of value 11 (so they should be red, not red)
  • the two yellow pixels in the top row -- they are of value 8, so they should be yellow-green, not yellow

Can anybody explain the discrepancies and how to fix them?

Here's my source:

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

EDIT

I see what's happening here now. Basically, imshow seems to be doing this:

  • determining the dynamic range (as [ min(image), max(image) ]
  • represent this using the number of colors specified in the color map (13 colors)

What I want it to do is:

  • use the dynamic range that I specified when creating the color map (13)
  • represent this using the 13 colors in the color map

I can verify this by forcing the dynamic range of the image to be 13 (see the line labelled HACK). Is there a better way to do this?

Here's an updated image: enter image description here

like image 700
mpenkov Avatar asked May 25 '11 15:05

mpenkov


1 Answers

The solution is to set im.set_clim(vmin, vmax). Basically the values in the image were being translated to cover the entire color range. For example if 3 was the largest value in your data, it would be assigned the maximum color value.

Instead you need to tell it that max_nodes is the highest value (13 in your case), even though it doesn't appear in the data, e.g. im.set_clim(0, 13).

I changed your code slightly to work with other data files with different values for 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')

Some simpler test data to illustrate. Notice that the num_modes value is 10, but no data point reaches that level. This shows how the values index into the colormap 1:1:

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

Output:

enter image description here

like image 58
samplebias Avatar answered Sep 23 '22 19:09

samplebias