If Lightroom takes ICC files as camera input profiles, making one from the camconst dcraw_matrix can be done. I do it in my rawproc raw processor, but I donāt have code there to write it out to a file. Your inquiry prompted me to finish something Iāve been meaning to do, capture that code in a command-line program.
Just wrote and tested it, adobecoeff2icc.cpp. It uses the LittleCMS color management library, the psuedoinverse function from dcraw.c, and some helper functions from rawproc. Hereās the entire program:
#include <string>
#include <vector>
#include <algorithm>
#include <lcms2.h>
static cmsCIEXYZ d65_media_whitepoint = {0.95045471, 1.0, 1.08905029};
std::vector<std::string> split(std::string s, std::string delim)
{
std::vector<std::string> v;
if (s.find(delim) == std::string::npos) {
v.push_back(s);
return v;
}
size_t pos=0;
size_t start;
while (pos < s.length()) {
start = pos;
pos = s.find(delim,pos);
if (pos == std::string::npos) {
v.push_back(s.substr(start,s.length()-start));
return v;
}
v.push_back(s.substr(start, pos-start));
pos += delim.length();
}
return v;
}
//psuedoinverse, from dcraw.c:
void pseudoinverse (double (*in)[3], double (*out)[3], int size)
{
double work[3][6], num;
int i, j, k;
for (i=0; i < 3; i++) {
for (j=0; j < 6; j++)
work[i][j] = j == i+3;
for (j=0; j < 3; j++)
for (k=0; k < size; k++)
work[i][j] += in[k][i] * in[k][j];
}
for (i=0; i < 3; i++) {
num = work[i][i];
for (j=0; j < 6; j++)
work[i][j] /= num;
for (k=0; k < 3; k++) {
if (k==i) continue;
num = work[k][i];
for (j=0; j < 6; j++)
work[k][j] -= work[i][j] * num;
}
}
for (i=0; i < size; i++)
for (j=0; j < 3; j++)
for (out[i][j]=k=0; k < 3; k++)
out[i][j] += work[j][k+3] * in[i][k];
}
const cmsCIExyY cmsCIEXYZ2cmsCIExyY(cmsCIEXYZ in)
{
cmsCIExyY out;
double s = in.X+in.Y+in.Z;
out.x = in.X/s;
out.y = in.Y/s;
out.Y = in.Y;
return out;
}
// make a linear D65 profile from a dcraw adobe_coeff entry,
// e.g., D7000: 8198,-2239,-724,-4871,12389,2798,-1043,2050,7181
// also takes a json array, e.g., [ 8198, -2239, -724, -4871, 12389, 2798, -1043, 2050, 7181 ]
cmsHPROFILE makeLCMSAdobeCoeffProfile(std::string adobecoeff)
{
double in_XYZ[3][3], inverse[3][3], out_XYZ[3][3];
//if json, remove the extraneous characters;
adobecoeff.erase(std::remove(adobecoeff.begin(), adobecoeff.end(), ' '), adobecoeff.end());
adobecoeff.erase(std::remove(adobecoeff.begin(), adobecoeff.end(), '['), adobecoeff.end());
adobecoeff.erase(std::remove(adobecoeff.begin(), adobecoeff.end(), ']'), adobecoeff.end());
std::vector<std::string> mat = split(adobecoeff, ",");
for (unsigned i=0; i<3; i++) {
for (unsigned j=0; j<3; j++) {
unsigned pos = i*3+j;
float coeff = atof(mat[pos].c_str());
if (pos < mat.size()) {
if (abs(coeff) > 10.0)
in_XYZ[i][j] = coeff/10000.0;
else
in_XYZ[i][j] = coeff;
}
else {
in_XYZ[i][j] = 0.0;
}
}
}
pseudoinverse(in_XYZ, inverse, 3);
//because pseudoinverse delivers it rotated:
for (unsigned i=0; i<3; i++)
for (unsigned j=0; j<3; j++)
out_XYZ[i][j] = inverse[j][i];
cmsHPROFILE profile;
cmsCIExyYTRIPLE c;
cmsCIExyY cw;
cmsCIEXYZ p, w;
cmsToneCurve *curve[3], *tonecurve;
cw = cmsCIEXYZ2cmsCIExyY(d65_media_whitepoint);
p.X = out_XYZ[0][0]; p.Y = out_XYZ[1][0]; p.Z = out_XYZ[2][0];
c.Red = cmsCIEXYZ2cmsCIExyY(p);
p.X = out_XYZ[0][1]; p.Y = out_XYZ[1][1]; p.Z = out_XYZ[2][1];
c.Green = cmsCIEXYZ2cmsCIExyY(p);
p.X = out_XYZ[0][2]; p.Y = out_XYZ[1][2]; p.Z = out_XYZ[2][2];
c.Blue = cmsCIEXYZ2cmsCIExyY(p);
tonecurve = cmsBuildGamma (NULL, 1.0); //hardcoded linear, for now...
curve[0] = curve[1] = curve[2] = tonecurve;
profile = cmsCreateRGBProfile (&cw, &c, curve);
if (profile) {
std::string descr = "adobe_coeff linear profile";
cmsMLU *description;
description = cmsMLUalloc(NULL, 1);
cmsMLUsetASCII(description, "en", "US", descr.c_str());
cmsWriteTag(profile, cmsSigProfileDescriptionTag, description);
cmsMLUfree(description);
}
return profile;
}
void makeICCProfile(cmsHPROFILE hProfile, char *& profile, cmsUInt32Number &profilesize)
{
if (hProfile != NULL) {
cmsSaveProfileToMem(hProfile, NULL, &profilesize);
profile = new char[profilesize];
cmsSaveProfileToMem(hProfile, profile, &profilesize);
}
}
void err(std::string msg)
{
printf("Error: %s\n", msg.c_str());
exit(0);
}
int main(int argc, char **argv)
{
char * profile;
cmsUInt32Number profilesize;
if (argc < 2) err("No adobe coeff string (a,b,c,d,e,f,g,h,i)");
if (argc < 3) err("No destination file name");
cmsHPROFILE cmspprof = makeLCMSAdobeCoeffProfile(std::string(argv[1]));
makeICCProfile(cmspprof, profile, profilesize);
FILE* f = fopen(argv[2], "wb");
if (f) {
fwrite(profile, profilesize, 1, f);
fclose(f);
}
}
I didnāt say it would be easy⦠