1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Adding macbeth chart detector to objdetect module from opencv_contrib (#26906)

* Added mcc to opencv modules

* Removed color correction module

* Updated parameters return type

* Added python sample for macbeth_chart_detection

* Added models.yml support to samples

* Removed unnecessary headers and classes

* fixed datatype conversion

* fixed datatype conversion

* Cleaned headers and added reference/actual colors to samples

* Added mcc tutorial

* fixed datatype and header

* replaced unsigned with int

* Aligned actual and reference color function, added imread

* Fixed shadow variable

* Updated samples

* Added last frame colors prints

* updated detector class

* Added getter functions and useNet function

* Refactoring

* Fixes in test

* fixed infinite divison issue
This commit is contained in:
Gursimar Singh
2025-03-28 13:43:30 +05:30
committed by GitHub
parent a674ae1bce
commit 69b91cabb4
31 changed files with 5754 additions and 0 deletions
@@ -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<DetectorParameters> 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<CCheckerDetector> 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.
@@ -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
<path_of_your_opencv_build_directory>/bin/example_cpp_macbeth_chart_detection -t=<type_of_chart> -v=<optional_path_to_video_if_not_provided_webcam_will_be_used.mp4> --ci=<optional_camera_id_needed_only_if_video_not_provided> --nc=<optional_maximum_number_of_charts_to_look_for>
```
* -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 <opencv2/mcc.hpp>
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<CCheckerDetector> detector = CCheckerDetector::create();
@endcode
-# **Or create the detector object with neural network**
@code{.cpp}
Ptr<CCheckerDetector> 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<cv::Ptr<mcc::CChecker>> 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
@@ -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
@@ -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
@@ -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 <opencv2/core.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
//---------------------------------------------------------------
// #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<CChecker> create();
public:
CV_WRAP virtual void setTarget(ColorChart _target) = 0;
CV_WRAP virtual void setBox(std::vector<Point2f> _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<Point2f> 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<Point2f> 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<Rect> &regionsOfInterest,
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<mcc::CChecker> getBestColorChecker() = 0;
/** @brief Get the list of all detected colorcheckers
* @return checkers vector of colorcheckers
*/
CV_WRAP virtual std::vector<Ptr<CChecker>> getListColorChecker() = 0;
/** @brief Returns the implementation of the CCheckerDetector.
*
*/
CV_WRAP static Ptr<CCheckerDetector> 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<CCheckerDetector> 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<Ptr<CChecker>>& 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 &params) = 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
+3
View File
@@ -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":
@@ -0,0 +1,4 @@
#include "opencv2/objdetect/mcc_checker_detector.hpp"
typedef std::vector<cv::Ptr<mcc::CChecker>> vector_Ptr_CChecker;
typedef dnn::Net dnn_Net;
+29
View File
@@ -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<CCheckerDetector> detector = CCheckerDetector::create();
detector->setColorChartType(MCC24);
// detect MCC24 board
TEST_CYCLE() {
ASSERT_TRUE(detector->process(img, 1));
}
SANITY_CHECK_NOTHING();
}
} // namespace
} // namespace opencv_test
+180
View File
@@ -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<Point2f> 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<Point3f> 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<int> 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<int> idx;
std::vector<Point3f> 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<Point3f> 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<float> 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<Point3f> lines;
lines.resize(4);
for (size_t i = 0; i < 4; i++)
lines[i] = Lc[idx[i]];
Point3f Vcart;
Point2f Vhom;
std::vector<Point2f> 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
+83
View File
@@ -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<CChart> &chartIn) { chart = chartIn; }
void getCorners(std::vector<Point2f> &cornersOut) { cornersOut = corners; }
void calculate();
private:
std::vector<CChart> chart;
std::vector<Point2f> corners;
private:
bool validateLine(const std::vector<Point3f> &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
+96
View File
@@ -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<Point2f> 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
+90
View File
@@ -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<Point2f> p);
public:
std::vector<Point2f> 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
File diff suppressed because it is too large Load Diff
@@ -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<Point> PointsVector;
typedef std::vector<PointsVector> ContoursVector;
public:
CCheckerDetectorImpl();
CCheckerDetectorImpl(const dnn::Net& _net){
net = _net;
}
virtual ~CCheckerDetectorImpl();
bool process(InputArray image, const std::vector<Rect> &regionsOfInterest,
const int nc = 1) CV_OVERRIDE;
bool process(InputArray image, const int nc = 1) CV_OVERRIDE;
Ptr<CChecker> getBestColorChecker() CV_OVERRIDE
{
if (m_checkers.size())
return m_checkers[0];
return nullptr;
}
std::vector<Ptr<CChecker>> getListColorChecker() CV_OVERRIDE
{
return m_checkers;
}
virtual Mat getRefColors() CV_OVERRIDE;
virtual void setDetectionParams(const DetectorParametersMCC &params) 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<Ptr<CChecker>>& 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<Rect> 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<CChart> &detectedCharts);
/// clustersAnalysis
/** @brief clusters charts analysis
* @param detectedCharts
* @param groups
*/
virtual void
clustersAnalysis(const std::vector<CChart> &detectedCharts, std::vector<int> &groups);
/// checkerRecognize
/** @brief checker color recognition
* @param img
* @param detectedCharts
* @param G
* @param colorChartsOut
*/
virtual void
checkerRecognize(InputArray img, const std::vector<CChart> &detectedCharts, const std::vector<int> &G,
std::vector<std::vector<Point2f>> &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<std::vector<Point2f>> &colorCharts,
std::vector<Ptr<CChecker>> &checkers, float asp,
const Mat &img_rgb_org,
const Mat &img_ycbcr_org,
std::vector<Mat> &rgb_planes,
std::vector<Mat> &ycbcr_planes);
virtual void
removeTooCloseDetections();
protected:
std::vector<Ptr<CChecker>> 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<Point2f> &points,
std::vector<Point2f> &chartPhy);
void reduce_array(
const std::vector<float> &x,
std::vector<float> &x_new,
float tol);
void transform_points_inverse(
InputArray T,
const std::vector<Point2f> &X,
std::vector<Point2f> &Xt);
void get_profile(
const std::vector<Point2f> &ibox,
OutputArray charts_rgb,
OutputArray charts_ycbcr,
InputArray im_rgb,
InputArray im_ycbcr,
std::vector<Mat> &rgb_planes,
std::vector<Mat> &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<Point2f> &ibox);
};
} // namespace mcc
} // namespace cv
#endif //_MCC_CHECKER_DETECTOR_HPP
+487
View File
@@ -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<float>(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<float>(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<float>(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<Vec3f>((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<Point2f> _cellchart(cellchart.size());
std::vector<Point2f> _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<Point2f> _cellchart(cellchart.size());
std::vector<Point2f> _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<Vec3f>(i, 0);
Vec3f v2 = _lab2.at<Vec3f>(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<Vec3f>((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<float> 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> CChecker::create()
{
return makePtr<CCheckerImpl>();
}
void CCheckerImpl::setTarget(ColorChart _target)
{
target = _target;
}
void CCheckerImpl::setBox(std::vector<Point2f> _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<Point2f> CCheckerImpl::getBox()
{
return box;
}
std::vector<Point2f> CCheckerImpl::getColorCharts()
{
// color chart classic model
CChartModel cccm(getTarget());
Mat lab;
size_t N;
std::vector<Point2f> fbox = cccm.box;
std::vector<Point2f> cellchart = cccm.cellchart;
std::vector<Point2f> charts(cellchart.size());
// tranformation
Matx33f ccT = getPerspectiveTransform(fbox, getBox());
std::vector<Point2f> 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<Point2f> &X, std::vector<Point2f> &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
+161
View File
@@ -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<Point2f> 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<Point2f> box;
std::vector<Point2f> cellchart;
std::vector<Point2f> center;
std::vector<std::vector<float>> 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<Point2f> _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<Point2f> getBox() CV_OVERRIDE;
std::vector<Point2f> 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<Point2f> 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<Point2f> &X, std::vector<Point2f> &Xt);
} // namespace mcc
} // namespace cv
#endif //_MCC_CHECKER_MODEL_HPP
+111
View File
@@ -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<Point2f> &poly, Size size, InputOutputArray mask)
{
// Create Polygon from vertices
std::vector<Point> 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<Point2f> &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<Point2f> &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<Point2f> &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<Point2f> &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
+133
View File
@@ -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<Point2f> &poly, Size size, InputOutputArray mask);
template <typename T>
void circshift(std::vector<T> &A, int shiff)
{
if (A.empty() || shiff < 1)
return;
int n = A.size();
if (shiff >= n)
return;
std::vector<T> Tmp(n);
for (int i = shiff; i < n + shiff; i++)
Tmp[(i % n)] = A[i - shiff];
A = Tmp;
}
float perimeter(const std::vector<Point2f> &ps);
Point2f mace_center(const std::vector<Point2f> &ps);
template <typename T>
void unique(const std::vector<T> &A, std::vector<T> &U)
{
int n = (int)A.size();
std::vector<T> 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<Point2f> &points);
void polyclockwise(std::vector<Point2f> &points);
// Does lexical cast of the input argument to string
template <typename T>
std::string ToString(const T &value)
{
std::ostringstream stream;
stream << value;
return stream.str();
}
template <typename T>
void change(T &a, T &b)
{
T c = a;
a = b;
b = c;
}
template <typename T>
void sort(std::vector<T> &A, std::vector<int> &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
+68
View File
@@ -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 <opencv2/highgui.hpp>
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
+50
View File
@@ -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
File diff suppressed because it is too large Load Diff
+139
View File
@@ -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<double> 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<int> pos_b0;
find(Y, pos_b0);
size_t m = pos_b0.size();
if (!m)
continue;
std::vector<int> 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<int> 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<int> 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
@@ -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<Point> &V) { X = V; }
inline void setB0(const std::vector<double> &b0) { B0 = b0; }
inline void setWeight(const std::vector<double> &Weight) { W = Weight; }
void group();
void getGroup(std::vector<int> &g) { g = G; }
private:
//entrada
std::vector<Point> X;
std::vector<double> B0;
std::vector<double> W;
//salida
std::vector<int> G;
private:
template <typename T>
void find(const std::vector<T> &A, std::vector<int> &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
+46
View File
@@ -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 <limits>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/3d.hpp>
#include <opencv2/dnn.hpp>
#include <vector>
#include <string>
#include "opencv2/objdetect.hpp"
#include "common.hpp"
#endif //_MCC_PRECOMP_HPP
@@ -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
@@ -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
+46
View File
@@ -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 <vector>
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<CCheckerDetector> 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
+3
View File
@@ -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'],
}
+194
View File
@@ -0,0 +1,194 @@
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#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<CCheckerDetector> detector, Mat& src, Mat& tgt, int nc){
Mat imageCopy = frame.clone();
if (!detector->process(frame, nc))
{
return false;
}
vector<Ptr<CChecker>> 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<String>("@alias");
string zooFile = parser.get<String>("zoo");
const char* path = getenv("OPENCV_SAMPLES_DATA_PATH");
if ((path != NULL) || parser.has("@alias") || (parser.get<String>("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<int>("type");
CV_Assert(0 <= t && t <= 2);
ColorChart chartType = ColorChart(t);
const string sha1 = parser.get<String>("sha1");
const string model_path = findModel(parser.get<string>("model"), sha1);
const string config_sha1 = parser.get<String>("config_sha1");
const string pbtxt_path = findModel(parser.get<string>("config"), config_sha1);
const string backend = parser.get<String>("backend");
const string target = parser.get<String>("target");
int nc = parser.get<int>("num_charts");
Ptr<CCheckerDetector> 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."<<endl;
}
else{
detector = CCheckerDetector::create();
}
detector->setColorChartType(chartType);
bool isVideo = true;
Mat image;
VideoCapture cap;
if (parser.has("input")){
const string inputFile = parser.get<String>("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"<<endl;
while (cap.grab())
{
Mat frame;
cap.retrieve(frame);
found = processFrame(frame, detector, src, tgt, nc);
int key = waitKey(10);
if (key == ' '){
if(found){
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
cout<<"Actual colors: "<<src<<endl<<endl;
cout<<"Press spacebar to resume."<<endl;
waitKey(0);
cout << "Resumed! Processing continues..." << endl;
}
else{
cout<<"No color chart detected!!"<<endl;
}
}
else if (key == 27) exit(0);
}
if(found){
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
cout<<"Actual colors: "<<src<<endl<<endl;
}
}
else{
found = processFrame(image, detector, src, tgt, nc);
if(found){
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
cout<<"Actual colors: "<<src<<endl<<endl;
waitKey(0);
}
else{
cout<<"No chart detected!!"<<endl;
}
}
return 0;
}
+15
View File
@@ -477,3 +477,18 @@ ldm_inpainting:
height: 512
rgb: true
sample: "ldm_inpainting"
################################################################################
# Macbeth chart detection model.
################################################################################
mcc:
load_info:
url: "https://github.com/gursimarsingh/opencv_zoo/raw/refs/heads/mcc_model/models/macbeth_chart_detector/frozen_inference_graph.pb?download="
sha1: "fae7dbef14c4ae1fca76f3662220fbd460ed5ed6"
model: "frozen_inference_graph.pb"
config_load_info:
url: "https://github.com/gursimarsingh/opencv_zoo/raw/refs/heads/mcc_model/models/macbeth_chart_detector/graph.pbtxt?download="
sha1: "8350cb8f078ecefa1cd566e89930ede25a192310"
config: "graph.pbtxt"
sample: "mcc"
+155
View File
@@ -0,0 +1,155 @@
import cv2 as cv
import numpy as np
import argparse
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
from dnn.common import *
def get_args_parser(func_args):
backends = ("default", "openvino", "opencv", "vkcom", "cuda")
targets = ("cpu", "opencl", "opencl_fp16", "ncs2_vpu", "hddl_vpu", "vulkan", "cuda", "cuda_fp16")
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--zoo', default=os.path.join(os.path.dirname(os.path.abspath(__file__)), '../dnn', 'models.yml'),
help='An optional path to file with preprocessing parameters.')
parser.add_argument('--input', help='Path to input image or video file. Skip this argument to capture frames from a camera.', default=0, required=False)
parser.add_argument('--method', help='choose method: dexined or canny', default='canny', required=False)
parser.add_argument('--chart_type', type=int, default=0,
help='chartType: 0-Standard, 1-DigitalSG, 2-Vinyl, default:0')
parser.add_argument('--num_charts', type=int, default=1,
help='Maximum number of charts in the image')
parser.add_argument('--backend', default="default", type=str, choices=backends,
help="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")
parser.add_argument('--target', default="cpu", type=str, choices=targets,
help="Choose one of target computation devices: "
"cpu: CPU target (by default), "
"opencl: OpenCL, "
"opencl_fp16: OpenCL fp16 (half-float precision), "
"ncs2_vpu: NCS2 VPU, "
"hddl_vpu: HDDL VPU, "
"vulkan: Vulkan, "
"cuda: CUDA, "
"cuda_fp16: CUDA fp16 (half-float preprocess)")
args, _ = parser.parse_known_args()
add_preproc_args(args.zoo, parser, 'mcc', 'mcc')
parser = argparse.ArgumentParser(parents=[parser],
description='''
To run:
Default:
python macbeth_chart_detection.py --input=path/to/your/input/image/or/video (don't give --input flag if want to use device camera)
DNN model:
python macbeth_chart_detection.py mcc --input=path/to/your/input/image/or/video
Model path can also be specified using --model argument. And config path can be specified using --config.
''', formatter_class=argparse.RawTextHelpFormatter)
return parser.parse_args(func_args)
def process_frame(frame, detector, num_charts):
image_copy = frame.copy()
if not detector.process(frame, num_charts):
return None, None
checkers = detector.getListColorChecker()
detector.draw(checkers, frame)
src = checkers[0].getChartsRGB(False)
tgt = detector.getRefColors()
cv.imshow("image result | Press ESC to quit", frame)
cv.imshow("original", image_copy)
return src, tgt
def main(func_args=None):
args = get_args_parser(func_args)
if not (0 <= args.chart_type <= 2):
raise ValueError("chartType must be 0, 1, or 2")
if os.getenv('OPENCV_SAMPLES_DATA_PATH') is not None:
try:
args.model = findModel(args.model, args.sha1)
args.config = findModel(args.config, args.config_sha1)
except:
print("[WARN] Model file not provided, using default detector. Pass model using --model and config using --config to use dnn based detector.\n\n")
args.model = None
args.config = None
else:
args.model = None
args.config = None
print("[WARN] Model file not provided, using default detector. Pass model using --model and config using --config to use dnn based detector. Or, set OPENCV_SAMPLES_DATA_PATH environment variable.\n\n")
if args.model and args.config:
# Load the DNN from TensorFlow model
engine = cv.dnn.ENGINE_AUTO
if args.backend != "default" or args.target != "cpu":
engine = cv.dnn.ENGINE_CLASSIC
net = cv.dnn.readNetFromTensorflow(args.model, args.config, engine)
net.setPreferableBackend(get_backend_id(args.backend))
net.setPreferableTarget(get_target_id(args.target))
detector = cv.mcc_CCheckerDetector.create(net)
print("Detecting checkers using neural network.")
else:
detector = cv.mcc_CCheckerDetector.create()
print("Detecting checkers using default method (no DNN).")
detector.setColorChartType(args.chart_type)
is_video = True
if args.input:
image = cv.imread(findFile(args.input))
if image is not None:
is_video = False
else:
cap = cv.VideoCapture(findFile(args.input))
else:
cap = cv.VideoCapture(0)
if is_video:
print("To print the actual colors and reference colors for current frame press SPACEBAR. To resume press SPACEBAR again")
while True:
ret, frame = cap.read()
if not ret:
break
src, tgt = process_frame(frame, detector, args.num_charts)
key = cv.waitKey(10) & 0xFF
if key == ord(' '):
if src is None or tgt is None:
print("No color chart detected!!")
else:
print("Actual colors: ", src)
print("Reference colors: ", tgt)
print("Press spacebar to resume.")
cv.waitKey(0)
print("Resumed! Processing continues...")
elif key == 27:
exit(0)
if src is not None or tgt is not None:
print("Actual colors: ", src)
print("Reference colors: ", tgt)
else:
src, tgt = process_frame(image, detector, args.num_charts)
if src is None or tgt is None:
print("No color chart detected!!")
else:
print("Actual colors: ", src)
print("Reference colors: ", tgt)
cv.waitKey(0)
cv.destroyAllWindows()
if __name__ == "__main__":
main()