首页 > 解决方案 > 反转 Pillow Image Library Rotation 的平移

问题描述

我对此有点新手,试图在 Python Pillow 中旋转图像而不改变旋转图像中心的位置。或通过枕头旋转事物的外观...将中心返回到其原始旋转位置。

在 Pillow (Image.py) 中有一个旋转图像的功能。该功能如下:-

def rotate(
        self,
        angle,
        resample=NEAREST,
        expand=0,
        center=None,
        translate=None,
        fillcolor=None,
    ):
        """
        Returns a rotated copy of this image.  This method returns a
        copy of this image, rotated the given number of degrees counter
        clockwise around its centre.
        :param angle: In degrees counter clockwise.
        :param resample: An optional resampling filter.  This can be
           one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
           :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
           environment), or :py:attr:`PIL.Image.BICUBIC`
           (cubic spline interpolation in a 4x4 environment).
           If omitted, or if the image has mode "1" or "P", it is
           set to :py:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`.
        :param expand: Optional expansion flag.  If true, expands the output
           image to make it large enough to hold the entire rotated image.
           If false or omitted, make the output image the same size as the
           input image.  Note that the expand flag assumes rotation around
           the center and no translation.
        :param center: Optional center of rotation (a 2-tuple).  Origin is
           the upper left corner.  Default is the center of the image.
        :param translate: An optional post-rotate translation (a 2-tuple).
        :param fillcolor: An optional color for area outside the rotated image.
        :returns: An :py:class:`~PIL.Image.Image` object.
        """

        angle = angle % 360.0

        # Fast paths regardless of filter, as long as we're not
        # translating or changing the center.
        if not (center or translate):
            if angle == 0:
                return self.copy()
            if angle == 180:
                return self.transpose(ROTATE_180)
            if angle == 90 and expand:
                return self.transpose(ROTATE_90)
            if angle == 270 and expand:
                return self.transpose(ROTATE_270)

        # Calculate the affine matrix.  Note that this is the reverse
        # transformation (from destination image to source) because we
        # want to interpolate the (discrete) destination pixel from
        # the local area around the (floating) source pixel.

        # The matrix we actually want (note that it operates from the right):
        # (1, 0, tx)   (1, 0, cx)   ( cos a, sin a, 0)   (1, 0, -cx)
        # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy)
        # (0, 0,  1)   (0, 0,  1)   (     0,     0, 1)   (0, 0,   1)

        # The reverse matrix is thus:
        # (1, 0, cx)   ( cos -a, sin -a, 0)   (1, 0, -cx)   (1, 0, -tx)
        # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty)
        # (0, 0,  1)   (      0,      0, 1)   (0, 0,   1)   (0, 0,   1)

        # In any case, the final translation may be updated at the end to
        # compensate for the expand flag.

        w, h = self.size

        if translate is None:
            post_trans = (0, 0)
        else:
            post_trans = translate
        if center is None:
            # FIXME These should be rounded to ints?
            rotn_center = (w / 2.0, h / 2.0)
        else:
            rotn_center = center

        angle = -math.radians(angle)
        matrix = [
            round(math.cos(angle), 15),
            round(math.sin(angle), 15),
            0.0,
            round(-math.sin(angle), 15),
            round(math.cos(angle), 15),
            0.0,
        ]

        def transform(x, y, matrix):
            (a, b, c, d, e, f) = matrix
            return a * x + b * y + c, d * x + e * y + f

        matrix[2], matrix[5] = transform(
            -rotn_center[0] - post_trans[0], -rotn_center[1] - post_trans[1], matrix
        )
        matrix[2] += rotn_center[0]
        matrix[5] += rotn_center[1]

        if expand:
            # calculate output size
            xx = []
            yy = []
            for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
                x, y = transform(x, y, matrix)
                xx.append(x)
                yy.append(y)
            nw = math.ceil(max(xx)) - math.floor(min(xx))
            nh = math.ceil(max(yy)) - math.floor(min(yy))

            # We multiply a translation matrix from the right.  Because of its
            # special form, this is the same as taking the image of the
            # translation vector as new translation vector.
            matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix)
            w, h = nw, nh

        return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor)

