Python 透视转换在PIL中是如何工作的?
PIL的透视模式需要8个元组的数据,但我不知道如何将30度的右倾斜转换为该元组Python 透视转换在PIL中是如何工作的?,python,python-imaging-library,perspective,Python,Python Imaging Library,Perspective,PIL的透视模式需要8个元组的数据,但我不知道如何将30度的右倾斜转换为该元组 有人能解释一下吗?要应用透视变换,首先必须知道平面a中的四个点,这些点将映射到平面B中的四个点。通过这些点,可以导出同音变换。通过这样做,您可以获得8个系数,并且可以进行变换 站点镜像:,以及许多其他文本,描述了如何确定这些系数。为了让事情变得简单,这里有一个直接的实现,来自上述链接: import numpy def find_coeffs(pa, pb): matrix = [] for p1,
有人能解释一下吗?要应用透视变换,首先必须知道平面a中的四个点,这些点将映射到平面B中的四个点。通过这些点,可以导出同音变换。通过这样做,您可以获得8个系数,并且可以进行变换 站点镜像:,以及许多其他文本,描述了如何确定这些系数。为了让事情变得简单,这里有一个直接的实现,来自上述链接:
import numpy
def find_coeffs(pa, pb):
matrix = []
for p1, p2 in zip(pa, pb):
matrix.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
matrix.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])
A = numpy.matrix(matrix, dtype=numpy.float)
B = numpy.array(pb).reshape(8)
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
return numpy.array(res).reshape(8)
其中,pb是当前平面中的四个顶点,pa包含结果平面中的四个顶点
因此,假设我们变换图像,如中所示:
import sys
from PIL import Image
img = Image.open(sys.argv[1])
width, height = img.size
m = -0.5
xshift = abs(m) * width
new_width = width + int(round(xshift))
img = img.transform((new_width, height), Image.AFFINE,
(1, m, -xshift if m > 0 else 0, 0, 1, 0), Image.BICUBIC)
img.save(sys.argv[2])
以下是带有上述代码的输入和输出示例:
我们可以继续上一个代码,并执行透视变换以恢复剪切:
coeffs = find_coeffs(
[(0, 0), (256, 0), (256, 256), (0, 256)],
[(0, 0), (256, 0), (new_width, height), (xshift, height)])
img.transform((width, height), Image.PERSPECTIVE, coeffs,
Image.BICUBIC).save(sys.argv[3])
导致:
您还可以在目的地点享受一些乐趣:
我要稍微劫持一下这个问题,因为它是Google上唯一一个与Python透视转换相关的东西。下面是一些基于上述内容的更通用的代码,该代码创建透视变换矩阵并生成一个函数,该函数将在任意点上运行该变换:
import numpy as np
def create_perspective_transform_matrix(src, dst):
""" Creates a perspective transformation matrix which transforms points
in quadrilateral ``src`` to the corresponding points on quadrilateral
``dst``.
Will raise a ``np.linalg.LinAlgError`` on invalid input.
"""
# See:
# * http://xenia.media.mit.edu/~cwren/interpolator/
# * http://stackoverflow.com/a/14178717/71522
in_matrix = []
for (x, y), (X, Y) in zip(src, dst):
in_matrix.extend([
[x, y, 1, 0, 0, 0, -X * x, -X * y],
[0, 0, 0, x, y, 1, -Y * x, -Y * y],
])
A = np.matrix(in_matrix, dtype=np.float)
B = np.array(dst).reshape(8)
af = np.dot(np.linalg.inv(A.T * A) * A.T, B)
return np.append(np.array(af).reshape(8), 1).reshape((3, 3))
def create_perspective_transform(src, dst, round=False, splat_args=False):
""" Returns a function which will transform points in quadrilateral
``src`` to the corresponding points on quadrilateral ``dst``::
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... )
>>> transform((5, 5))
(74.99999999999639, 74.999999999999957)
If ``round`` is ``True`` then points will be rounded to the nearest
integer and integer values will be returned.
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... round=True,
... )
>>> transform((5, 5))
(75, 75)
If ``splat_args`` is ``True`` the function will accept two arguments
instead of a tuple.
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... splat_args=True,
... )
>>> transform(5, 5)
(74.99999999999639, 74.999999999999957)
If the input values yield an invalid transformation matrix an identity
function will be returned and the ``error`` attribute will be set to a
description of the error::
>>> tranform = create_perspective_transform(
... np.zeros((4, 2)),
... np.zeros((4, 2)),
... )
>>> transform((5, 5))
(5.0, 5.0)
>>> transform.error
'invalid input quads (...): Singular matrix
"""
try:
transform_matrix = create_perspective_transform_matrix(src, dst)
error = None
except np.linalg.LinAlgError as e:
transform_matrix = np.identity(3, dtype=np.float)
error = "invalid input quads (%s and %s): %s" %(src, dst, e)
error = error.replace("\n", "")
to_eval = "def perspective_transform(%s):\n" %(
splat_args and "*pt" or "pt",
)
to_eval += " res = np.dot(transform_matrix, ((pt[0], ), (pt[1], ), (1, )))\n"
to_eval += " res = res / res[2]\n"
if round:
to_eval += " return (int(round(res[0][0])), int(round(res[1][0])))\n"
else:
to_eval += " return (res[0][0], res[1][0])\n"
locals = {
"transform_matrix": transform_matrix,
}
locals.update(globals())
exec to_eval in locals, locals
res = locals["perspective_transform"]
res.matrix = transform_matrix
res.error = error
return res
这里是生成转换系数的纯Python版本,正如我所看到的一些人所要求的那样。我制作并使用它来制作纯Python图像绘图包
如果将其用于您自己的项目,请注意,计算需要几个高级矩阵运算,这意味着该函数需要另一个(幸运的是纯Python)矩阵库,名为matfunc,最初由Raymond Hettinger编写,您可以或
8个变换系数a、b、c、d、e、f、g、h对应于以下变换: x'=ax+by+c/gx+hy+1 y'=dx+ey+f/gx+hy+1 这8个系数通常可以从解8个线性方程组中找到,该方程组定义了平面上的4个点如何变换2D->8方程组中的4个点,请参见mmgp的答案,以获取解决此问题的代码,尽管您可能会发现更改直线更为精确
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
到
i、 也就是说,没有真正的理由将A矩阵倒过来,或者将A矩阵乘以它的转置,从而失去一点精度,从而求解方程
至于你的问题,对于围绕x0,y0的θ度的简单倾斜,你要寻找的系数是:
def find_rotation_coeffs(theta, x0, y0):
ct = cos(theta)
st = sin(theta)
return np.array([ct, -st, x0*(1-ct) + y0*st, st, ct, y0*(1-ct)-x0*st,0,0])
一般来说,任何仿射变换的g,h都必须等于零。希望有帮助 你知道透视变换涉及的方程吗?看你的答案是非常有帮助和明确的。非常感谢。您知道def find_coeffspa,pb的任何纯python实现吗?我希望避免为系统的非核心部分添加numpy依赖项。我想我自己可以解决这个问题,但我希望它已经在某个地方出现了。对于您的特定项目@kobejohn来说可能已经太晚了,但我刚刚发布了一个新的答案,它有一个用于生成系数的纯Python解决方案。@mmgp您在答案中提供的链接现在已断开,给出403.如何去除图像周围打开的空间中出现的黑色?我不知道我是否应该尝试裁剪新图像,或者是否有一种简单的方法可以使黑色部分透明。我想将转换后的图像粘贴到另一个图像的顶部,但我不能在侧面周围有额外的黑色。回答了我自己关于将转换后的图像粘贴到另一个图像上而不使“透明”图像显示为黑色的问题。检查这个问题:相关位是background.pasteforeground,0,0,foreground,需要将粘贴的图像作为第一个和第三个参数传递,以将其设置为mask,这很漂亮!谢谢你给我留言。我通过自己计算基本仿射系数完成了我的应用程序,但我可能会在将来使用它进行更复杂的变换。
res = numpy.linalg.solve(A, B)
def find_rotation_coeffs(theta, x0, y0):
ct = cos(theta)
st = sin(theta)
return np.array([ct, -st, x0*(1-ct) + y0*st, st, ct, y0*(1-ct)-x0*st,0,0])