​    我记得曾经有人对OpenCV的旋转吐槽,意思是它自己没有很好的关于选择的算法。在新的版本里面添加了这些函数(我还没有时间去看是什么时候pr的)。现在一个比较棘手的问题,就是OpenCV中旋转是如何定量的,什么是正方向?什么是负方向?什么时候用角度?什么时候用弧度?
下面就是针对这几个问题,通过查资料、做实验的方式搞清楚。
一、OpenCV中旋转式如何定量的
也就是坐标系问题。OpenCV坐标系以(0,0)点为原点,以向下为Y轴正方向,以向右为X轴正方向。
对于旋转而言,通过“旋转中点”和“旋转的角度”两个值来定量一个“旋转”。

二、什么是正方向?什么是负方向?什么时候用角度?什么时候用弧度?
一般来说,坐标系中以X轴正方向为原点,而以逆时针为正、顺时针为负。
至于什么时候用角度、什么时候用弧度,一般来说,角度180度对应于弧度的PI,由于在使用函数的时候,一般只能以数值类型(不带单位)作为参数,所以需要根据函数自己的要求选择弧度还是角度.

三、相关的函数和使用方法。
可能这才是最为关键的。我这样来归纳。和旋转相关的函数可以大致分为“获得旋转”和“使用旋转”两类。
“获得旋转”而言,旋转矩形、PCA都可以获得旋转;
“使用旋转”而言,可以使用放射变换warpAffine

具体而言,包括以下内容:

1、CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode);
这是OpenCV在新版本里面提供的选择函数,第3个参数是旋转的选择,具体如下
enum RotateFlags {
    ROTATE_90_CLOCKWISE = 0, //Rotate 90 degrees clockwise
    ROTATE_180 = 1, //Rotate 180 degrees clockwise
    ROTATE_90_COUNTERCLOCKWISE = 2, //Rotate 270 degrees clockwise
};
/** @brief Rotates a 2D array in multiples of 90 degrees.
The function rotate rotates the array in one of three different ways:
*   Rotate by 90 degrees clockwise (rotateCode = ROTATE_90).
*   Rotate by 180 degrees clockwise (rotateCode = ROTATE_180).
*   Rotate by 270 degrees clockwise (rotateCode = ROTATE_270).
@param src input array.
@param dst output array of the same type as src.  The size is the same with ROTATE_180,
and the rows and cols are switched for ROTATE_90 and ROTATE_270.
@param rotateCode an enum to specify how to rotate the array; see the enum RotateFlags
@sa transpose , repeat , completeSymm, flip, RotateFlags
*/

那么它存在的问题就是只能旋转具体的角度(90、180)等,不是很灵活

2、rotate修改
为了解决不是很灵活的问题,自己重新写的旋转函数,第3个参数直接是旋转的角度
void rotate(const Mat& src, Mat& dst, float angle)
{
    CV_Assert(!src.empty());
    float radian = angle /180.0 * PI;
    int uniSize = max(src.cols, src.rows) * 2;
    int dx = (uniSize - src.cols) / 2;
    int dy = (uniSize - src.rows) / 2;
    copyMakeBorder(src, dst, dy, dy, dx, dx, BORDER_CONSTANT);
     //旋转中心
     Point2f center(dst.cols/2, dst.rows/2);
     Mat affine_matrix = getRotationMatrix2D( center, angle, 1.0 );
     warpAffine(dst, dst, affine_matrix, dst.size());
     float sinVal = fabs(sin(radian));
     float cosVal = fabs(cos(radian));
     //旋转后的图像大小
     Size targetSize(src.cols * cosVal + src.rows * sinVal,src.cols * sinVal + src.rows * cosVal);
     //剪掉四周边框
     int x = (dst.cols - targetSize.width) / 2;
     int y = (dst.rows - targetSize.height) / 2;
     Rect rect(x, y, targetSize.width, targetSize.height);
     dst = Mat(dst, rect);
 }


