【问题标题】:Rotate image by 90, 180 or 270 degrees将图像旋转 90、180 或 270 度
【发布时间】:2013-04-22 08:29:00
【问题描述】:

我需要将图像旋转 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解决方案你总结了吗,可以发一下吗
  • Core.rotate(mRgba, mRgba, Core.ROTATE_180);Core.flip(mRgba, mRgba, -1); 在我的小米红米 4 Prime 上会占用大约 12-14 毫秒的 CPU。非常糟糕的表现。我想反转相机字节帧,但它太多了

标签: opencv rotation


【解决方案1】:

我对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 的 cmets 和 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);
        }
    }
}

【讨论】:

  • 你的循环让它比必要的伙伴更贵。
  • 我不喜欢 src.t() 创建的临时图像:它每次都会导致分配成本很高,尤其是在 Android 中
  • @Antonio 创建函数只会在需要时分配新的缓冲区。换句话说,如果 dst 的维度和类型与 src 相同,它不会分配任何东西
  • 当然,但我说的是调用src.t()时创建的矩阵
  • @Antonio 感谢您指出 src.t(),我使用 transpose 来替换它,现在如果 dst 和 src 具有相同的大小和类型,它不会分配新的缓冲区
【解决方案2】:

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

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

【讨论】:

  • 哇,重新分配的答案!
【解决方案3】:

这会将图像旋转任意度数,使用 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 的情况下才有优势)
  • Pff 这个代码很危险。您将返回与 image 中传递的相同基础数据,除非轮换是默认值。此外,由于cv::Size(len, len),生成的画布太大。
  • 非常感谢!我为 android 编辑并移植了您的第一个解决方案 [0,90,180,270],我有一个 OpenCV 应用程序,我可以以正确的方式显示 JavaCameraView。祝你有美好的一天!
【解决方案4】:

这是一个使用 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;
}

【讨论】:

    【解决方案5】:

    这是我的 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
    

    【讨论】:

      【解决方案6】:

      我只使用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')
      

      【讨论】:

        【解决方案7】:

        您可以使用 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
        

        希望对您有所帮助..

        【讨论】:

        • rot90 函数及其k (times) 参数都非常出色。这意味着反向 90 度也可以写成np.rot90(image, 3)
        • 有一个小问题 - 结果是一个数组视图,它不是连续的。 imshow 对此没有任何问题,但绘图函数会抛出此问题:输出数组 img 的布局与 cv::Mat 不兼容 (step[ndims-1] != elemsize or step[1] != elemsize*nchannels )。可以用np.ascontiguousarray修复。
        【解决方案8】:

        使用numpy.rot90,如果你想要180度,就做两次。

        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)
        

        【讨论】:

          【解决方案9】:

          在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)
          

          【讨论】:

            【解决方案10】:

            这是一个任意角度旋转的函数[-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))
            

            用法

            import cv2
            import numpy as np
            
            image = cv2.imread('1.png')
            rotate = rotate_image(image, angle=90)
            

            【讨论】:

              【解决方案11】:

              没有人注意到这个简单的方法。使用cv2.rotate将图像顺时针旋转90度

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

              其他标志

              ROTATE_90_CLOCKWISE Python:cv.ROTATE_90_CLOCKWISE

              ROTATE_180 Python:cv.ROTATE_180

              ROTATE_90_COUNTERCLOCKWISE Python:cv.ROTATE_90_COUNTERCLOCKWISE

              cv2官方链接https://docs.opencv.org/3.4/d2/de8/group__core__array.html#ga4ad01c0978b0ce64baa246811deeac24

              【讨论】: