Skip to content

Commit

Permalink
Merge pull request #1433 from alicevision/dev/paramColorTempInImagePr…
Browse files Browse the repository at this point in the history
…ocessing

ColorTemperatureAsImageProcessingParameter
  • Loading branch information
cbentejac authored Jun 9, 2023
2 parents 4d62ed9 + 8999e61 commit c1f79a9
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 39 deletions.
95 changes: 70 additions & 25 deletions src/aliceVision/image/dcp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1768,7 +1768,7 @@ void DCPProfile::setChromaticityCoordinates(const double x, const double y, doub
du /= len;
dv /= len;

tint = (uu * du + vv * dv) * 3000.f;
tint = (uu * du + vv * dv) * (-3000.f);

break;
}
Expand Down Expand Up @@ -2164,23 +2164,60 @@ DCPProfile::Matrix DCPProfile::getCameraToSrgbLinearMatrix(const double x, const
return cameraToSrgbLinear;
}

DCPProfile::Matrix DCPProfile::getCameraToACES2065Matrix(const Triple& asShotNeutral, const bool sourceIsRaw, const bool useColorMatrixOnly) const
DCPProfile::Matrix DCPProfile::getCameraToACES2065Matrix(const Triple& asShotNeutral, double& cct, const bool sourceIsRaw, const bool useColorMatrixOnly) const
{
const Triple asShotNeutralInv = { 1.0 / asShotNeutral[0] , 1.0 / asShotNeutral[1] , 1.0 / asShotNeutral[2] };
Triple cctNeutral = { 1.0, 1.0, 1.0 };

double cctLocal, tintLocal;
Matrix invNeutralFromCct = IdentityMatrix;
double x, y;
getChromaticityCoordinatesFromCameraNeutral(IdentityMatrix, asShotNeutralInv, x, y);
double cct, tint;
setChromaticityCoordinates(x, y, cct, tint);

ALICEVISION_LOG_INFO("Estimated illuminant (cct; tint) : (" << cct << "; " << tint << ")");

Matrix neutral = IdentityMatrix;
if (sourceIsRaw)

if (cct <= 0.0)
{
neutral[0][0] = asShotNeutral[0];
neutral[1][1] = asShotNeutral[1];
neutral[2][2] = asShotNeutral[2];
getColorTemperatureAndTintFromNeutral(asShotNeutral, cctLocal, tintLocal);
getChromaticityCoordinates(cctLocal, tintLocal, x, y);
cct = cctLocal;
ALICEVISION_LOG_TRACE("Estimated illuminant (cct; tint): (" << cctLocal << "; " << tintLocal << ")");
if (sourceIsRaw)
{
// set neutral value from asShot metadata
neutral[0][0] = asShotNeutral[0];
neutral[1][1] = asShotNeutral[1];
neutral[2][2] = asShotNeutral[2];
}
}
else
{
cctLocal = cct;
tintLocal = 0.0;
getChromaticityCoordinates(cctLocal, tintLocal, x, y);
if (!useColorMatrixOnly && info.has_forward_matrix_1)
{
// compute neutral from cct
Triple xyz = getXyzFromChromaticityCoordinates(x, y);
Matrix xyzToCamera = getInterpolatedMatrix(cct, "color");
cctNeutral = matMult(xyzToCamera, xyz);
cctNeutral[0] = cctNeutral[1] / cctNeutral[0];
cctNeutral[1] = 1.0;
cctNeutral[2] = cctNeutral[1] / cctNeutral[2];
ALICEVISION_LOG_TRACE("Estimated neutral from cct: " << cctNeutral);
// The forward matrix will be used. Data must be white balanced before applying the matrix.
if (sourceIsRaw)
{
// set neutral value from cct
neutral[0][0] = cctNeutral[0];
neutral[1][1] = cctNeutral[1];
neutral[2][2] = cctNeutral[2];
}
else
{
// replace asShot neutral value with cct neutral value
neutral[0][0] = cctNeutral[0] / asShotNeutral[0];
neutral[1][1] = cctNeutral[1] / asShotNeutral[1];
neutral[2][2] = cctNeutral[2] / asShotNeutral[2];
}
}
}

Matrix cameraToXyzD50 = IdentityMatrix;
Expand All @@ -2190,7 +2227,7 @@ DCPProfile::Matrix DCPProfile::getCameraToACES2065Matrix(const Triple& asShotNeu
Matrix xyzToCamera = IdentityMatrix;
if (info.has_color_matrix_1 && info.has_color_matrix_2)
{
xyzToCamera = getInterpolatedMatrix(cct, "color");
xyzToCamera = getInterpolatedMatrix(cctLocal, "color");
}
else if (info.has_color_matrix_1)
{
Expand All @@ -2202,9 +2239,9 @@ DCPProfile::Matrix DCPProfile::getCameraToACES2065Matrix(const Triple& asShotNeu
{
// White balancing has been applied before demosaicing but color matrix is supposed to work on non white balanced data
// The white balance operation must be reversed
wbInv[0][0] = asShotNeutralInv[0];
wbInv[1][1] = asShotNeutralInv[1];
wbInv[2][2] = asShotNeutralInv[2];
wbInv[0][0] = 1.0 / asShotNeutral[0];
wbInv[1][1] = 1.0 / asShotNeutral[1];
wbInv[2][2] = 1.0 / asShotNeutral[2];
}
const Matrix cameraToXyz = matMult(matInv(xyzToCamera), wbInv);

Expand All @@ -2216,14 +2253,14 @@ DCPProfile::Matrix DCPProfile::getCameraToACES2065Matrix(const Triple& asShotNeu
}
else if ((info.has_forward_matrix_1) && (info.has_forward_matrix_2))
{
cameraToXyzD50 = matMult(getInterpolatedMatrix(cct, "forward"), neutral);
cameraToXyzD50 = matMult(getInterpolatedMatrix(cctLocal, "forward"), neutral);
}
else if (info.has_forward_matrix_1)
{
cameraToXyzD50 = matMult(forward_matrix_1, neutral);
}

ALICEVISION_LOG_INFO("cameraToXyzD50Matrix : " << cameraToXyzD50);
ALICEVISION_LOG_TRACE("cameraToXyzD50Matrix: " << cameraToXyzD50);

Matrix cameraToACES2065 = matMult(xyzD50ToACES2065Matrix, cameraToXyzD50);

Expand Down Expand Up @@ -2341,11 +2378,11 @@ void DCPProfile::setMatricesFromStrings(const std::string& type, std::vector<std
setMatrices(type, v_Mat);
}

void DCPProfile::applyLinear(OIIO::ImageBuf& image, const Triple& neutral, const bool sourceIsRaw, const bool useColorMatrixOnly) const
void DCPProfile::applyLinear(OIIO::ImageBuf& image, const Triple& neutral, double& cct, const bool sourceIsRaw, const bool useColorMatrixOnly) const
{
const Matrix cameraToACES2065Matrix = getCameraToACES2065Matrix(neutral, sourceIsRaw, useColorMatrixOnly);
const Matrix cameraToACES2065Matrix = getCameraToACES2065Matrix(neutral, cct, sourceIsRaw, useColorMatrixOnly);

ALICEVISION_LOG_INFO("cameraToACES2065Matrix : " << cameraToACES2065Matrix);
ALICEVISION_LOG_INFO("cameraToACES2065Matrix: " << cameraToACES2065Matrix);

#pragma omp parallel for
for (int i = 0; i < image.spec().height; ++i)
Expand All @@ -2367,11 +2404,11 @@ void DCPProfile::applyLinear(OIIO::ImageBuf& image, const Triple& neutral, const
}
}

void DCPProfile::applyLinear(Image<image::RGBAfColor>& image, const Triple& neutral, const bool sourceIsRaw, const bool useColorMatrixOnly) const
void DCPProfile::applyLinear(Image<image::RGBAfColor>& image, const Triple& neutral, double& cct, const bool sourceIsRaw, const bool useColorMatrixOnly) const
{
const Matrix cameraToACES2065Matrix = getCameraToACES2065Matrix(neutral, sourceIsRaw, useColorMatrixOnly);
const Matrix cameraToACES2065Matrix = getCameraToACES2065Matrix(neutral, cct, sourceIsRaw, useColorMatrixOnly);

ALICEVISION_LOG_INFO("cameraToACES2065Matrix : " << cameraToACES2065Matrix);
ALICEVISION_LOG_INFO("cameraToACES2065Matrix: " << cameraToACES2065Matrix);

#pragma omp parallel for
for (int i = 0; i < image.Height(); ++i)
Expand All @@ -2392,6 +2429,14 @@ void DCPProfile::applyLinear(Image<image::RGBAfColor>& image, const Triple& neut
}
}

void DCPProfile::getColorTemperatureAndTintFromNeutral(const Triple& neutral, double& cct, double& tint) const
{
const Triple invNeutral = { 1.0/neutral[0], 1.0/neutral[1], 1.0/neutral[2]};
double x, y;
getChromaticityCoordinatesFromCameraNeutral(IdentityMatrix, invNeutral, x, y);
setChromaticityCoordinates(x, y, cct, tint);
}

DCPDatabase::DCPDatabase(const std::string& databaseDirPath)
{
load(databaseDirPath, true);
Expand Down
16 changes: 13 additions & 3 deletions src/aliceVision/image/dcp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,19 +206,21 @@ class DCPProfile final
* @brief applyLinear applies the linear part of a DCP profile on an OIIO image buffer
* param[in] image The OIIO image on which the profile must be applied
* param[in] neutral The neutral value calculated from the camera multiplicators contained in the cam_mul OIIO metadata
* param[in] cct indicates the scene illuminant Correlated Color Temperature in Kelvin (if <= 0.0, the value extracted from metadata is used)
* param[in] sourceIsRaw indicates that the image buffer contains data in raw space (no neutralization <=> cam_mul not applied)
* param[in] useColorMatrixOnly indicates to apply a DCP profile computed only from the color matrices
*/
void applyLinear(OIIO::ImageBuf& image, const Triple& neutral, const bool sourceIsRaw = false, const bool useColorMatrixOnly = true) const;
void applyLinear(OIIO::ImageBuf& image, const Triple& neutral, double& cct, const bool sourceIsRaw = false, const bool useColorMatrixOnly = true) const;

/**
* @brief applyLinear applies the linear part of a DCP profile on an aliceVision image
* param[in] image The aliceVision image on which the profile must be applied
* param[in] neutral The neutral value calculated from the camera multiplicators contained in the cam_mul OIIO metadata
* param[in] cct indicates the scene illuminant Correlated Color Temperature in Kelvin (if <= 0.0, the value extracted from metadata is used)
* param[in] sourceIsRaw indicates that the image buffer contains data in raw space (no neutralization <=> cam_mul not applied)
* param[in] useColorMatrixOnly indicates to apply a DCP profile computed only from the color matrices
*/
void applyLinear(Image<image::RGBAfColor>& image, const Triple& neutral, const bool sourceIsRaw = false, const bool useColorMatrixOnly = false) const;
void applyLinear(Image<image::RGBAfColor>& image, const Triple& neutral, double& cct, const bool sourceIsRaw = false, const bool useColorMatrixOnly = false) const;

/**
* @brief apply applies the non linear part of a DCP profile on an OIIO image buffer
Expand All @@ -233,6 +235,14 @@ class DCPProfile final
*/
void apply(float* rgb, const DCPProfileApplyParams& params) const;

/**
* @brief compute color temperature and tint from neutral
* param[in] neutral The neutral triplet value
* param[out] cct The computed correlated color temperature value in Kelvin
* param[out] tint The computed tint value
*/
void getColorTemperatureAndTintFromNeutral(const Triple& neutral, double& cct, double& tint) const;

private:
struct HsbModify
{
Expand Down Expand Up @@ -278,7 +288,7 @@ class DCPProfile final
Matrix getChromaticAdaptationMatrix(const Triple& xyzSource, const Triple& xyzTarget) const;
Matrix getCameraToXyzD50Matrix(const double x, const double y) const;
Matrix getCameraToSrgbLinearMatrix(const double x, const double y) const;
Matrix getCameraToACES2065Matrix(const Triple& asShotNeutral, const bool sourceIsRaw = false, const bool useColorMatrixOnly = false) const;
Matrix getCameraToACES2065Matrix(const Triple& asShotNeutral, double& cct, const bool sourceIsRaw = false, const bool useColorMatrixOnly = false) const;

Matrix ws_sRGB; // working color space to sRGB
Matrix sRGB_ws; // sRGB to working color space
Expand Down
8 changes: 6 additions & 2 deletions src/aliceVision/image/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,9 @@ void readImage(const std::string& path,

ALICEVISION_LOG_TRACE("Apply DCP Linear processing with neutral = " << neutral);

dcpProfile.applyLinear(inBuf, neutral, imageReadOptions.doWBAfterDemosaicing, imageReadOptions.useDCPColorMatrixOnly);
double cct = imageReadOptions.correlatedColorTemperature;

dcpProfile.applyLinear(inBuf, neutral, cct, imageReadOptions.doWBAfterDemosaicing, imageReadOptions.useDCPColorMatrixOnly);
}

// color conversion
Expand Down Expand Up @@ -734,7 +736,9 @@ void readImage(const std::string& path,
neutral[i] = v_mult[i] / v_mult[1];
}

dcpProf.applyLinear(inBuf, neutral, imageReadOptions.doWBAfterDemosaicing, imageReadOptions.useDCPColorMatrixOnly);
double cct = imageReadOptions.correlatedColorTemperature;

dcpProf.applyLinear(inBuf, neutral, cct, imageReadOptions.doWBAfterDemosaicing, imageReadOptions.useDCPColorMatrixOnly);
fromColorSpaceName = "aces2065-1";
}

Expand Down
4 changes: 3 additions & 1 deletion src/aliceVision/image/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ struct ImageReadOptions
ERawColorInterpretation rawColorInterpretation = ERawColorInterpretation::LibRawWhiteBalancing,
const std::string& colorProfile = "", const bool useDCPColorMatrixOnly = true, const oiio::ROI& roi = oiio::ROI()) :
workingColorSpace(colorSpace), rawColorInterpretation(rawColorInterpretation), colorProfileFileName(colorProfile), useDCPColorMatrixOnly(useDCPColorMatrixOnly),
doWBAfterDemosaicing(false), demosaicingAlgo("AHD"), highlightMode(0), rawAutoBright(false), rawExposureAdjustment(1.0), subROI(roi)
doWBAfterDemosaicing(false), demosaicingAlgo("AHD"), highlightMode(0), rawAutoBright(false), rawExposureAdjustment(1.0),
correlatedColorTemperature(-1.0), subROI(roi)
{
}

Expand All @@ -232,6 +233,7 @@ struct ImageReadOptions
int highlightMode;
bool rawAutoBright;
float rawExposureAdjustment;
double correlatedColorTemperature;
//ROI for this image.
//If the image contains an roi, this is the roi INSIDE the roi.
oiio::ROI subROI;
Expand Down
3 changes: 2 additions & 1 deletion src/software/pipeline/main_panoramaPostProcessing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ void colorSpaceTransform(image::Image<image::RGBAfColor>& inputImage, image::EIm

if (fromColorSpace == image::EImageColorSpace::NO_CONVERSION)
{
dcpProf.applyLinear(inBuf, neutral, true, true);
double cct;
dcpProf.applyLinear(inBuf, neutral, cct, true, true);
fromColorSpace = image::EImageColorSpace::ACES2065_1;
}

Expand Down
45 changes: 38 additions & 7 deletions src/software/utils/main_imageProcessing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
// These constants define the current software version.
// They must be updated when the command line is changed.
#define ALICEVISION_SOFTWARE_VERSION_MAJOR 3
#define ALICEVISION_SOFTWARE_VERSION_MINOR 1
#define ALICEVISION_SOFTWARE_VERSION_MINOR 2

using namespace aliceVision;
namespace po = boost::program_options;
Expand Down Expand Up @@ -320,6 +320,8 @@ struct ProcessingParams
bool fixNonFinite = false;
bool applyDcpMetadata = false;
bool useDCPColorMatrixOnly = false;
bool sourceIsRaw = false;
double correlatedColorTemperature = -1.0;

LensCorrectionParams lensCorrection =
{
Expand Down Expand Up @@ -402,7 +404,7 @@ void undistortVignetting(aliceVision::image::Image<aliceVision::image::RGBAfColo
}
}

void processImage(image::Image<image::RGBAfColor>& image, const ProcessingParams& pParams, const std::map<std::string, std::string>& imageMetadata, const camera::IntrinsicBase* cam = NULL)
void processImage(image::Image<image::RGBAfColor>& image, const ProcessingParams& pParams, std::map<std::string, std::string>& imageMetadata, const camera::IntrinsicBase* cam = NULL)
{
const unsigned int nchannels = 4;

Expand Down Expand Up @@ -598,8 +600,7 @@ void processImage(image::Image<image::RGBAfColor>& image, const ProcessingParams
#endif
}


if (pParams.applyDcpMetadata)
if (pParams.applyDcpMetadata || (pParams.sourceIsRaw && pParams.correlatedColorTemperature <= 0.0))
{
bool dcpMetadataOK = map_has_non_empty_value(imageMetadata, "AliceVision:DCP:Temp1") &&
map_has_non_empty_value(imageMetadata, "AliceVision:DCP:Temp2") &&
Expand Down Expand Up @@ -674,7 +675,24 @@ void processImage(image::Image<image::RGBAfColor>& image, const ProcessingParams
neutral[i] = v_mult[i] / v_mult[1];
}

dcpProf.applyLinear(image, neutral, true, pParams.useDCPColorMatrixOnly);
double cct = pParams.correlatedColorTemperature;
double tint;

if (pParams.sourceIsRaw)
{
dcpProf.getColorTemperatureAndTintFromNeutral(neutral, cct, tint);
}

if (pParams.applyDcpMetadata)
{
dcpProf.applyLinear(image, neutral, cct, true, pParams.useDCPColorMatrixOnly);
}

imageMetadata["AliceVision:ColorTemperature"] = std::to_string(cct);
}
else if (pParams.sourceIsRaw && pParams.correlatedColorTemperature > 0.0)
{
imageMetadata["AliceVision:ColorTemperature"] = std::to_string(pParams.correlatedColorTemperature);
}
}

Expand Down Expand Up @@ -784,6 +802,7 @@ int aliceVision_main(int argc, char * argv[])
bool doWBAfterDemosaicing = false;
std::string demosaicingAlgo = "AHD";
int highlightMode = 0;
double correlatedColorTemperature = -1;

ProcessingParams pParams;

Expand Down Expand Up @@ -918,6 +937,10 @@ int aliceVision_main(int argc, char * argv[])
"Highlight management (see libRaw documentation).\n"
"0 = clip (default), 1 = unclip, 2 = blend, 3+ = rebuild.")

("correlatedColorTemperature", po::value<double>(&correlatedColorTemperature)->default_value(correlatedColorTemperature),
"Correlated Color Temperature in Kelvin of scene illuminant.\n"
"If less than or equal to 0.0, the value extracted from the metadata will be used.")

("storageDataType", po::value<image::EStorageDataType>(&storageDataType)->default_value(storageDataType),
("Storage data type: " + image::EStorageDataType_informations()).c_str())

Expand Down Expand Up @@ -1083,6 +1106,9 @@ int aliceVision_main(int argc, char * argv[])
options.highlightMode = highlightMode;
options.rawExposureAdjustment = std::pow(2.f, pParams.rawExposureAdjust);
options.rawAutoBright = pParams.rawAutoBright;
options.correlatedColorTemperature = correlatedColorTemperature;
pParams.correlatedColorTemperature = correlatedColorTemperature;
pParams.sourceIsRaw = true;
}

if (pParams.lensCorrection.enabled && pParams.lensCorrection.vignetting)
Expand Down Expand Up @@ -1119,8 +1145,10 @@ int aliceVision_main(int argc, char * argv[])
sfmData::Intrinsics::const_iterator iterIntrinsic = sfmData.getIntrinsics().find(view.getIntrinsicId());
const camera::IntrinsicBase* cam = iterIntrinsic->second.get();

std::map<std::string, std::string> viewMetadata = view.getMetadata();

// Image processing
processImage(image, pParams, view.getMetadata(), cam);
processImage(image, pParams, viewMetadata, cam);

if (pParams.applyDcpMetadata)
{
Expand All @@ -1143,7 +1171,7 @@ int aliceVision_main(int argc, char * argv[])
}

// Save the image
saveImage(image, viewPath, outputfilePath, view.getMetadata(), metadataFolders, outputFormat, writeOptions);
saveImage(image, viewPath, outputfilePath, viewMetadata, metadataFolders, outputFormat, writeOptions);

// Update view for this modification
view.setImagePath(outputfilePath);
Expand Down Expand Up @@ -1321,6 +1349,9 @@ int aliceVision_main(int argc, char * argv[])
readOptions.highlightMode = highlightMode;
readOptions.rawExposureAdjustment = std::pow(2.f, pParams.rawExposureAdjust);
readOptions.rawAutoBright = pParams.rawAutoBright;
readOptions.correlatedColorTemperature = correlatedColorTemperature;
pParams.correlatedColorTemperature = correlatedColorTemperature;
pParams.sourceIsRaw = true;

pParams.useDCPColorMatrixOnly = useDCPColorMatrixOnly;
if (pParams.applyDcpMetadata)
Expand Down

0 comments on commit c1f79a9

Please sign in to comment.