使用cv.rotate和cv.warpAffine时旋转图像的差异

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

Difference in rotated image when using cv.rotate and cv.warpAffine

问题

我观察到使用cv.warpAffine方法和cv.rotate方法进行旋转时存在差异。生成的图像不同 - 虽然cv.rotate是完美的,即旋转后的图像没有部分被截断,但cv.warpAffine生成的图像有部分被截断。附上示例图像和这两种方法的结果。

配置信息:python:3.9.13;opencv-python 4.7.0;Windows 10

示例

原始图像:

使用cv.rotate和cv.warpAffine时旋转图像的差异

使用cv.rotate后的图像:

使用cv.rotate和cv.warpAffine时旋转图像的差异

使用cv.warpAffine后的图像:

使用cv.rotate和cv.warpAffine时旋转图像的差异

重现问题的代码

>>> img = cv.imread(<图像路径>, cv.IMREAD_GRAYSCALE)
>>> rot_img = cv.rotate(img, cv.ROTATE_90_COUNTERCLOCKWISE)
>>> cv.imwrite('cv_rotate.jpg', rot_img)
True
>>> img_center = (img.shape[1]//2, img.shape[0]//2)
>>> M = cv.getRotationMatrix2D(img_center, 90, 1)
>>> rot_img = cv.warpAffine(img, M, (img.shape[0], img.shape[1]))
>>> cv.imwrite('cv_warpAffine.jpg', rot_img)
True

我期望这两种方法生成相同的输出。为什么会有差异?我尝试了一些其他方法,怀疑是不是弄混了(行、列)格式和(x、y)格式(即使在生成ndarray输出时,OpenCV为什么要使用不同的样式我也不知道),但这些方法没有起作用。请有人告诉我这里的问题是什么?

英文:

I have observed that there is a difference between rotation using cv.warpAffine method and the cv.rotate method. The resulting images are different - while cv.rotate is perfect i.e. rotated image with no parts of the image getting cutoff, cv.warpAffine generates an image that has part of the image cutoff. The example image and the results of the 2 methods are attached.

Configuration: python:3.9.13; opencv-python 4.7.0; Windows 10

Samples:

original:

使用cv.rotate和cv.warpAffine时旋转图像的差异

cv.rotate:

使用cv.rotate和cv.warpAffine时旋转图像的差异

cv.warpAffine:

使用cv.rotate和cv.warpAffine时旋转图像的差异

Code to reproduce problem

&gt;&gt;&gt; img = cv.imread(&lt;path to image&gt;, cv.IMREAD_GRAYSCALE)
&gt;&gt;&gt; rot_img = cv.rotate(img, cv.ROTATE_90_COUNTERCLOCKWISE)
&gt;&gt;&gt; cv.imwrite(&#39;cv_rotate.jpg&#39;, rot_img)
True
&gt;&gt;&gt; img_center = (img.shape[1]//2, img.shape[0]//2)
&gt;&gt;&gt; M = cv.getRotationMatrix2D(img_center, 90, 1)
&gt;&gt;&gt; rot_img = cv.warpAffine(img, M, (img.shape[0], img.shape[1]))
&gt;&gt;&gt; cv.imwrite(&#39;cv_warpAffine.jpg&#39;, rot_img)
True

I was expecting both the methods to generate the same output. Why is there a difference? I have tried a few other ways anticipating that I am messing up the (row, col) format with the (x, y) format (why OpenCV uses a different style even when producing ndarray output I don't know) but those did not work. Can someone please let me know what's the issue here?

答案1

得分: 2

如果您只需要90度的旋转,请使用 cv.rotate()。它以90度的步长旋转,不会重新采样或插值。

在评论中,Cris Luengo建议使用 np.rot90(),这可能是一项非常便宜的操作,因为NumPy可以计算新的步幅并为您提供原始数据的视图。cv.rotate()不会这样做,因为内部使用的 cv::Mat 不像NumPy数组那样灵活。


如果旋转中心正确,您可以使用 cv.getRotationMatrix2D() 来实现所需的结果。

如果选择矩形的中心作为旋转中心,那只会围绕该点旋转图像,结果就是您已经看到的:结果是绿色矩形,红色矩形表示图像内容。

使用cv.rotate和cv.warpAffine时旋转图像的差异

如果源和结果的左上角原点重合,矩形旋转90度的“正确”旋转中心取决于矩形的边长和旋转方向。考虑两个正方形,一个对应于矩形的每个边长。旋转中心位于一个正方形的中心,取决于旋转方向。

...
>>> ih, iw = img.shape[:2]
>>> img_center = (iw-1)/2, (iw-1)/2
>>> M = cv.getRotationMatrix2D(img_center, +90, 1)
...
>>> img_center = (ih-1)/2, (ih-1)/2
>>> M = cv.getRotationMatrix2D(img_center, -90, 1)

使用cv.rotate和cv.warpAffine时旋转图像的差异
使用cv.rotate和cv.warpAffine时旋转图像的差异


您还可以从基本元素构建自己的变换:

def translate(tx=0, ty=0):
    T = np.eye(3)
    T[:2, 2] = (tx, ty)
    return T

def rotate(angle):
    T = np.eye(3)
    T[:2, :] = cv.getRotationMatrix2D((0,0), angle, 1)
    # 它在X右、Y下方向上逆时针旋转
    return T

angle = 90

(ih, iw) = img.shape[:2]
ow, oh = ih, iw # 仅适用于90度旋转

icx, icy = (iw-1)/2, (ih-1)/2
ocx, ocy = (ow-1)/2, (oh-1)/2

T = translate(+ocx, +ocy) @ rotate(angle) @ translate(-icx, -icy)

rot_img = cv.warpAffine(img, M=T[:2], dsize=(ow, oh))

使用cv.rotate和cv.warpAffine时旋转图像的差异


要扭曲而不是图像,有用于仿射变换的 cv.transform() 函数。

点需要作为形状为 (N, 2)(N, 1, 2) 的NumPy数组给出。

对于透视变换,有 cv.perspectiveTransform()。它会处理“除以w”的操作。

英文:

If you only need 90 degree rotations, stick with cv.rotate(). This rotates in steps of 90 degrees. It does not resample or interpolate.

In the comments, Cris Luengo suggests np.rot90(), which potentially is an extremely cheap operation because numpy can just calculate new strides and give you a view on the original data. cv.rotate() does not do that because the internally used cv::Mat isn't as flexible as a numpy array.


You can achieve the desired result with cv.getRotationMatrix2D() if the center of rotation is correct.

If you pick the center of the rectangle, that will just rotate the image around that point, and result in what you've already seen: the result is the green rectangle, with the red rectangle representing the image content.

使用cv.rotate和cv.warpAffine时旋转图像的差异

If the top left origins of source and result coincide, the "correct" center of rotation for a 90 degree rotation of a rectangle depends on the rectangle's side lengths and the direction of the rotation. Consider two squares, one for each side length of the rectangle. The center of rotation lies in the center of one or the other square, depending on direction.

...
&gt;&gt;&gt; ih, iw = img.shape[:2]
&gt;&gt;&gt; img_center = (iw-1)/2, (iw-1)/2
&gt;&gt;&gt; M = cv.getRotationMatrix2D(img_center, +90, 1)
...
&gt;&gt;&gt; img_center = (ih-1)/2, (ih-1)/2
&gt;&gt;&gt; M = cv.getRotationMatrix2D(img_center, -90, 1)

使用cv.rotate和cv.warpAffine时旋转图像的差异
使用cv.rotate和cv.warpAffine时旋转图像的差异


You can also build your own transformation from primitives:

def translate(tx=0, ty=0):
    T = np.eye(3)
    T[:2, 2] = (tx, ty)
    return T

def rotate(angle):
    T = np.eye(3)
    T[:2, :] = cv.getRotationMatrix2D((0,0), angle, 1)
    # it&#39;s rotating counterclockwise in X-right, Y-down
    return T

angle = 90

(ih, iw) = img.shape[:2]
ow,oh = ih,iw # only valid for 90 degree rotation

icx, icy = (iw-1)/2, (ih-1)/2
ocx, ocy = (ow-1)/2, (oh-1)/2

T = translate(+ocx, +ocy) @ rotate(angle) @ translate(-icx, -icy)

rot_img = cv.warpAffine(img, M=T[:2], dsize=(ow, oh))

使用cv.rotate和cv.warpAffine时旋转图像的差异


To warp points instead of images, there is the function cv.transform() for affine transformations.

Points need to be given as a numpy array of shape (N, 2) or (N, 1, 2).

For perspective transforms, there is cv.perspectiveTransform(). It takes care of the "division by w".

huangapple
  • 本文由 发表于 2023年7月14日 05:14:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683278.html
匿名

发表评论

匿名网友

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

确定