1. 程式人生 > >手機廣角相機標定和畸變校正

手機廣角相機標定和畸變校正

攝像頭或者相機會因為鏡片的光學特性而發生有規律的變形或者畸變,包括桶型畸變,枕型畸變和線性畸變。普通相機的這些畸變十分輕微,人的肉眼幾乎分辨不出,所以這時可以不需要校正。對於廣角相機,魚眼相機,由於視角極大,相機透鏡的物理屬性十分明顯,從而容易導致相片極度畸變,這時除了一些追求特殊藝術效果的場合,大部分情況下都需要校正。

這裡使用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<Mat> rvecs = new ArrayList<Mat>();
    ArrayList<Mat> tvecs = new ArrayList<Mat>();
    Mat reprojectionErrors = new Mat();
    ArrayList<Mat> objectPoints = new ArrayList<Mat>();
    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.原始碼下載地址:

 使用方法,程式碼編繹APP裝入手機中

校正前校正前畸變十分嚴重,方形螢幕成了流體體



棋盤紙校正



圓圈陣列校正

校正後


前後對比