From e9a8c09c2c581c80bc8fe3dee8d5fac8a2e8b59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?llu=C3=ADs=20g=C3=B3mez=20i=20bigord=C3=A0?= Date: Thu, 14 Jul 2011 19:20:03 +0000 Subject: added pix_opencv_calibration: camera calibration function by Antoine Villeret helped by Cyrille Henry thanks guys! svn path=/trunk/externals/pix_opencv/; revision=15152 --- Makefile.in | 2 +- pix_opencv_calibration-help.pd | 258 ++++++++++++++++++++++ pix_opencv_calibration.cc | 476 +++++++++++++++++++++++++++++++++++++++++ pix_opencv_calibration.h | 115 ++++++++++ 4 files changed, 850 insertions(+), 1 deletion(-) create mode 100644 pix_opencv_calibration-help.pd create mode 100644 pix_opencv_calibration.cc create mode 100644 pix_opencv_calibration.h diff --git a/Makefile.in b/Makefile.in index c85978c..3384504 100644 --- a/Makefile.in +++ b/Makefile.in @@ -30,7 +30,7 @@ endif .SUFFIXES = $(EXTENSION) -SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc pix_opencv_of_bm.cc pix_opencv_of_hs.cc pix_opencv_of_lk.cc +SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc pix_opencv_of_bm.cc pix_opencv_of_hs.cc pix_opencv_of_lk.cc pix_opencv_calibration.cc all: $(SOURCES:.cc=.$(EXTENSION)) $(SOURCES_OPT:.cc=.$(EXTENSION)) diff --git a/pix_opencv_calibration-help.pd b/pix_opencv_calibration-help.pd new file mode 100644 index 0000000..9adb855 --- /dev/null +++ b/pix_opencv_calibration-help.pd @@ -0,0 +1,258 @@ +#N canvas 260 0 1106 660 10; +#X obj 182 329 cnv 15 400 180 empty empty empty 20 12 0 14 -4034 -66577 +0; +#X obj 145 -162 gemhead; +#X obj 192 525 pix_texture; +#X obj 356 -216 cnv 15 220 70 empty empty empty 20 12 0 14 -195568 +-66577 0; +#N canvas 0 22 454 304 gemwin 1; +#X obj 67 89 outlet; +#X obj 67 10 inlet; +#X obj 65 41 route create; +#X msg 67 70 set destroy; +#X msg 182 68 set create; +#N canvas 87 154 247 179 Gem.init 0; +#X obj 118 46 loadbang; +#X msg 118 81 reset; +#X obj 118 113 outlet; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X restore 289 80 pd Gem.init; +#X obj 162 241 gemwin; +#X msg 342 184 destroy; +#X msg 283 161 frame 25; +#X obj 164 126 t b b b; +#X msg 161 189 dimen 640 560 \, create \, 1; +#X connect 1 0 2 0; +#X connect 2 0 3 0; +#X connect 2 0 9 0; +#X connect 2 1 4 0; +#X connect 2 1 7 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 7 0 6 0; +#X connect 8 0 6 0; +#X connect 9 0 10 0; +#X connect 9 2 8 0; +#X connect 10 0 6 0; +#X restore 366 -170 pd gemwin; +#X msg 366 -190 destroy; +#X obj -232 499 pix_texture; +#X obj 192 251 separator; +#X obj 157 -135 bng 25 250 50 0 load empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 214 40 bng 15 250 50 0 empty empty end_reached 20 7 0 10 -262144 +-1 -1; +#X floatatom 196 5 5 0 10000 1 frame# - -; +#X obj 157 -106 openpanel; +#X msg 157 -86 open \$1; +#X obj 145 23 pix_film; +#X msg 163 -43 auto \$1; +#X obj 163 -61 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 +1; +#X text 364 -210 Create window and render; +#X msg 258 -60 colorspace RGBA; +#X msg 363 -60 colorspace RGB; +#X msg 467 -60 colorspace Grey; +#X obj 258 -88 loadbang; +#X obj 192 222 pix_separator; +#X obj -232 252 separator; +#X obj -232 223 pix_separator; +#N canvas 0 22 450 300 vswitch 0; +#X obj 144 263 outlet; +#X obj 36 26 inlet; +#X obj 260 29 inlet; +#X obj 36 133 spigot; +#X obj 260 141 spigot; +#X obj 119 64 loadbang; +#X msg 83 93 0; +#X msg 113 93 1; +#X msg 296 104 0; +#X msg 326 104 1; +#X obj 399 34 inlet; +#X obj 399 67 select 0; +#X connect 1 0 3 0; +#X connect 2 0 4 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 5 0 7 0; +#X connect 5 0 8 0; +#X connect 6 0 3 1; +#X connect 7 0 3 1; +#X connect 8 0 4 1; +#X connect 9 0 4 1; +#X connect 10 0 11 0; +#X connect 11 0 7 0; +#X connect 11 0 8 0; +#X connect 11 1 9 0; +#X connect 11 1 6 0; +#X restore -232 52 pd vswitch; +#X obj -136 53 tgl 15 0 empty load empty 17 7 0 10 -262144 -1 -1 0 +1; +#X obj -232 19 pix_video; +#X obj -232 -169 gemhead; +#X msg -213 -103 dimen 640 480; +#X msg -215 -128 dimen 320 240; +#X text 224 397 load distorsion coefficients and intrinsic matrix; +#X msg -186 -33 driver v4l2; +#X msg -202 -80 driver dc1394 \, dimen 640 480 \, colorspace grey; +#X msg -177 -12 driver dc1394; +#X obj 192 469 pix_opencv_calibration; +#X obj -232 160 pix_grey; +#X obj -232 289 translateXYZ 0 -2 0; +#X obj 192 285 translateXYZ 0 2 0; +#X obj -232 525 rectangle 2.666 2; +#X obj 192 546 rectangle 2.666 2; +#X msg 225 416 loadDist \$1; +#X msg 321 417 loadIntra \$1; +#X msg 225 376 writeDist \$1; +#X msg 321 377 writeIntra \$1; +#X text 224 357 write distorsion coefficients and intrinsic matrix +; +#X obj 353 79 cnv 15 500 220 empty empty empty 20 12 0 14 -4034 -66577 +0; +#X obj 361 90 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 +1; +#X msg 361 110 calibration \$1; +#X msg 361 154 view \$1; +#X text 400 132 number of view to take during calibration; +#X floatatom 361 136 5 0 0 0 - - -; +#X obj 361 195 pack f f; +#X msg 361 215 patternSize \$1 \$2; +#X floatatom 361 177 5 0 0 0 - - -; +#X floatatom 414 177 5 0 0 0 - - -; +#X text 451 173 number of inner corners on the chessboard; +#N canvas 130 67 612 325 advance-options 0; +#X msg 55 161 findChessFlag \$1 \$2 \$3; +#X obj 68 91 t b f; +#X obj 55 142 pack f f f; +#X obj 118 111 t b f; +#X obj 55 51 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 +; +#X obj 68 71 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 +; +#X obj 118 91 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +1; +#X obj 55 267 outlet; +#X text 53 27 findChessBoardCorner option; +#X text 75 50 CV_CALIB_CB_ADAPTIVE_TRESH : adaptive threhold (default +1); +#X text 84 70 CV_CALIB_CB_NORMALIZE_IMAGE : normalize image (default +0); +#X text 135 91 CV_CALIB_CB_FILTER_QUADS: advance filtering (default +1); +#X connect 0 0 7 0; +#X connect 1 0 2 0; +#X connect 1 1 2 1; +#X connect 2 0 0 0; +#X connect 3 0 2 0; +#X connect 3 1 2 2; +#X connect 4 0 2 0; +#X connect 5 0 1 0; +#X connect 6 0 3 0; +#X restore 360 277 pd advance-options; +#X floatatom 361 236 5 0 0 0 - - -; +#X msg 361 254 wait \$1; +#X text 399 235 number of frame to wait between two takes during calibration +; +#X text 380 90 start callibration; +#X text -168 -242 pix_opencv_calibration is an object allowing camera +calibration. you need to move a checkboard in front of the camera during +the calibration part.; +#X obj -112 160 pix_rgba; +#X obj -232 125 spigot 1; +#X text -119 51 switch source (camera / film); +#X obj -136 73 tgl 15 0 empty load empty 17 7 0 10 -262144 -1 -1 0 +1; +#X text -119 71 switch color mode ( grey / rgba ); +#X msg 225 446 loadDist XCD-V60_4mm-dist; +#X msg 414 446 loadIntra XCD-V60_4mm-intra; +#X msg -193 -56 device 1; +#X obj -164 100 == 0; +#X obj -112 124 spigot 0; +#X obj -108 375 cnv 15 200 50 empty empty empty 20 12 0 14 -4034 -66577 +0; +#N canvas 602 330 764 438 how-to 1; +#X text -93 -194 getting started with camera calibration :; +#X text -65 -108 3 Check calibration toggle to start and move the chessboard +in front of the camera (or move the camera in front of your screen). +Your computer may slow down during searching chessboard corners. When +all corners are found it takes a snapshot (and the image will be negative) +; +#X text -65 -128 2 Plug a supported camera and start rendering; +#X text -64 -158 1 Print the chessboard ona rigid paperboard (or just +show it on the screen); +#X text -65 -37 4 When enought chessboard have been found (20 by default) +it computes transformation matrix and map to undistord the image; +#X text -65 13 5 Then all incoming images are undistord \, you can +save the transformation and distorsion matrix to files to load it next +times; +#X text -99 96 for more information about this calibration see :; +#X text -89 146 - A flexible new technique for camera calibration \, +Zhang \, 1998 \, Microsoft Research; +#X text -89 116 - Learning Opencv \, Bradski and Kaehler \, 2008 \, +O'Reilly; +#X restore -78 391 pd how-to calibrate; +#X connect 1 0 13 0; +#X connect 2 0 39 0; +#X connect 4 0 5 0; +#X connect 5 0 4 0; +#X connect 6 0 38 0; +#X connect 7 0 37 0; +#X connect 8 0 11 0; +#X connect 9 0 10 0; +#X connect 10 0 13 1; +#X connect 11 0 12 0; +#X connect 12 0 13 0; +#X connect 13 0 24 1; +#X connect 13 2 9 0; +#X connect 14 0 13 0; +#X connect 15 0 14 0; +#X connect 17 0 13 0; +#X connect 17 0 26 0; +#X connect 18 0 13 0; +#X connect 19 0 13 0; +#X connect 20 0 17 0; +#X connect 20 0 15 0; +#X connect 21 0 7 0; +#X connect 22 0 36 0; +#X connect 23 0 22 0; +#X connect 24 0 63 0; +#X connect 24 0 71 0; +#X connect 25 0 24 2; +#X connect 26 0 24 0; +#X connect 27 0 26 0; +#X connect 28 0 26 0; +#X connect 29 0 26 0; +#X connect 31 0 26 0; +#X connect 32 0 26 0; +#X connect 33 0 26 0; +#X connect 34 0 2 0; +#X connect 35 0 23 0; +#X connect 35 0 21 0; +#X connect 36 0 6 0; +#X connect 37 0 34 0; +#X connect 40 0 34 0; +#X connect 41 0 34 0; +#X connect 42 0 34 0; +#X connect 43 0 34 0; +#X connect 46 0 47 0; +#X connect 47 0 34 0; +#X connect 48 0 34 0; +#X connect 50 0 48 0; +#X connect 51 0 52 0; +#X connect 52 0 34 0; +#X connect 53 0 51 0; +#X connect 54 0 51 1; +#X connect 56 0 34 0; +#X connect 57 0 58 0; +#X connect 58 0 34 0; +#X connect 62 0 23 0; +#X connect 62 0 21 0; +#X connect 63 0 35 0; +#X connect 65 0 70 0; +#X connect 65 0 71 1; +#X connect 68 0 34 0; +#X connect 69 0 26 0; +#X connect 70 0 63 1; +#X connect 71 0 62 0; diff --git a/pix_opencv_calibration.cc b/pix_opencv_calibration.cc new file mode 100644 index 0000000..f7681c3 --- /dev/null +++ b/pix_opencv_calibration.cc @@ -0,0 +1,476 @@ +//////////////////////////////////////////////////////// +// +// GEM - Graphics Environment for Multimedia +// +// zmoelnig@iem.kug.ac.at +// +// Implementation file +// +// Copyright (c) 1997-2000 Mark Danks. +// Copyright (c) Günther Geiger. +// Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM +// Copyright (c) 2002 James Tittle & Chris Clepper +// For information on usage and redistribution, and for a DISCLAIMER OF ALL +// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution. +// +///////////////////////////////////////////////////////// +// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) +// camera calibration function by Antoine Villeret helped by Cyrille Henry + +#include "pix_opencv_calibration.h" +#include +//#include +//#include + +CPPEXTERN_NEW(pix_opencv_calibration) + +///////////////////////////////////////////////////////// +// +// pix_opencv_calibration +// +///////////////////////////////////////////////////////// +// Constructor +// +///////////////////////////////////////////////////////// +pix_opencv_calibration :: pix_opencv_calibration() +{ + find_rgb = NULL; + find_gray = NULL; + rgb = NULL; + gray = NULL; + tmp = NULL; + mapx = NULL; + mapy = NULL; + + success_count = 0; + board_view_nb = 10; + calibration = 0; + patternSize[0] = 6; + patternSize[1] = 7; + frame = 0; + wait_n_frame = 10; + findChessFlag = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS; + + // allocate storage matrix + image_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 2, CV_32FC1); + object_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 3, CV_32FC1); + point_counts = cvCreateMat(board_view_nb, 1, CV_32SC1); + intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1); + distortion_coeffs = cvCreateMat(5, 1, CV_32FC1); + + // make an "empty" intrinsinc matrix + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 0 ) = 8; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 0 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 0 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 1 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 1 ) = 8; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 1 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 2 ) = 3; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 2 ) = 3; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 2 ) = 1; + + // zeros distortion coeffs + for ( int i = 0 ; i < 5 ; i++ ) { + CV_MAT_ELEM( *distortion_coeffs, float, i, 0 ) = 0.0; + } + + post("pix_opencv_calibration build on %s at %s", __DATE__, __TIME__); +} + +///////////////////////////////////////////////////////// +// Destructor +// +///////////////////////////////////////////////////////// +pix_opencv_calibration :: ~pix_opencv_calibration() +{ + //Destroy cv_images to clean memory + if ( find_rgb ) cvReleaseImage(&find_rgb); + if ( find_gray ) cvReleaseImage(&find_gray); + if ( gray ) cvReleaseImage(&gray); + if ( rgb ) cvReleaseImage(&rgb); + if ( tmp ) cvReleaseImage(&tmp); + if ( mapx ) cvReleaseImage(&mapx); + if ( mapy ) cvReleaseImage(&mapy); + cvReleaseMat(&intrinsic_matrix); + cvReleaseMat(&distortion_coeffs); + cvReleaseMat(&image_points); + cvReleaseMat(&object_points); + cvReleaseMat(&point_counts); +} + +///////////////////////////////////////////////////////// +// processImage +// +///////////////////////////////////////////////////////// +void pix_opencv_calibration :: processRGBAImage(imageStruct &image) +{ + if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) { + + // printf("process rgba image\n"); + this->comp_xsize = image.xsize; + this->comp_ysize = image.ysize; + if ( calibration ) error ( "image size changed, calibration was cancelled"); + calibration = 0; + + if ( find_rgb ) cvReleaseImage(&find_rgb); + if ( find_gray ) cvReleaseImage(&find_gray); + if ( gray ) cvReleaseImage(&gray); // TODO : cette ligne crash qd on passe du gray a couleur apres ou pendant calibration + if ( rgb ) cvReleaseImage(&rgb); + if ( tmp ) cvReleaseImage(&tmp); + if ( mapx ) cvReleaseImage(&mapx); + if ( mapy ) cvReleaseImage(&mapy); + + // used in findCorners + find_rgb = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4); + find_gray = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1); + + //create the images with new size + rgb = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4); + tmp = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4); + mapx = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1); + mapy = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1); + + // create new map + cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy); + } + + // no need to copy a lot of memory, just point to it... + rgb->imageData = (char*) image.data; + + // this will loop until we got enought views (x->board_view_nb) with all corners visible + if ( success_count < board_view_nb && calibration != 0 ) { + findCorners( rgb ); + image.data = (unsigned char*) rgb->imageData; + } + else if ( success_count >= board_view_nb && calibration != 0 ) { + computeCalibration( rgb ); + image.data = (unsigned char*) rgb->imageData; + } + else if ( this->calibration == 0 ) { + cvRemap(rgb,tmp,mapx,mapy); + image.data = (unsigned char*) tmp->imageData; + } +} + +void pix_opencv_calibration :: processRGBImage(imageStruct &image) { + error( "pix_opencv_calibration : rgb format not supported"); +} + +void pix_opencv_calibration :: processYUVImage(imageStruct &image) { + error( "pix_opencv_calibration : yuv format not supported" ); +} + +void pix_opencv_calibration :: processGrayImage(imageStruct &image) +{ + if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!gray)) { + + // printf("process gray image\n"); + this->comp_xsize = image.xsize; + this->comp_ysize = image.ysize; + if ( calibration ) error ( "image size changed, calibration was cancelled"); + calibration = 0; + + if ( find_rgb ) cvReleaseImage(&find_rgb); + if ( find_gray ) cvReleaseImage(&find_gray); + if ( gray ) cvReleaseImage(&gray); + if ( rgb ) cvReleaseImage(&rgb); + if ( tmp ) cvReleaseImage(&tmp); + if ( mapx ) cvReleaseImage(&mapx); + if ( mapy ) cvReleaseImage(&mapy); + + // used in findCorners + find_rgb = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4); + find_gray = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1); + + //create the images with new size + gray = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1); + tmp = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1); + mapx = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1); + mapy = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1); + + // create new map + cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy); + } + + // no need to copy a lot of memory, just point to it... + gray->imageData = (char*) image.data; + + // this will loop until we got enought views (x->board_view_nb) with all corners visible + if ( success_count < board_view_nb && calibration != 0 ) { + findCorners( gray ); + image.data = (unsigned char*) gray->imageData; + } + else if ( success_count >= board_view_nb && calibration != 0 ) { + computeCalibration( gray ); + image.data = (unsigned char*) gray->imageData; + } + else if ( this->calibration == 0 ) { + cvRemap(gray,tmp,mapx,mapy); + image.data = (unsigned char*) tmp->imageData; + } +} + +///////////////////////////////////////////////////////// +// findCorners +// +///////////////////////////////////////////////////////// +void pix_opencv_calibration :: findCorners ( IplImage *image ) +{ + int board_point_nb = this->patternSize[0]*this->patternSize[1]; + CvPoint2D32f *corners = new CvPoint2D32f[board_point_nb]; + int corner_count; + int step; + CvSize patternSize, image_size; + CvMat in_cv; + + patternSize = cvSize( this->patternSize[0], this->patternSize[1] ); + image_size = cvSize( image->width, image->height ); + // cvGetImage (&in_cv, &in_image); // create an IplImage from a CvMat + cvGetMat ( image, &in_cv ); // create a CvMat from IplImage + + // find chessboard corners (gray or RGBA image...) + int found = cvFindChessboardCorners(image, + patternSize, + corners, + &corner_count, + findChessFlag); + if (image->nChannels == 4) { + cvCvtColor( image , find_gray , CV_RGBA2GRAY); // convert color to gray + } else { + cvCopy(image, find_gray) ; + } + + // get subpixel accuracy on those corners (grayscale image only) + cvFindCornerSubPix(find_gray, + corners, + corner_count, + cvSize(11,11), + cvSize(-1,-1), + cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1)); + + cvCvtColor( find_gray , find_rgb , CV_GRAY2RGBA); // convert gray to color + + // draw chessboard corner (color image only) + cvDrawChessboardCorners(find_rgb, patternSize, corners, corner_count, found); + + this->frame++; + if ( this->frame % this->wait_n_frame == 0 ) { + // update arrays + + if( corner_count == board_point_nb ) { + step = this->success_count*board_point_nb; + for( int i=step, j=0; jimage_points, float,i,0) = corners[j].x; + CV_MAT_ELEM(*this->image_points, float,i,1) = corners[j].y; + CV_MAT_ELEM(*this->object_points,float,i,0) = j/this->patternSize[0]; + CV_MAT_ELEM(*this->object_points,float,i,1) = j%this->patternSize[0]; + CV_MAT_ELEM(*this->object_points,float,i,2) = 0.0f; + } + CV_MAT_ELEM(*this->point_counts, int,this->success_count,0) = board_point_nb; + this->success_count++; + + cvNot( find_rgb , find_rgb ); + } + post("take : %d/%d\n", success_count, board_view_nb); + } + + // convert color to gray + if (image->nChannels == 1) { + cvCvtColor( find_rgb , image, CV_RGBA2GRAY); // convert color to gray + } else { + cvCopy(find_rgb, image); + } + +} +///////////////////////////////////////////////////////// +// computeCalibration +// +///////////////////////////////////////////////////////// +void pix_opencv_calibration :: computeCalibration ( IplImage *image ) +{ + //CALIBRATE THE CAMERA! + cvCalibrateCamera2(this->object_points, + this->image_points, + this->point_counts, + cvSize( image->width , image->height ), + this->intrinsic_matrix, + this->distortion_coeffs, + NULL, + NULL, + 0); + cvReleaseImage(&mapx); + cvReleaseImage(&mapy); + this->mapx = cvCreateImage( cvSize( image->width, image->height ), IPL_DEPTH_32F, 1 ); + this->mapy = cvCreateImage( cvSize( image->width, image->height ), IPL_DEPTH_32F, 1 ); + + cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy); + calibration = 0; +} +///////////////////////////////////////////////////////// +// LoadMess +// +///////////////////////////////////////////////////////// + +void pix_opencv_calibration :: loadIntraMess (t_symbol *filename) +{ + if ( filename == NULL ) { error("NULL pointer passed to function loadIntra"); return;} + this->intrinsic_matrix = (CvMat*)cvLoad(filename->s_name, 0, 0, 0); + + if (intrinsic_matrix == NULL) { + intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1); + post("can't open file %s", filename->s_name); + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 0 ) = 8; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 0 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 0 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 1 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 1 ) = 8; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 1 ) = 0; + CV_MAT_ELEM( *intrinsic_matrix, float, 0, 2 ) = 3; + CV_MAT_ELEM( *intrinsic_matrix, float, 1, 2 ) = 3; + CV_MAT_ELEM( *intrinsic_matrix, float, 2, 2 ) = 1; + } + else post("load transformation matrix from %s",filename->s_name); + + // reinitialise size to force reinitialisation of mapx and mapy on next frame + this->comp_xsize = 0; +} + +void pix_opencv_calibration :: loadDistMess (t_symbol *filename) +{ + if ( filename == NULL ) { error("NULL pointer passed to function loadDist"); return;} + distortion_coeffs = (CvMat*)cvLoad(filename->s_name); + if (distortion_coeffs == NULL){ + distortion_coeffs = cvCreateMat(5, 1, CV_32FC1); + post("can't open file %s", filename->s_name); + // zeros distortion coeffs + for ( int i = 0 ; i < 5 ; i++ ) { + CV_MAT_ELEM( *distortion_coeffs, float, i, 0 ) = 0.0; + } + } + else post("load distortion coefficients from %s",filename->s_name); + + // reinitialise size to force reinitialisation of mapx and mapy on next frame + this->comp_xsize = 0; +} + +void pix_opencv_calibration :: writeIntraMess (t_symbol *filename) +{ + // printf("write intrinsic matrix\n"); + cvSave(filename->s_name,intrinsic_matrix); +} + +void pix_opencv_calibration :: writeDistMess (t_symbol *filename) +{ + // printf("write distorsion coeffs\n"); + cvSave(filename->s_name,distortion_coeffs); +} + +void pix_opencv_calibration :: floatCalibrationhMess (float calib_flag) +{ + this->calibration=calib_flag; + if ( this->calibration == 1 ) { + this->success_count = 0; + this->frame = 0; + } + post("calibration : %d", this->calibration); +} + +void pix_opencv_calibration :: patternSizeMess (float xsize, float ysize) +{ + if (calibration) error("you can't change pattern size during calibration"); return; + if ( xsize < 3 || ysize < 3 ) error("patternSize should be at least 3x3"); return; + this->patternSize[0]=xsize; + this->patternSize[1]=ysize; +} + +void pix_opencv_calibration :: viewMess (int view) +{ + if ( calibration == 1 ) {error("you can't change view number during calibration !"); return;} + board_view_nb=view<2?2:view; + if (view < 2) error("view should be greater or equal to 2"); + + cvReleaseMat(&image_points); + cvReleaseMat(&object_points); + cvReleaseMat(&point_counts); + image_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 2, CV_32FC1); + object_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 3, CV_32FC1); + point_counts = cvCreateMat(board_view_nb, 1, CV_32SC1); + +} + +void pix_opencv_calibration :: waitMess (int wait) +{ + wait_n_frame=wait<1?1:wait; + if (wait < 1) error("wait should be greater or equal to 1, you can't calibrate more often than each frame !"); +} + +void pix_opencv_calibration :: findChessFlagMess(int adaptThres, int normalize, int filter) +{ + adaptThres=adaptThres<=0?0:adaptThres>=1?1:adaptThres; + normalize=normalize<=0?0:normalize>=1?1:normalize; + filter=filter<=0?0:filter>=1?1:filter; + findChessFlag = CV_CALIB_CB_ADAPTIVE_THRESH * adaptThres + CV_CALIB_CB_NORMALIZE_IMAGE * normalize + CV_CALIB_CB_FILTER_QUADS * filter; +} +///////////////////////////////////////////////////////// +// static member function +// +///////////////////////////////////////////////////////// +void pix_opencv_calibration :: obj_setupCallback(t_class *classPtr) +{ + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::loadIntraMessCallback, + gensym("loadIntra"), A_SYMBOL, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::loadDistMessCallback, + gensym("loadDist"), A_SYMBOL, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::writeIntraMessCallback, + gensym("writeIntra"), A_SYMBOL, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::writeDistMessCallback, + gensym("writeDist"), A_SYMBOL, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::floatCalibrationMessCallback, + gensym("calibration"), A_FLOAT, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::patternSizeMessCallback, + gensym("patternSize"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::viewMessCallback, + gensym("view"), A_FLOAT, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::waitMessCallback, + gensym("wait"), A_FLOAT, A_NULL); + class_addmethod(classPtr, (t_method)&pix_opencv_calibration::findChessFlagMessCallback, + gensym("findChessFlag"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + +} +void pix_opencv_calibration :: loadIntraMessCallback(void *data, t_symbol* filename) +{ + GetMyClass(data)->loadIntraMess(filename); +} +void pix_opencv_calibration :: loadDistMessCallback(void *data, t_symbol* filename) +{ + GetMyClass(data)->loadDistMess(filename); +} +void pix_opencv_calibration :: writeIntraMessCallback(void *data, t_symbol* filename) +{ + GetMyClass(data)->writeIntraMess(filename); +} +void pix_opencv_calibration :: writeDistMessCallback(void *data, t_symbol* filename) +{ + GetMyClass(data)->writeDistMess(filename); +} +void pix_opencv_calibration :: floatCalibrationMessCallback(void *data, t_floatarg calib_flag) +{ + GetMyClass(data)->floatCalibrationhMess((float)calib_flag); +} +void pix_opencv_calibration :: patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize) +{ + GetMyClass(data)->patternSizeMess((float)xsize, (float)ysize); +} +void pix_opencv_calibration :: viewMessCallback(void *data, t_floatarg view) +{ + GetMyClass(data)->viewMess((int)view); +} +void pix_opencv_calibration :: waitMessCallback(void *data, t_floatarg wait) +{ + GetMyClass(data)->waitMess((int)wait); +} +void pix_opencv_calibration :: findChessFlagMessCallback(void *data, t_floatarg adaptThres, t_floatarg normalize, t_floatarg filter) +{ + GetMyClass(data)->findChessFlagMess((int) adaptThres, (int) normalize, (int) filter); +} + diff --git a/pix_opencv_calibration.h b/pix_opencv_calibration.h new file mode 100644 index 0000000..7114288 --- /dev/null +++ b/pix_opencv_calibration.h @@ -0,0 +1,115 @@ +/*----------------------------------------------------------------- +LOG + GEM - Graphics Environment for Multimedia + + Threshold filter + + Copyright (c) 1997-1999 Mark Danks. mark@danks.org + Copyright (c) Günther Geiger. geiger@epy.co.at + Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM. zmoelnig@iem.kug.ac.at + Copyright (c) 2002 James Tittle & Chris Clepper + For information on usage and redistribution, and for a DISCLAIMER OF ALL + WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution. + +-----------------------------------------------------------------*/ + +#ifndef INCLUDE_pix_opencv_calibration_H_ +#define INCLUDE_pix_opencv_calibration_H_ + +#ifndef _EiC +#include "cv.h" +#endif + +#include "Base/GemPixObj.h" + +/*----------------------------------------------------------------- +------------------------------------------------------------------- +CLASS + pix_opencv_calibration + + Threshold filter + +KEYWORDS + pix + +DESCRIPTION + +-----------------------------------------------------------------*/ +class GEM_EXTERN pix_opencv_calibration : public GemPixObj +{ + CPPEXTERN_HEADER(pix_opencv_calibration, GemPixObj) + + public: + + ////////// + // Constructor + pix_opencv_calibration(); + + protected: + + ////////// + // Destructor + virtual ~pix_opencv_calibration(); + + ////////// + // Do the processing + virtual void processRGBAImage(imageStruct &image); + virtual void processRGBImage(imageStruct &image); + virtual void processYUVImage(imageStruct &image); + virtual void processGrayImage(imageStruct &image); + + void findCorners ( IplImage *image ); + void computeCalibration ( IplImage *image ); + + ////////// + // Set the new edge threshold + void loadIntraMess(t_symbol *filename); + void loadDistMess(t_symbol *filename); + void writeIntraMess(t_symbol *filename); + void writeDistMess(t_symbol *filename); + void floatCalibrationhMess (float calib_flag); + void patternSizeMess (float xsize, float ysize); + void viewMess (int view); + void waitMess (int wait); + void findChessFlagMess(int adaptThres, int normalize, int filter); + + // to detect changes in the image size + int comp_xsize; + int comp_ysize; + + int success_count, /* number of images on wich we correctly found corners */ + board_view_nb, /* number of views to take */ + calibration, /* calibration flag */ + patternSize[2], /* size of the calibration chessboard */ + frame, /* number of frames analysed for chessboard corner */ + wait_n_frame, /* number of frames to wait between two take */ + findChessFlag; // flag for cvFindChessboardCorners + + + private: + + ////////// + // Static member functions + static void loadIntraMessCallback(void *data, t_symbol* filename); + static void loadDistMessCallback(void *data, t_symbol* filename); + static void writeIntraMessCallback(void *data, t_symbol* filename); + static void writeDistMessCallback(void *data, t_symbol* filename); + static void floatCalibrationMessCallback(void *data, t_floatarg calib_flag); + static void patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize); + static void viewMessCallback(void *data, t_floatarg view); + static void waitMessCallback(void *data, t_floatarg wait); + static void findChessFlagMessCallback(void *data, t_floatarg adaptThres, t_floatarg normalize, t_floatarg filter); + ///////// + // CvMat needed + CvMat *image_points, + *object_points, + *point_counts, + *intrinsic_matrix, + *distortion_coeffs; + + ///////// + // IplImage needed + IplImage *find_rgb, *find_gray, *rgb, *gray, *tmp, *mapx, *mapy; +}; + +#endif // for header file -- cgit v1.2.1