1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4
5 /*
6 * MIT License
7 *
8 * Copyright (c) 2018 Pedro Diamel Marrero Fernández
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 #include "precomp.hpp"
30
31 #include "checker_detector.hpp"
32 #include "graph_cluster.hpp"
33 #include "bound_min.hpp"
34 #include "wiener_filter.hpp"
35 #include "checker_model.hpp"
36 #include "debug.hpp"
37
38 namespace cv
39 {
40 namespace mcc
41 {
42
43 std::mutex mtx; // mutex for critical section
44
create()45 Ptr<CCheckerDetector> CCheckerDetector::create()
46 {
47 return makePtr<CCheckerDetectorImpl>();
48 }
49
50 CCheckerDetectorImpl::
CCheckerDetectorImpl()51 CCheckerDetectorImpl()
52
53 {
54 }
55
~CCheckerDetectorImpl()56 CCheckerDetectorImpl::~CCheckerDetectorImpl()
57 {
58 }
59
60 bool CCheckerDetectorImpl::
setNet(cv::dnn::Net _net)61 setNet(cv::dnn::Net _net)
62 {
63 net = _net;
64 return !net.empty();
65 }
66
67 bool CCheckerDetectorImpl::
_no_net_process(InputArray image,const TYPECHART chartType,const int nc,const Ptr<DetectorParameters> & params,std::vector<cv::Rect> regionsOfInterest)68 _no_net_process(InputArray image, const TYPECHART chartType, const int nc,
69 const Ptr<DetectorParameters> ¶ms,
70 std::vector<cv::Rect> regionsOfInterest)
71 {
72 m_checkers.clear();
73 this->net_used = false;
74
75 cv::Mat img = image.getMat();
76 for (const cv::Rect ®ion : regionsOfInterest)
77 {
78 //-------------------------------------------------------------------
79 // Run the model to find good regions
80 //-------------------------------------------------------------------
81 cv::Mat croppedImage = img(region);
82 #ifdef MCC_DEBUG
83 std::string pathOut = "./";
84 #endif
85 //-------------------------------------------------------------------
86 // prepare image
87 //-------------------------------------------------------------------
88
89 cv::Mat img_bgr, img_gray;
90 float asp;
91 prepareImage(croppedImage, img_gray, img_bgr, asp, params);
92
93 #ifdef MCC_DEBUG
94 showAndSave("prepare_image", img_gray, pathOut);
95 #endif
96 //-------------------------------------------------------------------
97 // thresholding
98 //-------------------------------------------------------------------
99 std::vector<cv::Mat> img_bw;
100 performThreshold(img_gray, img_bw, params);
101
102 cv::Mat3f img_rgb_f(img_bgr);
103 cv::cvtColor(img_rgb_f, img_rgb_f, COLOR_BGR2RGB);
104 img_rgb_f /= 255;
105
106 cv::Mat img_rgb_org, img_ycbcr_org;
107 std::vector<cv::Mat> rgb_planes(3), ycbcr_planes(3);
108
109 // Convert to RGB and YCbCr space
110 cv::cvtColor(croppedImage, img_rgb_org, COLOR_BGR2RGB);
111 cv::cvtColor(croppedImage, img_ycbcr_org, COLOR_BGR2YCrCb);
112
113 // Get chanels
114 split(img_rgb_org, rgb_planes);
115 split(img_ycbcr_org, ycbcr_planes);
116
117 parallel_for_(
118 Range(0, (int)img_bw.size()), [&](const Range &range) {
119 const int begin = range.start;
120 const int end = range.end;
121 for (int i = begin; i < end; i++)
122 {
123
124 #ifdef MCC_DEBUG
125 showAndSave("threshold_image", img_bw[i], pathOut);
126 #endif
127 // find contour
128 //-------------------------------------------------------------------
129 ContoursVector contours;
130 findContours(img_bw[i], contours, params);
131
132 if (contours.empty())
133 continue;
134 #ifdef MCC_DEBUG
135 cv::Mat im_contour(img_bgr.size(), CV_8UC1);
136 im_contour = cv::Scalar(0);
137 cv::drawContours(im_contour, contours, -1, cv::Scalar(255), 2, LINE_AA);
138 showAndSave("find_contour", im_contour, pathOut);
139 #endif
140 //-------------------------------------------------------------------
141 // find candidate
142 //-------------------------------------------------------------------
143
144 std::vector<CChart> detectedCharts;
145 findCandidates(contours, detectedCharts, params);
146
147 if (detectedCharts.empty())
148 continue;
149
150 #ifdef MCC_DEBUG
151 cv::Mat img_chart;
152 img_bgr.copyTo(img_chart);
153
154 for (size_t ind = 0; ind < detectedCharts.size(); ind++)
155 {
156
157 CChartDraw chrtdrw((detectedCharts[ind]), img_chart);
158 chrtdrw.drawCenter();
159 chrtdrw.drawContour();
160 }
161 showAndSave("find_candidate", img_chart, pathOut);
162 #endif
163 //-------------------------------------------------------------------
164 // clusters analysis
165 //-------------------------------------------------------------------
166
167 std::vector<int> G;
168 clustersAnalysis(detectedCharts, G, params);
169
170 if (G.empty())
171 continue;
172
173 #ifdef MCC_DEBUG
174 cv::Mat im_gru;
175 img_bgr.copyTo(im_gru);
176 RNG rng(0xFFFFFFFF);
177 int radius = 10, thickness = -1;
178
179 std::vector<int> g;
180 unique(G, g);
181 size_t Nc = g.size();
182 std::vector<cv::Scalar> colors(Nc);
183 for (size_t ind = 0; ind < Nc; ind++)
184 colors[ind] = randomcolor(rng);
185
186 for (size_t ind = 0; ind < detectedCharts.size(); ind++)
187 cv::circle(im_gru, detectedCharts[ind].center, radius, colors[G[ind]],
188 thickness);
189 showAndSave("clusters_analysis", im_gru, pathOut);
190 #endif
191 //-------------------------------------------------------------------
192 // checker color recognize
193 //-------------------------------------------------------------------
194
195 std::vector<std::vector<cv::Point2f>> colorCharts;
196 checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts, params);
197
198 if (colorCharts.empty())
199 continue;
200
201 #ifdef MCC_DEBUG
202 cv::Mat image_box;
203 img_bgr.copyTo(image_box);
204 for (size_t ind = 0; ind < colorCharts.size(); ind++)
205 {
206 std::vector<cv::Point2f> ibox = colorCharts[ind];
207 cv::Scalar color_box = CV_RGB(0, 0, 255);
208 int thickness_box = 2;
209 cv::line(image_box, ibox[0], ibox[1], color_box, thickness_box, LINE_AA);
210 cv::line(image_box, ibox[1], ibox[2], color_box, thickness_box, LINE_AA);
211 cv::line(image_box, ibox[2], ibox[3], color_box, thickness_box, LINE_AA);
212 cv::line(image_box, ibox[3], ibox[0], color_box, thickness_box, LINE_AA);
213 //cv::circle(image_box, ibox[0], 10, cv::Scalar(0, 0, 255), 3);
214 //cv::circle(image_box, ibox[1], 10, cv::Scalar(0, 255, 0), 3);
215 }
216 showAndSave("checker_recognition", image_box, pathOut);
217 #endif
218 //-------------------------------------------------------------------
219 // checker color analysis
220 //-------------------------------------------------------------------
221 std::vector<Ptr<CChecker>> checkers;
222 checkerAnalysis(img_rgb_f, chartType, nc, colorCharts, checkers, asp, params,
223 img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes);
224
225 #ifdef MCC_DEBUG
226 cv::Mat image_checker;
227 croppedImage.copyTo(image_checker);
228 for (size_t ck = 0; ck < checkers.size(); ck++)
229 {
230 Ptr<CCheckerDraw> cdraw = CCheckerDraw::create((checkers[ck]));
231 cdraw->draw(image_checker);
232 }
233 showAndSave("checker_analysis", image_checker, pathOut);
234 #endif
235 for (Ptr<CChecker> checker : checkers)
236 {
237 for (cv::Point2f &corner : checker->getBox())
238 corner += static_cast<cv::Point2f>(region.tl());
239
240 mtx.lock(); // push_back is not thread safe
241 m_checkers.push_back(checker);
242 mtx.unlock();
243 }
244 }
245 #ifdef MCC_DEBUG
246 },
247 1); //Run only one thread in debug mode
248 #else
249 });
250 #endif
251 }
252 //remove too close detections
253 removeTooCloseDetections(params);
254 m_checkers.resize(min(nc, (int)m_checkers.size()));
255 return !m_checkers.empty();
256 }
257
258 bool CCheckerDetectorImpl::
process(InputArray image,const TYPECHART chartType,const std::vector<cv::Rect> & regionsOfInterest,const int nc,bool useNet,const Ptr<DetectorParameters> & params)259 process(InputArray image, const TYPECHART chartType,const std::vector<cv::Rect> ®ionsOfInterest,
260 const int nc /*= 1*/, bool useNet /*=false*/, const Ptr<DetectorParameters> ¶ms)
261 {
262 m_checkers.clear();
263
264 if (this->net.empty() || !useNet)
265 {
266 return _no_net_process(image, chartType, nc, params, regionsOfInterest);
267 }
268 this->net_used = true;
269
270 cv::Mat img = image.getMat();
271
272 cv::Mat img_rgb_org, img_ycbcr_org;
273 std::vector<cv::Mat> rgb_planes(3), ycbcr_planes(3);
274
275 // Convert to RGB and YCbCr space
276 cv::cvtColor(img, img_rgb_org, COLOR_BGR2RGB);
277 cv::cvtColor(img, img_ycbcr_org, COLOR_BGR2YCrCb);
278
279 // Get chanels
280 split(img_rgb_org, rgb_planes);
281 split(img_ycbcr_org, ycbcr_planes);
282
283 for (const cv::Rect ®ion : regionsOfInterest)
284 {
285 //-------------------------------------------------------------------
286 // Run the model to find good regions
287 //-------------------------------------------------------------------
288
289 cv::Mat croppedImage = img(region);
290
291 int rows = croppedImage.size[0];
292 int cols = croppedImage.size[1];
293 net.setInput(cv::dnn::blobFromImage(croppedImage, 1.0, cv::Size(), cv::Scalar(), true));
294 cv::Mat output = net.forward();
295
296 Mat detectionMat(output.size[2], output.size[3], CV_32F, output.ptr<float>());
297
298 for (int i = 0; i < detectionMat.rows; i++)
299 {
300 float confidence = detectionMat.at<float>(i, 2);
301 if (confidence > params->confidenceThreshold)
302 {
303 float xTopLeft = max(0.0f, detectionMat.at<float>(i, 3) * cols - params->borderWidth);
304 float yTopLeft = max(0.0f, detectionMat.at<float>(i, 4) * rows - params->borderWidth);
305 float xBottomRight = min((float)cols - 1, detectionMat.at<float>(i, 5) * cols + params->borderWidth);
306 float yBottomRight = min((float)rows - 1, detectionMat.at<float>(i, 6) * rows + params->borderWidth);
307
308 cv::Point2f topLeft = {xTopLeft, yTopLeft};
309 cv::Point2f bottomRight = {xBottomRight, yBottomRight};
310
311 cv::Rect innerRegion(topLeft, bottomRight);
312 cv::Mat innerCroppedImage = croppedImage(innerRegion);
313
314 #ifdef MCC_DEBUG
315 std::string pathOut = "./";
316 #endif
317 //-------------------------------------------------------------------
318 // prepare image
319 //-------------------------------------------------------------------
320
321 cv::Mat img_bgr, img_gray;
322 float asp;
323 prepareImage(innerCroppedImage, img_gray, img_bgr, asp, params);
324
325 //-------------------------------------------------------------------
326 // thresholding
327 //-------------------------------------------------------------------
328
329 std::vector<cv::Mat> img_bw;
330 performThreshold(img_gray, img_bw, params);
331
332 cv::Mat3f img_rgb_f(img_bgr);
333 cv::cvtColor(img_rgb_f, img_rgb_f, COLOR_BGR2RGB);
334 img_rgb_f /= 255;
335
336 parallel_for_(
337 Range(0, (int)img_bw.size()), [&](const Range &range) {
338 const int begin = range.start;
339 const int end = range.end;
340
341 for (int ind = begin; ind < end; ind++)
342 {
343
344 #ifdef MCC_DEBUG
345 showAndSave("threshold_image", img_bw[ind], pathOut);
346 #endif
347 //------------------------------------------------------------------
348 // find contour
349 //-------------------------------------------------------------------
350 ContoursVector contours;
351 findContours(img_bw[ind], contours, params);
352
353 if (contours.empty())
354 continue;
355 #ifdef MCC_DEBUG
356 cv::Mat im_contour(img_bgr.size(), CV_8UC1);
357 im_contour = cv::Scalar(0);
358 cv::drawContours(im_contour, contours, -1, cv::Scalar(255), 2, LINE_AA);
359 showAndSave("find_contour", im_contour, pathOut);
360 #endif
361 //-------------------------------------------------------------------
362 // find candidate
363 //-------------------------------------------------------------------
364
365 std::vector<CChart> detectedCharts;
366 findCandidates(contours, detectedCharts, params);
367
368 if (detectedCharts.empty())
369 continue;
370
371 #ifdef MCC_DEBUG
372 cv::Mat img_chart;
373 img_bgr.copyTo(img_chart);
374
375 for (size_t index = 0; index < detectedCharts.size(); index++)
376 {
377
378 CChartDraw chrtdrw((detectedCharts[index]), img_chart);
379 chrtdrw.drawCenter();
380 chrtdrw.drawContour();
381 }
382 showAndSave("find_candidate", img_chart, pathOut);
383 #endif
384 //-------------------------------------------------------------------
385 // clusters analysis
386 //-------------------------------------------------------------------
387
388 std::vector<int> G;
389 clustersAnalysis(detectedCharts, G, params);
390
391 if (G.empty())
392 continue;
393 #ifdef MCC_DEBUG
394 cv::Mat im_gru;
395 img_bgr.copyTo(im_gru);
396 RNG rng(0xFFFFFFFF);
397 int radius = 10, thickness = -1;
398
399 std::vector<int> g;
400 unique(G, g);
401 size_t Nc = g.size();
402 std::vector<cv::Scalar> colors(Nc);
403 for (size_t index = 0; index < Nc; index++)
404 colors[index] = randomcolor(rng);
405
406 for (size_t index = 0; index < detectedCharts.size(); index++)
407 cv::circle(im_gru, detectedCharts[index].center, radius, colors[G[index]],
408 thickness);
409 showAndSave("clusters_analysis", im_gru, pathOut);
410 #endif
411
412 //-------------------------------------------------------------------
413 // checker color recognize
414 //-------------------------------------------------------------------
415
416 std::vector<std::vector<cv::Point2f>> colorCharts;
417 checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts, params);
418
419 if (colorCharts.empty())
420 continue;
421
422 #ifdef MCC_DEBUG
423 cv::Mat image_box;
424 img_bgr.copyTo(image_box);
425 for (size_t index = 0; index < colorCharts.size(); index++)
426 {
427 std::vector<cv::Point2f> ibox = colorCharts[index];
428 cv::Scalar color_box = CV_RGB(0, 0, 255);
429 int thickness_box = 2;
430 cv::line(image_box, ibox[0], ibox[1], color_box, thickness_box, LINE_AA);
431 cv::line(image_box, ibox[1], ibox[2], color_box, thickness_box, LINE_AA);
432 cv::line(image_box, ibox[2], ibox[3], color_box, thickness_box, LINE_AA);
433 cv::line(image_box, ibox[3], ibox[0], color_box, thickness_box, LINE_AA);
434 //cv::circle(image_box, ibox[0], 10, cv::Scalar(0, 0, 255), 3);
435 //cv::circle(image_box, ibox[1], 10, cv::Scalar(0, 255, 0), 3);
436 }
437 showAndSave("checker_recognition", image_box, pathOut);
438 #endif
439 //-------------------------------------------------------------------
440 // checker color analysis
441 //-------------------------------------------------------------------
442 std::vector<Ptr<CChecker>> checkers;
443 checkerAnalysis(img_rgb_f, chartType, nc, colorCharts, checkers, asp, params,
444 img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes);
445 #ifdef MCC_DEBUG
446 cv::Mat image_checker;
447 innerCroppedImage.copyTo(image_checker);
448 for (size_t ck = 0; ck < checkers.size(); ck++)
449 {
450 Ptr<CCheckerDraw> cdraw = CCheckerDraw::create((checkers[ck]));
451 cdraw->draw(image_checker);
452 }
453 showAndSave("checker_analysis", image_checker, pathOut);
454 #endif
455 for (Ptr<CChecker> checker : checkers)
456 {
457 for (cv::Point2f &corner : checker->getBox())
458 corner += static_cast<cv::Point2f>(region.tl() + innerRegion.tl());
459 mtx.lock(); // push_back is not thread safe
460 m_checkers.push_back(checker);
461 mtx.unlock();
462 }
463 }
464 #ifdef MCC_DEBUG
465 },
466 1); //Run only one thread in debug mode
467 #else
468 });
469 #endif
470 }
471 }
472 }
473 // As a failsafe try the classical method
474 if (m_checkers.empty())
475 {
476 return _no_net_process(image, chartType, nc, params, regionsOfInterest);
477 }
478 //remove too close detections
479 removeTooCloseDetections(params);
480
481 m_checkers.resize(min(nc, (int)m_checkers.size()));
482
483 return !m_checkers.empty();
484 }
485
486
487 //Overload for the above function
488 bool CCheckerDetectorImpl::
process(InputArray image,const TYPECHART chartType,const int nc,bool useNet,const Ptr<DetectorParameters> & params)489 process(InputArray image, const TYPECHART chartType,
490 const int nc /*= 1*/, bool useNet /*=false*/, const Ptr<DetectorParameters> ¶ms)
491 {
492 return process(image, chartType, std::vector<cv::Rect>(1, Rect(0, 0, image.cols(), image.rows())),
493 nc,useNet, params);
494 }
495
496
497 void CCheckerDetectorImpl::
prepareImage(InputArray bgr,OutputArray grayOut,OutputArray bgrOut,float & aspOut,const Ptr<DetectorParameters> & params) const498 prepareImage(InputArray bgr, OutputArray grayOut,
499 OutputArray bgrOut, float &aspOut,
500 const Ptr<DetectorParameters> ¶ms) const
501 {
502
503 int min_size;
504 cv::Size size = bgr.size();
505 aspOut = 1;
506 bgr.copyTo(bgrOut);
507
508 // Resize image
509 min_size = std::min(size.width, size.height);
510 if (params->minImageSize > min_size)
511 {
512 aspOut = (float)params->minImageSize / min_size;
513 cv::resize(bgr, bgrOut, cv::Size(int(size.width * aspOut), int(size.height * aspOut)));
514 }
515
516 // Convert to grayscale
517 cv::cvtColor(bgrOut, grayOut, COLOR_BGR2GRAY);
518
519 // PDiamel: wiener adaptative methods to minimize the noise effets
520 // by illumination
521
522 CWienerFilter filter;
523 filter.wiener2(grayOut, grayOut, 5, 5);
524
525 //JLeandro: perform morphological open on the equalized image
526 //to minimize the noise effects by CLAHE and to even intensities
527 //inside the MCC patches (regions)
528
529 cv::Mat strelbox = cv::getStructuringElement(cv::MORPH_RECT, Size(5, 5));
530 cv::morphologyEx(grayOut, grayOut, MORPH_OPEN, strelbox);
531 }
532
533 void CCheckerDetectorImpl::
performThreshold(InputArray grayscaleImg,OutputArrayOfArrays thresholdImgs,const Ptr<DetectorParameters> & params) const534 performThreshold(InputArray grayscaleImg,
535 OutputArrayOfArrays thresholdImgs,
536 const Ptr<DetectorParameters> ¶ms) const
537 {
538 // number of window sizes (scales) to apply adaptive thresholding
539 int nScales = (params->adaptiveThreshWinSizeMax - params->adaptiveThreshWinSizeMin) / params->adaptiveThreshWinSizeStep + 1;
540 thresholdImgs.create(nScales, 1, CV_8U);
541 std::vector<cv::Mat> _thresholdImgs;
542 for (int i = 0; i < nScales; i++)
543 {
544 int currScale = params->adaptiveThreshWinSizeMin + i * params->adaptiveThreshWinSizeStep;
545
546 cv::Mat tempThresholdImg;
547 cv::adaptiveThreshold(grayscaleImg, tempThresholdImg, 255, cv::ADAPTIVE_THRESH_MEAN_C,
548 cv::THRESH_BINARY_INV, currScale, params->adaptiveThreshConstant);
549
550 _thresholdImgs.push_back(tempThresholdImg);
551 }
552
553 thresholdImgs.assign(_thresholdImgs);
554 }
555
556 void CCheckerDetectorImpl::
findContours(InputArray srcImg,ContoursVector & contours,const Ptr<DetectorParameters> & params) const557 findContours(
558 InputArray srcImg,
559 ContoursVector &contours,
560 const Ptr<DetectorParameters> ¶ms) const
561 {
562 // contour detected
563 // [Suzuki85] Suzuki, S. and Abe, K., Topological Structural Analysis of Digitized
564 // Binary Images by Border Following. CVGIP 30 1, pp 32-46 (1985)
565 ContoursVector allContours;
566 cv::findContours(srcImg, allContours, RETR_LIST, CHAIN_APPROX_NONE);
567
568 //select contours
569 contours.clear();
570
571 const long long int srcImgArea = srcImg.rows() * srcImg.cols();
572 for (size_t i = 0; i < allContours.size(); i++)
573 {
574
575 PointsVector contour;
576 contour = allContours[i];
577
578 int contourSize = (int)contour.size();
579 if (contourSize <= params->minContourPointsAllowed)
580 continue;
581
582 double area = cv::contourArea(contour);
583 // double perm = cv::arcLength(contour, true);
584
585 if (this->net_used && area / srcImgArea < params->minContoursAreaRate)
586 continue;
587
588 if (!this->net_used && area < params->minContoursArea)
589 continue;
590 // Circularity factor condition
591 // KORDECKI, A., & PALUS, H. (2014). Automatic detection of colour charts in images.
592 // Przegl?d Elektrotechniczny, 90(9), 197-202.
593 // 0.65 < \frac{4*pi*A}{P^2} < 0.97
594 // double Cf = 4 * CV_PI * area / (perm * perm);
595 // if (Cf < 0.5 || Cf > 0.97) continue;
596
597 // Soliditys
598 // This measure is proposed in this work.
599 PointsVector hull;
600 cv::convexHull(contour, hull);
601 double area_hull = cv::contourArea(hull);
602 double S = area / area_hull;
603 if (S < params->minContourSolidity)
604 continue;
605
606 // Texture analysis
607 // ...
608
609 contours.push_back(allContours[i]);
610 }
611 }
612
613 void CCheckerDetectorImpl::
findCandidates(const ContoursVector & contours,std::vector<CChart> & detectedCharts,const Ptr<DetectorParameters> & params)614 findCandidates(
615 const ContoursVector &contours,
616 std::vector<CChart> &detectedCharts,
617 const Ptr<DetectorParameters> ¶ms)
618 {
619 std::vector<cv::Point> approxCurve;
620 std::vector<CChart> possibleCharts;
621
622 // For each contour, analyze if it is a parallelepiped likely to be the chart
623 for (size_t i = 0; i < contours.size(); i++)
624 {
625 // Approximate to a polygon
626 // It uses the Douglas-Peucker algorithm
627 // http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
628 double eps = contours[i].size() * params->findCandidatesApproxPolyDPEpsMultiplier;
629 cv::approxPolyDP(contours[i], approxCurve, eps, true);
630
631 // We interested only in polygons that contains only four points
632 if (approxCurve.size() != 4)
633 continue;
634
635 // And they have to be convex
636 if (!cv::isContourConvex(approxCurve))
637 continue;
638
639 // Ensure that the distance between consecutive points is large enough
640 float minDist = INFINITY;
641
642 for (size_t j = 0; j < 4; j++)
643 {
644 cv::Point side = approxCurve[j] - approxCurve[(j + 1) % 4];
645 float squaredSideLength = (float)side.dot(side);
646 minDist = std::min(minDist, squaredSideLength);
647 }
648
649 // Check that distance is not very small
650 if (minDist < params->minContourLengthAllowed)
651 continue;
652
653 // All tests are passed. Save chart candidate:
654 CChart chart;
655
656 std::vector<cv::Point2f> corners(4);
657 for (int j = 0; j < 4; j++)
658 corners[j] = cv::Point2f((float)approxCurve[j].x, (float)approxCurve[j].y);
659 chart.setCorners(corners);
660
661 possibleCharts.push_back(chart);
662 }
663
664 // Remove these elements which corners are too close to each other.
665 // Eliminate overlaps!!!
666 // First detect candidates for removal:
667 std::vector<std::pair<int, int>> tooNearCandidates;
668 for (int i = 0; i < (int)possibleCharts.size(); i++)
669 {
670 const CChart &m1 = possibleCharts[i];
671
672 //calculate the average distance of each corner to the nearest corner of the other chart candidate
673 for (int j = i + 1; j < (int)possibleCharts.size(); j++)
674 {
675 const CChart &m2 = possibleCharts[j];
676
677 float distSquared = 0;
678
679 for (int c = 0; c < 4; c++)
680 {
681 cv::Point v = m1.corners[c] - m2.corners[c];
682 distSquared += v.dot(v);
683 }
684
685 distSquared /= 4;
686
687 if (distSquared < params->minInterContourDistance)
688 {
689 tooNearCandidates.push_back(std::pair<int, int>(i, j));
690 }
691 }
692 }
693
694 // Mark for removal the element of the pair with smaller perimeter
695 std::vector<bool> removalMask(possibleCharts.size(), false);
696
697 for (size_t i = 0; i < tooNearCandidates.size(); i++)
698 {
699 float p1 = perimeter(possibleCharts[tooNearCandidates[i].first].corners);
700 float p2 = perimeter(possibleCharts[tooNearCandidates[i].second].corners);
701
702 size_t removalIndex;
703 if (p1 > p2)
704 removalIndex = tooNearCandidates[i].second;
705 else
706 removalIndex = tooNearCandidates[i].first;
707
708 removalMask[removalIndex] = true;
709 }
710
711 // Return candidates
712 detectedCharts.clear();
713 for (size_t i = 0; i < possibleCharts.size(); i++)
714 {
715 if (removalMask[i])
716 continue;
717 detectedCharts.push_back(possibleCharts[i]);
718 }
719 }
720
721 void CCheckerDetectorImpl::
clustersAnalysis(const std::vector<CChart> & detectedCharts,std::vector<int> & groups,const Ptr<DetectorParameters> & params)722 clustersAnalysis(
723 const std::vector<CChart> &detectedCharts,
724 std::vector<int> &groups,
725 const Ptr<DetectorParameters> ¶ms)
726 {
727 size_t N = detectedCharts.size();
728 std::vector<cv::Point> X(N);
729 std::vector<double> B0(N), W(N);
730 std::vector<int> G;
731
732 CChart chart;
733 double b0;
734 for (size_t i = 0; i < N; i++)
735 {
736 chart = detectedCharts[i];
737 b0 = chart.large_side * params->B0factor;
738 X[i] = chart.center;
739 W[i] = chart.area;
740 B0[i] = b0;
741 }
742
743 CB0cluster bocluster;
744 bocluster.setVertex(X);
745 bocluster.setWeight(W);
746 bocluster.setB0(B0);
747 bocluster.group();
748 bocluster.getGroup(G);
749 groups = G;
750 }
751
752 void CCheckerDetectorImpl::
checkerRecognize(InputArray img,const std::vector<CChart> & detectedCharts,const std::vector<int> & G,const TYPECHART chartType,std::vector<std::vector<cv::Point2f>> & colorChartsOut,const Ptr<DetectorParameters> & params)753 checkerRecognize(
754 InputArray img,
755 const std::vector<CChart> &detectedCharts,
756 const std::vector<int> &G,
757 const TYPECHART chartType,
758 std::vector<std::vector<cv::Point2f>> &colorChartsOut,
759 const Ptr<DetectorParameters> ¶ms)
760 {
761 std::vector<int> gU;
762 unique(G, gU);
763 size_t Nc = gU.size(); //numero de grupos
764 size_t Ncc = detectedCharts.size(); //numero de charts
765
766 std::vector<std::vector<cv::Point2f>> colorCharts;
767
768 for (size_t g = 0; g < Nc; g++)
769 {
770
771 ///-------------------------------------------------
772 /// selecionar grupo i-esimo
773
774 std::vector<CChart> chartSub;
775 for (size_t i = 0; i < Ncc; i++)
776 if (G[i] == (int)g)
777 chartSub.push_back(detectedCharts[i]);
778
779 size_t Nsc = chartSub.size();
780 if (Nsc < params->minGroupSize)
781 continue;
782
783 ///-------------------------------------------------
784 /// min box estimation
785
786 CBoundMin bm;
787 std::vector<cv::Point2f> points;
788
789 bm.setCharts(chartSub);
790 bm.calculate();
791 bm.getCorners(points);
792
793 // boundary condition
794 if (points.size() == 0)
795 continue;
796
797 // sort the points in anti-clockwise order
798 polyanticlockwise(points);
799
800 ///-------------------------------------------------
801 /// box projective transformation
802
803 // get physical char box model
804 std::vector<cv::Point2f> chartPhy;
805 cv::Size size_box_phy;
806 get_subbox_chart_physical(points, chartPhy, size_box_phy);
807
808 // Find the perspective transformation that brings current chart to rectangular form
809 Matx33f ccT = cv::getPerspectiveTransform(points, chartPhy);
810
811 // transformer
812 std::vector<cv::Point2f> c(Nsc), ct;
813 std::vector<cv::Point2f> ch(4 * Nsc), cht;
814
815 for (size_t i = 0; i < Nsc; i++)
816 {
817
818 CChart cc = chartSub[i];
819 for (size_t j = 0; j < 4; j++)
820 ch[i * 4 + j] = cc.corners[j];
821 c[i] = chartSub[i].center;
822 }
823
824 transform_points_forward(ccT, c, ct);
825 transform_points_forward(ccT, ch, cht);
826
827 float wchart = 0, hchart = 0;
828 std::vector<float> cx(Nsc), cy(Nsc);
829 for (size_t i = 0, k = 0; i < Nsc; i++)
830 {
831 k = i * 4;
832 cv::Point2f v1 = cht[k + 1] - cht[k + 0];
833 cv::Point2f v2 = cht[k + 3] - cht[k + 0];
834 wchart += (float)norm(v1);
835 hchart += (float)norm(v2);
836 cx[i] = ct[i].x;
837 cy[i] = ct[i].y;
838 }
839
840 wchart /= Nsc;
841 hchart /= Nsc;
842
843 ///-------------------------------------------------
844 /// centers and color estimate
845
846 float tolx = wchart / 2, toly = hchart / 2;
847 std::vector<float> cxr, cyr;
848 reduce_array(cx, cxr, tolx);
849 reduce_array(cy, cyr, toly);
850
851 if (cxr.size() == 1 || cyr.size() == 1) //no information can be extracted if \
852 //only one row or columns in present
853 continue;
854 // color and center rectificate
855 cv::Size2i colorSize = cv::Size2i((int)cxr.size(), (int)cyr.size());
856 cv::Mat colorMat(colorSize, CV_32FC3);
857 std::vector<cv::Point2f> cte(colorSize.area());
858
859 int k = 0;
860
861 for (int i = 0; i < colorSize.height; i++)
862 {
863 for (int j = 0; j < colorSize.width; j++)
864 {
865 cv::Point2f vc = cv::Point2f(cxr[j], cyr[i]);
866 cte[k] = vc;
867
868 // recovery color
869 cv::Point2f cti;
870 cv::Matx31f p, xt;
871
872 p(0, 0) = vc.x;
873 p(1, 0) = vc.y;
874 p(2, 0) = 1;
875 xt = ccT.inv() * p;
876 cti.x = xt(0, 0) / xt(2, 0);
877 cti.y = xt(1, 0) / xt(2, 0);
878
879 // color
880 int x, y;
881 x = (int)cti.x;
882 y = (int)cti.y;
883 Vec3f &srgb = colorMat.at<Vec3f>(i, j);
884 Vec3b rgb;
885 if (0 <= y && y < img.rows() && 0 <= x && x < img.cols())
886 rgb = img.getMat().at<Vec3b>(y, x);
887
888 srgb[0] = (float)rgb[0] / 255;
889 srgb[1] = (float)rgb[1] / 255;
890 srgb[2] = (float)rgb[2] / 255;
891
892 k++;
893 }
894 }
895
896 CChartModel::SUBCCMModel scm;
897 scm.centers = cte;
898 scm.color_size = colorSize;
899 colorMat = colorMat.t();
900 scm.sub_chart = colorMat.reshape(3, colorSize.area());
901
902 ///-------------------------------------------------
903
904 // color chart model
905 CChartModel cccm(chartType);
906
907 int iTheta; // rotation angle of chart
908 int offset; // offset
909 float error; // min error
910 if (!cccm.evaluate(scm, offset, iTheta, error))
911 continue;
912 if (iTheta >= 4)
913 cccm.flip();
914
915 for (int i = 0; i < iTheta % 4; i++)
916 cccm.rotate90();
917
918 ///-------------------------------------------------
919 /// calculate coordanate
920
921 cv::Size2i dim = cccm.size;
922 std::vector<cv::Point2f> center = cccm.center;
923 std::vector<cv::Point2f> box = cccm.box;
924 int cols = dim.height - colorSize.width + 1;
925
926 int x = (offset) / cols;
927 int y = (offset) % cols;
928
929 // seleccionar sub grid centers of model
930 std::vector<cv::Point2f> ctss(colorSize.area());
931 cv::Point2f point_ac = cv::Point2f(0, 0);
932 int p = 0;
933
934 for (int i = x; i < (x + colorSize.height); i++)
935 {
936 for (int j = y; j < (y + colorSize.width); j++)
937 {
938 int iter = i * dim.height + j;
939 ctss[p] = center[iter];
940 point_ac += ctss[p];
941 p++;
942 }
943 }
944 // is colineal point
945 if (point_ac.x == ctss[0].x * p || point_ac.y == ctss[0].y * p)
946 continue;
947 // Find the perspective transformation
948 cv::Matx33f ccTe = cv::findHomography(ctss, cte);
949
950 std::vector<cv::Point2f> tbox, ibox;
951 transform_points_forward(ccTe, box, tbox);
952 transform_points_inverse(ccT, tbox, ibox);
953
954 // sort the points in anti-clockwise order
955 if (iTheta < 4)
956 mcc::polyanticlockwise(ibox);
957 else
958 mcc::polyclockwise(ibox);
959 // circshift(ibox, 4 - iTheta);
960 colorCharts.push_back(ibox);
961 }
962
963 // return
964 colorChartsOut = colorCharts;
965 }
966
967 void CCheckerDetectorImpl::
checkerAnalysis(InputArray img_f,const TYPECHART chartType,const unsigned int nc,const std::vector<std::vector<cv::Point2f>> & colorCharts,std::vector<Ptr<CChecker>> & checkers,float asp,const Ptr<DetectorParameters> & params,const cv::Mat & img_rgb_org,const cv::Mat & img_ycbcr_org,std::vector<cv::Mat> & rgb_planes,std::vector<cv::Mat> & ycbcr_planes)968 checkerAnalysis(
969 InputArray img_f,
970 const TYPECHART chartType,
971 const unsigned int nc,
972 const std::vector<std::vector<cv::Point2f>> &colorCharts,
973 std::vector<Ptr<CChecker>> &checkers,
974 float asp,
975 const Ptr<DetectorParameters> ¶ms,
976 const cv::Mat &img_rgb_org,
977 const cv::Mat &img_ycbcr_org,
978 std::vector<cv::Mat> &rgb_planes,
979 std::vector<cv::Mat> &ycbcr_planes)
980 {
981 size_t N;
982 std::vector<cv::Point2f> ibox;
983
984 // color chart classic model
985 CChartModel cccm(chartType);
986 cv::Mat lab;
987 cccm.copyToColorMat(lab, 0);
988 lab = lab.reshape(3, lab.size().area());
989 lab /= 255;
990
991 cv::Mat mask(img_f.size(), CV_8U);
992 mask.setTo(Scalar::all(0));
993
994 N = colorCharts.size();
995 std::vector<float> J(N);
996 for (size_t i = 0; i < N; i++)
997 {
998 ibox = colorCharts[i];
999 J[i] = cost_function(img_f, mask, lab, ibox, chartType);
1000 }
1001
1002 std::vector<int> idx;
1003 sort(J, idx);
1004 float invAsp = 1 / asp;
1005 size_t n = cv::min(nc, (unsigned)N);
1006 checkers.clear();
1007
1008 for (size_t i = 0; i < n; i++)
1009 {
1010 ibox = colorCharts[idx[i]];
1011
1012 if (J[i] > params->maxError)
1013 continue;
1014
1015 // redimention box
1016 for (size_t j = 0; j < 4; j++)
1017 ibox[j] = invAsp * ibox[j];
1018
1019 cv::Mat charts_rgb, charts_ycbcr;
1020 get_profile(ibox, chartType, charts_rgb, charts_ycbcr, img_rgb_org,
1021 img_ycbcr_org, rgb_planes, ycbcr_planes);
1022
1023 // result
1024 Ptr<CChecker> checker = CChecker::create();
1025 checker->setBox(ibox);
1026 checker->setTarget(chartType);
1027 checker->setChartsRGB(charts_rgb);
1028 checker->setChartsYCbCr(charts_ycbcr);
1029 checker->setCenter(mace_center(ibox));
1030 checker->setCost(J[i]);
1031
1032 checkers.push_back(checker);
1033 }
1034 }
1035
1036 void CCheckerDetectorImpl::
removeTooCloseDetections(const Ptr<DetectorParameters> & params)1037 removeTooCloseDetections(const Ptr<DetectorParameters> ¶ms)
1038 {
1039 // Remove these elements which corners are too close to each other.
1040 // Eliminate overlaps!!!
1041 // First detect candidates for removal:
1042 std::vector<std::pair<int, int>> tooNearCandidates;
1043 for (int i = 0; i < (int)m_checkers.size(); i++)
1044 {
1045 const Ptr<CChecker> &m1 = m_checkers[i];
1046
1047 //calculate the average distance of each corner to the nearest corner of the other chart candidate
1048 for (int j = i + 1; j < (int)m_checkers.size(); j++)
1049 {
1050 const Ptr<CChecker> &m2 = m_checkers[j];
1051
1052 float distSquared = 0;
1053
1054 for (int c = 0; c < 4; c++)
1055 {
1056 cv::Point v = m1->getBox()[c] - m2->getBox()[c];
1057 distSquared += v.dot(v);
1058 }
1059
1060 distSquared /= 4;
1061
1062 if (distSquared < params->minInterCheckerDistance)
1063 {
1064 tooNearCandidates.push_back(std::pair<int, int>(i, j));
1065 }
1066 }
1067 }
1068
1069 // Mark for removal the element of the pair with smaller cost
1070 std::vector<bool> removalMask(m_checkers.size(), false);
1071
1072 for (size_t i = 0; i < tooNearCandidates.size(); i++)
1073 {
1074 float p1 = m_checkers[tooNearCandidates[i].first]->getCost();
1075 float p2 = m_checkers[tooNearCandidates[i].second]->getCost();
1076
1077 size_t removalIndex;
1078 if (p1 < p2)
1079 removalIndex = tooNearCandidates[i].second;
1080 else
1081 removalIndex = tooNearCandidates[i].first;
1082
1083 removalMask[removalIndex] = true;
1084 }
1085
1086 std::vector<Ptr<CChecker>> copy_m_checkers = m_checkers;
1087 m_checkers.clear();
1088
1089 for (size_t i = 0; i < copy_m_checkers.size(); i++)
1090 {
1091 if (removalMask[i])
1092 continue;
1093 m_checkers.push_back(copy_m_checkers[i]);
1094 }
1095
1096 sort( m_checkers.begin(), m_checkers.end(),
1097 [&](const Ptr<CChecker> &a, const Ptr<CChecker> &b)
1098 {
1099 return a->getCost() < b->getCost();
1100 });
1101 }
1102
1103 void CCheckerDetectorImpl::
get_subbox_chart_physical(const std::vector<cv::Point2f> & points,std::vector<cv::Point2f> & chartPhy,cv::Size & size)1104 get_subbox_chart_physical(const std::vector<cv::Point2f> &points, std::vector<cv::Point2f> &chartPhy, cv::Size &size)
1105 {
1106 float w, h;
1107 cv::Point2f v1 = points[1] - points[0];
1108 cv::Point2f v2 = points[3] - points[0];
1109 float asp = (float)(norm(v2) / norm(v1));
1110
1111 w = 100;
1112 h = (float)floor(100 * asp + 0.5);
1113
1114 chartPhy.clear();
1115 chartPhy.resize(4);
1116 chartPhy[0] = cv::Point2f(0, 0);
1117 chartPhy[1] = cv::Point2f(w, 0);
1118 chartPhy[2] = cv::Point2f(w, h);
1119 chartPhy[3] = cv::Point2f(0, h);
1120
1121 size = cv::Size((int)w, (int)h);
1122 }
1123
1124 void CCheckerDetectorImpl::
reduce_array(const std::vector<float> & x,std::vector<float> & x_new,float tol)1125 reduce_array(const std::vector<float> &x, std::vector<float> &x_new, float tol)
1126 {
1127 size_t n = x.size(), nn;
1128 std::vector<float> xx = x;
1129 x_new.clear();
1130
1131 // sort array
1132 std::sort(xx.begin(), xx.end());
1133
1134 // label array
1135 std::vector<int> label(n);
1136 for (size_t i = 0; i < n; i++)
1137 label[i] = abs(xx[(n + i - 1) % n] - xx[i]) > tol;
1138
1139 // diff array
1140 for (size_t i = 1; i < n; i++)
1141 label[i] += label[i - 1];
1142
1143 // unique array
1144 std::vector<int> ulabel;
1145 unique(label, ulabel);
1146
1147 // mean for group
1148 nn = ulabel.size();
1149 x_new.resize(nn);
1150 for (size_t i = 0; i < nn; i++)
1151 {
1152 float mu = 0, s = 0;
1153 for (size_t j = 0; j < n; j++)
1154 {
1155 mu += (label[j] == ulabel[i]) * xx[j];
1156 s += (label[j] == ulabel[i]);
1157 }
1158 x_new[i] = mu / s;
1159 }
1160
1161 // diff array
1162 std::vector<float> dif(nn - 1);
1163 for (size_t i = 0; i < nn - 1; i++)
1164 dif[i] = (x_new[(i + 1) % nn] - x_new[i]);
1165
1166 // max and idx
1167 float fmax = 0;
1168 size_t idx = 0;
1169 for (size_t i = 0; i < nn - 1; i++)
1170 if (fmax < dif[i])
1171 {
1172 fmax = dif[i];
1173 idx = i;
1174 }
1175
1176 // add ... X[i] MAX X[i+] ...
1177 if (fmax > 4 * tol)
1178 x_new.insert(x_new.begin() + idx + 1, (x_new[idx] + x_new[idx + 1]) / 2);
1179 }
1180
1181 void CCheckerDetectorImpl::
transform_points_forward(InputArray T,const std::vector<cv::Point2f> & X,std::vector<cv::Point2f> & Xt)1182 transform_points_forward(InputArray T, const std::vector<cv::Point2f> &X, std::vector<cv::Point2f> &Xt)
1183 {
1184 size_t N = X.size();
1185 if (N == 0)
1186 return;
1187
1188 Xt.clear();
1189 Xt.resize(N);
1190 cv::Matx31f p, xt;
1191 cv::Point2f pt;
1192
1193 cv::Matx33f _T = T.getMat();
1194 for (int i = 0; i < (int)N; i++)
1195 {
1196 p(0, 0) = X[i].x;
1197 p(1, 0) = X[i].y;
1198 p(2, 0) = 1;
1199 xt = _T * p;
1200 pt.x = xt(0, 0) / xt(2, 0);
1201 pt.y = xt(1, 0) / xt(2, 0);
1202 Xt[i] = pt;
1203 }
1204 }
1205
1206 void CCheckerDetectorImpl::
transform_points_inverse(InputArray T,const std::vector<cv::Point2f> & X,std::vector<cv::Point2f> & Xt)1207 transform_points_inverse(InputArray T, const std::vector<cv::Point2f> &X, std::vector<cv::Point2f> &Xt)
1208 {
1209 cv::Matx33f _T = T.getMat();
1210 cv::Matx33f Tinv = _T.inv();
1211 transform_points_forward(Tinv, X, Xt);
1212 }
1213 void CCheckerDetectorImpl::
get_profile(const std::vector<cv::Point2f> & ibox,const TYPECHART chartType,OutputArray charts_rgb,OutputArray charts_ycbcr,InputArray im_rgb,InputArray im_ycbcr,std::vector<cv::Mat> & rgb_planes,std::vector<cv::Mat> & ycbcr_planes)1214 get_profile(
1215 const std::vector<cv::Point2f> &ibox,
1216 const TYPECHART chartType,
1217 OutputArray charts_rgb,
1218 OutputArray charts_ycbcr,
1219 InputArray im_rgb,
1220 InputArray im_ycbcr,
1221 std::vector<cv::Mat> &rgb_planes,
1222 std::vector<cv::Mat> &ycbcr_planes)
1223 {
1224 // color chart classic model
1225 CChartModel cccm(chartType);
1226 cv::Mat lab;
1227 size_t N;
1228 std::vector<cv::Point2f> fbox = cccm.box;
1229 std::vector<cv::Point2f> cellchart = cccm.cellchart;
1230
1231 // tranformation
1232 Matx33f ccT = cv::getPerspectiveTransform(fbox, ibox);
1233
1234 cv::Mat mask(im_rgb.size(), CV_8U);
1235 mask.setTo(Scalar::all(0));
1236 std::vector<cv::Point2f> bch(4), bcht(4);
1237 N = cellchart.size() / 4;
1238
1239 // Create table charts information
1240 // |p_size|average|stddev|max|min|
1241 // RGB | | | | | |
1242 // YCbCr |
1243
1244 Mat _charts_rgb = cv::Mat(cv::Size(5, 3 * (int)N), CV_64F);
1245 Mat _charts_ycbcr = cv::Mat(cv::Size(5, 3 * (int)N), CV_64F);
1246
1247 cv::Scalar mu_rgb, st_rgb, mu_ycb, st_ycb, p_size;
1248 double max_rgb[3], min_rgb[3], max_ycb[3], min_ycb[3];
1249
1250 for (int i = 0, k; i < (int)N; i++)
1251 {
1252 k = 4 * i;
1253 bch[0] = cellchart[k + 0];
1254 bch[1] = cellchart[k + 1];
1255 bch[2] = cellchart[k + 2];
1256 bch[3] = cellchart[k + 3];
1257 polyanticlockwise(bch);
1258 transform_points_forward(ccT, bch, bcht);
1259
1260 cv::Point2f c(0, 0);
1261 for (int j = 0; j < 4; j++)
1262 c += bcht[j];
1263 c /= 4;
1264 for (size_t j = 0; j < 4; j++)
1265 bcht[j] = ((bcht[j] - c) * 0.50) + c;
1266
1267 Rect roi = poly2mask(bcht, im_rgb.size(), mask);
1268 Mat submask = mask(roi);
1269 p_size = cv::sum(submask);
1270
1271 // rgb space
1272 cv::meanStdDev(im_rgb.getMat()(roi), mu_rgb, st_rgb, submask);
1273 cv::minMaxLoc(rgb_planes[0](roi), &min_rgb[0], &max_rgb[0], NULL, NULL, submask);
1274 cv::minMaxLoc(rgb_planes[1](roi), &min_rgb[1], &max_rgb[1], NULL, NULL, submask);
1275 cv::minMaxLoc(rgb_planes[2](roi), &min_rgb[2], &max_rgb[2], NULL, NULL, submask);
1276
1277 // create tabla
1278 //|p_size|average|stddev|max|min|
1279 // raw_r
1280 _charts_rgb.at<double>(3 * i + 0, 0) = p_size(0);
1281 _charts_rgb.at<double>(3 * i + 0, 1) = mu_rgb(0);
1282 _charts_rgb.at<double>(3 * i + 0, 2) = st_rgb(0);
1283 _charts_rgb.at<double>(3 * i + 0, 3) = min_rgb[0];
1284 _charts_rgb.at<double>(3 * i + 0, 4) = max_rgb[0];
1285 // raw_g
1286 _charts_rgb.at<double>(3 * i + 1, 0) = p_size(0);
1287 _charts_rgb.at<double>(3 * i + 1, 1) = mu_rgb(1);
1288 _charts_rgb.at<double>(3 * i + 1, 2) = st_rgb(1);
1289 _charts_rgb.at<double>(3 * i + 1, 3) = min_rgb[1];
1290 _charts_rgb.at<double>(3 * i + 1, 4) = max_rgb[1];
1291 // raw_b
1292 _charts_rgb.at<double>(3 * i + 2, 0) = p_size(0);
1293 _charts_rgb.at<double>(3 * i + 2, 1) = mu_rgb(2);
1294 _charts_rgb.at<double>(3 * i + 2, 2) = st_rgb(2);
1295 _charts_rgb.at<double>(3 * i + 2, 3) = min_rgb[2];
1296 _charts_rgb.at<double>(3 * i + 2, 4) = max_rgb[2];
1297
1298 // YCbCr space
1299 cv::meanStdDev(im_ycbcr.getMat()(roi), mu_ycb, st_ycb, submask);
1300 cv::minMaxLoc(ycbcr_planes[0](roi), &min_ycb[0], &max_ycb[0], NULL, NULL, submask);
1301 cv::minMaxLoc(ycbcr_planes[1](roi), &min_ycb[1], &max_ycb[1], NULL, NULL, submask);
1302 cv::minMaxLoc(ycbcr_planes[2](roi), &min_ycb[2], &max_ycb[2], NULL, NULL, submask);
1303
1304 // create tabla
1305 //|p_size|average|stddev|max|min|
1306 // raw_Y
1307 _charts_ycbcr.at<double>(3 * i + 0, 0) = p_size(0);
1308 _charts_ycbcr.at<double>(3 * i + 0, 1) = mu_ycb(0);
1309 _charts_ycbcr.at<double>(3 * i + 0, 2) = st_ycb(0);
1310 _charts_ycbcr.at<double>(3 * i + 0, 3) = min_ycb[0];
1311 _charts_ycbcr.at<double>(3 * i + 0, 4) = max_ycb[0];
1312 // raw_Cb
1313 _charts_ycbcr.at<double>(3 * i + 1, 0) = p_size(0);
1314 _charts_ycbcr.at<double>(3 * i + 1, 1) = mu_ycb(1);
1315 _charts_ycbcr.at<double>(3 * i + 1, 2) = st_ycb(1);
1316 _charts_ycbcr.at<double>(3 * i + 1, 3) = min_ycb[1];
1317 _charts_ycbcr.at<double>(3 * i + 1, 4) = max_ycb[1];
1318 // raw_Cr
1319 _charts_ycbcr.at<double>(3 * i + 2, 0) = p_size(0);
1320 _charts_ycbcr.at<double>(3 * i + 2, 1) = mu_ycb(2);
1321 _charts_ycbcr.at<double>(3 * i + 2, 2) = st_ycb(2);
1322 _charts_ycbcr.at<double>(3 * i + 2, 3) = min_ycb[2];
1323 _charts_ycbcr.at<double>(3 * i + 2, 4) = max_ycb[2];
1324
1325 submask.setTo(Scalar::all(0));
1326 }
1327 charts_rgb.assign(_charts_rgb);
1328 charts_ycbcr.assign(_charts_ycbcr);
1329 }
1330
1331 float CCheckerDetectorImpl::
cost_function(InputArray im_rgb,InputOutputArray mask,InputArray lab,const std::vector<cv::Point2f> & ibox,const TYPECHART chartType)1332 cost_function(InputArray im_rgb, InputOutputArray mask, InputArray lab,
1333 const std::vector<cv::Point2f> &ibox, const TYPECHART chartType)
1334 {
1335 CChartModel cccm(chartType);
1336 std::vector<cv::Point2f> fbox = cccm.box;
1337 std::vector<cv::Point2f> cellchart = cccm.cellchart;
1338
1339 // tranformation
1340 Matx33f ccT = cv::getPerspectiveTransform(fbox, ibox);
1341 std::vector<cv::Point2f> bch(4), bcht(4);
1342
1343 int N = (int)(cellchart.size() / 4);
1344
1345 cv::Mat _lab = lab.getMat();
1346 cv::Mat _im_rgb = im_rgb.getMat();
1347
1348 float ec = 0, es = 0;
1349 for (int i = 0, k; i < N; i++)
1350 {
1351 cv::Vec3f r = _lab.at<cv::Vec3f>(i);
1352
1353 k = 4 * i;
1354 bch[0] = cellchart[k + 0];
1355 bch[1] = cellchart[k + 1];
1356 bch[2] = cellchart[k + 2];
1357 bch[3] = cellchart[k + 3];
1358 polyanticlockwise(bch);
1359 transform_points_forward(ccT, bch, bcht);
1360
1361 cv::Point2f c(0, 0);
1362 for (int j = 0; j < 4; j++)
1363 c += bcht[j];
1364 c /= 4;
1365 for (int j = 0; j < 4; j++)
1366 bcht[j] = ((bcht[j] - c) * 0.75) + c;
1367
1368 cv::Scalar mu, st;
1369 Rect roi = poly2mask(bcht, _im_rgb.size(), mask);
1370 if (!roi.empty())
1371 {
1372 Mat submask = mask.getMat()(roi);
1373 cv::meanStdDev(_im_rgb(roi), mu, st, submask);
1374 submask.setTo(Scalar::all(0));
1375
1376 // cos error
1377 float costh;
1378 costh = (float)(mu.dot(cv::Scalar(r)) / (norm(mu) * norm(r) + FLT_EPSILON));
1379 ec += (1 - (1 + costh) / 2);
1380
1381 // standar desviation
1382 es += (float)st.dot(st);
1383 }
1384 }
1385
1386 // J = arg min ec + es
1387 float J = ec + es;
1388 return J / N;
1389 }
1390
1391 } // namespace mcc
1392 } // namespace cv
1393