chromaticmask.cpp
Go to the documentation of this file.
00001 /*****************************
00002 Copyright 2011 Rafael Muñoz Salinas. All rights reserved.
00003 
00004 Redistribution and use in source and binary forms, with or without modification, are
00005 permitted provided that the following conditions are met:
00006 
00007    1. Redistributions of source code must retain the above copyright notice, this list of
00008       conditions and the following disclaimer.
00009 
00010    2. Redistributions in binary form must reproduce the above copyright notice, this list
00011       of conditions and the following disclaimer in the documentation and/or other materials
00012       provided with the distribution.
00013 
00014 THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED
00015 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
00016 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR
00017 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00018 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00019 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00020 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00021 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00022 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00023 
00024 The views and conclusions contained in the software and documentation are those of the
00025 authors and should not be interpreted as representing official policies, either expressed
00026 or implied, of Rafael Muñoz Salinas.
00027 ********************************/
00028 
00029 #include "chromaticmask.h"
00030 #include <set>
00031 // #include <omp.h>
00032 
00033 
00036 EMClassifier::EMClassifier(unsigned int nelements) {
00037 #ifdef OPENCV_VERSION_3
00038     _classifier = cv::ml::EM::create();
00039     _classifier->setTermCriteria(cv::TermCriteria(cv::TermCriteria::COUNT, 4, FLT_EPSILON));
00040     _classifier->setClustersNumber(2);
00041     _classifier->setCovarianceMatrixType(cv::ml::EM::COV_MAT_DIAGONAL);
00042 #else
00043     _classifier = cv::EM(2, cv::EM::COV_MAT_DIAGONAL, cv::TermCriteria(cv::TermCriteria::COUNT , 4, FLT_EPSILON));
00044 #endif
00045     _nelem = nelements;
00046     _threshProb = 0.0001;
00047     for (unsigned int i = 0; i < 256; i++)
00048         _prob[i] = 0.5;
00049 }
00050 
00051 
00052 
00055 void EMClassifier::train() {
00056 
00057     // fill histogram
00058 
00059     for (unsigned int i = 0; i < 256; i++)
00060         _histogram[i] = 0;
00061 
00062     for (unsigned int i = 0; i < _samples.size(); i++) {
00063         uchar val = _samples[i];
00064         _histogram[val] += 3;
00065         if (val > 0)
00066             _histogram[val - 1] += 2;
00067         if (val < 255)
00068             _histogram[val + 1] += 2;
00069         if (val > 1)
00070             _histogram[val - 2] += 1;
00071         if (val < 254)
00072             _histogram[val + 2] += 1;
00073     }
00074 
00075     double sum = 0.;
00076     for (unsigned int i = 0; i < 256; i++)
00077         sum += _histogram[i];
00078     for (unsigned int i = 0; i < 256; i++)
00079         _histogram[i] /= sum;
00080 
00081 
00082     // discretize histogram to speed up training
00083     unsigned int histCount[256];
00084     int n = 0;
00085     for (unsigned int i = 0; i < 256; i++)
00086         n += (histCount[i] = (unsigned int)(_nelem * _histogram[i]));
00087 
00088     if (n < 10)
00089         return;
00090 
00091     cv::Mat samples(n, 1, CV_64FC1);
00092     unsigned int idx = 0;
00093     for (unsigned int i = 0; i < 256; i++) {
00094         for (unsigned int j = 0; j < histCount[i]; j++) {
00095             samples.ptr< double >(0)[idx] = i;
00096             idx++;
00097         }
00098     }
00099 
00100 #ifdef OPENCV_VERSION_3
00101     _classifier->trainEM(samples);
00102 #else
00103     _classifier.train(samples);
00104 #endif
00105 
00106     cv::Mat sampleAux(1, 1, CV_64FC1);
00107     for (unsigned int i = 0; i < 256; i++) {
00108         sampleAux.ptr< double >(0)[0] = i;
00109         cv::Mat probs;
00110 #ifdef OPENCV_VERSION_3
00111         cv::Vec2f r = _classifier->predict2(sampleAux, probs);
00112 #else
00113         cv::Vec2d r = _classifier.predict(sampleAux);
00114 #endif
00115         _prob[i] = exp(r[0]);
00116         if (_prob[i] > _threshProb)
00117             _inside[i] = true;
00118         else
00119             _inside[i] = false;
00120     }
00121 }
00122 
00123 
00124 void ChromaticMask::setParams(unsigned int mc, unsigned int nc, double threshProb, aruco::CameraParameters CP, aruco::BoardConfiguration BC, float markersize) {
00125     if (BC.mInfoType!=aruco::BoardConfiguration::METERS && markersize == -1) {
00126         std::cerr << "Invalid markersize in ChromaticMask::setParams" << std::endl;
00127         return;
00128     }
00129 
00130     if (BC.size() == 0) {
00131         std::cerr << "Invalid BoardConfiguration size in ChromaticMask::setParams" << std::endl;
00132         return;
00133     }
00134 
00135     if(BC.mInfoType==aruco::BoardConfiguration::METERS) {
00136         markersize = cv::norm(BC[0][0]-BC[0][1]);
00137     }
00138 
00139     // calculate corners from min and max in BC
00140     cv::Point3f min, max;
00141     min = max = BC[0][0];
00142     for (unsigned int i = 0; i < BC.size(); i++) {
00143         for (unsigned int j = 0; j < 4; j++) {
00144             if (BC[i][j].x <= min.x && BC[i][j].y <= min.y)
00145                 min = BC[i][j];
00146             if (BC[i][j].x >= max.x && BC[i][j].y >= max.y)
00147                 max = BC[i][j];
00148         }
00149     }
00150     double pixSize = fabs(markersize / (BC[0][1].x - BC[0][0].x));
00151     min.x *= pixSize;
00152     min.y *= pixSize;
00153     max.x *= pixSize;
00154     max.y *= pixSize;
00155 
00156     // calculate border corners coordinates
00157     vector< cv::Point3f > corners;
00158     corners.push_back(min);
00159     corners.push_back(cv::Point3f(min.x, max.y, 0));
00160     corners.push_back(max);
00161     corners.push_back(cv::Point3f(max.x, min.y, 0));
00162 
00163     setParams(mc, nc, threshProb, CP, BC, corners);
00164 }
00165 
00166 
00167 
00170 void ChromaticMask::setParams(unsigned int mc, unsigned int nc, double threshProb, aruco::CameraParameters CP, aruco::BoardConfiguration BC,
00171                               vector< cv::Point3f > corners) {
00172     _classifiers.resize(mc * nc);
00173     for (unsigned int i = 0; i < _classifiers.size(); i++)
00174         _classifiers[i].setProb(threshProb);
00175     _mc = mc;
00176     _nc = nc;
00177     _threshProb = threshProb;
00178     _cell_neighbours.resize(mc * nc);
00179     int idx_ = 0;
00180     // SGJ: revisar, no engo claro sepa bien que es i y j y los puedo estar confundiendo!!!
00181     for (unsigned int j = 0; j < nc; j++)
00182         for (unsigned int i = 0; i < mc; i++, idx_++) {
00183             _centers.push_back(cv::Point2f(i + 0.5, j + 0.5));
00184             for (unsigned int nj = max(j - 1, (unsigned int)0); nj < min(mc, j + 1); nj++) {
00185                 for (unsigned int ni = max(i - 1, (unsigned int)0); ni < min(mc, i + 1); ni++) {
00186                     size_t tidx = nj * mc + ni;
00187                     _cell_neighbours[idx_].push_back(tidx);
00188                 }
00189             }
00190         }
00191 
00192     // deterimne the idx of the neighbours
00193     _BD.getMarkerDetector().setThresholdParams(35, 7);
00194     _BD.getMarkerDetector().enableErosion(false);
00195     _BD.getMarkerDetector().setCornerRefinementMethod(aruco::MarkerDetector::LINES);
00196     _BD.setYPerpendicular(false);
00197 
00198     _BD.setParams(BC, CP);
00199 
00200     _objCornerPoints = corners;
00201     _CP = CP;
00202 
00203     _pixelsVector.clear();
00204     for (unsigned int i = 0; i < CP.CamSize.height / 2; i++)
00205         for (unsigned int j = 0; j < CP.CamSize.width / 2; j++)
00206             _pixelsVector.push_back(cv::Point2f(2 * j, 2 * i));
00207 
00208     resetMask();
00209     _cellMap = cv::Mat(CP.CamSize.height, CP.CamSize.width, CV_8UC1, cv::Scalar::all(0));
00210     _canonicalPos = cv::Mat(CP.CamSize.height, CP.CamSize.width, CV_8UC2);
00211 
00212     _cellCenters.resize(_classifiers.size());
00213     for (unsigned int i = 0; i < mc; i++) {
00214         for (unsigned int j = 0; j < nc; j++) {
00215             _cellCenters[i * mc + j].x = ((2 * i + 1) * _cellSize) / 2.;
00216             _cellCenters[i * mc + j].y = ((2 * j + 1) * _cellSize) / 2.;
00217         }
00218     }
00219 }
00220 
00221 pair< double, double > AvrgTime(0, 0);
00222 
00225 void ChromaticMask::calculateGridImage(const aruco::Board &board) {
00226 
00227     // project corner points to image
00228     cv::projectPoints(_objCornerPoints, board.Rvec, board.Tvec, _CP.CameraMatrix, _CP.Distorsion, _imgCornerPoints);
00229 
00230     // obtain the perspective transform
00231     cv::Point2f pointsRes[4], pointsIn[4];
00232     for (int i = 0; i < 4; i++)
00233         pointsIn[i] = _imgCornerPoints[i];
00234     pointsRes[0] = (cv::Point2f(0, 0));
00235     pointsRes[1] = cv::Point2f(_cellSize * _mc - 1, 0);
00236     pointsRes[2] = cv::Point2f(_cellSize * _mc - 1, _cellSize * _nc - 1);
00237     pointsRes[3] = cv::Point2f(0, _cellSize * _nc - 1);
00238     _perpTrans = cv::getPerspectiveTransform(pointsIn, pointsRes);
00239 
00240     double tick = (double)cv::getTickCount();
00241 
00242     vector< cv::Point2f > transformedPixels;
00243     cv::perspectiveTransform(_pixelsVector, transformedPixels, _perpTrans);
00244 
00245     AvrgTime.first += ((double)cv::getTickCount() - tick) / cv::getTickFrequency();
00246     AvrgTime.second++;
00247     cout << "Time cc detection=" << 1000 * AvrgTime.first / AvrgTime.second << " milliseconds" << endl;
00248 
00249 
00250 
00251     cv::Rect cellRect(0, 0, _mc, _nc);
00252     for (unsigned int i = 0; i < transformedPixels.size(); i++) {
00253         //_canonicalPos.at<cv::Vec2b>(_pixelsVector[i].y, _pixelsVector[i].x) = cv::Vec2b(transformedPixels[i].x, transformedPixels[i].y);
00254         transformedPixels[i].x /= _cellSize;
00255         transformedPixels[i].y /= _cellSize;
00256         if (!transformedPixels[i].inside(cellRect)) {
00257             _cellMap.at< uchar >(_pixelsVector[i].y, _pixelsVector[i].x) = 0;
00258             _cellMap.at< uchar >(_pixelsVector[i].y + 1, _pixelsVector[i].x) = 0;
00259             _cellMap.at< uchar >(_pixelsVector[i].y, _pixelsVector[i].x + 1) = 0;
00260             _cellMap.at< uchar >(_pixelsVector[i].y + 1, _pixelsVector[i].x + 1) = 0;
00261         } else {
00262             uchar cellNum = (unsigned int)transformedPixels[i].y * _nc + (unsigned int)transformedPixels[i].x;
00263             _cellMap.at< uchar >(_pixelsVector[i].y, _pixelsVector[i].x) = 1 + cellNum;
00264             _cellMap.at< uchar >(_pixelsVector[i].y + 1, _pixelsVector[i].x) = 1 + cellNum;
00265             _cellMap.at< uchar >(_pixelsVector[i].y, _pixelsVector[i].x + 1) = 1 + cellNum;
00266             _cellMap.at< uchar >(_pixelsVector[i].y + 1, _pixelsVector[i].x + 1) = 1 + cellNum;
00267         }
00268     }
00269 }
00270 
00271 
00272 
00275 void ChromaticMask::train(const cv::Mat &in, const aruco::Board &board) {
00276     calculateGridImage(board);
00277 
00278     for (unsigned int i = 0; i < _classifiers.size(); i++)
00279         _classifiers[i].clearSamples();
00280 
00281     for (unsigned int i = 0; i < in.rows; i++) {
00282         for (unsigned int j = 0; j < in.cols; j++) {
00283             uchar idx = _cellMap.at< uchar >(i, j);
00284             if (idx != 0)
00285                 _classifiers[idx - 1].addSample(in.at< uchar >(i, j));
00286         }
00287     }
00288 
00289     for (unsigned int i = 0; i < _classifiers.size(); i++)
00290         _classifiers[i].train();
00291 
00292 
00293     //   for(uint i=0; i<_mc; i++) {
00294     //     for(uint j=0; j<_nc; j++) {
00295     //       vector<uint> neighbors;
00296     //       for(int k=-1; k<=1; k++) {
00297     //  if((i+k)<0 || (i+k)>=_mc) continue;
00298     //  for(int l=-1; l<=1; l++) {
00299     //    if((j+l)<0 || (j+l)>=_nc) continue;
00300     //    neighbors.push_back((i+k)*_mc + (j+l));
00301     //  }
00302     //       }
00303     //
00304     //
00305     //       for(uint l=0; l<256; l++) {
00306     //  _classifiers[i*_mc+j].probConj[l] = 0.;
00307     //  for(uint k=0; k<neighbors.size(); k++) {
00308     //    _classifiers[i*_mc+j].probConj[l] +=  _classifiers[k].getProb(l);
00309     //  }
00310     //  _classifiers[i*_mc+j].probConj[l] +=  _classifiers[i*_mc+j].getProb(l); // center prob x2
00311     //  _classifiers[i*_mc+j].probConj[l] / (float)(neighbors.size()+1);
00312     //       }
00313     //
00314     //     }
00315     //   }
00316 
00317     _isValid = true;
00318 }
00319 
00320 
00323 void ChromaticMask::classify(const cv::Mat &in, const aruco::Board &board) {
00324     calculateGridImage(board);
00325 
00326     resetMask();
00327 
00328     for (unsigned int i = 0; i < in.rows; i++) {
00329         const uchar *in_ptr = in.ptr< uchar >(i);
00330         const uchar *_cellMap_ptr = _cellMap.ptr< uchar >(i);
00331         for (unsigned int j = 0; j < in.cols; j++) {
00332             uchar idx = _cellMap_ptr[j];
00333             if (idx != 0) {
00334 
00335                 // considering neighbours
00336                 //      float prob=0.0;
00337                 //      float totalW = 0.0;
00338                 //      cv::Point2d pix(i,j);
00339                 //      for(uint k=0; k<_classifiers.size()-17; k++) {
00340                 //        float w = 1./(1.+getDistance(pix,k));
00341                 //        totalW += w;
00342                 //        prob += w*_classifiers[k].getProb(in_ptr[j] );
00343                 //      }
00344                 //      prob /= totalW;
00345                 //      if(prob > _threshProb) _mask.at<uchar>(i,j)=1;
00346 
00347                 // not considering neighbours
00348                 if (_classifiers[idx - 1].classify(in.at< uchar >(i, j)))
00349                     _mask.at< uchar >(i, j) = 1;
00350                 //      if(_classifiers[idx-1].probConj[ in.at<uchar>(i,j)]>_threshProb ) _mask.at<uchar>(i,j)=1;
00351             }
00352         }
00353     }
00354 
00355     // apply closing to mask
00356     cv::Mat maskClose;
00357     cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
00358     cv::morphologyEx(_mask, maskClose, CV_MOP_CLOSE, element);
00359     _mask = maskClose;
00360 }
00361 
00362 cv::Rect fitRectToSize(cv::Rect r, cv::Size size) {
00363 
00364     r.x = max(r.x, 0);
00365     r.y = max(r.y, 0);
00366     int endx = r.x + r.width;
00367     int endy = r.y + r.height;
00368     endx = min(endx, size.width);
00369     endy = min(endy, size.height);
00370     r.width = endx - r.x;
00371     r.height = endy - r.y;
00372     return r;
00373 }
00374 
00377 void ChromaticMask::classify2(const cv::Mat &in, const aruco::Board &board) {
00378 
00379     _mask.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00380     _maskAux.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00381     _maskAux.setTo(cv::Scalar::all(0));
00382 
00383     cv::projectPoints(_objCornerPoints, board.Rvec, board.Tvec, _CP.CameraMatrix, _CP.Distorsion, _imgCornerPoints);
00384     // obtain the perspective transform
00385     cv::Point2f pointsRes[4], pointsIn[4];
00386     for (int i = 0; i < 4; i++)
00387         pointsIn[i] = _imgCornerPoints[i];
00388 
00389     pointsRes[0] = (cv::Point2f(0, 0));
00390     pointsRes[1] = cv::Point2f(_mc - 1, 0);
00391     pointsRes[2] = cv::Point2f(_mc - 1, _nc - 1);
00392     pointsRes[3] = cv::Point2f(0, _nc - 1);
00393     _perpTrans = cv::getPerspectiveTransform(pointsIn, pointsRes);
00394 
00395     cv::Mat pT_32;
00396     _perpTrans.convertTo(pT_32, CV_32F); // RMS: CAMBIA A FLOAT
00397 
00398     //       cout << _perpTrans << endl;
00399 
00400     cv::Rect r = cv::boundingRect(_imgCornerPoints);
00401     r = fitRectToSize(r, in.size()); // fit rectangle to image limits
00402     float *H = pT_32.ptr< float >(0);
00403     // #pragma omp parallel for
00404     int ny = 0;
00405     for (unsigned int y = r.y; y < r.y + r.height; y += 2, ny++) {
00406         const uchar *in_ptr = in.ptr< uchar >(y);
00407         uchar *_mask_ptr = _maskAux.ptr< uchar >(y);
00408         int startx = r.x + ny % 2; // alternate starting point
00409         for (unsigned int x = startx; x < r.x + r.width; x += 2) {
00410             cv::Point2f point;
00411             float _inv_pointz = 1. / (x * H[6] + y * H[7] + H[8]);
00412             point.x = _inv_pointz * (x * H[0] + y * H[1] + H[2]);
00413             point.y = _inv_pointz * (x * H[3] + y * H[4] + H[5]);
00414             cv::Point2i c;
00415             c.x = int(point.x + 0.5);
00416             c.y = int(point.y + 0.5);
00417             if (c.x < 0 || c.x > _mc - 1 || c.y < 0 || c.y > _nc - 1)
00418                 continue;
00419             size_t cell_idx = c.y * _mc + c.x; // SGJ: revisar si esto esta bien!!
00420             float prob = 0.0, totalW = 0.0, dist, w;
00421 
00422             for (unsigned int k = 0; k < _cell_neighbours[cell_idx].size(); k++) {
00423                 dist = fabs(point.x - _centers[k].x) + fabs(point.y - _centers[k].y);
00424                 w = (2 - dist);
00425                 w *= w;
00426                 //          w=1/(1- sqrt( (point.x-_centers[k].x)*(point.x-_centers[k].x) + (point.y-_centers[k].y)*(point.y-_centers[k].y));
00427                 totalW += w;
00428                 prob += w * _classifiers[_cell_neighbours[cell_idx][k]].getProb(in_ptr[x]);
00429             }
00430             prob /= totalW;
00431             //    prob/=float(_classifiers.size()-17);
00432             if (prob > _threshProb) {
00433                 _mask_ptr[x] = 1;
00434             }
00435         }
00436     }
00437     //     // apply closing to mask
00438     // _mask=_maskAux;
00439     cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
00440     cv::morphologyEx(_maskAux, _mask, CV_MOP_CLOSE, element);
00441 }
00442 
00443 
00444 
00445 void ChromaticMask::update(const cv::Mat &in) {
00446     cv::Mat maskCells;
00447     maskCells = _cellMap.mul(_mask);
00448 
00449     for (unsigned int i = 0; i < _classifiers.size(); i++)
00450         _classifiers[i].clearSamples();
00451 
00452     for (unsigned int i = 0; i < maskCells.rows; i++) {
00453         uchar *maskCells_ptr = maskCells.ptr< uchar >(i);
00454         const uchar *in_ptr = in.ptr< uchar >(i);
00455         for (unsigned int j = 0; j < maskCells.cols; j++) {
00456             if (maskCells_ptr[j] != 0)
00457                 _classifiers[maskCells_ptr[j] - 1].addSample(in_ptr[j]);
00458         }
00459     }
00460 
00461     for (unsigned int i = 0; i < _classifiers.size(); i++)
00462         if (_classifiers[i].numsamples() > 50) {
00463             _classifiers[i].train();
00464         }
00465 }
00466 
00467 
00468 
00469 void ChromaticMask::resetMask() {
00470 
00471     _mask.create(_CP.CamSize.height, _CP.CamSize.width, CV_8UC1);
00472     _mask.setTo(cv::Scalar::all(0));
00473 }


asr_aruco_marker_recognition
Author(s): Allgeyer Tobias, Meißner Pascal, Qattan Mohamad
autogenerated on Thu Jun 6 2019 21:14:12