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> &params,
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 &region : 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> &regionsOfInterest,
260             const int nc /*= 1*/, bool useNet /*=false*/, const Ptr<DetectorParameters> &params)
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 &region : 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> &params)
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> &params) 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> &params) 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> &params) 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> &params)
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> &params)
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> &params)
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> &params,
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> &params)
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