将图像旋转90度、180度或270度。

30

我需要将一张图片旋转90度、180度或270度。在OpenCV4Android中,我可以使用:

Imgproc.getRotationMatrix2D(new Point(center, center), degrees, 1);
Imgproc.warpAffine(src, dst, rotationMatrix, dst.size());

然而,这是我图像处理算法的一个巨大瓶颈。当然,将图像旋转多个90度的简单旋转比warpAffine的最普通情况要简单得多,并且可以更有效地完成。例如,对于180度,我可以使用:

Core.flip(src, dst, -1);

当值为-1时,表示对水平和垂直轴进行翻转。是否有类似的优化方法可用于90度或270度旋转?


你已经完成了Java的解决方案吗?能否发布一下呢? - Abhishek Choudhary
Core.rotate(mRgba, mRgba, Core.ROTATE_180);Core.flip(mRgba, mRgba, -1);在我的小米红米4 Prime上都需要大约12-14毫秒的CPU时间。性能非常差。我想反转相机字节帧,但这太耗费资源了。 - user924
11个回答

46

我不太熟悉Java API,这段代码是用C++开发的。 逻辑应该是相同的,使用转置+翻转来旋转图像,角度为90n(n属于N=-int的最小值,......,-3,-2,-1,0,1,2,3,......,int的最大值)。

/*
 *@brief rotate image by multiple of 90 degrees
 *
 *@param source : input image
 *@param dst : output image
 *@param angle : factor of 90, even it is not factor of 90, the angle
 * will be mapped to the range of [-360, 360].
 * {angle = 90n; n = {-4, -3, -2, -1, 0, 1, 2, 3, 4} }
 * if angle bigger than 360 or smaller than -360, the angle will
 * be map to -360 ~ 360.
 * mapping rule is : angle = ((angle / 90) % 4) * 90;
 *
 * ex : 89 will map to 0, 98 to 90, 179 to 90, 270 to 3, 360 to 0.
 *
 */
void rotate_image_90n(cv::Mat &src, cv::Mat &dst, int angle)
{   
   if(src.data != dst.data){
       src.copyTo(dst);
   }

   angle = ((angle / 90) % 4) * 90;

   //0 : flip vertical; 1 flip horizontal
   bool const flip_horizontal_or_vertical = angle > 0 ? 1 : 0;
   int const number = std::abs(angle / 90);          

   for(int i = 0; i != number; ++i){
       cv::transpose(dst, dst);
       cv::flip(dst, dst, flip_horizontal_or_vertical);
   }
}

修改:改进性能,感谢TimZaman的评论和1''的实现。

void rotate_90n(cv::Mat const &src, cv::Mat &dst, int angle)
{        
     CV_Assert(angle % 90 == 0 && angle <= 360 && angle >= -360);
     if(angle == 270 || angle == -90){
        // Rotate clockwise 270 degrees
        cv::transpose(src, dst);
        cv::flip(dst, dst, 0);
    }else if(angle == 180 || angle == -180){
        // Rotate clockwise 180 degrees
        cv::flip(src, dst, -1);
    }else if(angle == 90 || angle == -270){
        // Rotate clockwise 90 degrees
        cv::transpose(src, dst);
        cv::flip(dst, dst, 1);
    }else if(angle == 360 || angle == 0 || angle == -360){
        if(src.data != dst.data){
            src.copyTo(dst);
        }
    }
}

3
你的循环使得它比必要的更昂贵,伙计。 - TimZaman
我不喜欢src.t()创建的临时图像:它会导致每次分配,这可能在Android上特别昂贵。 - Antonio
@Antonio create函数只有在需要时才会分配新的缓冲区。换句话说,如果目标(dst)的维度和类型与源(src)相同,则不会分配任何内容。 - StereoMatching
@Antonio 感谢您指出src.t()的问题,我使用transpose来替换它,现在如果dst和src具有相同的大小和类型,则不会分配新的缓冲区。 - StereoMatching
@Antonio 不用担心,如果dst的大小和类型与src相同,则flip不需要分配内存。 代码位于core / copy.cpp。 - StereoMatching
显示剩余4条评论

19

这是在谷歌上搜索时的第一个结果,但这些解决方案都没有真正回答问题或者正确简洁。

Core.rotate(Mat src, Mat dst, Core.ROTATE_90_CLOCKWISE); //ROTATE_180 or ROTATE_90_COUNTERCLOCKWISE

哇,一个重新分配的答案! - Tom

9

