OpenCV CvCalibFilter - eiichiromomma/CVMLAB GitHub Wiki
(OpenCV) CvCalibFilter
CvCalibFilterを使った歪み補正
- 1カメラの時はUndistortのみ。
- 2カメラの時はRectifyも可。
- 3カメラの時はPushとDrawまで可。
- 4カメラ以上は未対応。
cvaux.hで定義されている。 キャリブレーションを楽に利用可能。
ビルドの際はライブラリにcvaux.libを含める。
OpenCVのdocsに置かれているキャリブレーションパターンのコーナー数は6x8なのだが、EtalonParamsで定義する時は各々+1する必要がある
USBカメラでのハンドリングが良いvideoInputを利用。OpenCV/videoInput Libraryによるビデオキャプチャ - Point at infinity などを参照。
OpenCV/docsにあるpattern.pdf)を印刷して硬い板に貼り、カメラの前でヒラヒラさせる。 色々な向きでカメラの視界いっぱいにヒラヒラさせる程良い。
CalibFilter.SetFrames()で設定した枚数に達するとUndistortされた映像とオリジナルの映像が表示される。ESCで終了。
//CvCalibFilterを使った1カメラキャリブレーション 2008/5/19 Eiichiro Momma
#include <stdio.h>
#include <time.h>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <videoInput.h>
IplImage *current_frame_rgb;
double EtalonParams[3] = { 7, 9, 3 };
//EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
//+1になっているのは内部の処理の関係?
int main(int argc, char *argv[])
{
bool verbose = true;
videoInput VI;
int numDevices = VI.listDevices();
if (numDevices > 0){
VI.setupDevice(0,640,480);
}
unsigned char * myBuffer;
int image_width = VI.getWidth(0);
int image_height = VI.getHeight(0);
current_frame_rgb = cvCreateImage(cvSize(image_width, image_height), IPL_DEPTH_8U, 3);
myBuffer = (unsigned char*)(current_frame_rgb->imageData);
cvNamedWindow( "Window 0", CV_WINDOW_AUTOSIZE);
printf("WHEN THE COUNTER HITS 10, YOU'RE DONE \n");
int c=0;
CvCalibFilter CalibFilter;
CalibFilter.SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
//1台
CalibFilter.SetCameraCount(1);
//10枚利用(多い程良い)
CalibFilter.SetFrames(10);
IplImage* images[] = { current_frame_rgb};
int prev_time = 0;
while (c!=27){
//更新待ち(無くても良いかも?)
while(!VI.isFrameNew(0));
VI.getPixels(0, myBuffer, false, true);
bool found = CalibFilter.FindEtalon( images );
CalibFilter.DrawPoints( images );
cvShowImage("Window 0",current_frame_rgb);
c = cvWaitKey( 150 );
//キャリブレーションボードを発見したらXORにして反転させて知らせる
if( found ){
int cur_time = clock();
if( cur_time >= prev_time + 1000 )
{
prev_time = cur_time;
CalibFilter.Push();
cvXorS( current_frame_rgb, cvScalarAll(255), current_frame_rgb );
}
if( CalibFilter.IsCalibrated() )
{
break;
}
cvShowImage("Window 0",current_frame_rgb);
//1000ms待たせる
c = cvWaitKey(1000);
}
}
cvNamedWindow("org",1);
cvNamedWindow("undist",1);
while(c!=27){
while(!VI.isFrameNew(0));
VI.getPixels(0,myBuffer, false, true);
cvShowImage("org",current_frame_rgb);
//Undistortで上書き
CalibFilter.Undistort(images, images);
cvShowImage("undist",current_frame_rgb);
c=cvWaitKey(100);
}
exit(0);
cvDestroyAllWindows();
}
CVS版のStereoDemoがベース。
詳細はまた今度
Undistortと殆ど同じ。 カメラを2つにしてCalibFilter.Rectifyするだけでキャリブレーション画像が得られる。
あとは距離計測するだけ。
そこそこの精度。
//CvCalibFilterを使った2カメラキャリブレーション+距離計測 2008/5/19 Eiichiro Momma
#include <stdio.h>
#include <time.h>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <videoInput.h>
IplImage *current_frame_rgb;
double EtalonParams[3] = { 7, 9, 3 };
//EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
//+1になっているのは内部の処理の関係?
int main(int argc, char *argv[])
{
bool verbose = true;
videoInput VI;
int i;
int numDevices = VI.listDevices();
if (numDevices > 2){
numDevices = 2;
}
for (i = 0; i< numDevices; i++){
VI.setupDevice(i,640,480);
}
IplImage **imList = new IplImage *[numDevices];
char **myBufferList = new char *[numDevices];
int image_width = VI.getWidth(0);
int image_height = VI.getHeight(0);
for (i=0; i<numDevices; i++){
*(imList + i) = cvCreateImage(cvSize(VI.getWidth(i),VI.getHeight(i)),IPL_DEPTH_8U,3);
if (*(imList + i) == NULL){
return 1;
}
*(myBufferList + i) = (*(imList+i))->imageData;
}
char buf[16];
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvNamedWindow(buf,1);
}
int c=0;
CvCalibFilter CalibFilter;
CalibFilter.SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
CalibFilter.SetCameraCount(numDevices);
CalibFilter.SetFrames(10);
int prev_time = 0;
while (c!=27){
//更新待ち(無くても良いかも?)
for (i = 0; i< numDevices; i++){
while(!VI.isFrameNew(i));
VI.getPixels(i, (unsigned char*)(myBufferList[i]), false, true);
}
bool found = CalibFilter.FindEtalon( imList );
CalibFilter.DrawPoints( imList );
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvShowImage(buf,imList[i]);
}
c = cvWaitKey( 150 );
//キャリブレーションボードを発見したらXORにして反転させて知らせる
if( found ){
int cur_time = clock();
if( cur_time >= prev_time + 1000 )
{
prev_time = cur_time;
CalibFilter.Push();
for (i = 0; i< numDevices; i++){
cvXorS( imList[i], cvScalarAll(255), imList[i] );
}
}
if( CalibFilter.IsCalibrated() )
{
break;
}
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvShowImage(buf,imList[i]);
}
//1000ms待たせる
c = cvWaitKey(1000);
}
}
int maxdisp=100;
cvNamedWindow("disparity",1);
cvCreateTrackbar( "maxdisp", "disparity", &maxdisp, 255, 0 );
IplImage *vis_disp = cvCreateImage(cvGetSize(imList[0]),IPL_DEPTH_8U,1);
IplImage *gray0 = cvCloneImage(vis_disp);
IplImage *gray1 = cvCloneImage(vis_disp);
while(c!=27){
for (i = 0; i< numDevices; i++){
while(!VI.isFrameNew(i));
VI.getPixels(i, (unsigned char*)myBufferList[i], false, true);
}
CalibFilter.Rectify(imList,imList);
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvShowImage(buf,imList[i]);
}
vis_disp->origin = imList[0]->origin;
cvCvtColor(imList[0],gray0,CV_BGR2GRAY);
cvCvtColor(imList[1],gray1,CV_BGR2GRAY);
cvFindStereoCorrespondence(
gray0,gray1,
CV_DISPARITY_BIRCHFIELD,
vis_disp,
maxdisp ,15,3,6,8,15);
cvConvertScale(vis_disp, vis_disp, 255.f/maxdisp);
cvShowImage("disparity", vis_disp);
c=cvWaitKey(100);
}
exit(0);
cvDestroyAllWindows();
}
そのまま使うと3台までしか使えず、Rectifyも非対応。(2台のみ)
残念ながらこのクラスではSSSDのような実装は無いらしい。 複数台あるカメラを互い違いにキャリブレーションして多点計測にしてみた。
処理が重たくなるので320x240で使用。 精度は落ちる。
//CvCalibFilterを使った複数カメラのキャリブレーション+距離計測 2008/5/20 Eiichiro Momma
#include <stdio.h>
#include <time.h>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <videoInput.h>
enum{
MY_MAX_CAMERA=6
};
double EtalonParams[3] = { 7, 9, 3 };
//EtalonParamsは{コーナー数+1、もう一方のコーナー数+1、実空間単位での一片のサイズ(怪しいので要調査)}
//+1になっているのは内部の処理の関係?
int main(int argc, char *argv[])
{
bool verbose = true;
videoInput VI;
int i;
int numDevices = VI.listDevices();
//CvCalibFilterの仕様でカメラは3台までで、Rectifyは2台の時のみ働く状態なので
//互い違いに2台ずつキャリブレーションする
if (numDevices > MY_MAX_CAMERA){
numDevices = MY_MAX_CAMERA;
}
for (i = 0; i< numDevices; i++){
//VGAレベルにするとマシンが死にそうになる
VI.setupDevice(i,320,240);
}
IplImage **imList = new IplImage *[numDevices];
char **myBufferList = new char *[numDevices];
int image_width = VI.getWidth(0);
int image_height = VI.getHeight(0);
for (i=0; i<numDevices; i++){
*(imList + i) = cvCreateImage(cvSize(VI.getWidth(i),VI.getHeight(i)),IPL_DEPTH_8U,3);
if (*(imList + i) == NULL){
return 1;
}
*(myBufferList + i) = (*(imList+i))->imageData;
}
char buf[16];
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvNamedWindow(buf,1);
}
int c=0;
CvCalibFilter *CalibFilterList = new CvCalibFilter[numDevices -1];
int prev_time = 0;
int camNum;
for (camNum = 0; camNum < numDevices-1; camNum++){
CalibFilterList[camNum].SetEtalon( CV_CALIB_ETALON_CHESSBOARD, EtalonParams );
CalibFilterList[camNum].SetCameraCount(2);
CalibFilterList[camNum].SetFrames(10);
IplImage* imPair[] = {imList[camNum], imList[camNum+1]};
while (c!=27){
for (i = 0; i< numDevices; i++){
//更新待ち(無くても良いかも?)
//while(!VI.isFrameNew(i));
VI.getPixels(i, (unsigned char*)(myBufferList[i]), false, true);
}
bool found = CalibFilterList[camNum].FindEtalon( imPair);
CalibFilterList[camNum].DrawPoints( imPair);
for (i=0; i<numDevices; i++){
sprintf(buf,"win%d",i);
cvShowImage(buf,imList[i]);
}
c = cvWaitKey( 150 );
//キャリブレーションボードを発見したらXORにして反転させて知らせる
if( found ){
int cur_time = clock();
if( cur_time >= prev_time + 1000 )
{
prev_time = cur_time;
CalibFilterList[camNum].Push();
for (i = 0; i< 2; i++){
cvXorS( imList[i+camNum], cvScalarAll(255), imList[i+camNum] );
}
}
if( CalibFilterList[camNum].IsCalibrated() )
{
break;
}
for (i=0; i<2; i++){
sprintf(buf,"win%d",i+camNum);
cvShowImage(buf,imList[i+camNum]);
}
//1000ms待たせる
c = cvWaitKey(1000);
}
}
}
cvDestroyAllWindows();
//距離計測
int maxdisp = 100;
for (i=0; i<numDevices-1; i++){
sprintf(buf,"disp%d",i);
cvNamedWindow(buf,1);
cvCreateTrackbar( "maxdisp", buf, &maxdisp, 255, 0 );
}
IplImage *vis_disp = cvCreateImage(cvGetSize(imList[0]),IPL_DEPTH_8U,1);
IplImage *gray0 = cvCloneImage(vis_disp);
IplImage *gray1 = cvCloneImage(vis_disp);
IplImage *imPairx[] = {gray0, gray1};
//Rectifyの結果を一枚の画像に
IplImage *imRect = cvCreateImage(cvSize(2*vis_disp->width,(numDevices-1)*vis_disp->height),IPL_DEPTH_8U,1);
cvNamedWindow("Rect",1);
while(c!=27){
for (i = 0; i< numDevices; i++){
//while(!VI.isFrameNew(i));
VI.getPixels(i, (unsigned char*)myBufferList[i], false, true);
}
for (i = 0; i< numDevices-1; i++){
cvCvtColor(imList[i],gray0,CV_BGR2GRAY);
cvCvtColor(imList[i+1],gray1,CV_BGR2GRAY);
CalibFilterList[i].Rectify(imPairx,imPairx);
cvSetImageROI(imRect,cvRect(0,i*gray0->height,gray0->width,gray0->height));
cvCopyImage(gray0,imRect);
cvResetImageROI(imRect);
cvSetImageROI(imRect,cvRect(gray0->width,i*gray0->height,gray0->width,gray0->height));
cvCopyImage(gray1,imRect);
cvResetImageROI(imRect);
cvShowImage("Rect",imRect);
cvFindStereoCorrespondence(
gray0,gray1,
CV_DISPARITY_BIRCHFIELD,
vis_disp,
maxdisp ,15,3,6,8,15);
cvConvertScale(vis_disp, vis_disp, 255.f/maxdisp);
sprintf(buf,"disp%d",i);
cvShowImage(buf, vis_disp);
}
c=cvWaitKey(100);
}
exit(0);
cvDestroyAllWindows();
}