29

I need to rotate an image by either 90, 180 or 270 degrees. In OpenCV4Android I can use:

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

However, this is a huge bottleneck in my image processing algorithm. Of course, a simple rotation by a multiple of 90 degrees is much simpler than the most general case of warpAffine, and can be done much more efficiently. For 180 degrees, for instance, I could use:

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

where -1 means to flip about both horizontal and vertical axes. Is there a similar optimization I could use for 90 or 270 degree rotations?

1''
  • 23,546
  • 28
  • 128
  • 192
  • have you concluded the java solution , can you post the same – Abhishek Choudhary Oct 09 '13 at 17:42
  • both `Core.rotate(mRgba, mRgba, Core.ROTATE_180);` & `Core.flip(mRgba, mRgba, -1);` will eat about ~12-14 ms CPU on my Xiaomi Redmi 4 Prime. very bad performance. I wanted to inverse camera byte frames but it's too much – user924 May 03 '18 at 07:03

11 Answers11

45

I don't know the java api very well, this codes are developed by c++. The logics should be the same, use transpose + flip to rotate the image with 90n(n belongs to N = -minimum value of int, ....., -3, -2, -1, 0, 1, 2, 3, ..., max value of 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);
   }
}

Edit : Improve performance, thanks for the comments of TimZaman and the implementation of 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);
        }
    }
}
StereoMatching
  • 4,411
  • 5
  • 34
  • 62
  • 2
    Your loop makes it more expensive than necessary mate. – TimZaman May 23 '15 at 09:49
  • I am not fond of the temporary image created by `src.t()`: it causes every time an allocation which could be expensive especially in Android – Antonio Feb 16 '16 at 12:20
  • @Antonio The create function will only allocate new buffer when needed. In other words, it would not allocate anything if the dimensions and type of the dst same as src – StereoMatching Feb 16 '16 at 14:15
  • Sure, but I am speaking about the matrix created when calling `src.t()` – Antonio Feb 16 '16 at 14:26
  • @Antonio Thanks for pointing out src.t(), I use transpose to replace it, now it would not allocate new buffer if the dst and src have same size and type – StereoMatching Feb 16 '16 at 15:49
  • @StereoMatching I am afraid flip with same source and dest will cause an allocation. I might be wrong, it would be necessary to check opencv source code. – Antonio Feb 16 '16 at 20:21
  • @Antonio Do not worry, flip do not need to allocate memory if the size and type of the dst same as src. The codes are located at core/copy.cpp – StereoMatching Feb 17 '16 at 00:26
  • @StereoMatching You are totally right! One funny note: The only thing in which it will not work is if the 2 input Mats are 2 partially overlapping sub-Mats of the same Mat, but that's just a crazy case! – Antonio Feb 17 '16 at 09:58
  • (1) `dst.create(src.size(), src.type());` isn't needed. (2) if a wrong degree integer is passed, `dst` will be created with memory garbage. either assert or catch that or use flag integers (3) use `const` in passing src variable. – TimZaman Aug 22 '16 at 09:20
17

This is the first result when you Google it and none of these solutions really answer the question or is correct or succinct.

Core.rotate(Mat src, Mat dst, Core.ROTATE_90_CLOCKWISE); //ROTATE_180 or ROTATE_90_COUNTERCLOCKWISE
nishant1000
  • 148
  • 1
  • 7
Brian Burgess
  • 254
  • 3
  • 6
9

This will rotate an image any number of degrees, using the most efficient means for multiples of 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
        }
    }

But with opencv 3.0, this is done by just via the cv::rotate command:

cv::rotate(image, dest, e.g. cv::ROTATE_90_COUNTERCLOCKWISE);
PhilLab
  • 4,430
  • 19
  • 63
Joel Teply
  • 3,013
  • 1
  • 27
  • 20
  • Usually the output image should be passed as parameter, otherwise allocation will happen at each call. (With your implementation you have only an advantage in the case of rotation = 0) – Antonio Feb 17 '16 at 10:34
  • Pff this code is dangerous. You are returning the same underlying data as is passed in `image`, unless the rotation is default. Moreover, the canvas generated is too big due to `cv::Size(len, len)`. – TimZaman Aug 22 '16 at 09:24
  • thanks a lot! I edited and ported your first solution [0,90,180,270] for android where I had an OpenCV app and I could show the JavaCameraView in the right way. Have a good day! – Antonino Jul 19 '17 at 02:53
6

Here is a solution using the Android API. Here, I am using it to rotate images from a camera which could be mounted in various orientations.

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;
}
1''
  • 23,546
  • 28
  • 128
  • 192
3

Here is my Python translation (and thanks to all the posters):

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
Lucas W
  • 816
  • 3
  • 10
  • 21
hum3
  • 1,163
  • 9
  • 18
2

I wrote this Python version using Numpy only, which are much faster than using cv2.transpose() and 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')
Kun
  • 51
  • 1
  • 7
2

You can rotate image using numpy rot90 function

like

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

Hope this help ..

Wickkiey
  • 3,501
  • 2
  • 32
  • 39
  • 2
    Both the `rot90` function and its `k` (times) parameters are superb. This means reverse 90 deg can be also written as `np.rot90(image, 3)`. – Tomasz Gandor Apr 02 '19 at 15:53
  • 1
    There's a little gotcha - the result is an array view, which is not contiguous. `imshow` has no problem with that, but the drawing functions can throw this: Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels). It can be fixed with `np.ascontiguousarray`. – Tomasz Gandor Apr 03 '19 at 10:10
0

Use the numpy.rot90,if you want 180 degrees,just do it twice.

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)
CTD
  • 149
  • 1
  • 5
0

In 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)
David
  • 21
  • 4
0

Here's a function to rotate by any angle [-360 ... 360]

def rotate_image(image, angle):
    # Grab the dimensions of the image and then determine the center
    (h, w) = image.shape[:2]
    (cX, cY) = (w / 2, h / 2)

    # Grab the rotation matrix (applying the negative of the
    # angle to rotate clockwise), then grab the sine and cosine
    # (i.e., the rotation components of the matrix)
    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    # Compute the new bounding dimensions of the image
    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    # Adjust the rotation matrix to take into account translation
    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    # Perform the actual rotation and return the image
    return cv2.warpAffine(image, M, (nW, nH))

Usage

import cv2
import numpy as np

image = cv2.imread('1.png')
rotate = rotate_image(image, angle=90)
nathancy
  • 26,679
  • 11
  • 67
  • 86
0

Noone notices this simple method. use cv2.rotate to rotate the image 90 degrees in clockwise

image = cv2.rotate(src, cv2.cv2.ROTATE_90_CLOCKWISE) 

Other flags

ROTATE_90_CLOCKWISE Python: cv.ROTATE_90_CLOCKWISE

ROTATE_180 Python: cv.ROTATE_180

ROTATE_90_COUNTERCLOCKWISE Python: cv.ROTATE_90_COUNTERCLOCKWISE

cv2 official link https://docs.opencv.org/3.4/d2/de8/group__core__array.html#ga4ad01c0978b0ce64baa246811deeac24

RCvaram
  • 2,052
  • 2
  • 7
  • 22