image中存在扭曲,通过相机标定和remap,可以解决
针对径向因素影响(引起鱼眼效果),重新计算的位置为:
针对切向畸变(由于镜头并不是完全平行于成像平面),重新计算的位置为:
所以共有五个畸变参数:
转换使用:
w(w=Z),代表平面坐标系统
fx,fy为摄像机的焦距
cx,cy以像素表示的光学中心
Opencv支持的校准形式:
[1] Classical black-white chessboard,经典黑白棋盘
[2]Symmetrical circle pattern,旋转对称的圆型图案
[3]Asymmetrical circle pattern,不对称的圆图案
一棋盘标定
1>找到棋盘的角点
bool findChessboardCorners(InputArray image,
Size patternSize, OutputArray corners,
intflags=CALIB_CB_ADAPTIVE_THRESH
+CALIB_CB_NORMALIZE_IMAGE )
/*image,为8-bit的灰度图或者彩色图像
*patternSize,棋盘每一行和每一列的角点数量
*corners,用于输出发现的角点,vector<Point2f>
*flags(可以结合):
*CV_CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转化成二值图像
*CV_CALIB_CB_NORMALIZE_IMAGE:归一化图像灰度系数(用直方图均衡
*化或者自适应阈值)
*CV_CALIB_CB_FILTER_QUADS:在轮廓提取阶段,使用附加条件排除错
*误的假设
*CALIB_CB_FAST_CHECK:加快检测速度
*corners会特定排序,一行接一行,并且从左到右,如果没有检测到所有
*点或者没有成功排序,则会返回0
*/
2>呈现检测到棋盘角
void drawChessboardCorners(InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound)
//image为8-bit,三通道图像
//patternSize,每一行每一列的角
//corners,已经检测到的角
//patternWasFound,findChessboardCorners的返回值
3>细化角的位置
void cornerSubPix(InputArray image, InputOutputArray corners,
Size winSize, Size zeroZone,
TermCriteria criteria)
//image源图像
//corners,提供角点的初始坐标,返回更加精确的点
//winSize,搜索窗口的一般尺寸,如果winSize=Size(5,5),则search
//windows为11*11
//winSize,死区的一般尺寸,用来避免自相关矩阵的奇点,(-1,1)表示没
//有死区
//criteria,控制迭代次数和精度
4>计算标定参数
double calibrateCamera(InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
int flags=0,
TermCriteria criteria
=TermCriteria(TermCriteria::COUNT
+TermCriteria::EPS, 30,DBL_EPSILON) )
/*objectPoints,世界坐标,用vector<vector<Vec3f>>,输入x,y坐
*标,z坐标为0
*imagePoints,图像坐标,vector<vector<Vec2f>>
*imageSize,用于初始化标定摄像机的image的size
*cameraMatrix,输出3*3的浮点型矩矩阵,
*CV_CALIB_USE_INTRINSIC_GUESS或者*
*CV_CALIB_FIX_ASPECT_RATIO,fx,fy,cx,cy必须提前初始化
*distCoeffs,输出的畸变系数,(k1,k2,p1,p2[,k3[,k4,k5,k6]]),
*可能为4,5,8个结果
*rvecs,针对每个视角的旋转矩阵,vector<Mat>>
*tvecs,针对每个视角的转换矩阵
*flags,可以组合
*/
5>计算畸变参数
void initUndistortRectifyMap(InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size, int m1type,
OutputArray map1,
OutputArray map2)
/*cameraMatrix,3*3的浮点型矩矩阵
*输出的畸变系数,(k1,k2,p1,p2[,k3[,k4,k5,k6]]),
*可能为4,5,8个结果
*R,在客观空间中的转换对象
*newCameraMatrix,新的3*3的浮点型矩矩阵
*size,为失真图像的大小
*m1type,第一个输出的map,类型为CV_32FC1或CV_16SC2
*map1,x映射函数
*map2,y映射函数
*/
应用:
calibrate.h
//棋盘标定类
class Calibrate
{
private:
//世界坐标
std::vector<std::vector<cv::Point3f>> objectPoints;
//图像坐标
std::vector<std::vector<cv::Point2f>> imagePoints;
//输出矩阵
cv::Mat camerMatirx;
cv::Mat disCoeffs;
//计算输出矩阵时的flags
int flags;
//remap的变换矩阵
cv::Mat map1,map2;
//是否已经,计算去畸变参数
bool mustInitUndistort;
//增加标定点
void addPoints(const std::vector<cv::Point2f>&corners,
const std::vector<cv::Point3f> &objects);
public:
Calibrate():flags(0),mustInitUndistort(true){}
//检测图像中棋盘角点,并且存储
cv::Mat addChessboardPoints(const cv::Mat &image,
cv::Size boardSize);
//计算标定参数
double calibrate(cv::Size imageSize);
//去畸变
cv::Mat reMap(const cv::Mat &image);
};
calibrate.cpp
#include "calibrate.h"
void Calibrate::addPoints(const std::vector<cv::Point2f>
&corners,const std::vector<cv::Point3f> &objects)
{
imagePoints.push_back(corners);
objectPoints.push_back(objects);
}
cv::Mat Calibrate::addChessboardPoints
(const cv::Mat &image,cv::Size patternsize)
{
//存储检测到的角点
std::vector<cv::Point2f> corners;
//存储世界点
std::vector<cv::Point3f> objects;
//给世界点赋值
for(int i=0;i<patternsize.height;i++)
for(int j=0;j<patternsize.width;j++)
objects.push_back(cv::Point3f(i,j,0));
//进行角点检测
bool patternfound=
cv::findChessboardCorners(image,patternsize,corners,
CV_CALIB_CB_ADAPTIVE_THRESH
+CV_CALIB_CB_NORMALIZE_IMAGE
+CV_CALIB_CB_FAST_CHECK);
//如果corners已经全部找到,并且已经有序
if(patternfound)
{
//进行亚像素,细致化
cv::cornerSubPix(image,corners,cv::Size(11,11),
cv::Size(-1,-1),cv::TermCriteria
(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));
//将得到点,进行添加
addPoints(corners,objects);
}
cv::Mat result;
cv::cvtColor(image,result,CV_GRAY2BGR);
//将检测到的角点进行绘制
cv::drawChessboardCorners(result,patternsize,
corners,patternfound);
return result;
}
double Calibrate::calibrate(cv::Size imageSize)
{
mustInitUndistort=true;
std::vector<cv::Mat> rvecs,tvecs;
//计算camerMatirx和disCoeffs
return cv::calibrateCamera(objectPoints,imagePoints,
imageSize,camerMatirx,
disCoeffs,rvecs,tvecs,
flags);
}
cv::Mat Calibrate::reMap(const cv::Mat &image)
{
cv::Mat result;
if(mustInitUndistort)
{
//得到x,y方向的映射关系map1,map2
cv::initUndistortRectifyMap(camerMatirx,disCoeffs,
cv::Mat(),cv::Mat(),
image.size(),CV_32FC1,
map1,map2);
}
cv::remap(image,result,map1,map2,cv::INTER_LINEAR);
return result;
}
main.cpp
#include "calibrate.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
Calibrate C;
int num;
cv::Mat image;
//输入进行棋盘标定的图像数目
cin>>num;
for(int i=0;i<num;i++)
{
string filename;
cin>>filename;
image=cv::imread(filename,0);
//查找相应的棋盘角点
cv::Matresulr
=C.addChessboardPoints(image,cv::Size(9,6));
cv::imshow("Chess",result);
cv::waitKey(30);
}
//进行标定
C.calibrate(image.size());
string imagename;
cin>>imagename;
//显示去畸变图像
image=cv::imread(imagename,0);
image=C.reMap(image);
cv::imshow("Calibrate",image);
cv::waitKey();
return 0;
}
效果图:
[1]角点查找结果
[2]去畸变
二圆标定
1>发现圆网络的中心
bool findCirclesGrid(InputArray image, Size patternSize,
OutputArray centers,
int flags=CALIB_CB_SYMMETRIC_GRID,
const Ptr<FeatureDetector>&
blobDetector=new SimpleBlobDetector() )
/*image,8-bit灰度图或者彩色图像
*patternSize,每行每列的circle数目
*centers,输出的圆的中心,vector<Point2f>
*flags(只可选择一个):
*CALIB_CB_SYMMETRIC_GRID,使用对称的圆
*CALIB_CB_ASYMMETRIC_GRID,使用非对称的圆
*CALIB_CB_CLUSTERING,处理透视畸变的的能力更强,对背景杂波更加敏感
*blobDetector,特征探测器,exp:在浅色背景上的黑圈
*/
应用:
cv::cvtColor(Image,Image,CV_BGR2GRAY);
cv::Size patternSize(4,11);
std::vector<cv::Point2f> centers;
//检测圆
bool patternFound=cv::findCirclesGrid(Image,patternSize,
centers,cv::CALIB_CB_ASYMMETRIC_GRID);
//呈现查找出来的圆
cv::drawChessboardCorners(result,patternSize,
cv::Mat(centers),patternFound);
效果图: