diff --git a/modules/mcc/include/opencv2/mcc.hpp b/modules/mcc/include/opencv2/mcc.hpp index 2027485fb2a..5d671aec003 100644 --- a/modules/mcc/include/opencv2/mcc.hpp +++ b/modules/mcc/include/opencv2/mcc.hpp @@ -1,30 +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. - -/* - * 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. - */ +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright(C) 2020, Huawei Technologies Co.,Ltd. All rights reserved. +// Third party copyrights are property of their respective owners. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Longbu Wang +// Jinheng Zhang +// Chenqi Shan #ifndef __OPENCV_MCC_HPP__ #define __OPENCV_MCC_HPP__ @@ -32,6 +31,7 @@ #include "mcc/checker_detector.hpp" #include "mcc/checker_model.hpp" +#include "mcc/ccm.hpp" /** @defgroup mcc Macbeth Chart module diff --git a/modules/mcc/include/opencv2/mcc/distance.hpp b/modules/mcc/include/opencv2/mcc/distance.hpp index d0c9ac16307..e34980e4e9f 100644 --- a/modules/mcc/include/opencv2/mcc/distance.hpp +++ b/modules/mcc/include/opencv2/mcc/distance.hpp @@ -49,28 +49,12 @@ enum DISTANCE_TYPE RGBL }; -double deltaCIE76(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCIE94(cv::Vec3d lab1, cv::Vec3d lab2, double kH = 1.0, - double kC = 1.0, double kL = 1.0, double k1 = 0.045, - double k2 = 0.015); -double deltaCIE94GraphicArts(cv::Vec3d lab1, cv::Vec3d lab2); -double toRad(double degree); -double deltaCIE94Textiles(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCIEDE2000_(cv::Vec3d lab1, cv::Vec3d lab2, double kL = 1.0, - double kC = 1.0, double kH = 1.0); -double deltaCIEDE2000(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCMC(cv::Vec3d lab1, cv::Vec3d lab2, double kL = 1, double kC = 1); -double deltaCMC1To1(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCMC2To1(cv::Vec3d lab1, cv::Vec3d lab2); -cv::Mat distance(cv::Mat src, cv::Mat ref, DISTANCE_TYPE distance_type); - - /* *\ brief distance between two points in formula CIE76 *\ param lab1 a 3D vector *\ param lab2 a 3D vector *\ return distance between lab1 and lab2 */ -double deltaCIE76(cv::Vec3d lab1, cv::Vec3d lab2) { return norm(lab1 - lab2); }; +double deltaCIE76(cv::Vec3d lab1, cv::Vec3d lab2); /* *\ brief distance between two points in formula CIE94 *\ param lab1 a 3D vector @@ -82,36 +66,15 @@ double deltaCIE76(cv::Vec3d lab1, cv::Vec3d lab2) { return norm(lab1 - lab2); }; *\ param k2 second scale parameter *\ return distance between lab1 and lab2 */ -double deltaCIE94(cv::Vec3d lab1, cv::Vec3d lab2, double kH, - double kC, double kL, double k1, double k2) -{ - double dl = lab1[0] - lab2[0]; - double c1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2)); - double c2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2)); - double dc = c1 - c2; - double da = lab1[1] - lab2[1]; - double db = lab1[2] - lab2[2]; - double dh = pow(da, 2) + pow(db, 2) - pow(dc, 2); - double sc = 1.0 + k1 * c1; - double sh = 1.0 + k2 * c1; - double sl = 1.0; - double res = - pow(dl / (kL * sl), 2) + pow(dc / (kC * sc), 2) + dh / pow(kH * sh, 2); - - return res > 0 ? sqrt(res) : 0; -} +double deltaCIE94(cv::Vec3d lab1, cv::Vec3d lab2, double kH = 1.0, + double kC = 1.0, double kL = 1.0, double k1 = 0.045, + double k2 = 0.015); -double deltaCIE94GraphicArts(cv::Vec3d lab1, cv::Vec3d lab2) -{ - return deltaCIE94(lab1, lab2); -} +double deltaCIE94GraphicArts(cv::Vec3d lab1, cv::Vec3d lab2); -double toRad(double degree) { return degree / 180 * CV_PI; }; +double toRad(double degree); -double deltaCIE94Textiles(cv::Vec3d lab1, cv::Vec3d lab2) -{ - return deltaCIE94(lab1, lab2, 1.0, 1.0, 2.0, 0.048, 0.014); -} +double deltaCIE94Textiles(cv::Vec3d lab1, cv::Vec3d lab2); /* *\ brief distance between two points in formula CIE2000 *\ param lab1 a 3D vector @@ -121,99 +84,9 @@ double deltaCIE94Textiles(cv::Vec3d lab1, cv::Vec3d lab2) *\ param kH Hue scale *\ return distance between lab1 and lab2 */ -double deltaCIEDE2000_(cv::Vec3d lab1, cv::Vec3d lab2, double kL, - double kC, double kH) -{ - double delta_L_apo = lab2[0] - lab1[0]; - double l_bar_apo = (lab1[0] + lab2[0]) / 2.0; - double C1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2)); - double C2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2)); - double C_bar = (C1 + C2) / 2.0; - double G = sqrt(pow(C_bar, 7) / (pow(C_bar, 7) + pow(25, 7))); - double a1_apo = lab1[1] + lab1[1] / 2.0 * (1.0 - G); - double a2_apo = lab2[1] + lab2[1] / 2.0 * (1.0 - G); - double C1_apo = sqrt(pow(a1_apo, 2) + pow(lab1[2], 2)); - double C2_apo = sqrt(pow(a2_apo, 2) + pow(lab2[2], 2)); - double C_bar_apo = (C1_apo + C2_apo) / 2.0; - double delta_C_apo = C2_apo - C1_apo; - - double h1_apo; - if (C1_apo == 0) - { - h1_apo = 0.0; - } - else - { - h1_apo = atan2(lab1[2], a1_apo); - if (h1_apo < 0.0) h1_apo += 2. * CV_PI; - } - - double h2_apo; - if (C2_apo == 0) - { - h2_apo = 0.0; - } - else - { - h2_apo = atan2(lab2[2], a2_apo); - if (h2_apo < 0.0) h2_apo += 2. * CV_PI; - } - - double delta_h_apo; - if (abs(h2_apo - h1_apo) <= CV_PI) - { - delta_h_apo = h2_apo - h1_apo; - } - else if (h2_apo <= h1_apo) - { - delta_h_apo = h2_apo - h1_apo + 2. * CV_PI; - } - else - { - delta_h_apo = h2_apo - h1_apo - 2. * CV_PI; - } - - double H_bar_apo; - if (C1_apo == 0 || C2_apo == 0) - { - H_bar_apo = h1_apo + h2_apo; - } - else if (abs(h1_apo - h2_apo) <= CV_PI) - { - H_bar_apo = (h1_apo + h2_apo) / 2.0; - } - else if (h1_apo + h2_apo < 2. * CV_PI) - { - H_bar_apo = (h1_apo + h2_apo + 2. * CV_PI) / 2.0; - } - else - { - H_bar_apo = (h1_apo + h2_apo - 2. * CV_PI) / 2.0; - } - - double delta_H_apo = 2.0 * sqrt(C1_apo * C2_apo) * sin(delta_h_apo / 2.0); - double T = 1.0 - 0.17 * cos(H_bar_apo - toRad(30.)) + - 0.24 * cos(2.0 * H_bar_apo) + - 0.32 * cos(3.0 * H_bar_apo + toRad(6.0)) - - 0.2 * cos(4.0 * H_bar_apo - toRad(63.0)); - double sC = 1.0 + 0.045 * C_bar_apo; - double sH = 1.0 + 0.015 * C_bar_apo * T; - double sL = 1.0 + ((0.015 * pow(l_bar_apo - 50.0, 2.0)) / - sqrt(20.0 + pow(l_bar_apo - 50.0, 2.0))); - double RT = -2.0 * G * - sin(toRad(60.0) * - exp(-pow((H_bar_apo - toRad(275.0)) / toRad(25.0), 2.0))); - double res = - (pow(delta_L_apo / (kL * sL), 2.0) + pow(delta_C_apo / (kC * sC), 2.0) + - pow(delta_H_apo / (kH * sH), 2.0) + - RT * (delta_C_apo / (kC * sC)) * (delta_H_apo / (kH * sH))); - return res > 0 ? sqrt(res) : 0; -} - -double deltaCIEDE2000(cv::Vec3d lab1, cv::Vec3d lab2) -{ - return deltaCIEDE2000_(lab1, lab2); -} +double deltaCIEDE2000_(cv::Vec3d lab1, cv::Vec3d lab2, double kL = 1.0, + double kC = 1.0, double kH = 1.0); +double deltaCIEDE2000(cv::Vec3d lab1, cv::Vec3d lab2); /* *\ brief distance between two points in formula CMC *\ param lab1 a 3D vector @@ -222,77 +95,16 @@ double deltaCIEDE2000(cv::Vec3d lab1, cv::Vec3d lab2) *\ param kC Chroma scale *\ return distance between lab1 and lab2 */ -double deltaCMC(cv::Vec3d lab1, cv::Vec3d lab2, double kL, double kC) -{ - double dL = lab2[0] - lab1[0]; - double da = lab2[1] - lab1[1]; - double db = lab2[2] - lab1[2]; - double C1 = sqrt(pow(lab1[1], 2.0) + pow(lab1[2], 2.0)); - double C2 = sqrt(pow(lab2[1], 2.0) + pow(lab2[2], 2.0)); - double dC = C2 - C1; - double dH = sqrt(pow(da, 2) + pow(db, 2) - pow(dC, 2)); - - double H1; - if (C1 == 0.) - { - H1 = 0.0; - } - else - { - H1 = atan2(lab1[2], lab1[1]); - if (H1 < 0.0) H1 += 2. * CV_PI; - } - - double F = pow(C1, 2) / sqrt(pow(C1, 4) + 1900); - double T = (H1 > toRad(164) && H1 <= toRad(345)) - ? 0.56 + abs(0.2 * cos(H1 + toRad(168))) - : 0.36 + abs(0.4 * cos(H1 + toRad(35))); - double sL = - lab1[0] < 16. ? 0.511 : (0.040975 * lab1[0]) / (1.0 + 0.01765 * lab1[0]); - double sC = (0.0638 * C1) / (1.0 + 0.0131 * C1) + 0.638; - double sH = sC * (F * T + 1.0 - F); +double deltaCMC(cv::Vec3d lab1, cv::Vec3d lab2, double kL = 1, double kC = 1); - return sqrt(pow(dL / (kL * sL), 2.0) + pow(dC / (kC * sC), 2.0) + - pow(dH / sH, 2.0)); -} +double deltaCMC1To1(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCMC1To1(cv::Vec3d lab1, cv::Vec3d lab2) -{ - return deltaCMC(lab1, lab2); -} +double deltaCMC2To1(cv::Vec3d lab1, cv::Vec3d lab2); -double deltaCMC2To1(cv::Vec3d lab1, cv::Vec3d lab2) -{ - return deltaCMC(lab1, lab2, 2, 1); -} +Mat distance(Mat src, Mat ref, DISTANCE_TYPE distance_type); -cv::Mat distance(cv::Mat src, cv::Mat ref, DISTANCE_TYPE distance_type) -{ - switch (distance_type) - { - case cv::ccm::CIE76: - return distanceWise(src, ref, deltaCIE76); - case cv::ccm::CIE94_GRAPHIC_ARTS: - return distanceWise(src, ref, deltaCIE94GraphicArts); - case cv::ccm::CIE94_TEXTILES: - return distanceWise(src, ref, deltaCIE94Textiles); - case cv::ccm::CIE2000: - return distanceWise(src, ref, deltaCIEDE2000); - case cv::ccm::CMC_1TO1: - return distanceWise(src, ref, deltaCMC1To1); - case cv::ccm::CMC_2TO1: - return distanceWise(src, ref, deltaCMC2To1); - case cv::ccm::RGB: - return distanceWise(src, ref, deltaCIE76); - case cv::ccm::RGBL: - return distanceWise(src, ref, deltaCIE76); - default: - throw std::invalid_argument{ "Wrong distance_type!" }; - break; - } -}; +} // namespace ccm +} // namespace cv -} // namespace ccm -} // namespace cv -#endif +#endif \ No newline at end of file diff --git a/modules/mcc/include/opencv2/mcc/io.hpp b/modules/mcc/include/opencv2/mcc/io.hpp index dd62d46e423..e3d7f0bf58d 100644 --- a/modules/mcc/include/opencv2/mcc/io.hpp +++ b/modules/mcc/include/opencv2/mcc/io.hpp @@ -28,10 +28,8 @@ #ifndef __OPENCV_MCC_IO_HPP__ #define __OPENCV_MCC_IO_HPP__ -#include -#include -#include #include +#include namespace cv { @@ -39,29 +37,17 @@ namespace ccm { /* *\ brief Io is the meaning of illuminant and observer. See notes of ccm.hpp - * for supported list for illuminant and observer*/ -class IO + * for supported list for illuminant and observer*/ +class CV_EXPORTS_W IO { public: - std::string illuminant; std::string observer; - - IO() {}; - - IO(std::string illuminant_, std::string observer_) :illuminant(illuminant_), observer(observer_) {}; - - virtual ~IO() {}; - - bool operator<(const IO& other) const - { - return (illuminant < other.illuminant || ((illuminant == other.illuminant) && (observer < other.observer))); - } - - bool operator==(const IO& other) const - { - return illuminant == other.illuminant && observer == other.observer; - }; + IO(){}; + IO(std::string illuminant_, std::string observer_) ; + virtual ~IO(){}; + bool operator<(const IO& other) const; + bool operator==(const IO& other) const; }; const IO A_2("A", "2"), A_10("A", "10"), @@ -83,27 +69,9 @@ const static std::map> illuminants_xy = }; std::vector xyY2XYZ(const std::vector& xyY); -std::vector xyY2XYZ(const std::vector& xyY) -{ - double Y = xyY.size() >= 3 ? xyY[2] : 1; - return { Y * xyY[0] / xyY[1], Y, Y / xyY[1] * (1 - xyY[0] - xyY[1]) }; -} - -/* *\ brief function to get illuminants*/ -static std::map > getIlluminant(); -static std::map > getIlluminant() -{ - std::map > illuminants; - for (auto it = illuminants_xy.begin(); it != illuminants_xy.end(); ++it) - { - illuminants[it->first] = xyY2XYZ(it->second); - } - return illuminants; -} -const std::map > illuminants = getIlluminant(); } // namespace ccm } // namespace cv -#endif \ No newline at end of file +#endif diff --git a/modules/mcc/include/opencv2/mcc/utils.hpp b/modules/mcc/include/opencv2/mcc/utils.hpp index d1f56854c85..37cfb1febcb 100644 --- a/modules/mcc/include/opencv2/mcc/utils.hpp +++ b/modules/mcc/include/opencv2/mcc/utils.hpp @@ -28,38 +28,59 @@ #ifndef __OPENCV_MCC_UTILS_HPP__ #define __OPENCV_MCC_UTILS_HPP__ -#include -#include -#include -#include #include -namespace cv -{ -namespace ccm -{ +namespace cv { +namespace ccm { -double gammaCorrection_(const double& element, const double& gamma); -cv::Mat gammaCorrection(const cv::Mat& src, const double& gamma); -cv::Mat maskCopyTo(const cv::Mat& src, const cv::Mat& mask); -cv::Mat multiple(const cv::Mat& xyz, const cv::Mat& ccm); -cv::Mat saturate(cv::Mat& src, const double& low, const double& up); -cv::Mat rgb2gray(cv::Mat rgb); +/* *\ brief gamma correction ,see ColorSpace.pdf for details. + *\ param src the input array,type of Mat. + *\ param gamma a constant for gamma correction. + */ +CV_EXPORTS_W double gammaCorrection_(const double& element, const double& gamma); -/* *\ brief function for elementWise operation - *\ param src the input array, type of cv::Mat - *\ lambda a for operation +CV_EXPORTS_W Mat gammaCorrection(const Mat& src, const double& gamma); + +/* *\ brief maskCopyTo a function to delete unsatisfied elementwise. + *\ param src the input array, type of Mat. + *\ param mask operation mask that used to choose satisfided elementwise. + */ +CV_EXPORTS_W Mat maskCopyTo(const Mat& src, const Mat& mask); + +/* *\ brief multiple the function used to compute an array with n channels + *mulipied by ccm. \ param src the input array, type of Mat. \ param ccm the + *ccm matrix to make color correction. + */ +CV_EXPORTS_W Mat multiple(const Mat& xyz, const Mat& ccm); + +/* *\ brief multiple the function used to get the mask of saturated colors, + colors between low and up will be choosed. + *\ param src the input array, type of Mat. + *\ param low the threshold to choose saturated colors + *\ param up the threshold to choose saturated colors */ +CV_EXPORTS_W Mat saturate(Mat& src, const double& low, const double& up); + +/* *\ brief rgb2gray it is an approximation grayscale function for relative RGB + *color space, see Miscellaneous.pdf for details; \ param rgb the input array, + *type of Mat. + */ +CV_EXPORTS_W Mat rgb2gray(Mat rgb); + +/* *\ brief function for elementWise operation + *\ param src the input array, type of Mat + *\ lambda a for operation + */ template -cv::Mat elementWise(const cv::Mat& src, F&& lambda) +Mat elementWise(const Mat& src, F&& lambda) { - cv::Mat dst = src.clone(); + Mat dst = src.clone(); const int channel = src.channels(); switch (channel) { case 1: { - cv::MatIterator_ it, end; + MatIterator_ it, end; for (it = dst.begin(), end = dst.end(); it != end; ++it) { (*it) = lambda((*it)); @@ -68,7 +89,7 @@ cv::Mat elementWise(const cv::Mat& src, F&& lambda) } case 3: { - cv::MatIterator_ it, end; + MatIterator_ it, end; for (it = dst.begin(), end = dst.end(); it != end; ++it) { for (int j = 0; j < 3; j++) @@ -79,21 +100,21 @@ cv::Mat elementWise(const cv::Mat& src, F&& lambda) break; } default: - throw std::invalid_argument{ "Wrong channel!" }; + throw std::invalid_argument { "Wrong channel!" }; break; } return dst; } /* *\ brief function for channel operation - *\ param src the input array, type of cv::Mat + *\ param src the input array, type of Mat *\ lambda the function for operation */ template -cv::Mat channelWise(const cv::Mat& src, F&& lambda) +Mat channelWise(const Mat& src, F&& lambda) { - cv::Mat dst = src.clone(); - cv::MatIterator_ it, end; + Mat dst = src.clone(); + MatIterator_ it, end; for (it = dst.begin(), end = dst.end(); it != end; ++it) { *it = lambda(*it); @@ -102,17 +123,17 @@ cv::Mat channelWise(const cv::Mat& src, F&& lambda) } /* *\ brief function for distance operation. - *\ param src the input array, type of cv::Mat. - *\ param ref another input array, type of cv::Mat. - *\ param lambda the computing method for distance . -*/ + *\ param src the input array, type of Mat. + *\ param ref another input array, type of Mat. + *\ param lambda the computing method for distance . + */ template -cv::Mat distanceWise(cv::Mat& src, cv::Mat& ref, F&& lambda) +Mat distanceWise(Mat& src, Mat& ref, F&& lambda) { - cv::Mat dst = cv::Mat(src.size(), CV_64FC1); - cv::MatIterator_ it_src = src.begin(), end_src = src.end(), + Mat dst = Mat(src.size(), CV_64FC1); + MatIterator_ it_src = src.begin(), end_src = src.end(), it_ref = ref.begin(); - cv::MatIterator_ it_dst = dst.begin(); + MatIterator_ it_dst = dst.begin(); for (; it_src != end_src; ++it_src, ++it_ref, ++it_dst) { *it_dst = lambda(*it_src, *it_ref); @@ -120,116 +141,11 @@ cv::Mat distanceWise(cv::Mat& src, cv::Mat& ref, F&& lambda) return dst; } +CV_EXPORTS_W Mat multiple(const Mat& xyz, const Mat& ccm); -double gammaCorrection_(const double& element, const double& gamma) -{ - return (element >= 0 ? pow(element, gamma) : -pow((-element), gamma)); -} - -/* *\ brief gamma correction, see ColorSpace.pdf for details. - *\ param src the input array, type of cv::Mat. - *\ param gamma a constant for gamma correction. -*/ -cv::Mat gammaCorrection(const cv::Mat& src, const double& gamma) -{ - return elementWise(src, [gamma](double element)->double {return gammaCorrection_(element, gamma); }); -} - -/* *\ brief maskCopyTo a function to delete unsatisfied elementwise. - *\ param src the input array, type of cv::Mat. - *\ param mask operation mask that used to choose satisfided elementwise. -*/ -cv::Mat maskCopyTo(const cv::Mat& src, const cv::Mat& mask) -{ - cv::Mat dst(countNonZero(mask), 1, src.type()); - const int channel = src.channels(); - auto it_mask = mask.begin(); - switch (channel) - { - case 1: - { - auto it_src = src.begin(), end_src = src.end(); - auto it_dst = dst.begin(); - for (; it_src != end_src; ++it_src, ++it_mask) - { - if (*it_mask) - { - (*it_dst) = (*it_src); - ++it_dst; - } - } - break; - } - case 3: - { - auto it_src = src.begin(), end_src = src.end(); - auto it_dst = dst.begin(); - for (; it_src != end_src; ++it_src, ++it_mask) - { - if (*it_mask) - { - (*it_dst) = (*it_src); - ++it_dst; - } - } - break; - } - default: - throw std::invalid_argument{ "Wrong channel!" }; - break; - } - return dst; -} - -/* *\ brief multiple the function used to compute an array with n channels mulipied by ccm. - *\ param src the input array, type of cv::Mat. - *\ param ccm the ccm matrix to make color correction. -*/ -cv::Mat multiple(const cv::Mat& xyz, const cv::Mat& ccm) -{ - cv::Mat tmp = xyz.reshape(1, xyz.rows * xyz.cols); - cv::Mat res = tmp * ccm; - res = res.reshape(res.cols, xyz.rows); - return res; -} - -/* *\ brief multiple the function used to get the mask of saturated colors, - colors between low and up will be choosed. - *\ param src the input array, type of cv::Mat. - *\ param low the threshold to choose saturated colors - *\ param up the threshold to choose saturated colors -*/ -cv::Mat saturate(cv::Mat& src, const double& low, const double& up) -{ - cv::Mat dst = cv::Mat::ones(src.size(), CV_8UC1); - cv::MatIterator_ it_src = src.begin(), end_src = src.end(); - cv::MatIterator_ it_dst = dst.begin(); - for (; it_src != end_src; ++it_src, ++it_dst) - { - for (int i = 0; i < 3; ++i) - { - if ((*it_src)[i] > up || (*it_src)[i] < low) - { - *it_dst = 0; - break; - } - } - } - return dst; -} - -const static cv::Mat m_gray = (cv::Mat_(3, 1) << 0.2126, 0.7152, 0.0722); - -/* *\ brief rgb2gray it is an approximation grayscale function for relative RGB color space, - * see Miscellaneous.pdf for details; - *\ param rgb the input array, type of cv::Mat. -*/ -cv::Mat rgb2gray(cv::Mat rgb) -{ - return multiple(rgb, m_gray); -} -} // namespace ccm -} // namespace cv +const static Mat m_gray = (Mat_(3, 1) << 0.2126, 0.7152, 0.0722); +} // namespace ccm +} // namespace cv -#endif \ No newline at end of file +#endif diff --git a/modules/mcc/samples/color_correction_model.cpp b/modules/mcc/samples/color_correction_model.cpp index fd5ffef8f2b..9da005c4d48 100644 --- a/modules/mcc/samples/color_correction_model.cpp +++ b/modules/mcc/samples/color_correction_model.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include using namespace std; @@ -113,7 +112,7 @@ int main(int argc, char *argv[]) // Save the calibrated image to {FILE_NAME}.calibrated.{FILE_EXT} string filename = filepath.substr(filepath.find_last_of('/')+1); - int dotIndex = filename.find_last_of('.'); + size_t dotIndex = filename.find_last_of('.'); string baseName = filename.substr(0, dotIndex); string ext = filename.substr(dotIndex+1, filename.length()-dotIndex); string calibratedFilePath = baseName + ".calibrated." + ext; diff --git a/modules/mcc/src/distance.cpp b/modules/mcc/src/distance.cpp new file mode 100644 index 00000000000..dd39f0c614a --- /dev/null +++ b/modules/mcc/src/distance.cpp @@ -0,0 +1,233 @@ +// 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. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright(C) 2020, Huawei Technologies Co.,Ltd. All rights reserved. +// Third party copyrights are property of their respective owners. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Longbu Wang +// Jinheng Zhang +// Chenqi Shan + +#include "precomp.hpp" + +namespace cv +{ +namespace ccm +{ + +double deltaCIE76(cv::Vec3d lab1, cv::Vec3d lab2) { return norm(lab1 - lab2); }; + +double deltaCIE94(cv::Vec3d lab1, cv::Vec3d lab2, double kH, + double kC, double kL, double k1, double k2) +{ + double dl = lab1[0] - lab2[0]; + double c1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2)); + double c2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2)); + double dc = c1 - c2; + double da = lab1[1] - lab2[1]; + double db = lab1[2] - lab2[2]; + double dh = pow(da, 2) + pow(db, 2) - pow(dc, 2); + double sc = 1.0 + k1 * c1; + double sh = 1.0 + k2 * c1; + double sl = 1.0; + double res = + pow(dl / (kL * sl), 2) + pow(dc / (kC * sc), 2) + dh / pow(kH * sh, 2); + + return res > 0 ? sqrt(res) : 0; +} + +double deltaCIE94GraphicArts(cv::Vec3d lab1, cv::Vec3d lab2) +{ + return deltaCIE94(lab1, lab2); +} + +double toRad(double degree) { return degree / 180 * CV_PI; }; + +double deltaCIE94Textiles(cv::Vec3d lab1, cv::Vec3d lab2) +{ + return deltaCIE94(lab1, lab2, 1.0, 1.0, 2.0, 0.048, 0.014); +} + +double deltaCIEDE2000_(cv::Vec3d lab1, cv::Vec3d lab2, double kL, + double kC, double kH) +{ + double delta_L_apo = lab2[0] - lab1[0]; + double l_bar_apo = (lab1[0] + lab2[0]) / 2.0; + double C1 = sqrt(pow(lab1[1], 2) + pow(lab1[2], 2)); + double C2 = sqrt(pow(lab2[1], 2) + pow(lab2[2], 2)); + double C_bar = (C1 + C2) / 2.0; + double G = sqrt(pow(C_bar, 7) / (pow(C_bar, 7) + pow(25, 7))); + double a1_apo = lab1[1] + lab1[1] / 2.0 * (1.0 - G); + double a2_apo = lab2[1] + lab2[1] / 2.0 * (1.0 - G); + double C1_apo = sqrt(pow(a1_apo, 2) + pow(lab1[2], 2)); + double C2_apo = sqrt(pow(a2_apo, 2) + pow(lab2[2], 2)); + double C_bar_apo = (C1_apo + C2_apo) / 2.0; + double delta_C_apo = C2_apo - C1_apo; + + double h1_apo; + if (C1_apo == 0) + { + h1_apo = 0.0; + } + else + { + h1_apo = atan2(lab1[2], a1_apo); + if (h1_apo < 0.0) h1_apo += 2. * CV_PI; + } + + double h2_apo; + if (C2_apo == 0) + { + h2_apo = 0.0; + } + else + { + h2_apo = atan2(lab2[2], a2_apo); + if (h2_apo < 0.0) h2_apo += 2. * CV_PI; + } + + double delta_h_apo; + if (abs(h2_apo - h1_apo) <= CV_PI) + { + delta_h_apo = h2_apo - h1_apo; + } + else if (h2_apo <= h1_apo) + { + delta_h_apo = h2_apo - h1_apo + 2. * CV_PI; + } + else + { + delta_h_apo = h2_apo - h1_apo - 2. * CV_PI; + } + + double H_bar_apo; + if (C1_apo == 0 || C2_apo == 0) + { + H_bar_apo = h1_apo + h2_apo; + } + else if (abs(h1_apo - h2_apo) <= CV_PI) + { + H_bar_apo = (h1_apo + h2_apo) / 2.0; + } + else if (h1_apo + h2_apo < 2. * CV_PI) + { + H_bar_apo = (h1_apo + h2_apo + 2. * CV_PI) / 2.0; + } + else + { + H_bar_apo = (h1_apo + h2_apo - 2. * CV_PI) / 2.0; + } + + double delta_H_apo = 2.0 * sqrt(C1_apo * C2_apo) * sin(delta_h_apo / 2.0); + double T = 1.0 - 0.17 * cos(H_bar_apo - toRad(30.)) + + 0.24 * cos(2.0 * H_bar_apo) + + 0.32 * cos(3.0 * H_bar_apo + toRad(6.0)) - + 0.2 * cos(4.0 * H_bar_apo - toRad(63.0)); + double sC = 1.0 + 0.045 * C_bar_apo; + double sH = 1.0 + 0.015 * C_bar_apo * T; + double sL = 1.0 + ((0.015 * pow(l_bar_apo - 50.0, 2.0)) / + sqrt(20.0 + pow(l_bar_apo - 50.0, 2.0))); + double RT = -2.0 * G * + sin(toRad(60.0) * + exp(-pow((H_bar_apo - toRad(275.0)) / toRad(25.0), 2.0))); + double res = + (pow(delta_L_apo / (kL * sL), 2.0) + pow(delta_C_apo / (kC * sC), 2.0) + + pow(delta_H_apo / (kH * sH), 2.0) + + RT * (delta_C_apo / (kC * sC)) * (delta_H_apo / (kH * sH))); + return res > 0 ? sqrt(res) : 0; +} + +double deltaCIEDE2000(cv::Vec3d lab1, cv::Vec3d lab2) +{ + return deltaCIEDE2000_(lab1, lab2); +} + +double deltaCMC(cv::Vec3d lab1, cv::Vec3d lab2, double kL, double kC) +{ + double dL = lab2[0] - lab1[0]; + double da = lab2[1] - lab1[1]; + double db = lab2[2] - lab1[2]; + double C1 = sqrt(pow(lab1[1], 2.0) + pow(lab1[2], 2.0)); + double C2 = sqrt(pow(lab2[1], 2.0) + pow(lab2[2], 2.0)); + double dC = C2 - C1; + double dH = sqrt(pow(da, 2) + pow(db, 2) - pow(dC, 2)); + + double H1; + if (C1 == 0.) + { + H1 = 0.0; + } + else + { + H1 = atan2(lab1[2], lab1[1]); + if (H1 < 0.0) H1 += 2. * CV_PI; + } + + double F = pow(C1, 2) / sqrt(pow(C1, 4) + 1900); + double T = (H1 > toRad(164) && H1 <= toRad(345)) + ? 0.56 + abs(0.2 * cos(H1 + toRad(168))) + : 0.36 + abs(0.4 * cos(H1 + toRad(35))); + double sL = + lab1[0] < 16. ? 0.511 : (0.040975 * lab1[0]) / (1.0 + 0.01765 * lab1[0]); + double sC = (0.0638 * C1) / (1.0 + 0.0131 * C1) + 0.638; + double sH = sC * (F * T + 1.0 - F); + + return sqrt(pow(dL / (kL * sL), 2.0) + pow(dC / (kC * sC), 2.0) + + pow(dH / sH, 2.0)); +} + +double deltaCMC1To1(cv::Vec3d lab1, cv::Vec3d lab2) +{ + return deltaCMC(lab1, lab2); +} + +double deltaCMC2To1(cv::Vec3d lab1, cv::Vec3d lab2) +{ + return deltaCMC(lab1, lab2, 2, 1); +} + +Mat distance(Mat src, Mat ref, DISTANCE_TYPE distance_type) +{ + switch (distance_type) + { + case cv::ccm::CIE76: + return distanceWise(src, ref, deltaCIE76); + case cv::ccm::CIE94_GRAPHIC_ARTS: + return distanceWise(src, ref, deltaCIE94GraphicArts); + case cv::ccm::CIE94_TEXTILES: + return distanceWise(src, ref, deltaCIE94Textiles); + case cv::ccm::CIE2000: + return distanceWise(src, ref, deltaCIEDE2000); + case cv::ccm::CMC_1TO1: + return distanceWise(src, ref, deltaCMC1To1); + case cv::ccm::CMC_2TO1: + return distanceWise(src, ref, deltaCMC2To1); + case cv::ccm::RGB: + return distanceWise(src, ref, deltaCIE76); + case cv::ccm::RGBL: + return distanceWise(src, ref, deltaCIE76); + default: + throw std::invalid_argument{ "Wrong distance_type!" }; + break; + } +}; + +} // namespace ccm +} // namespace cv \ No newline at end of file diff --git a/modules/mcc/src/io.cpp b/modules/mcc/src/io.cpp new file mode 100644 index 00000000000..6d061326b48 --- /dev/null +++ b/modules/mcc/src/io.cpp @@ -0,0 +1,53 @@ +// 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. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright(C) 2020, Huawei Technologies Co.,Ltd. All rights reserved. +// Third party copyrights are property of their respective owners. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Longbu Wang +// Jinheng Zhang +// Chenqi Shan + +#include "opencv2/mcc/io.hpp" +namespace cv +{ +namespace ccm +{ +IO::IO(std::string illuminant_, std::string observer_) :illuminant(illuminant_), observer(observer_) {}; + +bool IO::operator<(const IO& other) const +{ + return (illuminant < other.illuminant || ((illuminant == other.illuminant) && (observer < other.observer))); +} + +bool IO::operator==(const IO& other) const +{ + return illuminant == other.illuminant && observer == other.observer; +}; + +// data from https://en.wikipedia.org/wiki/Standard_illuminant. +std::vector xyY2XYZ(const std::vector& xyY) +{ + double Y = xyY.size() >= 3 ? xyY[2] : 1; + return { Y * xyY[0] / xyY[1], Y, Y / xyY[1] * (1 - xyY[0] - xyY[1]) }; +} + +} // namespace ccm +} // namespace cv diff --git a/modules/mcc/src/utils.cpp b/modules/mcc/src/utils.cpp new file mode 100644 index 00000000000..56f3be7aaba --- /dev/null +++ b/modules/mcc/src/utils.cpp @@ -0,0 +1,120 @@ +// 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. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright(C) 2020, Huawei Technologies Co.,Ltd. All rights reserved. +// Third party copyrights are property of their respective owners. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Longbu Wang +// Jinheng Zhang +// Chenqi Shan + +#include "precomp.hpp" + +namespace cv +{ +namespace ccm +{ + +double gammaCorrection_(const double& element, const double& gamma) +{ + return (element >= 0 ? pow(element, gamma) : -pow((-element), gamma)); +} + +cv::Mat gammaCorrection(const cv::Mat& src, const double& gamma) +{ + return elementWise(src, [gamma](double element)->double {return gammaCorrection_(element, gamma); }); +} + +cv::Mat maskCopyTo(const cv::Mat& src, const cv::Mat& mask) +{ + cv::Mat dst(countNonZero(mask), 1, src.type()); + const int channel = src.channels(); + auto it_mask = mask.begin(); + switch (channel) + { + case 1: + { + auto it_src = src.begin(), end_src = src.end(); + auto it_dst = dst.begin(); + for (; it_src != end_src; ++it_src, ++it_mask) + { + if (*it_mask) + { + (*it_dst) = (*it_src); + ++it_dst; + } + } + break; + } + case 3: + { + auto it_src = src.begin(), end_src = src.end(); + auto it_dst = dst.begin(); + for (; it_src != end_src; ++it_src, ++it_mask) + { + if (*it_mask) + { + (*it_dst) = (*it_src); + ++it_dst; + } + } + break; + } + default: + throw std::invalid_argument { "Wrong channel!" }; + break; + } + return dst; +} + +cv::Mat multiple(const cv::Mat& xyz, const cv::Mat& ccm) +{ + cv::Mat tmp = xyz.reshape(1, xyz.rows * xyz.cols); + cv::Mat res = tmp * ccm; + res = res.reshape(res.cols, xyz.rows); + return res; +} + +cv::Mat saturate(cv::Mat& src, const double& low, const double& up) +{ + cv::Mat dst = cv::Mat::ones(src.size(), CV_8UC1); + cv::MatIterator_ it_src = src.begin(), end_src = src.end(); + cv::MatIterator_ it_dst = dst.begin(); + for (; it_src != end_src; ++it_src, ++it_dst) + { + for (int i = 0; i < 3; ++i) + { + if ((*it_src)[i] > up || (*it_src)[i] < low) + { + *it_dst = 0; + break; + } + } + } + return dst; +} + +cv::Mat rgb2gray(cv::Mat rgb) +{ + return multiple(rgb, m_gray); +} + +} // namespace ccm +} // namespace cv diff --git a/modules/mcc/tutorials/basic_ccm/color_correction_model.markdown b/modules/mcc/tutorials/basic_ccm/color_correction_model.markdown index 0999c92cd72..6a761f66dbb 100644 --- a/modules/mcc/tutorials/basic_ccm/color_correction_model.markdown +++ b/modules/mcc/tutorials/basic_ccm/color_correction_model.markdown @@ -181,7 +181,7 @@ Supported Color Space: The first part is to detect the ColorChecker position. -@code{.cpp}#include #include #include #include #include #include using namespace std;using namespace cv;using namespace mcc;using namespace ccm;using namespace std;@endcode +@code{.cpp}#include #include #include #include #include using namespace std;using namespace cv;using namespace mcc;using namespace ccm;using namespace std;@endcode ``` Here is sets of header and namespaces. You can set other namespace like the code above. @@ -235,4 +235,4 @@ The member function infer_image is to make correction correction using ccm matri ``` Save the calibrated image. -``` \ No newline at end of file +```