摄像头或者相机会因为镜片的光学特性而发生有规律的变形或者畸变,包括桶型畸变,枕型畸变和线性畸变。普通相机的这些畸变十分轻微,人的肉眼几乎分辨不出,所以这时可以不需要校正。对于广角相机,鱼眼相机,由于视角极大,相机透镜的物理属性十分明显,从而容易导致相片极度畸变,这时除了一些追求特殊艺术效果的场合,大部分情况下都需要校正。
这里使用OPENCV算法对相机标定和畸变校正。OPENCV是非常牛B的图像,视频,相机处理算法库,封装了图像计算,图像处理,分像解析,图像分离,图像切割,图像识别,图像滤镜,图像查找,图像匹配等十分强大的函数库。在VR,AR,3D,二维码,车牌,身份证,银行卡识别方面也应用很广。OPENCV底层使用C和C++实现,这使其也有十分优异的速度。智能手机可以使用很多种访求调用OPENCV完成工作,android从早期的JNI和现在的服务调用。从调用方式上来说是越来越简单了,但从应用角度来说,却是选择越来越多,根据不同场合和需求,既可以直接原始JNI,也可以使用JAR调用C,还可以通过OPENCV MANAGER的服务实现。
摄像头或者相机,或者图片的校正分两个步骤,一是标定,二是反畸变。标定过程是求出摄像头或者相机或者图片的畸变参数,通过这些参数使用特定畸变算法对图像进行校正。
1.相机标定算法比较复杂,详情参考我的前面博客。这里不再对算法做具体解释说明,只简单介绍一下标定的方法。在OPECV中,标定有两种算法,一种是棋盘纸,类似方格子纸,一种是圆圈阵列纸,两种方法实现的原理一样,都是通过分析图像上固定角点的位置实现,取得的标定的参数也一样,下面具体实现代码,我通过一个布尔变量使得我们的校正算法可以兼容棋盘和圆圈阵列两种方式。核心函数是findCirclesGrid和findChessboardCorners
boolean isChessboard = false;
private void findPattern(Mat grayFrame) {
isChessboard = false;
mPatternWasFound = Calib3d.findCirclesGrid(grayFrame, mPatternSize,
mCorners, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
if (!mPatternWasFound) {
isChessboard = true;
mPatternWasFound = Calib3d.findChessboardCorners(grayFrame, mPatternSize2,
mCorners, Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_FAST_CHECK);
}
}
2.求得到角点会在后面转为畸变矩阵存储mDistortionCoefficients,以备反畸变算法调用。核心函数是calibrateCamera
public void calibrate() {
ArrayList rvecs = new ArrayList();
ArrayList tvecs = new ArrayList();
Mat reprojectionErrors = new Mat();
ArrayList objectPoints = new ArrayList();
if (isChessboard){
objectPoints.add(Mat.zeros(mCornersSize2, 1, CvType.CV_32FC3));
}else {
objectPoints.add(Mat.zeros(mCornersSize, 1, CvType.CV_32FC3));
}
calcBoardCornerPositions(objectPoints.get(0));
for (int i = 1; i < mCornersBuffer.size(); i++) {
objectPoints.add(objectPoints.get(0));
}
Calib3d.calibrateCamera(objectPoints, mCornersBuffer, mImageSize,
mCameraMatrix, mDistortionCoefficients, rvecs, tvecs, mFlags);
mIsCalibrated = Core.checkRange(mCameraMatrix)
&& Core.checkRange(mDistortionCoefficients);
mRms = computeReprojectionErrors(objectPoints, rvecs, tvecs, reprojectionErrors);
Log.i(TAG, String.format("Average re-projection error: %f", mRms));
Log.i(TAG, "Camera matrix: " + mCameraMatrix.dump());
Log.i(TAG, "Distortion coefficients: " + mDistortionCoefficients.dump());
}
3.最后使用反畸变函数undistort把校正后的图像还原。
public Mat render(CvCameraViewFrame inputFrame) {
Mat renderedFrame = new Mat(inputFrame.rgba().size(), inputFrame.rgba().type());
Imgproc.undistort(inputFrame.rgba(), renderedFrame,
mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients());
return renderedFrame;
}
4两种算法的标定比较,两种方法是独立的,都可以达到标定的目的,棋盘纸的方法比较比较慢,圆圈阵列比较快,在低端手机上校正,可以使用圆圈阵列。
5.源码下载地址:
https://github.com/blogercn/CameraCalibration
使用方法,代码编绎APP装入手机中
校正前校正前畸变十分严重,方形屏幕成了流体体
棋盘纸校正
校正后
前后对比