使用摄像头,采集一副图像,然后对图像畸变校正,摄像头事先标定好。
#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
#include "cvcam.h"
//图像的像素直接提取
#define _I(img,x,y) ((unsigned char*)((img)->imageData + (img)->widthStep*(y)))[(x)]
//亚像素级灰度值
#define _IF(image,x,y) ( ((int)(x+1)-(x))*((int)(y+1)-(y))*_I((image),(int)(x),(int)(y)) + ((int)(x+1)-(x))*((y)-(int)(y))*_I((image),(int)(x),(int)(y+1)) + ((x)-(int)(x))*((int)(y+1)-(y))*_I((image),(int)(x+1),(int)(y)) + ((x)-(int)(x))*((y)-(int)(y))*_I((image),(int)(x+1),(int)(y+1)) )//插值后的像素值(IN表示interpolation),x、y可以为小数
void callback(IplImage* image);
void main()
{
int ncams = cvcamGetCamerasCount( );//返回可以访问的摄像头数目
HWND mywin;
cvcamSetProperty(0, CVCAM_PROP_ENABLE, CVCAMTRUE);
cvcamSetProperty(0, CVCAM_PROP_RENDER, CVCAMTRUE);
mywin = (HWND)cvGetWindowHandle("cvcam window");
cvcamSetProperty(0, CVCAM_PROP_WINDOW, &mywin);
cvcamSetProperty(0, CVCAM_PROP_CALLBACK, callback);
//cvcamGetProperty(0, CVCAM_VIDEOFORMAT,NULL);
cvNamedWindow( "径向矫正1", 1 );//创建窗口
cvNamedWindow( "径向矫正2", 1 );//创建窗口
cvcamInit( );
cvcamStart( );
cvWaitKey(0);
cvcamStop( );
cvcamExit( );
cvDestroyWindow( "径向矫正1" );//销毁窗口
cvDestroyWindow( "径向矫正2" );//销毁窗口
}
void callback(IplImage* image)
{
IplImage* Show1 = cvCreateImage( cvSize(320,240), IPL_DEPTH_8U, 1);
IplImage* Show2 = cvCreateImage( cvSize(420,340), IPL_DEPTH_8U, 1);
IplImage* ImageC1 = cvCreateImage( cvSize(320,240), IPL_DEPTH_8U, 1);
//转换为灰度图
cvCvtColor( image, ImageC1, CV_RGB2GRAY);
cvFlip( ImageC1, NULL, 0);
double *mi;
double *md;
mi = new double[3*3];
md = new double[4];
CvMat intrinsic_matrix,distortion_coeffs;
//摄像机内参数
cvInitMatHeader(&intrinsic_matrix,3,3,CV_64FC1,mi);
//镜头畸变参数
cvInitMatHeader(&distortion_coeffs,1,4,CV_64FC1,md);
/**//
320*240 120度广角镜头
//参数由matlab获得
double fc1,fc2,cc1,cc2,kc1,kc2,kc3,kc4;
fc1 = 667.23923/2.5;
fc2 = 669.78156/2.5;
cc1 = 429.96933/2.5;
cc2 = 351.48350/2.5;
kc1 = -0.40100;
kc2 = 0.19463;
kc3 = 0.00508;
kc4 = -0.00051;
cvmSet(&intrinsic_matrix, 0, 0, fc1);
cvmSet(&intrinsic_matrix, 0, 1, 0);
cvmSet(&intrinsic_matrix, 0, 2, cc1);
cvmSet(&intrinsic_matrix, 1, 0, 0);
cvmSet(&intrinsic_matrix, 1, 1, fc2);
cvmSet(&intrinsic_matrix, 1, 2, cc2);
cvmSet(&intrinsic_matrix, 2, 0, 0);
cvmSet(&intrinsic_matrix, 2, 1, 0);
cvmSet(&intrinsic_matrix, 2, 2, 1);
cvmSet(&distortion_coeffs, 0, 0, kc1);
cvmSet(&distortion_coeffs, 0, 1, kc2);
cvmSet(&distortion_coeffs, 0, 2, kc3);
cvmSet(&distortion_coeffs, 0, 3, kc4);
/**/320*240 120度广角镜头
/
//矫正畸变(opencv)
cvUndistort2( ImageC1, Show1, &intrinsic_matrix, &distortion_coeffs);
//矫正畸变
for (int nx=0; nx<420; nx++)
{
for (int ny=0; ny<340; ny++)
{
double x=nx-50;
double y=ny-50;
double xx=(x-cc1)/fc1;
double yy=(y-cc2)/fc2;
double r2=pow(xx,2)+pow(yy,2);
double r4=pow(r2,2);
double xxx=xx*(1+kc1*r2+kc2*r4)+2*kc3*xx*yy+kc4*(r2+2*xx*xx);
double yyy=yy*(1+kc1*r2+kc2*r4)+2*kc4*xx*yy+kc3*(r2+2*yy*yy);
double xxxx = xxx*fc1+cc1;
double yyyy = yyy*fc2+cc2;
if (xxxx>0 && xxxx<320 && yyyy>0 && yyyy<240)
{
_I(Show2,nx,ny) = (int)_IF(ImageC1,xxxx,yyyy);
}
else
{
_I(Show2,nx,ny) = 0;
}
}
}
//画线
cvLine( Show1, cvPoint(0,10), cvPoint(320,10), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(0,230), cvPoint(320,230), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(10,0), cvPoint(10,240), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(310,0), cvPoint(310,240), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(0,0), cvPoint(320,240), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(0,240), cvPoint(320,0), cvScalar(255,255,255),3 );
cvLine( Show1, cvPoint(0,10), cvPoint(320,10), cvScalar(0,0,0) );
cvLine( Show1, cvPoint(0,230), cvPoint(320,230), cvScalar(0,0,0) );
cvLine( Show1, cvPoint(10,0), cvPoint(10,240), cvScalar(0,0,0) );
cvLine( Show1, cvPoint(310,0), cvPoint(310,240), cvScalar(0,0,0) );
cvLine( Show1, cvPoint(0,0), cvPoint(320,240), cvScalar(0,0,0) );
cvLine( Show1, cvPoint(0,240), cvPoint(320,0), cvScalar(0,0,0) );
//显示
cvShowImage("径向矫正1", Show1);
cvShowImage("径向矫正2", Show2);
cvWaitKey(1);
cvReleaseImage( &Show1 );
cvReleaseImage( &Show2 );
cvReleaseImage( &ImageC1 );
}
http://www.cnblogs.com/wqj1212/archive/2008/01/25/1052875.html
一、标定
关于摄像头的标定,说实话网上的资料太多了,方法也很多的。个人觉得想要研究这个的话,可以先了解基本原理,然后看opencv的例子calibration。如果你还了解的更深入的话,建议下载一些论文看看。
当然我推荐一个网址:http://www.ti-times.com/dfiles.asp?fl=download(深圳科创时代)我也是在查找资料的过程中发现的,觉得它上面为我们总结了很多有用的资料。
在这里说明一点。opencv的calibration是基于张正友标定法的,我们在标定的过程中要避免两次标定板的角度倾斜相差过小,这样会产生很大的误差(当然我们可以结合一些论文上的方法的来改进算法)。
上一张截图 : http://hi.csdn.net/space-9784-do-album-picid-497526-goto-down.html(CSDN的blog上传图片功能被和谐了)
二、畸形矫正
其实在opencv的calibration这个例子中完成了畸形矫正。但是我在我的工程中,由于一些条件的限制,在没有做矫正的情况下,2.8的镜头刚好照满整个触摸区域,但是经过矫正后,由于主要是径向畸变,可是opencv是校正前后图像一样大小的做法,必然导致有一部分校正后的图像被裁剪了。没办法,我只好自己去按原理写最基本的校正算法了。现在贴一些基本算法原理出来。
图片 : http://hi.csdn.net/space-9784-do-album-picid-497527-goto-down.html
具体代码如下:
//#pragma parallel omp for
for (int i = 0; i < realWidth; i ++)
{
//#pragma parallel omp for
for (int j = 0; j < realHeight; j ++)
{
// 图像物理坐标系中点在图像像素坐标系中的对应点u、v
double u = i - 65;
double v = j - 25;
// 根据图像在图像物理和图像像素坐标系中点的关系来计算图像物理坐标系中的点
//
// u = x / dx + u0 fx = 1 / dx
// v = y / dy + v0 fy = 1 / dy
double x = (u - u0) / fx;
double y = (v - v0) / fy;
double r2 = pow(x, 2) + pow(y, 2);
double r4 = pow(r2, 2);
double fr = 1 + k1 * r2 + k2 * r4 + k3 * r2 * r4;
double ud = (u - u0) * fr + u0;
double vd = (v - v0) * fr + v0;
int u0 = int(ud);
int v0 = int(vd);
int u1 = int(1 + ud);
int v1 = int(1 + vd);
if (ud > 0 && ud < 640 && vd > 0 && vd < 480)
{
fcImagePlus->image.pData[i + j * realWidth] = pixelInterpolation(tmpImage[0].image.pData,
ud, vd, u0, v0, u1, v1);
}
}
}