In this tutorial you will learn how to use the 'Color Correction Model' to do a color correction in a image.
Reference
See details of ColorCorrection Algorithm at https://github.com/riskiest/color_calibration/tree/v4/doc/pdf/English/Algorithm
Building
When building OpenCV, run the following command to build all the contrib modules:
cmake -D OPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules/
Or only build the mcc module:
cmake -D OPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules/mcc
Or make sure you check the mcc module in the GUI version of CMake: cmake-gui.
Source Code of the sample
The sample has two parts of code, the first is the color checker detector model, see details at Detecting colorcheckers using basic algorithms, the second part is to make collor calibration.
Here are the parameters for ColorCorrectionModel
src :
detected colors of ColorChecker patches;
NOTICE: the color type is RGB not BGR, and the color values are in [0, 1];
constcolor :
the Built-in color card;
Supported list:
Macbeth: Macbeth ColorChecker ;
Vinyl: DKK ColorChecker ;
DigitalSG: DigitalSG ColorChecker with 140 squares;
Mat colors :
the reference color values
and corresponding color space
NOTICE: the color values are in [0, 1]
ref_cs :
the corresponding color space
If the color type is some RGB, the format is RGB not BGR;
Supported Color Space:
Supported list of RGB color spaces:
COLOR_SPACE_sRGB;
COLOR_SPACE_AdobeRGB;
COLOR_SPACE_WideGamutRGB;
COLOR_SPACE_ProPhotoRGB;
COLOR_SPACE_DCI_P3_RGB;
COLOR_SPACE_AppleRGB;
COLOR_SPACE_REC_709_RGB;
COLOR_SPACE_REC_2020_RGB;
Supported list of linear RGB color spaces:
COLOR_SPACE_sRGBL;
COLOR_SPACE_AdobeRGBL;
COLOR_SPACE_WideGamutRGBL;
COLOR_SPACE_ProPhotoRGBL;
COLOR_SPACE_DCI_P3_RGBL;
COLOR_SPACE_AppleRGBL;
COLOR_SPACE_REC_709_RGBL;
COLOR_SPACE_REC_2020_RGBL;
Supported list of non-RGB color spaces:
COLOR_SPACE_Lab_D50_2;
COLOR_SPACE_Lab_D65_2;
COLOR_SPACE_XYZ_D50_2;
COLOR_SPACE_XYZ_D65_2;
COLOR_SPACE_XYZ_D65_10;
COLOR_SPACE_XYZ_D50_10;
COLOR_SPACE_XYZ_A_2;
COLOR_SPACE_XYZ_A_10;
COLOR_SPACE_XYZ_D55_2;
COLOR_SPACE_XYZ_D55_10;
COLOR_SPACE_XYZ_D75_2;
COLOR_SPACE_XYZ_D75_10;
COLOR_SPACE_XYZ_E_2;
COLOR_SPACE_XYZ_E_10;
COLOR_SPACE_Lab_D65_10;
COLOR_SPACE_Lab_D50_10;
COLOR_SPACE_Lab_A_2;
COLOR_SPACE_Lab_A_10;
COLOR_SPACE_Lab_D55_2;
COLOR_SPACE_Lab_D55_10;
COLOR_SPACE_Lab_D75_2;
COLOR_SPACE_Lab_D75_10;
COLOR_SPACE_Lab_E_2;
COLOR_SPACE_Lab_E_10;
Code
#include <iostream>
using namespace std;
using namespace mcc;
using namespace ccm;
using namespace std;
const char *about = "Basic chart detection";
const char *keys =
"{ help h | | show this message }"
"{t | | chartType: 0-Standard, 1-DigitalSG, 2-Vinyl }"
"{v | | Input from video file, if ommited, input comes from camera }"
"{ci | 0 | Camera id if input doesnt come from video (-v) }"
"{f | 1 | Path of the file to process (-v) }"
"{nc | 1 | Maximum number of charts in the image }";
int main(int argc, char *argv[])
{
parser.about(about);
if (argc==1 || parser.has("help"))
{
parser.printMessage();
return 0;
}
int t = parser.get<int>("t");
int nc = parser.get<int>("nc");
string filepath = parser.get<string>("f");
if (!parser.check())
{
parser.printErrors();
return 0;
}
{
cout << "Invalid Image!" << endl;
return 1;
}
if (!detector->process(image, chartType, nc))
{
printf("ChartColor not detected \n");
return 2;
}
vector<Ptr<mcc::CChecker>> checkers = detector->getListColorChecker();
{
cdraw->draw(image);
Mat chartsRGB = checker->getChartsRGB();
src /= 255.0;
model1.run();
Mat ccm = model1.getCCM();
std::cout<<"ccm "<<ccm<<std::endl;
double loss = model1.getLoss();
std::cout<<"loss "<<loss<<std::endl;
const int inp_size = 255;
const int out_size = 255;
img_ = img_ / inp_size;
Mat calibratedImage= model1.infer(img_);
Mat out_ = calibratedImage * out_size;
string filename = filepath.substr(filepath.find_last_of('/')+1);
size_t dotIndex = filename.find_last_of('.');
string baseName = filename.substr(0, dotIndex);
string ext = filename.substr(dotIndex+1, filename.length()-dotIndex);
string calibratedFilePath = baseName + ".calibrated." + ext;
imwrite(calibratedFilePath, out_img);
}
return 0;
}
Explanation
The first part is to detect the ColorChecker position.
vector<Ptr<mcc::CChecker>> checkers = detector->getListColorChecker();
CommandLineParser parser(argc, argv, keys);
parser.about(about);
if (argc==1 || parser.has("help"))
{
parser.printMessage();
return 0;
}
int t = parser.get<int>("t");
int nc = parser.get<int>("nc");
string filepath = parser.get<string>("f");
if (!parser.check())
{
parser.printErrors();
return 0;
}
if (!image.data)
{
cout << "Invalid Image!" << endl;
return 1;
}
Preparation for ColorChecker detection to get messages for the image.
Ptr<CCheckerDraw> cdraw = CCheckerDraw::create(checker);
cdraw->draw(image);
Mat chartsRGB = checker->getChartsRGB();
src /= 255.0;
The CCheckerDetectorobject is created and uses getListColorChecker function to get ColorChecker message.
model1.run();
Mat ccm = model1.getCCM();
std::cout<<"ccm "<<ccm<<std::endl;
double loss = model1.getLoss();
std::cout<<"loss "<<loss<<std::endl;
For every ColorChecker, we can compute a ccm matrix for color correction. Model1 is an object of ColorCorrectionModel class. The parameters should be changed to get the best effect of color correction. See other parameters' detail at the Parameters.
If you use a customized ColorChecker, you can use your own reference color values and corresponding color space as shown above.
Mat img_;
const int inp_size = 255;
const int out_size = 255;
img_ = img_ / inp_size;
Mat calibratedImage= model1.infer(img_);
Mat out_ = calibratedImage * out_size;
The member function infer_image is to make correction correction using ccm matrix.
Mat img_out =
min(
max(out_, 0), out_size);
Mat out_img;
string filename = filepath.substr(filepath.find_last_of('/')+1);
size_t dotIndex = filename.find_last_of('.');
string baseName = filename.substr(0, dotIndex);
string ext = filename.substr(dotIndex+1, filename.length()-dotIndex);
string calibratedFilePath = baseName + ".calibrated." + ext;
imwrite(calibratedFilePath, out_img);
Save the calibrated image.