vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "linearize.hpp"
|
||||
#include <cmath>
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
class ColorCorrectionModel::Impl
|
||||
{
|
||||
public:
|
||||
Mat src;
|
||||
|
||||
Color ref = Color();
|
||||
Mat dist;
|
||||
RGBBase_& cs;
|
||||
// Track initialization parameters for serialization
|
||||
ColorSpace csEnum;
|
||||
Mat mask;
|
||||
|
||||
// RGBl of detected data and the reference
|
||||
Mat srcRgbl;
|
||||
Mat dstRgbl;
|
||||
|
||||
// ccm type and shape
|
||||
CcmType ccmType;
|
||||
int shape;
|
||||
|
||||
// linear method and distance
|
||||
std::shared_ptr<Linear> linear = std::make_shared<Linear>();
|
||||
DistanceType distance;
|
||||
LinearizationType linearizationType;
|
||||
|
||||
Mat weights;
|
||||
Mat weightsList;
|
||||
Mat ccm;
|
||||
Mat ccm0;
|
||||
double gamma;
|
||||
int deg;
|
||||
std::vector<double> saturatedThreshold;
|
||||
InitialMethodType initialMethodType;
|
||||
double weightsCoeff;
|
||||
int maskedLen;
|
||||
double loss;
|
||||
int maxCount;
|
||||
double epsilon;
|
||||
bool rgb;
|
||||
Impl();
|
||||
|
||||
/** @brief Make no change for CCM_LINEAR.
|
||||
convert cv::Mat A to [A, 1] in CCM_AFFINE.
|
||||
@param inp the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat prepare(const Mat& inp);
|
||||
|
||||
/** @brief Calculate weights and mask.
|
||||
@param weightsList the input array, type of cv::Mat.
|
||||
@param weightsCoeff type of double.
|
||||
@param saturateMask the input array, type of cv::Mat.
|
||||
*/
|
||||
void calWeightsMasks(const Mat& weightsList, double weightsCoeff, Mat saturateMask);
|
||||
|
||||
/** @brief Fitting nonlinear - optimization initial value by white balance.
|
||||
@return the output array, type of Mat
|
||||
*/
|
||||
void initialWhiteBalance(void);
|
||||
|
||||
/** @brief Fitting nonlinear-optimization initial value by least square.
|
||||
@param fit if fit is True, return optimalization for rgbl distance function.
|
||||
*/
|
||||
void initialLeastSquare(bool fit = false);
|
||||
|
||||
double calcLoss_(Color color);
|
||||
double calcLoss(const Mat ccm_);
|
||||
|
||||
/** @brief Fitting ccm if distance function is associated with CIE Lab color space.
|
||||
see details in https://github.com/opencv/opencv/blob/master/modules/core/include/opencv2/core/optim.hpp
|
||||
Set terminal criteria for solver is possible.
|
||||
*/
|
||||
void fitting(void);
|
||||
|
||||
void getColor(Mat& img_, bool islinear = false);
|
||||
void getColor(ColorCheckerType constColor);
|
||||
void getColor(Mat colors_, ColorSpace cs_, Mat colored_);
|
||||
void getColor(Mat colors_, ColorSpace refColorSpace_);
|
||||
|
||||
/** @brief Loss function base on cv::MinProblemSolver::Function.
|
||||
see details in https://github.com/opencv/opencv/blob/master/modules/core/include/opencv2/core/optim.hpp
|
||||
*/
|
||||
class LossFunction : public MinProblemSolver::Function
|
||||
{
|
||||
public:
|
||||
ColorCorrectionModel::Impl* ccmLoss;
|
||||
LossFunction(ColorCorrectionModel::Impl* ccm)
|
||||
: ccmLoss(ccm) {};
|
||||
|
||||
/** @brief Reset dims to ccm->shape.
|
||||
*/
|
||||
int getDims() const CV_OVERRIDE
|
||||
{
|
||||
return ccmLoss->shape;
|
||||
}
|
||||
|
||||
/** @brief Reset calculation.
|
||||
*/
|
||||
double calc(const double* x) const CV_OVERRIDE
|
||||
{
|
||||
Mat ccm_(ccmLoss->shape, 1, CV_64F);
|
||||
for (int i = 0; i < ccmLoss->shape; i++)
|
||||
{
|
||||
ccm_.at<double>(i, 0) = x[i];
|
||||
}
|
||||
ccm_ = ccm_.reshape(0, ccmLoss->shape / 3);
|
||||
return ccmLoss->calcLoss(ccm_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ColorCorrectionModel::Impl::Impl()
|
||||
: cs(*GetCS::getInstance().getRgb(COLOR_SPACE_SRGB))
|
||||
, csEnum(COLOR_SPACE_SRGB)
|
||||
, ccmType(CCM_LINEAR)
|
||||
, distance(DISTANCE_CIE2000)
|
||||
, linearizationType(LINEARIZATION_GAMMA)
|
||||
, weights(Mat())
|
||||
, gamma(2.2)
|
||||
, deg(3)
|
||||
, saturatedThreshold({ 0, 0.98 })
|
||||
, initialMethodType(INITIAL_METHOD_LEAST_SQUARE)
|
||||
, weightsCoeff(0)
|
||||
, maxCount(5000)
|
||||
, epsilon(1.e-4)
|
||||
, rgb(true)
|
||||
{}
|
||||
|
||||
Mat ColorCorrectionModel::Impl::prepare(const Mat& inp)
|
||||
{
|
||||
switch (ccmType)
|
||||
{
|
||||
case cv::ccm::CCM_LINEAR:
|
||||
shape = 9;
|
||||
return inp;
|
||||
case cv::ccm::CCM_AFFINE:
|
||||
{
|
||||
shape = 12;
|
||||
Mat ones(inp.size(), CV_64F, Scalar(1));
|
||||
Mat out(inp.size(), CV_64FC4);
|
||||
const Mat srcs[] = { inp, ones };
|
||||
const int fromTo[] = { 0,0, 1,1, 2,2, 3,3 }; // inp[ch] → out[ch]
|
||||
mixChannels(srcs, 2, &out, 1, fromTo, 4);
|
||||
return out;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong ccmType!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::calWeightsMasks(const Mat& weightsList_, double weightsCoeff_, Mat saturateMask)
|
||||
{
|
||||
// weights
|
||||
if (!weightsList_.empty())
|
||||
{
|
||||
weights = weightsList_;
|
||||
}
|
||||
else if (weightsCoeff_ != 0)
|
||||
{
|
||||
pow(ref.toLuminant(cs.illumobserver), weightsCoeff_, weights);
|
||||
}
|
||||
|
||||
// masks
|
||||
Mat weight_mask = Mat::ones(src.rows, 1, CV_8U);
|
||||
if (!weights.empty())
|
||||
{
|
||||
weight_mask = weights > 0;
|
||||
}
|
||||
this->mask = (weight_mask) & (saturateMask);
|
||||
|
||||
// weights' mask
|
||||
if (!weights.empty())
|
||||
{
|
||||
Mat weights_masked = maskCopyTo(this->weights, this->mask);
|
||||
weights = weights_masked / mean(weights_masked)[0];
|
||||
}
|
||||
maskedLen = (int)sum(mask)[0];
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::initialWhiteBalance()
|
||||
{
|
||||
// sum over all pixels – Scalar holds per-channel sums
|
||||
const cv::Scalar srcSum = cv::sum(srcRgbl);
|
||||
const cv::Scalar dstSum = cv::sum(dstRgbl);
|
||||
|
||||
// channel-wise gain factors
|
||||
const double gR = dstSum[0] / srcSum[0];
|
||||
const double gG = dstSum[1] / srcSum[1];
|
||||
const double gB = dstSum[2] / srcSum[2];
|
||||
|
||||
// shape == 9 for a 3×3 linear CCM, or 12 for a 3×4 affine CCM
|
||||
if (shape == 9) {
|
||||
// 3×3 diagonal matrix
|
||||
ccm0 = cv::Mat::zeros(3, 3, CV_64F);
|
||||
ccm0.at<double>(0, 0) = gR;
|
||||
ccm0.at<double>(1, 1) = gG;
|
||||
ccm0.at<double>(2, 2) = gB;
|
||||
}
|
||||
else {
|
||||
// 3×4 affine matrix (last column = zeros)
|
||||
ccm0 = cv::Mat::zeros(3, 4, CV_64F);
|
||||
ccm0.at<double>(0, 0) = gR;
|
||||
ccm0.at<double>(1, 1) = gG;
|
||||
ccm0.at<double>(2, 2) = gB;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ColorCorrectionModel::Impl::initialLeastSquare(bool fit)
|
||||
{
|
||||
Mat A, B, w;
|
||||
if (weights.empty())
|
||||
{
|
||||
A = srcRgbl;
|
||||
B = dstRgbl;
|
||||
}
|
||||
else
|
||||
{
|
||||
pow(weights, 0.5, w);
|
||||
Mat w_;
|
||||
merge(std::vector<Mat> { w, w, w }, w_);
|
||||
A = w_.mul(srcRgbl);
|
||||
B = w_.mul(dstRgbl);
|
||||
}
|
||||
solve(A.reshape(1, A.rows), B.reshape(1, B.rows), ccm0, DECOMP_SVD);
|
||||
|
||||
// if fit is True, return optimalization for rgbl distance function.
|
||||
if (fit)
|
||||
{
|
||||
ccm = ccm0;
|
||||
Mat residual = A.reshape(1, A.rows) * ccm.reshape(0, shape / 3) - B.reshape(1, B.rows);
|
||||
Scalar s = residual.dot(residual);
|
||||
double sum = s[0];
|
||||
loss = sqrt(sum / maskedLen);
|
||||
}
|
||||
}
|
||||
|
||||
double ColorCorrectionModel::Impl::calcLoss_(Color color)
|
||||
{
|
||||
Mat distlist = color.diff(ref, distance);
|
||||
Color lab = color.to(COLOR_SPACE_LAB_D50_2);
|
||||
Mat dist_;
|
||||
pow(distlist, 2, dist_);
|
||||
if (!weights.empty())
|
||||
{
|
||||
dist_ = weights.mul(dist_);
|
||||
}
|
||||
Scalar ss = sum(dist_);
|
||||
return ss[0];
|
||||
}
|
||||
|
||||
double ColorCorrectionModel::Impl::calcLoss(const Mat ccm_)
|
||||
{
|
||||
Mat converted = srcRgbl.reshape(1, 0) * ccm_;
|
||||
Color color(converted.reshape(3, 0), *(cs.l));
|
||||
return calcLoss_(color);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::fitting(void)
|
||||
{
|
||||
cv::Ptr<DownhillSolver> solver = cv::DownhillSolver::create();
|
||||
cv::Ptr<LossFunction> ptr_F(new LossFunction(this));
|
||||
solver->setFunction(ptr_F);
|
||||
Mat reshapeCcm = ccm0.clone().reshape(0, 1);
|
||||
Mat step = Mat::ones(reshapeCcm.size(), CV_64F);
|
||||
solver->setInitStep(step);
|
||||
TermCriteria termcrit = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, maxCount, epsilon);
|
||||
solver->setTermCriteria(termcrit);
|
||||
double res = solver->minimize(reshapeCcm);
|
||||
ccm = reshapeCcm.reshape(0, shape / 3);
|
||||
loss = sqrt(res / maskedLen);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel()
|
||||
: p(std::make_shared<Impl>())
|
||||
{}
|
||||
|
||||
void ColorCorrectionModel::correctImage(InputArray src, OutputArray ref, bool islinear)
|
||||
{
|
||||
if (!p->ccm.data)
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "No CCM values!" );
|
||||
}
|
||||
Mat img, normImg;
|
||||
if (p->rgb){
|
||||
cvtColor(src.getMat(), img, COLOR_BGR2RGB);
|
||||
} else {
|
||||
img = src.getMat();
|
||||
}
|
||||
|
||||
double scale;
|
||||
int type = img.type();
|
||||
switch (type) {
|
||||
case CV_8UC3:
|
||||
scale = 1.0 / 255.0;
|
||||
break;
|
||||
case CV_16UC3:
|
||||
scale = 1.0 / 65535.0;
|
||||
break;
|
||||
case CV_32FC3:
|
||||
scale = 1.0; // Already in [0,1] range
|
||||
break;
|
||||
default:
|
||||
CV_Error( cv::Error::StsUnsupportedFormat, "8-bit, 16-bit unsigned or 32-bit float 3-channel input images are supported");
|
||||
}
|
||||
|
||||
img.convertTo(normImg, CV_64F, scale);
|
||||
Mat linearImg = (p->linear)->linearize(normImg);
|
||||
Mat ccm = p->ccm.reshape(0, p->shape / 3);
|
||||
Mat imgCcm = multiple(p->prepare(linearImg), ccm);
|
||||
if (islinear == true)
|
||||
{
|
||||
imgCcm.copyTo(ref);
|
||||
}
|
||||
Mat imgCorrected = p->cs.fromLFunc(imgCcm, linearImg);
|
||||
|
||||
imgCorrected *= 1.0/scale;
|
||||
imgCorrected.convertTo(imgCorrected, type);
|
||||
|
||||
if (p->rgb)
|
||||
cvtColor(imgCorrected, imgCorrected, COLOR_RGB2BGR);
|
||||
imgCorrected.copyTo(ref);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(ColorCheckerType constColor)
|
||||
{
|
||||
ref = GetColor().getColor(constColor);
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(Mat colors_, ColorSpace refColorSpace_)
|
||||
{
|
||||
ref = Color(colors_, *GetCS::getInstance().getCS(refColorSpace_));
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::Impl::getColor(Mat colors_, ColorSpace cs_, Mat colored_)
|
||||
{
|
||||
ref = Color(colors_, *GetCS::getInstance().getCS(cs_), colored_);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, int constColor): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(static_cast<ColorCheckerType>(constColor));
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, InputArray colors_, ColorSpace refColorSpace_): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(colors_.getMat(), refColorSpace_);
|
||||
}
|
||||
|
||||
ColorCorrectionModel::ColorCorrectionModel(InputArray src_, InputArray colors_, ColorSpace cs_, InputArray coloredPatchesMask_): p(std::make_shared<Impl>())
|
||||
{
|
||||
p->src = src_.getMat();
|
||||
p->getColor(colors_.getMat(), cs_, coloredPatchesMask_.getMat());
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::setColorSpace(ColorSpace cs_)
|
||||
{
|
||||
p->cs = *GetCS::getInstance().getRgb(cs_);
|
||||
}
|
||||
void ColorCorrectionModel::setCcmType(CcmType ccmType_)
|
||||
{
|
||||
p->ccmType = ccmType_;
|
||||
}
|
||||
void ColorCorrectionModel::setDistance(DistanceType distance_)
|
||||
{
|
||||
p->distance = distance_;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearization(LinearizationType linearizationType)
|
||||
{
|
||||
p->linearizationType = linearizationType;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearizationGamma(double gamma)
|
||||
{
|
||||
p->gamma = gamma;
|
||||
}
|
||||
void ColorCorrectionModel::setLinearizationDegree(int deg)
|
||||
{
|
||||
p->deg = deg;
|
||||
}
|
||||
void ColorCorrectionModel::setSaturatedThreshold(double lower, double upper)
|
||||
{ //std::vector<double> saturatedThreshold
|
||||
p->saturatedThreshold = { lower, upper };
|
||||
}
|
||||
void ColorCorrectionModel::setWeightsList(const Mat& weightsList)
|
||||
{
|
||||
p->weightsList = weightsList;
|
||||
}
|
||||
void ColorCorrectionModel::setWeightCoeff(double weightsCoeff)
|
||||
{
|
||||
p->weightsCoeff = weightsCoeff;
|
||||
}
|
||||
void ColorCorrectionModel::setInitialMethod(InitialMethodType initialMethodType)
|
||||
{
|
||||
p->initialMethodType = initialMethodType;
|
||||
}
|
||||
void ColorCorrectionModel::setMaxCount(int maxCount_)
|
||||
{
|
||||
p->maxCount = maxCount_;
|
||||
}
|
||||
void ColorCorrectionModel::setEpsilon(double epsilon_)
|
||||
{
|
||||
p->epsilon = epsilon_;
|
||||
}
|
||||
void ColorCorrectionModel::setRGB(bool rgb_)
|
||||
{
|
||||
p->rgb = rgb_;
|
||||
}
|
||||
Mat ColorCorrectionModel::compute()
|
||||
{
|
||||
|
||||
Mat saturateMask = saturate(p->src, p->saturatedThreshold[0], p->saturatedThreshold[1]);
|
||||
p->linear = getLinear(p->gamma, p->deg, p->src, p->ref, saturateMask, (p->cs), p->linearizationType);
|
||||
p->calWeightsMasks(p->weightsList, p->weightsCoeff, saturateMask);
|
||||
p->srcRgbl = p->linear->linearize(maskCopyTo(p->src, p->mask));
|
||||
p->ref.colors = maskCopyTo(p->ref.colors, p->mask);
|
||||
p->dstRgbl = p->ref.to(*(p->cs.l)).colors;
|
||||
|
||||
// make no change for CCM_LINEAR, make change for CCM_AFFINE.
|
||||
p->srcRgbl = p->prepare(p->srcRgbl);
|
||||
|
||||
// distance function may affect the loss function and the fitting function
|
||||
switch (p->distance)
|
||||
{
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
p->initialLeastSquare(true);
|
||||
break;
|
||||
default:
|
||||
switch (p->initialMethodType)
|
||||
{
|
||||
case cv::ccm::INITIAL_METHOD_WHITE_BALANCE:
|
||||
p->initialWhiteBalance();
|
||||
break;
|
||||
case cv::ccm::INITIAL_METHOD_LEAST_SQUARE:
|
||||
p->initialLeastSquare();
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong initial_methoddistance_type!" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
p->fitting();
|
||||
|
||||
return p->ccm;
|
||||
}
|
||||
Mat ColorCorrectionModel::getColorCorrectionMatrix() const
|
||||
{
|
||||
return p->ccm;
|
||||
}
|
||||
double ColorCorrectionModel::getLoss() const
|
||||
{
|
||||
return p->loss;
|
||||
}
|
||||
Mat ColorCorrectionModel::getSrcLinearRGB() const{
|
||||
return p->srcRgbl;
|
||||
}
|
||||
Mat ColorCorrectionModel::getRefLinearRGB() const{
|
||||
return p->dstRgbl;
|
||||
}
|
||||
Mat ColorCorrectionModel::getMask() const{
|
||||
return p->mask;
|
||||
}
|
||||
Mat ColorCorrectionModel::getWeights() const{
|
||||
return p->weights;
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::write(FileStorage& fs) const
|
||||
{
|
||||
fs << "ColorCorrectionModel" << "{"
|
||||
<< "ccm" << p->ccm
|
||||
<< "loss" << p->loss
|
||||
<< "csEnum" << p->csEnum
|
||||
<< "ccm_type" << p->ccmType
|
||||
<< "shape" << p->shape
|
||||
<< "linear" << *p->linear
|
||||
<< "distance" << p->distance
|
||||
<< "linear_type" << p->linearizationType
|
||||
<< "gamma" << p->gamma
|
||||
<< "deg" << p->deg
|
||||
<< "saturated_threshold" << p->saturatedThreshold
|
||||
<< "}";
|
||||
}
|
||||
|
||||
void ColorCorrectionModel::read(const FileNode& node)
|
||||
{
|
||||
node["ccm"] >> p->ccm;
|
||||
node["loss"] >> p->loss;
|
||||
node["ccm_type"] >> p->ccmType;
|
||||
node["shape"] >> p->shape;
|
||||
node["distance"] >> p->distance;
|
||||
node["gamma"] >> p->gamma;
|
||||
node["deg"] >> p->deg;
|
||||
node["saturated_threshold"] >> p->saturatedThreshold;
|
||||
|
||||
ColorSpace csEnum;
|
||||
node["csEnum"] >> csEnum;
|
||||
setColorSpace(csEnum);
|
||||
|
||||
node["linear_type"] >> p->linearizationType;
|
||||
switch (p->linearizationType) {
|
||||
case cv::ccm::LINEARIZATION_GAMMA:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGamma());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearColor<Polyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_IDENTITY:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearIdentity());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORLOGPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearColor<LogPolyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGray<Polyfit>());
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYLOGPOLYFIT:
|
||||
p->linear = std::shared_ptr<Linear>(new LinearGray<LogPolyfit>());
|
||||
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& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
ccm = defaultValue;
|
||||
else
|
||||
ccm.read(node);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,391 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "color.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
Color::Color()
|
||||
: colors(Mat())
|
||||
, cs(std::make_shared<ColorSpaceBase>())
|
||||
{}
|
||||
Color::Color(Mat colors_, enum ColorSpace cs_)
|
||||
: colors(colors_)
|
||||
, cs(GetCS::getInstance().getCS(cs_))
|
||||
{}
|
||||
|
||||
Color::Color(Mat colors_, enum ColorSpace cs_, Mat colored_)
|
||||
: colors(colors_)
|
||||
, cs(GetCS::getInstance().getCS(cs_))
|
||||
, colored(colored_)
|
||||
{
|
||||
grays = ~colored;
|
||||
}
|
||||
Color::Color(Mat colors_, const ColorSpaceBase& cs_, Mat colored_)
|
||||
: colors(colors_)
|
||||
, cs(std::make_shared<ColorSpaceBase>(cs_))
|
||||
, colored(colored_)
|
||||
{
|
||||
grays = ~colored;
|
||||
}
|
||||
|
||||
Color::Color(Mat colors_, const ColorSpaceBase& cs_)
|
||||
: colors(colors_)
|
||||
, cs(std::make_shared<ColorSpaceBase>(cs_))
|
||||
{}
|
||||
|
||||
Color::Color(Mat colors_, std::shared_ptr<ColorSpaceBase> cs_)
|
||||
: colors(colors_)
|
||||
, cs(cs_)
|
||||
{}
|
||||
|
||||
Color Color::to(const ColorSpaceBase& other, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
auto it = history.find(other);
|
||||
if ( it != history.end() )
|
||||
{
|
||||
return *(it->second);
|
||||
}
|
||||
if (cs->relate(other))
|
||||
{
|
||||
return Color(cs->relation(other).run(colors), other);
|
||||
}
|
||||
Operations ops;
|
||||
ops.add(cs->to).add(XYZ(cs->illumobserver).cam(other.illumobserver, method)).add(other.from);
|
||||
Mat converted = ops.run(colors);
|
||||
if (save)
|
||||
{
|
||||
auto ptr = std::make_shared<Color>(converted, other);
|
||||
history[other] = ptr;
|
||||
return *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Color(converted, other);
|
||||
}
|
||||
}
|
||||
|
||||
Color Color::to(ColorSpace other, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
return to(*GetCS::getInstance().getCS(other), method, save);
|
||||
}
|
||||
|
||||
Mat Color::channel(Mat m, int i)
|
||||
{
|
||||
Mat dchannels[3];
|
||||
split(m, dchannels);
|
||||
return dchannels[i];
|
||||
}
|
||||
|
||||
Mat Color::toGray(const IllumObserver& illumobserver, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
XYZ xyz = *XYZ::get(illumobserver);
|
||||
return channel(this->to(xyz, method, save).colors, 1);
|
||||
}
|
||||
|
||||
Mat Color::toLuminant(const IllumObserver& illumobserver, ChromaticAdaptationType method, bool save)
|
||||
{
|
||||
Lab lab = *Lab::get(illumobserver);
|
||||
return channel(this->to(lab, method, save).colors, 0);
|
||||
}
|
||||
|
||||
Mat Color::diff(Color& other, DistanceType method)
|
||||
{
|
||||
return diff(other, cs->illumobserver, method);
|
||||
}
|
||||
|
||||
Mat Color::diff(Color& other, const IllumObserver& illumobserver, DistanceType method)
|
||||
{
|
||||
Lab lab = *Lab::get(illumobserver);
|
||||
switch (method)
|
||||
{
|
||||
case cv::ccm::DISTANCE_CIE76:
|
||||
case cv::ccm::DISTANCE_CIE94_GRAPHIC_ARTS:
|
||||
case cv::ccm::DISTANCE_CIE94_TEXTILES:
|
||||
case cv::ccm::DISTANCE_CIE2000:
|
||||
case cv::ccm::DISTANCE_CMC_1TO1:
|
||||
case cv::ccm::DISTANCE_CMC_2TO1:
|
||||
return distance(to(lab).colors, other.to(lab).colors, method);
|
||||
case cv::ccm::DISTANCE_RGB:
|
||||
return distance(to(*cs->nl).colors, other.to(*cs->nl).colors, method);
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
return distance(to(*cs->l).colors, other.to(*cs->l).colors, method);
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong method!" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Color::getGray(double JDN)
|
||||
{
|
||||
if (!grays.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Mat lab = to(COLOR_SPACE_LAB_D65_2).colors;
|
||||
Mat gray(colors.size(), colors.type());
|
||||
int fromto[] = { 0, 0, -1, 1, -1, 2 };
|
||||
mixChannels(&lab, 1, &gray, 1, fromto, 3);
|
||||
Mat d = distance(lab, gray, DISTANCE_CIE2000);
|
||||
this->grays = d < JDN;
|
||||
this->colored = ~grays;
|
||||
}
|
||||
|
||||
Color Color::operator[](Mat mask)
|
||||
{
|
||||
return Color(maskCopyTo(colors, mask), cs);
|
||||
}
|
||||
|
||||
Mat GetColor::getColorChecker(const double* checker, int row)
|
||||
{
|
||||
Mat res(row, 1, CV_64FC3);
|
||||
for (int i = 0; i < row; ++i)
|
||||
{
|
||||
res.at<Vec3d>(i, 0) = Vec3d(checker[3 * i], checker[3 * i + 1], checker[3 * i + 2]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat GetColor::getColorCheckerMask(const uchar* checker, int row)
|
||||
{
|
||||
Mat res(row, 1, CV_8U);
|
||||
for (int i = 0; i < row; ++i)
|
||||
{
|
||||
res.at<uchar>(i, 0) = checker[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Color GetColor::getColor(ColorCheckerType const_color)
|
||||
{
|
||||
|
||||
/** @brief Data is from https://www.imatest.com/wp-content/uploads/2011/11/Lab-data-Iluminate-D65-D50-spectro.xls
|
||||
see Miscellaneous.md for details.
|
||||
*/
|
||||
static const double ColorChecker2005_LAB_D50_2[24][3] = { { 37.986, 13.555, 14.059 },
|
||||
{ 65.711, 18.13, 17.81 },
|
||||
{ 49.927, -4.88, -21.925 },
|
||||
{ 43.139, -13.095, 21.905 },
|
||||
{ 55.112, 8.844, -25.399 },
|
||||
{ 70.719, -33.397, -0.199 },
|
||||
{ 62.661, 36.067, 57.096 },
|
||||
{ 40.02, 10.41, -45.964 },
|
||||
{ 51.124, 48.239, 16.248 },
|
||||
{ 30.325, 22.976, -21.587 },
|
||||
{ 72.532, -23.709, 57.255 },
|
||||
{ 71.941, 19.363, 67.857 },
|
||||
{ 28.778, 14.179, -50.297 },
|
||||
{ 55.261, -38.342, 31.37 },
|
||||
{ 42.101, 53.378, 28.19 },
|
||||
{ 81.733, 4.039, 79.819 },
|
||||
{ 51.935, 49.986, -14.574 },
|
||||
{ 51.038, -28.631, -28.638 },
|
||||
{ 96.539, -0.425, 1.186 },
|
||||
{ 81.257, -0.638, -0.335 },
|
||||
{ 66.766, -0.734, -0.504 },
|
||||
{ 50.867, -0.153, -0.27 },
|
||||
{ 35.656, -0.421, -1.231 },
|
||||
{ 20.461, -0.079, -0.973 } };
|
||||
|
||||
static const uchar ColorChecker2005_COLORED_MASK[24] = { 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0 };
|
||||
static const double Vinyl_LAB_D50_2[18][3] = { { 100, 0.00520000001, -0.0104 },
|
||||
{ 73.0833969, -0.819999993, -2.02099991 },
|
||||
{ 62.493, 0.425999999, -2.23099995 },
|
||||
{ 50.4640007, 0.446999997, -2.32399988 },
|
||||
{ 37.7970009, 0.0359999985, -1.29700005 },
|
||||
{ 0, 0, 0 },
|
||||
{ 51.5880013, 73.5179977, 51.5690002 },
|
||||
{ 93.6989975, -15.7340002, 91.9420013 },
|
||||
{ 69.4079971, -46.5940018, 50.4869995 },
|
||||
{ 66.61000060000001, -13.6789999, -43.1720009 },
|
||||
{ 11.7110004, 16.9799995, -37.1759987 },
|
||||
{ 51.973999, 81.9440002, -8.40699959 },
|
||||
{ 40.5489998, 50.4399986, 24.8490009 },
|
||||
{ 60.8160019, 26.0690002, 49.4420013 },
|
||||
{ 52.2529984, -19.9500008, -23.9960003 },
|
||||
{ 51.2859993, 48.4700012, -15.0579996 },
|
||||
{ 68.70700069999999, 12.2959995, 16.2129993 },
|
||||
{ 63.6839981, 10.2930002, 16.7639999 } };
|
||||
static const uchar Vinyl_COLORED_MASK[18] = { 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1 };
|
||||
static const double DigitalSG_LAB_D50_2[140][3] = { { 96.55, -0.91, 0.57 },
|
||||
{ 6.43, -0.06, -0.41 },
|
||||
{ 49.7, -0.18, 0.03 },
|
||||
{ 96.5, -0.89, 0.59 },
|
||||
{ 6.5, -0.06, -0.44 },
|
||||
{ 49.66, -0.2, 0.01 },
|
||||
{ 96.52, -0.91, 0.58 },
|
||||
{ 6.49, -0.02, -0.28 },
|
||||
{ 49.72, -0.2, 0.04 },
|
||||
{ 96.43, -0.91, 0.67 },
|
||||
{ 49.72, -0.19, 0 },
|
||||
{ 32.6, 51.58, -10.85 },
|
||||
{ 60.75, 26.22, -18.6 },
|
||||
{ 28.69, 48.28, -39 },
|
||||
{ 49.38, -15.43, -48.48 },
|
||||
{ 60.63, -30.77, -26.23 },
|
||||
{ 19.29, -26.37, -6.15 },
|
||||
{ 60.15, -41.77, -12.6 },
|
||||
{ 21.42, 1.67, 8.79 },
|
||||
{ 49.69, -0.2, 0.01 },
|
||||
{ 6.5, -0.03, -0.67 },
|
||||
{ 21.82, 17.33, -18.35 },
|
||||
{ 41.53, 18.48, -37.26 },
|
||||
{ 19.99, -0.16, -36.29 },
|
||||
{ 60.16, -18.45, -31.42 },
|
||||
{ 19.94, -17.92, -20.96 },
|
||||
{ 60.68, -6.05, -32.81 },
|
||||
{ 50.81, -49.8, -9.63 },
|
||||
{ 60.65, -39.77, 20.76 },
|
||||
{ 6.53, -0.03, -0.43 },
|
||||
{ 96.56, -0.91, 0.59 },
|
||||
{ 84.19, -1.95, -8.23 },
|
||||
{ 84.75, 14.55, 0.23 },
|
||||
{ 84.87, -19.07, -0.82 },
|
||||
{ 85.15, 13.48, 6.82 },
|
||||
{ 84.17, -10.45, 26.78 },
|
||||
{ 61.74, 31.06, 36.42 },
|
||||
{ 64.37, 20.82, 18.92 },
|
||||
{ 50.4, -53.22, 14.62 },
|
||||
{ 96.51, -0.89, 0.65 },
|
||||
{ 49.74, -0.19, 0.03 },
|
||||
{ 31.91, 18.62, 21.99 },
|
||||
{ 60.74, 38.66, 70.97 },
|
||||
{ 19.35, 22.23, -58.86 },
|
||||
{ 96.52, -0.91, 0.62 },
|
||||
{ 6.66, 0, -0.3 },
|
||||
{ 76.51, 20.81, 22.72 },
|
||||
{ 72.79, 29.15, 24.18 },
|
||||
{ 22.33, -20.7, 5.75 },
|
||||
{ 49.7, -0.19, 0.01 },
|
||||
{ 6.53, -0.05, -0.61 },
|
||||
{ 63.42, 20.19, 19.22 },
|
||||
{ 34.94, 11.64, -50.7 },
|
||||
{ 52.03, -44.15, 39.04 },
|
||||
{ 79.43, 0.29, -0.17 },
|
||||
{ 30.67, -0.14, -0.53 },
|
||||
{ 63.6, 14.44, 26.07 },
|
||||
{ 64.37, 14.5, 17.05 },
|
||||
{ 60.01, -44.33, 8.49 },
|
||||
{ 6.63, -0.01, -0.47 },
|
||||
{ 96.56, -0.93, 0.59 },
|
||||
{ 46.37, -5.09, -24.46 },
|
||||
{ 47.08, 52.97, 20.49 },
|
||||
{ 36.04, 64.92, 38.51 },
|
||||
{ 65.05, 0, -0.32 },
|
||||
{ 40.14, -0.19, -0.38 },
|
||||
{ 43.77, 16.46, 27.12 },
|
||||
{ 64.39, 17, 16.59 },
|
||||
{ 60.79, -29.74, 41.5 },
|
||||
{ 96.48, -0.89, 0.64 },
|
||||
{ 49.75, -0.21, 0.01 },
|
||||
{ 38.18, -16.99, 30.87 },
|
||||
{ 21.31, 29.14, -27.51 },
|
||||
{ 80.57, 3.85, 89.61 },
|
||||
{ 49.71, -0.2, 0.03 },
|
||||
{ 60.27, 0.08, -0.41 },
|
||||
{ 67.34, 14.45, 16.9 },
|
||||
{ 64.69, 16.95, 18.57 },
|
||||
{ 51.12, -49.31, 44.41 },
|
||||
{ 49.7, -0.2, 0.02 },
|
||||
{ 6.67, -0.05, -0.64 },
|
||||
{ 51.56, 9.16, -26.88 },
|
||||
{ 70.83, -24.26, 64.77 },
|
||||
{ 48.06, 55.33, -15.61 },
|
||||
{ 35.26, -0.09, -0.24 },
|
||||
{ 75.16, 0.25, -0.2 },
|
||||
{ 44.54, 26.27, 38.93 },
|
||||
{ 35.91, 16.59, 26.46 },
|
||||
{ 61.49, -52.73, 47.3 },
|
||||
{ 6.59, -0.05, -0.5 },
|
||||
{ 96.58, -0.9, 0.61 },
|
||||
{ 68.93, -34.58, -0.34 },
|
||||
{ 69.65, 20.09, 78.57 },
|
||||
{ 47.79, -33.18, -30.21 },
|
||||
{ 15.94, -0.42, -1.2 },
|
||||
{ 89.02, -0.36, -0.48 },
|
||||
{ 63.43, 25.44, 26.25 },
|
||||
{ 65.75, 22.06, 27.82 },
|
||||
{ 61.47, 17.1, 50.72 },
|
||||
{ 96.53, -0.89, 0.66 },
|
||||
{ 49.79, -0.2, 0.03 },
|
||||
{ 85.17, 10.89, 17.26 },
|
||||
{ 89.74, -16.52, 6.19 },
|
||||
{ 84.55, 5.07, -6.12 },
|
||||
{ 84.02, -13.87, -8.72 },
|
||||
{ 70.76, 0.07, -0.35 },
|
||||
{ 45.59, -0.05, 0.23 },
|
||||
{ 20.3, 0.07, -0.32 },
|
||||
{ 61.79, -13.41, 55.42 },
|
||||
{ 49.72, -0.19, 0.02 },
|
||||
{ 6.77, -0.05, -0.44 },
|
||||
{ 21.85, 34.37, 7.83 },
|
||||
{ 42.66, 67.43, 48.42 },
|
||||
{ 60.33, 36.56, 3.56 },
|
||||
{ 61.22, 36.61, 17.32 },
|
||||
{ 62.07, 52.8, 77.14 },
|
||||
{ 72.42, -9.82, 89.66 },
|
||||
{ 62.03, 3.53, 57.01 },
|
||||
{ 71.95, -27.34, 73.69 },
|
||||
{ 6.59, -0.04, -0.45 },
|
||||
{ 49.77, -0.19, 0.04 },
|
||||
{ 41.84, 62.05, 10.01 },
|
||||
{ 19.78, 29.16, -7.85 },
|
||||
{ 39.56, 65.98, 33.71 },
|
||||
{ 52.39, 68.33, 47.84 },
|
||||
{ 81.23, 24.12, 87.51 },
|
||||
{ 81.8, 6.78, 95.75 },
|
||||
{ 71.72, -16.23, 76.28 },
|
||||
{ 20.31, 14.45, 16.74 },
|
||||
{ 49.68, -0.19, 0.05 },
|
||||
{ 96.48, -0.88, 0.68 },
|
||||
{ 49.69, -0.18, 0.03 },
|
||||
{ 6.39, -0.04, -0.33 },
|
||||
{ 96.54, -0.9, 0.67 },
|
||||
{ 49.72, -0.18, 0.05 },
|
||||
{ 6.49, -0.03, -0.41 },
|
||||
{ 96.51, -0.9, 0.69 },
|
||||
{ 49.7, -0.19, 0.07 },
|
||||
{ 6.47, 0, -0.38 },
|
||||
{ 96.46, -0.89, 0.7 } };
|
||||
|
||||
switch (const_color)
|
||||
{
|
||||
|
||||
case cv::ccm::COLORCHECKER_MACBETH:
|
||||
{
|
||||
Mat ColorChecker2005_LAB_D50_2_ = GetColor::getColorChecker(*ColorChecker2005_LAB_D50_2, 24);
|
||||
Mat ColorChecker2005_COLORED_MASK_ = GetColor::getColorCheckerMask(ColorChecker2005_COLORED_MASK, 24);
|
||||
Color Macbeth_D50_2 = Color(ColorChecker2005_LAB_D50_2_, COLOR_SPACE_LAB_D50_2, ColorChecker2005_COLORED_MASK_);
|
||||
return Macbeth_D50_2;
|
||||
}
|
||||
|
||||
case cv::ccm::COLORCHECKER_VINYL:
|
||||
{
|
||||
Mat Vinyl_LAB_D50_2__ = GetColor::getColorChecker(*Vinyl_LAB_D50_2, 18);
|
||||
Mat Vinyl_COLORED_MASK__ = GetColor::getColorCheckerMask(Vinyl_COLORED_MASK, 18);
|
||||
Color Vinyl_D50_2 = Color(Vinyl_LAB_D50_2__, COLOR_SPACE_LAB_D50_2, Vinyl_COLORED_MASK__);
|
||||
return Vinyl_D50_2;
|
||||
}
|
||||
|
||||
case cv::ccm::COLORCHECKER_DIGITAL_SG:
|
||||
{
|
||||
Mat DigitalSG_LAB_D50_2__ = GetColor::getColorChecker(*DigitalSG_LAB_D50_2, 140);
|
||||
Color DigitalSG_D50_2 = Color(DigitalSG_LAB_D50_2__, COLOR_SPACE_LAB_D50_2);
|
||||
return DigitalSG_D50_2;
|
||||
}
|
||||
}
|
||||
CV_Error(Error::StsNotImplemented, "");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_COLOR_HPP__
|
||||
#define __OPENCV_CCM_COLOR_HPP__
|
||||
|
||||
#include "distance.hpp"
|
||||
#include "colorspace.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Color defined by color_values and color space
|
||||
*/
|
||||
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
/** @param grays mask of grayscale color
|
||||
@param colored mask of colored color
|
||||
@param history storage of historical conversion
|
||||
*/
|
||||
Mat colors;
|
||||
std::shared_ptr<ColorSpaceBase> cs;
|
||||
Mat grays;
|
||||
Mat colored;
|
||||
std::map<ColorSpaceBase, std::shared_ptr<Color>> history;
|
||||
|
||||
Color();
|
||||
Color(Mat colors_, enum ColorSpace cs_);
|
||||
Color(Mat colors_, enum ColorSpace cs_, Mat colored);
|
||||
Color(Mat colors_, const ColorSpaceBase& cs, Mat colored);
|
||||
Color(Mat colors_, const ColorSpaceBase& cs);
|
||||
Color(Mat colors_, std::shared_ptr<ColorSpaceBase> cs_);
|
||||
virtual ~Color() {};
|
||||
|
||||
/** @brief Change to other color space.
|
||||
The conversion process incorporates linear transformations to speed up.
|
||||
@param other type of ColorSpaceBase.
|
||||
@param method the chromatic adapation method.
|
||||
@param save when save if True, get data from history first.
|
||||
@return Color.
|
||||
*/
|
||||
Color to(const ColorSpaceBase& other, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Convert color to another color space using ColorSpace enum.
|
||||
@param other type of ColorSpace.
|
||||
@param method the method of chromatic adaptation.
|
||||
@param save whether to save the conversion history.
|
||||
@return the output array, type of Color.
|
||||
*/
|
||||
Color to(ColorSpace other, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Channels split.
|
||||
@return each channel.
|
||||
*/
|
||||
Mat channel(Mat m, int i);
|
||||
|
||||
/** @brief To Gray.
|
||||
*/
|
||||
Mat toGray(const IllumObserver& illumobserver, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief To Luminant.
|
||||
*/
|
||||
Mat toLuminant(const IllumObserver& illumobserver, ChromaticAdaptationType method = BRADFORD, bool save = true);
|
||||
|
||||
/** @brief Diff without IllumObserver.
|
||||
@param other type of Color.
|
||||
@param method type of distance.
|
||||
@return distance between self and other
|
||||
*/
|
||||
Mat diff(Color& other, DistanceType method = DISTANCE_CIE2000);
|
||||
|
||||
/** @brief Diff with IllumObserver.
|
||||
@param other type of Color.
|
||||
@param illumobserver type of IllumObserver.
|
||||
@param method type of distance.
|
||||
@return distance between self and other
|
||||
*/
|
||||
Mat diff(Color& other, const IllumObserver& illumobserver, DistanceType method = DISTANCE_CIE2000);
|
||||
|
||||
/** @brief Calculate gray mask.
|
||||
*/
|
||||
void getGray(double JDN = 2.0);
|
||||
|
||||
/** @brief Operator for mask copy.
|
||||
*/
|
||||
Color operator[](Mat mask);
|
||||
};
|
||||
|
||||
class GetColor
|
||||
{
|
||||
public:
|
||||
Color getColor(ColorCheckerType const_color);
|
||||
static Mat getColorChecker(const double* checker, int row);
|
||||
static Mat getColorCheckerMask(const uchar* checker, int row);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,769 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "colorspace.hpp"
|
||||
#include "operations.hpp"
|
||||
#include "illumobserver.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
static const std::vector<double>& getIlluminants(const IllumObserver& illumobserver)
|
||||
{
|
||||
static const std::map<IllumObserver, std::vector<double>> illuminants = {
|
||||
{ IllumObserver::getIllumObservers(A_2), { 1.098466069456375, 1, 0.3558228003436005 } },
|
||||
{ IllumObserver::getIllumObservers(A_10), { 1.111420406956693, 1, 0.3519978321919493 } },
|
||||
{ IllumObserver::getIllumObservers(D50_2), { 0.9642119944211994, 1, 0.8251882845188288 } },
|
||||
{ IllumObserver::getIllumObservers(D50_10), { 0.9672062750333777, 1, 0.8142801513128616 } },
|
||||
{ IllumObserver::getIllumObservers(D55_2), { 0.956797052643698, 1, 0.9214805860173273 } },
|
||||
{ IllumObserver::getIllumObservers(D55_10), { 0.9579665682254781, 1, 0.9092525159847462 } },
|
||||
{ IllumObserver::getIllumObservers(D65_2), { 0.95047, 1., 1.08883 } },
|
||||
{ IllumObserver::getIllumObservers(D65_10), { 0.94811, 1., 1.07304 } },
|
||||
{ IllumObserver::getIllumObservers(D75_2), { 0.9497220898840717, 1, 1.226393520724154 } },
|
||||
{ IllumObserver::getIllumObservers(D75_10), { 0.9441713925645873, 1, 1.2064272211720228 } },
|
||||
{ IllumObserver::getIllumObservers(E_2), { 1., 1., 1. } },
|
||||
{ IllumObserver::getIllumObservers(E_10), { 1., 1., 1. } },
|
||||
};
|
||||
auto it = illuminants.find(illumobserver);
|
||||
CV_Assert(it != illuminants.end());
|
||||
return it->second;
|
||||
};
|
||||
|
||||
/* @brief Basic class for ColorSpaceBase.
|
||||
*/
|
||||
bool ColorSpaceBase::relate(const ColorSpaceBase& other) const
|
||||
{
|
||||
return (type == other.type) && (illumobserver == other.illumobserver);
|
||||
};
|
||||
|
||||
Operations ColorSpaceBase::relation(const ColorSpaceBase& /*other*/) const
|
||||
{
|
||||
return Operations::getIdentityOps();
|
||||
}
|
||||
|
||||
bool ColorSpaceBase::operator<(const ColorSpaceBase& other) const
|
||||
{
|
||||
return (illumobserver < other.illumobserver || (illumobserver == other.illumobserver && type < other.type) || (illumobserver == other.illumobserver && type == other.type && linear < other.linear));
|
||||
}
|
||||
|
||||
/* @brief Base of RGB color space;
|
||||
* the argument values are from AdobeRGB;
|
||||
* Data from https://en.wikipedia.org/wiki/Adobe_RGB_color_space
|
||||
*/
|
||||
Operations RGBBase_::relation(const ColorSpaceBase& other) const
|
||||
{
|
||||
if (linear == other.linear)
|
||||
{
|
||||
return Operations::getIdentityOps();
|
||||
}
|
||||
if (linear)
|
||||
{
|
||||
return Operations({ Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
|
||||
}
|
||||
return Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); })});
|
||||
}
|
||||
|
||||
/* @brief Initial operations.
|
||||
*/
|
||||
void RGBBase_::init()
|
||||
{
|
||||
setParameter();
|
||||
calLinear();
|
||||
calM();
|
||||
calOperations();
|
||||
}
|
||||
|
||||
/* @brief Produce color space instance with linear and non-linear versions.
|
||||
* @param rgbl type of RGBBase_.
|
||||
*/
|
||||
void RGBBase_::bind(RGBBase_& rgbl)
|
||||
{
|
||||
init();
|
||||
rgbl.init();
|
||||
l = &rgbl;
|
||||
rgbl.l = &rgbl;
|
||||
nl = this;
|
||||
rgbl.nl = this;
|
||||
}
|
||||
|
||||
/* @brief Calculation of M_RGBL2XYZ_base.
|
||||
*/
|
||||
void RGBBase_::calM()
|
||||
{
|
||||
Mat XYZr, XYZg, XYZb, XYZ_rgbl, Srgb;
|
||||
XYZr = Mat(xyY2XYZ({ xr, yr }), true);
|
||||
XYZg = Mat(xyY2XYZ({ xg, yg }), true);
|
||||
XYZb = Mat(xyY2XYZ({ xb, yb }), true);
|
||||
merge(std::vector<Mat> { XYZr, XYZg, XYZb }, XYZ_rgbl);
|
||||
XYZ_rgbl = XYZ_rgbl.reshape(1, (int)XYZ_rgbl.total());
|
||||
Mat XYZw = Mat(getIlluminants(illumobserver), true);
|
||||
XYZw = XYZw.reshape(1, (int)XYZw.total());
|
||||
solve(XYZ_rgbl, XYZw, Srgb);
|
||||
merge(std::vector<Mat> { Srgb.at<double>(0) * XYZr, Srgb.at<double>(1) * XYZg,
|
||||
Srgb.at<double>(2) * XYZb },
|
||||
M_to);
|
||||
M_to = M_to.reshape(1, (int)M_to.total());
|
||||
M_from = M_to.inv();
|
||||
};
|
||||
|
||||
/* @brief operations to or from XYZ.
|
||||
*/
|
||||
void RGBBase_::calOperations()
|
||||
{
|
||||
if (linear)
|
||||
{
|
||||
to = Operations({ Operation(M_to.t()) });
|
||||
from = Operations({ Operation(M_from.t()) });
|
||||
}
|
||||
else
|
||||
{
|
||||
// rgb -> rgbl
|
||||
to = Operations({ Operation([this](Mat rgb) -> Mat { return toLFunc(rgb); }), Operation(M_to.t()) });
|
||||
// rgbl -> rgb
|
||||
from = Operations({ Operation(M_from.t()), Operation([this](Mat rgbl) -> Mat { return fromLFunc(rgbl); }) });
|
||||
}
|
||||
}
|
||||
|
||||
Mat RGBBase_::toLFunc(Mat& /*rgb*/) const { return Mat(); }
|
||||
|
||||
Mat RGBBase_::fromLFunc(Mat& /*rgbl*/, Mat dst) const { return dst; }
|
||||
|
||||
/* @brief Base of Adobe RGB color space;
|
||||
*/
|
||||
|
||||
Mat AdobeRGBBase_::toLFunc(Mat& rgb) const
|
||||
{
|
||||
Mat out;
|
||||
gammaCorrection(rgb, out, gamma);
|
||||
return out;
|
||||
}
|
||||
|
||||
Mat AdobeRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
|
||||
{
|
||||
gammaCorrection(rgbl, dst, 1. / gamma);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* @brief Base of sRGB color space;
|
||||
*/
|
||||
|
||||
void sRGBBase_::calLinear()
|
||||
{
|
||||
alpha = a + 1;
|
||||
K0 = a / (gamma - 1);
|
||||
phi = (pow(alpha, gamma) * pow(gamma - 1, gamma - 1)) / (pow(a, gamma - 1) * pow(gamma, gamma));
|
||||
beta = K0 / phi;
|
||||
}
|
||||
|
||||
/* @brief Used by toLFunc.
|
||||
*/
|
||||
double sRGBBase_::toLFuncEW(double x) const
|
||||
{
|
||||
if (x > K0)
|
||||
{
|
||||
return pow(((x + alpha - 1) / alpha), gamma);
|
||||
}
|
||||
else if (x >= -K0)
|
||||
{
|
||||
return x / phi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -(pow(((-x + alpha - 1) / alpha), gamma));
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief Linearization.
|
||||
* @param rgb the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat sRGBBase_::toLFunc(Mat& rgb) const
|
||||
{
|
||||
return elementWise(rgb,
|
||||
[this](double a_) -> double { return toLFuncEW(a_); });
|
||||
}
|
||||
|
||||
/* @brief Used by fromLFunc.
|
||||
*/
|
||||
double sRGBBase_::fromLFuncEW(double x) const
|
||||
{
|
||||
if (x > beta)
|
||||
{
|
||||
return alpha * pow(x, 1 / gamma) - (alpha - 1);
|
||||
}
|
||||
else if (x >= -beta)
|
||||
{
|
||||
return x * phi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -(alpha * pow(-x, 1 / gamma) - (alpha - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* @brief Delinearization.
|
||||
* @param rgbl the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat sRGBBase_::fromLFunc(Mat& rgbl, Mat dst) const
|
||||
{
|
||||
return elementWise(rgbl, [this](double a_) -> double { return fromLFuncEW(a_); }, dst);
|
||||
}
|
||||
|
||||
/* @brief sRGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/SRGB.
|
||||
*/
|
||||
void sRGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.3;
|
||||
yg = 0.6;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
a = 0.055;
|
||||
gamma = 2.4;
|
||||
}
|
||||
|
||||
/* @brief Adobe RGB color space.
|
||||
*/
|
||||
void AdobeRGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.21;
|
||||
yg = 0.71;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief Wide-gamut RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space.
|
||||
*/
|
||||
void WideGamutRGB_::setParameter()
|
||||
{
|
||||
xr = 0.7347;
|
||||
yr = 0.2653;
|
||||
xg = 0.1152;
|
||||
yg = 0.8264;
|
||||
xb = 0.1566;
|
||||
yb = 0.0177;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief ProPhoto RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space.
|
||||
*/
|
||||
void ProPhotoRGB_::setParameter()
|
||||
{
|
||||
xr = 0.734699;
|
||||
yr = 0.265301;
|
||||
xg = 0.159597;
|
||||
yg = 0.840403;
|
||||
xb = 0.036598;
|
||||
yb = 0.000105;
|
||||
gamma = 1.8;
|
||||
}
|
||||
|
||||
/* @brief DCI-P3 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/DCI-P3.
|
||||
*/
|
||||
|
||||
void DCI_P3_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.68;
|
||||
yr = 0.32;
|
||||
xg = 0.265;
|
||||
yg = 0.69;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
gamma = 2.2;
|
||||
}
|
||||
|
||||
/* @brief Apple RGB color space.
|
||||
* data from
|
||||
* http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html.
|
||||
*/
|
||||
void AppleRGB_::setParameter()
|
||||
{
|
||||
xr = 0.625;
|
||||
yr = 0.34;
|
||||
xg = 0.28;
|
||||
yg = 0.595;
|
||||
xb = 0.155;
|
||||
yb = 0.07;
|
||||
gamma = 1.8;
|
||||
}
|
||||
|
||||
/* @brief REC_709 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Rec._709.
|
||||
*/
|
||||
void REC_709_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.64;
|
||||
yr = 0.33;
|
||||
xg = 0.3;
|
||||
yg = 0.6;
|
||||
xb = 0.15;
|
||||
yb = 0.06;
|
||||
a = 0.099;
|
||||
gamma = 1 / 0.45;
|
||||
}
|
||||
|
||||
/* @brief REC_2020 RGB color space.
|
||||
* data from https://en.wikipedia.org/wiki/Rec._2020.
|
||||
*/
|
||||
|
||||
void REC_2020_RGB_::setParameter()
|
||||
{
|
||||
xr = 0.708;
|
||||
yr = 0.292;
|
||||
xg = 0.17;
|
||||
yg = 0.797;
|
||||
xb = 0.131;
|
||||
yb = 0.046;
|
||||
a = 0.09929682680944;
|
||||
gamma = 1 / 0.45;
|
||||
}
|
||||
|
||||
Operations XYZ::cam(IllumObserver dio, ChromaticAdaptationType method)
|
||||
{
|
||||
return (illumobserver == dio) ? Operations()
|
||||
: Operations({ Operation(cam_(illumobserver, dio, method).t()) });
|
||||
}
|
||||
Mat XYZ::cam_(IllumObserver sio, IllumObserver dio, ChromaticAdaptationType method) const
|
||||
{
|
||||
static std::map<std::tuple<IllumObserver, IllumObserver, ChromaticAdaptationType>, Mat> cams;
|
||||
|
||||
if (sio == dio)
|
||||
{
|
||||
return Mat::eye(cv::Size(3, 3), CV_64FC1);
|
||||
}
|
||||
if (cams.count(std::make_tuple(dio, sio, method)) == 1)
|
||||
{
|
||||
return cams[std::make_tuple(dio, sio, method)];
|
||||
}
|
||||
/* @brief XYZ color space.
|
||||
* Chromatic adaption matrices.
|
||||
*/
|
||||
|
||||
static const Mat Von_Kries = (Mat_<double>(3, 3) << 0.40024, 0.7076, -0.08081, -0.2263, 1.16532, 0.0457, 0., 0., 0.91822);
|
||||
static const Mat Bradford = (Mat_<double>(3, 3) << 0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296);
|
||||
static const std::map<ChromaticAdaptationType, std::vector<Mat>> MAs = {
|
||||
{ IDENTITY, { Mat::eye(Size(3, 3), CV_64FC1), Mat::eye(Size(3, 3), CV_64FC1) } },
|
||||
{ VON_KRIES, { Von_Kries, Von_Kries.inv() } },
|
||||
{ BRADFORD, { Bradford, Bradford.inv() } }
|
||||
};
|
||||
|
||||
// Function from http://www.brucelindbloom.com/index.html?ColorCheckerRGB.html.
|
||||
Mat XYZws = Mat(getIlluminants(dio));
|
||||
Mat XYZWd = Mat(getIlluminants(sio));
|
||||
XYZws = XYZws.reshape(1, (int)XYZws.total());
|
||||
XYZWd = XYZWd.reshape(1, (int)XYZWd.total());
|
||||
Mat MA = MAs.at(method)[0];
|
||||
Mat MA_inv = MAs.at(method)[1];
|
||||
Mat M = MA_inv * Mat::diag((MA * XYZws) / (MA * XYZWd)) * MA;
|
||||
cams[std::make_tuple(dio, sio, method)] = M;
|
||||
cams[std::make_tuple(sio, dio, method)] = M.inv();
|
||||
return M;
|
||||
}
|
||||
|
||||
std::shared_ptr<XYZ> XYZ::get(IllumObserver illumobserver)
|
||||
{
|
||||
static std::map<IllumObserver, std::shared_ptr<XYZ>> xyz_cs;
|
||||
|
||||
if (xyz_cs.count(illumobserver) == 1)
|
||||
{
|
||||
return xyz_cs[illumobserver];
|
||||
}
|
||||
std::shared_ptr<XYZ> XYZ_CS = std::make_shared<XYZ>(illumobserver);
|
||||
xyz_cs[illumobserver] = XYZ_CS;
|
||||
return xyz_cs[illumobserver];
|
||||
}
|
||||
|
||||
/* @brief Lab color space.
|
||||
*/
|
||||
Lab::Lab(IllumObserver illumobserver_)
|
||||
: ColorSpaceBase(illumobserver_, "Lab", true)
|
||||
{
|
||||
to = { Operation([this](Mat src) -> Mat { return tosrc(src); }) };
|
||||
from = { Operation([this](Mat src) -> Mat { return fromsrc(src); }) };
|
||||
}
|
||||
|
||||
Vec3d Lab::fromxyz(const Vec3d& xyz)
|
||||
{
|
||||
auto& il = getIlluminants(illumobserver);
|
||||
double x = xyz[0] / il[0],
|
||||
y = xyz[1] / il[1],
|
||||
z = xyz[2] / il[2];
|
||||
auto f = [](double t) -> double {
|
||||
return t > T0 ? std::cbrt(t) : (M * t + C);
|
||||
};
|
||||
double fx = f(x), fy = f(y), fz = f(z);
|
||||
return { 116. * fy - 16., 500 * (fx - fy), 200 * (fy - fz) };
|
||||
}
|
||||
|
||||
/* @brief Calculate From.
|
||||
* @param src the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat Lab::fromsrc(Mat& src)
|
||||
{
|
||||
return channelWise(src,
|
||||
[this](cv::Vec3d a) -> cv::Vec3d { return fromxyz(a); });
|
||||
}
|
||||
|
||||
Vec3d Lab::tolab(const Vec3d& lab)
|
||||
{
|
||||
auto f_inv = [](double t) -> double {
|
||||
return t > DELTA ? pow(t, 3.0) : (t - C) / M;
|
||||
};
|
||||
double L = (lab[0] + 16.) / 116., a = lab[1] / 500., b = lab[2] / 200.;
|
||||
auto& il = getIlluminants(illumobserver);
|
||||
return { il[0] * f_inv(L + a),
|
||||
il[1] * f_inv(L),
|
||||
il[2] * f_inv(L - b) };
|
||||
}
|
||||
|
||||
/* @brief Calculate To.
|
||||
* @param src the input array, type of cv::Mat.
|
||||
* @return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat Lab::tosrc(Mat& src)
|
||||
{
|
||||
return channelWise(src,
|
||||
[this](cv::Vec3d a) -> cv::Vec3d { return tolab(a); });
|
||||
}
|
||||
|
||||
std::shared_ptr<Lab> Lab::get(IllumObserver illumobserver)
|
||||
{
|
||||
static std::map<IllumObserver, std::shared_ptr<Lab>> lab_cs;
|
||||
|
||||
if (lab_cs.count(illumobserver) == 1)
|
||||
{
|
||||
return lab_cs[illumobserver];
|
||||
}
|
||||
std::shared_ptr<Lab> Lab_CS(new Lab(illumobserver));
|
||||
lab_cs[illumobserver] = Lab_CS;
|
||||
return lab_cs[illumobserver];
|
||||
}
|
||||
|
||||
GetCS::GetCS()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
GetCS& GetCS::getInstance()
|
||||
{
|
||||
static GetCS instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::shared_ptr<RGBBase_> GetCS::getRgb(enum ColorSpace cs_name)
|
||||
{
|
||||
switch (cs_name)
|
||||
{
|
||||
case cv::ccm::COLOR_SPACE_SRGB:
|
||||
if (map_cs.find(COLOR_SPACE_SRGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<sRGB_> sRGB_CS(new sRGB_(false));
|
||||
std::shared_ptr<sRGB_> sRGBL_CS(new sRGB_(true));
|
||||
(*sRGB_CS).bind(*sRGBL_CS);
|
||||
map_cs[COLOR_SPACE_SRGB] = sRGB_CS;
|
||||
map_cs[COLOR_SPACE_SRGBL] = sRGBL_CS;
|
||||
}
|
||||
return std::dynamic_pointer_cast<RGBBase_>(map_cs[COLOR_SPACE_SRGB]);
|
||||
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGB:
|
||||
if (map_cs.find(COLOR_SPACE_ADOBE_RGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGB_CS(new AdobeRGB_(false));
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGBL_CS(new AdobeRGB_(true));
|
||||
(*AdobeRGB_CS).bind(*AdobeRGBL_CS);
|
||||
map_cs[COLOR_SPACE_ADOBE_RGB] = AdobeRGB_CS;
|
||||
map_cs[COLOR_SPACE_ADOBE_RGBL] = AdobeRGBL_CS;
|
||||
}
|
||||
return std::dynamic_pointer_cast<RGBBase_>(map_cs[COLOR_SPACE_ADOBE_RGB]);
|
||||
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGB_CS(new WideGamutRGB_(false));
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGBL_CS(new WideGamutRGB_(true));
|
||||
(*WideGamutRGB_CS).bind(*WideGamutRGBL_CS);
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGB] = WideGamutRGB_CS;
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGBL] = WideGamutRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGB_CS(new ProPhotoRGB_(false));
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGBL_CS(new ProPhotoRGB_(true));
|
||||
(*ProPhotoRGB_CS).bind(*ProPhotoRGBL_CS);
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGB] = ProPhotoRGB_CS;
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGBL] = ProPhotoRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGB_CS(new DCI_P3_RGB_(false));
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGBL_CS(new DCI_P3_RGB_(true));
|
||||
(*DCI_P3_RGB_CS).bind(*DCI_P3_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGB] = DCI_P3_RGB_CS;
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGBL] = DCI_P3_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AppleRGB_> AppleRGB_CS(new AppleRGB_(false));
|
||||
std::shared_ptr<AppleRGB_> AppleRGBL_CS(new AppleRGB_(true));
|
||||
(*AppleRGB_CS).bind(*AppleRGBL_CS);
|
||||
map_cs[COLOR_SPACE_APPLE_RGB] = AppleRGB_CS;
|
||||
map_cs[COLOR_SPACE_APPLE_RGBL] = AppleRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGB_CS(new REC_709_RGB_(false));
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGBL_CS(new REC_709_RGB_(true));
|
||||
(*REC_709_RGB_CS).bind(*REC_709_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_709_RGB] = REC_709_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_709_RGBL] = REC_709_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGB:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGB_CS(new REC_2020_RGB_(false));
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGBL_CS(new REC_2020_RGB_(true));
|
||||
(*REC_2020_RGB_CS).bind(*REC_2020_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_2020_RGB] = REC_2020_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_2020_RGBL] = REC_2020_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_SRGBL:
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGBL:
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGBL:
|
||||
CV_Error(Error::StsBadArg, "linear RGB colorspaces are not supported, you should assigned as normal RGB color space");
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Only RGB color spaces are supported");
|
||||
}
|
||||
return (std::dynamic_pointer_cast<RGBBase_>)(map_cs[cs_name]);
|
||||
}
|
||||
|
||||
std::shared_ptr<ColorSpaceBase> GetCS::getCS(enum ColorSpace cs_name)
|
||||
{
|
||||
switch (cs_name)
|
||||
{
|
||||
case cv::ccm::COLOR_SPACE_SRGB:
|
||||
case cv::ccm::COLOR_SPACE_SRGBL:
|
||||
if (map_cs.find(COLOR_SPACE_SRGB) == map_cs.end())
|
||||
{
|
||||
std::shared_ptr<sRGB_> sRGB_CS(new sRGB_(false));
|
||||
std::shared_ptr<sRGB_> sRGBL_CS(new sRGB_(true));
|
||||
(*sRGB_CS).bind(*sRGBL_CS);
|
||||
map_cs[COLOR_SPACE_SRGB] = sRGB_CS;
|
||||
map_cs[COLOR_SPACE_SRGBL] = sRGBL_CS;
|
||||
}
|
||||
return map_cs[cs_name];
|
||||
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGB:
|
||||
case cv::ccm::COLOR_SPACE_ADOBE_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGB_CS(new AdobeRGB_(false));
|
||||
std::shared_ptr<AdobeRGB_> AdobeRGBL_CS(new AdobeRGB_(true));
|
||||
(*AdobeRGB_CS).bind(*AdobeRGBL_CS);
|
||||
map_cs[COLOR_SPACE_ADOBE_RGB] = AdobeRGB_CS;
|
||||
map_cs[COLOR_SPACE_ADOBE_RGBL] = AdobeRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGB:
|
||||
case cv::ccm::COLOR_SPACE_WIDE_GAMUT_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGB_CS(new WideGamutRGB_(false));
|
||||
std::shared_ptr<WideGamutRGB_> WideGamutRGBL_CS(new WideGamutRGB_(true));
|
||||
(*WideGamutRGB_CS).bind(*WideGamutRGBL_CS);
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGB] = WideGamutRGB_CS;
|
||||
map_cs[COLOR_SPACE_WIDE_GAMUT_RGBL] = WideGamutRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGB:
|
||||
case cv::ccm::COLOR_SPACE_PRO_PHOTO_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGB_CS(new ProPhotoRGB_(false));
|
||||
std::shared_ptr<ProPhotoRGB_> ProPhotoRGBL_CS(new ProPhotoRGB_(true));
|
||||
(*ProPhotoRGB_CS).bind(*ProPhotoRGBL_CS);
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGB] = ProPhotoRGB_CS;
|
||||
map_cs[COLOR_SPACE_PRO_PHOTO_RGBL] = ProPhotoRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGB:
|
||||
case cv::ccm::COLOR_SPACE_DCI_P3_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGB_CS(new DCI_P3_RGB_(false));
|
||||
std::shared_ptr<DCI_P3_RGB_> DCI_P3_RGBL_CS(new DCI_P3_RGB_(true));
|
||||
(*DCI_P3_RGB_CS).bind(*DCI_P3_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGB] = DCI_P3_RGB_CS;
|
||||
map_cs[COLOR_SPACE_DCI_P3_RGBL] = DCI_P3_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGB:
|
||||
case cv::ccm::COLOR_SPACE_APPLE_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<AppleRGB_> AppleRGB_CS(new AppleRGB_(false));
|
||||
std::shared_ptr<AppleRGB_> AppleRGBL_CS(new AppleRGB_(true));
|
||||
(*AppleRGB_CS).bind(*AppleRGBL_CS);
|
||||
map_cs[COLOR_SPACE_APPLE_RGB] = AppleRGB_CS;
|
||||
map_cs[COLOR_SPACE_APPLE_RGBL] = AppleRGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGB:
|
||||
case cv::ccm::COLOR_SPACE_REC_709_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGB_CS(new REC_709_RGB_(false));
|
||||
std::shared_ptr<REC_709_RGB_> REC_709_RGBL_CS(new REC_709_RGB_(true));
|
||||
(*REC_709_RGB_CS).bind(*REC_709_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_709_RGB] = REC_709_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_709_RGBL] = REC_709_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGB:
|
||||
case cv::ccm::COLOR_SPACE_REC_2020_RGBL:
|
||||
{
|
||||
if (map_cs.count(cs_name) < 1)
|
||||
{
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGB_CS(new REC_2020_RGB_(false));
|
||||
std::shared_ptr<REC_2020_RGB_> REC_2020_RGBL_CS(new REC_2020_RGB_(true));
|
||||
(*REC_2020_RGB_CS).bind(*REC_2020_RGBL_CS);
|
||||
map_cs[COLOR_SPACE_REC_2020_RGB] = REC_2020_RGB_CS;
|
||||
map_cs[COLOR_SPACE_REC_2020_RGBL] = REC_2020_RGBL_CS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D65_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D65_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D50_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D50_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D65_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D65_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D50_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D50_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_A_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(A_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_A_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(A_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D55_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D55_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D55_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D55_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D75_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D75_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_D75_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(D75_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_E_2:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(E_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_XYZ_E_10:
|
||||
return XYZ::get(IllumObserver::getIllumObservers(E_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D65_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D65_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D50_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D50_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D65_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D65_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D50_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D50_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_A_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(A_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_A_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(A_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D55_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D55_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D55_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D55_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D75_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D75_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_D75_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(D75_10));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_E_2:
|
||||
return Lab::get(IllumObserver::getIllumObservers(E_2));
|
||||
break;
|
||||
case cv::ccm::COLOR_SPACE_LAB_E_10:
|
||||
return Lab::get(IllumObserver::getIllumObservers(E_10));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return map_cs[cs_name];
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,343 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_COLORSPACE_HPP__
|
||||
#define __OPENCV_CCM_COLORSPACE_HPP__
|
||||
|
||||
#include "operations.hpp"
|
||||
#include "illumobserver.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Basic class for ColorSpace.
|
||||
*/
|
||||
class ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
typedef std::function<Mat(Mat)> MatFunc;
|
||||
IllumObserver illumobserver;
|
||||
std::string type;
|
||||
bool linear;
|
||||
Operations to;
|
||||
Operations from;
|
||||
ColorSpaceBase* l;
|
||||
ColorSpaceBase* nl;
|
||||
|
||||
ColorSpaceBase() {};
|
||||
|
||||
ColorSpaceBase(IllumObserver illumobserver_, std::string type_, bool linear_)
|
||||
: illumobserver(illumobserver_)
|
||||
, type(type_)
|
||||
, linear(linear_) {};
|
||||
|
||||
virtual ~ColorSpaceBase()
|
||||
{
|
||||
l = 0;
|
||||
nl = 0;
|
||||
};
|
||||
virtual bool relate(const ColorSpaceBase& other) const;
|
||||
|
||||
virtual Operations relation(const ColorSpaceBase& /*other*/) const;
|
||||
|
||||
bool operator<(const ColorSpaceBase& other) const;
|
||||
};
|
||||
|
||||
/** @brief Base of RGB color space;
|
||||
the argument values are from AdobeRGB;
|
||||
Data from https://en.wikipedia.org/wiki/Adobe_RGB_color_space
|
||||
*/
|
||||
|
||||
class RGBBase_ : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
// primaries
|
||||
double xr;
|
||||
double yr;
|
||||
double xg;
|
||||
double yg;
|
||||
double xb;
|
||||
double yb;
|
||||
Mat M_to;
|
||||
Mat M_from;
|
||||
|
||||
using ColorSpaceBase::ColorSpaceBase;
|
||||
|
||||
/** @brief There are 3 kinds of relationships for RGB:
|
||||
1. Different types; - no operation
|
||||
1. Same type, same linear; - copy
|
||||
2. Same type, different linear, self is nonlinear; - 2 toL
|
||||
3. Same type, different linear, self is linear - 3 fromL
|
||||
@param other type of ColorSpaceBase.
|
||||
@return Operations.
|
||||
*/
|
||||
Operations relation(const ColorSpaceBase& other) const CV_OVERRIDE;
|
||||
|
||||
/** @brief Initial operations.
|
||||
*/
|
||||
void init();
|
||||
/** @brief Produce color space instance with linear and non-linear versions.
|
||||
@param rgbl type of RGBBase_.
|
||||
*/
|
||||
void bind(RGBBase_& rgbl);
|
||||
|
||||
virtual Mat toLFunc(Mat& /*rgb*/) const;
|
||||
|
||||
virtual Mat fromLFunc(Mat& /*rgbl*/, Mat dst=Mat()) const;
|
||||
private:
|
||||
virtual void setParameter() {};
|
||||
|
||||
/** @brief Calculation of M_RGBL2XYZ_base.
|
||||
*/
|
||||
virtual void calM();
|
||||
|
||||
/** @brief operations to or from XYZ.
|
||||
*/
|
||||
virtual void calOperations();
|
||||
|
||||
virtual void calLinear() {};
|
||||
};
|
||||
|
||||
/** @brief Base of Adobe RGB color space;
|
||||
*/
|
||||
class AdobeRGBBase_ : public RGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
using RGBBase_::RGBBase_;
|
||||
double gamma;
|
||||
|
||||
private:
|
||||
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
|
||||
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Base of sRGB color space;
|
||||
*/
|
||||
class sRGBBase_ : public RGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
using RGBBase_::RGBBase_;
|
||||
double a;
|
||||
double gamma;
|
||||
double alpha;
|
||||
double beta;
|
||||
double phi;
|
||||
double K0;
|
||||
|
||||
private:
|
||||
/** @brief linearization parameters
|
||||
*/
|
||||
virtual void calLinear() CV_OVERRIDE;
|
||||
/** @brief Used by toLFunc.
|
||||
*/
|
||||
double toLFuncEW(double x) const;
|
||||
|
||||
/** @brief Linearization.
|
||||
@param rgb the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat toLFunc(Mat& rgb) const CV_OVERRIDE;
|
||||
|
||||
/** @brief Used by fromLFunc.
|
||||
*/
|
||||
double fromLFuncEW(double x) const;
|
||||
|
||||
/** @brief Delinearization.
|
||||
@param rgbl the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat fromLFunc(Mat& rgbl, Mat dst=Mat()) const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief sRGB color space.
|
||||
data from https://en.wikipedia.org/wiki/SRGB.
|
||||
*/
|
||||
class sRGB_ : public sRGBBase_
|
||||
|
||||
{
|
||||
public:
|
||||
sRGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "sRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Adobe RGB color space.
|
||||
*/
|
||||
class AdobeRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
AdobeRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "AdobeRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Wide-gamut RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Wide-gamut_RGB_color_space.
|
||||
*/
|
||||
class WideGamutRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
WideGamutRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D50_2), "WideGamutRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief ProPhoto RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space.
|
||||
*/
|
||||
|
||||
class ProPhotoRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
ProPhotoRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D50_2), "ProPhotoRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief DCI-P3 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/DCI-P3.
|
||||
*/
|
||||
class DCI_P3_RGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
DCI_P3_RGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "DCI_P3_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Apple RGB color space.
|
||||
data from http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html.
|
||||
*/
|
||||
class AppleRGB_ : public AdobeRGBBase_
|
||||
{
|
||||
public:
|
||||
AppleRGB_(bool linear_ = false)
|
||||
: AdobeRGBBase_(IllumObserver::getIllumObservers(D65_2), "AppleRGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief REC_709 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Rec._709.
|
||||
*/
|
||||
class REC_709_RGB_ : public sRGBBase_
|
||||
{
|
||||
public:
|
||||
REC_709_RGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "REC_709_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief REC_2020 RGB color space.
|
||||
data from https://en.wikipedia.org/wiki/Rec._2020.
|
||||
*/
|
||||
class REC_2020_RGB_ : public sRGBBase_
|
||||
{
|
||||
public:
|
||||
REC_2020_RGB_(bool linear_)
|
||||
: sRGBBase_(IllumObserver::getIllumObservers(D65_2), "REC_2020_RGB", linear_) {};
|
||||
|
||||
private:
|
||||
void setParameter() CV_OVERRIDE;
|
||||
};
|
||||
|
||||
/** @brief Enum of the possible types of Chromatic Adaptation Models.
|
||||
*/
|
||||
enum ChromaticAdaptationType
|
||||
{
|
||||
IDENTITY,
|
||||
VON_KRIES,
|
||||
BRADFORD
|
||||
};
|
||||
|
||||
|
||||
/** @brief XYZ color space.
|
||||
Chromatic adaption matrices.
|
||||
*/
|
||||
class XYZ : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
XYZ(IllumObserver illumobserver_)
|
||||
: ColorSpaceBase(illumobserver_, "XYZ", true) {};
|
||||
Operations cam(IllumObserver dio, ChromaticAdaptationType method = BRADFORD);
|
||||
static std::shared_ptr<XYZ> get(IllumObserver illumobserver);
|
||||
|
||||
private:
|
||||
/** @brief Get cam.
|
||||
@param sio the input IllumObserver of src.
|
||||
@param dio the input IllumObserver of dst.
|
||||
@param method type of Chromatic Adaptation Model.
|
||||
@return the output array, type of cv::Mat.
|
||||
*/
|
||||
Mat cam_(IllumObserver sio, IllumObserver dio, ChromaticAdaptationType method = BRADFORD) const;
|
||||
};
|
||||
|
||||
/** @brief Lab color space.
|
||||
*/
|
||||
class Lab : public ColorSpaceBase
|
||||
{
|
||||
public:
|
||||
Lab(IllumObserver illumobserver_);
|
||||
static std::shared_ptr<Lab> get(IllumObserver illumobserver);
|
||||
|
||||
private:
|
||||
static constexpr double DELTA = (6. / 29.);
|
||||
static constexpr double M = 1. / (3. * DELTA * DELTA);
|
||||
static constexpr double T0 = DELTA * DELTA * DELTA;
|
||||
static constexpr double C = 4. / 29.;
|
||||
|
||||
Vec3d fromxyz(const Vec3d& xyz);
|
||||
|
||||
/** @brief Calculate From.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat fromsrc(Mat& src);
|
||||
|
||||
Vec3d tolab(const Vec3d& lab);
|
||||
|
||||
/** @brief Calculate To.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@return the output array, type of cv::Mat
|
||||
*/
|
||||
Mat tosrc(Mat& src);
|
||||
};
|
||||
|
||||
class GetCS
|
||||
{
|
||||
protected:
|
||||
std::map<enum ColorSpace, std::shared_ptr<ColorSpaceBase>> map_cs;
|
||||
|
||||
GetCS(); // singleton, use getInstance()
|
||||
public:
|
||||
static GetCS& getInstance();
|
||||
|
||||
std::shared_ptr<RGBBase_> getRgb(enum ColorSpace cs_name);
|
||||
std::shared_ptr<ColorSpaceBase> getCS(enum ColorSpace cs_name);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "distance.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
double deltaCIE76(const Vec3d& lab1, const Vec3d& lab2) { return norm(lab1 - lab2); };
|
||||
|
||||
double deltaCIE94(const Vec3d& lab1, const 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(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIE94(lab1, lab2);
|
||||
}
|
||||
|
||||
double toRad(double degree) { return degree / 180 * CV_PI; };
|
||||
|
||||
double deltaCIE94Textiles(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIE94(lab1, lab2, 1.0, 1.0, 2.0, 0.048, 0.014);
|
||||
}
|
||||
|
||||
double deltaCIEDE2000_(const Vec3d& lab1, const Vec3d& lab2, double kL,
|
||||
double kC, double kH)
|
||||
{
|
||||
double deltaLApo = lab2[0] - lab1[0];
|
||||
double lBarApo = (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 cBar = (C1 + C2) / 2.0;
|
||||
double G = sqrt(pow(cBar, 7) / (pow(cBar, 7) + pow(25, 7)));
|
||||
double a1Apo = lab1[1] + lab1[1] / 2.0 * (1.0 - G);
|
||||
double a2Apo = lab2[1] + lab2[1] / 2.0 * (1.0 - G);
|
||||
double c1Apo = sqrt(pow(a1Apo, 2) + pow(lab1[2], 2));
|
||||
double c2Apo = sqrt(pow(a2Apo, 2) + pow(lab2[2], 2));
|
||||
double cBarApo = (c1Apo + c2Apo) / 2.0;
|
||||
double deltaCApo = c2Apo - c1Apo;
|
||||
|
||||
double h1Apo;
|
||||
if (c1Apo == 0)
|
||||
{
|
||||
h1Apo = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
h1Apo = atan2(lab1[2], a1Apo);
|
||||
if (h1Apo < 0.0)
|
||||
h1Apo += 2. * CV_PI;
|
||||
}
|
||||
|
||||
double h2Apo;
|
||||
if (c2Apo == 0)
|
||||
{
|
||||
h2Apo = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
h2Apo = atan2(lab2[2], a2Apo);
|
||||
if (h2Apo < 0.0)
|
||||
h2Apo += 2. * CV_PI;
|
||||
}
|
||||
|
||||
double deltaHApo;
|
||||
if (abs(h2Apo - h1Apo) <= CV_PI)
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo;
|
||||
}
|
||||
else if (h2Apo <= h1Apo)
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo + 2. * CV_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
deltaHApo = h2Apo - h1Apo - 2. * CV_PI;
|
||||
}
|
||||
|
||||
double hBarApo;
|
||||
if (c1Apo == 0 || c2Apo == 0)
|
||||
{
|
||||
hBarApo = h1Apo + h2Apo;
|
||||
}
|
||||
else if (abs(h1Apo - h2Apo) <= CV_PI)
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo) / 2.0;
|
||||
}
|
||||
else if (h1Apo + h2Apo < 2. * CV_PI)
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo + 2. * CV_PI) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hBarApo = (h1Apo + h2Apo - 2. * CV_PI) / 2.0;
|
||||
}
|
||||
|
||||
double deltaH_Apo = 2.0 * sqrt(c1Apo * c2Apo) * sin(deltaHApo / 2.0);
|
||||
double T = 1.0 - 0.17 * cos(hBarApo - toRad(30.)) + 0.24 * cos(2.0 * hBarApo) + 0.32 * cos(3.0 * hBarApo + toRad(6.0)) - 0.2 * cos(4.0 * hBarApo - toRad(63.0));
|
||||
double sC = 1.0 + 0.045 * cBarApo;
|
||||
double sH = 1.0 + 0.015 * cBarApo * T;
|
||||
double sL = 1.0 + ((0.015 * pow(lBarApo - 50.0, 2.0)) / sqrt(20.0 + pow(lBarApo - 50.0, 2.0)));
|
||||
double rC = 2.0 * sqrt(pow(cBarApo, 7.0) / (pow(cBarApo, 7.0) + pow(25, 7)));
|
||||
double rT = -sin(toRad(60.0) * exp(-pow((hBarApo - toRad(275.0)) / toRad(25.0), 2.0))) * rC;
|
||||
double res = (pow(deltaLApo / (kL * sL), 2.0) + pow(deltaCApo / (kC * sC), 2.0) + pow(deltaH_Apo / (kH * sH), 2.0) + rT * (deltaCApo / (kC * sC)) * (deltaH_Apo / (kH * sH)));
|
||||
return res > 0 ? sqrt(res) : 0;
|
||||
}
|
||||
|
||||
double deltaCIEDE2000(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCIEDE2000_(lab1, lab2);
|
||||
}
|
||||
|
||||
double deltaCMC(const Vec3d& lab1, const 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(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCMC(lab1, lab2);
|
||||
}
|
||||
|
||||
double deltaCMC2To1(const Vec3d& lab1, const Vec3d& lab2)
|
||||
{
|
||||
return deltaCMC(lab1, lab2, 2, 1);
|
||||
}
|
||||
|
||||
Mat distance(Mat src, Mat ref, DistanceType distanceType)
|
||||
{
|
||||
switch (distanceType)
|
||||
{
|
||||
case cv::ccm::DISTANCE_CIE76:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
case cv::ccm::DISTANCE_CIE94_GRAPHIC_ARTS:
|
||||
return distanceWise(src, ref, deltaCIE94GraphicArts);
|
||||
case cv::ccm::DISTANCE_CIE94_TEXTILES:
|
||||
return distanceWise(src, ref, deltaCIE94Textiles);
|
||||
case cv::ccm::DISTANCE_CIE2000:
|
||||
return distanceWise(src, ref, deltaCIEDE2000);
|
||||
case cv::ccm::DISTANCE_CMC_1TO1:
|
||||
return distanceWise(src, ref, deltaCMC1To1);
|
||||
case cv::ccm::DISTANCE_CMC_2TO1:
|
||||
return distanceWise(src, ref, deltaCMC2To1);
|
||||
case cv::ccm::DISTANCE_RGB:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
case cv::ccm::DISTANCE_RGBL:
|
||||
return distanceWise(src, ref, deltaCIE76);
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong distanceType!" );
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace ccm
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_DISTANCE_HPP__
|
||||
#define __OPENCV_CCM_DISTANCE_HPP__
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
/** possibale functions to calculate the distance between
|
||||
colors.see https://en.wikipedia.org/wiki/Color_difference for details;*/
|
||||
|
||||
/** @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(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CIE94
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kH Hue scale
|
||||
@param kC Chroma scale
|
||||
@param kL Lightness scale
|
||||
@param k1 first scale parameter
|
||||
@param k2 second scale parameter
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
|
||||
double deltaCIE94(const Vec3d& lab1, const Vec3d& lab2, double kH = 1.0,
|
||||
double kC = 1.0, double kL = 1.0, double k1 = 0.045,
|
||||
double k2 = 0.015);
|
||||
|
||||
double deltaCIE94GraphicArts(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
double toRad(double degree);
|
||||
|
||||
double deltaCIE94Textiles(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CIE2000
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kL Lightness scale
|
||||
@param kC Chroma scale
|
||||
@param kH Hue scale
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
double deltaCIEDE2000_(const Vec3d& lab1, const Vec3d& lab2, double kL = 1.0,
|
||||
double kC = 1.0, double kH = 1.0);
|
||||
double deltaCIEDE2000(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
/** @brief distance between two points in formula CMC
|
||||
@param lab1 a 3D vector
|
||||
@param lab2 a 3D vector
|
||||
@param kL Lightness scale
|
||||
@param kC Chroma scale
|
||||
@return distance between lab1 and lab2
|
||||
*/
|
||||
|
||||
double deltaCMC(const Vec3d& lab1, const Vec3d& lab2, double kL = 1, double kC = 1);
|
||||
|
||||
double deltaCMC1To1(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
double deltaCMC2To1(const Vec3d& lab1, const Vec3d& lab2);
|
||||
|
||||
Mat distance(Mat src,Mat ref, DistanceType distanceType);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "illumobserver.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
IllumObserver::IllumObserver(std::string illuminant_, std::string observer_)
|
||||
: illuminant(illuminant_)
|
||||
, observer(observer_) {};
|
||||
|
||||
bool IllumObserver::operator<(const IllumObserver& other) const
|
||||
{
|
||||
return (illuminant < other.illuminant || ((illuminant == other.illuminant) && (observer < other.observer)));
|
||||
}
|
||||
|
||||
bool IllumObserver::operator==(const IllumObserver& other) const
|
||||
{
|
||||
return illuminant == other.illuminant && observer == other.observer;
|
||||
};
|
||||
|
||||
IllumObserver IllumObserver::getIllumObservers(IllumObserverType illumobserver)
|
||||
{
|
||||
switch (illumobserver)
|
||||
{
|
||||
case cv::ccm::A_2:
|
||||
{
|
||||
IllumObserver A_2_IllumObserver("A", "2");
|
||||
return A_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::A_10:
|
||||
{
|
||||
IllumObserver A_1O_IllumObserver("A", "10");
|
||||
return A_1O_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D50_2:
|
||||
{
|
||||
IllumObserver D50_2_IllumObserver("D50", "2");
|
||||
return D50_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D50_10:
|
||||
{
|
||||
IllumObserver D50_10_IllumObserver("D50", "10");
|
||||
return D50_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D55_2:
|
||||
{
|
||||
IllumObserver D55_2_IllumObserver("D55", "2");
|
||||
return D55_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D55_10:
|
||||
{
|
||||
IllumObserver D55_10_IllumObserver("D55", "10");
|
||||
return D55_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D65_2:
|
||||
{
|
||||
IllumObserver D65_2_IllumObserver("D65", "2");
|
||||
return D65_2_IllumObserver;
|
||||
}
|
||||
case cv::ccm::D65_10:
|
||||
{
|
||||
IllumObserver D65_10_IllumObserver("D65", "10");
|
||||
return D65_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D75_2:
|
||||
{
|
||||
IllumObserver D75_2_IllumObserver("D75", "2");
|
||||
return D75_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::D75_10:
|
||||
{
|
||||
IllumObserver D75_10_IllumObserver("D75", "10");
|
||||
return D75_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::E_2:
|
||||
{
|
||||
IllumObserver E_2_IllumObserver("E", "2");
|
||||
return E_2_IllumObserver;
|
||||
break;
|
||||
}
|
||||
case cv::ccm::E_10:
|
||||
{
|
||||
IllumObserver E_10_IllumObserver("E", "10");
|
||||
return E_10_IllumObserver;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return IllumObserver();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// data from https://en.wikipedia.org/wiki/Standard_illuminant.
|
||||
std::vector<double> xyY2XYZ(const std::vector<double>& 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 cv::ccm
|
||||
@@ -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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_IllumObserver_HPP__
|
||||
#define __OPENCV_CCM_IllumObserver_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
enum IllumObserverType
|
||||
{
|
||||
A_2,
|
||||
A_10,
|
||||
D50_2,
|
||||
D50_10,
|
||||
D55_2,
|
||||
D55_10,
|
||||
D65_2,
|
||||
D65_10,
|
||||
D75_2,
|
||||
D75_10,
|
||||
E_2,
|
||||
E_10
|
||||
};
|
||||
|
||||
/** @brief IllumObserver is the meaning of illuminant and observer. See notes of ccm.hpp
|
||||
for supported list for illuminant and observer*/
|
||||
class IllumObserver
|
||||
{
|
||||
public:
|
||||
std::string illuminant;
|
||||
std::string observer;
|
||||
IllumObserver() {};
|
||||
IllumObserver(std::string illuminant, std::string observer);
|
||||
virtual ~IllumObserver() {};
|
||||
bool operator<(const IllumObserver& other) const;
|
||||
bool operator==(const IllumObserver& other) const;
|
||||
static IllumObserver getIllumObservers(IllumObserverType illumobserver);
|
||||
};
|
||||
std::vector<double> xyY2XYZ(const std::vector<double>& xyY);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,284 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "linearize.hpp"
|
||||
|
||||
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& defaultValue) {
|
||||
if(node.empty())
|
||||
polyfit = defaultValue;
|
||||
else
|
||||
polyfit.read(node);
|
||||
}
|
||||
|
||||
Polyfit::Polyfit(Mat x, Mat y, int deg_)
|
||||
: deg(deg_)
|
||||
{
|
||||
int n = x.cols * x.rows * x.channels();
|
||||
x = x.reshape(1, n);
|
||||
y = y.reshape(1, n);
|
||||
Mat_<double> A = Mat_<double>::ones(n, deg + 1);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
for (int j = 1; j < A.cols; ++j)
|
||||
{
|
||||
A.at<double>(i, j) = x.at<double>(i) * A.at<double>(i, j - 1);
|
||||
}
|
||||
}
|
||||
Mat y_(y);
|
||||
cv::solve(A, y_, p, DECOMP_SVD);
|
||||
}
|
||||
|
||||
Mat Polyfit::operator()(const Mat& inp)
|
||||
{
|
||||
return elementWise(inp, [this](double x) -> double { return fromEW(x); });
|
||||
};
|
||||
|
||||
double Polyfit::fromEW(double x)
|
||||
{
|
||||
double res = 0;
|
||||
for (int d = 0; d <= deg; ++d)
|
||||
{
|
||||
res += pow(x, d) * p.at<double>(d, 0);
|
||||
}
|
||||
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& defaultValue) {
|
||||
if(node.empty())
|
||||
logpolyfit = defaultValue;
|
||||
else
|
||||
logpolyfit.read(node);
|
||||
}
|
||||
|
||||
LogPolyfit::LogPolyfit(Mat x, Mat y, int deg_)
|
||||
: deg(deg_)
|
||||
{
|
||||
Mat mask_ = (x > 0) & (y > 0);
|
||||
Mat src_, dst_, s_, d_;
|
||||
src_ = maskCopyTo(x, mask_);
|
||||
dst_ = maskCopyTo(y, mask_);
|
||||
log(src_, s_);
|
||||
log(dst_, d_);
|
||||
p = Polyfit(s_, d_, deg);
|
||||
}
|
||||
|
||||
Mat LogPolyfit::operator()(const Mat& inp)
|
||||
{
|
||||
Mat mask_ = inp >= 0;
|
||||
Mat y, y_, res;
|
||||
log(inp, y);
|
||||
y = p(y);
|
||||
exp(y, y_);
|
||||
y_.copyTo(res, mask_);
|
||||
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 <typename T>
|
||||
void LinearColor<T>::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "deg" << deg << "pr" << pr << "pg" << pg << "pb" << pb << "}";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearColor<T>::read(const cv::FileNode& node)
|
||||
{
|
||||
node["deg"] >> deg;
|
||||
node["pr"] >> pr;
|
||||
node["pg"] >> pg;
|
||||
node["pb"] >> pb;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearGray<T>::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "{" << "deg" << deg << "p" << p << "}";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void LinearGray<T>::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& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linear = defaultValue;
|
||||
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& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linearidentity = defaultValue;
|
||||
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& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
lineargamma = defaultValue;
|
||||
else
|
||||
lineargamma.read(node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearColor<T>& linearcolor)
|
||||
{
|
||||
linearcolor.write(fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearColor<T>& linearcolor, const LinearColor<T>& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
linearcolor = defaultValue;
|
||||
else
|
||||
linearcolor.read(node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGray<T>& lineargray)
|
||||
{
|
||||
lineargray.write(fs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearGray<T>& lineargray, const LinearGray<T>& defaultValue)
|
||||
{
|
||||
if (node.empty())
|
||||
lineargray = defaultValue;
|
||||
else
|
||||
lineargray.read(node);
|
||||
}
|
||||
|
||||
Mat Linear::linearize(Mat inp)
|
||||
{
|
||||
return inp;
|
||||
};
|
||||
|
||||
Mat LinearGamma::linearize(Mat inp)
|
||||
{
|
||||
Mat out;
|
||||
gammaCorrection(inp, out, gamma);
|
||||
return out;
|
||||
};
|
||||
|
||||
std::shared_ptr<Linear> getLinear(double gamma, int deg, Mat src, Color dst, Mat mask, RGBBase_ cs, LinearizationType linearizationType)
|
||||
{
|
||||
std::shared_ptr<Linear> p = std::make_shared<Linear>();
|
||||
switch (linearizationType)
|
||||
{
|
||||
case cv::ccm::LINEARIZATION_IDENTITY:
|
||||
p = std::make_shared<LinearIdentity>();
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GAMMA:
|
||||
p = std::make_shared<LinearGamma>(gamma);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORPOLYFIT:
|
||||
p = std::make_shared<LinearColor<Polyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_COLORLOGPOLYFIT:
|
||||
p = std::make_shared<LinearColor<LogPolyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYPOLYFIT:
|
||||
p = std::make_shared<LinearGray<Polyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
case cv::ccm::LINEARIZATION_GRAYLOGPOLYFIT:
|
||||
p = std::make_shared<LinearGray<LogPolyfit>>(deg, src, dst, mask, cs);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong linearizationType!" );
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,260 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_LINEARIZE_HPP__
|
||||
#define __OPENCV_CCM_LINEARIZE_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <map>
|
||||
#include "color.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Polyfit model.
|
||||
*/
|
||||
class Polyfit
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
Mat p;
|
||||
Polyfit();
|
||||
|
||||
/** @brief Polyfit method.
|
||||
https://en.wikipedia.org/wiki/Polynomial_regression
|
||||
polynomial: yi = a0 + a1*xi + a2*xi^2 + ... + an*xi^deg (i = 1,2,...,n)
|
||||
and deduct: Ax = y
|
||||
*/
|
||||
Polyfit(Mat x, Mat y, int deg);
|
||||
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& defaultValue = Polyfit());
|
||||
|
||||
/** @brief Logpolyfit model.
|
||||
*/
|
||||
class LogPolyfit
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
Polyfit p;
|
||||
|
||||
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& defaultValue = LogPolyfit());
|
||||
|
||||
/** @brief Linearization base.
|
||||
*/
|
||||
|
||||
class Linear
|
||||
{
|
||||
public:
|
||||
Linear() {};
|
||||
virtual ~Linear() {};
|
||||
|
||||
/** @brief Inference.
|
||||
@param inp the input array, type of cv::Mat.
|
||||
*/
|
||||
virtual Mat linearize(Mat inp);
|
||||
/** @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& defaultValue = Linear());
|
||||
|
||||
/** @brief Linearization identity.
|
||||
make no change.
|
||||
*/
|
||||
class LinearIdentity : public Linear
|
||||
{
|
||||
public:
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// 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& defaultValue = LinearIdentity());
|
||||
|
||||
/** @brief Linearization gamma correction.
|
||||
*/
|
||||
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 CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_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& defaultValue = LinearGamma());
|
||||
|
||||
/** @brief Linearization.
|
||||
Grayscale polynomial fitting.
|
||||
*/
|
||||
template <class T>
|
||||
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_)
|
||||
{
|
||||
dst.getGray();
|
||||
Mat lear_gray_mask = mask & dst.grays;
|
||||
|
||||
// the grayscale function is approximate for src is in relative color space.
|
||||
Mat gray;
|
||||
cvtColor(src, gray, COLOR_RGB2GRAY);
|
||||
gray.copyTo(src);
|
||||
|
||||
Mat dst_ = maskCopyTo(dst.toGray(cs.illumobserver), lear_gray_mask);
|
||||
calc(src, dst_);
|
||||
}
|
||||
|
||||
/** @brief monotonically increase is not guaranteed.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@param dst the input array, type of cv::Mat.
|
||||
*/
|
||||
void calc(const Mat& src, const Mat& dst)
|
||||
{
|
||||
p = T(src, dst, deg);
|
||||
};
|
||||
|
||||
Mat linearize(Mat inp) CV_OVERRIDE
|
||||
{
|
||||
return p(inp);
|
||||
};
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearGray
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearGray<T>& lineargray);
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearGray<T>& lineargray, const LinearGray<T>& defaultValue = LinearGray<T>());
|
||||
|
||||
/** @brief Linearization.
|
||||
Fitting channels respectively.
|
||||
*/
|
||||
template <class T>
|
||||
class LinearColor : public Linear
|
||||
{
|
||||
public:
|
||||
int deg;
|
||||
T pr;
|
||||
T pg;
|
||||
T pb;
|
||||
|
||||
LinearColor(): deg(3) {};
|
||||
|
||||
LinearColor(int deg_, Mat src_, Color dst, Mat mask, RGBBase_ cs)
|
||||
: deg(deg_)
|
||||
{
|
||||
Mat src = maskCopyTo(src_, mask);
|
||||
Mat dst_ = maskCopyTo(dst.to(*cs.l).colors, mask);
|
||||
calc(src, dst_);
|
||||
}
|
||||
|
||||
void calc(const Mat& src, const Mat& dst)
|
||||
{
|
||||
Mat schannels[3];
|
||||
Mat dchannels[3];
|
||||
split(src, schannels);
|
||||
split(dst, dchannels);
|
||||
pr = T(schannels[0], dchannels[0], deg);
|
||||
pg = T(schannels[1], dchannels[1], deg);
|
||||
pb = T(schannels[2], dchannels[2], deg);
|
||||
};
|
||||
|
||||
Mat linearize(Mat inp) CV_OVERRIDE
|
||||
{
|
||||
Mat channels[3];
|
||||
split(inp, channels);
|
||||
std::vector<Mat> channel;
|
||||
Mat res;
|
||||
merge(std::vector<Mat> { pr(channels[0]), pg(channels[1]), pb(channels[2]) }, res);
|
||||
return res;
|
||||
};
|
||||
|
||||
// Serialization support
|
||||
void write(cv::FileStorage& fs) const CV_OVERRIDE;
|
||||
void read(const cv::FileNode& node) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
// Global functions for FileStorage for LinearColor
|
||||
template <typename T>
|
||||
void write(cv::FileStorage& fs, const std::string&, const LinearColor<T>& linearcolor);
|
||||
template <typename T>
|
||||
void read(const cv::FileNode& node, LinearColor<T>& linearcolor, const LinearColor<T>& defaultValue = LinearColor<T>());
|
||||
|
||||
/** @brief Get linearization method.
|
||||
used in ccm model.
|
||||
@param gamma used in LinearGamma.
|
||||
@param deg degrees.
|
||||
@param src the input array, type of cv::Mat.
|
||||
@param dst the input array, type of cv::Mat.
|
||||
@param mask the input array, type of cv::Mat.
|
||||
@param cs type of RGBBase_.
|
||||
@param linearizationType type of linear.
|
||||
*/
|
||||
|
||||
std::shared_ptr<Linear> getLinear(double gamma, int deg, Mat src, Color dst, Mat mask, RGBBase_ cs, LinearizationType linearizationType);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "operations.hpp"
|
||||
#include "utils.hpp"
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
Mat Operation::operator()(Mat& abc)
|
||||
{
|
||||
if (!linear)
|
||||
{
|
||||
return f(abc);
|
||||
}
|
||||
if (M.empty())
|
||||
{
|
||||
return abc;
|
||||
}
|
||||
return multiple(abc, M);
|
||||
};
|
||||
|
||||
void Operation::add(const Operation& other)
|
||||
{
|
||||
if (M.empty())
|
||||
{
|
||||
M = other.M.clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
M = M * other.M;
|
||||
}
|
||||
};
|
||||
|
||||
void Operation::clear()
|
||||
{
|
||||
M = Mat();
|
||||
};
|
||||
|
||||
Operations& Operations::add(const Operations& other)
|
||||
{
|
||||
ops.insert(ops.end(), other.ops.begin(), other.ops.end());
|
||||
return *this;
|
||||
};
|
||||
|
||||
Mat Operations::run(Mat abc)
|
||||
{
|
||||
Operation hd;
|
||||
for (auto& op : ops)
|
||||
{
|
||||
if (op.linear)
|
||||
{
|
||||
hd.add(op);
|
||||
}
|
||||
else
|
||||
{
|
||||
abc = hd(abc);
|
||||
hd.clear();
|
||||
abc = op(abc);
|
||||
}
|
||||
}
|
||||
abc = hd(abc);
|
||||
return abc;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_OPERATIONS_HPP__
|
||||
#define __OPENCV_CCM_OPERATIONS_HPP__
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
/** @brief Operation class contains some operarions used for color space
|
||||
conversion containing linear transformation and non-linear transformation
|
||||
*/
|
||||
class Operation
|
||||
{
|
||||
public:
|
||||
typedef std::function<Mat(Mat)> MatFunc;
|
||||
bool linear;
|
||||
Mat M;
|
||||
MatFunc f;
|
||||
|
||||
Operation()
|
||||
: linear(true)
|
||||
, M(Mat()) {};
|
||||
Operation(Mat M_)
|
||||
: linear(true)
|
||||
, M(M_) {};
|
||||
Operation(MatFunc f_)
|
||||
: linear(false)
|
||||
, f(f_) {};
|
||||
virtual ~Operation() {};
|
||||
|
||||
/** @brief operator function will run operation
|
||||
*/
|
||||
Mat operator()(Mat& abc);
|
||||
|
||||
/** @brief add function will conbine this operation
|
||||
with other linear transformation operation
|
||||
*/
|
||||
void add(const Operation& other);
|
||||
|
||||
void clear();
|
||||
static Operation& getIdentityOp()
|
||||
{
|
||||
static Operation identity_op([](Mat x) { return x; });
|
||||
return identity_op;
|
||||
}
|
||||
};
|
||||
|
||||
class Operations
|
||||
{
|
||||
public:
|
||||
std::vector<Operation> ops;
|
||||
Operations()
|
||||
: ops {} {};
|
||||
Operations(std::initializer_list<Operation> op)
|
||||
: ops { op } {};
|
||||
virtual ~Operations() {};
|
||||
|
||||
/** @brief add function will conbine this operation with other transformation operations
|
||||
*/
|
||||
Operations& add(const Operations& other);
|
||||
|
||||
/** @brief run operations to make color conversion
|
||||
*/
|
||||
Mat run(Mat abc);
|
||||
static const Operations& getIdentityOps()
|
||||
{
|
||||
static Operations Operation_op {Operation::getIdentityOp()};
|
||||
return Operation_op;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
|
||||
void gammaCorrection(InputArray _src, OutputArray _dst, double gamma)
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
CV_Assert(gamma > 0);
|
||||
|
||||
double maxVal;
|
||||
int depth = src.depth();
|
||||
switch (depth)
|
||||
{
|
||||
case CV_8U: maxVal = 255.0; break;
|
||||
case CV_16U: maxVal = 65535.0; break;
|
||||
case CV_16S: maxVal = 32767.0; break;
|
||||
case CV_32F: maxVal = 1.0; break;
|
||||
case CV_64F: maxVal = 1.0; break;
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat,
|
||||
"gammaCorrection: unsupported image depth");
|
||||
}
|
||||
|
||||
// Special‐case for uint8 with a LUT
|
||||
if (depth == CV_8U)
|
||||
{
|
||||
Mat lut(1, 256, CV_8U);
|
||||
uchar* p = lut.ptr<uchar>();
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
double fn = std::pow(i / 255.0, gamma) * 255.0;
|
||||
p[i] = cv::saturate_cast<uchar>(fn + 0.5);
|
||||
}
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
cv::LUT(src, lut, dst);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat f;
|
||||
src.convertTo(f, CV_64F, 1.0 / maxVal);
|
||||
cv::pow(f, gamma, f);
|
||||
|
||||
_dst.create(src.size(), src.type());
|
||||
Mat dst = _dst.getMat();
|
||||
f.convertTo(dst, src.type(), maxVal);
|
||||
}
|
||||
|
||||
|
||||
Mat maskCopyTo(const Mat& src, const Mat& mask)
|
||||
{
|
||||
Mat fullMasked;
|
||||
src.copyTo(fullMasked, mask);
|
||||
|
||||
std::vector<Point> nonZeroLocations;
|
||||
findNonZero(mask, nonZeroLocations);
|
||||
|
||||
Mat dst(static_cast<int>(nonZeroLocations.size()), 1, src.type());
|
||||
|
||||
int channels = src.channels();
|
||||
if (channels == 1)
|
||||
{
|
||||
for (size_t i = 0; i < nonZeroLocations.size(); i++)
|
||||
{
|
||||
dst.at<double>(static_cast<int>(i), 0) = fullMasked.at<double>(nonZeroLocations[i]);
|
||||
}
|
||||
}
|
||||
else if (channels == 3)
|
||||
{
|
||||
for (size_t i = 0; i < nonZeroLocations.size(); i++)
|
||||
{
|
||||
dst.at<Vec3d>(static_cast<int>(i), 0) = fullMasked.at<Vec3d>(nonZeroLocations[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsBadArg, "Unsupported number of channels");
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
Mat multiple(const Mat& xyz, const Mat& ccm)
|
||||
{
|
||||
Mat tmp = xyz.reshape(1, xyz.rows * xyz.cols);
|
||||
Mat res = tmp * ccm;
|
||||
res = res.reshape(res.cols, xyz.rows);
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat saturate(Mat& src, double low, double up)
|
||||
{
|
||||
CV_Assert(src.type() == CV_64FC3);
|
||||
Scalar lower_bound(low, low, low);
|
||||
Scalar upper_bound(up, up, up);
|
||||
|
||||
Mat mask;
|
||||
inRange(src, lower_bound, upper_bound, mask);
|
||||
mask /= 255;
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
@@ -0,0 +1,145 @@
|
||||
// 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.
|
||||
//
|
||||
// Author: Longbu Wang <wanglongbu@huawei.com.com>
|
||||
// Jinheng Zhang <zhangjinheng1@huawei.com>
|
||||
// Chenqi Shan <shanchenqi@huawei.com>
|
||||
|
||||
#ifndef __OPENCV_CCM_UTILS_HPP__
|
||||
#define __OPENCV_CCM_UTILS_HPP__
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
namespace cv {
|
||||
namespace ccm {
|
||||
/** @brief gamma correction.
|
||||
\f[
|
||||
C_l=C_n^{\gamma},\qquad C_n\ge0\\
|
||||
C_l=-(-C_n)^{\gamma},\qquad C_n<0\\\\
|
||||
\f]
|
||||
@param src the input array,type of Mat.
|
||||
@param gamma a constant for gamma correction greater than zero.
|
||||
@param dst the output array, type of Mat.
|
||||
*/
|
||||
CV_EXPORTS_W void gammaCorrection(InputArray src, OutputArray dst, 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.
|
||||
*/
|
||||
Mat maskCopyTo(const Mat& src, const Mat& mask);
|
||||
|
||||
/** @brief multiple the function used to compute an array with n channels
|
||||
mulipied by ccm.
|
||||
@param xyz the input array, type of Mat.
|
||||
@param ccm the ccm matrix to make color correction.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
Mat saturate(Mat& src, double low, double up);
|
||||
|
||||
/** @brief function for elementWise operation
|
||||
@param src the input array, type of Mat
|
||||
@param lambda a for operation
|
||||
*/
|
||||
template <typename F>
|
||||
Mat elementWise(const Mat& src, F&& lambda, Mat dst=Mat())
|
||||
{
|
||||
if (dst.empty() || !dst.isContinuous() || dst.total() != src.total() || dst.type() != src.type())
|
||||
dst = Mat(src.rows, src.cols, src.type());
|
||||
const int channel = src.channels();
|
||||
if (src.isContinuous()) {
|
||||
const int num_elements = (int)src.total()*channel;
|
||||
const double *psrc = (double*)src.data;
|
||||
double *pdst = (double*)dst.data;
|
||||
const int batch = getNumThreads() > 1 ? 128 : num_elements;
|
||||
const int N = (num_elements / batch) + ((num_elements % batch) > 0);
|
||||
parallel_for_(Range(0, N),[&](const Range& range) {
|
||||
const int start = range.start * batch;
|
||||
const int end = std::min(range.end*batch, num_elements);
|
||||
for (int i = start; i < end; i++) {
|
||||
pdst[i] = lambda(psrc[i]);
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
switch (channel)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
|
||||
MatIterator_<double> it, end;
|
||||
for (it = dst.begin<double>(), end = dst.end<double>(); it != end; ++it)
|
||||
{
|
||||
(*it) = lambda((*it));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
MatIterator_<Vec3d> it, end;
|
||||
for (it = dst.begin<Vec3d>(), end = dst.end<Vec3d>(); it != end; ++it)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
(*it)[j] = lambda((*it)[j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Wrong channel!" );
|
||||
break;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** @brief function for channel operation
|
||||
@param src the input array, type of Mat
|
||||
@param lambda the function for operation
|
||||
*/
|
||||
template <typename F>
|
||||
Mat channelWise(const Mat& src, F&& lambda)
|
||||
{
|
||||
Mat dst = src.clone();
|
||||
MatIterator_<Vec3d> it, end;
|
||||
for (it = dst.begin<Vec3d>(), end = dst.end<Vec3d>(); it != end; ++it)
|
||||
{
|
||||
*it = lambda(*it);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/** @brief function for distance operation.
|
||||
@param src the input array, type of Mat.
|
||||
@param ref another input array, type of Mat.
|
||||
@param lambda the computing method for distance .
|
||||
*/
|
||||
template <typename F>
|
||||
Mat distanceWise(Mat& src, Mat& ref, F&& lambda)
|
||||
{
|
||||
Mat dst = Mat(src.size(), CV_64FC1);
|
||||
MatIterator_<Vec3d> it_src = src.begin<Vec3d>(), end_src = src.end<Vec3d>(),
|
||||
it_ref = ref.begin<Vec3d>();
|
||||
MatIterator_<double> it_dst = dst.begin<double>();
|
||||
for (; it_src != end_src; ++it_src, ++it_ref, ++it_dst)
|
||||
{
|
||||
*it_dst = lambda(*it_src, *it_ref);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
Mat multiple(const Mat& xyz, const Mat& ccm);
|
||||
|
||||
}
|
||||
} // namespace cv::ccm
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user