diff --git a/doc/tutorials/objdetect/macbeth_chart_detection/debugging_the_system.markdown b/doc/tutorials/objdetect/macbeth_chart_detection/debugging_the_system.markdown new file mode 100644 index 0000000000..ac0c5348dd --- /dev/null +++ b/doc/tutorials/objdetect/macbeth_chart_detection/debugging_the_system.markdown @@ -0,0 +1,39 @@ +Customising and Debugging the detection system{#tutorial_mcc_debugging_the_system} +=========================== + +There are many hyperparameters that are involved in the detection of a chart.The default values are chosen to maximize the detections in the average case. But these might not be best for your use case.These values can be configured to improve the accuracy for a particular use case. To do this, you would need to create an +instance of `DetectorParameters`. + +``` + mcc::Ptr params = mcc::DetectorParameters::create(); +``` +* `mcc::` is important. + +It contains a lot of values, the complete list can be found in the documentation for `DetectorParameters`. For this tutorial we will be playing with the value of `maxError`. The other values can be configured similarly. + +`maxError` controls how much error is allowed in detection. Like if some chart cell is occluded. It will increase the error. The default value allows some level of tolerance to occlusions, increasing(or decreasing) `maxError`, will increase(or decrease) this tolerance. + +You can change its value simply like this. + +``` + params.maxError = 0.5; +``` + +To use this in the detection system, you would need to pass it to the process function. + +``` + Ptr detector = CCheckerDetector::create(); + detector->process(image, chartType, params = params); +``` + +Thats how easy is it to play with the values. But there is a catch, there are a lot of parts in the detection pipeline. If you simply run it like this you would not be able to see the effect of this change in isolation. It is possible that the preceding parts detected no possible colorchecker candidates, and so changing the value of `maxError` will have no effect. Luckily OpenCV provides a solution for this. You can make the code output a multiple images, each one showing the effect of one part of the pipeling. This is disabled by default. + +* This can only be used if you are compiling from sources. If you can't build from souces, and still need this feature,try raising as issue in the OpenCV repo. + +To do this : Open the file `opencv/modules/objdetect/include/opencv2/objdetect/mcc_checker_detector.hpp`, near the top there is this line + +``` +// #define MCC_DEBUG +``` + +Uncomment this line and rebuild opencv. After this whenever you run the detector, It will show you multiple images, each corresponding to a part of the pipeline. Also you might see some repetetions like first you will see `Thresholding Output`, then some more images, and again `Thresholding Output` corresponding to same image, but slightly different from previous one, it is because internally the image is thesholded multiple times, with different parameters to adjust for different possible sizes of the colorchecker. diff --git a/doc/tutorials/objdetect/macbeth_chart_detection/macbeth_chart_detection.markdown b/doc/tutorials/objdetect/macbeth_chart_detection/macbeth_chart_detection.markdown new file mode 100644 index 0000000000..c4520f0114 --- /dev/null +++ b/doc/tutorials/objdetect/macbeth_chart_detection/macbeth_chart_detection.markdown @@ -0,0 +1,88 @@ +Detecting colorcheckers{#tutorial_macbeth_chart_detection} +=========================== + +In this tutorial you will learn how to use the 'mcc' module to detect colorcharts in a image. +Here we will only use the basic detection algorithm and an improved version that enhances accuracy using a neural network. + +Source Code of the sample +----------- + +``` +run +/bin/example_cpp_macbeth_chart_detection -t= -v= --ci= --nc= +``` + +* -t=# is the chart type where 0 (Standard), 1 (DigitalSG), 2 (Vinyl) +* --ci=# is the camera ID where 0 (default is the main camera), 1 (secondary camera) etc +* --nc=# By default its values is 1 which means only the best chart will be detected + +Examples: + +``` +Run a movie on a standard macbeth chart: +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=0 -v=mcc24.mp4 + +Or run on a vinyl macbeth chart from camera 0: +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=2 --ci=0 + +Or run on a vinyl macbeth chart, detecting the best 5 charts(Detections can be less than 5 but never more): +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=2 --ci=0 --nc=5 + +``` + +``` +Simple run on CPU with neural network (GPU wont be used) +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=0 -m=/home/model.pb --pb=/home/model.pbtxt -v=mcc24.mp4 +``` + +``` +To run on GPU with neural network +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=0 -m=/home/model.pb --pb=/home/model.pbtxt -v=mcc24.mp4 --use_gpu + +To run on GPU with neural network and detect the best 5 charts (Detections can be less than 5 but not more than 5) +/home/opencv/build/bin/example_cpp_macbeth_chart_detection -t=0 -m=/home/model.pb --pb=/home/model.pbtxt -v=mcc24.mp4 --use_gpu --nc=5 +``` + +@includelineno samples/cpp/macbeth_chart_detection.cpp + +Explanation +----------- + +-# **Set header and namespaces** + @code{.cpp} + #include + using namespace std; + using namespace cv; + using namespace mcc; + @endcode + + If you want you can set the namespace like the code above. +-# **Create the detector object** + @code{.cpp} + Ptr detector = CCheckerDetector::create(); + @endcode +-# **Or create the detector object with neural network** + @code{.cpp} + Ptr detector = CCheckerDetector::create(net); + @endcode + + This is just to create the object. +-# **Run the detector** + @code{.cpp} + detector->process(image, chartType); + @endcode + + If the detector successfully detects atleast one chart, it return true otherwise it returns false. In the above given code we print a failure message if no chart were detected. Otherwise if it were successful, the list of colorcharts is stored inside the detector itself, we will see in the next step on how to extract it. By default it will detect atmost one chart, but you can tune the third parameter, nc(maximum number of charts), for detecting more charts. +-# **Get List of ColorCheckers** + @code{.cpp} + std::vector> checkers; + detector->getListColorChecker(checkers); + @endcode + + All the colorcheckers that were detected are now stored in the 'checkers' vector. + +-# **Draw the colorcheckers back to the image** + @code{.cpp} + + detector->draw(checkers, image); + @endcode diff --git a/doc/tutorials/objdetect/table_of_content_objdetect.markdown b/doc/tutorials/objdetect/table_of_content_objdetect.markdown index a79b29dd7a..d6558b66a8 100644 --- a/doc/tutorials/objdetect/table_of_content_objdetect.markdown +++ b/doc/tutorials/objdetect/table_of_content_objdetect.markdown @@ -8,3 +8,5 @@ Object Detection (objdetect module) {#tutorial_table_of_content_objdetect} - @subpage tutorial_aruco_calibration - @subpage tutorial_aruco_faq - @subpage tutorial_barcode_detect_and_decode +- @subpage tutorial_macbeth_chart_detection +- @subpage tutorial_mcc_debugging_the_system diff --git a/modules/objdetect/include/opencv2/objdetect.hpp b/modules/objdetect/include/opencv2/objdetect.hpp index 2fbe7e7127..c812c43c26 100644 --- a/modules/objdetect/include/opencv2/objdetect.hpp +++ b/modules/objdetect/include/opencv2/objdetect.hpp @@ -47,6 +47,7 @@ #include "opencv2/core.hpp" #include "opencv2/objdetect/aruco_detector.hpp" #include "opencv2/objdetect/graphical_code_detector.hpp" +#include "opencv2/objdetect/mcc_checker_detector.hpp" /** @defgroup objdetect Object Detection diff --git a/modules/objdetect/include/opencv2/objdetect/mcc_checker_detector.hpp b/modules/objdetect/include/opencv2/objdetect/mcc_checker_detector.hpp new file mode 100644 index 0000000000..39fa6b8d48 --- /dev/null +++ b/modules/objdetect/include/opencv2/objdetect/mcc_checker_detector.hpp @@ -0,0 +1,300 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef OPENCV_OBJDETECT_MCC_CHECKER_DETECTOR_HPP +#define OPENCV_OBJDETECT_MCC_CHECKER_DETECTOR_HPP +#include +#include +#include + +//--------------------------------------------------------------- +// #define MCC_DEBUG +//--------------------------------------------------------------- + +namespace cv +{ +namespace mcc +{ +//! @addtogroup mcc +//! @{ + +/** ColorChart + * + * @brief enum to hold the type of the checker + */ +enum ColorChart +{ + MCC24 = 0, ///< Standard Macbeth Chart with 24 squares + SG140, ///< DigitalSG with 140 squares + VINYL18, ///< DKK color chart with 12 squares and 6 rectangle +}; + +/** CChecker + * + * @brief checker object + * + * This class contains the information about the detected checkers,i.e, their + * type, the corners of the chart, the color profile, the cost, centers chart, + * etc. + */ +class CV_EXPORTS_W CChecker: public Algorithm +{ +public: + CChecker() {} + virtual ~CChecker() {} + /** @brief Create a new CChecker object. + * + * @return A pointer to the implementation of the CChecker + */ + CV_WRAP static Ptr create(); +public: + CV_WRAP virtual void setTarget(ColorChart _target) = 0; + CV_WRAP virtual void setBox(std::vector _box) = 0; + CV_WRAP virtual void setChartsRGB(Mat _chartsRGB) = 0; + CV_WRAP virtual void setChartsYCbCr(Mat _chartsYCbCr) = 0; + CV_WRAP virtual void setCost(float _cost) = 0; + CV_WRAP virtual void setCenter(Point2f _center) = 0; + + CV_WRAP virtual ColorChart getTarget() = 0; + CV_WRAP virtual std::vector getBox() = 0; + + /** @brief Computes and returns the coordinates of the central parts of the charts modules. + * + * This method computes transformation matrix from the checkers's coordinates (`CChecker::getBox()`) + * and find by this the coordinates of the central parts of the charts modules. + * It is used in `CCheckerDetector::draw()` and in `ChartsRGB` calculation. + */ + CV_WRAP virtual std::vector getColorCharts() = 0; + + CV_WRAP virtual Mat getChartsRGB(bool getStats = true) = 0; + CV_WRAP virtual Mat getChartsYCbCr() = 0; + CV_WRAP virtual float getCost() = 0; + CV_WRAP virtual Point2f getCenter() = 0; +}; + +/** @brief struct DetectorParametersMCC is used by CCheckerDetector + */ +struct CV_EXPORTS_W_SIMPLE DetectorParametersMCC +{ + CV_WRAP DetectorParametersMCC(){ + adaptiveThreshWinSizeMin=23; + adaptiveThreshWinSizeMax=153; + adaptiveThreshWinSizeStep=16; + adaptiveThreshConstant=7; + minContoursAreaRate=0.003; + minContoursArea=100; + confidenceThreshold=0.5; + minContourSolidity=0.9; + findCandidatesApproxPolyDPEpsMultiplier=0.05; + borderWidth=0; + B0factor=1.25f; + maxError=0.1f; + minContourPointsAllowed=4; + minContourLengthAllowed=100; + minInterContourDistance=100; + minInterCheckerDistance=10000; + minImageSize=1000; + minGroupSize=4; + } + /// minimum window size for adaptive thresholding before finding contours (default 23). + CV_PROP_RW int adaptiveThreshWinSizeMin; + + /// maximum window size for adaptive thresholding before finding contours (default 153). + CV_PROP_RW int adaptiveThreshWinSizeMax; + + /// increments from adaptiveThreshWinSizeMin to adaptiveThreshWinSizeMax during the thresholding (default 16). + CV_PROP_RW int adaptiveThreshWinSizeStep; + + /// constant for adaptive thresholding before finding contours (default 7) + CV_PROP_RW double adaptiveThreshConstant; + + /** @brief determine minimum area for marker contour to be detected + * + * This is defined as a rate respect to the area of the input image. Used only if neural network is used (default 0.03). + */ + CV_PROP_RW double minContoursAreaRate; + + /** @brief determine minimum area for marker contour to be detected + * + * This is defined as the actual area. Used only if neural network is used (default 100). + */ + CV_PROP_RW double minContoursArea; + + /// minimum confidence for a bounding box detected by neural network to classify as detection.(default 0.5) (0<=confidenceThreshold<=1) + CV_PROP_RW double confidenceThreshold; + + /// minimum solidity of a contour for it be detected as a square in the chart. (default 0.9). + CV_PROP_RW double minContourSolidity; + + /// multipler to be used in ApproxPolyDP function (default 0.05) + CV_PROP_RW double findCandidatesApproxPolyDPEpsMultiplier; + + /// width of the padding used to pass the inital neural network detection in the succeeding system.(default 0) + CV_PROP_RW int borderWidth; + + /// distance between two neighboring squares of the same chart as a ratio of the large dimension of a square (default 1.25). + CV_PROP_RW float B0factor; + + /// maximum allowed error in the detection of a chart (default 0.1). + CV_PROP_RW float maxError; + + /// minimum points in a detected contour (default 4). + CV_PROP_RW int minContourPointsAllowed; + + /// minimum length of a contour (default 100). + CV_PROP_RW int minContourLengthAllowed; + + /// minimum distance between two contours (default 100). + CV_PROP_RW int minInterContourDistance; + + /// minimum distance between two checkers (default 10000). + CV_PROP_RW int minInterCheckerDistance; + + /// minimum size of the smaller dimension of the image (default 1000). + CV_PROP_RW int minImageSize; + + /// minimum number of squares in a chart that must be detected (default 4). + CV_PROP_RW int minGroupSize; +}; + +/** @brief A class to find the positions of the ColorCharts in the image. + */ + +class CV_EXPORTS_W CCheckerDetector : public Algorithm +{ +public: + /** @brief Find the ColorCharts in the given image. + * + * The found charts are not returned but instead stored in the + * detector, these can be accessed later on using getBestColorChecker() + * and getListColorChecker() + * @param image image in color space BGR + * @param regionsOfInterest regions of image to look for the chart, if + * it is empty, charts are looked for in the + * entire image + * @param nc number of charts in the image, if you don't know the exact + * then keeping this number high helps. + * @return true if atleast one chart is detected otherwise false + */ + + CV_WRAP_AS(processWithROI) virtual bool + process(InputArray image, const std::vector ®ionsOfInterest, + const int nc = 1) = 0; + + + /** @brief Find the ColorCharts in the given image. + * + * Differs from the above one only in the arguments. + * + * This version searches for the chart in the full image. + * + * The found charts are not returned but instead stored in the + * detector, these can be accessed later on using getBestColorChecker() + * and getListColorChecker() + * @param image image in color space BGR + * @param nc number of charts in the image, if you don't know the exact + * then keeping this number high helps. + * @return true if atleast one chart is detected otherwise false + */ + + CV_WRAP virtual bool + process(InputArray image, const int nc = 1) = 0; + + /** @brief Get the best color checker. By the best it means the one + * detected with the highest confidence. + * @return checker A single colorchecker, if atleast one colorchecker + * was detected, 'nullptr' otherwise. + */ + CV_WRAP virtual Ptr getBestColorChecker() = 0; + + /** @brief Get the list of all detected colorcheckers + * @return checkers vector of colorcheckers + */ + CV_WRAP virtual std::vector> getListColorChecker() = 0; + + /** @brief Returns the implementation of the CCheckerDetector. + * + */ + CV_WRAP static Ptr create(); + /** @brief Set the net which will be used to find the approximate + * bounding boxes for the color charts. And returns the implementation of the CCheckerDetector. + * + * It is not necessary to use this, but this usually results in + * better detection rate. + * + * @param net the neural network, if the network in empty, then + * the function will return false. + */ + CV_WRAP static Ptr create(const dnn::Net &net); + + /** @brief Draws the checker to the given image. + * @param img image in color space BGR + * @param checkers The checkers which will be drawn by this object. + * @param color The color by with which the squares of the checker + * will be drawn + * @param thickness The thickness with which the sqaures will be + * drawn + */ + + CV_WRAP virtual void draw(std::vector>& checkers, InputOutputArray img, const Scalar color = CV_RGB(0,250,0), const int thickness = 2) = 0; + + /** @brief Gets the reference color for chart. + */ + + CV_WRAP virtual Mat getRefColors() = 0; + + /** @brief Sets the detection paramaters for mcc. + * @param params DetectorParametersMCC structure containing detection configuration parameters. + */ + + CV_WRAP virtual void setDetectionParams(const DetectorParametersMCC ¶ms) = 0; + + /** @brief Sets the color chart type for MCC detection. + * @param chartType ColorChart enum specifying the type of color chart to detect. + */ + + CV_WRAP virtual void setColorChartType(ColorChart chartType) = 0; + + /** @brief Enables or disables the use of the neural network for detection. + * @param useDnn Boolean flag to indicate whether to use neural network (true) or not (false). + */ + + CV_WRAP virtual void setUseDnnModel(bool useDnn) = 0; + + CV_WRAP virtual bool getUseDnnModel() const = 0; + + CV_WRAP virtual const DetectorParametersMCC& getDetectionParams() const = 0; + + CV_WRAP virtual ColorChart getColorChartType() const = 0; +}; + +//! @} mcc +} // namespace mcc +} // namespace cv + +#endif diff --git a/modules/objdetect/misc/js/gen_dict.json b/modules/objdetect/misc/js/gen_dict.json index dee3e4c93a..7d3bb61735 100644 --- a/modules/objdetect/misc/js/gen_dict.json +++ b/modules/objdetect/misc/js/gen_dict.json @@ -19,6 +19,9 @@ "QRCodeDetectorAruco_Params": ["Params"], "QRCodeDetectorAruco": ["QRCodeDetectorAruco", "decode", "detect", "detectAndDecode", "detectMulti", "decodeMulti", "detectAndDecodeMulti", "setDetectorParameters", "setArucoParameters"], "barcode_BarcodeDetector": ["BarcodeDetector", "decode", "detect", "detectAndDecode", "detectMulti", "decodeMulti", "detectAndDecodeMulti", "decodeWithType", "detectAndDecodeWithType"], + "mcc_CheckerDetector": ["process", "getBestColorChecker", "getListColorChecker", "create", "draw", "getRefColors", "setDetectionParams", "getDetectionParams", "setColorChartType", "getColorChartType", "setUseDnnModel", "getUseDnnModel"], + "mcc_DetectorParameters": ["DetectorParametersMCC"], + "mcc_Checker": ["setTarget", "setBox", "setChartsRGB", "setChartsYCbCr", "setCost", "setCenter", "getTarget", "getBox", "getColorCharts", "getChartsRGB", "getChartsYCbCr", "getCost", "getCenter"], "FaceDetectorYN": ["setInputSize", "getInputSize", "setScoreThreshold", "getScoreThreshold", "setNMSThreshold", "getNMSThreshold", "setTopK", "getTopK", "detect", "create"] }, "namespace_prefix_override": diff --git a/modules/objdetect/misc/python/pyopencv_cchecker.hpp b/modules/objdetect/misc/python/pyopencv_cchecker.hpp new file mode 100644 index 0000000000..39c2b8bd00 --- /dev/null +++ b/modules/objdetect/misc/python/pyopencv_cchecker.hpp @@ -0,0 +1,4 @@ +#include "opencv2/objdetect/mcc_checker_detector.hpp" + +typedef std::vector> vector_Ptr_CChecker; +typedef dnn::Net dnn_Net; diff --git a/modules/objdetect/perf/perf_mcc.cpp b/modules/objdetect/perf/perf_mcc.cpp new file mode 100644 index 0000000000..90d890b54a --- /dev/null +++ b/modules/objdetect/perf/perf_mcc.cpp @@ -0,0 +1,29 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "perf_precomp.hpp" + +namespace opencv_test +{ +namespace +{ + +using namespace std; +using namespace cv::mcc; + +PERF_TEST(CV_mcc_perf, detect) { + string path = cvtest::findDataFile("cv/mcc/mcc_ccm_test.jpg"); + Mat img = imread(path, IMREAD_COLOR); + Ptr detector = CCheckerDetector::create(); + detector->setColorChartType(MCC24); + + // detect MCC24 board + TEST_CYCLE() { + ASSERT_TRUE(detector->process(img, 1)); + } + SANITY_CHECK_NOTHING(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/objdetect/src/mcc/bound_min.cpp b/modules/objdetect/src/mcc/bound_min.cpp new file mode 100644 index 0000000000..e26579fb31 --- /dev/null +++ b/modules/objdetect/src/mcc/bound_min.cpp @@ -0,0 +1,180 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" +#include "bound_min.hpp" + +namespace cv +{ +namespace mcc +{ +CBoundMin::CBoundMin() + +{ +} + +CBoundMin::~CBoundMin() +{ +} + +void CBoundMin::calculate() +{ + + corners.clear(); + size_t N = chart.size(); + if (!N) + return; + + std::vector X(4 * N); + for (size_t i = 0; i < N; i++) + { + mcc::CChart cc = chart[i]; + for (size_t j = 0; j < 4; j++) + { + X[i * 4 + j] = cc.corners[j]; + } + } + + // media + Point2f mu(0, 0); + for (size_t i = 0; i < 4 * N; i++) + mu += X[i]; + mu /= (4 * (int)N); + + for (size_t i = 0; i < 4 * N; i++) + X[i] -= mu; + + // calculate all line + std::vector L; + L.resize(4 * N); + for (size_t i = 0; i < N; i++) + { + Point3f v0, v1, v2, v3; + v0.x = X[4 * i + 0].x; + v0.y = X[4 * i + 0].y; + v0.z = 1; + v1.x = X[4 * i + 1].x; + v1.y = X[4 * i + 1].y; + v1.z = 1; + v2.x = X[4 * i + 2].x; + v2.y = X[4 * i + 2].y; + v2.z = 1; + v3.x = X[4 * i + 3].x; + v3.y = X[4 * i + 3].y; + v3.z = 1; + + L[4 * i + 0] = v0.cross(v1); + L[4 * i + 1] = v1.cross(v2); + L[4 * i + 2] = v2.cross(v3); + L[4 * i + 3] = v3.cross(v0); + } + + // line convex hull + std::vector dist; + dist.resize(4 * N); + Point2f n; + float d; + + for (size_t i = 0; i < 4 * N; i++) + { + n.x = L[i].x; + n.y = L[i].y; + d = L[i].z; + + int s = 0; + for (size_t j = 0; j < N; j++) + s += (X[j].dot(n) + d) <= 0; + dist[i] = s; + } + + // sort + std::vector idx; + std::vector Ls; + Ls.resize(4 * N); + mcc::sort(dist, idx); + for (size_t i = 0; i < 4 * N; i++) + Ls[i] = L[idx[i]]; + + std::vector Lc; + Lc.resize(4 * N); + Lc[0] = Ls[0]; + Point3f ln; + + int j, k = 0; + for (size_t i = 0; i < 4 * N; i++) + { + + ln = Ls[i]; //current line + if (!validateLine(Lc, ln, k, j)) + { + + Lc[k] = ln; + k++; + } + else if ((abs(Lc[j].z) < abs(ln.z)) && (abs(dist[i] - dist[j]) < 2)) + { + Lc[j] = ln; + } + if (k == 4 && abs(dist[i] - dist[k - 1]) > 2) + break; + } + if (k < 4) + return; + + std::vector thetas; + thetas.resize(4); + for (size_t i = 0; i < 4; i++) + thetas[i] = atan2(Lc[i].y / Lc[i].z, Lc[i].x / Lc[i].z); + + sort(thetas, idx, false); + std::vector lines; + lines.resize(4); + for (size_t i = 0; i < 4; i++) + lines[i] = Lc[idx[i]]; + + Point3f Vcart; + Point2f Vhom; + std::vector V; + V.resize(4); + + for (size_t i = 0; i < 4; i++) + { + j = (i + 1) % 4; + Vcart = lines[i].cross(lines[j]); + if (fabs(Vcart.z) <= 1e-6){ + continue; + } + Vhom.x = Vcart.x / Vcart.z; + Vhom.y = Vcart.y / Vcart.z; + V[i] = Vhom + mu; + } + + corners = V; +} +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/bound_min.hpp b/modules/objdetect/src/mcc/bound_min.hpp new file mode 100644 index 0000000000..257b7ab33d --- /dev/null +++ b/modules/objdetect/src/mcc/bound_min.hpp @@ -0,0 +1,83 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_BOUND_MIN_HPP +#define _MCC_BOUND_MIN_HPP + +#include "charts.hpp" + +namespace cv +{ + +namespace mcc +{ + +class CBoundMin +{ + +public: + CBoundMin(); + ~CBoundMin(); + + void setCharts(const std::vector &chartIn) { chart = chartIn; } + void getCorners(std::vector &cornersOut) { cornersOut = corners; } + void calculate(); + +private: + std::vector chart; + std::vector corners; + +private: + bool validateLine(const std::vector &Lc, Point3f ln, + int k, int &j) + { + + double theta; + Point2d v0, v1; + + for (j = 0; j < k; j++) + { + v0.x = Lc[j].x; + v0.y = Lc[j].y; + v1.x = ln.x; + v1.y = ln.y; + theta = v0.dot(v1) / (norm(v0) * norm(v1)); + theta = acos(theta); + + if (theta < 0.5) + return true; + } + + return false; + } +}; + +} // namespace mcc + +} // namespace cv +#endif //_MCC_BOUND_MIN_HPP diff --git a/modules/objdetect/src/mcc/charts.cpp b/modules/objdetect/src/mcc/charts.cpp new file mode 100644 index 0000000000..9fb7100619 --- /dev/null +++ b/modules/objdetect/src/mcc/charts.cpp @@ -0,0 +1,96 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" +#include "charts.hpp" + +namespace cv +{ +namespace mcc +{ +CChart::CChart() + : perimetro(0), area(0), large_side(0) +{ +} + +CChart::~CChart() +{ +} + +void CChart:: + setCorners(std::vector p) +{ + Point v1, v2; + if (p.empty()) + return; + + // copy + corners = p; + + // Sort the corners in anti-clockwise order + polyanticlockwise(corners); + + // Properties + area = contourArea(corners); + perimetro = perimeter(corners); + center = mace_center(corners); + + v1 = corners[2] - corners[0]; + v2 = corners[3] - corners[1]; + large_side = std::max(norm(v1), norm(v2)); +} + +////////////////////////////////////////////////////////////////////////////////////////////// + +CChartDraw:: + CChartDraw(CChart &pChart, InputOutputArray image) + : m_pChart(pChart), m_image(image.getMat()) +{ +} + +void CChartDraw:: + drawContour(Scalar color /*= CV_RGB(0, 250, 0)*/) const +{ + + //Draw lines + int thickness = 2; + line(m_image, (m_pChart).corners[0], (m_pChart).corners[1], color, thickness, LINE_AA); + line(m_image, (m_pChart).corners[1], (m_pChart).corners[2], color, thickness, LINE_AA); + line(m_image, (m_pChart).corners[2], (m_pChart).corners[3], color, thickness, LINE_AA); + line(m_image, (m_pChart).corners[3], (m_pChart).corners[0], color, thickness, LINE_AA); +} + +void CChartDraw:: + drawCenter(Scalar color /*= CV_RGB(0, 0, 255)*/) const +{ + int radius = 3; + int thickness = 2; + circle(m_image, (m_pChart).center, radius, color, thickness); +} +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/charts.hpp b/modules/objdetect/src/mcc/charts.hpp new file mode 100644 index 0000000000..985595d197 --- /dev/null +++ b/modules/objdetect/src/mcc/charts.hpp @@ -0,0 +1,90 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_CHARTS_HPP +#define _MCC_CHARTS_HPP + +namespace cv +{ +namespace mcc +{ + +/** @brief Chart model + * + * .--------. <- px,py + * | | one chart for checker color + * | RGB | + * | Lab | + * | | + * .--------. + * + * @author Pedro Marrero Fernndez + */ + +class CChart +{ + +public: + CChart(); + ~CChart(); + + /** @brief set corners + *@param p[in] new corners + */ + void setCorners(std::vector p); + +public: + std::vector corners; + Point2f center; + double perimetro; + double area; + double large_side; +}; + +/** @brief Chart draw */ +class CChartDraw +{ +public: + /** @brief contructor */ + CChartDraw(CChart &pChart, InputOutputArray image); + + /** @brief draw the chart contour over the image */ + void drawContour(Scalar color = CV_RGB(0, 250, 0)) const; + + /** @brief draw the chart center over the image */ + void drawCenter(Scalar color = CV_RGB(0, 0, 255)) const; + +private: + CChart &m_pChart; + Mat m_image; +}; + +} // namespace mcc +} // namespace cv + +#endif //_MCC_CHARTS_HPP diff --git a/modules/objdetect/src/mcc/checker_detector.cpp b/modules/objdetect/src/mcc/checker_detector.cpp new file mode 100644 index 0000000000..caf65d3e56 --- /dev/null +++ b/modules/objdetect/src/mcc/checker_detector.cpp @@ -0,0 +1,1597 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" + +#include "checker_detector.hpp" +#include "graph_cluster.hpp" +#include "bound_min.hpp" +#include "wiener_filter.hpp" +#include "checker_model.hpp" +#include "debug.hpp" + +namespace cv +{ +namespace mcc +{ + +Ptr CCheckerDetector::create() +{ + return makePtr(); +} +Ptr CCheckerDetector::create(const dnn::Net& net) +{ + return makePtr(net); +} + +CCheckerDetectorImpl:: + CCheckerDetectorImpl() +{ +} + +CCheckerDetectorImpl::~CCheckerDetectorImpl() +{ +} + +bool CCheckerDetectorImpl:: + _no_net_process(InputArray image, const int nc, + std::vector regionsOfInterest) +{ + m_checkers.clear(); + + Mat img = image.getMat(); + for (const Rect ®ion : regionsOfInterest) + { + //------------------------------------------------------------------- + // Run the model to find good regions + //------------------------------------------------------------------- + Mat croppedImage = img(region); +#ifdef MCC_DEBUG + std::string pathOut = "./"; +#endif + //------------------------------------------------------------------- + // prepare image + //------------------------------------------------------------------- + + Mat img_bgr, img_gray; + float asp; + prepareImage(croppedImage, img_gray, img_bgr, asp); + +#ifdef MCC_DEBUG + showAndSave("prepare_image", img_gray, pathOut); +#endif + //------------------------------------------------------------------- + // thresholding + //------------------------------------------------------------------- + std::vector img_bw; + performThreshold(img_gray, img_bw); + + Mat3f img_rgb_f(img_bgr); + cvtColor(img_rgb_f, img_rgb_f, COLOR_BGR2RGB); + img_rgb_f /= 255; + + Mat img_rgb_org, img_ycbcr_org; + std::vector rgb_planes(3), ycbcr_planes(3); + + // Convert to RGB and YCbCr space + cvtColor(croppedImage, img_rgb_org, COLOR_BGR2RGB); + cvtColor(croppedImage, img_ycbcr_org, COLOR_BGR2YCrCb); + + // Get chanels + split(img_rgb_org, rgb_planes); + split(img_ycbcr_org, ycbcr_planes); + Mutex mtx; + parallel_for_( + Range(0, (int)img_bw.size()), [&](const Range &range) { + const int begin = range.start; + const int end = range.end; + for (int i = begin; i < end; i++) + { + +#ifdef MCC_DEBUG + showAndSave("threshold_image", img_bw[i], pathOut); +#endif + // find contour + //------------------------------------------------------------------- + ContoursVector contours; + findContours(img_bw[i], contours); + + if (contours.empty()) + continue; +#ifdef MCC_DEBUG + Mat im_contour(img_bgr.size(), CV_8UC1); + im_contour = Scalar(0); + drawContours(im_contour, contours, -1, Scalar(255), 2, LINE_AA); + showAndSave("find_contour", im_contour, pathOut); +#endif + //------------------------------------------------------------------- + // find candidate + //------------------------------------------------------------------- + + std::vector detectedCharts; + findCandidates(contours, detectedCharts); + + if (detectedCharts.empty()) + continue; + +#ifdef MCC_DEBUG + Mat img_chart; + img_bgr.copyTo(img_chart); + + for (size_t ind = 0; ind < detectedCharts.size(); ind++) + { + + CChartDraw chrtdrw((detectedCharts[ind]), img_chart); + chrtdrw.drawCenter(); + chrtdrw.drawContour(); + } + showAndSave("find_candidate", img_chart, pathOut); +#endif + //------------------------------------------------------------------- + // clusters analysis + //------------------------------------------------------------------- + + std::vector G; + clustersAnalysis(detectedCharts, G); + + if (G.empty()) + continue; + +#ifdef MCC_DEBUG + Mat im_gru; + img_bgr.copyTo(im_gru); + RNG rng(0xFFFFFFFF); + int radius = 10, thickness = -1; + + std::vector g; + unique(G, g); + size_t Nc = g.size(); + std::vector colors(Nc); + for (size_t ind = 0; ind < Nc; ind++) + colors[ind] = randomcolor(rng); + + for (size_t ind = 0; ind < detectedCharts.size(); ind++) + circle(im_gru, detectedCharts[ind].center, radius, colors[G[ind]], + thickness); + showAndSave("clusters_analysis", im_gru, pathOut); +#endif + //------------------------------------------------------------------- + // checker color recognize + //------------------------------------------------------------------- + + std::vector> colorCharts; + checkerRecognize(img_bgr, detectedCharts, G, colorCharts); + + if (colorCharts.empty()) + continue; + +#ifdef MCC_DEBUG + Mat image_box; + img_bgr.copyTo(image_box); + for (size_t ind = 0; ind < colorCharts.size(); ind++) + { + std::vector ibox = colorCharts[ind]; + Scalar color_box = CV_RGB(0, 0, 255); + int thickness_box = 2; + line(image_box, ibox[0], ibox[1], color_box, thickness_box, LINE_AA); + line(image_box, ibox[1], ibox[2], color_box, thickness_box, LINE_AA); + line(image_box, ibox[2], ibox[3], color_box, thickness_box, LINE_AA); + line(image_box, ibox[3], ibox[0], color_box, thickness_box, LINE_AA); + } + showAndSave("checker_recognition", image_box, pathOut); +#endif + //------------------------------------------------------------------- + // checker color analysis + //------------------------------------------------------------------- + std::vector> checkers; + checkerAnalysis(img_rgb_f, nc, colorCharts, checkers, asp, + img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); + +#ifdef MCC_DEBUG + Mat image_checker; + croppedImage.copyTo(image_checker); + draw(checkers, image_checker); + showAndSave("checker_analysis", image_checker, pathOut); +#endif + for (Ptr checker : checkers) + { + const std::vector& checkerBox = checker->getBox(); + std::vector restore_box(checkerBox.size()); + for (size_t a = 0; a < checkerBox.size(); ++a) { + restore_box[a] = checkerBox[a] + static_cast(region.tl()); + } + checker->setBox(restore_box); + { + AutoLock lock(mtx); + m_checkers.push_back(checker); + } + } + } +#ifdef MCC_DEBUG + }, + 1); //Run only one thread in debug mode +#else + }); +#endif + } + //remove too close detections + removeTooCloseDetections(); + m_checkers.resize(min(nc, (int)m_checkers.size())); + return !m_checkers.empty(); +} + +bool CCheckerDetectorImpl:: + process(InputArray image, const std::vector ®ionsOfInterest, + const int nc /*= 1*/) +{ + m_checkers.clear(); + + if (this->net.empty() || !m_useDnn) + { + return _no_net_process(image, nc, regionsOfInterest); + } + + Mat img = image.getMat(); + + Mat img_rgb_org, img_ycbcr_org; + std::vector rgb_planes(3), ycbcr_planes(3); + + // Convert to RGB and YCbCr space + cvtColor(img, img_rgb_org, COLOR_BGR2RGB); + cvtColor(img, img_ycbcr_org, COLOR_BGR2YCrCb); + + // Get chanels + split(img_rgb_org, rgb_planes); + split(img_ycbcr_org, ycbcr_planes); + + for (const Rect ®ion : regionsOfInterest) + { + //------------------------------------------------------------------- + // Run the model to find good regions + //------------------------------------------------------------------- + + Mat croppedImage = img(region); + + int rows = croppedImage.size[0]; + int cols = croppedImage.size[1]; + net.setInput(dnn::blobFromImage(croppedImage, 1.0, Size(), Scalar(), true)); + Mat output = net.forward(); + + Mat detectionMat(output.size[2], output.size[3], CV_32F, output.ptr()); + + for (int i = 0; i < detectionMat.rows; i++) + { + float confidence = detectionMat.at(i, 2); + if (confidence > m_params.confidenceThreshold) + { + float xTopLeft = max(0.0f, detectionMat.at(i, 3) * cols - m_params.borderWidth); + float yTopLeft = max(0.0f, detectionMat.at(i, 4) * rows - m_params.borderWidth); + float xBottomRight = min((float)cols - 1, detectionMat.at(i, 5) * cols + m_params.borderWidth); + float yBottomRight = min((float)rows - 1, detectionMat.at(i, 6) * rows + m_params.borderWidth); + + Point2f topLeft = {xTopLeft, yTopLeft}; + Point2f bottomRight = {xBottomRight, yBottomRight}; + + Rect innerRegion(topLeft, bottomRight); + Mat innerCroppedImage = croppedImage(innerRegion); + +#ifdef MCC_DEBUG + std::string pathOut = "./"; +#endif + //------------------------------------------------------------------- + // prepare image + //------------------------------------------------------------------- + + Mat img_bgr, img_gray; + float asp; + prepareImage(innerCroppedImage, img_gray, img_bgr, asp); + + //------------------------------------------------------------------- + // thresholding + //------------------------------------------------------------------- + + std::vector img_bw; + performThreshold(img_gray, img_bw); + + Mat3f img_rgb_f(img_bgr); + cvtColor(img_rgb_f, img_rgb_f, COLOR_BGR2RGB); + img_rgb_f /= 255; + Mutex mtx; + parallel_for_( + Range(0, (int)img_bw.size()), [&](const Range &range) { + const int begin = range.start; + const int end = range.end; + + for (int ind = begin; ind < end; ind++) + { + +#ifdef MCC_DEBUG + showAndSave("threshold_image", img_bw[ind], pathOut); +#endif + //------------------------------------------------------------------ + // find contour + //------------------------------------------------------------------- + ContoursVector contours; + findContours(img_bw[ind], contours); + + if (contours.empty()) + continue; +#ifdef MCC_DEBUG + Mat im_contour(img_bgr.size(), CV_8UC1); + im_contour = Scalar(0); + drawContours(im_contour, contours, -1, Scalar(255), 2, LINE_AA); + showAndSave("find_contour", im_contour, pathOut); +#endif + //------------------------------------------------------------------- + // find candidate + //------------------------------------------------------------------- + + std::vector detectedCharts; + findCandidates(contours, detectedCharts); + + if (detectedCharts.empty()) + continue; + +#ifdef MCC_DEBUG + Mat img_chart; + img_bgr.copyTo(img_chart); + + for (size_t index = 0; index < detectedCharts.size(); index++) + { + + CChartDraw chrtdrw((detectedCharts[index]), img_chart); + chrtdrw.drawCenter(); + chrtdrw.drawContour(); + } + showAndSave("find_candidate", img_chart, pathOut); +#endif + //------------------------------------------------------------------- + // clusters analysis + //------------------------------------------------------------------- + + std::vector G; + clustersAnalysis(detectedCharts, G); + + if (G.empty()) + continue; +#ifdef MCC_DEBUG + Mat im_gru; + img_bgr.copyTo(im_gru); + RNG rng(0xFFFFFFFF); + int radius = 10, thickness = -1; + + std::vector g; + unique(G, g); + size_t Nc = g.size(); + std::vector colors(Nc); + for (size_t index = 0; index < Nc; index++) + colors[index] = randomcolor(rng); + + for (size_t index = 0; index < detectedCharts.size(); index++) + circle(im_gru, detectedCharts[index].center, radius, colors[G[index]], + thickness); + showAndSave("clusters_analysis", im_gru, pathOut); +#endif + + //------------------------------------------------------------------- + // checker color recognize + //------------------------------------------------------------------- + + std::vector> colorCharts; + checkerRecognize(img_bgr, detectedCharts, G, colorCharts); + + if (colorCharts.empty()) + continue; + +#ifdef MCC_DEBUG + Mat image_box; + img_bgr.copyTo(image_box); + for (size_t index = 0; index < colorCharts.size(); index++) + { + std::vector ibox = colorCharts[index]; + Scalar color_box = CV_RGB(0, 0, 255); + int thickness_box = 2; + line(image_box, ibox[0], ibox[1], color_box, thickness_box, LINE_AA); + line(image_box, ibox[1], ibox[2], color_box, thickness_box, LINE_AA); + line(image_box, ibox[2], ibox[3], color_box, thickness_box, LINE_AA); + line(image_box, ibox[3], ibox[0], color_box, thickness_box, LINE_AA); + //circle(image_box, ibox[0], 10, Scalar(0, 0, 255), 3); + //circle(image_box, ibox[1], 10, Scalar(0, 255, 0), 3); + } + showAndSave("checker_recognition", image_box, pathOut); +#endif + //------------------------------------------------------------------- + // checker color analysis + //------------------------------------------------------------------- + std::vector> checkers; + checkerAnalysis(img_rgb_f, nc, colorCharts, checkers, asp, + img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); +#ifdef MCC_DEBUG + Mat image_checker; + innerCroppedImage.copyTo(image_checker); + draw(checkers, image_checker); + showAndSave("checker_analysis", image_checker, pathOut); +#endif + for (Ptr checker : checkers) + { + const std::vector& checkerBox = checker->getBox(); + std::vector restore_box(checkerBox.size()); + for (size_t a = 0; a < checkerBox.size(); ++a) { + restore_box[a] = checkerBox[a] + static_cast(region.tl() + innerRegion.tl()); + } + checker->setBox(restore_box); + { + AutoLock lock(mtx); + m_checkers.push_back(checker); + } + } + } +#ifdef MCC_DEBUG + }, + 1); //Run only one thread in debug mode +#else + }); +#endif + } + } + } + // As a failsafe try the classical method + if (m_checkers.empty()) + { + return _no_net_process(image, nc, regionsOfInterest); + } + //remove too close detections + removeTooCloseDetections(); + + m_checkers.resize(min(nc, (int)m_checkers.size())); + + return !m_checkers.empty(); +} + + +//Overload for the above function +bool CCheckerDetectorImpl:: + process(InputArray image, const int nc /*= 1*/) +{ + return process(image, std::vector(1, Rect(0, 0, image.cols(), image.rows())), + nc); +} + +void CCheckerDetectorImpl::setDetectionParams(const DetectorParametersMCC ¶ms) +{ + this->m_params = params; +} + +void CCheckerDetectorImpl::setColorChartType(ColorChart chartType) +{ + this->m_chartType = chartType; +} + +void CCheckerDetectorImpl::setUseDnnModel(bool useDnn) +{ + this->m_useDnn = useDnn; +} + +bool CCheckerDetectorImpl::getUseDnnModel() const +{ + return m_useDnn; +} + +const DetectorParametersMCC& CCheckerDetectorImpl::getDetectionParams() const +{ + return m_params; +} + +ColorChart CCheckerDetectorImpl::getColorChartType() const +{ + return m_chartType; +} + +void CCheckerDetectorImpl:: + prepareImage(InputArray bgr, OutputArray grayOut, + OutputArray bgrOut, float &aspOut) const +{ + + int min_size; + Size size = bgr.size(); + aspOut = 1; + bgr.copyTo(bgrOut); + + // Resize image + min_size = std::min(size.width, size.height); + if (m_params.minImageSize > min_size) + { + aspOut = (float)m_params.minImageSize / min_size; + resize(bgr, bgrOut, Size(int(size.width * aspOut), int(size.height * aspOut)), INTER_LINEAR_EXACT); + } + + // Convert to grayscale + cvtColor(bgrOut, grayOut, COLOR_BGR2GRAY); + + // PDiamel: wiener adaptative methods to minimize the noise effets + // by illumination + + CWienerFilter filter; + filter.wiener2(grayOut, grayOut, 5, 5); + + //JLeandro: perform morphological open on the equalized image + //to minimize the noise effects by CLAHE and to even intensities + //inside the MCC patches (regions) + + Mat strelbox = getStructuringElement(MORPH_RECT, Size(5, 5)); + morphologyEx(grayOut, grayOut, MORPH_OPEN, strelbox); +} + +void CCheckerDetectorImpl:: + performThreshold(InputArray grayscaleImg, + OutputArrayOfArrays thresholdImgs) const +{ + // number of window sizes (scales) to apply adaptive thresholding + int nScales = (m_params.adaptiveThreshWinSizeMax - m_params.adaptiveThreshWinSizeMin) / m_params.adaptiveThreshWinSizeStep + 1; + thresholdImgs.create(nScales, 1, CV_8U); + std::vector _thresholdImgs(nScales); + parallel_for_(Range(0, nScales),[&](const Range& range) { + const int start = range.start; + const int end = range.end; + for (int i = start; i < end; i++) { + int currScale = m_params.adaptiveThreshWinSizeMin + i * m_params.adaptiveThreshWinSizeStep; + Mat tempThresholdImg; + adaptiveThreshold(grayscaleImg, tempThresholdImg, 255, ADAPTIVE_THRESH_MEAN_C, + THRESH_BINARY_INV, currScale, m_params.adaptiveThreshConstant); + _thresholdImgs[i] = tempThresholdImg; + } + }); + + thresholdImgs.assign(_thresholdImgs); +} + +void CCheckerDetectorImpl:: + findContours( + InputArray srcImg, + ContoursVector &contours) const +{ + // contour detected + // [Suzuki85] Suzuki, S. and Abe, K., Topological Structural Analysis of Digitized + // Binary Images by Border Following. CVGIP 30 1, pp 32-46 (1985) + ContoursVector allContours; + cv::findContours(srcImg, allContours, RETR_LIST, CHAIN_APPROX_NONE); + + //select contours + contours.clear(); + + for (size_t i = 0; i < allContours.size(); i++) + { + + PointsVector contour; + contour = allContours[i]; + + int contourSize = (int)contour.size(); + if (contourSize <= m_params.minContourPointsAllowed) + continue; + + double area = contourArea(contour); + + if (area < m_params.minContoursArea) + continue; + + PointsVector hull; + convexHull(contour, hull); + double area_hull = contourArea(hull); + double S = area / area_hull; + if (S < m_params.minContourSolidity) + continue; + + contours.push_back(allContours[i]); + } +} + +void CCheckerDetectorImpl:: + findCandidates( + const ContoursVector &contours, + std::vector &detectedCharts) +{ + std::vector approxCurve; + std::vector possibleCharts; + + // For each contour, analyze if it is a parallelepiped likely to be the chart + for (size_t i = 0; i < contours.size(); i++) + { + // Approximate to a polygon + // It uses the Douglas-Peucker algorithm + // http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + double eps = contours[i].size() * m_params.findCandidatesApproxPolyDPEpsMultiplier; + approxPolyDP(contours[i], approxCurve, eps, true); + + // We interested only in polygons that contains only four points + if (approxCurve.size() != 4) + continue; + + // And they have to be convex + if (!isContourConvex(approxCurve)) + continue; + + // Ensure that the distance between consecutive points is large enough + float minDist = INFINITY; + + for (size_t j = 0; j < 4; j++) + { + Point side = approxCurve[j] - approxCurve[(j + 1) % 4]; + float squaredSideLength = (float)side.dot(side); + minDist = std::min(minDist, squaredSideLength); + } + + // Check that distance is not very small + if (minDist < m_params.minContourLengthAllowed) + continue; + + // All tests are passed. Save chart candidate: + CChart chart; + + std::vector corners(4); + for (int j = 0; j < 4; j++) + corners[j] = Point2f((float)approxCurve[j].x, (float)approxCurve[j].y); + chart.setCorners(corners); + + possibleCharts.push_back(chart); + } + + // Remove these elements which corners are too close to each other. + // Eliminate overlaps!!! + // First detect candidates for removal: + std::vector> tooNearCandidates; + for (int i = 0; i < (int)possibleCharts.size(); i++) + { + const CChart &m1 = possibleCharts[i]; + + //calculate the average distance of each corner to the nearest corner of the other chart candidate + for (int j = i + 1; j < (int)possibleCharts.size(); j++) + { + const CChart &m2 = possibleCharts[j]; + + float distSquared = 0; + + for (int c = 0; c < 4; c++) + { + Point v = m1.corners[c] - m2.corners[c]; + distSquared += v.dot(v); + } + + distSquared /= 4; + + if (distSquared < m_params.minInterContourDistance) + { + tooNearCandidates.push_back(std::pair(i, j)); + } + } + } + + // Mark for removal the element of the pair with smaller perimeter + std::vector removalMask(possibleCharts.size(), false); + + for (size_t i = 0; i < tooNearCandidates.size(); i++) + { + float p1 = perimeter(possibleCharts[tooNearCandidates[i].first].corners); + float p2 = perimeter(possibleCharts[tooNearCandidates[i].second].corners); + + size_t removalIndex; + if (p1 > p2) + removalIndex = tooNearCandidates[i].second; + else + removalIndex = tooNearCandidates[i].first; + + removalMask[removalIndex] = true; + } + + detectedCharts.clear(); + for (size_t i = 0; i < possibleCharts.size(); i++) + { + if (removalMask[i]) + continue; + detectedCharts.push_back(possibleCharts[i]); + } +} + +void CCheckerDetectorImpl:: + clustersAnalysis( + const std::vector &detectedCharts, + std::vector &groups) +{ + size_t N = detectedCharts.size(); + std::vector X(N); + std::vector B0(N), W(N); + std::vector G; + + CChart chart; + double b0; + for (size_t i = 0; i < N; i++) + { + chart = detectedCharts[i]; + b0 = chart.large_side * m_params.B0factor; + X[i] = chart.center; + W[i] = chart.area; + B0[i] = b0; + } + + CB0cluster bocluster; + bocluster.setVertex(X); + bocluster.setWeight(W); + bocluster.setB0(B0); + bocluster.group(); + bocluster.getGroup(G); + groups = G; +} + +void CCheckerDetectorImpl:: + checkerRecognize( + InputArray img, + const std::vector &detectedCharts, + const std::vector &G, + std::vector> &colorChartsOut) +{ + std::vector gU; + unique(G, gU); + size_t Nc = gU.size(); //numero de grupos + size_t Ncc = detectedCharts.size(); //numero de charts + + std::vector> colorCharts; + + for (size_t g = 0; g < Nc; g++) + { + + ///------------------------------------------------- + /// selecionar grupo i-esimo + + std::vector chartSub; + for (size_t i = 0; i < Ncc; i++) + if (G[i] == (int)g) + chartSub.push_back(detectedCharts[i]); + + size_t Nsc = chartSub.size(); + if (static_cast(Nsc) < m_params.minGroupSize) + continue; + + ///------------------------------------------------- + /// min box estimation + + CBoundMin bm; + std::vector points; + + bm.setCharts(chartSub); + bm.calculate(); + bm.getCorners(points); + + // boundary condition + if (points.size() == 0) + continue; + + // sort the points in anti-clockwise order + polyanticlockwise(points); + + ///------------------------------------------------- + /// box projective transformation + + // get physical char box model + std::vector chartPhy; + get_subbox_chart_physical(points, chartPhy); + + // Find the perspective transformation that brings current chart to rectangular form + Matx33f ccT = getPerspectiveTransform(points, chartPhy); + + // transformer + std::vector c(Nsc), ct; + std::vector ch(4 * Nsc), cht; + + for (size_t i = 0; i < Nsc; i++) + { + + CChart cc = chartSub[i]; + for (size_t j = 0; j < 4; j++) + ch[i * 4 + j] = cc.corners[j]; + c[i] = chartSub[i].center; + } + + transform_points_forward(ccT, c, ct); + transform_points_forward(ccT, ch, cht); + + float wchart = 0, hchart = 0; + std::vector cx(Nsc), cy(Nsc); + for (size_t i = 0, k = 0; i < Nsc; i++) + { + k = i * 4; + Point2f v1 = cht[k + 1] - cht[k + 0]; + Point2f v2 = cht[k + 3] - cht[k + 0]; + wchart += (float)norm(v1); + hchart += (float)norm(v2); + cx[i] = ct[i].x; + cy[i] = ct[i].y; + } + + wchart /= Nsc; + hchart /= Nsc; + + ///------------------------------------------------- + /// centers and color estimate + + float tolx = wchart / 2, toly = hchart / 2; + std::vector cxr, cyr; + reduce_array(cx, cxr, tolx); + reduce_array(cy, cyr, toly); + + if (cxr.size() == 1 || cyr.size() == 1) //no information can be extracted if \ + //only one row or columns in present + continue; + // color and center rectificate + Size2i colorSize = Size2i((int)cxr.size(), (int)cyr.size()); + Mat colorMat(colorSize, CV_32FC3); + std::vector cte(colorSize.area()); + + int k = 0; + + for (int i = 0; i < colorSize.height; i++) + { + for (int j = 0; j < colorSize.width; j++) + { + Point2f vc = Point2f(cxr[j], cyr[i]); + cte[k] = vc; + + // recovery color + Point2f cti; + Matx31f p, xt; + + p(0, 0) = vc.x; + p(1, 0) = vc.y; + p(2, 0) = 1; + xt = ccT.inv() * p; + cti.x = xt(0, 0) / xt(2, 0); + cti.y = xt(1, 0) / xt(2, 0); + + // color + int x, y; + x = (int)cti.x; + y = (int)cti.y; + Vec3f &srgb = colorMat.at(i, j); + Vec3b rgb; + if (0 <= y && y < img.rows() && 0 <= x && x < img.cols()) + rgb = img.getMat().at(y, x); + + srgb[0] = (float)rgb[0] / 255; + srgb[1] = (float)rgb[1] / 255; + srgb[2] = (float)rgb[2] / 255; + + k++; + } + } + + CChartModel::SUBCCMModel scm; + scm.centers = cte; + scm.color_size = colorSize; + colorMat = colorMat.t(); + scm.sub_chart = colorMat.reshape(3, colorSize.area()); + + ///------------------------------------------------- + + // color chart model + CChartModel cccm(m_chartType); + + int iTheta; // rotation angle of chart + int offset; // offset + float error; // min error + if (!cccm.evaluate(scm, offset, iTheta, error)) + continue; + if (iTheta >= 4) + cccm.flip(); + + for (int i = 0; i < iTheta % 4; i++) + cccm.rotate90(); + + ///------------------------------------------------- + /// calculate coordanate + + Size2i dim = cccm.size; + std::vector center = cccm.center; + std::vector box = cccm.box; + int cols = dim.height - colorSize.width + 1; + + int x = (offset) / cols; + int y = (offset) % cols; + + // seleccionar sub grid centers of model + std::vector ctss(colorSize.area()); + Point2f point_ac = Point2f(0, 0); + int p = 0; + + for (int i = x; i < (x + colorSize.height); i++) + { + for (int j = y; j < (y + colorSize.width); j++) + { + int iter = i * dim.height + j; + ctss[p] = center[iter]; + point_ac += ctss[p]; + p++; + } + } + // is colineal point + if (point_ac.x == ctss[0].x * p || point_ac.y == ctss[0].y * p) + continue; + // Find the perspective transformation + Matx33f ccTe = findHomography(ctss, cte); + + std::vector tbox, ibox; + transform_points_forward(ccTe, box, tbox); + transform_points_inverse(ccT, tbox, ibox); + + // sort the points in anti-clockwise order + if (iTheta < 4) + mcc::polyanticlockwise(ibox); + else + mcc::polyclockwise(ibox); + colorCharts.push_back(ibox); + } + + colorChartsOut = colorCharts; +} +void CCheckerDetectorImpl::draw(std::vector>& checkers, InputOutputArray img, Scalar color, int thickness) +{ + for (Ptr checker : checkers) + { + std::vector charts = checker->getColorCharts(); + size_t N = charts.size() / 4; + for (size_t i = 0, k; i < N; i++) + { + k = 4 * i; + for (size_t j = 0; j < 4; j++) + line(img, charts[k+j], charts[k+((j + 1) % 4)], color, thickness, LINE_AA); + } + } +} +Mat CCheckerDetectorImpl::getRefColors() { + static const double ColorChecker2005_LAB_D50_2[24][3] = { { 37.986, 13.555, 14.059 }, + { 65.711, 18.13, 17.81 }, + { 49.927, -4.88, -21.925 }, + { 43.139, -13.095, 21.905 }, + { 55.112, 8.844, -25.399 }, + { 70.719, -33.397, -0.199 }, + { 62.661, 36.067, 57.096 }, + { 40.02, 10.41, -45.964 }, + { 51.124, 48.239, 16.248 }, + { 30.325, 22.976, -21.587 }, + { 72.532, -23.709, 57.255 }, + { 71.941, 19.363, 67.857 }, + { 28.778, 14.179, -50.297 }, + { 55.261, -38.342, 31.37 }, + { 42.101, 53.378, 28.19 }, + { 81.733, 4.039, 79.819 }, + { 51.935, 49.986, -14.574 }, + { 51.038, -28.631, -28.638 }, + { 96.539, -0.425, 1.186 }, + { 81.257, -0.638, -0.335 }, + { 66.766, -0.734, -0.504 }, + { 50.867, -0.153, -0.27 }, + { 35.656, -0.421, -1.231 }, + { 20.461, -0.079, -0.973 } }; + + static const double Vinyl_LAB_D50_2[18][3] = { { 100, 0.00520000001, -0.0104 }, + { 73.0833969, -0.819999993, -2.02099991 }, + { 62.493, 0.425999999, -2.23099995 }, + { 50.4640007, 0.446999997, -2.32399988 }, + { 37.7970009, 0.0359999985, -1.29700005 }, + { 0, 0, 0 }, + { 51.5880013, 73.5179977, 51.5690002 }, + { 93.6989975, -15.7340002, 91.9420013 }, + { 69.4079971, -46.5940018, 50.4869995 }, + { 66.61000060000001, -13.6789999, -43.1720009 }, + { 11.7110004, 16.9799995, -37.1759987 }, + { 51.973999, 81.9440002, -8.40699959 }, + { 40.5489998, 50.4399986, 24.8490009 }, + { 60.8160019, 26.0690002, 49.4420013 }, + { 52.2529984, -19.9500008, -23.9960003 }, + { 51.2859993, 48.4700012, -15.0579996 }, + { 68.70700069999999, 12.2959995, 16.2129993 }, + { 63.6839981, 10.2930002, 16.7639999 } }; + + static const double DigitalSG_LAB_D50_2[140][3] = { { 96.55, -0.91, 0.57 }, + { 6.43, -0.06, -0.41 }, + { 49.7, -0.18, 0.03 }, + { 96.5, -0.89, 0.59 }, + { 6.5, -0.06, -0.44 }, + { 49.66, -0.2, 0.01 }, + { 96.52, -0.91, 0.58 }, + { 6.49, -0.02, -0.28 }, + { 49.72, -0.2, 0.04 }, + { 96.43, -0.91, 0.67 }, + { 49.72, -0.19, 0 }, + { 32.6, 51.58, -10.85 }, + { 60.75, 26.22, -18.6 }, + { 28.69, 48.28, -39 }, + { 49.38, -15.43, -48.48 }, + { 60.63, -30.77, -26.23 }, + { 19.29, -26.37, -6.15 }, + { 60.15, -41.77, -12.6 }, + { 21.42, 1.67, 8.79 }, + { 49.69, -0.2, 0.01 }, + { 6.5, -0.03, -0.67 }, + { 21.82, 17.33, -18.35 }, + { 41.53, 18.48, -37.26 }, + { 19.99, -0.16, -36.29 }, + { 60.16, -18.45, -31.42 }, + { 19.94, -17.92, -20.96 }, + { 60.68, -6.05, -32.81 }, + { 50.81, -49.8, -9.63 }, + { 60.65, -39.77, 20.76 }, + { 6.53, -0.03, -0.43 }, + { 96.56, -0.91, 0.59 }, + { 84.19, -1.95, -8.23 }, + { 84.75, 14.55, 0.23 }, + { 84.87, -19.07, -0.82 }, + { 85.15, 13.48, 6.82 }, + { 84.17, -10.45, 26.78 }, + { 61.74, 31.06, 36.42 }, + { 64.37, 20.82, 18.92 }, + { 50.4, -53.22, 14.62 }, + { 96.51, -0.89, 0.65 }, + { 49.74, -0.19, 0.03 }, + { 31.91, 18.62, 21.99 }, + { 60.74, 38.66, 70.97 }, + { 19.35, 22.23, -58.86 }, + { 96.52, -0.91, 0.62 }, + { 6.66, 0, -0.3 }, + { 76.51, 20.81, 22.72 }, + { 72.79, 29.15, 24.18 }, + { 22.33, -20.7, 5.75 }, + { 49.7, -0.19, 0.01 }, + { 6.53, -0.05, -0.61 }, + { 63.42, 20.19, 19.22 }, + { 34.94, 11.64, -50.7 }, + { 52.03, -44.15, 39.04 }, + { 79.43, 0.29, -0.17 }, + { 30.67, -0.14, -0.53 }, + { 63.6, 14.44, 26.07 }, + { 64.37, 14.5, 17.05 }, + { 60.01, -44.33, 8.49 }, + { 6.63, -0.01, -0.47 }, + { 96.56, -0.93, 0.59 }, + { 46.37, -5.09, -24.46 }, + { 47.08, 52.97, 20.49 }, + { 36.04, 64.92, 38.51 }, + { 65.05, 0, -0.32 }, + { 40.14, -0.19, -0.38 }, + { 43.77, 16.46, 27.12 }, + { 64.39, 17, 16.59 }, + { 60.79, -29.74, 41.5 }, + { 96.48, -0.89, 0.64 }, + { 49.75, -0.21, 0.01 }, + { 38.18, -16.99, 30.87 }, + { 21.31, 29.14, -27.51 }, + { 80.57, 3.85, 89.61 }, + { 49.71, -0.2, 0.03 }, + { 60.27, 0.08, -0.41 }, + { 67.34, 14.45, 16.9 }, + { 64.69, 16.95, 18.57 }, + { 51.12, -49.31, 44.41 }, + { 49.7, -0.2, 0.02 }, + { 6.67, -0.05, -0.64 }, + { 51.56, 9.16, -26.88 }, + { 70.83, -24.26, 64.77 }, + { 48.06, 55.33, -15.61 }, + { 35.26, -0.09, -0.24 }, + { 75.16, 0.25, -0.2 }, + { 44.54, 26.27, 38.93 }, + { 35.91, 16.59, 26.46 }, + { 61.49, -52.73, 47.3 }, + { 6.59, -0.05, -0.5 }, + { 96.58, -0.9, 0.61 }, + { 68.93, -34.58, -0.34 }, + { 69.65, 20.09, 78.57 }, + { 47.79, -33.18, -30.21 }, + { 15.94, -0.42, -1.2 }, + { 89.02, -0.36, -0.48 }, + { 63.43, 25.44, 26.25 }, + { 65.75, 22.06, 27.82 }, + { 61.47, 17.1, 50.72 }, + { 96.53, -0.89, 0.66 }, + { 49.79, -0.2, 0.03 }, + { 85.17, 10.89, 17.26 }, + { 89.74, -16.52, 6.19 }, + { 84.55, 5.07, -6.12 }, + { 84.02, -13.87, -8.72 }, + { 70.76, 0.07, -0.35 }, + { 45.59, -0.05, 0.23 }, + { 20.3, 0.07, -0.32 }, + { 61.79, -13.41, 55.42 }, + { 49.72, -0.19, 0.02 }, + { 6.77, -0.05, -0.44 }, + { 21.85, 34.37, 7.83 }, + { 42.66, 67.43, 48.42 }, + { 60.33, 36.56, 3.56 }, + { 61.22, 36.61, 17.32 }, + { 62.07, 52.8, 77.14 }, + { 72.42, -9.82, 89.66 }, + { 62.03, 3.53, 57.01 }, + { 71.95, -27.34, 73.69 }, + { 6.59, -0.04, -0.45 }, + { 49.77, -0.19, 0.04 }, + { 41.84, 62.05, 10.01 }, + { 19.78, 29.16, -7.85 }, + { 39.56, 65.98, 33.71 }, + { 52.39, 68.33, 47.84 }, + { 81.23, 24.12, 87.51 }, + { 81.8, 6.78, 95.75 }, + { 71.72, -16.23, 76.28 }, + { 20.31, 14.45, 16.74 }, + { 49.68, -0.19, 0.05 }, + { 96.48, -0.88, 0.68 }, + { 49.69, -0.18, 0.03 }, + { 6.39, -0.04, -0.33 }, + { 96.54, -0.9, 0.67 }, + { 49.72, -0.18, 0.05 }, + { 6.49, -0.03, -0.41 }, + { 96.51, -0.9, 0.69 }, + { 49.7, -0.19, 0.07 }, + { 6.47, 0, -0.38 }, + { 96.46, -0.89, 0.7 } }; + + Mat lab_color(1, 1, CV_32FC3), rgb_color(1, 1, CV_32FC3); + + const double (*colorData)[3] = nullptr; + int colorCount = 0; + + switch (m_chartType) { + case MCC24: + colorData = ColorChecker2005_LAB_D50_2; + colorCount = 24; + break; + case VINYL18: + colorData = Vinyl_LAB_D50_2; + colorCount = 18; + break; + case SG140: + colorData = DigitalSG_LAB_D50_2; + colorCount = 140; + break; + default: + throw std::runtime_error("Unknown color type requested."); + } + + Mat out = Mat(colorCount, 1, CV_32SC3); + + for (int i = 0; i < colorCount; i++) { + lab_color.at(0, 0) = Vec3f( + static_cast(colorData[i][0]), + static_cast(colorData[i][1]), + static_cast(colorData[i][2]) + ); + + cvtColor(lab_color, rgb_color, COLOR_Lab2BGR); + + // Convert to 8-bit RGB values (0-255) but store as int + Vec3f rgb = rgb_color.at(0, 0) * 255; + out.at(i, 0) = Vec3i( + std::min(255, std::max(0, static_cast(rgb[0]))), // Blue + std::min(255, std::max(0, static_cast(rgb[1]))), // Green + std::min(255, std::max(0, static_cast(rgb[2]))) // Red + ); + } + return out; +} + +void CCheckerDetectorImpl:: + checkerAnalysis( + InputArray img_f, + const unsigned int nc, + const std::vector> &colorCharts, + std::vector> &checkers, + float asp, + const Mat &img_rgb_org, + const Mat &img_ycbcr_org, + std::vector &rgb_planes, + std::vector &ycbcr_planes) +{ + size_t N; + std::vector ibox; + + // color chart classic model + CChartModel cccm(m_chartType); + Mat lab; + cccm.copyToColorMat(lab, 0); + lab = lab.reshape(3, lab.size().area()); + lab /= 255; + + Mat mask(img_f.size(), CV_8U); + mask.setTo(Scalar::all(0)); + + N = colorCharts.size(); + std::vector J(N); + for (size_t i = 0; i < N; i++) + { + ibox = colorCharts[i]; + J[i] = cost_function(img_f, mask, lab, ibox); + } + + std::vector idx; + sort(J, idx); + float invAsp = 1 / asp; + size_t n = min(nc, (unsigned)N); + checkers.clear(); + + for (size_t i = 0; i < n; i++) + { + ibox = colorCharts[idx[i]]; + + if (J[i] > m_params.maxError) + continue; + + // redimention box + for (size_t j = 0; j < 4; j++) + ibox[j] = invAsp * ibox[j]; + + Mat charts_rgb, charts_ycbcr; + get_profile(ibox, charts_rgb, charts_ycbcr, img_rgb_org, + img_ycbcr_org, rgb_planes, ycbcr_planes); + + // result + Ptr checker = CChecker::create(); + checker->setBox(ibox); + checker->setTarget(m_chartType); + checker->setChartsRGB(charts_rgb); + checker->setChartsYCbCr(charts_ycbcr); + checker->setCenter(mace_center(ibox)); + checker->setCost(J[i]); + + checkers.push_back(checker); + } +} + +void CCheckerDetectorImpl:: + removeTooCloseDetections() +{ + // Remove these elements which corners are too close to each other. + // Eliminate overlaps!!! + // First detect candidates for removal: + std::vector> tooNearCandidates; + for (int i = 0; i < (int)m_checkers.size(); i++) + { + const Ptr &m1 = m_checkers[i]; + + //calculate the average distance of each corner to the nearest corner of the other chart candidate + for (int j = i + 1; j < (int)m_checkers.size(); j++) + { + const Ptr &m2 = m_checkers[j]; + + float distSquared = 0; + + for (int c = 0; c < 4; c++) + { + Point v = m1->getBox()[c] - m2->getBox()[c]; + distSquared += v.dot(v); + } + + distSquared /= 4; + + if (distSquared < m_params.minInterCheckerDistance) + { + tooNearCandidates.push_back(std::pair(i, j)); + } + } + } + + // Mark for removal the element of the pair with smaller cost + std::vector removalMask(m_checkers.size(), false); + + for (size_t i = 0; i < tooNearCandidates.size(); i++) + { + float p1 = m_checkers[tooNearCandidates[i].first]->getCost(); + float p2 = m_checkers[tooNearCandidates[i].second]->getCost(); + + size_t removalIndex; + if (p1 < p2) + removalIndex = tooNearCandidates[i].second; + else + removalIndex = tooNearCandidates[i].first; + + removalMask[removalIndex] = true; + } + + std::vector> copy_m_checkers = m_checkers; + m_checkers.clear(); + + for (size_t i = 0; i < copy_m_checkers.size(); i++) + { + if (removalMask[i]) + continue; + m_checkers.push_back(copy_m_checkers[i]); + } + + sort( m_checkers.begin(), m_checkers.end(), + [&](const Ptr &a, const Ptr &b) + { + return a->getCost() < b->getCost(); + }); +} + +void CCheckerDetectorImpl:: + get_subbox_chart_physical(const std::vector &points, std::vector &chartPhy) +{ + float w, h; + Point2f v1 = points[1] - points[0]; + Point2f v2 = points[3] - points[0]; + float asp = (float)(norm(v2) / norm(v1)); + + w = 100; + h = (float)floor(100 * asp + 0.5); + + chartPhy.clear(); + chartPhy.resize(4); + chartPhy[0] = Point2f(0, 0); + chartPhy[1] = Point2f(w, 0); + chartPhy[2] = Point2f(w, h); + chartPhy[3] = Point2f(0, h); +} + +void CCheckerDetectorImpl:: + reduce_array(const std::vector &x, std::vector &x_new, float tol) +{ + size_t n = x.size(), nn; + std::vector xx = x; + x_new.clear(); + + // sort array + std::sort(xx.begin(), xx.end()); + + // label array + std::vector label(n); + for (size_t i = 0; i < n; i++) + label[i] = abs(xx[(n + i - 1) % n] - xx[i]) > tol; + + // diff array + for (size_t i = 1; i < n; i++) + label[i] += label[i - 1]; + + // unique array + std::vector ulabel; + unique(label, ulabel); + + // mean for group + nn = ulabel.size(); + x_new.resize(nn); + for (size_t i = 0; i < nn; i++) + { + float mu = 0, s = 0; + for (size_t j = 0; j < n; j++) + { + mu += (label[j] == ulabel[i]) * xx[j]; + s += (label[j] == ulabel[i]); + } + x_new[i] = mu / s; + } + + // diff array + std::vector dif(nn - 1); + for (size_t i = 0; i < nn - 1; i++) + dif[i] = (x_new[(i + 1) % nn] - x_new[i]); + + // max and idx + float fmax = 0; + size_t idx = 0; + for (size_t i = 0; i < nn - 1; i++) + if (fmax < dif[i]) + { + fmax = dif[i]; + idx = i; + } + + // add ... X[i] MAX X[i+] ... + if (fmax > 4 * tol) + x_new.insert(x_new.begin() + idx + 1, (x_new[idx] + x_new[idx + 1]) / 2); +} + +void CCheckerDetectorImpl:: + transform_points_inverse(InputArray T, const std::vector &X, std::vector &Xt) +{ + Matx33f _T = T.getMat(); + Matx33f Tinv = _T.inv(); + transform_points_forward(Tinv, X, Xt); +} +void CCheckerDetectorImpl:: + get_profile( + const std::vector &ibox, + OutputArray charts_rgb, + OutputArray charts_ycbcr, + InputArray im_rgb, + InputArray im_ycbcr, + std::vector &rgb_planes, + std::vector &ycbcr_planes) +{ + // color chart classic model + CChartModel cccm(m_chartType); + Mat lab; + size_t N; + std::vector fbox = cccm.box; + std::vector cellchart = cccm.cellchart; + + // tranformation + Matx33f ccT = getPerspectiveTransform(fbox, ibox); + + Mat mask(im_rgb.size(), CV_8U); + mask.setTo(Scalar::all(0)); + std::vector bch(4), bcht(4); + N = cellchart.size() / 4; + + // Create table charts information + // |p_size|average|stddev|max|min| + // RGB | | | | | | + // YCbCr | + + Mat _charts_rgb = Mat(Size(5, 3 * (int)N), CV_64F); + Mat _charts_ycbcr = Mat(Size(5, 3 * (int)N), CV_64F); + + Scalar mu_rgb, st_rgb, mu_ycb, st_ycb, p_size; + double max_rgb[3], min_rgb[3], max_ycb[3], min_ycb[3]; + + for (int i = 0, k; i < (int)N; i++) + { + k = 4 * i; + bch[0] = cellchart[k + 0]; + bch[1] = cellchart[k + 1]; + bch[2] = cellchart[k + 2]; + bch[3] = cellchart[k + 3]; + polyanticlockwise(bch); + transform_points_forward(ccT, bch, bcht); + + Point2f c(0, 0); + for (int j = 0; j < 4; j++) + c += bcht[j]; + c /= 4; + for (size_t j = 0; j < 4; j++) + bcht[j] = ((bcht[j] - c) * 0.50) + c; + + Rect roi = poly2mask(bcht, im_rgb.size(), mask); + Mat submask = mask(roi); + p_size = sum(submask); + + // rgb space + meanStdDev(im_rgb.getMat()(roi), mu_rgb, st_rgb, submask); + minMaxLoc(rgb_planes[0](roi), &min_rgb[0], &max_rgb[0], NULL, NULL, submask); + minMaxLoc(rgb_planes[1](roi), &min_rgb[1], &max_rgb[1], NULL, NULL, submask); + minMaxLoc(rgb_planes[2](roi), &min_rgb[2], &max_rgb[2], NULL, NULL, submask); + + // create tabla + //|p_size|average|stddev|max|min| + // raw_r + _charts_rgb.at(3 * i + 0, 0) = p_size(0); + _charts_rgb.at(3 * i + 0, 1) = mu_rgb(0); + _charts_rgb.at(3 * i + 0, 2) = st_rgb(0); + _charts_rgb.at(3 * i + 0, 3) = min_rgb[0]; + _charts_rgb.at(3 * i + 0, 4) = max_rgb[0]; + // raw_g + _charts_rgb.at(3 * i + 1, 0) = p_size(0); + _charts_rgb.at(3 * i + 1, 1) = mu_rgb(1); + _charts_rgb.at(3 * i + 1, 2) = st_rgb(1); + _charts_rgb.at(3 * i + 1, 3) = min_rgb[1]; + _charts_rgb.at(3 * i + 1, 4) = max_rgb[1]; + // raw_b + _charts_rgb.at(3 * i + 2, 0) = p_size(0); + _charts_rgb.at(3 * i + 2, 1) = mu_rgb(2); + _charts_rgb.at(3 * i + 2, 2) = st_rgb(2); + _charts_rgb.at(3 * i + 2, 3) = min_rgb[2]; + _charts_rgb.at(3 * i + 2, 4) = max_rgb[2]; + + // YCbCr space + meanStdDev(im_ycbcr.getMat()(roi), mu_ycb, st_ycb, submask); + minMaxLoc(ycbcr_planes[0](roi), &min_ycb[0], &max_ycb[0], NULL, NULL, submask); + minMaxLoc(ycbcr_planes[1](roi), &min_ycb[1], &max_ycb[1], NULL, NULL, submask); + minMaxLoc(ycbcr_planes[2](roi), &min_ycb[2], &max_ycb[2], NULL, NULL, submask); + + // create tabla + //|p_size|average|stddev|max|min| + // raw_Y + _charts_ycbcr.at(3 * i + 0, 0) = p_size(0); + _charts_ycbcr.at(3 * i + 0, 1) = mu_ycb(0); + _charts_ycbcr.at(3 * i + 0, 2) = st_ycb(0); + _charts_ycbcr.at(3 * i + 0, 3) = min_ycb[0]; + _charts_ycbcr.at(3 * i + 0, 4) = max_ycb[0]; + // raw_Cb + _charts_ycbcr.at(3 * i + 1, 0) = p_size(0); + _charts_ycbcr.at(3 * i + 1, 1) = mu_ycb(1); + _charts_ycbcr.at(3 * i + 1, 2) = st_ycb(1); + _charts_ycbcr.at(3 * i + 1, 3) = min_ycb[1]; + _charts_ycbcr.at(3 * i + 1, 4) = max_ycb[1]; + // raw_Cr + _charts_ycbcr.at(3 * i + 2, 0) = p_size(0); + _charts_ycbcr.at(3 * i + 2, 1) = mu_ycb(2); + _charts_ycbcr.at(3 * i + 2, 2) = st_ycb(2); + _charts_ycbcr.at(3 * i + 2, 3) = min_ycb[2]; + _charts_ycbcr.at(3 * i + 2, 4) = max_ycb[2]; + + submask.setTo(Scalar::all(0)); + } + charts_rgb.assign(_charts_rgb); + charts_ycbcr.assign(_charts_ycbcr); +} + +float CCheckerDetectorImpl:: + cost_function(InputArray im_rgb, InputOutputArray mask, InputArray lab, + const std::vector &ibox) +{ + CChartModel cccm(m_chartType); + std::vector fbox = cccm.box; + std::vector cellchart = cccm.cellchart; + + // tranformation + Matx33f ccT = getPerspectiveTransform(fbox, ibox); + std::vector bch(4), bcht(4); + + int N = (int)(cellchart.size() / 4); + + Mat _lab = lab.getMat(); + Mat _im_rgb = im_rgb.getMat(); + + float ec = 0, es = 0; + for (int i = 0, k; i < N; i++) + { + Vec3f r = _lab.at(i); + + k = 4 * i; + bch[0] = cellchart[k + 0]; + bch[1] = cellchart[k + 1]; + bch[2] = cellchart[k + 2]; + bch[3] = cellchart[k + 3]; + polyanticlockwise(bch); + transform_points_forward(ccT, bch, bcht); + + Point2f c(0, 0); + for (int j = 0; j < 4; j++) + c += bcht[j]; + c /= 4; + for (int j = 0; j < 4; j++) + bcht[j] = ((bcht[j] - c) * 0.75) + c; + + Scalar mu, st; + Rect roi = poly2mask(bcht, _im_rgb.size(), mask); + if (!roi.empty()) + { + Mat submask = mask.getMat()(roi); + meanStdDev(_im_rgb(roi), mu, st, submask); + submask.setTo(Scalar::all(0)); + + // cos error + float costh; + costh = (float)(mu.dot(Scalar(r)) / (norm(mu) * norm(r) + FLT_EPSILON)); + ec += (1 - (1 + costh) / 2); + + // standar desviation + es += (float)st.dot(st); + } + } + + // J = arg min ec + es + float J = ec + es; + return J / N; +} + +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/checker_detector.hpp b/modules/objdetect/src/mcc/checker_detector.hpp new file mode 100644 index 0000000000..a50f73ef6d --- /dev/null +++ b/modules/objdetect/src/mcc/checker_detector.hpp @@ -0,0 +1,207 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_CHECKER_DETECTOR_HPP +#define _MCC_CHECKER_DETECTOR_HPP + +#include "opencv2/objdetect.hpp" +#include "charts.hpp" + +namespace cv +{ +namespace mcc +{ + +class CCheckerDetectorImpl : public CCheckerDetector +{ + + typedef std::vector PointsVector; + typedef std::vector ContoursVector; + +public: + CCheckerDetectorImpl(); + CCheckerDetectorImpl(const dnn::Net& _net){ + net = _net; + } + virtual ~CCheckerDetectorImpl(); + + bool process(InputArray image, const std::vector ®ionsOfInterest, + const int nc = 1) CV_OVERRIDE; + + bool process(InputArray image, const int nc = 1) CV_OVERRIDE; + + Ptr getBestColorChecker() CV_OVERRIDE + { + if (m_checkers.size()) + return m_checkers[0]; + return nullptr; + } + + std::vector> getListColorChecker() CV_OVERRIDE + { + return m_checkers; + } + virtual Mat getRefColors() CV_OVERRIDE; + + virtual void setDetectionParams(const DetectorParametersMCC ¶ms) CV_OVERRIDE; + + virtual void setColorChartType(ColorChart chartType) CV_OVERRIDE; + + virtual void setUseDnnModel(bool useDnn) CV_OVERRIDE; + + virtual bool getUseDnnModel() const CV_OVERRIDE; + + virtual const DetectorParametersMCC& getDetectionParams() const CV_OVERRIDE; + + virtual ColorChart getColorChartType() const CV_OVERRIDE; + + virtual void draw(std::vector>& checkers, InputOutputArray img, const Scalar color = CV_RGB(0,250,0), const int thickness = 2) CV_OVERRIDE; + +protected: // methods pipeline + bool _no_net_process(InputArray image, const int nc, std::vector regionsOfInterest); + /// prepareImage + /** @brief Prepare Image + * @param bgrMat image in color space BGR + * @param grayOut gray scale + * @param bgrOut rescale image + * @param aspOut aspect ratio + * @param max_size rescale factor in max dim + */ + virtual void + prepareImage(InputArray bgr, OutputArray grayOut, OutputArray bgrOut, float &aspOut) const; + + /// performThreshold + /** @brief Adaptative threshold + * @param grayscaleImg gray scale image + * @param thresholdImg binary image + * @param wndx, wndy windows size + * @param step + */ + virtual void + performThreshold(InputArray grayscaleImg, OutputArrayOfArrays thresholdImg) const; + + /// findContours + /** @brief find contour in the image + * @param srcImg binary imagen + * @param contours + * @param minContourPointsAllowed + */ + virtual void + findContours(InputArray srcImg, ContoursVector &contours) const; + + /// findCandidates + /** @brief find posibel candidates + * @param contours + * @param detectedCharts + * @param minContourLengthAllowed + */ + virtual void + findCandidates(const ContoursVector &contours, std::vector &detectedCharts); + + /// clustersAnalysis + /** @brief clusters charts analysis + * @param detectedCharts + * @param groups + */ + virtual void + clustersAnalysis(const std::vector &detectedCharts, std::vector &groups); + + /// checkerRecognize + /** @brief checker color recognition + * @param img + * @param detectedCharts + * @param G + * @param colorChartsOut + */ + virtual void + checkerRecognize(InputArray img, const std::vector &detectedCharts, const std::vector &G, + std::vector> &colorChartsOut); + + /// checkerAnalysis + /** @brief evaluate checker + * @param img + * @param img_org + * @param colorCharts + * @param checker + * @param asp + */ + virtual void + checkerAnalysis(InputArray img_rgb_f, const unsigned int nc, + const std::vector> &colorCharts, + std::vector> &checkers, float asp, + const Mat &img_rgb_org, + const Mat &img_ycbcr_org, + std::vector &rgb_planes, + std::vector &ycbcr_planes); + + virtual void + removeTooCloseDetections(); + +protected: + std::vector> m_checkers; + dnn::Net net; + DetectorParametersMCC m_params = DetectorParametersMCC(); + ColorChart m_chartType; + bool m_useDnn = true; + +private: // methods aux + void get_subbox_chart_physical( + const std::vector &points, + std::vector &chartPhy); + + void reduce_array( + const std::vector &x, + std::vector &x_new, + float tol); + + void transform_points_inverse( + InputArray T, + const std::vector &X, + std::vector &Xt); + + void get_profile( + const std::vector &ibox, + OutputArray charts_rgb, + OutputArray charts_ycbcr, + InputArray im_rgb, + InputArray im_ycbcr, + std::vector &rgb_planes, + std::vector &ycbcr_planes); + + /** @brief cost function + * e(p) = ||f(p)||^2 = \sum_k (mu_{k,p}*r_k')/||mu_{k,p}||||r_k|| + ... + * + \sum_k || \sigma_{k,p} ||^2 + */ + float cost_function(InputArray img, InputOutputArray mask, InputArray lab, + const std::vector &ibox); +}; + +} // namespace mcc +} // namespace cv + +#endif //_MCC_CHECKER_DETECTOR_HPP diff --git a/modules/objdetect/src/mcc/checker_model.cpp b/modules/objdetect/src/mcc/checker_model.cpp new file mode 100644 index 0000000000..7f8fae253a --- /dev/null +++ b/modules/objdetect/src/mcc/checker_model.cpp @@ -0,0 +1,487 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" + +#include "checker_model.hpp" +#include "dictionary.hpp" + +namespace cv +{ +namespace mcc +{ + +/////////////////////////////////////////////////////////////////////////////// +/// CChartModel +CChartModel::CChartModel(const ColorChart chartType) +{ + switch (chartType) + { + case MCC24: //Standard + size = Size2i(4, 6); + boxsize = Size2f(11.25, 16.75); + box.resize(4); + box[0] = Point2f(0.00, 0.00); + box[1] = Point2f(16.75, 0.00); + box[2] = Point2f(16.75, 11.25); + box[3] = Point2f(0.00, 11.25); + + cellchart.assign(CChartClassicModelCellchart, CChartClassicModelCellchart + 4 * 24); + center.assign(CChartClassicModelCenter, CChartClassicModelCenter + 24); + + chart.resize(size.area(), std::vector(9)); + for(int color = 0 ; color < (int)chart.size() ; color++) + chart[color].assign(CChartClassicModelColors[color] , CChartClassicModelColors[color] + 9 ); + + break; + case SG140: //DigitalSG + size = Size2i(10, 14); + boxsize = Size2f(27.75, 38.75); + box.resize(4); + box[0] = Point2f(0.00, 0.00); + box[1] = Point2f(38.75, 0.00); + box[2] = Point2f(38.75, 27.75); + box[3] = Point2f(0.00, 27.75); + + cellchart.assign(CChartDigitalSGCellchart, CChartDigitalSGCellchart + 4 * 140); + center.assign(CChartDigitalSGCenter, CChartDigitalSGCenter + 140); + + chart.resize(size.area(), std::vector(9)); + for(int color = 0 ; color < (int)chart.size() ; color++) + chart[color].assign(CChartDigitalSGColors[color] , CChartDigitalSGColors[color] + 9 ); + + break; + case VINYL18: //Vinyl + size = Size2i(3, 6); + boxsize = Size2f(12.50, 18.25); + box.resize(4); + box[0] = Point2f(0.00, 0.00); + box[1] = Point2f(18.25, 0.00); + box[2] = Point2f(18.25, 12.50); + box[3] = Point2f(0.00, 12.50); + + cellchart.assign(CChartVinylCellchart, CChartVinylCellchart + 4 * 18); + center.assign(CChartVinylCenter, CChartVinylCenter + 18); + + chart.resize(size.area(), std::vector(9)); + for(int color = 0 ; color < (int)chart.size() ; color++) + chart[color].assign(CChartVinylColors[color] , CChartVinylColors[color] + 9 ); + + break; + } +} + +CChartModel::~CChartModel() +{ +} + +bool CChartModel:: + evaluate(const SUBCCMModel &subModel, int &offset, int &iTheta, float &error) +{ + + float tError; + int tTheta, tOffset; + error = INFINITY; + bool beval = false; + + // para todas las orientaciones + // min_{ theta,dt } | CC_e - CC | + for (tTheta = 0; tTheta < 8; tTheta++) + { + if (match(subModel, tTheta, tError, tOffset) && tError < error) + { + error = tError; + iTheta = tTheta; + offset = tOffset; + beval = true; + } + } + + return beval; +} + +void CChartModel:: + copyToColorMat(OutputArray lab, int cs) +{ + size_t N, M, k; + + N = size.width; + M = size.height; + Mat im_lab_org((int)N, (int)M, CV_32FC3); + int type_color = 3 * cs; + k = 0; + + for (size_t i = 0; i < N; i++) + { + for (size_t j = 0; j < M; j++) + { + Vec3f &lab_values = im_lab_org.at((int)i, (int)j); + lab_values[0] = chart[k][type_color + 0]; + lab_values[1] = chart[k][type_color + 1]; + lab_values[2] = chart[k][type_color + 2]; + k++; + } + } + + lab.assign(im_lab_org); +} + +void mcc::CChartModel:: + rotate90() +{ + + size = Size2i(size.height, size.width); + + //the matrix is roated clockwise 90 degree, so first row will become last column, second row second last column and so on + //doing this inplace will make the code a bit hard to read, so creating a temporary array + + std::vector _cellchart(cellchart.size()); + std::vector _center(center.size()); + + int k = 0; + for (int i = 0; i < size.width; i++) + { + for (int j = 0; j < size.height; j++) + { + //k contains the new coordintes, + int old_i = size.height - j - 1; + int old_j = i; + int old_k = (old_i)*size.width + old_j; + + _cellchart[4 * k + 0] = cellchart[4 * old_k + 3]; + _cellchart[4 * k + 1] = cellchart[4 * old_k + 0]; + _cellchart[4 * k + 2] = cellchart[4 * old_k + 1]; + _cellchart[4 * k + 3] = cellchart[4 * old_k + 2]; + + // center + _center[k] = center[old_k]; + k++; + } + } + cellchart = _cellchart; + center = _center; + + boxsize = Size2f(boxsize.height, boxsize.width); +} + +void mcc::CChartModel:: + flip() +{ + + std::vector _cellchart(cellchart.size()); + std::vector _center(center.size()); + + int k = 0; + for (int i = 0; i < size.width; i++) + { + for (int j = 0; j < size.height; j++) + { + //k contains the new coordintes, + int old_i = i; + int old_j = size.height - j - 1; + int old_k = (old_i)*size.height + old_j; + + _cellchart[4 * k + 0] = cellchart[4 * old_k + 1]; + _cellchart[4 * k + 1] = cellchart[4 * old_k + 0]; + _cellchart[4 * k + 2] = cellchart[4 * old_k + 3]; + _cellchart[4 * k + 3] = cellchart[4 * old_k + 2]; + + // center + _center[k] = center[old_k]; + k++; + } + } + cellchart = _cellchart; + center = _center; +} + +float CChartModel:: + dist_color_lab(InputArray lab1, InputArray lab2) +{ + + int N = lab1.rows(); + float dist = 0, dist_i; + + Mat _lab1 = lab1.getMat(), _lab2 = lab2.getMat(); + for (int i = 0; i < N; i++) + { + Vec3f v1 = _lab1.at(i, 0); + Vec3f v2 = _lab2.at(i, 0); + // v1[0] = 1; + // v2[0] = 1; // L <- 0 + + // euclidean + Vec3f v = v1 - v2; + dist_i = v.dot(v); + dist += sqrt(dist_i); + + // cosine + //float costh = v1.dot(v2) / (norm(v1)*norm(v2)); + //dist += 1 - (1 + costh) / 2; + } + + dist /= N; + return dist; +} + +bool CChartModel:: + match(const SUBCCMModel &subModel, int iTheta, float &error, int &ierror) +{ + + size_t N, M, k; + + N = size.width; + M = size.height; + Mat im_lab_org((int)N, (int)M, CV_32FC3); + int type_color = 3; + k = 0; + + for (size_t i = 0; i < N; i++) + { + for (size_t j = 0; j < M; j++) + { + Vec3f &lab = im_lab_org.at((int)i, (int)j); + lab[0] = chart[k][type_color + 0]; + lab[1] = chart[k][type_color + 1]; + lab[2] = chart[k][type_color + 2]; + k++; + } + } + + rot90(im_lab_org, iTheta); + N = im_lab_org.rows; + M = im_lab_org.cols; + + size_t n, m; + n = subModel.color_size.height; + m = subModel.color_size.width; + + // boundary condition + if (N < n || M < m) + return false; + + // rgb to La*b* + Mat rgb_est = subModel.sub_chart; + Mat lab_est; + + cvtColor(rgb_est, lab_est, COLOR_BGR2Lab); + + size_t nN, mM; + nN = N - n + 1; + mM = M - m + 1; + std::vector lEcm(nN * mM); + k = 0; + for (size_t i = 0; i < nN; i++) + { + for (size_t j = 0; j < mM; j++) + { + Mat lab_curr, lab_roi; + lab_roi = im_lab_org(Rect((int)j, (int)i, (int)m, (int)n)); + lab_roi.copyTo(lab_curr); + lab_curr = lab_curr.t(); + lab_curr = lab_curr.reshape(3, (int)n * (int)m); + + // Mean squared error + // ECM = 1 / N sum_i(Y - Yp) ^ 2 + lEcm[k] = dist_color_lab(lab_curr, lab_est) / (M * N); + k++; + } + } + + // minimo + error = lEcm[0]; + ierror = 0; + for (int i = 1; i < (int)lEcm.size(); i++) + if (error > lEcm[i]) + { + error = lEcm[i]; + ierror = i; + } + + return true; +} + +void CChartModel:: + rot90(InputOutputArray mat, int itheta) +{ + + //1=CW, 2=CCW, 3=180 + switch (itheta) + { + case 1: //transpose+flip(1)=CW + transpose(mat, mat); + cv::flip(mat, mat, 1); + break; + case 2: //flip(-1)=180 + cv::flip(mat, mat, -1); + break; + case 3: //transpose+flip(0)=CCW + transpose(mat, mat); + cv::flip(mat, mat, 0); + break; + //flipped images start here + case 4: //flip(1)=no rotation, just flipped + cv::flip(mat, mat, 1); + break; + case 5: //flip(1)+transpose + flip(1)=CW + cv::flip(mat, mat, 1); + transpose(mat, mat); + cv::flip(mat, mat, 1); + break; + case 6: //flip(1)+flip(-1)=180 + cv::flip(mat, mat, 1); + cv::flip(mat, mat, -1); + break; + case 7: //flip(1)+transpose+flip(0)=CCW + cv::flip(mat, mat, 1); + transpose(mat, mat); + cv::flip(mat, mat, 0); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// // CChecker + +Ptr CChecker::create() +{ + return makePtr(); +} + +void CCheckerImpl::setTarget(ColorChart _target) +{ + target = _target; +} +void CCheckerImpl::setBox(std::vector _box) +{ + box = _box; +} +void CCheckerImpl::setChartsRGB(Mat _chartsRGB) +{ + chartsRGB = _chartsRGB; +} +void CCheckerImpl::setChartsYCbCr(Mat _chartsYCbCr) +{ + chartsYCbCr = _chartsYCbCr; +} +void CCheckerImpl::setCost(float _cost) +{ + cost = _cost; +} +void CCheckerImpl::setCenter(Point2f _center) +{ + center = _center; +} + +ColorChart CCheckerImpl::getTarget() +{ + return target; +} +std::vector CCheckerImpl::getBox() +{ + return box; +} +std::vector CCheckerImpl::getColorCharts() +{ + // color chart classic model + CChartModel cccm(getTarget()); + Mat lab; + size_t N; + std::vector fbox = cccm.box; + std::vector cellchart = cccm.cellchart; + std::vector charts(cellchart.size()); + + // tranformation + Matx33f ccT = getPerspectiveTransform(fbox, getBox()); + + std::vector bch(4), bcht(4); + N = cellchart.size() / 4; + for (size_t i = 0, k; i < N; i++) + { + k = 4 * i; + for (size_t j = 0ull; j < 4ull; j++) + bch[j] = cellchart[k + j]; + + polyanticlockwise(bch); + transform_points_forward(ccT, bch, bcht); + + Point2f c(0, 0); + for (size_t j = 0; j < 4; j++) + c += bcht[j]; + c /= 4; + for (size_t j = 0ull; j < 4ull; j++) + charts[k+j] = ((bcht[j] - c) * 0.50) + c; + } + return charts; +} +Mat CCheckerImpl::getChartsRGB(bool getStats /* = true */) +{ + if (!getStats){ + Mat src = chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3); + return src; + } + return chartsRGB; +} +Mat CCheckerImpl::getChartsYCbCr() +{ + return chartsYCbCr; +} +float CCheckerImpl::getCost() +{ + return cost; +} +Point2f CCheckerImpl::getCenter() +{ + return center; +} + +void transform_points_forward(const Matx33f& T, const std::vector &X, std::vector &Xt) +{ + size_t N = X.size(); + if (Xt.size() != N) + Xt.resize(N); + std::fill(Xt.begin(), Xt.end(), Point2f(0.f, 0.f)); + if (N == 0) + return; + + Matx31f p, xt; + Point2f pt; + for (size_t i = 0; i < N; i++) + { + p(0, 0) = X[i].x; + p(1, 0) = X[i].y; + p(2, 0) = 1; + xt = T * p; + pt.x = xt(0, 0) / xt(2, 0); + pt.y = xt(1, 0) / xt(2, 0); + Xt[i] = pt; + } +} + +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/checker_model.hpp b/modules/objdetect/src/mcc/checker_model.hpp new file mode 100644 index 0000000000..3da520611f --- /dev/null +++ b/modules/objdetect/src/mcc/checker_model.hpp @@ -0,0 +1,161 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_CHECKER_MODEL_HPP +#define _MCC_CHECKER_MODEL_HPP + +namespace cv +{ +namespace mcc +{ + +//! @addtogroup mcc +//! @{ + +/** CChartModel + * + * @brief Class for handing the different chart models. + * + * This class contains the variables and functions which are specific + * to a given chart type and will be used for it detections. + + */ + +class CChartModel +{ + +public: + /** SUBCCMModel + * + * @brief Information about a continuous subregion of the chart. + * + * Usually not all the cells of the chart can be detected by the + * detector. This submodel contains the information about the + * detected squares. + */ + + typedef struct + _SUBCCMModel + { + + Mat sub_chart; + Size2i color_size; + std::vector centers; + + } SUBCCMModel; + +public: + CChartModel(const ColorChart chartType); + ~CChartModel(); + + /** @brief evaluate submodel in this checker type*/ + bool evaluate(const SUBCCMModel &subModel, int &offset, int &iTheta, float &error); + + // function utils + + void copyToColorMat(OutputArray lab, int cs = 0); + void rotate90(); + void flip(); + +public: + // Cie L*a*b* values use illuminant D50 2 degree observer sRGB values for + // for iluminante D65. + + Size2i size; + Size2f boxsize; + std::vector box; + std::vector cellchart; + std::vector center; + std::vector> chart; + +protected: + /** \brief match checker color + * \param[in] subModel sub-checker + * \param[in] iTheta angle + * \param[out] error + * \param[out] ierror + * \return state + */ + bool match(const SUBCCMModel &subModel, int iTheta, float &error, int &ierror); + + /** \brief euclidian dist L2 for Lab space + * \note + * \f$ \sum_i \sqrt (\sum_k (ab1-ab2)_k.^2) \f$ + * \param[in] lab1 + * \param[in] lab2 + * \return distance + */ + float dist_color_lab(InputArray lab1, InputArray lab2); + + /** \brief rotate matrix 90 degree */ + void rot90(InputOutputArray mat, int itheta); +}; +/** CChecker + * + * \brief checker model + * \author Pedro Marrero Fernandez + * + */ +class CCheckerImpl : public CChecker +{ +public: +public: + CCheckerImpl() {} + ~CCheckerImpl() {} + + void setTarget(ColorChart _target) CV_OVERRIDE; + void setBox(std::vector _box) CV_OVERRIDE; + void setChartsRGB(Mat _chartsRGB) CV_OVERRIDE; + void setChartsYCbCr(Mat _chartsYCbCr) CV_OVERRIDE; + void setCost(float _cost) CV_OVERRIDE; + void setCenter(Point2f _center) CV_OVERRIDE; + + ColorChart getTarget() CV_OVERRIDE; + std::vector getBox() CV_OVERRIDE; + std::vector getColorCharts() CV_OVERRIDE; + Mat getChartsRGB(bool getStats = true) CV_OVERRIDE; + Mat getChartsYCbCr() CV_OVERRIDE; + float getCost() CV_OVERRIDE; + Point2f getCenter() CV_OVERRIDE; + +private: + ColorChart target; ///< type of checkercolor + std::vector box; ///< positions of the corners + Mat chartsRGB; ///< charts profile in rgb color space + Mat chartsYCbCr; ///< charts profile in YCbCr color space + float cost; ///< cost to aproximate + Point2f center; ///< center of the chart. +}; +// @} + +void transform_points_forward(const Matx33f& T, const std::vector &X, std::vector &Xt); + +} // namespace mcc +} // namespace cv + +#endif //_MCC_CHECKER_MODEL_HPP diff --git a/modules/objdetect/src/mcc/common.cpp b/modules/objdetect/src/mcc/common.cpp new file mode 100644 index 0000000000..5b9eddf592 --- /dev/null +++ b/modules/objdetect/src/mcc/common.cpp @@ -0,0 +1,111 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" +#include "common.hpp" + +namespace cv +{ +namespace mcc +{ +Rect poly2mask(const std::vector &poly, Size size, InputOutputArray mask) +{ + // Create Polygon from vertices + std::vector roi_poly; + approxPolyDP(poly, roi_poly, 1.0, true); + + Rect roi = boundingRect(roi_poly); + + // Fill polygon white + fillConvexPoly(mask, &roi_poly[0], (int)roi_poly.size(), 1, 8, 0); + + roi &= Rect(0, 0, size.width, size.height); + if (roi.empty()) + roi = Rect(0, 0, 1, 1); + return roi; +} + +float perimeter(const std::vector &ps) +{ + float sum = 0, dx, dy; + + for (size_t i = 0; i < ps.size(); i++) + { + int i2 = (i + 1) % (int)ps.size(); + + dx = ps[i].x - ps[i2].x; + dy = ps[i].y - ps[i2].y; + + sum += sqrt(dx * dx + dy * dy); + } + + return sum; +} + +Point2f +mace_center(const std::vector &ps) +{ + Point2f center; + int n; + + center = Point2f(0); + n = (int)ps.size(); + for (int i = 0; i < n; i++) + center += ps[i]; + center /= n; + + return center; +} + +void polyanticlockwise(std::vector &points) +{ + // Sort the points in anti-clockwise order + // Trace a line between the first and second point. + // If the third point is at the right side, then the points are anti-clockwise + Point2f v1 = points[1] - points[0]; + Point2f v2 = points[2] - points[0]; + + //if the third point is in the left side, then sort in anti-clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) < 0.0) + std::swap(points[1], points[3]); +} +void polyclockwise(std::vector &points) +{ + // Sort the points in clockwise order + // Trace a line between the first and second point. + // If the third point is at the right side, then the points are clockwise + Point2f v1 = points[1] - points[0]; + Point2f v2 = points[2] - points[0]; + + //if the third point is in the left side, then sort in clockwise order + if ((v1.x * v2.y) - (v1.y * v2.x) > 0.0) + std::swap(points[1], points[3]); +} + +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/common.hpp b/modules/objdetect/src/mcc/common.hpp new file mode 100644 index 0000000000..12c5bb7419 --- /dev/null +++ b/modules/objdetect/src/mcc/common.hpp @@ -0,0 +1,133 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_COMMON_HPP +#define _MCC_COMMON_HPP + +namespace cv +{ +namespace mcc +{ + +Rect poly2mask(const std::vector &poly, Size size, InputOutputArray mask); + +template +void circshift(std::vector &A, int shiff) +{ + if (A.empty() || shiff < 1) + return; + int n = A.size(); + + if (shiff >= n) + return; + + std::vector Tmp(n); + for (int i = shiff; i < n + shiff; i++) + Tmp[(i % n)] = A[i - shiff]; + + A = Tmp; +} + +float perimeter(const std::vector &ps); + +Point2f mace_center(const std::vector &ps); + +template +void unique(const std::vector &A, std::vector &U) +{ + + int n = (int)A.size(); + std::vector Tm = A; + + std::sort(Tm.begin(), Tm.end()); + + U.clear(); + U.push_back(Tm[0]); + for (int i = 1; i < n; i++) + if (Tm[i] != Tm[i - 1]) + U.push_back(Tm[i]); +} + +void polyanticlockwise(std::vector &points); +void polyclockwise(std::vector &points); + +// Does lexical cast of the input argument to string +template +std::string ToString(const T &value) +{ + std::ostringstream stream; + stream << value; + return stream.str(); +} + +template +void change(T &a, T &b) +{ + T c = a; + a = b; + b = c; +} + +template +void sort(std::vector &A, std::vector &idx, bool ord = true) +{ + size_t N = A.size(); + if (N == 0) + return; + + idx.clear(); + idx.resize(N); + for (size_t i = 0; i < N; i++) + idx[i] = (int)i; + + for (size_t i = 0; i < N - 1; i++) + { + + size_t k = i; + T valor = A[i]; + for (size_t j = i + 1; j < N; j++) + { + if ((A[j] < valor && ord) || (A[j] > valor && !ord)) + { + valor = A[j]; + k = j; + } + } + + if (k == i) + continue; + + change(A[i], A[k]); + change(idx[i], idx[k]); + } +} + +} // namespace mcc +} // namespace cv + +#endif //_MCC_COMMON_HPP diff --git a/modules/objdetect/src/mcc/debug.cpp b/modules/objdetect/src/mcc/debug.cpp new file mode 100644 index 0000000000..15d353aefc --- /dev/null +++ b/modules/objdetect/src/mcc/debug.cpp @@ -0,0 +1,68 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" +#include "debug.hpp" + +#ifdef MCC_DEBUG + +#include + +namespace cv +{ +namespace mcc +{ +Scalar randomcolor(RNG &rng) +{ + int icolor = (unsigned)rng; + return Scalar(icolor & 255, (icolor >> 8) & 255, (icolor >> 16) & 255); +} + +void imshow_250xN(const std::string &name_, InputArray patch) +{ + + Mat bigpatch; + Size size = patch.size(); + float asp = (float)size.height / size.width; + int new_size = 550; + resize(patch, bigpatch, Size((int)new_size, int(new_size * asp))); + imshow(name_, bigpatch); +} + +void showAndSave(std::string name, InputArray m, std::string path) +{ + imshow_250xN(name, m); + imwrite(path + "/" + name + ".png", m); + if (waitKey(0) == 'q') + return; +} + +} // namespace mcc +} // namespace cv + +#endif diff --git a/modules/objdetect/src/mcc/debug.hpp b/modules/objdetect/src/mcc/debug.hpp new file mode 100644 index 0000000000..8bf5333b7f --- /dev/null +++ b/modules/objdetect/src/mcc/debug.hpp @@ -0,0 +1,50 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_DEBUG_HPP +#define _MCC_DEBUG_HPP + +#include "opencv2/objdetect/mcc_checker_detector.hpp" +#ifdef MCC_DEBUG + +namespace cv +{ +namespace mcc +{ + +Scalar randomcolor(RNG &rng); + +void imshow_250xN(const std::string &name_, InputArray patch); + +void showAndSave(std::string name, InputArray m, std::string path); +} // namespace mcc +} // namespace cv + +#endif + +#endif //_MCC_DEBUG_HPP diff --git a/modules/objdetect/src/mcc/dictionary.hpp b/modules/objdetect/src/mcc/dictionary.hpp new file mode 100644 index 0000000000..2cc1290fde --- /dev/null +++ b/modules/objdetect/src/mcc/dictionary.hpp @@ -0,0 +1,1187 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_DICTIONARY_HPP +#define _MCC_DICTIONARY_HPP + + + +namespace cv +{ +namespace mcc +{ +/////////////////////////////////////////////////////////////////////////////// +/// CChartClassicModel +const float CChartClassicModelColors[24][9] = { + + // sRGB CIE L*a*b* Munsell Notation + // --------------- ------------------------ Hue Value / Chroma + // R G B L* a* b* + {115.0f, 82.0f, 68.0f, 37.986f, 13.555f, 14.059f, 3.00f, 3.70f, 3.2f}, //1. dark shin + {194.0f, 150.0f, 130.0f, 65.711f, 18.130f, 17.810f, 2.20f, 6.47f, 4.1f}, //2. light skin + {98.0f, 122.0f, 157.0f, 49.927f, -4.880f, -21.925f, 4.30f, 4.95f, 5.0f}, //3. blue skin + {87.0f, 108.0f, 67.0f, 43.139f, -13.095f, 21.905f, 6.70f, 4.20f, 4.1f}, //4. foliage + {133.0f, 128.0f, 177.0f, 55.112f, 8.844f, -25.399f, 9.70f, 5.47f, 6.7f}, //5. blue flower + {103.0f, 189.0f, 170.0f, 70.719f, -33.395f, 0.199f, 2.50f, 7.00f, 6.0f}, //6. bluish green + {214.0f, 126.0f, 44.0f, 62.661f, 36.067f, 57.096f, 5.00f, 6.00f, 11.0f}, //7. orange + {80.0f, 91.0f, 166.0f, 40.020f, 10.410f, -45.964f, 7.50f, 4.00f, 10.7f}, //8. purplish blue + {193.0f, 90.0f, 99.0f, 51.124f, 48.239f, 16.248f, 2.50f, 5.00f, 10.0f}, //9. moderate red + {94.0f, 60.0f, 108.0f, 30.325f, 22.976f, -21.587f, 5.00f, 3.00f, 7.0f}, //10. purple + {157.0f, 188.0f, 64.0f, 72.532f, -23.709f, 57.255f, 5.00f, 7.10f, 9.1f}, //11. yelow green + {224.0f, 163.0f, 46.0f, 71.941f, 19.363f, 67.857f, 10.00f, 7.00f, 10.5f}, //12. orange yellow + {56.0f, 61.0f, 150.0f, 28.778f, 14.179f, -50.297f, 7.50f, 2.90f, 12.7f}, //13. blue + {70.0f, 148.0f, 73.0f, 55.261f, -38.342f, 31.370f, 0.25f, 5.40f, 8.65f}, //14. green + {175.0f, 54.0f, 60.0f, 42.101f, 53.378f, 28.190f, 5.00f, 4.00f, 12.0f}, //15. red + {231.0f, 199.0f, 31.0f, 81.733f, 4.039f, 79.819f, 5.00f, 8.00f, 11.1f}, //16. yellow + {187.0f, 86.0f, 149.0f, 51.935f, 49.986f, -14.574f, 2.50f, 5.00f, 12.0f}, //17. magenta + {8.0f, 133.0f, 161.0f, 51.038f, -28.631f, -28.638f, 5.00f, 5.00f, 8.0f}, //18. cyan + {243.0f, 243.0f, 242.0f, 96.539f, -0.425f, 1.186f, 0.00f, 9.50f, 0.0f}, //19. white(.05*) + {200.0f, 200.0f, 200.0f, 81.257f, -0.638f, -0.335f, 0.00f, 8.00f, 0.0f}, //20. neutral 8(.23*) + {160.0f, 160.0f, 160.0f, 66.766f, -0.734f, -0.504f, 0.00f, 6.50f, 0.0f}, //21. neutral 6.5(.44*) + {122.0f, 122.0f, 121.0f, 50.867f, -0.153f, -0.270f, 0.00f, 5.00f, 0.0f}, //22. neutral 5(.70*) + {85.0f, 85.0f, 85.0f, 35.656f, -0.421f, -1.231f, 0.00f, 3.50f, 0.0f}, //23. neutral 3.5(.1.05*) + {52.0f, 52.0f, 52.0f, 20.461f, -0.079f, -0.973f, 0.00f, 2.00f, 0.0f}, //24. black(1.50*) + +}; + +const Point2f CChartClassicModelCellchart[96] = { + {0.25f, 0.25f}, + {2.75f, 0.25f}, + {2.75f, 2.75f}, + {0.25f, 2.75f}, + {3.00f, 0.25f}, + {5.50f, 0.25f}, + {5.50f, 2.75f}, + {3.00f, 2.75f}, + {5.75f, 0.25f}, + {8.25f, 0.25f}, + {8.25f, 2.75f}, + {5.75f, 2.75f}, + {8.50f, 0.25f}, + {11.00f, 0.25f}, + {11.00f, 2.75f}, + {8.50f, 2.75f}, + {11.25f, 0.25f}, + {13.75f, 0.25f}, + {13.75f, 2.75f}, + {11.25f, 2.75f}, + {14.00f, 0.25f}, + {16.50f, 0.25f}, + {16.50f, 2.75f}, + {14.00f, 2.75f}, + {0.25f, 3.00f}, + {2.75f, 3.00f}, + {2.75f, 5.50f}, + {0.25f, 5.50f}, + {3.00f, 3.00f}, + {5.50f, 3.00f}, + {5.50f, 5.50f}, + {3.00f, 5.50f}, + {5.75f, 3.00f}, + {8.25f, 3.00f}, + {8.25f, 5.50f}, + {5.75f, 5.50f}, + {8.50f, 3.00f}, + {11.00f, 3.00f}, + {11.00f, 5.50f}, + {8.50f, 5.50f}, + {11.25f, 3.00f}, + {13.75f, 3.00f}, + {13.75f, 5.50f}, + {11.25f, 5.50f}, + {14.00f, 3.00f}, + {16.50f, 3.00f}, + {16.50f, 5.50f}, + {14.00f, 5.50f}, + {0.25f, 5.75f}, + {2.75f, 5.75f}, + {2.75f, 8.25f}, + {0.25f, 8.25f}, + {3.00f, 5.75f}, + {5.50f, 5.75f}, + {5.50f, 8.25f}, + {3.00f, 8.25f}, + {5.75f, 5.75f}, + {8.25f, 5.75f}, + {8.25f, 8.25f}, + {5.75f, 8.25f}, + {8.50f, 5.75f}, + {11.00f, 5.75f}, + {11.00f, 8.25f}, + {8.50f, 8.25f}, + {11.25f, 5.75f}, + {13.75f, 5.75f}, + {13.75f, 8.25f}, + {11.25f, 8.25f}, + {14.00f, 5.75f}, + {16.50f, 5.75f}, + {16.50f, 8.25f}, + {14.00f, 8.25f}, + {0.25f, 8.50f}, + {2.75f, 8.50f}, + {2.75f, 11.00f}, + {0.25f, 11.00f}, + {3.00f, 8.50f}, + {5.50f, 8.50f}, + {5.50f, 11.00f}, + {3.00f, 11.00f}, + {5.75f, 8.50f}, + {8.25f, 8.50f}, + {8.25f, 11.00f}, + {5.75f, 11.00f}, + {8.50f, 8.50f}, + {11.00f, 8.50f}, + {11.00f, 11.00f}, + {8.50f, 11.00f}, + {11.25f, 8.50f}, + {13.75f, 8.50f}, + {13.75f, 11.00f}, + {11.25f, 11.00f}, + {14.00f, 8.50f}, + {16.50f, 8.50f}, + {16.50f, 11.00f}, + {14.00f, 11.00f}, + +}; + +const Point2f CChartClassicModelCenter[24] = { + {1.50f, 1.50f}, + {4.25f, 1.50f}, + {7.00f, 1.50f}, + {9.75f, 1.50f}, + {12.50f, 1.50f}, + {15.25f, 1.50f}, + {1.50f, 4.25f}, + {4.25f, 4.25f}, + {7.00f, 4.25f}, + {9.75f, 4.25f}, + {12.50f, 4.25f}, + {15.25f, 4.25f}, + {1.50f, 7.00f}, + {4.25f, 7.00f}, + {7.00f, 7.00f}, + {9.75f, 7.00f}, + {12.50f, 7.00f}, + {15.25f, 7.00f}, + {1.50f, 9.75f}, + {4.25f, 9.75f}, + {7.00f, 9.75f}, + {9.75f, 9.75f}, + {12.50f, 9.75f}, + {15.25f, 9.75f}, + +}; + +////////////////////////////////////////////////////////////////////////////////////////////// +/// CChartDigitalSG +const float CChartDigitalSGColors[140][9] = { + + // sRGB CIE L*a*b* Munsell Notation + // --------------- ------------------------ Hue Value / Chroma + // R G B L* a* b* + {243.6785f, 245.5509f, 243.9088f, 96.55f, -0.91f, 0.57f, -1.0f, -1.0f, -1.0f}, + {19.8630f, 20.3003f, 20.8469f, 6.43f, -0.06f, -0.41f, -1.0f, -1.0f, -1.0f}, + {117.8584f, 118.2530f, 118.1025f, 49.7f, -0.18f, 0.03f, -1.0f, -1.0f, -1.0f}, + {243.5942f, 245.3933f, 243.7273f, 96.5f, -0.89f, 0.59f, -1.0f, -1.0f, -1.0f}, + {19.9993f, 20.4566f, 21.0413f, 6.5f, -0.06f, -0.44f, -1.0f, -1.0f, -1.0f}, + {117.7060f, 118.1645f, 118.0348f, 49.66f, -0.2f, 0.01f, -1.0f, -1.0f, -1.0f}, + {243.6013f, 245.4638f, 243.8033f, 96.52f, -0.91f, 0.58f, -1.0f, -1.0f, -1.0f}, + {20.1535f, 20.4065f, 20.7898f, 6.49f, -0.02f, -0.28f, -1.0f, -1.0f, -1.0f}, + {117.8806f, 118.3134f, 118.1353f, 49.72f, -0.2f, 0.04f, -1.0f, -1.0f, -1.0f}, + {243.4242f, 245.1981f, 243.3717f, 96.43f, -0.91f, 0.67f, -1.0f, -1.0f, -1.0f}, + {117.8668f, 118.3107f, 118.2028f, 49.72f, -0.19f, 0.0f, -1.0f, -1.0f, -1.0f}, + {141.3577f, 28.0252f, 95.0288f, 32.6f, 51.58f, -10.85f, -1.0f, -1.0f, -1.0f}, + {177.5963f, 131.1085f, 179.7258f, 60.75f, 26.22f, -18.6f, -1.0f, -1.0f, -1.0f}, + {108.2022f, 30.1190f, 128.8980f, 28.69f, 48.28f, -39.00f, -1.0f, -1.0f, -1.0f}, + {0.0f, 129.2029f, 199.4861f, 49.38f, -15.43f, -48.48f, -1.0f, -1.0f, -1.0f}, + {0.0f, 162.7924f, 191.4749f, 60.63f, -30.77f, -26.23f, -1.0f, -1.0f, -1.0f}, + {0.0f, 56.2386f, 55.1200f, 19.29f, -26.37f, -6.15f, -1.0f, -1.0f, -1.0f}, + {0.0f, 164.6157f, 165.9222f, 60.15f, -41.77f, -12.60f, -1.0f, -1.0f, -1.0f}, + {58.6915f, 50.1355f, 38.5574f, 21.42f, 1.67f, 8.79f, -1.0f, -1.0f, -1.0f}, + {117.7814f, 118.2399f, 118.1103f, 49.69f, -0.2f, 0.01f, -1.0f, -1.0f, -1.0f}, + {19.8846f, 20.4558f, 21.3702f, 6.5f, -0.03f, -0.67f, -1.0f, -1.0f, -1.0f}, + {64.2572f, 43.9020f, 79.5758f, 21.82f, 17.33f, -18.35f, -1.0f, -1.0f, -1.0f}, + {91.6752f, 90.6539f, 159.4087f, 41.53f, 18.48f, -37.26f, -1.0f, -1.0f, -1.0f}, + {0.0f, 51.7097f, 101.8051f, 19.99f, -0.16f, -36.29f, -1.0f, -1.0f, -1.0f}, + {16.4706f, 156.7786f, 199.5879f, 60.16f, -18.45f, -31.42f, -1.0f, -1.0f, -1.0f}, + {0.0f, 56.3863f, 78.4458f, 19.94f, -17.92f, -20.96f, -1.0f, -1.0f, -1.0f}, + {85.8767f, 152.3418f, 203.7400f, 60.68f, -6.05f, -32.81f, -1.0f, -1.0f, -1.0f}, + {0.0f, 141.8412f, 136.0915f, 50.81f, -49.8f, -9.63f, -1.0f, -1.0f, -1.0f}, + {73.4809f, 163.3852f, 108.1566f, 60.65f, -39.77f, 20.76f, -1.0f, -1.0f, -1.0f}, + {20.1214f, 20.5079f, 21.0922f, 6.53f, -0.03f, -0.43f, -1.0f, -1.0f, -1.0f}, + {243.7254f, 245.5782f, 243.8991f, 96.56f, -0.91f, 0.59f, -1.0f, -1.0f, -1.0f}, + {198.0681f, 211.7588f, 225.2718f, 84.19f, -1.95f, -8.23f, -1.0f, -1.0f, -1.0f}, + {239.3827f, 202.2618f, 211.6298f, 84.75f, 14.55f, 0.23f, -1.0f, -1.0f, -1.0f}, + {169.1422f, 222.5039f, 212.7889f, 84.87f, -19.07f, -0.82f, -1.0f, -1.0f, -1.0f}, + {243.3181f, 203.6053f, 200.4170f, 85.15f, 13.48f, 6.82f, -1.0f, -1.0f, -1.0f}, + {209.6511f, 214.2775f, 159.1455f, 84.17f, -10.45f, 26.78f, -1.0f, -1.0f, -1.0f}, + {215.5821f, 125.8663f, 86.0613f, 61.74f, 31.06f, 36.42f, -1.0f, -1.0f, -1.0f}, + {202.3497f, 141.5423f, 123.4314f, 64.37f, 20.82f, 18.92f, -1.0f, -1.0f, -1.0f}, + {0.0f, 140.4951f, 93.3754f, 50.4f, -53.22f, 14.62f, -1.0f, -1.0f, -1.0f}, + {243.6773f, 245.4176f, 243.6408f, 96.51f, -0.89f, 0.65f, -1.0f, -1.0f, -1.0f}, + {117.9409f, 118.3590f, 118.2027f, 49.74f, -0.19f, 0.03f, -1.0f, -1.0f, -1.0f}, + {110.5974f, 62.7615f, 41.3328f, 31.91f, 18.62f, 21.99f, -1.0f, -1.0f, -1.0f}, + {228.7579f, 115.4417f, 0.0f, 60.74f, 38.66f, 70.97f, -1.0f, -1.0f, -1.0f}, + {0.0f, 42.4374f, 135.4228f, 19.35f, 22.23f, -58.86f, -1.0f, -1.0f, -1.0f}, + {243.6376f, 245.4608f, 243.7265f, 96.52f, -0.91f, 0.62f, -1.0f, -1.0f, -1.0f}, + {20.5471f, 20.7706f, 21.1856f, 6.66f, 0.00f, -0.30f, -1.0f, -1.0f, -1.0f}, + {239.4052f, 173.7086f, 147.9643f, 76.51f, 20.81f, 22.72f, -1.0f, -1.0f, -1.0f}, + {241.8648f, 157.2827f, 136.0207f, 72.79f, 29.15f, 24.18f, -1.0f, -1.0f, -1.0f}, + {12.9473f, 61.0106f, 44.3207f, 22.33f, -20.70f, 5.75f, -1.0f, -1.0f, -1.0f}, + {117.8245f, 118.2597f, 118.1357f, 49.7f, -0.19f, 0.01f, -1.0f, -1.0f, -1.0f}, + {19.9625f, 20.5277f, 21.3483f, 6.53f, -0.05f, -0.61f, -1.0f, -1.0f, -1.0f}, + {198.8077f, 139.4916f, 120.4582f, 63.42f, 20.19f, 19.22f, -1.0f, -1.0f, -1.0f}, + {0.0f, 81.2537f, 163.8455f, 34.94f, 11.64f, -50.70f, -1.0f, -1.0f, -1.0f}, + {54.9984f, 141.2109f, 51.9967f, 52.03f, -44.15f, 39.04f, -1.0f, -1.0f, -1.0f}, + {197.1775f, 196.5945f, 197.0767f, 79.43f, 0.29f, -0.17f, -1.0f, -1.0f, -1.0f}, + {71.5553f, 72.2783f, 72.9901f, 30.67f, -0.14f, -0.53f, -1.0f, -1.0f, -1.0f}, + {193.4891f, 143.5716f, 108.3919f, 63.6f, 14.44f, 26.07f, -1.0f, -1.0f, -1.0f}, + {191.7291f, 146.0328f, 126.4483f, 64.37f, 14.50f, 17.05f, -1.0f, -1.0f, -1.0f}, + {9.3958f, 163.8812f, 128.3477f, 60.01f, -44.33f, 8.49f, -1.0f, -1.0f, -1.0f}, + {20.3474f, 20.7197f, 21.3632f, 6.63f, -0.01f, -0.47f, -1.0f, -1.0f, -1.0f}, + {243.6841f, 245.5904f, 243.8984f, 96.56f, -0.93f, 0.59f, -1.0f, -1.0f, -1.0f}, + {67.5763f, 114.3549f, 150.4402f, 46.37f, -5.09f, -24.46f, -1.0f, -1.0f, -1.0f}, + {195.8311f, 65.0081f, 80.2132f, 47.08f, 52.97f, 20.49f, -1.0f, -1.0f, -1.0f}, + {178.5490f, 0.0f, 27.2862f, 36.04f, 64.92f, 38.51f, -1.0f, -1.0f, -1.0f}, + {157.5618f, 157.8520f, 158.3960f, 65.05f, 0.00f, -0.32f, -1.0f, -1.0f, -1.0f}, + {93.9493f, 94.6924f, 95.1781f, 40.14f, -0.19f, -0.38f, -1.0f, -1.0f, -1.0f}, + {141.2464f, 92.1355f, 59.0432f, 43.77f, 16.46f, 27.12f, -1.0f, -1.0f, -1.0f}, + {195.4679f, 144.4185f, 127.4304f, 64.39f, 17.00f, 16.59f, -1.0f, -1.0f, -1.0f}, + {116.6521f, 159.0566f, 69.5398f, 60.79f, -29.74f, 41.50f, -1.0f, -1.0f, -1.0f}, + {243.5820f, 245.3320f, 243.5738f, 96.48f, -0.89f, 0.64f, -1.0f, -1.0f, -1.0f}, + {117.9142f, 118.3962f, 118.2609f, 49.75f, -0.21f, 0.01f, -1.0f, -1.0f, -1.0f}, + {78.3936f, 96.2850f, 37.2418f, 38.18f, -16.99f, 30.87f, -1.0f, -1.0f, -1.0f}, + {72.2142f, 34.3584f, 92.2239f, 21.31f, 29.14f, -27.51f, -1.0f, -1.0f, -1.0f}, + {244.5244f, 193.8503f, 0.0f, 80.57f, 3.85f, 89.61f, -1.0f, -1.0f, -1.0f}, + {117.8475f, 118.2889f, 118.1270f, 49.71f, -0.20f, 0.03f, -1.0f, -1.0f, -1.0f}, + {145.0665f, 145.2389f, 145.9725f, 60.27f, 0.08f, -0.41f, -1.0f, -1.0f, -1.0f}, + {199.9555f, 153.8996f, 134.3363f, 67.34f, 14.45f, 16.90f, -1.0f, -1.0f, -1.0f}, + {197.1786f, 145.1237f, 124.6796f, 64.69f, 16.95f, 18.57f, -1.0f, -1.0f, -1.0f}, + {36.6899f, 140.3283f, 37.0131f, 51.12f, -49.31f, 44.41f, -1.0f, -1.0f, -1.0f}, + {117.8144f, 118.2644f, 118.1186f, 49.7f, -0.20f, 0.02f, -1.0f, -1.0f, -1.0f}, + {20.2541f, 20.8349f, 21.6861f, 6.67f, -0.05f, -0.64f, -1.0f, -1.0f, -1.0f}, + {112.6726f, 119.9236f, 168.6891f, 51.56f, 9.16f, -26.88f, -1.0f, -1.0f, -1.0f}, + {163.7979f, 183.1273f, 39.4915f, 70.83f, -24.26f, 64.77f, -1.0f, -1.0f, -1.0f}, + {187.9194f, 68.6530f, 141.7077f, 48.06f, 55.33f, -15.61f, -1.0f, -1.0f, -1.0f}, + {82.5699f, 82.9620f, 83.2778f, 35.26f, -0.09f, -0.24f, -1.0f, -1.0f, -1.0f}, + {185.3466f, 184.8995f, 185.4040f, 75.16f, 0.25f, -0.20f, -1.0f, -1.0f, -1.0f}, + {158.9114f, 86.2593f, 40.1532f, 44.54f, 26.27f, 38.93f, -1.0f, -1.0f, -1.0f}, + {119.8508f, 73.3212f, 42.4862f, 35.91f, 16.59f, 26.46f, -1.0f, -1.0f, -1.0f}, + {60.8859f, 169.4042f, 57.1272f, 61.49f, -52.73f, 47.30f, -1.0f, -1.0f, -1.0f}, + {20.1742f, 20.6528f, 21.3198f, 6.59f, -0.05f, -0.50f, -1.0f, -1.0f, -1.0f}, + {243.8216f, 245.6282f, 243.9185f, 96.58f, -0.90f, 0.61f, -1.0f, -1.0f, -1.0f}, + {79.7809f, 185.0914f, 167.7500f, 68.93f, -34.58f, -0.34f, -1.0f, -1.0f, -1.0f}, + {232.6743f, 153.8163f, 0.0f, 69.65f, 20.09f, 78.57f, -1.0f, -1.0f, -1.0f}, + {0.0f, 130.0449f, 163.4567f, 47.79f, -33.18f, -30.21f, -1.0f, -1.0f, -1.0f}, + {38.1665f, 39.8882f, 41.3055f, 15.94f, -0.42f, -1.20f, -1.0f, -1.0f, -1.0f}, + {222.3959f, 223.8081f, 224.4490f, 89.02f, -0.36f, -0.48f, -1.0f, -1.0f, -1.0f}, + {209.4225f, 135.2473f, 108.2643f, 63.43f, 25.44f, 26.25f, -1.0f, -1.0f, -1.0f}, + {211.8297f, 143.7736f, 111.0546f, 65.75f, 22.06f, 27.82f, -1.0f, -1.0f, -1.0f}, + {198.7094f, 135.2492f, 55.8580f, 61.47f, 17.10f, 50.72f, -1.0f, -1.0f, -1.0f}, + {243.7438f, 245.4744f, 243.6791f, 96.53f, -0.89f, 0.66f, -1.0f, -1.0f, -1.0f}, + {118.0486f, 118.4902f, 118.3282f, 49.79f, -0.20f, 0.03f, -1.0f, -1.0f, -1.0f}, + {245.4618f, 204.6880f, 180.8277f, 85.17f, 10.89f, 17.26f, -1.0f, -1.0f, -1.0f}, + {196.2073f, 234.5558f, 213.2861f, 89.74f, -16.52f, 6.19f, -1.0f, -1.0f, -1.0f}, + {215.5873f, 208.3601f, 222.5759f, 84.55f, 5.07f, -6.12f, -1.0f, -1.0f, -1.0f}, + {169.8225f, 217.9657f, 225.3475f, 84.02f, -13.87f, -8.72f, -1.0f, -1.0f, -1.0f}, + {172.9317f, 173.0792f, 173.7270f, 70.76f, 0.07f, -0.35f, -1.0f, -1.0f, -1.0f}, + {107.9946f, 107.9158f, 107.5227f, 45.59f, -0.05f, 0.23f, -1.0f, -1.0f, -1.0f}, + {48.8294f, 48.9265f, 49.4057f, 20.3f, 0.07f, -0.32f, -1.0f, -1.0f, -1.0f}, + {154.4353f, 153.9054f, 41.5640f, 61.79f, -13.41f, 55.42f, -1.0f, -1.0f, -1.0f}, + {117.8827f, 118.3094f, 118.1692f, 49.72f, -0.19f, 0.02f, -1.0f, -1.0f, -1.0f}, + {20.6133f, 21.0396f, 21.6157f, 6.77f, -0.05f, -0.44f, -1.0f, -1.0f, -1.0f}, + {98.5619f, 24.5454f, 42.2740f, 21.85f, 34.37f, 7.83f, -1.0f, -1.0f, -1.0f}, + {203.5372f, 4.2888f, 23.0539f, 42.66f, 67.43f, 48.42f, -1.0f, -1.0f, -1.0f}, + {206.9131f, 119.5498f, 140.5764f, 60.33f, 36.56f, 3.56f, -1.0f, -1.0f, -1.0f}, + {215.5830f, 120.8032f, 119.0661f, 61.22f, 36.61f, 17.32f, -1.0f, -1.0f, -1.0f}, + {251.8782f, 103.8825f, 0.0f, 62.07f, 52.80f, 77.14f, -1.0f, -1.0f, -1.0f}, + {197.8463f, 179.7436f, 0.0f, 72.42f, -9.82f, 89.66f, -1.0f, -1.0f, -1.0f}, + {182.0047f, 145.2731f, 40.3520f, 62.03f, 3.53f, 57.01f, -1.0f, -1.0f, -1.0f}, + {163.8131f, 187.4426f, 0.0f, 71.95f, -27.34f, 73.69f, -1.0f, -1.0f, -1.0f}, + {20.2251f, 20.6453f, 21.2489f, 6.59f, -0.04f, -0.45f, -1.0f, -1.0f, -1.0f}, + {118.0243f, 118.4338f, 118.2614f, 49.77f, -0.19f, 0.04f, -1.0f, -1.0f, -1.0f}, + {188.2188f, 31.6548f, 85.2439f, 41.84f, 62.05f, 10.01f, -1.0f, -1.0f, -1.0f}, + {81.8360f, 27.8344f, 59.8794f, 19.78f, 29.16f, -7.85f, -1.0f, -1.0f, -1.0f}, + {190.3670f, 0.0f, 42.9164f, 39.56f, 65.98f, 33.71f, -1.0f, -1.0f, -1.0f}, + {236.3141f, 51.3514f, 46.5787f, 52.39f, 68.33f, 47.84f, -1.0f, -1.0f, -1.0f}, + {276.7517f, 181.6850f, 0.0f, 81.23f, 24.12f, 87.51f, -1.0f, -1.0f, -1.0f}, + {253.7462f, 195.2511f, 0.0f, 81.8f, 6.78f, 95.75f, -1.0f, -1.0f, -1.0f}, + {183.1389f, 181.4049f, 0.0f, 71.72f, -16.23f, 76.28f, -1.0f, -1.0f, -1.0f}, + {74.5051f, 40.0398f, 25.0285f, 20.31f, 14.45f, 16.74f, -1.0f, -1.0f, -1.0f}, + {117.8060f, 118.2068f, 118.0183f, 49.68f, -0.19f, 0.05f, -1.0f, -1.0f, -1.0f}, + {243.6388f, 245.3229f, 243.4973f, 96.48f, -0.88f, 0.68f, -1.0f, -1.0f, -1.0f}, + {117.8333f, 118.2279f, 118.0773f, 49.69f, -0.18f, 0.03f, -1.0f, -1.0f, -1.0f}, + {19.8618f, 20.1973f, 20.6441f, 6.39f, -0.04f, -0.33f, -1.0f, -1.0f, -1.0f}, + {243.7610f, 245.5085f, 243.6882f, 96.54f, -0.90f, 0.67f, -1.0f, -1.0f, -1.0f}, + {117.9245f, 118.3020f, 118.1192f, 49.72f, -0.18f, 0.05f, -1.0f, -1.0f, -1.0f}, + {20.0461f, 20.4187f, 20.9774f, 6.49f, -0.03f, -0.41f, -1.0f, -1.0f, -1.0f}, + {243.6929f, 245.4207f, 243.5636f, 96.51f, -0.90f, 0.69f, -1.0f, -1.0f, -1.0f}, + {117.8721f, 118.2558f, 118.0350f, 49.7f, -0.19f, 0.07f, -1.0f, -1.0f, -1.0f}, + {20.0704f, 20.3587f, 20.8917f, 6.47f, 0.00f, -0.38f, -1.0f, -1.0f, -1.0f}, + {243.5788f, 245.2700f, 243.4010f, 96.46f, -0.89f, 0.7f, -1.0f, -1.0f, -1.0f}, + +}; + +const Point2f CChartDigitalSGCellchart[560] = { + + {0.25f, 0.25f}, + {2.75f, 0.25f}, + {2.75f, 2.75f}, + {0.25f, 2.75f}, + {3.0f, 0.25f}, + {5.5f, 0.25f}, + {5.5f, 2.75f}, + {3.0f, 2.75f}, + {5.75f, 0.25f}, + {8.25f, 0.25f}, + {8.25f, 2.75f}, + {5.75f, 2.75f}, + {8.5f, 0.25f}, + {11.0f, 0.25f}, + {11.0f, 2.75f}, + {8.5f, 2.75f}, + {11.25f, 0.25f}, + {13.75f, 0.25f}, + {13.75f, 2.75f}, + {11.25f, 2.75f}, + {14.0f, 0.25f}, + {16.5f, 0.25f}, + {16.5f, 2.75f}, + {14.0f, 2.75f}, + {16.75f, 0.25f}, + {19.25f, 0.25f}, + {19.25f, 2.75f}, + {16.75f, 2.75f}, + {19.5f, 0.25f}, + {22.0f, 0.25f}, + {22.0f, 2.75f}, + {19.5f, 2.75f}, + {22.25f, 0.25f}, + {24.75f, 0.25f}, + {24.75f, 2.75f}, + {22.25f, 2.75f}, + {25.0f, 0.25f}, + {27.5f, 0.25f}, + {27.5f, 2.75f}, + {25.0f, 2.75f}, + {27.75f, 0.25f}, + {30.25f, 0.25f}, + {30.25f, 2.75f}, + {27.75f, 2.75f}, + {30.5f, 0.25f}, + {33.0f, 0.25f}, + {33.0f, 2.75f}, + {30.5f, 2.75f}, + {33.25f, 0.25f}, + {35.75f, 0.25f}, + {35.75f, 2.75f}, + {33.25f, 2.75f}, + {36.0f, 0.25f}, + {38.5f, 0.25f}, + {38.5f, 2.75f}, + {36.0f, 2.75f}, + {0.25f, 3.0f}, + {2.75f, 3.0f}, + {2.75f, 5.5f}, + {0.25f, 5.5f}, + {3.0f, 3.0f}, + {5.5f, 3.0f}, + {5.5f, 5.5f}, + {3.0f, 5.5f}, + {5.75f, 3.0f}, + {8.25f, 3.0f}, + {8.25f, 5.5f}, + {5.75f, 5.5f}, + {8.5f, 3.0f}, + {11.0f, 3.0f}, + {11.0f, 5.5f}, + {8.5f, 5.5f}, + {11.25f, 3.0f}, + {13.75f, 3.0f}, + {13.75f, 5.5f}, + {11.25f, 5.5f}, + {14.0f, 3.0f}, + {16.5f, 3.0f}, + {16.5f, 5.5f}, + {14.0f, 5.5f}, + {16.75f, 3.0f}, + {19.25f, 3.0f}, + {19.25f, 5.5f}, + {16.75f, 5.5f}, + {19.5f, 3.0f}, + {22.0f, 3.0f}, + {22.0f, 5.5f}, + {19.5f, 5.5f}, + {22.25f, 3.0f}, + {24.75f, 3.0f}, + {24.75f, 5.5f}, + {22.25f, 5.5f}, + {25.0f, 3.0f}, + {27.5f, 3.0f}, + {27.5f, 5.5f}, + {25.0f, 5.5f}, + {27.75f, 3.0f}, + {30.25f, 3.0f}, + {30.25f, 5.5f}, + {27.75f, 5.5f}, + {30.5f, 3.0f}, + {33.0f, 3.0f}, + {33.0f, 5.5f}, + {30.5f, 5.5f}, + {33.25f, 3.0f}, + {35.75f, 3.0f}, + {35.75f, 5.5f}, + {33.25f, 5.5f}, + {36.0f, 3.0f}, + {38.5f, 3.0f}, + {38.5f, 5.5f}, + {36.0f, 5.5f}, + {0.25f, 5.75f}, + {2.75f, 5.75f}, + {2.75f, 8.25f}, + {0.25f, 8.25f}, + {3.0f, 5.75f}, + {5.5f, 5.75f}, + {5.5f, 8.25f}, + {3.0f, 8.25f}, + {5.75f, 5.75f}, + {8.25f, 5.75f}, + {8.25f, 8.25f}, + {5.75f, 8.25f}, + {8.5f, 5.75f}, + {11.0f, 5.75f}, + {11.0f, 8.25f}, + {8.5f, 8.25f}, + {11.25f, 5.75f}, + {13.75f, 5.75f}, + {13.75f, 8.25f}, + {11.25f, 8.25f}, + {14.0f, 5.75f}, + {16.5f, 5.75f}, + {16.5f, 8.25f}, + {14.0f, 8.25f}, + {16.75f, 5.75f}, + {19.25f, 5.75f}, + {19.25f, 8.25f}, + {16.75f, 8.25f}, + {19.5f, 5.75f}, + {22.0f, 5.75f}, + {22.0f, 8.25f}, + {19.5f, 8.25f}, + {22.25f, 5.75f}, + {24.75f, 5.75f}, + {24.75f, 8.25f}, + {22.25f, 8.25f}, + {25.0f, 5.75f}, + {27.5f, 5.75f}, + {27.5f, 8.25f}, + {25.0f, 8.25f}, + {27.75f, 5.75f}, + {30.25f, 5.75f}, + {30.25f, 8.25f}, + {27.75f, 8.25f}, + {30.5f, 5.75f}, + {33.0f, 5.75f}, + {33.0f, 8.25f}, + {30.5f, 8.25f}, + {33.25f, 5.75f}, + {35.75f, 5.75f}, + {35.75f, 8.25f}, + {33.25f, 8.25f}, + {36.0f, 5.75f}, + {38.5f, 5.75f}, + {38.5f, 8.25f}, + {36.0f, 8.25f}, + {0.25f, 8.5f}, + {2.75f, 8.5f}, + {2.75f, 11.0f}, + {0.25f, 11.0f}, + {3.0f, 8.5f}, + {5.5f, 8.5f}, + {5.5f, 11.0f}, + {3.0f, 11.0f}, + {5.75f, 8.5f}, + {8.25f, 8.5f}, + {8.25f, 11.0f}, + {5.75f, 11.0f}, + {8.5f, 8.5f}, + {11.0f, 8.5f}, + {11.0f, 11.0f}, + {8.5f, 11.0f}, + {11.25f, 8.5f}, + {13.75f, 8.5f}, + {13.75f, 11.0f}, + {11.25f, 11.0f}, + {14.0f, 8.5f}, + {16.5f, 8.5f}, + {16.5f, 11.0f}, + {14.0f, 11.0f}, + {16.75f, 8.5f}, + {19.25f, 8.5f}, + {19.25f, 11.0f}, + {16.75f, 11.0f}, + {19.5f, 8.5f}, + {22.0f, 8.5f}, + {22.0f, 11.0f}, + {19.5f, 11.0f}, + {22.25f, 8.5f}, + {24.75f, 8.5f}, + {24.75f, 11.0f}, + {22.25f, 11.0f}, + {25.0f, 8.5f}, + {27.5f, 8.5f}, + {27.5f, 11.0f}, + {25.0f, 11.0f}, + {27.75f, 8.5f}, + {30.25f, 8.5f}, + {30.25f, 11.0f}, + {27.75f, 11.0f}, + {30.5f, 8.5f}, + {33.0f, 8.5f}, + {33.0f, 11.0f}, + {30.5f, 11.0f}, + {33.25f, 8.5f}, + {35.75f, 8.5f}, + {35.75f, 11.0f}, + {33.25f, 11.0f}, + {36.0f, 8.5f}, + {38.5f, 8.5f}, + {38.5f, 11.0f}, + {36.0f, 11.0f}, + {0.25f, 11.25f}, + {2.75f, 11.25f}, + {2.75f, 13.75f}, + {0.25f, 13.75f}, + {3.0f, 11.25f}, + {5.5f, 11.25f}, + {5.5f, 13.75f}, + {3.0f, 13.75f}, + {5.75f, 11.25f}, + {8.25f, 11.25f}, + {8.25f, 13.75f}, + {5.75f, 13.75f}, + {8.5f, 11.25f}, + {11.0f, 11.25f}, + {11.0f, 13.75f}, + {8.5f, 13.75f}, + {11.25f, 11.25f}, + {13.75f, 11.25f}, + {13.75f, 13.75f}, + {11.25f, 13.75f}, + {14.0f, 11.25f}, + {16.5f, 11.25f}, + {16.5f, 13.75f}, + {14.0f, 13.75f}, + {16.75f, 11.25f}, + {19.25f, 11.25f}, + {19.25f, 13.75f}, + {16.75f, 13.75f}, + {19.5f, 11.25f}, + {22.0f, 11.25f}, + {22.0f, 13.75f}, + {19.5f, 13.75f}, + {22.25f, 11.25f}, + {24.75f, 11.25f}, + {24.75f, 13.75f}, + {22.25f, 13.75f}, + {25.0f, 11.25f}, + {27.5f, 11.25f}, + {27.5f, 13.75f}, + {25.0f, 13.75f}, + {27.75f, 11.25f}, + {30.25f, 11.25f}, + {30.25f, 13.75f}, + {27.75f, 13.75f}, + {30.5f, 11.25f}, + {33.0f, 11.25f}, + {33.0f, 13.75f}, + {30.5f, 13.75f}, + {33.25f, 11.25f}, + {35.75f, 11.25f}, + {35.75f, 13.75f}, + {33.25f, 13.75f}, + {36.0f, 11.25f}, + {38.5f, 11.25f}, + {38.5f, 13.75f}, + {36.0f, 13.75f}, + {0.25f, 14.0f}, + {2.75f, 14.0f}, + {2.75f, 16.5f}, + {0.25f, 16.5f}, + {3.0f, 14.0f}, + {5.5f, 14.0f}, + {5.5f, 16.5f}, + {3.0f, 16.5f}, + {5.75f, 14.0f}, + {8.25f, 14.0f}, + {8.25f, 16.5f}, + {5.75f, 16.5f}, + {8.5f, 14.0f}, + {11.0f, 14.0f}, + {11.0f, 16.5f}, + {8.5f, 16.5f}, + {11.25f, 14.0f}, + {13.75f, 14.0f}, + {13.75f, 16.5f}, + {11.25f, 16.5f}, + {14.0f, 14.0f}, + {16.5f, 14.0f}, + {16.5f, 16.5f}, + {14.0f, 16.5f}, + {16.75f, 14.0f}, + {19.25f, 14.0f}, + {19.25f, 16.5f}, + {16.75f, 16.5f}, + {19.5f, 14.0f}, + {22.0f, 14.0f}, + {22.0f, 16.5f}, + {19.5f, 16.5f}, + {22.25f, 14.0f}, + {24.75f, 14.0f}, + {24.75f, 16.5f}, + {22.25f, 16.5f}, + {25.0f, 14.0f}, + {27.5f, 14.0f}, + {27.5f, 16.5f}, + {25.0f, 16.5f}, + {27.75f, 14.0f}, + {30.25f, 14.0f}, + {30.25f, 16.5f}, + {27.75f, 16.5f}, + {30.5f, 14.0f}, + {33.0f, 14.0f}, + {33.0f, 16.5f}, + {30.5f, 16.5f}, + {33.25f, 14.0f}, + {35.75f, 14.0f}, + {35.75f, 16.5f}, + {33.25f, 16.5f}, + {36.0f, 14.0f}, + {38.5f, 14.0f}, + {38.5f, 16.5f}, + {36.0f, 16.5f}, + {0.25f, 16.75f}, + {2.75f, 16.75f}, + {2.75f, 19.25f}, + {0.25f, 19.25f}, + {3.0f, 16.75f}, + {5.5f, 16.75f}, + {5.5f, 19.25f}, + {3.0f, 19.25f}, + {5.75f, 16.75f}, + {8.25f, 16.75f}, + {8.25f, 19.25f}, + {5.75f, 19.25f}, + {8.5f, 16.75f}, + {11.0f, 16.75f}, + {11.0f, 19.25f}, + {8.5f, 19.25f}, + {11.25f, 16.75f}, + {13.75f, 16.75f}, + {13.75f, 19.25f}, + {11.25f, 19.25f}, + {14.0f, 16.75f}, + {16.5f, 16.75f}, + {16.5f, 19.25f}, + {14.0f, 19.25f}, + {16.75f, 16.75f}, + {19.25f, 16.75f}, + {19.25f, 19.25f}, + {16.75f, 19.25f}, + {19.5f, 16.75f}, + {22.0f, 16.75f}, + {22.0f, 19.25f}, + {19.5f, 19.25f}, + {22.25f, 16.75f}, + {24.75f, 16.75f}, + {24.75f, 19.25f}, + {22.25f, 19.25f}, + {25.0f, 16.75f}, + {27.5f, 16.75f}, + {27.5f, 19.25f}, + {25.0f, 19.25f}, + {27.75f, 16.75f}, + {30.25f, 16.75f}, + {30.25f, 19.25f}, + {27.75f, 19.25f}, + {30.5f, 16.75f}, + {33.0f, 16.75f}, + {33.0f, 19.25f}, + {30.5f, 19.25f}, + {33.25f, 16.75f}, + {35.75f, 16.75f}, + {35.75f, 19.25f}, + {33.25f, 19.25f}, + {36.0f, 16.75f}, + {38.5f, 16.75f}, + {38.5f, 19.25f}, + {36.0f, 19.25f}, + {0.25f, 19.5f}, + {2.75f, 19.5f}, + {2.75f, 22.0f}, + {0.25f, 22.0f}, + {3.0f, 19.5f}, + {5.5f, 19.5f}, + {5.5f, 22.0f}, + {3.0f, 22.0f}, + {5.75f, 19.5f}, + {8.25f, 19.5f}, + {8.25f, 22.0f}, + {5.75f, 22.0f}, + {8.5f, 19.5f}, + {11.0f, 19.5f}, + {11.0f, 22.0f}, + {8.5f, 22.0f}, + {11.25f, 19.5f}, + {13.75f, 19.5f}, + {13.75f, 22.0f}, + {11.25f, 22.0f}, + {14.0f, 19.5f}, + {16.5f, 19.5f}, + {16.5f, 22.0f}, + {14.0f, 22.0f}, + {16.75f, 19.5f}, + {19.25f, 19.5f}, + {19.25f, 22.0f}, + {16.75f, 22.0f}, + {19.5f, 19.5f}, + {22.0f, 19.5f}, + {22.0f, 22.0f}, + {19.5f, 22.0f}, + {22.25f, 19.5f}, + {24.75f, 19.5f}, + {24.75f, 22.0f}, + {22.25f, 22.0f}, + {25.0f, 19.5f}, + {27.5f, 19.5f}, + {27.5f, 22.0f}, + {25.0f, 22.0f}, + {27.75f, 19.5f}, + {30.25f, 19.5f}, + {30.25f, 22.0f}, + {27.75f, 22.0f}, + {30.5f, 19.5f}, + {33.0f, 19.5f}, + {33.0f, 22.0f}, + {30.5f, 22.0f}, + {33.25f, 19.5f}, + {35.75f, 19.5f}, + {35.75f, 22.0f}, + {33.25f, 22.0f}, + {36.0f, 19.5f}, + {38.5f, 19.5f}, + {38.5f, 22.0f}, + {36.0f, 22.0f}, + {0.25f, 22.25f}, + {2.75f, 22.25f}, + {2.75f, 24.75f}, + {0.25f, 24.75f}, + {3.0f, 22.25f}, + {5.5f, 22.25f}, + {5.5f, 24.75f}, + {3.0f, 24.75f}, + {5.75f, 22.25f}, + {8.25f, 22.25f}, + {8.25f, 24.75f}, + {5.75f, 24.75f}, + {8.5f, 22.25f}, + {11.0f, 22.25f}, + {11.0f, 24.75f}, + {8.5f, 24.75f}, + {11.25f, 22.25f}, + {13.75f, 22.25f}, + {13.75f, 24.75f}, + {11.25f, 24.75f}, + {14.0f, 22.25f}, + {16.5f, 22.25f}, + {16.5f, 24.75f}, + {14.0f, 24.75f}, + {16.75f, 22.25f}, + {19.25f, 22.25f}, + {19.25f, 24.75f}, + {16.75f, 24.75f}, + {19.5f, 22.25f}, + {22.0f, 22.25f}, + {22.0f, 24.75f}, + {19.5f, 24.75f}, + {22.25f, 22.25f}, + {24.75f, 22.25f}, + {24.75f, 24.75f}, + {22.25f, 24.75f}, + {25.0f, 22.25f}, + {27.5f, 22.25f}, + {27.5f, 24.75f}, + {25.0f, 24.75f}, + {27.75f, 22.25f}, + {30.25f, 22.25f}, + {30.25f, 24.75f}, + {27.75f, 24.75f}, + {30.5f, 22.25f}, + {33.0f, 22.25f}, + {33.0f, 24.75f}, + {30.5f, 24.75f}, + {33.25f, 22.25f}, + {35.75f, 22.25f}, + {35.75f, 24.75f}, + {33.25f, 24.75f}, + {36.0f, 22.25f}, + {38.5f, 22.25f}, + {38.5f, 24.75f}, + {36.0f, 24.75f}, + {0.25f, 25.0f}, + {2.75f, 25.0f}, + {2.75f, 27.5f}, + {0.25f, 27.5f}, + {3.0f, 25.0f}, + {5.5f, 25.0f}, + {5.5f, 27.5f}, + {3.0f, 27.5f}, + {5.75f, 25.0f}, + {8.25f, 25.0f}, + {8.25f, 27.5f}, + {5.75f, 27.5f}, + {8.5f, 25.0f}, + {11.0f, 25.0f}, + {11.0f, 27.5f}, + {8.5f, 27.5f}, + {11.25f, 25.0f}, + {13.75f, 25.0f}, + {13.75f, 27.5f}, + {11.25f, 27.5f}, + {14.0f, 25.0f}, + {16.5f, 25.0f}, + {16.5f, 27.5f}, + {14.0f, 27.5f}, + {16.75f, 25.0f}, + {19.25f, 25.0f}, + {19.25f, 27.5f}, + {16.75f, 27.5f}, + {19.5f, 25.0f}, + {22.0f, 25.0f}, + {22.0f, 27.5f}, + {19.5f, 27.5f}, + {22.25f, 25.0f}, + {24.75f, 25.0f}, + {24.75f, 27.5f}, + {22.25f, 27.5f}, + {25.0f, 25.0f}, + {27.5f, 25.0f}, + {27.5f, 27.5f}, + {25.0f, 27.5f}, + {27.75f, 25.0f}, + {30.25f, 25.0f}, + {30.25f, 27.5f}, + {27.75f, 27.5f}, + {30.5f, 25.0f}, + {33.0f, 25.0f}, + {33.0f, 27.5f}, + {30.5f, 27.5f}, + {33.25f, 25.0f}, + {35.75f, 25.0f}, + {35.75f, 27.5f}, + {33.25f, 27.5f}, + {36.0f, 25.0f}, + {38.5f, 25.0f}, + {38.5f, 27.5f}, + {36.0f, 27.5f} + +}; + +const Point2f CChartDigitalSGCenter[140] = { + {1.5f, 1.5f}, + {4.25f, 1.5f}, + {7.0f, 1.5f}, + {9.75f, 1.5f}, + {12.5f, 1.5f}, + {15.25f, 1.5f}, + {18.0f, 1.5f}, + {20.75f, 1.5f}, + {23.5f, 1.5f}, + {26.25f, 1.5f}, + {29.0f, 1.5f}, + {31.75f, 1.5f}, + {34.5f, 1.5f}, + {37.25f, 1.5f}, + {1.5f, 4.25f}, + {4.25f, 4.25f}, + {7.0f, 4.25f}, + {9.75f, 4.25f}, + {12.5f, 4.25f}, + {15.25f, 4.25f}, + {18.0f, 4.25f}, + {20.75f, 4.25f}, + {23.5f, 4.25f}, + {26.25f, 4.25f}, + {29.0f, 4.25f}, + {31.75f, 4.25f}, + {34.5f, 4.25f}, + {37.25f, 4.25f}, + {1.5f, 7.0f}, + {4.25f, 7.0f}, + {7.0f, 7.0f}, + {9.75f, 7.0f}, + {12.5f, 7.0f}, + {15.25f, 7.0f}, + {18.0f, 7.0f}, + {20.75f, 7.0f}, + {23.5f, 7.0f}, + {26.25f, 7.0f}, + {29.0f, 7.0f}, + {31.75f, 7.0f}, + {34.5f, 7.0f}, + {37.25f, 7.0f}, + {1.5f, 9.75f}, + {4.25f, 9.75f}, + {7.0f, 9.75f}, + {9.75f, 9.75f}, + {12.5f, 9.75f}, + {15.25f, 9.75f}, + {18.0f, 9.75f}, + {20.75f, 9.75f}, + {23.5f, 9.75f}, + {26.25f, 9.75f}, + {29.0f, 9.75f}, + {31.75f, 9.75f}, + {34.5f, 9.75f}, + {37.25f, 9.75f}, + {1.5f, 12.5f}, + {4.25f, 12.5f}, + {7.0f, 12.5f}, + {9.75f, 12.5f}, + {12.5f, 12.5f}, + {15.25f, 12.5f}, + {18.0f, 12.5f}, + {20.75f, 12.5f}, + {23.5f, 12.5f}, + {26.25f, 12.5f}, + {29.0f, 12.5f}, + {31.75f, 12.5f}, + {34.5f, 12.5f}, + {37.25f, 12.5f}, + {1.5f, 15.25f}, + {4.25f, 15.25f}, + {7.0f, 15.25f}, + {9.75f, 15.25f}, + {12.5f, 15.25f}, + {15.25f, 15.25f}, + {18.0f, 15.25f}, + {20.75f, 15.25f}, + {23.5f, 15.25f}, + {26.25f, 15.25f}, + {29.0f, 15.25f}, + {31.75f, 15.25f}, + {34.5f, 15.25f}, + {37.25f, 15.25f}, + {1.5f, 18.0f}, + {4.25f, 18.0f}, + {7.0f, 18.0f}, + {9.75f, 18.0f}, + {12.5f, 18.0f}, + {15.25f, 18.0f}, + {18.0f, 18.0f}, + {20.75f, 18.0f}, + {23.5f, 18.0f}, + {26.25f, 18.0f}, + {29.0f, 18.0f}, + {31.75f, 18.0f}, + {34.5f, 18.0f}, + {37.25f, 18.0f}, + {1.5f, 20.75f}, + {4.25f, 20.75f}, + {7.0f, 20.75f}, + {9.75f, 20.75f}, + {12.5f, 20.75f}, + {15.25f, 20.75f}, + {18.0f, 20.75f}, + {20.75f, 20.75f}, + {23.5f, 20.75f}, + {26.25f, 20.75f}, + {29.0f, 20.75f}, + {31.75f, 20.75f}, + {34.5f, 20.75f}, + {37.25f, 20.75f}, + {1.5f, 23.5f}, + {4.25f, 23.5f}, + {7.0f, 23.5f}, + {9.75f, 23.5f}, + {12.5f, 23.5f}, + {15.25f, 23.5f}, + {18.0f, 23.5f}, + {20.75f, 23.5f}, + {23.5f, 23.5f}, + {26.25f, 23.5f}, + {29.0f, 23.5f}, + {31.75f, 23.5f}, + {34.5f, 23.5f}, + {37.25f, 23.5f}, + {1.5f, 26.25f}, + {4.25f, 26.25f}, + {7.0f, 26.25f}, + {9.75f, 26.25f}, + {12.5f, 26.25f}, + {15.25f, 26.25f}, + {18.0f, 26.25f}, + {20.75f, 26.25f}, + {23.5f, 26.25f}, + {26.25f, 26.25f}, + {29.0f, 26.25f}, + {31.75f, 26.25f}, + {34.5f, 26.25f}, + {37.25f, 26.25f}, + +}; + +////////////////////////////////////////////////////////////////////////////////////////////// +/// CChartVinyl + +const float CChartVinylColors[18][9] = { + // sRGB CIE L*a*b* Munsell Notation + // --------------- ------------------------ Hue Value / Chroma + // R G B L* a* b* + {255.0f, 255.0f, 255.0f, 100.0f, 0.0052f, -0.0104f, -1.0f, -1.0f, -1.0f}, + {176.0f, 180.0f, 183.0f, 73.0834f, -0.820f, -2.021f, -1.0f, -1.0f, -1.0f}, + {150.0f, 151.0f, 155.0f, 62.493f, 0.426f, -2.231f, -1.0f, -1.0f, -1.0f}, + {119.0f, 120.0f, 124.0f, 50.464f, 0.447f, -2.324f, -1.0f, -1.0f, -1.0f}, + {88.0f, 89.0f, 91.0f, 37.797f, 0.036f, -1.297f, -1.0f, -1.0f, -1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, -1.0f}, + {237.0f, 27.0f, 36.0f, 51.588f, 73.518f, 51.569f, -1.0f, -1.0f, -1.0f}, + {254.0f, 242.0f, 0.0f, 93.699f, -15.734f, 91.942f, -1.0f, -1.0f, -1.0f}, + {106.0f, 189.0f, 71.0f, 69.408f, -46.594f, 50.487f, -1.0f, -1.0f, -1.0f}, + {0.0f, 173.0f, 239.0f, 66.610f, -13.679f, -43.172f, -1.0f, -1.0f, -1.0f}, + {0.0f, 26.0f, 83.0f, 11.711f, 16.980f, -37.176f, -1.0f, -1.0f, -1.0f}, + {238.0f, 1.0f, 141.0f, 51.974f, 81.944f, -8.407f, -1.0f, -1.0f, -1.0f}, + {174.0f, 50.0f, 58.0f, 40.549f, 50.440f, 24.849f, -1.0f, -1.0f, -1.0f}, + {209.0f, 127.0f, 41.0f, 60.816f, 26.069f, 49.442f, -1.0f, -1.0f, -1.0f}, + {7.0f, 136.0f, 165.0f, 52.253f, -19.950f, -23.996f, -1.0f, -1.0f, -1.0f}, + {188.0f, 86.0f, 149.0f, 51.286f, 48.470f, -15.058f, -1.0f, -1.0f, -1.0f}, + {200.0f, 159.0f, 139.0f, 68.707f, 12.296f, 16.213f, -1.0f, -1.0f, -1.0f}, + {183.0f, 147.0f, 125.0f, 63.684f, 10.293f, 16.764f, -1.0f, -1.0f, -1.0f}, +}; + +const Point2f CChartVinylCellchart[72] = { + {0.25f, 0.25f}, + {3.0f, 0.25f}, + {3.0f, 6.25f}, + {0.25f, 6.25f}, + {3.25f, 0.25f}, + {6.0f, 0.25f}, + {6.0f, 6.25f}, + {3.25f, 6.25f}, + {6.25f, 0.25f}, + {9.0f, 0.25f}, + {9.0f, 6.25f}, + {6.25f, 6.25f}, + {9.25f, 0.25f}, + {12.0f, 0.25f}, + {12.0f, 6.25f}, + {9.25f, 6.25f}, + {12.25f, 0.25f}, + {15.0f, 0.25f}, + {15.0f, 6.25f}, + {12.25f, 6.25f}, + {15.25f, 0.25f}, + {18.0f, 0.25f}, + {18.0f, 6.25f}, + {15.25f, 6.25f}, + {0.25f, 6.5f}, + {3.0f, 6.5f}, + {3.0f, 9.25f}, + {0.25f, 9.25f}, + {3.25f, 6.5f}, + {6.0f, 6.5f}, + {6.0f, 9.25f}, + {3.25f, 9.25f}, + {6.25f, 6.5f}, + {9.0f, 6.5f}, + {9.0f, 9.25f}, + {6.25f, 9.25f}, + {9.25f, 6.5f}, + {12.0f, 6.5f}, + {12.0f, 9.25f}, + {9.25f, 9.25f}, + {12.25f, 6.5f}, + {15.0f, 6.5f}, + {15.0f, 9.25f}, + {12.25f, 9.25f}, + {15.25f, 6.5f}, + {18.0f, 6.5f}, + {18.0f, 9.25f}, + {15.25f, 9.25f}, + {0.25f, 9.5f}, + {3.0f, 9.5f}, + {3.0f, 12.25f}, + {0.25f, 12.25f}, + {3.25f, 9.5f}, + {6.0f, 9.5f}, + {6.0f, 12.25f}, + {3.25f, 12.25f}, + {6.25f, 9.5f}, + {9.0f, 9.5f}, + {9.0f, 12.25f}, + {6.25f, 12.25f}, + {9.25f, 9.5f}, + {12.0f, 9.5f}, + {12.0f, 12.25f}, + {9.25f, 12.25f}, + {12.25f, 9.5f}, + {15.0f, 9.5f}, + {15.0f, 12.25f}, + {12.25f, 12.25f}, + {15.25f, 9.5f}, + {18.0f, 9.5f}, + {18.0f, 12.25f}, + {15.25f, 12.25f}, + +}; + +const Point2f CChartVinylCenter[18] = { + {1.625f, 3.25f}, + {4.625f, 3.25f}, + {7.625f, 3.25f}, + {10.625f, 3.25f}, + {13.625f, 3.25f}, + {16.625f, 3.25f}, + {1.625f, 7.875f}, + {4.625f, 7.875f}, + {7.625f, 7.875f}, + {10.625f, 7.875f}, + {13.625f, 7.875f}, + {16.625f, 7.875f}, + {1.625f, 10.875f}, + {4.625f, 10.875f}, + {7.625f, 10.875f}, + {10.625f, 10.875f}, + {13.625f, 10.875f}, + {16.625f, 10.875f}, + +}; + +} // namespace mcc +} // namespace cv + +#endif diff --git a/modules/objdetect/src/mcc/graph_cluster.cpp b/modules/objdetect/src/mcc/graph_cluster.cpp new file mode 100644 index 0000000000..3c1cea3df1 --- /dev/null +++ b/modules/objdetect/src/mcc/graph_cluster.cpp @@ -0,0 +1,139 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "precomp.hpp" +#include "graph_cluster.hpp" + +namespace cv +{ +namespace mcc +{ +CB0cluster::CB0cluster() +{ +} + +CB0cluster::~CB0cluster() +{ +} + +void CB0cluster:: + group() +{ + + size_t n = X.size(); + G.clear(); + G.resize(n); + + for (int i = 0; i < (int)n - 1; i++) + { + std::vector Y; + Y.clear(); + Y.resize(n - i); + Y[0] = 0; + + // 1. group similar blobs + double dist, w, y; + for (int j = i + 1, k = 1; j < (int)n; j++, k++) + { + //dist(X_i,X_j) + dist = norm(X[i] - X[j]); + + //heuristic to combine two sub charts, This is pretty ugly at the moment, + //Looking for somthing better + w = min(abs(W[i] - W[j]) / (W[i] + W[j]), + abs(max(W[i], W[j]) - 24 / 11.0f * min(W[i], W[j])) / (max(W[i], W[j]) + 24 / 11.0f * min(W[i], W[j]))); + w = (w < 0.1); + + y = w * dist; + Y[k] = (y < B0[i]) * y; + ; + } + + if (!G[i]) + G[i] = i + 1; + + std::vector pos_b0; + find(Y, pos_b0); + + size_t m = pos_b0.size(); + if (!m) + continue; + + std::vector pos_nz, pos_z; + for (int j = 0; j < (int)m; j++) + { + pos_b0[j] = pos_b0[j] + i; + if (G[pos_b0[j]]) + pos_nz.push_back(j); + else + pos_z.push_back(j); + } + + for (int j = 0; j < (int)pos_z.size(); j++) + { + pos_z[j] = pos_b0[pos_z[j]]; + G[pos_z[j]] = G[i]; + } + + if (!pos_nz.size()) + continue; + + std::vector g; + for (size_t j = 0; j < pos_nz.size(); j++) + { + pos_nz[j] = pos_b0[pos_nz[j]]; + g.push_back(G[pos_nz[j]]); + } + + unique(g, g); + for (size_t k = 0; k < g.size(); k++) + { + int gk = g[k]; + for (size_t j = 0; j < G.size(); j++) + if (G[j] == gk) + G[j] = G[i]; + } + } + + if (!G[n - 1]) + G[n - 1] = (int)n; + + std::vector S; + S = G; + unique(S, S); + for (int k = 0; k < (int)S.size(); k++) + { + int gk = S[k]; + for (int j = 0; j < (int)G.size(); j++) + if (G[j] == gk) + G[j] = k; + } +} + +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/graph_cluster.hpp b/modules/objdetect/src/mcc/graph_cluster.hpp new file mode 100644 index 0000000000..b9ba1941ca --- /dev/null +++ b/modules/objdetect/src/mcc/graph_cluster.hpp @@ -0,0 +1,74 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_GRAPH_CLUSTERS_HPP +#define _MCC_GRAPH_CLUSTERS_HPP + +namespace cv +{ +namespace mcc +{ + +class CB0cluster +{ +public: + CB0cluster(); + ~CB0cluster(); + + inline void setVertex(const std::vector &V) { X = V; } + inline void setB0(const std::vector &b0) { B0 = b0; } + inline void setWeight(const std::vector &Weight) { W = Weight; } + + void group(); + + void getGroup(std::vector &g) { g = G; } + +private: + //entrada + std::vector X; + std::vector B0; + std::vector W; + + //salida + std::vector G; + +private: + template + void find(const std::vector &A, std::vector &indx) + { + indx.clear(); + for (int i = 0; i < (int)A.size(); i++) + if (A[i]) + indx.push_back(i); + } +}; + +} // namespace mcc +} // namespace cv + +#endif //_MCC_GRAPH_CLUSTERS_HPP diff --git a/modules/objdetect/src/mcc/precomp.hpp b/modules/objdetect/src/mcc/precomp.hpp new file mode 100644 index 0000000000..a470c502a1 --- /dev/null +++ b/modules/objdetect/src/mcc/precomp.hpp @@ -0,0 +1,46 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MCC_PRECOMP_HPP +#define _MCC_PRECOMP_HPP + +#include + +#include +#include +#include +#include + +#include +#include + +#include "opencv2/objdetect.hpp" + +#include "common.hpp" + +#endif //_MCC_PRECOMP_HPP diff --git a/modules/objdetect/src/mcc/wiener_filter.cpp b/modules/objdetect/src/mcc/wiener_filter.cpp new file mode 100644 index 0000000000..5a5d374b1c --- /dev/null +++ b/modules/objdetect/src/mcc/wiener_filter.cpp @@ -0,0 +1,98 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "wiener_filter.hpp" + +namespace cv +{ +namespace mcc +{ +CWienerFilter::CWienerFilter() +{ +} + +CWienerFilter::~CWienerFilter() +{ +} + +void CWienerFilter:: + wiener2(InputArray _src, OutputArray _dst, int szWindowX, int szWindowY) +{ + + CV_Assert(szWindowX > 0 && szWindowY > 0); + + Mat src = _src.getMat(); + + int nRows; + int nCols; + Scalar v = 0; + Mat p_kernel; + Mat srcStub; + //Now create a temporary holding matrix + Mat p_tmpMat1, p_tmpMat2, p_tmpMat3, p_tmpMat4; + double noise_power; + + nRows = szWindowY; + nCols = szWindowX; + + p_kernel = Mat(nRows, nCols, CV_32F, Scalar(1.0 / (double)(nRows * nCols))); + + //Local mean of input + filter2D(src, p_tmpMat1, -1, p_kernel, Point(nCols / 2, nRows / 2)); //localMean + + //Local variance of input + p_tmpMat2 = src.mul(src); + filter2D(p_tmpMat2, p_tmpMat3, -1, p_kernel, Point(nCols / 2, nRows / 2)); + + //Subtract off local_mean^2 from local variance + p_tmpMat4 = p_tmpMat1.mul(p_tmpMat1); //localMean^2 + p_tmpMat3 = p_tmpMat3 - p_tmpMat4; + // Sub(p_tmpMat3, p_tmpMat4, p_tmpMat3); //filter(in^2) - localMean^2 ==> localVariance + + //Estimate noise power + v = mean(p_tmpMat3); + noise_power = v.val[0]; + // result = local_mean + ( max(0, localVar - noise) ./ max(localVar, noise)) .* (in - local_mean) + + p_tmpMat4 = src - p_tmpMat1; //in - local_mean + + p_tmpMat2 = max(p_tmpMat3, noise_power); //max(localVar, noise) + + add(p_tmpMat3, Scalar(-noise_power), p_tmpMat3); //localVar - noise + p_tmpMat3 = max(p_tmpMat3, 0); // max(0, localVar - noise) + + p_tmpMat3 = (p_tmpMat3 / p_tmpMat2); //max(0, localVar-noise) / max(localVar, noise) + + Mat dst = p_tmpMat3.mul(p_tmpMat4); + dst = dst + p_tmpMat1; + + _dst.assign(dst); +} + +} // namespace mcc +} // namespace cv diff --git a/modules/objdetect/src/mcc/wiener_filter.hpp b/modules/objdetect/src/mcc/wiener_filter.hpp new file mode 100644 index 0000000000..381329a9d3 --- /dev/null +++ b/modules/objdetect/src/mcc/wiener_filter.hpp @@ -0,0 +1,68 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +/* + * MIT License + * + * Copyright (c) 2018 Pedro Diamel Marrero Fernández + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * @file wiener_filter + * @brief filter wiener for denoise + * @author: Pedro D. Marrero Fernandez + * @data: 17/05/2016 + */ + +#ifndef _WIENER_FILTER_HPP +#define _WIENER_FILTER_HPP + +#include "precomp.hpp" +namespace cv +{ +namespace mcc +{ + +/// CWienerFilter +/** @brief wiener class filter for denoise + * @author: Pedro D. Marrero Fernandez + * @data: 17/05/2016 + */ +class CWienerFilter +{ +public: + CWienerFilter(); + ~CWienerFilter(); + + /** cvWiener2 + * @brief A Wiener 2D Filter implementation for OpenCV + * @author: Ray Juang / rayver{ _at_ } hkn{ / _dot_ / } berkeley(_dot_) edu + * @date : 12.1.2006 + */ + void wiener2(InputArray _src, OutputArray _dst, int szWindowX, int szWindowY); +}; + +} // namespace mcc + +} // namespace cv + +#endif //_WIENER_FILTER_HPP diff --git a/modules/objdetect/test/test_mcc.cpp b/modules/objdetect/test/test_mcc.cpp new file mode 100644 index 0000000000..1bfb28cc4a --- /dev/null +++ b/modules/objdetect/test/test_mcc.cpp @@ -0,0 +1,46 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "test_precomp.hpp" + +#include + +namespace opencv_test +{ +namespace +{ + +using namespace std; +using namespace cv::mcc; + +/****************************************************************************************\ + * Test detection works properly on the simplest images +\****************************************************************************************/ + +void runCCheckerDetectorBasic(std::string image_name, ColorChart chartType) +{ + Ptr detector = CCheckerDetector::create(); + std::string path = cvtest::findDataFile("mcc/" + image_name); + cv::Mat img = imread(path); + ASSERT_FALSE(img.empty()) << "Test image can't be loaded: " << path; + + detector->setColorChartType(chartType); + ASSERT_TRUE(detector->process(img)); +} +TEST(CV_mccRunCCheckerDetectorBasic, accuracy_SG140) +{ + runCCheckerDetectorBasic("SG140.png", SG140); +} +TEST(CV_mccRunCCheckerDetectorBasic, accuracy_MCC24) +{ + runCCheckerDetectorBasic("MCC24.png", MCC24); +} + +TEST(CV_mccRunCCheckerDetectorBasic, accuracy_VINYL18) +{ + runCCheckerDetectorBasic("VINYL18.png", VINYL18); +} + +} // namespace +} // namespace opencv_test diff --git a/platforms/js/opencv_js.config.py b/platforms/js/opencv_js.config.py index 18891f573f..ac6e4007e0 100644 --- a/platforms/js/opencv_js.config.py +++ b/platforms/js/opencv_js.config.py @@ -148,6 +148,9 @@ objdetect = {'': ['getPredefinedDictionary', 'extendDictionary', 'QRCodeDetectorAruco_Params': ['Params'], 'QRCodeDetectorAruco': ['QRCodeDetectorAruco', 'decode', 'detect', 'detectAndDecode', 'detectMulti', 'decodeMulti', 'detectAndDecodeMulti', 'setDetectorParameters', 'setArucoParameters'], 'barcode_BarcodeDetector': ['BarcodeDetector', 'decode', 'detect', 'detectAndDecode', 'detectMulti', 'decodeMulti', 'detectAndDecodeMulti', 'decodeWithType', 'detectAndDecodeWithType'], + 'mcc_CheckerDetector': ['process', 'getBestColorChecker', 'getListColorChecker', 'create', 'draw', 'getRefColors', 'setDetectionParams', 'getDetectionParams', 'setColorChartType', 'getColorChartType', 'setUseDnnModel', 'getUseDnnModel'], + 'mcc_DetectorParameters': ['DetectorParametersMCC'], + 'mcc_Checker': ['setTarget', 'setBox', 'setChartsRGB', 'setChartsYCbCr', 'setCost', 'setCenter', 'getTarget', 'getBox', 'getColorCharts', 'getChartsRGB', 'getChartsYCbCr', 'getCost', 'getCenter'], 'FaceDetectorYN': ['setInputSize', 'getInputSize', 'setScoreThreshold', 'getScoreThreshold', 'setNMSThreshold', 'getNMSThreshold', 'setTopK', 'getTopK', 'detect', 'create'], } diff --git a/samples/cpp/macbeth_chart_detection.cpp b/samples/cpp/macbeth_chart_detection.cpp new file mode 100644 index 0000000000..f253d29751 --- /dev/null +++ b/samples/cpp/macbeth_chart_detection.cpp @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include "../dnn/common.hpp" + +using namespace std; +using namespace cv; +using namespace cv::dnn; +using namespace mcc; + +const string about = + "This sample demonstrates mcc checker detection with DNN based model and thresholding (default) techniques.\n\n" + "To run default:\n" + "\t ./example_cpp_macbeth_chart_detection --input=path/to/your/input/image/or/video (don't give --input flag if want to use device camera)\n" + "With DNN model:\n" + "\t ./example_cpp_macbeth_chart_detection mcc --input=path/to/your/input/image/or/video\n\n" + "Model path can also be specified using --model argument. And config path can be specified using --config. Download it using python download_models.py mcc from dnn samples directory\n\n"; + +const string param_keys = + "{ help h | | Print help message. }" + "{ @alias | | An alias name of model to extract preprocessing parameters from models.yml file. }" + "{ zoo | ../dnn/models.yml | An optional path to file with preprocessing parameters }" + "{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera.}" + "{ type | 0 | chartType: 0-Standard, 1-DigitalSG, 2-Vinyl, default:0 }" + "{ num_charts | 1 | Maximum number of charts in the image }" + "{ model | | Path to the model file for using dnn model. }"; + +const string backend_keys = format( + "{ backend | default | Choose one of computation backends: " + "default: automatically (by default), " + "openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " + "opencv: OpenCV implementation, " + "vkcom: VKCOM, " + "cuda: CUDA, " + "webnn: WebNN }"); + +const string target_keys = format( + "{ target | cpu | Choose one of target computation devices: " + "cpu: CPU target (by default), " + "opencl: OpenCL, " + "opencl_fp16: OpenCL fp16 (half-float precision), " + "vpu: VPU, " + "vulkan: Vulkan, " + "cuda: CUDA, " + "cuda_fp16: CUDA fp16 (half-float preprocess) }"); + +string keys = param_keys + backend_keys + target_keys; + +static bool processFrame(const Mat& frame, Ptr detector, Mat& src, Mat& tgt, int nc){ + Mat imageCopy = frame.clone(); + if (!detector->process(frame, nc)) + { + return false; + } + vector> checkers = detector->getListColorChecker(); + detector->draw(checkers, frame); + src = checkers[0]->getChartsRGB(false); + tgt = detector->getRefColors(); + + imshow("Image result", frame); + imshow("Original", imageCopy); + + return true; +} + +int main(int argc, char *argv[]) +{ + CommandLineParser parser(argc, argv, keys); + parser.about(about); + + if (parser.has("help")) + { + cout << about << endl; + parser.printMessage(); + return -1; + } + string modelName = parser.get("@alias"); + string zooFile = parser.get("zoo"); + const char* path = getenv("OPENCV_SAMPLES_DATA_PATH"); + if ((path != NULL) || parser.has("@alias") || (parser.get("model") != "")) { + zooFile = findFile(zooFile); + } + else{ + cout<<"[WARN] set the environment variables or pass the arguments --model, --config and models.yml file using --zoo for using dnn based detector. Continuing with default detector.\n\n"; + } + + keys += genPreprocArguments(modelName, zooFile); + parser = CommandLineParser(argc, argv, keys); + + int t = parser.get("type"); + + CV_Assert(0 <= t && t <= 2); + ColorChart chartType = ColorChart(t); + + const string sha1 = parser.get("sha1"); + const string model_path = findModel(parser.get("model"), sha1); + const string config_sha1 = parser.get("config_sha1"); + const string pbtxt_path = findModel(parser.get("config"), config_sha1); + const string backend = parser.get("backend"); + const string target = parser.get("target"); + + int nc = parser.get("num_charts"); + + Ptr detector; + if (model_path != "" && pbtxt_path != ""){ + EngineType engine = ENGINE_AUTO; + if (backend != "default" || target != "cpu"){ + engine = ENGINE_CLASSIC; + } + Net net = readNetFromTensorflow(model_path, pbtxt_path, engine); + net.setPreferableBackend(getBackendID(backend)); + net.setPreferableTarget(getTargetID(target)); + + detector = CCheckerDetector::create(net); + cout<<"Detecting checkers using neural network."<setColorChartType(chartType); + + bool isVideo = true; + Mat image; + VideoCapture cap; + + if (parser.has("input")){ + const string inputFile = parser.get("input"); + image = imread(findFile(inputFile)); + if (!image.empty()) + { + isVideo = false; + } + else + { + // Not an image, so try opening it as a video. + cap.open(findFile(inputFile)); + if (!cap.isOpened()) + { + cout << "[ERROR] Could not open file as an image or video: " << inputFile << endl; + return -1; + } + } + } + else + cap.open(0); + + Mat src, tgt; + bool found = false; + if (isVideo){ + cout<<"To print the actual colors and reference colors for current frame press SPACEBAR. To resume press SPACEBAR again"<