如何将仿射变换应用于 PIL 中的单个点?
how to apply a affine transformation to a single dot in PIL?
在 PIL 中,我将一个矩形旋转了一个角度并将其粘贴回背景图像。
我想知道特定角的新坐标是什么。
函数(old_coord) => 新坐标
我在文档中看到它提到默认情况下旋转中心是图像的中心。
这是我的代码:
import PIL
from PIL import Image, ImageDraw
import os
background = Image.open('background.jpg')
rectangle = Image.open("rectangle.png")
my_angle = 30
rectangle_rotate = Image.Image.rotate(rectangle, angle=my_angle, resample=Image.BICUBIC, expand=True)
# box: 2-tuple giving the upper left corner
px = int(background.size[0] / 2)
py = int(background.size[1] / 2)
background.paste(im=rectangle_rotate,
box=(px, py),
mask=rectangle_rotate)
# The new position I'm getting is wrong, how come?????
pos_xNew = px * math.cos(math.radians(my_angle)) + py * math.sin(math.radians(my_angle))
pos_yNew = -px * math.sin(math.radians(my_angle)) + py * math.cos(math.radians(my_angle))
print('pos_xNew:', pos_xNew)
print('pos_yNew:', pos_yNew)
draw_img_pts = ImageDraw.Draw(background)
r = 10
# Drawing a simple small circle circle for visualization
draw_img_pts.ellipse((pos_xNew - r, pos_yNew - r, pos_xNew + r, pos_yNew + r), fill='red')
background.save('example_with_roatation.png')
如何找到新的坐标值?我一直收到 错误的值。
背景图片(输入):
矩形图像(输入):
我按预期获得零旋转的输出:
旋转30度后得到的输出:
内联评论
import math
import os
import numpy as np # many operations are more concise in matrix form
import PIL
from PIL import Image, ImageDraw
def get_rotation_matrix(angle):
""" For background, https://en.wikipedia.org/wiki/Rotation_matrix
rotation is clockwise in traditional descartes, and counterclockwise,
if y goes down (as in picture coordinates)
"""
return np.array([
[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
def rotate(points, pivot, angle):
""" Get coordinates of points rotated by a given angle counterclocwise
Args:
points (np.array): point coordinates shaped (n, 2)
pivot (np.array): [x, y] coordinates of rotation center
angle (float): counterclockwise rotation angle in radians
Returns:
np.array of new coordinates shaped (n, 2)
"""
relative_points = points - pivot
return relative_points.dot(get_rotation_matrix(angle)) + pivot
background = Image.open('background.jpg')
rectangle = Image.open("rectangle.png")
my_angle_deg = 30
my_angle = math.radians(my_angle_deg)
rsize_x, rsize_y = rectangle.size
# to get shift introduced by rotation+clipping we'll need to rotate all four corners
# starting from top-right corners, counter-clockwise
rectangle_corners = np.array([
[rsize_x, 0], # top-right
[0, 0], # top-left
[0, rsize_y], # bottom-left
[rsize_x, rsize_y] # bottom-right
])
# rectangle_corners now are:
# array([[262, 0],
# [ 0, 0],
# [ 0, 67],
# [262, 67]])
rotated_corners = rotate(rectangle_corners, rectangle_corners[0], my_angle)
# as a result of rotation, one of the corners might end up left from 0,
# e.g. if the rectangle is really tall and rotated 90 degrees right
# or, leftmost corner is no longer at 0, so part of the canvas is clipped
shift_introduced_by_rotation_clip = rotated_corners.min(axis=0)
rotated_shifted_corners = rotated_corners - shift_introduced_by_rotation_clip
# finally, demo
# this part is just copied from the question
rectangle_rotate = Image.Image.rotate(rectangle, angle=my_angle_deg, resample=Image.BICUBIC, expand=True)
# box: 2-tuple giving the upper left corner
px = int(background.size[0] / 2)
py = int(background.size[1] / 2)
background.paste(im=rectangle_rotate,
box=(px, py),
mask=rectangle_rotate)
# let's see if dots land right after these translations:
draw_img_pts = ImageDraw.Draw(background)
r = 10
for point in rotated_shifted_corners:
pos_xNew, pos_yNew = point + [px, py]
draw_img_pts.ellipse((pos_xNew - r, pos_yNew - r, pos_xNew + r, pos_yNew + r), fill='red')
在 PIL 中,我将一个矩形旋转了一个角度并将其粘贴回背景图像。 我想知道特定角的新坐标是什么。
函数(old_coord) => 新坐标
我在文档中看到它提到默认情况下旋转中心是图像的中心。
这是我的代码:
import PIL
from PIL import Image, ImageDraw
import os
background = Image.open('background.jpg')
rectangle = Image.open("rectangle.png")
my_angle = 30
rectangle_rotate = Image.Image.rotate(rectangle, angle=my_angle, resample=Image.BICUBIC, expand=True)
# box: 2-tuple giving the upper left corner
px = int(background.size[0] / 2)
py = int(background.size[1] / 2)
background.paste(im=rectangle_rotate,
box=(px, py),
mask=rectangle_rotate)
# The new position I'm getting is wrong, how come?????
pos_xNew = px * math.cos(math.radians(my_angle)) + py * math.sin(math.radians(my_angle))
pos_yNew = -px * math.sin(math.radians(my_angle)) + py * math.cos(math.radians(my_angle))
print('pos_xNew:', pos_xNew)
print('pos_yNew:', pos_yNew)
draw_img_pts = ImageDraw.Draw(background)
r = 10
# Drawing a simple small circle circle for visualization
draw_img_pts.ellipse((pos_xNew - r, pos_yNew - r, pos_xNew + r, pos_yNew + r), fill='red')
background.save('example_with_roatation.png')
如何找到新的坐标值?我一直收到 错误的值。
背景图片(输入):
矩形图像(输入):
我按预期获得零旋转的输出:
旋转30度后得到的输出:
内联评论
import math
import os
import numpy as np # many operations are more concise in matrix form
import PIL
from PIL import Image, ImageDraw
def get_rotation_matrix(angle):
""" For background, https://en.wikipedia.org/wiki/Rotation_matrix
rotation is clockwise in traditional descartes, and counterclockwise,
if y goes down (as in picture coordinates)
"""
return np.array([
[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
def rotate(points, pivot, angle):
""" Get coordinates of points rotated by a given angle counterclocwise
Args:
points (np.array): point coordinates shaped (n, 2)
pivot (np.array): [x, y] coordinates of rotation center
angle (float): counterclockwise rotation angle in radians
Returns:
np.array of new coordinates shaped (n, 2)
"""
relative_points = points - pivot
return relative_points.dot(get_rotation_matrix(angle)) + pivot
background = Image.open('background.jpg')
rectangle = Image.open("rectangle.png")
my_angle_deg = 30
my_angle = math.radians(my_angle_deg)
rsize_x, rsize_y = rectangle.size
# to get shift introduced by rotation+clipping we'll need to rotate all four corners
# starting from top-right corners, counter-clockwise
rectangle_corners = np.array([
[rsize_x, 0], # top-right
[0, 0], # top-left
[0, rsize_y], # bottom-left
[rsize_x, rsize_y] # bottom-right
])
# rectangle_corners now are:
# array([[262, 0],
# [ 0, 0],
# [ 0, 67],
# [262, 67]])
rotated_corners = rotate(rectangle_corners, rectangle_corners[0], my_angle)
# as a result of rotation, one of the corners might end up left from 0,
# e.g. if the rectangle is really tall and rotated 90 degrees right
# or, leftmost corner is no longer at 0, so part of the canvas is clipped
shift_introduced_by_rotation_clip = rotated_corners.min(axis=0)
rotated_shifted_corners = rotated_corners - shift_introduced_by_rotation_clip
# finally, demo
# this part is just copied from the question
rectangle_rotate = Image.Image.rotate(rectangle, angle=my_angle_deg, resample=Image.BICUBIC, expand=True)
# box: 2-tuple giving the upper left corner
px = int(background.size[0] / 2)
py = int(background.size[1] / 2)
background.paste(im=rectangle_rotate,
box=(px, py),
mask=rectangle_rotate)
# let's see if dots land right after these translations:
draw_img_pts = ImageDraw.Draw(background)
r = 10
for point in rotated_shifted_corners:
pos_xNew, pos_yNew = point + [px, py]
draw_img_pts.ellipse((pos_xNew - r, pos_yNew - r, pos_xNew + r, pos_yNew + r), fill='red')