像素坐标与绘图坐标

时间:2018-04-08 17:23:11

标签: python numpy python-imaging-library pillow

在下面的代码片段中,传递x和y值会将点放在(y,x)坐标中,而绘图在(x,y)中完成。设置绘图缓冲区的正确方法是什么,以便将像素和绘图放在同一个坐标系中?

from PIL import Image, ImageDraw

def visual_test(x, y):
    grid = np.zeros((100, 100, 3), dtype=np.uint8)
    grid[:] = [0, 0, 0]
    grid[x, y] = [255, 0, 0]
    img = Image.fromarray(grid, 'RGB')
    draw = ImageDraw.Draw(img)
    draw.line((x, y, x, y-5), fill=(255,255,255), width=1)
    img.show()

1 个答案:

答案 0 :(得分:1)

注意:使用"轴"我指的是图像坐标,而不是NumPy的数组维度。

问题在于对ndarray维度的解释(" N维数组")或坐标系的定义在那种情况下。

Pillow,很清楚:

  

Coordinate System

     

Python Imaging Library使用笛卡尔像素坐标系,   左上角有(0,0)。请注意,坐标参考   到隐含的像素角;像素的中心寻址为(0,   0)实际上位于(0.5,0.5)。

     

坐标通常以2元组(x,y)的形式传递给库。   矩形表示为4元组,左上角   首先给出。例如,一个覆盖所有800x600像素的矩形   图像写为(0,0,800,600)。

这看起来像这样(图像 - >公共领域):

Pillow's XY coordinate system

您的代码经过修改后可创建2x2像素图像:

import numpy as np
from PIL import Image # Pillow

w, h, d = 2,2,3
x,y = 0,1

grid = np.zeros((w, h, d), dtype=np.uint8) # NumPyarray for image data
#test = np.zeros(w*h*d, dtype=np.uint8).reshape(w, h, d)
#print(np.array_equal(grid,test)) # => True

# red pixel with NumPy
grid[x, y] = [255, 0, 0]

print(grid[::])

# green pixel with Pillow
img = Image.fromarray(grid, 'RGB')
pixels = img.load()
pixels[x,y] = (0, 255, 0)

# display temporary image file with default application
scale = 100
img.resize((w*scale,h*scale)).show()

显示问题(在(0,1)处绘制像素,绿色:枕头,红色:ndarray):

generated image

X和Y确实被交换了:

ndarrays YX axes

是因为NumPy还是Pillow?

ndarray打印为

[[[  0   0   0]
  [255   0   0]]

 [[  0   0   0]
  [  0   0   0]]]

可轻松重新格式化以在视觉上对应于图像像素

[
 [ [  0   0   0] [255   0   0] ]
 [ [  0   0   0] [  0   0   0] ]
]

表明Pillow正如人们所期望的那样解释数组。

但为什么NumPy ndarray似乎交换了轴?

让我们进一步分开

[ # grid
 [ # grid[0]
   [  0   0   0]  #grid[0,0]
                  [255   0   0] #grid[0,1]
 ]
 [ #grid[1]
   [  0   0   0]  #grid[1,0]
                  [  0   0   0] #grid[1,1]
 ]
]

让我们测试一下(-i一旦脚本完成,Python就会以交互模式运行):

>py -i t.py
[[[  0   0   0]
  [255   0   0]]

 [[  0   0   0]
  [  0   0   0]]]
>>> grid[0,1]
array([255,   0,   0], dtype=uint8)
>>> grid[0]
array([[  0,   0,   0],
       [255,   0,   0]], dtype=uint8)
>>> ^Z

确认了上面假定的指数。

ndarray的第一个维度如何对应于图像线或Y轴,第二个维度对应于图像列或X轴(第三个维度对应于RGB像素值)。

所以,要匹配"坐标系",要么......

  1. ...轴需要"交换"
  2. ......数据需要"交换"
  3. ...轴解释需要"交换"
  4. 让我们看看:

    <强> 1。在写入ndarray

    时,只需交换索引变量即可
    # red pixel with NumPy
    grid[y, x] = [255, 0, 0]
    

    预计会产生

    [[[  0   0   0]
      [  0   0   0]]
    
     [[255   0   0]
      [  0   0   0]]]
    

    enter image description here

    当然包装函数可以做到这一点。

    2。 Transposing数组suggested by zch无法在三维数组上轻松 ,因为此函数会影响默认情况下所有尺寸:

    grid = np.transpose(grid)
    print("transposed\n", grid)
    print("shape:", grid.shape)
    

    结果

    [[[  0   0]
      [255   0]]
    
     [[  0   0]
      [  0   0]]
    
     [[  0   0]
      [  0   0]]]
    shape: (3, 2, 2)
    

    并且由于指定了Pillow RGB图像模式,因此抛出了异常:

    ValueError: not enough image data
    

    np.transposeaxes还有一个额外的论点:

      

    ...根据给定的值对轴进行置换。

    我们只想交换01,而不是2,所以:

    grid = np.transpose(grid, (1,0,2))
    

    还有其他类似的功能,例如

    grid = np.swapaxes(grid,0,1)
    

    第3。改变解释?

    Pillow&#39; PIL.Image.fromarray可以用交换轴解释ndarray吗?除了mode之外,它没有任何其他参数用于颜色(实际上,请参阅source code)。

      

    使用缓冲区协议从导出阵列接口的对象创建映像内存。   如果obj不连续,则调用tobytes方法并使用frombuffer()。

    该功能指出如何拨打PIL.Image.frombuffer()source),其中包含更多选项,用于&#34;解码器&#34;。

    Array interfaceBuffer protocol?这对于现在来说都太低了......

    <强> TL; DR
    只需交换索引变量(或者)!

    <小时/> 进一步阅读: - https://docs.scipy.org/doc/numpy-dev/user/quickstart.html