这将旋转图像任意角度,对于90的倍数使用最有效的方法。

    void
    rotate_cw(const cv::Mat& image, cv::Mat& dest, int degrees)
    {
        switch (degrees % 360) {
            case 0:
                dest = image.clone();
                break;
            case 90:
                cv::flip(image.t(), dest, 1);
                break;
            case 180:
                cv::flip(image, dest, -1);
                break;
            case 270:
                cv::flip(image.t(), dest, 0);
                break;
            default:
                cv::Mat r = cv::getRotationMatrix2D({image.cols/2.0F, image.rows/2.0F}, degrees, 1.0);
                int len = std::max(image.cols, image.rows);
                cv::warpAffine(image, dest, r, cv::Size(len, len));
                break; //image size will change
        }
    }

然而在OpenCV 3.0中,这可以通过使用 cv::rotate 命令来完成:

cv::rotate(image, dest, e.g. cv::ROTATE_90_COUNTERCLOCKWISE);

通常情况下,输出图像应作为参数传递,否则每次调用都会发生分配。(使用您的实现,只有在旋转= 0的情况下才具有优势) - Antonio
这段代码很危险。除非旋转是默认的,否则您将返回与“image”传入的相同的基础数据。此外,“cv::Size(len, len)”生成的画布太大了。 - TimZaman
非常感谢您的帮助!我编辑并移植了您给出的解决方案 [0,90,180,270] 到 Android 平台,我的 OpenCV 应用程序可以正确显示 JavaCameraView。祝您有愉快的一天! - Antonino

6

以下是使用Android API的解决方案。这里,我将其用于从相机旋转图像,该相机可以安装在各种方向。

if (mCameraOrientation == 270) {
    // Rotate clockwise 270 degrees
    Core.flip(src.t(), dst, 0);
} else if (mCameraOrientation == 180) {
    // Rotate clockwise 180 degrees
    Core.flip(src, dst, -1);
} else if (mCameraOrientation == 90) {
    // Rotate clockwise 90 degrees
    Core.flip(src.t(), dst, 1);
} else if (mCameraOrientation == 0) {
    // No rotation
    dst = src;
}

3

这是我的Python翻译(感谢所有的发帖者):

import cv2
def rot90(img, rotflag):
    """ rotFlag 1=CW, 2=CCW, 3=180"""
    if rotflag == 1:
        img = cv2.transpose(img)  
        img = cv2.flip(img, 1)  # transpose+flip(1)=CW
    elif rotflag == 2:
        img = cv2.transpose(img)  
        img = cv2.flip(img, 0)  # transpose+flip(0)=CCW
    elif rotflag ==3:
        img = cv2.flip(img, -1)  # transpose+flip(-1)=180
    elif rotflag != 0:  # if not 0,1,2,3
        raise Exception("Unknown rotation flag({})".format(rotflag))
    return img

2
您可以使用numpy的rot90函数旋转图像。例如:
def rotate_image(image,deg):
    if deg ==90:
        return np.rot90(image)
    if deg ==180:
        return np.rot90(image,2)
    if deg == 270:
        return np.rot90(image,-1) #Reverse 90 deg rotation

希望这可以帮到您..

2
rot90函数及其k(次数)参数都非常出色。这意味着反向旋转90度也可以写成np.rot90(image, 3) - Tomasz Gandor
1
有一个小问题 - 结果是一个非连续的数组视图。imshow没有问题,但是绘图函数可能会抛出以下错误:输出数组img的布局与cv :: Mat不兼容(step [ndims-1]!= elemsize或step [1]!= elemsize * nchannels)。可以使用np.ascontiguousarray来解决这个问题。 - Tomasz Gandor
注意:此答案显示的是 Python/Numpy,而问题显示的是 Java - Christoph Rackwitz

2
我使用仅限于Numpy的Python版本进行编写,这比使用cv2.transpose()cv2.flip()要快得多。
def rotate_image_90(im, angle):
    if angle % 90 == 0:
        angle = angle % 360
        if angle == 0:
            return im
        elif angle == 90:
            return im.transpose((1,0, 2))[:,::-1,:]
        elif angle == 180:
            return im[::-1,::-1,:]
        elif angle == 270:
            return im.transpose((1,0, 2))[::-1,:,:]

    else:
        raise Exception('Error')

0

如果你想要旋转180度,只需使用numpy.rot90两次即可。

import numpy as np
import cv2

img = cv2.imread('img.png',1)
cv2.imshow('',img)
cv2.waitKey(0)

img90 = np.rot90(img)
cv2.imshow('',img90)
cv2.waitKey(0)

0
在Python中:
# import the necessary packages
import numpy as np
import cv2

# initialize the camera and grab a reference to the raw camera capture
vs = cv2.VideoCapture(0)
(ret, image_original) = vs.read()
image_rotated_90 = np.rot90(image_original)
image_rotated_180 = np.rot90(image_rotated_90)

# show the frame and press any key to quit the image frame
cv2.imshow("Frame", image_rotated_180)
cv2.waitKey(0)

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接