learning OpenCV(6)仿射和透视变换

重映射(校正标定和立体的图像)

  • 把一个图像中一个位置的像素放置到另一个图片指定位置的过程.

  • 为了完成映射过程, 有必要获得一些插值为非整数像素坐标,因为源图像与目标图像的像素坐标不是一一对应的.

  • 我们通过重映射来表达每个像素的位置 (x,y) :

    g(x,y) = f ( h(x,y) )

    这里 g() 是目标图像, f() 是源图像, h(x,y) 是作用于 (x,y) 的映射方法函数.

cvRemap(CvArr* src, CvArr* dst, CvArr * mapx, CvArr* mapy, int flags = CV_INTER_LINEAR|CV_WARP_FILL_OUTLIERS, CvScalar fillval = cvScalarAll(0));

mapx,mapy大小和源图像相同,但是通道数为1, 深度IPL_DEPTH_32F


 #include<highgui.h>
 #include <cv.h>
 #include <iostream>
 #include <stdio.h>

 using namespace std;

 /// Global variables
IplImage* src;
IplImage* dst;
IplImage* map_x;
IplImage* map_y;
 char* remap_window = "Remap demo";
 int ind = 1;

 /// Function Headers
 void update_map( void );

 /**
 * @function main
 */
 int main( int argc, char** argv )
 {
   /// Load the image
   src = cvLoadImage( argv[1], 1 );

  /// Create dst, map_x and map_y with the same size as src:
  dst=cvCloneImage(src);
 map_x = cvCreateImage(cvGetSize(src),IPL_DEPTH_32F, 1);
 map_y = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1);

  /// Create window
  cvNamedWindow( remap_window, CV_WINDOW_AUTOSIZE );

  /// Loop
  while( true )
  {
    /// Each 1 sec. Press ESC to exit the program
    int c = cvWaitKey( 1000 );

    if( (char)c == 27 )
      { break; }

    /// Update map_x & map_y. Then apply remap
    update_map();
    cvRemap( src, dst, map_x, map_y, CV_INTER_LINEAR, cvScalarAll(0) );

    /// Display results
    cvShowImage( remap_window, dst );
  }
  return 0;
 }

 /**
 * @function update_map
 * @brief Fill the map_x and map_y matrices with 4 types of mappings
 */
 void update_map( void )
 {
   ind = ind%4;

   for( int j = 0; j < src->height; j++ )
   { for( int i = 0; i < src->width; i++ )
       {
         switch( ind )
         {
           /*case 0:
             if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
               {
                 map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
                 map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
                }
             else
               { map_x.at<float>(j,i) = 0 ;
                 map_y.at<float>(j,i) = 0 ;
               }
                 break;*/
           case 1:
                *((float*)cvPtr2D(map_x, j, i)) =  i;
				*((float*)cvPtr2D(map_y, j, i)) =  src->height - j;
                 //*((float*)cvPtr2D(map_x, j, i)) = src->width - j ;
                 break;
         /*  case 2:
                 map_x.at<float>(j,i) = src.cols - i ;
                 map_y.at<float>(j,i) = j ;
                 break;
           case 3:
                 map_x.at<float>(j,i) = src.cols - i ;
                 map_y.at<float>(j,i) = src.rows - j ;
                 break;*/
         } // end of switch
       }
    }
//  ind++;
}
仿射变换的原理

  1. 一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移).

  2. 综上所述, 我们能够用仿射变换来表示:

    1. 旋转 (线性变换)
    2. 平移 (向量加)
    3. 缩放操作 (线性变换)

    你现在可以知道, 事实上, 仿射变换代表的是两幅图之间的 关系 .

  3. 我们通常使用 2 \times 3 矩阵来表示仿射变换.

    A = \begin{bmatrix}     a_{00} & a_{01} \\     a_{10} & a_{11}     \end{bmatrix}_{2 \times 2} B = \begin{bmatrix}     b_{00} \\     b_{10}     \end{bmatrix}_{2 \times 1} M = \begin{bmatrix}     A & B     \end{bmatrix} =\begin{bmatrix}     a_{00} & a_{01} & b_{00} \\     a_{10} & a_{11} & b_{10}\end{bmatrix}_{2 \times 3}

    考虑到我们要使用矩阵 A 和 B 对二维向量 X = \begin{bmatrix}x \\ y\end{bmatrix} 做变换, 所以也能表示为下列形式:

    T = A \cdot \begin{bmatrix}x \\ y\end{bmatrix} + B or T = M \cdot  [x, y, 1]^{T}

    T =  \begin{bmatrix}    a_{00}x + a_{01}y + b_{00} \\    a_{10}x + a_{11}y + b_{10}    \end{bmatrix}


