vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+557
View File
@@ -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
+391
View File
@@ -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
+108
View File
@@ -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
+769
View File
@@ -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
+343
View File
@@ -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
+204
View File
@@ -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
+80
View File
@@ -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
+114
View File
@@ -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
+53
View File
@@ -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
+284
View File
@@ -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
+260
View File
@@ -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
+71
View File
@@ -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
+83
View File
@@ -0,0 +1,83 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// 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
+113
View File
@@ -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");
}
// Specialcase 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
+145
View File
@@ -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