在imshow中出现一个错误?

时间:2011-05-25 15:51:36

标签: python matplotlib

我正在绘制PGM图像: enter image description here 这是我正在使用的data

问题是某些显示的像素是错误的。例如:

  • 图像顶部附近的三个灰色框的值为11(所以它们应该是红色,而不是红色)
  • 顶行的两个黄色像素 - 它们的值为8,所以它们应该是黄绿色,而不是黄色

有人可以解释这些差异以及如何解决这些差异吗?

这是我的来源:

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

修改

我现在看到这里发生了什么。基本上,imshow似乎是这样做的:

  • 确定动态范围([ min(image), max(image) ]
  • 使用颜色图中指定的颜色数(13种颜色)
  • 表示

我想要它做的是:

  • 使用我在创建颜色贴图时指定的动态范围(13)
  • 使用颜色映射中的13种颜色表示这一点

我可以通过强制图像的动态范围为13来验证这一点(请参阅标记为HACK的行)。有更好的方法吗?

这是一张更新的图片: enter image description here

2 个答案:

答案 0 :(得分:5)

解决方案是设置im.set_clim(vmin, vmax)。基本上,图像中的值被翻译为覆盖整个颜色范围。例如,如果3是数据中的最大值,则会为其分配最大颜色值。

相反,您需要告诉它max_nodes是最高值(在您的情况下为13),即使它没有出现在数据中,例如im.set_clim(0, 13)

我稍微更改了您的代码以使用其他具有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')

一些更简单的测试数据来说明。请注意,num_modes值为10,但没有数据点达到该级别。这显示了值如何索引到色彩映射1:1:

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

输出:

enter image description here

答案 1 :(得分:3)

没有差异,您只需手动设置刻度线,使其标注的值不是实际值。

请注意,您的LABELS仅为range(13),而实际的定位位置(ticks)不在0到12之间。

所以,你手动标记顶部刻度线,其位置为10.6,为12!

尝试取出cb.ax.set_yticklabels(map(str, LABELS))行,你会看到我的意思(另外,matplotlib会自动将它们转换为字符串。没有理由调用map(str, LABELS))。

也许您不应该使用静态数字集作为标签,而应将实际的刻度位置转换为标签?像[round(tick) for tick in ticks]

这样的东西

编辑:对不起,这听起来比我想要的还要糟糕......我不是故意这样说的! :)

<强> EDIT2 : 响应更新的问题,是的,imshow自动从输入的最小值和最大值确定范围。 (我很困惑......它还能做什么?)

如果您想要一个没有插值的直接颜色映射,请使用其中一个离散色图,而不是LinearSegmentedColormap。但是,最简单的方法就是在matplotlib的LinearSegmentedColormap之一(matplotlib.cm.spectral)上手动设置限制。

如果要手动设置所用颜色映射的范围,只需在set_clim([0,12])返回的coloraxis对象上调用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