此功能还应用了一些平移(位置偏移),以便将旋转的图像角保持在图像内。应用翻译的代码部分是这一行

matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix)

我想做的是提取矩阵[2] 和矩阵[5] 的值,以便在moviepy 中调用旋转时可以反转这种平移。

为了实现这样的目标......

import moviepy.editor as mped
image_clip = mped.ImageClip("image.jpg", duration=3)
rotated_image = image_clip.rotate(20).set_position((pillow_rotate_x.
                (-matrix[2]),pillow_rotate_y.(-matrix[5]))

这样它就撤消了枕头平移,并将图像中心返回到它最初旋转的位置。

我想知道如何以最少的代码重复来实现这一点?

例如使用以下代码:-

import moviepy.editor as mped
import sys
import numpy as np

print("Python Version", sys.version)

baboon = mped.ImageClip("baboon.png", duration=3)
colour_clip = mped.ColorClip(size=[500, 50], color=np.array([250, 90, 0]).astype(np.uint8), duration=3) # important to use .astype(np.uint8)
cameraman = mped.ImageClip("cameraman.jpg", duration=3)
print("baboon_size", baboon.size)
print("colour_clip size", colour_clip.size)
print("cameraman size", cameraman.size)

rot_trans_col_clip = colour_clip.add_mask().rotate(20)
rot_trans_cameraman = cameraman.add_mask().rotate(20)
stacked_clips = mped.CompositeVideoClip([baboon, rot_trans_col_clip, rot_trans_cameraman])
stacked_clips.write_videofile('rotated_imagery_on_baboon.mp4', fps=5)

使用上面的代码,您可以将一些不同类型的内容分层并旋转它们。

狒狒和摄影师的两个输入图像文件可以在这里下载:- https://drive.google.com/file/d/17_s1IunwIAy1npJrsLRicieTG4NZYV4o/view?usp=sharing https://drive.google.com/file/d /1G5YbApGX035-9mJtuz9GNgLr6jGywk-Z/view?usp=sharing

使用下面的翻译代码(即在枕头 image.py 文件中)

matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix)

它对图像的影响如下所示:-

https://drive.google.com/file/d/1d_prYqb-fqizFcV0MD0rMXOIny2L0KW5/view?usp=sharing 在这里可以看到两个旋转图像的中心已经移动了,所以它们的角仍然在视图中(未裁剪)。

如果没有枕头旋转功能中的枕头翻译代码,它看起来像这样:-

https://drive.google.com/file/d/17POoZcuk9QAxJrnwD2LFsYd--SXdR9JA/view?usp=sharing

您可以在这里看到,虽然角落被剪掉了一点,但图像的中心并没有移动。

这就是我想要的结果。但是,枕头旋转在最后应用了平移。

有趣的是,如果您在枕头旋转上设置 expand=False:-

rot_trans_cameraman = cameraman.add_mask().rotate(20, unit='deg', expand=False)

你得到这个: -

https://drive.google.com/open?id=1QEzJN3NlWK_sjxPLGC_BNs2xfxxfhAIH

具有相同的中心点。因此,如果没有将扩展标志设置为 false,中心点似乎会移动,但是将其设置为 false,所有角都会被对称裁剪。

这很有用的原因是,如果您通过一个角度来旋转枕头,结果是确定性的,而不是还包含取决于图像大小的平移。

所以我的问题是,如何恢复旋转中心位置?

标签: pythonpython-3.xmatrixpython-imaging-librarymoviepy

解决方案


这个问题的答案在这里:-

https://github.com/python-pillow/Pillow/issues/4556

新的参考是在扩展和中心重新定位之后。然后可以在全局系统中使用它来重新定位元素。


推荐阅读