3、getRotationMatrix2D函数
主要用于获得图像绕着 某一点的旋转矩阵 
函数调用形式:
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
参数详解:
Point2f center:表示旋转的中心点
double angle:表示旋转的角度
double scale:图像缩放因子
例子
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
/// 全局变量
char* source_window = "Source image";
char* warp_window = "Warp";
char* warp_rotate_window = "Warp + Rotate";
/** @function main */
int main( int argc, char** argv )
{
Point2f srcTri[3];
Point2f dstTri[3];
Mat rot_mat( 2, 3, CV_32FC1 );
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst;
/// 加载源图像
src = imread( argv[1], 1 );
/// 设置目标图像的大小和类型与源图像一致
warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
/// 设置源图像和目标图像上的三组点以计算仿射变换
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1, 0 );
srcTri[2] = Point2f( 0, src.rows - 1 );
dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 ); 
/// 求得仿射变换
warp_mat = getAffineTransform( srcTri, dstTri );
/// 对源图像应用上面求得的仿射变换
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
/** 对图像扭曲后再旋转 */
/// 计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
double angle = -50.0;
double scale = 0.6;
/// 通过上面的旋转细节信息求得旋转矩阵
rot_mat = getRotationMatrix2D( center, angle, scale );
/// 旋转已扭曲图像
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
/// 显示结果
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
imshow( source_window, src );
namedWindow( warp_window, CV_WINDOW_AUTOSIZE );
imshow( warp_window, warp_dst );
namedWindow( warp_rotate_window, CV_WINDOW_AUTOSIZE );
imshow( warp_rotate_window, warp_rotate_dst );
/// 等待用户按任意按键退出程序
waitKey(0);
return 0;
}


4、通过pca获得图像的弧度

double getOrientation(vector<Point> &pts, Point2f& pos,Mat& img)
{
    //Construct a buffer used by the pca analysis
    Mat data_pts = Mat(pts.size(), 2, CV_64FC1);
    for (int i = 0; i < data_pts.rows; ++i)
    {
        data_pts.at<double>(i, 0) = pts[i].x;
        data_pts.at<double>(i, 1) = pts[i].y;
    }
    //Perform PCA analysis
    PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW);
    //Store the position of the object
    pos = Point2f(pca_analysis.mean.at<double>(0, 0),
        pca_analysis.mean.at<double>(0, 1));
    //Store the eigenvalues and eigenvectors
    vector<Point2d> eigen_vecs(2);
    vector<double> eigen_val(2);
    for (int i = 0; i < 2; ++i)
    {
        eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0),
            pca_analysis.eigenvectors.at<double>(i, 1));
        eigen_val[i] = pca_analysis.eigenvalues.at<double>(i,0);
    }
    return atan2(eigen_vecs[0].y, eigen_vecs[0].x);
}


这个函数的目的,在于通过PCA方法,获得当前轮廓的主要方向。

5、直接获得某点旋转以后位置,这个地方使用的是弧度

//获得单个点经过旋转后所在精确坐标
Point2f GetPointAfterRotate(Point2f inputpoint,Point2f center,double angle){
    Point2d preturn;
    preturn.x = (inputpoint.x - center.x)*cos(-angle) - (inputpoint.y - center.y)*sin(-angle)+center.x;
    preturn.y = (inputpoint.x - center.x)*sin(-angle) + (inputpoint.y - center.y)*cos(-angle)+center.y;
    return preturn;
}

此外值得注意的一点就是,其实我们可以不基于OpenCV函数,直接计算某点旋转以后位置,采用的是数学方法。


四、使用例子
综合使用,请查看:
感谢阅读至此,希望有所帮助。