cvWrapAffine(CvArr * src, CvArr* dst, CvMat* map_matrix, int flags =  CV_INTER_LINER| CV_WARP_OUTLINERS, CvScalar fillval = cvScalarAll(0));

 #include<highgui.h>
 #include <cv.h>
 #include <iostream>
 #include <stdio.h>

 using namespace std;

 /// Global variables
IplImage* src;
IplImage* dst;
IplImage* map_x;
IplImage* map_y;
 char* remap_window = "Remap demo";
 int ind = 1;

 /// Function Headers
 void update_map( void );

 /**
 * @function main
 */
 int main( int argc, char** argv )
 {
   /// Load the image
   src = cvLoadImage( argv[1], 1 );

  /// Create dst, map_x and map_y with the same size as src:
  dst=cvCloneImage(src);
 map_x = cvCreateImage(cvGetSize(src),IPL_DEPTH_32F, 1);
 map_y = cvCreateImage(cvGetSize(src), IPL_DEPTH_32F, 1);

  /// Create window
  cvNamedWindow( remap_window, CV_WINDOW_AUTOSIZE );

  /// Loop
  while( true )
  {
    /// Each 1 sec. Press ESC to exit the program
    int c = cvWaitKey( 1000 );

    if( (char)c == 27 )
      { break; }

    /// Update map_x & map_y. Then apply remap
    update_map();
    cvRemap( src, dst, map_x, map_y, CV_INTER_LINEAR, cvScalarAll(0) );
	

    /// Display results
    cvShowImage( remap_window, dst );
  }
  return 0;
 }

 /**
 * @function update_map
 * @brief Fill the map_x and map_y matrices with 4 types of mappings
 */
 void update_map( void )
 {
   ind = ind%4;

   for( int j = 0; j < src->height; j++ )
   { for( int i = 0; i < src->width; i++ )
       {
         switch( ind )
         {
           /*case 0:
             if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
               {
                 map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
                 map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
                }
             else
               { map_x.at<float>(j,i) = 0 ;
                 map_y.at<float>(j,i) = 0 ;
               }
                 break;*/
           case 1:
                *((float*)cvPtr2D(map_x, j, i)) =  i;
				*((float*)cvPtr2D(map_y, j, i)) =  src->height - j;
                 //*((float*)cvPtr2D(map_x, j, i)) = src->width - j ;
                 break;
         /*  case 2:
                 map_x.at<float>(j,i) = src.cols - i ;
                 map_y.at<float>(j,i) = j ;
                 break;
           case 3:
                 map_x.at<float>(j,i) = src.cols - i ;
                 map_y.at<float>(j,i) = src.rows - j ;
                 break;*/
         } // end of switch
       }
    }
//  ind++;
}


cvGetQuadrangleSubPix

使用cvWarpAffine涉及开销的问题,另一种方法是利用cvGetQuadrangleSubPix(CvArr* src, CvArr* dst, CvMat* map_matrix);

#include "cv.h"
#include "highgui.h"
#include "math.h"

int main( int argc, char** argv )
{
    IplImage* src;
    /* the first command line parameter must be image file name */
    if( argc==2 && (src = cvLoadImage(argv[1], -1))!=0)
    {
        IplImage* dst = cvCloneImage( src );
        int delta = 1;
        int angle = 0;

        cvNamedWindow( "src", 0 );
        cvShowImage( "src", src );

        for(;;)
        {
            float m[6];
            double factor = (cos(angle*CV_PI/180.) + 1.1)*3;
            CvMat M = cvMat( 2, 3, CV_32F, m );
            int w = src->width;
            int h = src->height;

            m[0] = (float)(factor*cos(-angle*2*CV_PI/180.));
            m[1] = (float)(factor*sin(-angle*2*CV_PI/180.));
            m[2] = h*0.5f;
            m[3] = -m[1];
            m[4] = m[0];
            m[5] = w*0.5f;

            cvGetQuadrangleSubPix( src, dst, &M);

            cvNamedWindow( "dst", 0 );
            cvShowImage( "dst", dst );

            if( cvWaitKey(5) == 27 )
                break;

            angle = (angle + delta) % 360;
        }
    }
    return 0;
}<img src="https://img-blog.csdn.net/20141118145039609" alt="" />

