Откалибруйте одну камеру, используя OpenCV 2.3.1 и C++
Я пытаюсь откалибровать веб-камеру, используя OpenCV 2.3.1 и Visual Studio 2010 (консольное приложение C++). Я использую этот класс:
class CameraCalibrator{
private:
std::vector<std::vector<cv::Point3f>> objectPoints;
std::vector<std::vector<cv::Point2f>> imagePoints;
//Square Lenght
float squareLenght;
//output Matrices
cv::Mat cameraMatrix; //intrinsic
cv::Mat distCoeffs;
//flag to specify how calibration is done
int flag;
//used in image undistortion
cv::Mat map1,map2;
bool mustInitUndistort;
public:
CameraCalibrator(): flag(0), squareLenght(36.0), mustInitUndistort(true){};
int addChessboardPoints(const std::vector<std::string>& filelist,cv::Size& boardSize){
std::vector<std::string>::const_iterator itImg;
std::vector<cv::Point2f> imageCorners;
std::vector<cv::Point3f> objectCorners;
//initialize the chessboard corners in the chessboard reference frame
//3d scene points
for(int i = 0; i<boardSize.height; i++){
for(int j=0;j<boardSize.width;j++){
objectCorners.push_back(cv::Point3f(float(i)*squareLenght,float(j)*squareLenght,0.0f));
}
}
//2D Image points:
cv::Mat image; //to contain chessboard image
int successes = 0;
//cv::namedWindow("Chess");
for(itImg=filelist.begin(); itImg!=filelist.end(); itImg++){
image = cv::imread(*itImg,0);
bool found = cv::findChessboardCorners(image, boardSize, imageCorners);
//cv::drawChessboardCorners(image, boardSize, imageCorners, found);
//cv::imshow("Chess",image);
//cv::waitKey(1000);
cv::cornerSubPix(image, imageCorners, cv::Size(5,5),cv::Size(-1,-1),
cv::TermCriteria(cv::TermCriteria::MAX_ITER+cv::TermCriteria::EPS,30,0.1));
//if we have a good board, add it to our data
if(imageCorners.size() == boardSize.area()){
addPoints(imageCorners,objectCorners);
successes++;
}
}
return successes;
}
void addPoints(const std::vector<cv::Point2f>& imageCorners,const std::vector<cv::Point3f>& objectCorners){
//2D image point from one view
imagePoints.push_back(imageCorners);
//corresponding 3D scene points
objectPoints.push_back(objectCorners);
}
double calibrate(cv::Size &imageSize){
mustInitUndistort = true;
std::vector<cv::Mat> rvecs,tvecs;
return
cv::calibrateCamera(objectPoints, //the 3D points
imagePoints,
imageSize,
cameraMatrix, //output camera matrix
distCoeffs,
rvecs,tvecs,
flag);
}
void remap(const cv::Mat &image, cv::Mat &undistorted){
std::cout << cameraMatrix;
if(mustInitUndistort){ //called once per calibration
cv::initUndistortRectifyMap(
cameraMatrix,
distCoeffs,
cv::Mat(),
cameraMatrix,
image.size(),
CV_32FC1,
map1,map2);
mustInitUndistort = false;
}
//apply mapping functions
cv::remap(image,undistorted,map1,map2,cv::INTER_LINEAR);
}
};
Я использую 10 изображений шахматной доски (предположим, этого достаточно для калибровки) с разрешением 640x480. Основная функция выглядит так:
int main(){
CameraCalibrator calibrateCam;
std::vector<std::string> filelist;
filelist.push_back("img10.jpg");
filelist.push_back("img09.jpg");
filelist.push_back("img08.jpg");
filelist.push_back("img07.jpg");
filelist.push_back("img06.jpg");
filelist.push_back("img05.jpg");
filelist.push_back("img04.jpg");
filelist.push_back("img03.jpg");
filelist.push_back("img02.jpg");
filelist.push_back("img01.jpg");
cv::Size boardSize(8,6);
double calibrateError;
int success;
success = calibrateCam.addChessboardPoints(filelist,boardSize);
std::cout<<"Success:" << success << std::endl;
cv::Size imageSize;
cv::Mat inputImage, outputImage;
inputImage = cv::imread("img10.jpg",0);
outputImage = inputImage.clone();
imageSize = inputImage.size();
calibrateError = calibrateCam.calibrate(imageSize);
std::cout<<"Calibration error:" << calibrateError << std::endl;
calibrateCam.remap(inputImage,outputImage);
cv::namedWindow("Original");
cv::imshow("Original",inputImage);
cv::namedWindow("Undistorted");
cv::imshow("Undistorted",outputImage);
cv::waitKey();
return 0;
}
Все работает без ошибок. cameraMatrix выглядит примерно так:
685,65 0 365,14
0 686,38 206,98
0 0 1
Ошибка калибровки составляет 0,310157, что является приемлемым.
Но когда я использую переназначение, выходное изображение выглядит даже хуже, чем оригинал. Вот образец:
Исходное изображение: ]
Неискаженное изображение: ]
Итак, вопрос в том, что я делаю что-то не так в процессе калибровки? Достаточно ли для калибровки 10 разных изображений шахматной доски? У вас есть какие-нибудь предложения?
2 ответа
Матрица камеры не искажает объектив, эти 4 значения - это просто фокусное расстояние (в H и V) и центр изображения (в X и Y)
Есть еще 3 или 4 строки значения матрицы (distCoeffs
в вашем коде), который содержит отображение объектива - см. ответ Карла, например, код
Калибровка выполняется с помощью численной оптимизации, которая имеет довольно небольшой уклон вблизи решения. Кроме того, минимизируемая функция очень нелинейна. Итак, я думаю, что ваши 10 изображений не достаточно. Я калибрую камеры с очень широкоугольными объективами (то есть с сильно искаженными изображениями) и пытаюсь получить 50 или 60 изображений.
Я пытаюсь получить изображения с шахматной доски в 3 или 4 положениях вдоль каждого края изображения, а также в середине, с несколькими ориентациями относительно камеры и на 3 разных расстояниях (очень близко, типично и насколько вы можете получить и все еще разрешить шахматную доску).
Получение шахматной доски в углах очень важно. Ваши примеры изображений не имеют шахматную доску очень близко к углу изображения. Это те точки, которые ограничивают калибровку, чтобы делать правильные вещи в очень искаженных частях изображения (углах).