Python/PIL仿射变换

标签 python matlab python-imaging-library transformation pillow

这是 PIL 中的一个基本转换问题。我至少试过几次 在过去的几年里正确地实现了这一点,似乎有 关于 PIL 中的 Image.transform,我不太了解。我想要 在我可以的地方实现相似变换(或仿射变换) 清楚地说明图像的限制。为了确保我的方法有效,我 在 Matlab 中实现它。

Matlab 实现如下:

im = imread('test.jpg');
y = size(im,1);
x = size(im,2);
angle = 45*3.14/180.0;
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)];
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)];
m = [cos(angle) sin(angle) -min(xextremes); -sin(angle) cos(angle) -min(yextremes); 0 0 1];
tform = maketform('affine',m')
round( [max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)])
im = imtransform(im,tform,'bilinear','Size',round([max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)]));
imwrite(im,'output.jpg');

function y = rot_x(angle,ptx,pty),
    y = cos(angle)*ptx + sin(angle)*pty

function y = rot_y(angle,ptx,pty),
    y = -sin(angle)*ptx + cos(angle)*pty

这按预期工作。这是输入:

enter image description here

这是输出:

enter image description here

这是实现相同的Python/PIL代码 转换:

import Image
import math

def rot_x(angle,ptx,pty):
    return math.cos(angle)*ptx + math.sin(angle)*pty

def rot_y(angle,ptx,pty):
    return -math.sin(angle)*ptx + math.cos(angle)*pty

angle = math.radians(45)
im = Image.open('test.jpg')
(x,y) = im.size
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)]
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)]
mnx = min(xextremes)
mxx = max(xextremes)
mny = min(yextremes)
mxy = max(yextremes)
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),-mnx,-math.sin(angle),math.cos(angle),-mny),resample=Image.BILINEAR)
im.save('outputpython.jpg')

这是 Python 的输出:

enter image description here

这些年来,我已经在多个操作系统上使用多个版本的 Python 和 PIL 进行了尝试,结果总是大体相同。

这是说明问题的最简单的可能情况,我知道如果它是我想要的旋转,我可以使用 im.rotate 调用进行旋转,但我也想剪切和缩放,这只是一个例子来说明一个问题。我想为所有仿射变换获得相同的输出。我希望能够做到这一点。

编辑:

如果我将变换线更改为:

im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),0,-math.sin(angle),math.cos(angle),0),resample=Image.BILINEAR)

这是我得到的输出:

enter image description here

编辑 #2

我旋转了 -45 度并将偏移量更改为 -0.5*mnx 和 -0.5*mny 并获得了这个:

enter image description here

最佳答案

好的!所以我整个周末都在努力理解这一点,我想我有一个 令我满意的答案。感谢大家的意见和建议!

我先看看这个:

affine transform in PIL python

虽然我看到作者可以进行任意相似性变换 没有解释为什么我的代码不起作用,也没有解释空间 我们需要变换的图像布局,他也不提供线性 我的问题的代数解。

但我确实从他的代码中看到我确实看到他正在划分 矩阵 (a,b,d 和 e) 到让我感到奇怪的尺度。我回去读书了 我引用的 PIL 文档:

“im.transform(size, AFFINE, data, filter) => image

对图像应用仿射变换,并将结果放入新图像中 具有给定的大小。

数据是一个 6 元组(a、b、c、d、e、f),其中包含来自 仿射变换矩阵。对于输出图像中的每个像素 (x, y),新的 值取自输入中的位置 (a x + b y + c, d x + e y + f) 图像,四舍五入到最近的像素。

这个函数可以用来对原图进行缩放、平移、旋转、剪切 图片。”

所以参数 (a,b,c,d,e,f) 是一个变换矩阵,但是映射的那个 目标图像中的 (x,y) 到源图像中的 (a x + b y + c, d x + e y + f) 图片。但不是您要应用的变换矩阵的参数,而是 它的倒数。即:

  • 奇怪
  • 与 Matlab 不同
  • 但现在,幸运的是,我完全理解

我附上我的代码:

import Image
import math
from numpy import matrix
from numpy import linalg

def rot_x(angle,ptx,pty):
    return math.cos(angle)*ptx + math.sin(angle)*pty

def rot_y(angle,ptx,pty):
    return -math.sin(angle)*ptx + math.cos(angle)*pty

angle = math.radians(45)
im = Image.open('test.jpg')
(x,y) = im.size
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)]
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)]
mnx = min(xextremes)
mxx = max(xextremes)
mny = min(yextremes)
mxy = max(yextremes)
print mnx,mny
T = matrix([[math.cos(angle),math.sin(angle),-mnx],[-math.sin(angle),math.cos(angle),-mny],[0,0,1]])
Tinv = linalg.inv(T);
print Tinv
Tinvtuple = (Tinv[0,0],Tinv[0,1], Tinv[0,2], Tinv[1,0],Tinv[1,1],Tinv[1,2])
print Tinvtuple
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,Tinvtuple,resample=Image.BILINEAR)
im.save('outputpython2.jpg')

和python的输出:

enter image description here

让我在最后的总结中再次陈述这个问题的答案:

PIL 需要您要应用的仿射变换的逆。

关于Python/PIL仿射变换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17056209/

相关文章:

python - 如何扩展和填充黑白图像的第三个暗角

python - 使用 PIL 和 cv2 压缩 PNG

python - 在使用 Python 的 vscode 中,ctrl+F5 总是要求输入 "select environment"

python - Pandas 获取部分数据框并对值进行归一化

python - 哪个 Pandas 数据框更好 : super long dataframe VS badly structured one with lists

c++ - 为什么 Visual Studio 在 64 位平台上链接 MSVCR90.dll (x86)?

MATLAB 提高求和运算符的性能

python - 将图像从目录 (Python) 导入列表或字典

python - 为什么 python 不将异常记录到 syslog(但它确实记录了?)

matlab - 从文件中将带空格的字符串读入 MATLAB