Python Numpy兼容的图像绘图库

Python Numpy兼容的图像绘图库,python,image,numpy,drawing,Python,Image,Numpy,Drawing,我需要在python中的图像上过度绘制一般坐标网格。我可以计算网格线的像素坐标,所以我只需要一个模块,能够将它们绘制为图像顶部的虚线。图像以numpy数组的形式出现,因此我需要能够在这些和绘图库使用的图像格式之间进行转换(一个方向就足够了-我可以绘制网格并将其导出到numpy,或者导入numpy数组并在其上绘制)。它还需要相当快的速度 以下是我尝试过的: 魔杖 最近获得了绘制虚线的支持,并且与numpy兼容: with Drawing() as draw: draw.stroke_ant

我需要在python中的图像上过度绘制一般坐标网格。我可以计算网格线的像素坐标,所以我只需要一个模块,能够将它们绘制为图像顶部的虚线。图像以
numpy
数组的形式出现,因此我需要能够在这些和绘图库使用的图像格式之间进行转换(一个方向就足够了-我可以绘制网格并将其导出到
numpy
,或者导入
numpy
数组并在其上绘制)。它还需要相当快的速度

以下是我尝试过的:

魔杖 最近获得了绘制虚线的支持,并且与numpy兼容:

with Drawing() as draw:
    draw.stroke_antialias = False
    draw.stroke_dash_array = [1,3]
    draw.stroke_color = Color("gray")
    draw.fill_opacity = 0
    points = calc_grid_points()
    draw.polyline(points)
    with Image(width=width, height=height) as img:
        draw(img)
        return np.fromstring(img.make_blob("RGBA"),np.uint8).reshape(img.height, img.width, 4)
但是,使用此库在2000x1000图像上绘制几百条虚线需要30秒!几乎所有的时间都花在了
draw(img)
上。所以,除非我在这里做错了什么,否则旺德就是太慢了

皮尔 Python图像库一般工作正常,但它似乎不支持虚线。我没见过有人直接说出来,但谷歌搜索只会有2-3个人问这个问题,却没有得到任何答案。非虚线坐标网格看起来不太好看,并且覆盖了大部分正在绘制的图像。PIL比魔杖快得多。此无破折号但与上述魔杖版本等效的版本仅需0.06 让我们来画画。比魔杖快450倍

    img  = PIL.Image.new("RGBA", (width, height))
    draw = PIL.ImageDraw.Draw(img)
    segs = calc_grid_points()
    for seg in segs:
        draw.line([tuple(i) for i in seg], fill=(0,0,0,32))
    return np.array(img)
gd
支持虚线,但我没有找到一种有效的方法将其图像与
numpy
数组进行转换

Matplotlib Matplotlib对绘制坐标网格和轴具有适当的支持,但遗憾的是,似乎无法避免重新混合。它坚持构建新的像素集,这些像素与原始像素之间永远不会有1对1的映射。这对于平滑图像很好,但对于像素之间变化较大的图像则不行

以下是matplotlib重新混合的示例:

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
n = 1001
data = np.arange(n*n).reshape(n,n)%2
plt.imshow(data)
plt.savefig("test.png")
此图像包含一个棋盘模式,每个像素在0和1之间切换。但据我所知,无法确保一个像素的数据将对应于输出中的一个像素。如果没有,您将得到moiree模式,如此代码生成的:

手动调整dpi设置可以减少此问题,但不能消除它。正确的输出将使绘图的数据部分恰好占据1001×1001像素(因此总图像将大于该像素),并在每个方向上显示500次重复的图案

编辑:添加
interpolation='none'
没有帮助。这只会导致matplotlib使用最近邻。它仍然没有显示所有的数据。下面是输出的结果:

下面是正确输出的样子(我将其裁剪为500x500,完整版本为1001x1001):

但是matplotlib并不是我真正的问题——它只是用python在图像上画虚线。Matplotlib只是一种可能的方法

选择?
所以我想知道,有没有其他的图像库可以实现这一点?还是我忽略了上面那些使它们可用的功能?

使用matplotlib,在删除帧和记号后,使用dpi似乎可以获得不错的结果

然而,有一点是边界框计算会出现舍入错误,因此我们需要手动修复bbox大小:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

# Some fake data
n = 1001
data = np.arange(n*n).reshape(n,n)%2
data = data[:n//2,:]

def get_bitmap_frame(data, dpi=None):
    """
    Construct a figure for overlaying elements on top of bitmap data.

    Returns
    -------
    fig, ax
        Matplotlib figure and axis objects
    """
    if dpi is None:
        dpi = matplotlib.rcParams['savefig.dpi']
    dpi = float(dpi)

    fig = plt.figure(figsize=(data.shape[1]/dpi, data.shape[0]/dpi), dpi=dpi)
    ax = fig.add_axes([0, 0, 1, 1], frame_on=False)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    ax.imshow(data, interpolation='none')

    # Note: it can happen that floating point rounding error in
    # bbox calculation results to the bbox size being off by one
    # pixel. Because of this, we set it manually
    fig.bbox = matplotlib.transforms.Bbox.from_bounds(0, 0, data.shape[1], data.shape[0])
    return fig, ax

fig, ax = get_bitmap_frame(data)

# Annotate
ax.text(100, 100, 'Hello!', color='w')

fig.savefig("test.png")

您能否添加一个最小的代码示例,以便我们了解数据的格式?例如。我认为matplotlib可以做到这一点,因此如果您可以添加您尝试过的代码,以便我们可以查看(并希望修复)您描述的“1对1映射”问题,那将是一件非常棒的事情。您可以添加一个示例图像:from
from skimage.data import coffee
image=coffee()
。我想你的问题应该更多地集中在你的具体问题上。一个可复制的例子和一幅预期输出应该是什么样子的图像就好了。现在,这个问题可以解释为一个查找库的请求,这与主题无关。我现在添加了一个matplotlib重新混合的简短示例。@cel:这是一个查找库的请求。这里的大部分问题都是通过找到合适的库来解决的。如果不是的话,这个问题的主题会在哪里?@E先生:谢谢你调查这个问题。如果matplotlib能够正常工作,那将是非常棒的,并且将为我节省大量时间。我上传了一个示例,展示了正确的输出。你认为在matplotlib中实现这一点有可能吗?这是非常有希望的!这将是我所需要的,如果有可能做到这一点,同时仍然保持帧和网格。不过,我想这会使计算正确的边界变得困难。您可以删除
xaxis.set_visible(False)
行,然后添加
ax.grid(True)
来完成此操作,也可以手动绘制它们。它们绘制在图像的顶部,不影响缩放。我删除它们只是因为上面的“正确结果”图像中没有它们。好的,你确实可以用matplotlib绘制网格,而不会弄乱像素,所以这回答了我的问题。谢谢但是这个解决方案似乎不太容易扩展到沿轴添加带编号的记号、颜色条等,对吗?您可能只需要确保轴的大小,如fig.add_轴所示,与像素大小相对应。上图中,坐标轴填满了整个图形。是否可以以一个像素的舍入结束这一问题——不知道,但在浮点精度耗尽之前,可能需要巨大的数字。