imshow 中出现一个错误?

发布于 2024-11-09 09:36:06 字数 1913 浏览 0 评论 0原文

我正在绘制 PGM 图像: 在此处输入图像描述 这是我正在使用的数据

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

  • 图像顶部附近的三个灰色框的值为 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 的行)是否有更好的方法来执行此操作?

这是更新的图像: 在此处输入图像描述

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟花肆意 2024-11-16 09:36:06

解决方案是设置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

输出:

在此处输入图像描述

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

北城挽邺 2024-11-16 09:36:06

没有差异,您只是手动将刻度设置为用并非实际值的值进行标记。

请注意,您的 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]之类的东西?

编辑:抱歉,这听起来比我想要的更尖酸刻薄……我不是故意的! :)

编辑2
为了回答更新的问题,是的,imshow 根据输入的最小值和最大值自动确定范围。 (我很困惑......它还能做什么?)

如果您想要无需插值的直接颜色映射,请使用离散颜色图之一,而不是 LinearSegmentedColormap。然而,最简单的方法是手动设置 matplotlib 的 LinearSegmentedColormap 之一(这就是 matplotlib.cm.spectral )的限制。

如果您想手动设置所使用的颜色映射的范围,只需在 imshow 返回的 coloraxis 对象上调用 set_clim([0,12]) 即可。

例如

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

在此处输入图像描述

There's no discrepancy, you're just manually setting the ticks to be labeled with values that aren't what they actually are.

Notice that your LABELS is just range(13), while your actual tick locations (ticks) don't range from 0 to 12.

So, you're manually labeling the top tick, which has a position of 10.6, as 12!

Try taking out the line cb.ax.set_yticklabels(map(str, LABELS)), and you'll see what I mean (Also, matplotlib will automatically cast them to strings. There's no reason to call map(str, LABELS)).

Perhaps instead of using a static set of numbers as labels, you should just convert your actual tick locations to labels? Something like [round(tick) for tick in ticks]?

Edit: Sorry, that sounded snarkier than I intended it to... I didn't mean it to sound that way! :)

Edit2:
In response to the updated question, yes, imshow determines the range automatically from the min and max of the input. (I'm confused... What else would it do?)

If you want a direct color mapping with no interpolation, then use one of the discrete colormaps, not a LinearSegmentedColormap. However, it's easiest to just manually set the limits on a one of matplotlib's LinearSegmentedColormaps (which is what matplotlib.cm.spectral is).

If you want to manually set the range of the color mapping used, just call set_clim([0,12]) on the coloraxis object that imshow returns.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文