一个问题:如何确定map_matrix??
方法一:(适用于变形)

由三个不共线点计算仿射变换

CvMat* cvGetAffineTransform( const CvPoint2D32f* src, const CvPoint2D32f* dst, CvMat* map_matrix );
src
输入图像的三角形顶点坐标。(分别为左上点,右上点,左下点
dst
输出图像的相应的三角形顶点坐标。
map_matrix
指向2×3输出矩阵的指针。
方法二:适用于指定中心点的旋转,缩放

2DRotationMatrix

计算二维旋转的仿射变换矩阵

CvMat* cv2DRotationMatrix( CvPoint2D32f center, double angle,
                           double scale, CvMat* map_matrix );
center
输入图像的旋转中心坐标
angle
旋转角度(度)。正值表示逆时针旋转(坐标原点假设在左上角).
scale
各项同性的尺度因子
map_matrix
输出 2×3 矩阵的指针

函数 cv2DRotationMatrix 计算矩阵:

[  α  β  |  (1-α)*center.x - β*center.y ]
[ -β  α  |  β*center.x + (1-α)*center.y ]这里的平移变量不是很理解???

where α=scale*cos(angle), β=scale*sin(angle)

该变换并不改变原始旋转中心点的坐标,如果这不是操作目的,则可以通过调整平移量改变其坐标(译者注:通过简单的推导可知,仿射变换的实现是首先将旋转中心置为坐标原点,再进行旋转和尺度变换,最后重新将坐标原点设定为输入图像的左上角,这里的平移量是center.x, center.y).

//
//Example 6-2. An affine transformation
// Usage: warp_affine <image>
//
/* *************** License:**************************
   Oct. 3, 2008
   Right to use this code in any way you want without warrenty, support or any guarentee of it working.

   BOOK: It would be nice if you cited it:
   Learning OpenCV: Computer Vision with the OpenCV Library
     by Gary Bradski and Adrian Kaehler
     Published by O'Reilly Media, October 3, 2008
 
   AVAILABLE AT: 
     http://www.amazon.com/Learning-OpenCV-Computer-Vision-Library/dp/0596516134
     Or: http://oreilly.com/catalog/9780596516130/
     ISBN-10: 0596516134 or: ISBN-13: 978-0596516130    

   OTHER OPENCV SITES:
   * The source code is on sourceforge at:
     http://sourceforge.net/projects/opencvlibrary/
   * The OpenCV wiki page (As of Oct 1, 2008 this is down for changing over servers, but should come back):
     http://opencvlibrary.sourceforge.net/
   * An active user group is at:
     http://tech.groups.yahoo.com/group/OpenCV/
   * The minutes of weekly OpenCV development meetings are at:
     http://pr.willowgarage.com/wiki/OpenCV
   ************************************************** */

// warp_affine <image>
#include <cv.h>
#include <highgui.h>
#define affine 
#ifdef affine
int main(int argc, char** argv)
{
   CvPoint2D32f srcTri[3], dstTri[3];
   CvMat* rot_mat = cvCreateMat(2,3,CV_32FC1);
   CvMat* warp_mat = cvCreateMat(2,3,CV_32FC1);
   IplImage *src, *dst;
    if( argc == 2 && ((src=cvLoadImage(argv[1],1)) != 0 ))
    {
   dst = cvCloneImage(src);
   dst->origin = src->origin;
   cvZero(dst);

   //COMPUTE WARP MATRIX
   srcTri[0].x = 0;          //src Top left
   srcTri[0].y = 0;
   srcTri[1].x = src->width - 1;    //src Top right
   srcTri[1].y = 0;
   srcTri[2].x8 = 0;          //src Bottom left
   srcTri[2].y = src->height - 1;
   //- - - - - - - - - - - - - - -//
   dstTri[0].x = src->width*0.0;    //dst Top left
   dstTri[0].y = src->height*0.33;
   dstTri[1].x = src->width*0.85; //dst Top right
   dstTri[1].y = src->height*0.25;
   dstTri[2].x = src->width*0.15; //dst Bottom left
   dstTri[2].y = src->height*0.7;
   cvGetAffineTransform(srcTri,dstTri,warp_mat);
   cvWarpAffine(src,dst,warp_mat);
   cvCopy(dst,src);

   //COMPUTE ROTATION MATRIX
   CvPoint2D32f center = cvPoint2D32f(src->width/2,
                                         src->height/2);
   double angle = -50.0;
   double scale = 0.6;
   cv2DRotationMatrix(center,angle,scale,rot_mat);
   cvWarpAffine(src,dst,rot_mat);

   //DO THE TRANSFORM:
   cvNamedWindow( "Affine_Transform", 1 );
      cvShowImage( "Affine_Transform", dst );
      cvWaitKey();
    }
   cvReleaseImage(&dst);
   cvReleaseMat(&rot_mat);
   cvReleaseMat(&warp_mat);
    return 0;
}
#endif


顺时针的角度为负,逆时针角度为正~~~~

WarpPerspective

对图像进行透视变换

void cvWarpPerspective( const CvArr* src, CvArr* dst, const CvMat* map_matrix,
                        int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,
                        CvScalar fillval=cvScalarAll(0) );
src
输入图像.
dst
输出图像.
map_matrix
3×3 变换矩阵
flags
插值方法和以下开关选项的组合:
  • CV_WARP_FILL_OUTLIERS - 填充所有缩小图像的象素。如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
  • CV_WARP_INVERSE_MAP - 指定 matrix 是输出图像到输入图像的反变换,因此可以直接用来做象素插值。否则, 函数从 map_matrix 得到反变换。
fillval
用来填充边界外面的值

函数 cvWarpPerspective 利用下面指定矩阵变换输入图像: dst(x',y') \leftarrow src(x,y)

  • 如果没有指定 CV_WARP_INVERSE_MAP , (x',y')^T=map\_matrix \cdot (x,y,1)^T ,
  • 否则,(x, y)^T=map\_matrix \cdot (x',y',1)^T

要变换稀疏矩阵,使用 cxcore 中的函数 cvTransform 。

GetPerspectiveTransform

由四边形的4个点计算透射变换

CvMat* cvGetPerspectiveTransform( const CvPoint2D32f* src, const CvPoint2D32f* dst,
                                  CvMat* map_matrix );

#define cvWarpPerspectiveQMatrix cvGetPerspectiveTransform
src
输入图像的四边形顶点坐标。
dst
输出图像的相应的四边形顶点坐标。
map_matrix
指向3×3输出矩阵的指针。

函数cvGetPerspectiveTransform计算满足以下关系的透射变换矩阵:

(t_ix'_i,t_iy'_i,t_i)^T=map\_matrix\cdot(x_i,y_i,1)^T

这里, dst(i) = (x'i,y'i),src(i) = (xi,yi),i = 0..3.
#include <cv.h>
#include <highgui.h>

int main(int argc, char** argv)
{
   CvPoint2D32f srcQuad[4], dstQuad[4];
   CvMat* warp_matrix = cvCreateMat(3,3,CV_32FC1);
   IplImage *src, *dst;
    if( argc == 2 && ((src=cvLoadImage(argv[1],1)) != 0 ))
    {
   dst = cvCloneImage(src);
   dst->origin = src->origin;
   cvZero(dst);

   srcQuad[0].x = 0;           //src Top left
   srcQuad[0].y = 0;
   srcQuad[1].x = src->width - 1;  //src Top right
   srcQuad[1].y = 0;
   srcQuad[2].x = 0;           //src Bottom left
   srcQuad[2].y = src->height - 1;
   srcQuad[3].x = src->width - 1;  //src Bot right
   srcQuad[3].y = src->height - 1;
      //- - - - - - - - - - - - - -//
   dstQuad[0].x = src->width*0.05;  //dst Top left
   dstQuad[0].y = src->height*0.33;
   dstQuad[1].x = src->width*0.9;  //dst Top right
   dstQuad[1].y = src->height*0.25;
   dstQuad[2].x = src->width*0.2;  //dst Bottom left
   dstQuad[2].y = src->height*0.7;      
   dstQuad[3].x = src->width*0.8;  //dst Bot right
   dstQuad[3].y = src->height*0.9;

   cvGetPerspectiveTransform(srcQuad,dstQuad,
                                     warp_matrix);
   cvWarpPerspective(src,dst,warp_matrix);
   cvNamedWindow( "Perspective_Warp", 1 );
      cvShowImage( "Perspective_Warp", dst );
      cvWaitKey();
    }
   cvReleaseImage(&dst);
   cvReleaseMat(&warp_matrix);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值