从多维numpy数组创建高质量灰度图像
我有一个数组,大小是50行乘33列。我想把它导入到Python的numpy数组中。这个数组里的值从0到4096,所以我把它缩放到0到255之间,然后试着用它创建一张灰度图像并保存。
import numpy
from PIL import Image
data = numpy.loadtxt('result.txt',delimiter='\t',dtype=None)
rescaled = (255.0 / 4096 * (data)).astype(numpy.uint8)
image = Image.fromarray(rescaled)
image.save('test_fullsize.jpeg', quality=95)
虽然没有报错,但生成的图像只有50x33像素。这意味着图片非常小(我能看出来这确实是我想要的那种图像,但它实在是太小了)。
我尝试在保存之前添加这一行代码来调整图像大小:
image = image.resize((480, 640), Image.ANTIALIAS)
但是这样输出的图像变得非常像素化(质量很差),变成了480x640的大小。我需要这张图像有一个合适的分辨率和大小,这样才能放在海报上。
谢谢。
4 个回答
你可以试着在你查看文件的工具上放大一下小版本的文件,我觉得这样会更好。不过,如果你真的需要更高的分辨率,可以试着把
image = image.resize((480, 640), Image.ANTIALIAS)
换成
image = image.resize((480, 640), Image.NEAREST)
或者直接用
image = image.resize((480, 640))
因为NEAREST
是这个方法的默认设置。
在调整大小的方法中,第二个参数是用来调整大小时的插值/过滤器。ANTIALIAS
是一个过滤器,可以在缩小图像时减少锯齿状的边缘。NEAREST
则是把所有像素设置成原始图像中最近的像素的颜色。这样处理后,图像会显得很“方块”或者“像素化”,我猜这正是你想要的效果。
作为@John1024建议的另一种方法,如果你的数据在行或列上有一些趋势,你可以把数据分散到480x640像素的区域,并对缺失的值进行插值处理。可以使用Pandas或scipy来帮助你完成这项工作。
如果你的数据没有任何趋势,你可以用一组像素来表示你的单个数据点,比如第一行和第一列的值可以覆盖480/33 x 640/50像素的区域。
你可以通过多种方式来实现后者:
逐个像素解析
magnifier = 10
im = Image.new('RGB',(rows*magnifier,cols*magnifier))
pix = im.load()
for row in range(im.size[0]):
for col in range(im.size[1]):
value = rescaled[row/rescaled.shape[0], col/rescaled.shape[1]],rescaled[row/rescaled.shape[0], col/rescaled.shape[1]],rescaled[row/rescaled.shape[0], col/rescaled.shape[1]]
pix[row,col] = value
im.save('test_fullsize.jpeg', quality=95)
这样做会得到非常清晰的边缘,没有任何模糊。
使用matplotlib和scipy
import matplotlib.pyplot as plt
from scipy.ndimage.interpolation import zoom
# With interpolation
rescaled2 = zoom(rescaled,zoom=10)
# plt.imshow(rescaled2, cmap=plt.cm.hot) # Color map
plt.imshow(rescaled2, cmap=plt.cm.gray)
# Just the data
plt.imshow(rescaled, cmap=plt.cm.gray)
结果还是有点模糊。如果插值处理可以接受,你可以使用zoom
来过采样图像,也就是说,把图像做得比实际需要的要大很多(比如最终大小的两到三倍,例如zoom=200
),然后用PIL缩小图像的大小。这样可以减少一些模糊。
另一种选择是使用不同的方式来表示数据,比如根据你想传达的信息,使用不同大小的圆圈来表示每个数据点。
简单来说,你有一张50x33的图片,想把它放大到一个“合适的分辨率/大小,以便能放在海报上”。
这种问题叫做超分辨率。这是一个正在积极研究的领域。有一些超分辨率的算法可以在opencv
这个库里找到。opencv
也有适用于Python的版本。你可以在这里了解可用的超分辨率算法。
根据你手上的图片类型,从50x33放大到看起来不错的640x480分辨率,可能对即使是最好的算法来说也是有点困难的。你可能需要调整一下你的目标。
根据我的理解,你想把矩阵中的数字显示成不同深浅的灰色,并且要做得足够大,以便可以放在海报上。所以,就像@pandita说的,我也会使用Matplotlib,因为这样做起来非常简单。
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
height, width = 50, 33 #in pixels
spines = 'left', 'right', 'top', 'bottom'
labels = ['label' + spine for spine in spines]
tick_params = {spine : False for spine in spines}
tick_params.update({label : False for label in labels})
img = np.random.randint(0, 4097, (height, width))
img *= 255. / 4096
desired_width = 8 #in inches
scale = desired_width / float(width)
fig, ax = plt.subplots(1, 1, figsize=(desired_width, height*scale))
img = ax.imshow(img, cmap=cm.Greys_r, interpolation='none')
#remove spines
for spine in spines:
ax.spines[spine].set_visible(False)
#hide ticks and labels
ax.tick_params(**tick_params)
#preview
plt.show()
#save
fig.savefig('test.png', dpi=300)
这样会生成下面的图片...
