imshow 中出现一个错误?
我正在绘制 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:
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:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
解决方案是设置
im.set_clim(vmin, vmax)
。基本上,图像中的值被转换为覆盖整个颜色范围。例如,如果3
是数据中的最大值,则会为其分配最大颜色值。相反,您需要告诉它
max_nodes
是最高值(在您的情况下为 13),即使它没有出现在数据中,例如im.set_clim(0, 13)< /代码>。
我稍微更改了您的代码,以便与具有不同
num_modes
值的其他数据文件一起使用:一些更简单的测试数据来说明。请注意,
num_modes
值为 10,但没有数据点达到该级别。这显示了值如何以 1:1 的比例索引到颜色映射中:输出:
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 if3
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
: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:Output:
没有差异,您只是手动将刻度设置为用并非实际值的值进行标记。
请注意,您的
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])
即可。例如
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 justrange(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 callmap(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'sLinearSegmentedColormap
s (which is whatmatplotlib.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 thatimshow
returns.E.g.