diff --git a/modules/mcc/include/opencv2/mcc/ccm.hpp b/modules/mcc/include/opencv2/mcc/ccm.hpp index a3686db473d..2fd52b3a9da 100644 --- a/modules/mcc/include/opencv2/mcc/ccm.hpp +++ b/modules/mcc/include/opencv2/mcc/ccm.hpp @@ -360,6 +360,8 @@ Produce a ColorCorrectionModel instance for inference class CV_EXPORTS_W ColorCorrectionModel { public: + CV_WRAP ColorCorrectionModel(); + /** @brief Color Correction Model Supported list of color cards: @@ -507,11 +509,17 @@ class CV_EXPORTS_W ColorCorrectionModel */ CV_WRAP Mat infer(const Mat& img, bool islinear = false); + CV_WRAP void write(cv::FileStorage& fs) const; + CV_WRAP void read(const cv::FileNode& node); + class Impl; private: std::shared_ptr p; }; +CV_EXPORTS void write(cv::FileStorage& fs, const std::string&, const ColorCorrectionModel& ccm); +CV_EXPORTS void read(const cv::FileNode& node, ColorCorrectionModel& ccm, const ColorCorrectionModel& default_value = ColorCorrectionModel()); + //! @} ccm } // namespace ccm } // namespace cv diff --git a/modules/mcc/src/ccm.cpp b/modules/mcc/src/ccm.cpp index aec535e05b4..416689cb64a 100644 --- a/modules/mcc/src/ccm.cpp +++ b/modules/mcc/src/ccm.cpp @@ -27,15 +27,28 @@ #include "opencv2/mcc/ccm.hpp" #include "linearize.hpp" + namespace cv { namespace ccm { class ColorCorrectionModel::Impl { public: Mat src; + std::shared_ptr dst = std::make_shared(); + // Track initialization parameters for serialization + Mat dst_colors; + COLOR_SPACE dst_cs_enum; + Mat dst_colored; + CONST_COLOR dst_constcolor; + bool dst_use_constcolor; + Mat dist; + RGBBase_& cs; + // Track initialization parameters for serialization + COLOR_SPACE cs_enum; + Mat mask; // RGBl of detected data and the reference @@ -138,6 +151,7 @@ class ColorCorrectionModel::Impl ColorCorrectionModel::Impl::Impl() : cs(*GetCS::getInstance().get_rgb(COLOR_SPACE_sRGB)) + , cs_enum(COLOR_SPACE_sRGB) , ccm_type(CCM_3x3) , distance(DISTANCE_CIE2000) , linear_type(LINEARIZATION_GAMMA) @@ -282,6 +296,10 @@ void ColorCorrectionModel::Impl::fitting(void) loss = pow((res / masked_len), 0.5); } +ColorCorrectionModel::ColorCorrectionModel() + : p(std::make_shared()) +{} + Mat ColorCorrectionModel::infer(const Mat& img, bool islinear) { if (!p->ccm.data) @@ -300,14 +318,24 @@ Mat ColorCorrectionModel::infer(const Mat& img, bool islinear) void ColorCorrectionModel::Impl::getColor(CONST_COLOR constcolor) { + dst_use_constcolor = true; + dst_constcolor = constcolor; dst = (GetColor::getColor(constcolor)); } void ColorCorrectionModel::Impl::getColor(Mat colors_, COLOR_SPACE ref_cs_) { + dst_use_constcolor = false; + dst_colors = colors_; + dst_cs_enum = ref_cs_; + dst_colored = Mat(); dst.reset(new Color(colors_, *GetCS::getInstance().get_cs(ref_cs_))); } void ColorCorrectionModel::Impl::getColor(Mat colors_, COLOR_SPACE cs_, Mat colored_) { + dst_use_constcolor = false; + dst_colors = colors_; + dst_cs_enum = cs_; + dst_colored = colored_; dst.reset(new Color(colors_, *GetCS::getInstance().get_cs(cs_), colored_)); } ColorCorrectionModel::ColorCorrectionModel(const Mat& src_, CONST_COLOR constcolor) @@ -331,6 +359,7 @@ ColorCorrectionModel::ColorCorrectionModel(const Mat& src_, Mat colors_, COLOR_S void ColorCorrectionModel::setColorSpace(COLOR_SPACE cs_) { + p->cs_enum = cs_; p->cs = *GetCS::getInstance().get_rgb(cs_); } void ColorCorrectionModel::setCCM_TYPE(CCM_TYPE ccm_type_) @@ -433,5 +462,134 @@ Mat ColorCorrectionModel::getMask() const{ Mat ColorCorrectionModel::getWeights() const{ return p->weights; } + +void ColorCorrectionModel::write(FileStorage& fs) const +{ + fs << "{" + << "ccm" << p->ccm + << "loss" << p->loss + << "src" << p->src + << "dist" << p->dist + << "cs_enum" << p->cs_enum + << "src_rgbl" << p->src_rgbl + << "dst_rgbl" << p->dst_rgbl + << "mask" << p->mask + << "ccm_type" << p->ccm_type + << "shape" << p->shape + << "linear" << *p->linear + << "distance" << p->distance + << "linear_type" << p->linear_type + << "weights" << p->weights + << "weights_list" << p->weights_list + << "ccm0" << p->ccm0 + << "gamma" << p->gamma + << "deg" << p->deg + << "saturated_threshold" << p->saturated_threshold + << "initial_method_type" << p->initial_method_type + << "weights_coeff" << p->weights_coeff + << "masked_len" << p->masked_len + << "max_count" << p->max_count + << "epsilon" << p->epsilon + << "dst_use_constcolor" << p->dst_use_constcolor; + + if (p->dst_use_constcolor) { + fs << "dst_constcolor" << p->dst_constcolor; + } else { + fs << "dst_colors" << p->dst_colors + << "dst_cs_enum" << p->dst_cs_enum + << "dst_colored" << p->dst_colored; + } + fs << "}"; +} + + +void ColorCorrectionModel::read(const FileNode& node) +{ + node["ccm"] >> p->ccm; + node["loss"] >> p->loss; + node["src"] >> p->src; + node["dist"] >> p->dist; + node["src_rgbl"] >> p->src_rgbl; + node["dst_rgbl"] >> p->dst_rgbl; + node["mask"] >> p->mask; + node["ccm_type"] >> p->ccm_type; + node["shape"] >> p->shape; + node["distance"] >> p->distance; + node["gamma"] >> p->gamma; + node["deg"] >> p->deg; + node["saturated_threshold"] >> p->saturated_threshold; + node["initial_method_type"] >> p->initial_method_type; + node["weights_coeff"] >> p->weights_coeff; + node["weights"] >> p->weights; + node["weights_list"] >> p->weights_list; + node["ccm0"] >> p->ccm0; + node["masked_len"] >> p->masked_len; + node["max_count"] >> p->max_count; + node["epsilon"] >> p->epsilon; + + COLOR_SPACE cs_enum; + node["cs_enum"] >> cs_enum; + setColorSpace(cs_enum); + + bool dst_use_constcolor; + node["dst_use_constcolor"] >> dst_use_constcolor; + if (dst_use_constcolor) { + CONST_COLOR dst_constcolor; + node["dst_constcolor"] >> dst_constcolor; + p->getColor(dst_constcolor); + } else { + Mat dst_colors; + node["dst_colors"] >> dst_colors; + COLOR_SPACE dst_cs_enum; + node["dst_cs_enum"] >> dst_cs_enum; + Mat dst_colored; + node["dst_colored"] >> dst_colored; + if (dst_colored.empty()) { + p->getColor(dst_colors, dst_cs_enum); + } else { + p->getColor(dst_colors, dst_cs_enum, dst_colored); + } + } + + node["linear_type"] >> p->linear_type; + switch (p->linear_type) { + case cv::ccm::LINEARIZATION_GAMMA: + p->linear = std::shared_ptr(new LinearGamma()); + break; + case cv::ccm::LINEARIZATION_COLORPOLYFIT: + p->linear = std::shared_ptr(new LinearColor()); + break; + case cv::ccm::LINEARIZATION_IDENTITY: + p->linear = std::shared_ptr(new LinearIdentity()); + break; + case cv::ccm::LINEARIZATION_COLORLOGPOLYFIT: + p->linear = std::shared_ptr(new LinearColor()); + break; + case cv::ccm::LINEARIZATION_GRAYPOLYFIT: + p->linear = std::shared_ptr(new LinearGray()); + break; + case cv::ccm::LINEARIZATION_GRAYLOGPOLYFIT: + p->linear = std::shared_ptr(new LinearGray()); + break; + default: + CV_Error(Error::StsBadArg, "Wrong linear_type!"); + break; + } + node["linear"] >> *p->linear; +} + +void write(FileStorage& fs, const std::string&, const cv::ccm::ColorCorrectionModel& ccm) +{ + ccm.write(fs); +} + +void read(const cv::FileNode& node, cv::ccm::ColorCorrectionModel& ccm, const cv::ccm::ColorCorrectionModel& default_value) +{ + if (node.empty()) + ccm = default_value; + else + ccm.read(node); +} + } } // namespace cv::ccm diff --git a/modules/mcc/src/linearize.cpp b/modules/mcc/src/linearize.cpp index 3a48a560eb8..2e982630f49 100644 --- a/modules/mcc/src/linearize.cpp +++ b/modules/mcc/src/linearize.cpp @@ -30,6 +30,28 @@ namespace cv { namespace ccm { +Polyfit::Polyfit() : deg(0) {} + +void Polyfit::write(cv::FileStorage& fs) const { + fs << "{" << "deg" << deg << "p" << p << "}"; +} + +void Polyfit::read(const cv::FileNode& node) { + node["deg"] >> deg; + node["p"] >> p; +} + +// Global functions to support FileStorage for Polyfit +void write(cv::FileStorage& fs, const std::string&, const Polyfit& polyfit) { + polyfit.write(fs); +} +void read(const cv::FileNode& node, Polyfit& polyfit, const Polyfit& default_value) { + if(node.empty()) + polyfit = default_value; + else + polyfit.read(node); +} + Polyfit::Polyfit(Mat x, Mat y, int deg_) : deg(deg_) { @@ -63,6 +85,29 @@ double Polyfit::fromEW(double x) return res; }; +// Default constructor for LogPolyfit +LogPolyfit::LogPolyfit() : deg(0) {} + +void LogPolyfit::write(cv::FileStorage& fs) const { + fs << "{" << "deg" << deg << "p" << p << "}"; +} + +void LogPolyfit::read(const cv::FileNode& node) { + node["deg"] >> deg; + node["p"] >> p; +} + +// Global functions to support FileStorage for LogPolyfit +void write(cv::FileStorage& fs, const std::string&, const LogPolyfit& logpolyfit) { + logpolyfit.write(fs); +} +void read(const cv::FileNode& node, LogPolyfit& logpolyfit, const LogPolyfit& default_value) { + if(node.empty()) + logpolyfit = default_value; + else + logpolyfit.read(node); +} + LogPolyfit::LogPolyfit(Mat x, Mat y, int deg_) : deg(deg_) { @@ -86,6 +131,132 @@ Mat LogPolyfit::operator()(const Mat& inp) return res; }; +void LinearIdentity::write(cv::FileStorage& fs) const +{ + fs << "{" << "}"; +} + +void LinearIdentity::read(const cv::FileNode&) +{ +} + +void LinearGamma::write(cv::FileStorage& fs) const +{ + fs << "{" << "gamma" << gamma << "}"; +} + +void LinearGamma::read(const cv::FileNode& node) +{ + node["gamma"] >> gamma; +} + +template +void LinearColor::write(cv::FileStorage& fs) const +{ + fs << "{" << "deg" << deg << "pr" << pr << "pg" << pg << "pb" << pb << "}"; +} + +template +void LinearColor::read(const cv::FileNode& node) +{ + node["deg"] >> deg; + node["pr"] >> pr; + node["pg"] >> pg; + node["pb"] >> pb; +} + +template +void LinearGray::write(cv::FileStorage& fs) const +{ + fs << "{" << "deg" << deg << "p" << p << "}"; +} + +template +void LinearGray::read(const cv::FileNode& node) +{ + node["deg"] >> deg; + node["p"] >> p; +} + +void Linear::write(cv::FileStorage&) const +{ + CV_Error(Error::StsNotImplemented, "This is a base class, so this shouldn't be called"); +} + +void Linear::read(const cv::FileNode&) +{ + CV_Error(Error::StsNotImplemented, "This is a base class, so this shouldn't be called"); +} + +void write(cv::FileStorage& fs, const std::string&, const Linear& linear) +{ + linear.write(fs); +} + +void read(const cv::FileNode& node, Linear& linear, const Linear& default_value) +{ + if (node.empty()) + linear = default_value; + else + linear.read(node); +} + +void write(cv::FileStorage& fs, const std::string&, const LinearIdentity& linearidentity) +{ + linearidentity.write(fs); +} + +void read(const cv::FileNode& node, LinearIdentity& linearidentity, const LinearIdentity& default_value) +{ + if (node.empty()) + linearidentity = default_value; + else + linearidentity.read(node); +} + +void write(cv::FileStorage& fs, const std::string&, const LinearGamma& lineargamma) +{ + lineargamma.write(fs); +} + +void read(const cv::FileNode& node, LinearGamma& lineargamma, const LinearGamma& default_value) +{ + if (node.empty()) + lineargamma = default_value; + else + lineargamma.read(node); +} + +template +void write(cv::FileStorage& fs, const std::string&, const LinearColor& linearcolor) +{ + linearcolor.write(fs); +} + +template +void read(const cv::FileNode& node, LinearColor& linearcolor, const LinearColor& default_value) +{ + if (node.empty()) + linearcolor = default_value; + else + linearcolor.read(node); +} + +template +void write(cv::FileStorage& fs, const std::string&, const LinearGray& lineargray) +{ + lineargray.write(fs); +} + +template +void read(const cv::FileNode& node, LinearGray& lineargray, const LinearGray& default_value) +{ + if (node.empty()) + lineargray = default_value; + else + lineargray.read(node); +} + Mat Linear::linearize(Mat inp) { return inp; diff --git a/modules/mcc/src/linearize.hpp b/modules/mcc/src/linearize.hpp index a703b5c5293..8cb958fd4c8 100644 --- a/modules/mcc/src/linearize.hpp +++ b/modules/mcc/src/linearize.hpp @@ -42,7 +42,7 @@ class Polyfit public: int deg; Mat p; - Polyfit() {}; + Polyfit(); /** @brief Polyfit method. https://en.wikipedia.org/wiki/Polynomial_regression @@ -53,10 +53,18 @@ class Polyfit virtual ~Polyfit() {}; Mat operator()(const Mat& inp); + // Serialization support + void write(cv::FileStorage& fs) const; + void read(const cv::FileNode& node); + private: double fromEW(double x); }; +// Global functions for FileStorage for Polyfit +void write(cv::FileStorage& fs, const std::string&, const Polyfit& polyfit); +void read(const cv::FileNode& node, Polyfit& polyfit, const Polyfit& default_value = Polyfit()); + /** @brief Logpolyfit model. */ class LogPolyfit @@ -66,15 +74,23 @@ class LogPolyfit int deg; Polyfit p; - LogPolyfit() {}; + LogPolyfit(); /** @brief Logpolyfit method. */ LogPolyfit(Mat x, Mat y, int deg); virtual ~LogPolyfit() {}; Mat operator()(const Mat& inp); + + // Serialization support + void write(cv::FileStorage& fs) const; + void read(const cv::FileNode& node); }; +// Global functions for FileStorage for LogPolyfit +void write(cv::FileStorage& fs, const std::string&, const LogPolyfit& logpolyfit); +void read(const cv::FileNode& node, LogPolyfit& logpolyfit, const LogPolyfit& default_value = LogPolyfit()); + /** @brief Linearization base. */ @@ -91,13 +107,29 @@ class Linear /* *\brief Evaluate linearization model. */ virtual void value(void) {}; + + // Serialization support + virtual void write(cv::FileStorage& fs) const; + virtual void read(const cv::FileNode& node); }; +// Global functions for FileStorage for Linear +void write(cv::FileStorage& fs, const std::string&, const Linear& linear); +void read(const cv::FileNode& node, Linear& linear, const Linear& default_value = Linear()); + /** @brief Linearization identity. make no change. */ class LinearIdentity : public Linear -{}; +{ + public: + void write(cv::FileStorage& fs) const; + void read(const cv::FileNode& node); +}; + +// Global functions for FileStorage for LinearIdentity +void write(cv::FileStorage& fs, const std::string&, const LinearIdentity& linearidentity); +void read(const cv::FileNode& node, LinearIdentity& linearidentity, const LinearIdentity& default_value = LinearIdentity()); /** @brief Linearization gamma correction. */ @@ -106,12 +138,23 @@ class LinearGamma : public Linear public: double gamma; + LinearGamma() + : gamma(1.0) {}; + LinearGamma(double gamma_) : gamma(gamma_) {}; Mat linearize(Mat inp) CV_OVERRIDE; + + // Serialization support + void write(cv::FileStorage& fs) const override; + void read(const cv::FileNode& node) override; }; +// Global functions for FileStorage for LinearGamma +void write(cv::FileStorage& fs, const std::string&, const LinearGamma& lineargamma); +void read(const cv::FileNode& node, LinearGamma& lineargamma, const LinearGamma& default_value = LinearGamma()); + /** @brief Linearization. Grayscale polynomial fitting. */ @@ -121,6 +164,9 @@ class LinearGray : public Linear public: int deg; T p; + + LinearGray(): deg(3) {}; + LinearGray(int deg_, Mat src, Color dst, Mat mask, RGBBase_ cs) : deg(deg_) { @@ -146,8 +192,18 @@ class LinearGray : public Linear { return p(inp); }; + + // Serialization support + void write(cv::FileStorage& fs) const override; + void read(const cv::FileNode& node) override; }; +// Global functions for FileStorage for LinearGray +template +void write(cv::FileStorage& fs, const std::string&, const LinearGray& lineargray); +template +void read(const cv::FileNode& node, LinearGray& lineargray, const LinearGray& default_value = LinearGray()); + /** @brief Linearization. Fitting channels respectively. */ @@ -160,6 +216,8 @@ class LinearColor : public Linear T pg; T pb; + LinearColor(): deg(3) {}; + LinearColor(int deg_, Mat src_, Color dst, Mat mask, RGBBase_ cs) : deg(deg_) { @@ -188,8 +246,18 @@ class LinearColor : public Linear merge(std::vector { pr(channels[0]), pg(channels[1]), pb(channels[2]) }, res); return res; }; + + // Serialization support + void write(cv::FileStorage& fs) const override; + void read(const cv::FileNode& node) override; }; +// Global functions for FileStorage for LinearColor +template +void write(cv::FileStorage& fs, const std::string&, const LinearColor& linearcolor); +template +void read(const cv::FileNode& node, LinearColor& linearcolor, const LinearColor& default_value = LinearColor()); + /** @brief Get linearization method. used in ccm model. @param gamma used in LinearGamma. diff --git a/modules/mcc/test/test_mcc.cpp b/modules/mcc/test/test_mcc.cpp index 37bd1f11fc7..c59b2e99209 100644 --- a/modules/mcc/test/test_mcc.cpp +++ b/modules/mcc/test/test_mcc.cpp @@ -178,6 +178,45 @@ TEST(CV_mcc_ccm_test, infer) EXPECT_MAT_NEAR(gold_img, calibratedImage, 0.1); } +TEST(CV_mcc_ccm_test, serialization) +{ + auto path1 = cvtest::findDataFile("mcc/mcc_ccm_test.yml"); + FileStorage fs(path1, FileStorage::READ); + Mat chartsRGB; + FileNode node = fs["chartsRGB"]; + node >> chartsRGB; + fs.release(); + + // compute CCM + ColorCorrectionModel model(chartsRGB.col(1).clone().reshape(3, chartsRGB.rows/3) / 255., COLORCHECKER_Macbeth); + model.run(); + + // write model + path1 = cvtest::tempfile(); + FileStorage fs1(path1, FileStorage::WRITE); + fs1 << "model" << model; + fs1.release(); + + // read model + ColorCorrectionModel model1; + FileStorage fs2(path1, FileStorage::READ); + fs2["model"] >> model1; + fs2.release(); + + // write model again + auto path2 = cvtest::tempfile(); + FileStorage fs3(path2, FileStorage::WRITE); + fs3 << "model" << model1; + fs3.release(); + + // read both yamls from disk + std::ifstream file1(path1); + std::string str1((std::istreambuf_iterator(file1)), std::istreambuf_iterator()); + std::ifstream file2(path2); + std::string str2((std::istreambuf_iterator(file2)), std::istreambuf_iterator()); + // compare them + EXPECT_EQ(str1, str2); +} } // namespace } // namespace opencv_test