使用OpenCV旋转图像90度的最简单方法是什么?

70

在c/c++中,如何最好地旋转IplImage / cv :: Mat 90度? 我认为一定有比使用矩阵进行变换更好的方法,但我似乎在API和在线上找不到其他方法。


1
C++的答案在这里 - Antonio
7个回答

103

从OpenCV3.2开始,生活变得更加轻松了,现在您可以用一行代码旋转图像:

cv::rotate(image, image, cv::ROTATE_90_CLOCKWISE);

对于方向,您可以选择以下任何一项:

ROTATE_90_CLOCKWISE
ROTATE_180
ROTATE_90_COUNTERCLOCKWISE

46
在Python中,通过调用cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)函数来对图像进行顺时针旋转90度,并将旋转后的图像保存在dst变量中。 - miracle-doh

64

旋转是由转置和翻转组成的。

R_{+90} = F_x \circ T

R_{-90} = F_y \circ T

在OpenCV中可以这样写(以下是Python示例):

img = cv.LoadImage("path_to_image.jpg")
timg = cv.CreateImage((img.height,img.width), img.depth, img.channels) # transposed image

# rotate counter-clockwise
cv.Transpose(img,timg)
cv.Flip(timg,timg,flipMode=0)
cv.SaveImage("rotated_counter_clockwise.jpg", timg)

# rotate clockwise
cv.Transpose(img,timg)
cv.Flip(timg,timg,flipMode=1)
cv.SaveImage("rotated_clockwise.jpg", timg)

1
请查看我的 cv2,链接如下:https://dev59.com/snE95IYBdhLWcg3wkekf#42359233 - Eran W
垂直翻转(flipMode=0)应该比水平翻转要稍微昂贵一些,因为您正在更好地使用缓存。因此,逆时针旋转应该通过先进行水平翻转,然后进行转置来更快地完成。 - Cris Luengo

22

这是我的 Python cv2 实现:

import cv2

img=cv2.imread("path_to_image.jpg")

# rotate ccw
out=cv2.transpose(img)
out=cv2.flip(out,flipCode=0)

# rotate cw
out=cv2.transpose(img)
out=cv2.flip(out,flipCode=1)

cv2.imwrite("rotated.jpg", out)

1
这种方法的好处是它具有正确的旋转方面,而不是保持原始图像方面。 - VoteCoffee
尽管这可能是一个正确的解决方案,但OP明确要求C/C++解决方案,而不是Python。此外,最好给OP提供解释而不是示例代码。 - Snakehater

10

转置更新:

你应该使用cvTranspose()cv::transpose(),因为(正如你所指出的),它更有效率。同样,我建议升级到OpenCV2.0,因为大多数的cvXXX函数只是将IplImage*结构体转换为Mat对象(没有深层复制)。如果你将图像存储在一个Mat对象中,Mat.t()将返回转置。

任意旋转:

你应该使用cvWarpAffine,在变换矩阵的一般框架下定义旋转矩阵。我强烈建议升级到OpenCV2.0,它有几个新特性以及一个封装了矩阵和图像的Mat类。使用2.0版本可以使用warpAffine进行上述操作。


谢谢!你确定这不比简单地对像素数组进行转置更低效吗?有件事是,我不确定OpenCV内部是否将图像表示为像素数组,例如32位int*之类的东西,如果它确实是这样,如何获得指向该数组的指针。 - Ben H
对不起,我没有仔细阅读你的问题,我已经更新了我的答案。 - Jacob
22
必须在转置(transpose)后跟随一个翻转(cvFlip)才能正确旋转图像。 - Amnon

5

这是一个没有使用新的C++接口的示例(适用于90度、180度和270度,使用param = 1、2和3)。在使用完图像后,请记得调用cvReleaseImage释放返回的图像。

IplImage *rotate_image(IplImage *image, int _90_degrees_steps_anti_clockwise)
{
    IplImage *rotated;

    if(_90_degrees_steps_anti_clockwise != 2)
        rotated = cvCreateImage(cvSize(image->height, image->width), image->depth, image->nChannels);
    else
        rotated = cvCloneImage(image);

    if(_90_degrees_steps_anti_clockwise != 2)
        cvTranspose(image, rotated);

    if(_90_degrees_steps_anti_clockwise == 3)
        cvFlip(rotated, NULL, 1);
    else if(_90_degrees_steps_anti_clockwise == 1)
        cvFlip(rotated, NULL, 0);
    else if(_90_degrees_steps_anti_clockwise == 2)
        cvFlip(rotated, NULL, -1);

    return rotated;
}

2

以下是我的EmguCV(OpenCV的C#端口)解决方案:

public static Image<TColor, TDepth> Rotate90<TColor, TDepth>(this Image<TColor, TDepth> img)
    where TColor : struct, IColor
    where TDepth : new()
{
    var rot = new Image<TColor, TDepth>(img.Height, img.Width);
    CvInvoke.cvTranspose(img.Ptr, rot.Ptr);
    rot._Flip(FLIP.HORIZONTAL);
    return rot;
}

public static Image<TColor, TDepth> Rotate180<TColor, TDepth>(this Image<TColor, TDepth> img)
    where TColor : struct, IColor
    where TDepth : new()
{
    var rot = img.CopyBlank();
    rot = img.Flip(FLIP.VERTICAL);
    rot._Flip(FLIP.HORIZONTAL);
    return rot;
}

public static void _Rotate180<TColor, TDepth>(this Image<TColor, TDepth> img)
    where TColor : struct, IColor
    where TDepth : new()
{
    img._Flip(FLIP.VERTICAL);
    img._Flip(FLIP.HORIZONTAL);
}

public static Image<TColor, TDepth> Rotate270<TColor, TDepth>(this Image<TColor, TDepth> img)
    where TColor : struct, IColor
    where TDepth : new()
{
    var rot = new Image<TColor, TDepth>(img.Height, img.Width);
    CvInvoke.cvTranspose(img.Ptr, rot.Ptr);
    rot._Flip(FLIP.VERTICAL);
    return rot;
}

不难将其翻译成C++。

1

我在寻找一些细节,但没有找到任何示例。因此,我发布了一个transposeImage函数,希望能帮助其他寻找直接旋转90°而不丢失数据的方法的人:

IplImage* transposeImage(IplImage* image) {

    IplImage *rotated = cvCreateImage(cvSize(image->height,image->width),   
        IPL_DEPTH_8U,image->nChannels);
    CvPoint2D32f center;
    float center_val = (float)((image->width)-1) / 2;
    center.x = center_val;
    center.y = center_val;
    CvMat *mapMatrix = cvCreateMat( 2, 3, CV_32FC1 );        
    cv2DRotationMatrix(center, 90, 1.0, mapMatrix);
    cvWarpAffine(image, rotated, mapMatrix, 
        CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, 
        cvScalarAll(0));      
    cvReleaseMat(&mapMatrix);

    return rotated;
}

问题:为什么这样做?
float center_val = (float)((image->width)-1) / 2; 

答案:因为它有效 :) 我发现唯一一个不翻译图像的中心。但如果有人有解释,我会很感兴趣。

1
你的计算错误可能是由于整数除法引起的。你想要做的是:float center_val = (float)image->width / 2.0f。 - Ben H
请注意,如果您只需要以90度为增量旋转,则cvTranspose + cvFlip方法略快。 - Brent Faust
对于宽度为N像素的图像,其宽度的数学中心在像素索引0处的像素中心和索引N-1处的像素中心之间的中点,因此位于(N-1)/2处。 - mikeTronix

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