反转 Pillow Image Library Rotation 的平移

Reversing the translation of the 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)

我想做的是提取 matrix[2] 和 matrix[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]))

这样它就取消了枕头平移,returns图像中心回到了它最初旋转的地方。

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

例如使用以下代码:-

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

使用下面的翻译代码(在 pillow 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

这里可以看到虽然边角被裁剪了一点,但是图片的中心没有移动。

这就是我想要的结果。但是,Pillow rotate 在最后应用平移。

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

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

你明白了:-

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

具有相同的中心点。所以看起来如果没有将 expand flag 设置为 false,中心点会移动,但是如果设置为 false,所有的角都会被对称裁剪。

之所以有用,是因为如果您将一个角度传递给枕头旋转,结果是确定性的,而不是同时包含依赖于图像大小的平移。

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

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

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

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