英文:
Rotate an image in Python, and get the original position of a pixel which is in the rotated image
问题
- 旋转图像并保留图像的全部内容(允许填充)需要使用指定的角度(比如θ)。
- 如果原始图像中有一个像素位于x,y处,则需要找到旋转后图像中的x_r,y_r,即该像素所在位置。
- 如果旋转后图像中有一个像素位于x_r, y_r处,则需要找到它在原始图像中的位置x,y。
我尝试使用PIL的image.rotate函数进行旋转,并保持expand=True
。
我开发了一个像素反转函数,对于90度的旋转工作正常,但对于0到90之间的角度,它无法正常工作。
下面是我迄今为止编写的代码。
import math
from PIL import Image, ImageDraw
from icecream import ic
def draw_circle(img, x, y, title):
# 创建一个ImageDraw对象来在图像上绘制
draw = ImageDraw.Draw(img)
# 设置点的中心坐标
point_x, point_y = x, y
# 设置点的大小(宽度和高度)(使其更大)
point_size = 50 # 您可以调整此值以更改点的大小
# 设置点的颜色(在此示例中为红色,表示为RGB)
point_color = (255, 0, 0)
# 计算椭圆的边界框(表示点)
point_bbox = (
point_x - point_size // 2,
point_y - point_size // 2,
point_x + point_size // 2,
point_y + point_size // 2,
)
# 在图像上绘制椭圆(表示点)
draw.ellipse(point_bbox, fill=point_color, outline=point_color)
img.show(title=title)
# 关闭ImageDraw对象
del draw
def rotate_pixel(x, y, theta, rotated_img):
"""
给定此函数,原始图像旋转的X、Y位置,原始图像旋转的角度,
和旋转后的图像本身。
返回:旋转后图像中该像素的X、Y坐标
要反转旋转,请提供结果X、Y坐标以及-theta,以及原始图像
"""
rotated_img_width, rotated_img_height = rotated_img.size
# 将theta转换为弧度
theta_rad = math.radians(theta)
# 计算旋转后的新X和Y坐标
new_x = round(x * math.cos(theta_rad) - y * math.sin(theta_rad))
new_y = round(x * math.sin(theta_rad) + y * math.cos(theta_rad))
if new_y < 0:
new_y = rotated_img_height + new_y
elif new_x < 0:
new_x = rotated_img_width + new_x
return new_x, new_y
strip_image_path = '../../dumb_data/perfect_strip.png'
angle = 90
img = Image.open(strip_image_path)
rot_img = img.rotate(angle, expand=True)
width, height = img.size
rot_width, rot_height = rot_img.size
original_x, original_y = 500, 100
rotated_x, rotated_y = rotate_pixel(original_x, original_y, -angle, rotated_img=rot_img) # 旋转坐标(img, +angle, original_x, original_y)
reverse_x, reverse_y = rotate_pixel(rotated_x, rotated_y, angle, rotated_img=img) # 旋转坐标(img, -angle, rotated_x, rotated_y)
# 定义图像尺寸
image_width, image_height = img.size
print()
ic(img.size)
ic(rot_img.size)
ic(original_x, original_y)
ic(rotated_x, rotated_y)
ic(reverse_x, reverse_y)
draw_circle(img, original_x, original_y, title="original")
draw_circle(rot_img, rotated_x, rotated_y, title="rotated")
欢迎任何能够实现上述目标的解决方案。
英文:
One part of the project I am currently working on, requires me to achieve the following:
- Rotate an image by any specified angle (theta let's say), without cropping out any portion of the image (padding is allowed)
- If I have a pixel at x,y in the original image, find the x_r,y_r in the rotated image, where the pixel lies.
- If I have a pixel at x_r, y_r in the rotated image, find the x,y where this pixel lies in the original image.
I tried using PIL's image.rotate function for rotation and kept expand=True
.
I developed a pixel reversal function which works fine for 90 degree rotation , but on using angles between 0 and 90, it doesn't work properly.
Here's a couple of images showing the rotation and it's results, with the red dot indicating the two pixels that are supposed to be identical. The image on the left is the original, whereas the one on the right is the rotated one
Here's the code I wrote so far.
import math
from PIL import Image, ImageDraw
from icecream import ic
def draw_circle(img, x, y,title):
# Create an ImageDraw object to draw on the image
draw = ImageDraw.Draw(img)
# Set the coordinates for the center of the point
point_x, point_y = x, y
# Set the size (width and height) of the point (making it bigger)
point_size = 50 # You can adjust this value to change the size of the point
# Set the color of the point (red in this example, represented as RGB)
point_color = (255, 0, 0)
# Calculate the bounding box for the ellipse
point_bbox = (
point_x - point_size // 2,
point_y - point_size // 2,
point_x + point_size // 2,
point_y + point_size // 2,
)
# Draw the ellipse (representing the point) on the image
draw.ellipse(point_bbox, fill=point_color, outline=point_color)
img.show(title=title)
# Close the ImageDraw object
del draw
def rotate_pixel(x, y, theta,rotated_img):
"""
Give this function, the X,Y positions from the original image, the angle by which the original image is rotated,
and the rotated image itself.
Returns: The X,Y coordinates of that exact pixel in the rotated image
To reverse the rotation, give the resulting X,Y coordinates, along with -theta, and the original image
"""
rotated_img_width, rotated_img_height = rotated_img.size
# Convert theta to radians
theta_rad = math.radians(theta)
# Calculate the new x and y coordinates after rotation
new_x = round(x * math.cos(theta_rad) - y * math.sin(theta_rad))
new_y = round(x * math.sin(theta_rad) + y * math.cos(theta_rad))
if new_y < 0:
new_y = rotated_img_height + new_y
elif new_x < 0:
new_x = rotated_img_width + new_x
return new_x, new_y
strip_image_path = '../../dumb_data/perfect_strip.png'
angle = 90
img = Image.open(strip_image_path)
rot_img = img.rotate(angle, expand=True)
width,height = img.size
rot_width,rot_height = rot_img.size
original_x, original_y = 500, 100
rotated_x, rotated_y = rotate_pixel(original_x,original_y,-angle,rotated_img=rot_img) # rotation_coordinates(img, +angle, original_x, original_y)
reverse_x, reverse_y = rotate_pixel(rotated_x,rotated_y,angle,rotated_img=img) #rotation_coordinates(img, -angle, rotated_x, rotated_y)
# Define the image dimensions
image_width, image_height = img.size
print()
ic(img.size)
ic(rot_img.size)
ic(original_x, original_y)
ic(rotated_x, rotated_y)
ic(reverse_x, reverse_y)
draw_circle(img, original_x, original_y,title="original")
draw_circle(rot_img, rotated_x, rotated_y,title="rotated")
Any solutions which achieve the general above mentioned goal are welcome
答案1
得分: 1
这是一个使用PIL(Python Imaging Library)的解决方案,带有类型提示和文档字符串。
图像被上传并旋转,但不进行裁剪,使用PIL库本身提供的方法。然后,只需在平面上应用简单的旋转。
如果这不是您要找的,请让我知道,我将尝试改进它。
from pathlib import Path
from typing import Tuple
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
def main(image_path_: Path, angle: float, pixel_coordinates: Tuple[int, int]) -> None:
"""
入口点。
Parameters
----------
image_path_ : Path
angle : float
pixel_coordinates : Tuple[int, int]
"""
image = Image.open(image_path_)
rotated_image = rotate_image(image, angle)
transformed_pixel_coordinates = transform_pixel_coordinates(
pixel_coordinates, angle, image, rotated_image
)
draw_images(image, rotated_image, pixel_coordinates, transformed_pixel_coordinates)
def rotate_image(image: Image, angle: float) -> Image:
"""
旋转图像。
Parameters
----------
image : Image
angle : float
Returns
-------
Image
"""
return image.rotate(angle, expand=True)
def transform_pixel_coordinates(
pixel_coordinates: Tuple[int, int],
angle: float,
image: Image,
rotated_image: Image,
) -> Tuple[int, int]:
"""
转换像素坐标。
Parameters
----------
pixel_coordinates : Tuple[int, int]
angle : float
image : Image
rotated_image : Image
Returns
-------
Tuple[int, int]
"""
x, y = pixel_coordinates
center = (image.width / 2, image.height / 2)
transformed_center = (rotated_image.width / 2, rotated_image.height / 2)
angle_radians = -np.deg2rad(angle)
x -= center[0]
y -= center[1]
x_transformed = x * np.cos(angle_radians) - y * np.sin(angle_radians)
y_transformed = x * np.sin(angle_radians) + y * np.cos(angle_radians)
x_transformed += transformed_center[0]
y_transformed += transformed_center[1]
return int(x_transformed), int(y_transformed)
def draw_images(
image, rotated_image, pixel_coordinates, transformed_pixel_coordinates
) -> None:
"""
绘制图像和像素。
Parameters
----------
image : Image
rotated_image : Image
pixel_coordinates : Tuple[int, int]
transformed_pixel_coordinates : Tuple[int, int]
"""
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].imshow(image)
axes[0].scatter(*pixel_coordinates, color="y", s=50)
axes[0].set_title("Image")
axes[0].axis("off")
axes[1].imshow(rotated_image)
axes[1].scatter(*transformed_pixel_coordinates, color="y", s=50)
axes[1].set_title("Rotated Image")
axes[1].axis("off")
plt.show()
if __name__ == "__main__":
image_path = Path("test.png")
angle = 30
pixel_coordinates = (100, 200)
main(image_path, angle, pixel_coordinates)
英文:
Here is a solution using PIL with type hints and docstrings.
The image is uploaded and rotated, but without cropping, using the method provided by the PIL library itself. Then, it's just about applying a simple rotation on the plane.
If it's not what you are looking for, I will try to make it better.
from pathlib import Path
from typing import Tuple
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
def main(image_path_: Path, angle: float, pixel_coordinates: Tuple[int, int]) -> None:
"""
Entry point.
Parameters
----------
image_path_ : Path
angle : float
pixel_coordinates : Tuple[int, int]
"""
image = Image.open(image_path_)
rotated_image = rotate_image(image, angle_)
transformed_pixel_coordinates = transform_pixel_coordinates(
pixel_coordinates, angle, image, rotated_image
)
draw_images(image, rotated_image, pixel_coordinates_, transformed_pixel_coordinates)
def rotate_image(image: Image, angle: float) -> Image:
"""
Rotate image.
Parameters
----------
image : Image
angle : float
Returns
-------
Image
"""
return image.rotate(angle, expand=True)
def transform_pixel_coordinates(
pixel_coordinates: Tuple[int, int],
angle: float,
image: Image,
rotated_image: Image,
) -> Tuple[int, int]:
"""
Transform pixel coordinates.
Parameters
----------
pixel_coordinates : Tuple[int, int]
angle : float
image : Image
rotated_image : Image
Returns
-------
Tuple[int, int]
"""
x, y = pixel_coordinates
center = (image.width / 2, image.height / 2)
transformed_center = (rotated_image.width / 2, rotated_image.height / 2)
angle_radians = -np.deg2rad(angle)
x -= center[0]
y -= center[1]
x_transformed = x * np.cos(angle_radians) - y * np.sin(angle_radians)
y_transformed = x * np.sin(angle_radians) + y * np.cos(angle_radians)
x_transformed += transformed_center[0]
y_transformed += transformed_center[1]
return int(x_transformed), int(y_transformed)
def draw_images(
image, rotated_image, pixel_coordinates, transformed_pixel_coordinates
) -> None:
"""
Draw images and pixel.
Parameters
----------
image : Image
rotated_image : Image
pixel_coordinates : Tuple[int, int]
transformed_pixel_coordinates : Tuple[int, int]
"""
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].imshow(image)
axes[0].scatter(*pixel_coordinates, color="y", s=50)
axes[0].set_title("Image")
axes[0].axis("off")
axes[1].imshow(rotated_image)
axes[1].scatter(*transformed_pixel_coordinates, color="y", s=50)
axes[1].set_title("Rotated Image")
axes[1].axis("off")
plt.show()
if __name__ == "__main__":
image_path = Path("test.png")
angle_ = 30
pixel_coordinates_ = (100, 200)
main(image_path, angle_, pixel_coordinates_)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论