diff --git a/modules/mcc/CMakeLists.txt b/modules/mcc/CMakeLists.txt index 806303505f3..7bd55435a45 100644 --- a/modules/mcc/CMakeLists.txt +++ b/modules/mcc/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Macbeth Chart Detection") -ocv_define_module(mcc opencv_core opencv_imgproc opencv_calib3d opencv_photo opencv_dnn WRAP python) +ocv_define_module(mcc opencv_core opencv_imgproc opencv_calib3d opencv_photo opencv_dnn WRAP python) diff --git a/modules/mcc/include/opencv2/mcc/checker_model.hpp b/modules/mcc/include/opencv2/mcc/checker_model.hpp index c13d5afc585..f42dab4f264 100644 --- a/modules/mcc/include/opencv2/mcc/checker_model.hpp +++ b/modules/mcc/include/opencv2/mcc/checker_model.hpp @@ -73,26 +73,35 @@ class CV_EXPORTS_W CChecker CV_WRAP static Ptr create(); public: - // CV_PROP_RW TYPECHART target; ///< type of checkercolor - // CV_PROP_RW std::vector box; ///< positions of the corners - // CV_PROP_RW cv::Mat charts_rgb; ///< charts profile in rgb color space - // CV_PROP_RW cv::Mat charts_ycbcr; ///< charts profile in YCbCr color space - // CV_PROP_RW float cost; ///< cost to aproximate - // CV_PROP_RW cv::Point2f center; ///< center of the chart. CV_WRAP virtual void setTarget(TYPECHART _target) = 0; CV_WRAP virtual void setBox(std::vector _box) = 0; + CV_WRAP virtual void setPerPatchCosts(std::vector _patchCosts) = 0; + + // Detected directly by the detector or found using geometry, useful to determine if a patch is occluded + CV_WRAP virtual void setActualDetectedContours(std::vector> detectionType) = 0; CV_WRAP virtual void setChartsRGB(Mat _chartsRGB) = 0; CV_WRAP virtual void setChartsYCbCr(Mat _chartsYCbCr) = 0; + CV_WRAP virtual void setActualChartsColors(Mat _actualChartsColors) = 0; // the acutal color profile CV_WRAP virtual void setCost(float _cost) = 0; CV_WRAP virtual void setCenter(Point2f _center) = 0; + CV_WRAP virtual void setPatchCenters(std::vector _patchCenters) = 0; + CV_WRAP virtual void setPatchBoundingBoxes(std::vector _patchBoundingBoxes) = 0; CV_WRAP virtual TYPECHART getTarget() = 0; CV_WRAP virtual std::vector getBox() = 0; + CV_WRAP virtual std::vector getPerPatchCosts() = 0; + CV_WRAP virtual std::vector> getActualDetectedContours() = 0; CV_WRAP virtual Mat getChartsRGB() = 0; CV_WRAP virtual Mat getChartsYCbCr() = 0; + CV_WRAP virtual Mat getActualChartsColors() = 0; // the acutal color profile CV_WRAP virtual float getCost() = 0; CV_WRAP virtual Point2f getCenter() = 0; + CV_WRAP virtual std::vector getPatchCenters() = 0; + CV_WRAP virtual std::vector getPatchBoundingBoxes(float sideRatio=0.5) =0; + + + CV_WRAP virtual bool calculate() = 0; ///< Return false if some error occured, true otherwise }; /** \brief checker draw @@ -115,10 +124,16 @@ class CV_EXPORTS_W CCheckerDraw public: virtual ~CCheckerDraw() {} /** \brief Draws the checker to the given image. - * \param img image in color space BGR + * \param img image in color space BGR, original image gets modified + * \param sideRatio ratio between the side length of drawed boxes, and the actual side of squares + * keeping sideRatio = 1.0 is not recommended as the border can be a bit inaccurate + * in detection, for best output keep it at less than 1.0 + * \param drawActualDetection draw the actually detected patch,i.e,, the non occuled ones, + * useful for debugging, default (false) * \return void */ - CV_WRAP virtual void draw(InputOutputArray img) = 0; + CV_WRAP virtual void draw(InputOutputArray img, float sideRatio = 0.5, + bool drawActualDetection = false) = 0; /** \brief Create a new CCheckerDraw object. * \param pChecker The checker which will be drawn by this object. * \param color The color by with which the squares of the checker @@ -132,6 +147,21 @@ class CV_EXPORTS_W CCheckerDraw int thickness = 2); }; +/** @brief draws a single mcc::ColorChecker on the given image + * This is a functional version of the CCheckerDraw class, provided for convenience + * \param img image in color space BGR, it gets changed by this call + * \param pChecker pointer to the CChecker, which contains the detection + * \param color color used for drawing + * \param thickness thickness of the boxes drawed + * \param sideRatio ratio between the side length of drawed boxes, and the actual side of squares + * keeping sideRatio = 1.0 is not recommended as the border can be a bit inaccurate + * in detection, for best output keep it at less than 1.0 + * \param drawActualDetection draw the actually detected patch,i.e,, the non occuled ones, + * useful for debugging, default (false) + */ +CV_EXPORTS_W void drawColorChecker(InputOutputArray img, Ptr pChecker, + cv::Scalar color = CV_RGB(0, 255, 0), int thickness = 2, float sideRatio = 0.5, + bool drawActualDetection = false); //! @} mcc } // namespace mcc } // namespace cv diff --git a/modules/mcc/samples/basic_color_calibration.cpp b/modules/mcc/samples/basic_color_calibration.cpp new file mode 100644 index 00000000000..699bae8f034 --- /dev/null +++ b/modules/mcc/samples/basic_color_calibration.cpp @@ -0,0 +1,168 @@ +#include + +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace mcc; + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace mcc; + +const char *about = "Basic color calibration using simple 3x3 linear color matrix"; +const char *keys = { + "{ help h usage ? | | show this message }" + "{t | 0 | chartType: 0-Standard, 1-DigitalSG, 2-Vinyl, default:0}" + "{m | | File path of model, if you don't have the model you can \ + find the link in the documentation}" + "{pb | | File path of pbtxt file, available along with with the model \ + file }" + "{v | | Input from video file, if ommited, input comes from camera }" + "{ci | 0 | Camera id if input doesnt come from video (-v) }" + "{nc | 1 | Maximum number of charts in the image }" + "{use_gpu | | Add this flag if you want to use gpu}"}; + +int main(int argc, char *argv[]) +{ + // ---------------------------------------------------------- + // Scroll down a bit (~50 lines) to find actual relevant code + // ---------------------------------------------------------- + + CommandLineParser parser(argc, argv, keys); + parser.about(about); + + if (parser.has("help")) + { + parser.printMessage(); + return -1; + } + + int t = parser.get("t"); + + CV_Assert(0 <= t && t <= 2); + TYPECHART chartType = TYPECHART(t); + + string model_path = parser.get("m"); + string pbtxt_path = parser.get("pb"); + + int camId = parser.get("ci"); + int nc = parser.get("nc"); + + String video; + + if (parser.has("v")) + video = parser.get("v"); + + bool use_gpu = parser.has("use_gpu"); + + if (!parser.check()) + { + parser.printErrors(); + parser.printMessage(); + return 0; + } + + VideoCapture inputVideo; + int waitTime; + if (!video.empty()) + { + inputVideo.open(video); + waitTime = 10; + } + else + { + inputVideo.open(camId); + waitTime = 10; + } + + //-------------------------------------------------------------------------- + //-------------------------Actual Relevant Code----------------------------- + //-------------------------------------------------------------------------- + + //load the network + + cv::dnn::Net net = cv::dnn::readNetFromTensorflow(model_path, pbtxt_path); + + if (use_gpu) + { + net.setPreferableBackend(dnn::DNN_BACKEND_CUDA); + net.setPreferableTarget(dnn::DNN_TARGET_CUDA); + } + + Ptr detector = CCheckerDetector::create(); + if (!detector->setNet(net)) + { + cout << "Loading Model failed: Aborting" << endl; + return 0; + } + namedWindow("original", WINDOW_NORMAL); + namedWindow("image result | q or esc to quit", WINDOW_NORMAL); + + while (inputVideo.grab()) + { + Mat image, imageCopy; + inputVideo.retrieve(image); + + imageCopy = image.clone(); + + // Marker type to detect + if (!detector->process(image, chartType, nc, true)) + { + printf("ChartColor not detected \n"); + } + else + { + + // get checker + std::vector> checkers = detector->getListColorChecker(); + for (Ptr checker : checkers) + { + // current checker + Ptr cdraw = CCheckerDraw::create(checker); + cdraw->draw(image); + Mat a = checker->getChartsRGB(); + Mat b = checker->getActualChartsColors(); + + a = a(Range::all(), Range(1, 2)).clone(); // second column contains the mean of each color + a = a.reshape(1, a.rows/3); + a.convertTo(a, CV_32F ); + + b = b(Range::all(), Range(0,3)).clone(); // The first 3 are rgb + + Mat x; + cv::solve(a, b, x,DECOMP_SVD); + + int originalRows= image.rows; + + cvtColor(image, image, COLOR_BGR2RGB); + image.convertTo(image, CV_32FC3); + image = image.reshape(1, image.rows*image.cols); + + image = image * x; + + image = image.reshape(3, originalRows); + image.convertTo(image, CV_8UC3); + cvtColor(image, image, COLOR_RGB2BGR); + + } + } + + imshow("image result | q or esc to quit", image); + imshow("original", imageCopy); + char key = (char)waitKey(waitTime); + if (key == 27) + break; + } + + return 0; +} diff --git a/modules/mcc/samples/chart_detection.cpp b/modules/mcc/samples/chart_detection.cpp index 792c54a3514..c47cdad7880 100644 --- a/modules/mcc/samples/chart_detection.cpp +++ b/modules/mcc/samples/chart_detection.cpp @@ -40,6 +40,7 @@ int main(int argc, char *argv[]) if (!parser.check()) { parser.printErrors(); + parser.printMessage(); return 0; } @@ -60,6 +61,9 @@ int main(int argc, char *argv[]) //-------------------------Actual Relevant Code----------------------------- //-------------------------------------------------------------------------- + namedWindow("original", WINDOW_NORMAL); + namedWindow("image result | q or esc to quit", WINDOW_NORMAL); + while (inputVideo.grab()) { diff --git a/modules/mcc/samples/chart_detection_with_network.cpp b/modules/mcc/samples/chart_detection_with_network.cpp index aab197cddb6..1bbec1a0199 100644 --- a/modules/mcc/samples/chart_detection_with_network.cpp +++ b/modules/mcc/samples/chart_detection_with_network.cpp @@ -58,6 +58,7 @@ int main(int argc, char *argv[]) if (!parser.check()) { parser.printErrors(); + parser.printMessage(); return 0; } @@ -95,6 +96,9 @@ int main(int argc, char *argv[]) return 0; } + namedWindow("original", WINDOW_NORMAL); + namedWindow("image result | q or esc to quit", WINDOW_NORMAL); + while (inputVideo.grab()) { Mat image, imageCopy; @@ -126,6 +130,7 @@ int main(int argc, char *argv[]) char key = (char)waitKey(waitTime); if (key == 27) break; + } return 0; diff --git a/modules/mcc/src/checker_detector.cpp b/modules/mcc/src/checker_detector.cpp index f5ce44ed569..c739f1895d5 100644 --- a/modules/mcc/src/checker_detector.cpp +++ b/modules/mcc/src/checker_detector.cpp @@ -35,6 +35,8 @@ #include "checker_model.hpp" #include "debug.hpp" + +#include namespace cv { namespace mcc @@ -193,7 +195,8 @@ bool CCheckerDetectorImpl:: //------------------------------------------------------------------- std::vector> colorCharts; - checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts, params); + std::vector> groupedCharts; + checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts,groupedCharts, params); if (colorCharts.empty()) continue; @@ -219,9 +222,9 @@ bool CCheckerDetectorImpl:: // checker color analysis //------------------------------------------------------------------- std::vector> checkers; - checkerAnalysis(img_rgb_f, chartType, nc, colorCharts, checkers, asp, params, - img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); + checkerAnalysis(img_rgb_f, chartType, nc, colorCharts, groupedCharts, checkers, asp, params, + img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); #ifdef MCC_DEBUG cv::Mat image_checker; croppedImage.copyTo(image_checker); @@ -234,9 +237,18 @@ bool CCheckerDetectorImpl:: #endif for (Ptr checker : checkers) { - for (cv::Point2f &corner : checker->getBox()) + auto box = checker->getBox(); + for (cv::Point2f &corner : box) corner += static_cast(region.tl()); + auto actualDetectedContours = checker->getActualDetectedContours(); + for (std::vector &corners : actualDetectedContours) + for (cv::Point2f & corner: corners) + corner += static_cast(region.tl()); + + checker->setBox(box); + checker->setActualDetectedContours(actualDetectedContours); + checker->calculate(); mtx.lock(); // push_back is not thread safe m_checkers.push_back(checker); mtx.unlock(); @@ -414,7 +426,8 @@ bool CCheckerDetectorImpl:: //------------------------------------------------------------------- std::vector> colorCharts; - checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts, params); + std::vector> groupedCharts; + checkerRecognize(img_bgr, detectedCharts, G, chartType, colorCharts, groupedCharts, params); if (colorCharts.empty()) continue; @@ -440,8 +453,14 @@ bool CCheckerDetectorImpl:: // checker color analysis //------------------------------------------------------------------- std::vector> checkers; - checkerAnalysis(img_rgb_f, chartType, nc, colorCharts, checkers, asp, params, - img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); + std::vector rgb_planes_inner(3), ycbcr_planes_inner(3); + for(int _i= 0;_i<3;_i++) + { + rgb_planes_inner[_i] = rgb_planes[_i](innerRegion); + ycbcr_planes_inner[_i] = ycbcr_planes[_i](innerRegion); + } + checkerAnalysis(img_rgb_f, chartType, nc, colorCharts,groupedCharts, checkers, asp, params, + img_rgb_org(innerRegion), img_ycbcr_org(innerRegion), rgb_planes_inner, ycbcr_planes_inner); #ifdef MCC_DEBUG cv::Mat image_checker; innerCroppedImage.copyTo(image_checker); @@ -454,8 +473,18 @@ bool CCheckerDetectorImpl:: #endif for (Ptr checker : checkers) { - for (cv::Point2f &corner : checker->getBox()) + auto box = checker->getBox(); + for (cv::Point2f &corner : box) corner += static_cast(region.tl() + innerRegion.tl()); + + auto actualDetectedContours = checker->getActualDetectedContours(); + for (std::vector &corners : actualDetectedContours) + for (cv::Point2f & corner: corners) + corner += static_cast(region.tl() + innerRegion.tl()); + + checker->setBox(box); + checker->setActualDetectedContours(actualDetectedContours); + checker->calculate(); mtx.lock(); // push_back is not thread safe m_checkers.push_back(checker); mtx.unlock(); @@ -528,6 +557,8 @@ void CCheckerDetectorImpl:: cv::Mat strelbox = cv::getStructuringElement(cv::MORPH_RECT, Size(5, 5)); cv::morphologyEx(grayOut, grayOut, MORPH_OPEN, strelbox); + + } void CCheckerDetectorImpl:: @@ -756,6 +787,7 @@ void CCheckerDetectorImpl:: const std::vector &G, const TYPECHART chartType, std::vector> &colorChartsOut, + std::vector> &groupedCharts, const Ptr ¶ms) { std::vector gU; @@ -775,7 +807,7 @@ void CCheckerDetectorImpl:: for (size_t i = 0; i < Ncc; i++) if (G[i] == (int)g) chartSub.push_back(detectedCharts[i]); - + std::vector chartSubCopy = chartSub; size_t Nsc = chartSub.size(); if (Nsc < params->minGroupSize) continue; @@ -958,6 +990,7 @@ void CCheckerDetectorImpl:: mcc::polyclockwise(ibox); // circshift(ibox, 4 - iTheta); colorCharts.push_back(ibox); + groupedCharts.push_back(chartSubCopy); } // return @@ -970,6 +1003,7 @@ void CCheckerDetectorImpl:: const TYPECHART chartType, const unsigned int nc, const std::vector> &colorCharts, + const std::vector> &groupedCharts, std::vector> &checkers, float asp, const Ptr ¶ms, @@ -981,7 +1015,6 @@ void CCheckerDetectorImpl:: size_t N; std::vector ibox; - // color chart classic model CChartModel cccm(chartType); cv::Mat lab; cccm.copyToColorMat(lab, 0); @@ -993,10 +1026,11 @@ void CCheckerDetectorImpl:: N = colorCharts.size(); std::vector J(N); + std::vector perPatchCost(N); for (size_t i = 0; i < N; i++) { ibox = colorCharts[i]; - J[i] = cost_function(img_f, mask, lab, ibox, chartType); + J[i] = cost_function(img_f, mask, lab, perPatchCost, ibox, chartType); } std::vector idx; @@ -1020,14 +1054,29 @@ void CCheckerDetectorImpl:: get_profile(ibox, chartType, charts_rgb, charts_ycbcr, img_rgb_org, img_ycbcr_org, rgb_planes, ycbcr_planes); + std::vector> directlyDetectedPatches; + for(auto chart : groupedCharts[idx[i]]) + { + for(auto &corners: chart.corners) + corners *= invAsp; + directlyDetectedPatches.push_back(chart.corners); + } + + + // result Ptr checker = CChecker::create(); checker->setBox(ibox); + checker->setActualDetectedContours(directlyDetectedPatches); + checker->setTarget(chartType); checker->setChartsRGB(charts_rgb); checker->setChartsYCbCr(charts_ycbcr); + checker->setActualChartsColors(Vect2Mat(cccm.chart)); checker->setCenter(mace_center(ibox)); checker->setCost(J[i]); + checker->calculate(); //does some precomputation based on the inputs, + //mainly used to keep the code a bit clean checkers.push_back(checker); } @@ -1178,38 +1227,6 @@ void CCheckerDetectorImpl:: x_new.insert(x_new.begin() + idx + 1, (x_new[idx] + x_new[idx + 1]) / 2); } -void CCheckerDetectorImpl:: - transform_points_forward(InputArray T, const std::vector &X, std::vector &Xt) -{ - size_t N = X.size(); - if (N == 0) - return; - - Xt.clear(); - Xt.resize(N); - cv::Matx31f p, xt; - cv::Point2f pt; - - cv::Matx33f _T = T.getMat(); - for (int i = 0; i < (int)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; - } -} - -void CCheckerDetectorImpl:: - transform_points_inverse(InputArray T, const std::vector &X, std::vector &Xt) -{ - cv::Matx33f _T = T.getMat(); - cv::Matx33f Tinv = _T.inv(); - transform_points_forward(Tinv, X, Xt); -} void CCheckerDetectorImpl:: get_profile( const std::vector &ibox, @@ -1330,6 +1347,7 @@ void CCheckerDetectorImpl:: float CCheckerDetectorImpl:: cost_function(InputArray im_rgb, InputOutputArray mask, InputArray lab, + std::vector &perPatchCost, const std::vector &ibox, const TYPECHART chartType) { CChartModel cccm(chartType); @@ -1342,6 +1360,8 @@ float CCheckerDetectorImpl:: int N = (int)(cellchart.size() / 4); + perPatchCost.assign(N, {-1, -1}); //-1 for patches outside the image + cv::Mat _lab = lab.getMat(); cv::Mat _im_rgb = im_rgb.getMat(); @@ -1376,10 +1396,11 @@ float CCheckerDetectorImpl:: // cos error float costh; costh = (float)(mu.dot(cv::Scalar(r)) / (norm(mu) * norm(r) + FLT_EPSILON)); - ec += (1 - (1 + costh) / 2); + perPatchCost[i] = {1 - (1 + costh)/2, (float)st.dot(st)}; + ec += perPatchCost[i].x; // standar desviation - es += (float)st.dot(st); + es += perPatchCost[i].y; } } diff --git a/modules/mcc/src/checker_detector.hpp b/modules/mcc/src/checker_detector.hpp index 83fc55ad767..129d6466bc0 100644 --- a/modules/mcc/src/checker_detector.hpp +++ b/modules/mcc/src/checker_detector.hpp @@ -130,8 +130,10 @@ class CCheckerDetectorImpl : public CCheckerDetector * \param[out] colorChartsOut */ virtual void - checkerRecognize(InputArray img, const std::vector &detectedCharts, const std::vector &G, - const TYPECHART chartType, std::vector> &colorChartsOut, + checkerRecognize(InputArray img, const std::vector &detectedCharts, + const std::vector &G, const TYPECHART chartType, + std::vector> &colorChartsOut, + std::vector> &groupedCharts, const Ptr ¶ms); /// checkerAnalysis @@ -146,6 +148,7 @@ class CCheckerDetectorImpl : public CCheckerDetector checkerAnalysis(InputArray img_rgb_f, const TYPECHART chartType, const unsigned int nc, const std::vector> &colorCharts, + const std::vector> &groupedCharts, std::vector> &checkers, float asp, const Ptr ¶ms, const cv::Mat &img_rgb_org, @@ -172,16 +175,6 @@ class CCheckerDetectorImpl : public CCheckerDetector std::vector &x_new, float tol); - void transform_points_forward( - InputArray T, - const std::vector &X, - std::vector &Xt); - - void transform_points_inverse( - InputArray T, - const std::vector &X, - std::vector &Xt); - void get_profile( const std::vector &ibox, const TYPECHART chartType, @@ -197,6 +190,7 @@ class CCheckerDetectorImpl : public CCheckerDetector * + \sum_k || \sigma_{k,p} ||^2 */ float cost_function(InputArray img, InputOutputArray mask, InputArray lab, + std::vector &perPatchCost, const std::vector &ibox, const TYPECHART chartType); }; diff --git a/modules/mcc/src/checker_model.cpp b/modules/mcc/src/checker_model.cpp index 2062e7705e5..50ebaff2d76 100644 --- a/modules/mcc/src/checker_model.cpp +++ b/modules/mcc/src/checker_model.cpp @@ -31,6 +31,9 @@ #include "checker_model.hpp" #include "dictionary.hpp" +#include +using namespace std; + namespace cv { namespace mcc @@ -386,6 +389,14 @@ void CCheckerImpl::setBox(std::vector _box) { box = _box; } +void CCheckerImpl::setPerPatchCosts(std::vector _perPatchCost) +{ + perPatchCost = _perPatchCost; +} +void CCheckerImpl::setActualDetectedContours(std::vector> _boxDetectionType) +{ + actualDetectedContours = _boxDetectionType; +} void CCheckerImpl::setChartsRGB(Mat _chartsRGB) { chartsRGB = _chartsRGB; @@ -394,6 +405,10 @@ void CCheckerImpl::setChartsYCbCr(Mat _chartsYCbCr) { chartsYCbCr = _chartsYCbCr; } +void CCheckerImpl::setActualChartsColors(Mat _actualChartsColors) +{ + actualChartsColors = _actualChartsColors; +} void CCheckerImpl::setCost(float _cost) { cost = _cost; @@ -402,6 +417,18 @@ void CCheckerImpl::setCenter(Point2f _center) { center = _center; } +void CCheckerImpl::setPatchCenters(std::vector _patchCenters) +{ + + patchCenters = _patchCenters; +} +void CCheckerImpl::setPatchBoundingBoxes(std::vector _patchBoundingBoxes) +{ + //Use of this function is not recommended as it can cause contradictory information in patBoundingBoxes and box + patchBoundingBoxes = _patchBoundingBoxes; + +} + TYPECHART CCheckerImpl::getTarget() { @@ -411,6 +438,14 @@ std::vector CCheckerImpl::getBox() { return box; } +std::vector CCheckerImpl::getPerPatchCosts() +{ + return perPatchCost; +} +std::vector> CCheckerImpl::getActualDetectedContours() +{ + return actualDetectedContours; +} Mat CCheckerImpl::getChartsRGB() { return chartsRGB; @@ -419,6 +454,10 @@ Mat CCheckerImpl::getChartsYCbCr() { return chartsYCbCr; } +Mat CCheckerImpl::getActualChartsColors() +{ + return actualChartsColors; +} float CCheckerImpl::getCost() { return cost; @@ -427,30 +466,51 @@ Point2f CCheckerImpl::getCenter() { return center; } +std::vector CCheckerImpl::getPatchCenters() +{ -////////////////////////////////////////////////////////////////////////////////////////////// -// CheckerDraw -Ptr CCheckerDraw::create(Ptr pChecker, cv::Scalar color /*= CV_RGB(0,250,0)*/, int thickness /*=2*/) + return patchCenters; +} + +std::vector CCheckerImpl::getPatchBoundingBoxes(float sideRatio) { - return makePtr(pChecker, color, thickness); + std::vector _patchBoundingBoxes; + + size_t N = patchBoundingBoxes.size() / 4; + + _patchBoundingBoxes.resize(4 * N); + + for (size_t i = 0; i < N; i++) + { + for (size_t j = 0; j < 4; j++) + _patchBoundingBoxes[4*i+j] = ((patchBoundingBoxes[4*i+j] - patchCenters[i] ) * sideRatio / defaultSideRatio) + patchCenters[i]; + + } + return _patchBoundingBoxes; } -void CCheckerDrawImpl:: - draw(InputOutputArray img) +bool CCheckerImpl::calculate() { + if(box.size() != 4) + return false; + //Currently it just calculates all the patch centers + + + + CChartModel cccm(this->getTarget()); - // color chart classic model - CChartModel cccm(m_pChecker->getTarget()); - cv::Mat lab; - size_t N; std::vector fbox = cccm.box; std::vector cellchart = cccm.cellchart; // tranformation - cv::Matx33f ccT = cv::getPerspectiveTransform(fbox, m_pChecker->getBox()); + cv::Matx33f ccT = cv::getPerspectiveTransform(fbox, this->getBox()); std::vector bch(4), bcht(4); - N = cellchart.size() / 4; + size_t N = cellchart.size() / 4; + + patchBoundingBoxes.resize(4 * N); + patchCenters.resize(N); + for (size_t i = 0, k; i < N; i++) { k = 4 * i; @@ -466,39 +526,61 @@ void CCheckerDrawImpl:: for (size_t j = 0; j < 4; j++) c += bcht[j]; c /= 4; + patchCenters[i] = c; for (size_t j = 0; j < 4; j++) - bcht[j] = ((bcht[j] - c) * 0.50) + c; + bcht[j] = ((bcht[j] - c) * defaultSideRatio) + c; - cv::line(img, bcht[0], bcht[1], m_color, m_thickness, LINE_AA); - cv::line(img, bcht[1], bcht[2], m_color, m_thickness, LINE_AA); - cv::line(img, bcht[2], bcht[3], m_color, m_thickness, LINE_AA); - cv::line(img, bcht[3], bcht[0], m_color, m_thickness, LINE_AA); + for (int j = 0; j < 4; j++) + patchBoundingBoxes[4 * i + j] = bcht[j]; } + return true; + + + +} +////////////////////////////////////////////////////////////////////////////////////////////// +// CheckerDraw +Ptr CCheckerDraw::create(Ptr pChecker, cv::Scalar color /*= CV_RGB(0,250,0)*/, int thickness /*=2*/) +{ + return makePtr(pChecker, color, thickness); } void CCheckerDrawImpl:: - transform_points_forward(InputArray T, const std::vector &X, std::vector &Xt) + draw(InputOutputArray img, float sideRatio, bool drawActualDetection) { - cv::Matx33f _T = T.getMat(); - size_t N = X.size(); - Xt.clear(); - Xt.resize(N); - if (N == 0) - return; - - cv::Matx31f p, xt; - cv::Point2f pt; + std::vector patchBoundingBoxes = m_pChecker->getPatchBoundingBoxes(sideRatio); + size_t N = patchBoundingBoxes.size()/4; 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; + + cv::line(img, patchBoundingBoxes[4*i +0], patchBoundingBoxes[4*i+1], m_color, m_thickness, LINE_AA); + cv::line(img, patchBoundingBoxes[4*i+1], patchBoundingBoxes[4*i+2], m_color, m_thickness, LINE_AA); + cv::line(img, patchBoundingBoxes[4*i+2], patchBoundingBoxes[4*i+3], m_color, m_thickness, LINE_AA); + cv::line(img, patchBoundingBoxes[4*i+3], patchBoundingBoxes[4*i+0], m_color, m_thickness, LINE_AA); + } + + if(drawActualDetection) + { + + vector> f; + for (auto i : m_pChecker->getActualDetectedContours()) + { + vector x; + for (auto j : i) + x.push_back(j); + f.push_back(x); + } + drawContours(img, f , -1, CV_RGB(255, 255, 0), 2); } } + +void drawColorChecker(InputOutputArray img, Ptr pChecker, + cv::Scalar color, int thickness, float sideRatio, + bool drawActualDetection) +{ + CCheckerDrawImpl(pChecker, color, thickness).draw(img, sideRatio, drawActualDetection); +} + } // namespace mcc } // namespace cv diff --git a/modules/mcc/src/checker_model.hpp b/modules/mcc/src/checker_model.hpp index 31b85a5a144..4343f7028a5 100644 --- a/modules/mcc/src/checker_model.hpp +++ b/modules/mcc/src/checker_model.hpp @@ -130,25 +130,45 @@ class CCheckerImpl : public CChecker void setTarget(TYPECHART _target) CV_OVERRIDE; void setBox(std::vector _box) CV_OVERRIDE; + void setPerPatchCosts(std::vector cost) CV_OVERRIDE; + + // Detected directly by the detector or found using geometry, useful to determine if a patch is occluded + void setActualDetectedContours(std::vector> detectionType) CV_OVERRIDE; void setChartsRGB(Mat _chartsRGB) CV_OVERRIDE; void setChartsYCbCr(Mat _chartsYCbCr) CV_OVERRIDE; + void setActualChartsColors(Mat _actualChartsColors) CV_OVERRIDE; // the acutal color profile void setCost(float _cost) CV_OVERRIDE; void setCenter(Point2f _center) CV_OVERRIDE; + void setPatchCenters(std::vector _patchCenters) CV_OVERRIDE; + void setPatchBoundingBoxes(std::vector _patchBoundingBoxes) CV_OVERRIDE; TYPECHART getTarget() CV_OVERRIDE; std::vector getBox() CV_OVERRIDE; + std::vector getPerPatchCosts() CV_OVERRIDE; + std::vector> getActualDetectedContours() CV_OVERRIDE; Mat getChartsRGB() CV_OVERRIDE; Mat getChartsYCbCr() CV_OVERRIDE; + Mat getActualChartsColors() CV_OVERRIDE; // the actual color profile float getCost() CV_OVERRIDE; Point2f getCenter() CV_OVERRIDE; + std::vector getPatchCenters() CV_OVERRIDE; + std::vector getPatchBoundingBoxes(float sideRatio=0.5) CV_OVERRIDE; + + bool calculate() CV_OVERRIDE;//basic precomp private: TYPECHART target; ///< type of checkercolor std::vector box; ///< positions of the corners + std::vector perPatchCost; ///< Cost of each patch in chart + std::vector patchBoundingBoxes; ///< Bounding box of the patches + std::vector patchCenters; ///< Centers of all the patches + std::vector> actualDetectedContours; ///< contours of patches directly dectected, not using geometry cv::Mat chartsRGB; ///< charts profile in rgb color space cv::Mat chartsYCbCr; ///< charts profile in YCbCr color space + cv::Mat actualChartsColors; ///< expected charts profile, contains both rgb and YCbCr float cost; ///< cost to aproximate cv::Point2f center; ///< center of the chart. + float defaultSideRatio = 0.5; ///< ratio of side of patchBoundingBox and actual patch size }; ////////////////////////////////////////////////////////////////////////////////////////////// @@ -167,19 +187,13 @@ class CCheckerDrawImpl : public CCheckerDraw CV_Assert(pChecker); } - void draw(InputOutputArray img) CV_OVERRIDE; + void draw(InputOutputArray img, float sideRatio =0.5,bool drawActualDetection=false) CV_OVERRIDE; private: Ptr m_pChecker; cv::Scalar m_color; int m_thickness; -private: - /** \brief transformation perspetive*/ - void transform_points_forward( - InputArray T, - const std::vector &X, - std::vector &Xt); }; // @} diff --git a/modules/mcc/src/common.cpp b/modules/mcc/src/common.cpp index 71cba1e8bc5..b3e95bf5771 100644 --- a/modules/mcc/src/common.cpp +++ b/modules/mcc/src/common.cpp @@ -86,5 +86,39 @@ mace_center(const std::vector &ps) return center; } +void transform_points_forward(InputArray T, const std::vector &X, + std::vector &Xt) +{ + size_t N = X.size(); + if (N == 0) + return; + + Xt.clear(); + Xt.resize(N); + cv::Matx31f p, xt; + cv::Point2f pt; + + cv::Matx33f _T = T.getMat(); + for (int i = 0; i < (int)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; + } +} + +void transform_points_inverse(InputArray T, const std::vector &X, + std::vector &Xt) +{ + cv::Matx33f _T = T.getMat(); + cv::Matx33f Tinv = _T.inv(); + transform_points_forward(Tinv, X, Xt); +} + + } // namespace mcc } // namespace cv diff --git a/modules/mcc/src/common.hpp b/modules/mcc/src/common.hpp index aa4c383f556..ffdaf1f0fdb 100644 --- a/modules/mcc/src/common.hpp +++ b/modules/mcc/src/common.hpp @@ -36,6 +36,33 @@ namespace mcc Rect poly2mask(const std::vector &poly, Size size, InputOutputArray mask); + +float perimeter(const std::vector &ps); + +cv::Point2f mace_center(const std::vector &ps); + +void transform_points_forward(InputArray T, const std::vector &X, + std::vector &Xt); + +void transform_points_inverse(InputArray T, const std::vector &X, + std::vector &Xt); + + +template +cv::Mat Vect2Mat(std::vector> vect) +{ + cv::Mat matrix = cv::Mat::zeros((int)vect.size(), (int)vect[0].size(), cv::DataType::type); + //Mat mtx; + + // copy data + for (int i=0; i<(int)vect.size(); i++) + for (int j=0; j<(int)vect[i].size(); j++) + { + matrix.at(i,j) = vect[i][j]; + } + + return matrix; +} template void circshift(std::vector &A, int shiff) { @@ -53,10 +80,6 @@ void circshift(std::vector &A, int shiff) A = Tmp; } -float perimeter(const std::vector &ps); - -cv::Point2f mace_center(const std::vector &ps); - template void unique(const std::vector &A, std::vector &U) { diff --git a/modules/mcc/src/mcc.cpp b/modules/mcc/src/mcc.cpp index 9f2d96ab7fc..83b1fc2a4b8 100644 --- a/modules/mcc/src/mcc.cpp +++ b/modules/mcc/src/mcc.cpp @@ -38,7 +38,7 @@ namespace mcc */ DetectorParameters::DetectorParameters() : adaptiveThreshWinSizeMin(23), - adaptiveThreshWinSizeMax(153), + adaptiveThreshWinSizeMax(303), adaptiveThreshWinSizeStep(16), adaptiveThreshConstant(7), minContoursAreaRate(0.003), @@ -46,9 +46,10 @@ DetectorParameters::DetectorParameters() confidenceThreshold(0.5), minContourSolidity(0.9), findCandidatesApproxPolyDPEpsMultiplier(0.05), - borderWidth(0), + borderWidth(20), B0factor(1.25f), maxError(0.1f), + minContourPointsAllowed(4), minContourLengthAllowed(100), minInterContourDistance(100), diff --git a/modules/mcc/test/test_mcc.cpp b/modules/mcc/test/test_mcc.cpp index 4aa8ae31302..1800f188292 100644 --- a/modules/mcc/test/test_mcc.cpp +++ b/modules/mcc/test/test_mcc.cpp @@ -37,6 +37,8 @@ TEST(CV_mccRunCCheckerDrawTest, accuracy_MCC24) Ptr pChecker = CChecker::create(); pChecker->setTarget(MCC24); pChecker->setBox({{0, 0}, {480, 0}, {480, 640}, {0, 640}}); + pChecker->calculate(); + runCCheckerDraw(pChecker, 640, 480, 24); } TEST(CV_mccRunCCheckerDrawTest, accuracy_SG140) @@ -44,6 +46,8 @@ TEST(CV_mccRunCCheckerDrawTest, accuracy_SG140) Ptr pChecker = CChecker::create(); pChecker->setTarget(SG140); pChecker->setBox({{0, 0}, {480, 0}, {480, 640}, {0, 640}}); + pChecker->calculate(); + runCCheckerDraw(pChecker, 640, 480, 140); } TEST(CV_mccRunCCheckerDrawTest, accuracy_VINYL18) @@ -51,6 +55,8 @@ TEST(CV_mccRunCCheckerDrawTest, accuracy_VINYL18) Ptr pChecker = CChecker::create(); pChecker->setTarget(VINYL18); pChecker->setBox({{0, 0}, {480, 0}, {480, 640}, {0, 640}}); + pChecker->calculate(); + runCCheckerDraw(pChecker, 640, 480, 18); }