(4opencv)对OpenCV中“旋转”的思考和实验的更多相关文章

  1. 解析opencv中Box Filter的实现并提出进一步加速的方案(源码共享)。

    说明:本文所有算法的涉及到的优化均指在PC上进行的,对于其他构架是否合适未知,请自行试验. Box Filter,最经典的一种领域操作,在无数的场合中都有着广泛的应用,作为一个很基础的函数,其性能的好 ...

  2. 2d,3d中旋转推导

    二维绕原点旋转,其实点为(x,y),旋转角度为黄色标注的角度. 推导过程如下: x' = r cos(al+be); y' = r sin(al+be);x '= rcosalcosbe-rsinal ...

  3. Opencv中直线的表示方法

                               [blog算法原理]Opencv中直线的表示方法  一.问题的提出:​          在实际项目编写过程中,需要对直线(Line)进行特定的处 ...

  4. OpenCV中的绘图函数-OpenCV步步精深

    OpenCV 中的绘图函数 画线 首先要为画的线创造出环境,就要生成一个空的黑底图像 img=np.zeros((512,512,3), np.uint8) 这是黑色的底,我们的画布,我把窗口名叫做i ...

  5. [OpenCV-Python] OpenCV 中摄像机标定和 3D 重构 部分 VII

    部分 VII摄像机标定和 3D 重构 OpenCV-Python 中文教程(搬运)目录 42 摄像机标定 目标 • 学习摄像机畸变以及摄像机的内部参数和外部参数 • 学习找到这些参数,对畸变图像进行修 ...

  6. OpenCV中cv2的用法

    一.读入图像 使用函数cv2.imread(filepath,flags)读入一副图片 filepath:要读入图片的完整路径 flags:读入图片的标志  cv2.IMREAD_COLOR:默认参数 ...

  7. [OpenCV-Python] OpenCV 中的 Gui特性 部分 II

    部分 IIOpenCV 中的 Gui 特性 OpenCV-Python 中文教程(搬运)目录 4 图片 目标 • 在这里你将学会怎样读入一幅图像,怎样显示一幅图像,以及如何保存一幅图像 • 你将要学习 ...

  8. [OpenCV-Python] OpenCV 中的图像处理 部分 IV (一)

    部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录   13 颜色空间转换 目标 • 你将学习如何对图像进行颜色空间转换,比如从 BGR 到灰度图,或者从BGR 到 ...

  9. [OpenCV-Python] OpenCV 中的图像处理 部分 IV (四)

    部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录 21 OpenCV 中的轮廓 21.1 初识轮廓目标 • 理解什么是轮廓 • 学习找轮廓,绘制轮廓等 • 函数: ...

随机推荐

  1. EntityFrameworkCore概览

    .NET Core 中 EntityFrameworkCore的支持库主要有: Script-migration 级联删除 protected override void OnConfiguring( ...

  2. Objective-C中NSArray的基本用法示例

    NSArray的一些用法 NSArray只允许装OC对象,并且不能装空值,空代表数组元素的结束 #pragma mark - NSArray的基本用法 // 创建一个空数组 NSArray *arra ...

  3. Tortoisegit和GitHub使用入门

    作为一个Code人对于github并不陌生吧,记录下使用说明: gitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名gitHub. 工具: git: ...

  4. 40.SEO----前端该懂的seo技巧

    SEO要点:1.语义化html标签,用合适的标签嵌套合适的内容,不可过分依赖div,对浏览器更友善就能更容易被抓取.2.重要的内容html代码放在前面,放在左边.搜索引擎爬虫是从左往右,从上到下进行抓 ...

  5. c#winform,知道图像路径,怎么在程序运行时往image里面添加图片

    貌似可以直接添加啊 ,要改变显示的图片,就是将图片的路径赋值给picturebox即可pictureBox1.ImageLocation="图片路径"动态的改变这个值就行了.

  6. hbase-java-api002(flush)

    package api; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apa ...

  7. 读取文件任意位置的内容——RandomAccessFile

    http://www.cnblogs.com/Sunw/p/3801145.html http://www.cnblogs.com/dukc/p/4776868.html http://www.cnb ...

  8. Gardener Bo (树剖 + 问题分解)

    一开始没看懂计算答案的第四部和update2,很迷.然后一直推敲之后才发下我计算的时候漏掉一个关键点.没有把加值的影响放到父节点上. #include<bits/stdc++.h> #de ...

  9. canvas添加水印

    <canvas id="canvas"></canvas><canvas id="water"></canvas> ...

  10. 【Hive学习之六】Hive Lateral View &视图&索引

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 hadoop-3.1.1 apache-hive-3.1.1 ...