在Python中旋转图像,并获取在旋转后的图像中的像素的原始位置。

huangapple go评论82阅读模式
英文:

Rotate an image in Python, and get the original position of a pixel which is in the rotated image

问题

  1. 旋转图像并保留图像的全部内容(允许填充)需要使用指定的角度(比如θ)。
  2. 如果原始图像中有一个像素位于x,y处,则需要找到旋转后图像中的x_r,y_r,即该像素所在位置。
  3. 如果旋转后图像中有一个像素位于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:

  1. Rotate an image by any specified angle (theta let's say), without cropping out any portion of the image (padding is allowed)
  2. 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.
  3. 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

θ=90
θ=45

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):
&quot;&quot;&quot;
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
&quot;&quot;&quot;
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 &lt; 0:
new_y = rotated_img_height + new_y
elif new_x &lt; 0:
new_x = rotated_img_width + new_x
return new_x, new_y
strip_image_path = &#39;../../dumb_data/perfect_strip.png&#39;
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=&quot;original&quot;)
draw_circle(rot_img, rotated_x, rotated_y,title=&quot;rotated&quot;)

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)

在Python中旋转图像,并获取在旋转后的图像中的像素的原始位置。

英文:

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]) -&gt; None:
    &quot;&quot;&quot;
    Entry point.

    Parameters
    ----------
    image_path_ : Path
    angle : float
    pixel_coordinates : Tuple[int, int]

    &quot;&quot;&quot;
    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) -&gt; Image:
    &quot;&quot;&quot;
    Rotate image.

    Parameters
    ----------
    image : Image
    angle : float

    Returns
    -------
    Image

    &quot;&quot;&quot;
    return image.rotate(angle, expand=True)


def transform_pixel_coordinates(
    pixel_coordinates: Tuple[int, int],
    angle: float,
    image: Image,
    rotated_image: Image,
) -&gt; Tuple[int, int]:
    &quot;&quot;&quot;
    Transform pixel coordinates.

    Parameters
    ----------
    pixel_coordinates : Tuple[int, int]
    angle : float
    image : Image
    rotated_image : Image

    Returns
    -------
    Tuple[int, int]

    &quot;&quot;&quot;

    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
) -&gt; None:
    &quot;&quot;&quot;
    Draw images and pixel.

    Parameters
    ----------
    image : Image
    rotated_image : Image
    pixel_coordinates : Tuple[int, int]
    transformed_pixel_coordinates : Tuple[int, int]

    &quot;&quot;&quot;
    fig, axes = plt.subplots(1, 2, figsize=(12, 6))

    axes[0].imshow(image)
    axes[0].scatter(*pixel_coordinates, color=&quot;y&quot;, s=50)
    axes[0].set_title(&quot;Image&quot;)
    axes[0].axis(&quot;off&quot;)

    axes[1].imshow(rotated_image)
    axes[1].scatter(*transformed_pixel_coordinates, color=&quot;y&quot;, s=50)
    axes[1].set_title(&quot;Rotated Image&quot;)
    axes[1].axis(&quot;off&quot;)
    plt.show()


if __name__ == &quot;__main__&quot;:
    image_path = Path(&quot;test.png&quot;)
    angle_ = 30
    pixel_coordinates_ = (100, 200)
    main(image_path, angle_, pixel_coordinates_)

在Python中旋转图像,并获取在旋转后的图像中的像素的原始位置。

huangapple
  • 本文由 发表于 2023年7月24日 19:39:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76754112.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定