重映射(校正标定和立体的图像)
-
把一个图像中一个位置的像素放置到另一个图片指定位置的过程.
-
为了完成映射过程, 有必要获得一些插值为非整数像素坐标,因为源图像与目标图像的像素坐标不是一一对应的.
-
我们通过重映射来表达每个像素的位置 :
这里 是目标图像, 是源图像, 是作用于 的映射方法函数.
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++;
}
仿射变换的原理
-
一个任意的仿射变换都能表示为 乘以一个矩阵 (线性变换) 接着再 加上一个向量 (平移).
-
综上所述, 我们能够用仿射变换来表示:
- 旋转 (线性变换)
- 平移 (向量加)
- 缩放操作 (线性变换)
你现在可以知道, 事实上, 仿射变换代表的是两幅图之间的 关系 .
-
我们通常使用 矩阵来表示仿射变换.
考虑到我们要使用矩阵 和 对二维向量 做变换, 所以也能表示为下列形式:
or
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 利用下面指定矩阵变换输入图像:
- 如果没有指定 CV_WARP_INVERSE_MAP , ,
- 否则,
要变换稀疏矩阵,使用 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计算满足以下关系的透射变换矩阵:
- 这里, 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;
}