zoukankan      html  css  js  c++  java
  • [opencv] 图像几何变换:旋转,缩放,斜切

    几何变换

    几何变换可以看成图像中物体(或像素)空间位置改变,或者说是像素的移动。

    几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标。这时就需要灰度级差值将映射的新坐标匹配到输出像素之间。最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿。这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值。

    插值算法感觉只要了解就可以了,图像处理中比较需要理解的还是空间变换。

    空间变换

    空间变换对应矩阵的仿射变换。一个坐标通过函数变换的新的坐标位置:

    所以在程序中我们可以使用一个2*3的数组结构来存储变换矩阵:

    以最简单的平移变换为例,平移(b1,b2)坐标可以表示为:

    因此,平移变换的变换矩阵及逆矩阵记为:

    缩放变换:将图像横坐标放大(或缩小)sx倍,纵坐标放大(或缩小)sy倍,变换矩阵及逆矩阵为:

    选择变换:图像绕原点逆时针旋转a角,其变换矩阵及逆矩阵(顺时针选择)为:

    OpenCV中的图像变换函数

    基本的放射变换函数:

    void cvWarpAffine(   
        const CvArr* src,//输入图像  
        CvArr* dst, //输出图像  
        const CvMat* map_matrix,   //2*3的变换矩阵  
        int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,   //插值方法的组合  
        CvScalar fillval=cvScalarAll(0)   //用来填充边界外的值  
    );  
    View Code

    另外一个比较类似的函数是cvGetQuadrangleSubPix:

    void cvGetQuadrangleSubPix(   
           const CvArr* src,  //输入图像   
           CvArr* dst,   // 提取的四边形  
           const CvMat* map_matrix //2*3的变换矩阵  
    );  
    View Code

    这个函数用以提取输入图像中的四边形,并通过map_matrix变换存储到dst中,与WarpAffine变换意义相同,

    即对应每个点的变换:

    WarpAffine与 GetQuadrangleSubPix 不同的在于cvWarpAffine 要求输入和输出图像具有同样的数据类型,有更大的资源开销(因此对小图像不太合适)而且输出图像的部分可以保留不变。而 cvGetQuadrangleSubPix 可以精确地从8位图像中提取四边形到浮点数缓存区中,具有比较小的系统开销,而且总是全部改变输出图像的内容。

    实践:图像旋转变换(原尺寸)

    首先用cvWarpAffine实验将图像逆时针旋转degree角度。
    //逆时针旋转图像degree角度(原尺寸)  
    void rotateImage(IplImage* img, int degree)  
    {  
        IplImage *img_rotate = cvCloneImage(img);
        cvZero(img_rotate);
        //旋转中心为图像中心  
        CvPoint2D32f center;    
        center.x=float (img->width/2.0+0.5);  
        center.y=float (img->height/2.0+0.5);  
        //计算二维旋转的仿射变换矩阵  
        float m[6];              
        CvMat M = cvMat( 2, 3, CV_32F, m );  
        cv2DRotationMatrix( center, degree,1, &M);  
        //变换图像,并用黑色填充其余值  
        cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
    
        cvNamedWindow("原图");
        cvNamedWindow("旋转后的图像");
        cvShowImage("原图",img);
        cvShowImage("旋转后的图像",img_rotate);
    }  
    View Code

    逆时针旋转30度结果:

    这里我们将新的图像还保留原来的图像尺寸。这样的效果显然不太好,我们通过计算相应放大图像尺寸。

    实践:图像旋转变换(保留原图内容,放大尺寸)

    需要计算新图的尺寸,示意图如下:
    所以新图size为(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))
    //旋转图像内容不变,尺寸相应变大  
    IplImage* rotateImage1(IplImage* img,int degree){  //逆时针旋转
        double angle = degree  * CV_PI / 180.; // 弧度    
        double a = sin(angle), b = cos(angle);   
        int width = img->width;    
        int height = img->height;    
        int width_rotate= int(height * fabs(a) + width * fabs(b));    
        int height_rotate=int(width * fabs(a) + height * fabs(b));    
        //旋转数组map  
        // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
        // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
        float map[6];  
        CvMat map_matrix = cvMat(2, 3, CV_32F, map);    
        // 旋转中心  
        CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); 
        //函数返回一个指向2X3矩阵的指针
        //PointF center:源图像的旋转中心
        //double angle:源图像旋转的角度,正值表示逆时针旋转(坐标原点假设在图像左上角)
        //double scale:等向比例因子
        //IntPer mapMatrix:用于返回的2X3矩阵
        cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
        map[2] += (width_rotate - width) / 2;    
        map[5] += (height_rotate - height) / 2;    
        IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);   
        //对图像做仿射变换  
        //输入图像
        //输出图像
        //2*3的变换矩阵 
        //插值方法的组合  CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。  
            //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.  
            //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换, 
        //用来填充边界外的值
        cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));    
    
        cvNamedWindow("原图");
        cvNamedWindow("旋转后的图像");
        cvShowImage("原图",img);
        cvShowImage("旋转后的图像",img_rotate);
    
        return img_rotate;  
    }  
    View Code

    实践:图像旋转变换(保留原图内容,放大尺寸)-2

    试一下用cvGetQuadrangleSubPix函数:
    //旋转图像内容不变,尺寸相应变大  
    IplImage* rotateImage2(IplImage* img, int degree)     //顺时针旋转
    {    
        double angle = degree  * CV_PI / 180.;   
        double a = sin(angle), b = cos(angle);   
        int width=img->width, height=img->height;  
        //旋转后的新图尺寸  
        int width_rotate= int(height * fabs(a) + width * fabs(b));    
        int height_rotate=int(width * fabs(a) + height * fabs(b));    
        IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);    
        cvZero(img_rotate);    
        //保证原图可以任意角度旋转的最小尺寸  
        int tempLength = sqrt((double)width * width + (double)height *height) + 10;    //sqrt()开平方根
        int tempX = (tempLength + 1) / 2 - width / 2;    
        int tempY = (tempLength + 1) / 2 - height / 2;    
        IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);    
        cvZero(temp);    
        //将原图复制到临时图像tmp中心  
        cvSetImageROI(temp, cvRect(tempX, tempY, width, height));    
        cvCopy(img, temp, NULL);    
        cvResetImageROI(temp);   
    
        cvNamedWindow("临时图像");
        cvShowImage("临时图像",temp);
    
        //旋转数组map  
        // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
        // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
        float m[6];    
        int w = temp->width;    
        int h = temp->height;    
        m[0] = b;    
        m[1] = a;    
        m[3] = -m[1];    
        m[4] = m[0];    
        // 将旋转中心移至图像中间    
        m[2] = w * 0.5f;    
        m[5] = h * 0.5f;    
        CvMat M = cvMat(2, 3, CV_32F, m);    
        cvGetQuadrangleSubPix(temp, img_rotate, &M);    
        cvReleaseImage(&temp);    
    
        cvNamedWindow("原图");
        cvNamedWindow("旋转后的图像");
        cvShowImage("原图",img);
        cvShowImage("旋转后的图像",img_rotate);
    
        return img_rotate;  
    }    
    View Code
     

    实践:图像放射变换(通过三点确定变换矩阵)

    在OpenCV 2.3的参考手册中《opencv_tutorials》介绍了另一种确定变换矩阵的方法,通过三个点变换的几何关系映射实现变换。
    变换示意图如下:
    即通过三个点就可以确定一个变换矩阵。(矩形变换后一定为平行四边形)
    以下是基于OpenCV 2.3的代码(需至少2.0以上版本的支持)
    //以下代码理论可参考:http://blog.csdn.net/fengbingchun/article/details/17713429
    Mat rotateImage3(Mat src){ //仿射变换
        Point2f srcTri[3];  
        Point2f dstTri[3];  
        Mat rot_mat( 2, 3, CV_32FC1 );  
        Mat warp_mat( 2, 3, CV_32FC1 );  
        Mat warp_dst, warp_rotate_dst;  
        warp_dst = Mat::zeros( src.rows, src.cols, src.type() );  
        // 用3个点确定A仿射变换  
        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() );  
        /// 旋转矩阵  
        Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );  
        double angle = -50.0;  
        double scale = 0.6;  
        ////获取旋转矩阵 scale为缩放因子(x、y方向保持一致),angle为旋转角度(弧长),centerx,centery为旋转中心
        //     | a  b  (1-a)*center.x-b*center.y |    a=scale*cos(angle)
        //  M= |                                 |
        //       | -b a   b*center.x+(1-a)*center.y|    b=scale*sin(angle)
        rot_mat = getRotationMatrix2D( center, angle, scale ); 
        
        warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );   //实现坐标系仿射变换
        ////OpenCV 1.0的形式  
        //IplImage * img=cvLoadImage("baboon.jpg");  
        //IplImage *img_rotate=cvCloneImage(img);  
        //CvMat M =warp_mat;  
        //cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
        //cvShowImage("Wrap2",img_rotate);  
    
        namedWindow( "Source", CV_WINDOW_AUTOSIZE );  
        imshow( "Source", src );  
        namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );   
        imshow( "Wrap", warp_dst );  
        namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );  
        imshow( "Wrap+Rotate", warp_rotate_dst );  
        return warp_dst;
    
    }
    View Code
    变换结果:

    主函数:

    // ImageRotation2.cpp : 定义控制台应用程序的入口点。
    // 本文代码参考:http://www.cnblogs.com/slysky/archive/2012/03/21/2410743.html
    
    #include "stdafx.h"
    #include <opencv2/core/core.hpp>   //cvGetSize  cvCreateImage
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/opencv.hpp>  //cvResize cvInitMatHeader cvGetMinMaxHistValue cvCvtColor
    #include <opencv2/imgproc/imgproc.hpp>
    
    #ifdef _DEBUG
    #pragma comment(lib, "opencv_core244d")
    #pragma comment(lib, "opencv_highgui244d")
    #pragma comment(lib, "opencv_imgproc244d")  //cvResize
    #else
    #pragma comment(lib, "opencv_core244")
    #pragma comment(lib, "opencv_highgui244")
    #pragma comment(lib, "opencv_imgproc244")  //cvResize
    #endif
    
    
    using namespace std;  
    //隐藏控制台窗口  
    #pragma comment(linker, "/subsystem:"windows" /entry:"mainCRTStartup"")  
    
    int main( )  
    {   
        //读入图像  
        //Mat src = imread( "./images/baboon.jpg", 1 );  
        //src = rotateImage3(src);
      
        int degree = 30;
        IplImage *src = cvLoadImage("./images/meng3.jpg",CV_LOAD_IMAGE_UNCHANGED);
        rotateImage(src, degree);
    
        //src = rotateImage1(src, degree);
    
        //src = rotateImage2(src, degree);
       
        waitKey(0);  
        return 0;  
    }  
    View Code

    本文转自:http://blog.csdn.net/xiaowei_cqu/article/details/7616044

  • 相关阅读:
    webpack学习总结
    jquery弹出下拉列表插件(实现kindeditor的@功能)
    html meta标签使用总结
    Techparty-广州Javascript技术专场(学习分享)
    一个三年工作经验的软件工程师的经验之谈
    cf--------(div1)1A. Theatre Square
    离线网页制作器(beta1.0)
    uva---(11549)CALCULATOR CONUNDRUM
    CF---(452)A. Eevee
    Uva----------(11078)Open Credit System
  • 原文地址:https://www.cnblogs.com/tianyalu/p/5698371.html
Copyright © 2011-2022 走看看