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 "chromaticmask.h"
00030 #include <set>
00031
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
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
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
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
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
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
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
00228 cv::projectPoints(_objCornerPoints, board.Rvec, board.Tvec, _CP.CameraMatrix, _CP.Distorsion, _imgCornerPoints);
00229
00230
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
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
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
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
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348 if (_classifiers[idx - 1].classify(in.at< uchar >(i, j)))
00349 _mask.at< uchar >(i, j) = 1;
00350
00351 }
00352 }
00353 }
00354
00355
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
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);
00397
00398
00399
00400 cv::Rect r = cv::boundingRect(_imgCornerPoints);
00401 r = fitRectToSize(r, in.size());
00402 float *H = pT_32.ptr< float >(0);
00403
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;
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;
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
00427 totalW += w;
00428 prob += w * _classifiers[_cell_neighbours[cell_idx][k]].getProb(in_ptr[x]);
00429 }
00430 prob /= totalW;
00431
00432 if (prob > _threshProb) {
00433 _mask_ptr[x] = 1;
00434 }
00435 }
00436 }
00437
00438
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 }