00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <aruco/chromaticmask.h>
00030 #include <set>
00031
00032
00033
00036 EMClassifier::EMClassifier(unsigned int nelements) : _classifier(2, cv::EM::COV_MAT_DIAGONAL, cv::TermCriteria(cv::TermCriteria::COUNT , 4, FLT_EPSILON))
00037 {
00038 _nelem = nelements;
00039 _threshProb = 0.0001;
00040 for(unsigned int i=0; i<256; i++) _prob[i] = 0.5;
00041 }
00042
00043
00044
00047 void EMClassifier::train()
00048 {
00049
00050
00051
00052 for(unsigned int i=0; i<256;i++) _histogram[i]=0;
00053
00054 for(unsigned int i=0; i<_samples.size(); i++) {
00055 uchar val = _samples[i];
00056 _histogram[val]+=3;
00057 if(val>0) _histogram[val-1]+=2;
00058 if(val<255) _histogram[val+1]+=2;
00059 if(val>1) _histogram[val-2]+=1;
00060 if(val<254) _histogram[val+2]+=1;
00061 }
00062
00063 double sum=0.;
00064 for(unsigned int i=0; i<256;i++) sum += _histogram[i];
00065 for(unsigned int i=0; i<256;i++) _histogram[i] /= sum;
00066
00067
00068
00069 unsigned int histCount[256];
00070 int n=0;
00071 for(unsigned int i=0; i<256; i++)
00072 n+= (histCount[i] = (unsigned int)(_nelem*_histogram[i]));
00073
00074 if(n<10) return;
00075
00076 cv::Mat samples(n,1,CV_64FC1);
00077 unsigned int idx=0;
00078 for(unsigned int i=0; i<256; i++) {
00079 for(unsigned int j=0; j<histCount[i]; j++) {
00080 samples.ptr<double>(0)[idx] = i;
00081 idx++;
00082 }
00083 }
00084
00085 _classifier.train(samples);
00086
00087 cv::Mat sampleAux(1,1,CV_64FC1);
00088 for(unsigned int i=0; i<256; i++) {
00089 sampleAux.ptr<double>(0)[0] = i;
00090 cv::Vec2d r = _classifier.predict(sampleAux);
00091 _prob[i] = exp(r[0]);
00092 if(_prob[i]>_threshProb) _inside[i]=true;
00093 else _inside[i]=false;
00094 }
00095
00096
00097 }
00098
00099
00100 void ChromaticMask::setParams(unsigned int mc, unsigned int nc, double threshProb, aruco::CameraParameters CP, aruco::BoardConfiguration BC, float markersize)
00101 {
00102 if(markersize==-1) {
00103 std::cerr << "Invalid markersize in ChromaticMask::setParams" << std::endl;
00104 return;
00105 }
00106
00107 if(BC.size()==0) {
00108 std::cerr << "Invalid BoardConfiguration size in ChromaticMask::setParams" << std::endl;
00109 return;
00110 }
00111
00112
00113 cv::Point3f min, max;
00114 min = max = BC[0][0];
00115 for(unsigned int i=0; i<BC.size(); i++) {
00116 for(unsigned int j=0; j<4; j++) {
00117 if(BC[i][j].x <= min.x && BC[i][j].y <= min.y) min = BC[i][j];
00118 if(BC[i][j].x >= max.x && BC[i][j].y >= max.y) max = BC[i][j];
00119 }
00120 }
00121 double pixSize = fabs(markersize/(BC[0][1].x - BC[0][0].x));
00122 min.x *= pixSize;
00123 min.y *= pixSize;
00124 max.x *= pixSize;
00125 max.y *= pixSize;
00126
00127
00128 vector<cv::Point3f> corners;
00129 corners.push_back(min);
00130 corners.push_back( cv::Point3f(min.x, max.y, 0) );
00131 corners.push_back(max);
00132 corners.push_back( cv::Point3f(max.x, min.y, 0) );
00133
00134 setParams(mc, nc, threshProb, CP, BC, corners);
00135
00136 }
00137
00138
00139
00142 void ChromaticMask::setParams(unsigned int mc, unsigned int nc, double threshProb, aruco::CameraParameters CP, aruco::BoardConfiguration BC, vector<cv::Point3f> corners)
00143 {
00144 _classifiers.resize(mc*nc);
00145 for(unsigned int i=0; i<_classifiers.size(); i++) _classifiers[i].setProb(threshProb);
00146 _mc = mc;
00147 _nc = nc;
00148 _threshProb = threshProb;
00149 _cell_neighbours.resize(mc*nc);
00150 int idx_=0;
00151
00152 for(unsigned int j=0; j<nc; j++)
00153 for(unsigned int i=0; i<mc; i++,idx_++) {
00154 _centers.push_back(cv::Point2f(i+0.5, j+0.5));
00155 for(unsigned int nj=max(j-1,(unsigned int)0); nj<min(mc,j+1); nj++) {
00156 for(unsigned int ni=max(i-1,(unsigned int)0); ni<min(mc,i+1); ni++) {
00157 size_t tidx=nj*mc+ni;
00158 _cell_neighbours[idx_].push_back(tidx);
00159 }
00160 }
00161 }
00162
00163
00164 _BD.getMarkerDetector().setThresholdParams(35,7);
00165 _BD.getMarkerDetector().enableErosion(false);
00166 _BD.getMarkerDetector().setCornerRefinementMethod(aruco::MarkerDetector::LINES);
00167 _BD.setYPerpendicular(false);
00168
00169 _BD.setParams(BC, CP);
00170
00171 _objCornerPoints = corners;
00172 _CP = CP;
00173
00174 _pixelsVector.clear();
00175 for(unsigned int i=0; i<CP.CamSize.height/2; i++)
00176 for(unsigned int j=0; j<CP.CamSize.width/2; j++)
00177 _pixelsVector.push_back( cv::Point2f(2*j,2*i) );
00178
00179 resetMask();
00180 _cellMap = cv::Mat(CP.CamSize.height, CP.CamSize.width, CV_8UC1, cv::Scalar::all(0));
00181 _canonicalPos = cv::Mat(CP.CamSize.height, CP.CamSize.width, CV_8UC2);
00182
00183 _cellCenters.resize(_classifiers.size());
00184 for(unsigned int i=0; i<mc; i++) {
00185 for(unsigned int j=0; j<nc; j++) {
00186 _cellCenters[i*mc+j].x = ((2*i+1)*_cellSize)/2.;
00187 _cellCenters[i*mc+j].y = ((2*j+1)*_cellSize)/2.;
00188 }
00189 }
00190
00191 }
00192
00193 pair<double,double> AvrgTime(0,0) ;
00194
00197 void ChromaticMask::calculateGridImage(const aruco::Board &board )
00198 {
00199
00200
00201 cv::projectPoints(_objCornerPoints, board.Rvec, board.Tvec, _CP.CameraMatrix, _CP.Distorsion, _imgCornerPoints);
00202
00203
00204 cv::Point2f pointsRes[4],pointsIn[4];
00205 for ( int i=0;i<4;i++ ) pointsIn[i]=_imgCornerPoints[i];
00206 pointsRes[0]= ( cv::Point2f ( 0,0 ) );
00207 pointsRes[1]= cv::Point2f ( _cellSize*_mc-1, 0 );
00208 pointsRes[2]= cv::Point2f ( _cellSize*_mc-1, _cellSize*_nc-1 );
00209 pointsRes[3]= cv::Point2f ( 0, _cellSize*_nc-1 );
00210 _perpTrans=cv::getPerspectiveTransform ( pointsIn,pointsRes );
00211
00212 double tick = (double)cv::getTickCount();
00213
00214 vector<cv::Point2f> transformedPixels;
00215 cv::perspectiveTransform(_pixelsVector, transformedPixels, _perpTrans);
00216
00217 AvrgTime.first+=((double)cv::getTickCount()-tick)/cv::getTickFrequency(); AvrgTime.second++;
00218 cout<<"Time cc detection="<<1000*AvrgTime.first/AvrgTime.second<<" milliseconds"<<endl;
00219
00220
00221
00222 cv::Rect cellRect(0,0,_mc,_nc);
00223 for(unsigned int i=0; i<transformedPixels.size(); i++) {
00224
00225 transformedPixels[i].x /= _cellSize;
00226 transformedPixels[i].y /= _cellSize;
00227 if( !transformedPixels[i].inside(cellRect) ) {
00228 _cellMap.at<uchar>( _pixelsVector[i].y, _pixelsVector[i].x ) = 0;
00229 _cellMap.at<uchar>( _pixelsVector[i].y+1, _pixelsVector[i].x ) = 0;
00230 _cellMap.at<uchar>( _pixelsVector[i].y, _pixelsVector[i].x+1 ) = 0;
00231 _cellMap.at<uchar>( _pixelsVector[i].y+1, _pixelsVector[i].x+1 ) = 0;
00232 }
00233 else {
00234 uchar cellNum = (unsigned int)transformedPixels[i].y*_nc + (unsigned int)transformedPixels[i].x;
00235 _cellMap.at<uchar>( _pixelsVector[i].y, _pixelsVector[i].x ) = 1+cellNum;
00236 _cellMap.at<uchar>( _pixelsVector[i].y+1, _pixelsVector[i].x ) = 1+cellNum;
00237 _cellMap.at<uchar>( _pixelsVector[i].y, _pixelsVector[i].x+1 ) = 1+cellNum;
00238 _cellMap.at<uchar>( _pixelsVector[i].y+1, _pixelsVector[i].x+1 ) = 1+cellNum;
00239 }
00240 }
00241
00242 }
00243
00244
00245
00248 void ChromaticMask::train(const cv::Mat& in, const aruco::Board &board)
00249 {
00250 calculateGridImage(board);
00251
00252 for(unsigned int i=0; i<_classifiers.size(); i++) _classifiers[i].clearSamples();
00253
00254 for(unsigned int i=0; i<in.rows; i++) {
00255 for(unsigned int j=0; j<in.cols; j++) {
00256 uchar idx = _cellMap.at<uchar>(i,j);
00257 if(idx!=0) _classifiers[idx-1].addSample( in.at<uchar>(i,j) );
00258 }
00259 }
00260
00261 for(unsigned int i=0; i<_classifiers.size(); i++) _classifiers[i].train();
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288 _isValid = true;
00289
00290 }
00291
00292
00295 void ChromaticMask::classify(const cv::Mat& in, const aruco::Board &board)
00296 {
00297 calculateGridImage(board);
00298
00299 resetMask();
00300
00301 for(unsigned int i=0; i<in.rows; i++) {
00302 const uchar* in_ptr = in.ptr<uchar>(i);
00303 const uchar* _cellMap_ptr = _cellMap.ptr<uchar>(i);
00304 for(unsigned int j=0; j<in.cols; j++) {
00305 uchar idx = _cellMap_ptr[j];
00306 if(idx!=0) {
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 if(_classifiers[idx-1].classify( in.at<uchar>(i,j) )) _mask.at<uchar>(i,j)=1;
00322
00323
00324 }
00325 }
00326 }
00327
00328
00329 cv::Mat maskClose;
00330 cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3));
00331 cv::morphologyEx(_mask, maskClose, CV_MOP_CLOSE, element);
00332 _mask = maskClose;
00333
00334 }
00335
00336 cv::Rect fitRectToSize(cv::Rect r,cv::Size size){
00337
00338 r.x=max(r.x,0);
00339 r.y=max(r.y,0);
00340 int endx=r.x+r.width;
00341 int endy=r.y+r.height;
00342 endx=min(endx,size.width);
00343 endy=min(endy,size.height);
00344 r.width=endx-r.x;
00345 r.height=endy-r.y;
00346 return r;
00347
00348 }
00349
00352 void ChromaticMask::classify2(const cv::Mat& in, const aruco::Board &board)
00353 {
00354
00355 _mask.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00356 _maskAux.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00357 _maskAux.setTo(cv::Scalar::all(0));
00358
00359 cv::projectPoints(_objCornerPoints, board.Rvec, board.Tvec, _CP.CameraMatrix, _CP.Distorsion, _imgCornerPoints);
00360
00361 cv::Point2f pointsRes[4],pointsIn[4];
00362 for ( int i=0;i<4;i++ ) pointsIn[i]=_imgCornerPoints[i];
00363
00364 pointsRes[0]= ( cv::Point2f ( 0,0 ) );
00365 pointsRes[1]= cv::Point2f ( _mc-1, 0 );
00366 pointsRes[2]= cv::Point2f ( _mc-1, _nc-1 );
00367 pointsRes[3]= cv::Point2f ( 0, _nc-1 );
00368 _perpTrans=cv::getPerspectiveTransform ( pointsIn,pointsRes );
00369
00370 cv::Mat pT_32;
00371 _perpTrans.convertTo(pT_32,CV_32F);
00372
00373
00374
00375 cv::Rect r = cv::boundingRect(_imgCornerPoints);
00376 r=fitRectToSize(r,in.size());
00377 float *H=pT_32.ptr<float>(0);
00378
00379 int ny=0;
00380 for(unsigned int y=r.y; y<r.y+r.height; y+=2,ny++) {
00381 const uchar* in_ptr = in.ptr<uchar>(y);
00382 uchar *_mask_ptr=_maskAux.ptr<uchar>(y);
00383 int startx=r.x+ny%2;
00384 for(unsigned int x=startx; x<r.x+r.width; x+=2) {
00385 cv::Point2f point;
00386 float _inv_pointz = 1./ (x*H[6] + y*H[7] + H[8]);
00387 point.x =_inv_pointz*( x*H[0] + y*H[1] + H[2]);
00388 point.y =_inv_pointz*( x*H[3] + y*H[4] + H[5]);
00389 cv::Point2i c;
00390 c.x = int(point.x+0.5);
00391 c.y = int(point.y+0.5);
00392 if(c.x<0 || c.x>_mc-1 || c.y<0 || c.y>_nc-1) continue;
00393 size_t cell_idx=c.y*_mc+ c.x;
00394 float prob=0.0,totalW=0.0,dist,w;
00395
00396 for(unsigned int k=0; k<_cell_neighbours[cell_idx].size(); k++) {
00397 dist = fabs(point.x-_centers[k].x)+fabs( point.y-_centers[k].y);
00398 w= (2-dist);
00399 w*=w;
00400
00401 totalW += w;
00402 prob += w*_classifiers[ _cell_neighbours[cell_idx][k] ] .getProb(in_ptr[x] );
00403 }
00404 prob /= totalW;
00405
00406 if(prob > _threshProb) {
00407 _mask_ptr[x]=1;
00408 }
00409 }
00410 }
00411
00412
00413 cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3));
00414 cv::morphologyEx(_maskAux,_mask, CV_MOP_CLOSE, element);
00415 }
00416
00417
00418
00419 void ChromaticMask::update(const cv::Mat& in)
00420 {
00421 cv::Mat maskCells;
00422 maskCells = _cellMap.mul(_mask);
00423
00424 for(unsigned int i=0; i<_classifiers.size(); i++) _classifiers[i].clearSamples();
00425
00426 for(unsigned int i=0; i<maskCells.rows; i++) {
00427 uchar* maskCells_ptr = maskCells.ptr<uchar>(i);
00428 const uchar* in_ptr = in.ptr<uchar>(i);
00429 for(unsigned int j=0; j<maskCells.cols; j++) {
00430 if(maskCells_ptr[j]!=0) _classifiers[maskCells_ptr[j]-1].addSample( in_ptr[j] );
00431 }
00432 }
00433
00434 for(unsigned int i=0; i<_classifiers.size(); i++)
00435 if(_classifiers[i].numsamples() > 50) {
00436 _classifiers[i].train();
00437 }
00438
00439
00440
00441 }
00442
00443
00444
00445 void ChromaticMask::resetMask()
00446 {
00447
00448 _mask.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00449 _mask.setTo(cv::Scalar::all(0));
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471