Clipless and fast DCP color profile engine

see issue #1295
This commit is contained in:
Oliver Duis 2012-03-31 19:02:42 +02:00
parent 76064f0d2a
commit 36b86ddaf3
38 changed files with 605 additions and 36 deletions

View File

@ -4,6 +4,7 @@ file (GLOB LANGUAGEFILES "languages/*")
file (GLOB SOUNDFILES "sounds/*")
file (GLOB INPUTICCFILES "iccprofiles/input/*")
file (GLOB OUTPUTICCFILES "iccprofiles/output/*")
file (GLOB DCPFILES "dcpprofiles/*")
# THEMEDIR includes subfolders for image resources for some themes; doing the normal glob won't work.
set (THEMEDIR "themes")
set (IMAGESDIR "images")
@ -33,6 +34,7 @@ install (FILES ${PROFILEFILES} DESTINATION ${DATADIR}/profiles)
install (FILES ${SOUNDFILES} DESTINATION ${DATADIR}/sounds)
install (FILES ${INPUTICCFILES} DESTINATION ${DATADIR}/iccprofiles/input)
install (FILES ${OUTPUTICCFILES} DESTINATION ${DATADIR}/iccprofiles/output)
install (FILES ${DCPFILES} DESTINATION ${DATADIR}/dcpprofiles)
install (DIRECTORY ${THEMEDIR} DESTINATION ${DATADIR})
install (DIRECTORY ${IMAGESDIR} DESTINATION ${DATADIR} FILES_MATCHING PATTERN "index.theme")
install (DIRECTORY ${IMAGESDIR} DESTINATION ${DATADIR} FILES_MATCHING PATTERN "*.png")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -886,14 +886,14 @@ TP_HSVEQUALIZER_VAL;V
TP_ICM_BLENDCMSMATRIX;Lichter aus Matrix einmischen
TP_ICM_BLENDCMSMATRIX_TOOLTIP;Stellt bei Verwendung von LUT-basierten\nICC-Profilen die Lichter wieder her
TP_ICM_FILEDLGFILTERANY;Alle Dateien
TP_ICM_FILEDLGFILTERICM;ICC-Profildateien
TP_ICM_INPUTCAMERAICC;Kamera-Standard oder ICC
TP_ICM_INPUTCAMERAICC_TOOLTIP;RawTherapee Standard ICC-Farbprofil verwenden; präziser als einfache Matrix, aber nur für einige Kameras verfügbar
TP_ICM_FILEDLGFILTERICM;Profildateien
TP_ICM_INPUTCAMERAICC;Kamera-Standard oder DCP/ICC
TP_ICM_INPUTCAMERAICC_TOOLTIP;RawTherapee Standard DCP/ICC-Farbprofil verwenden; präziser als einfache Matrix, aber nur für einige Kameras verfügbar
TP_ICM_INPUTCAMERA;Kamera-Standard
TP_ICM_INPUTCAMERA_TOOLTIP;Einfache Standard Farbkorrektur-Matrix von DCRAW oder die im DNG eingebettete verwenden
TP_ICM_INPUTCUSTOM;Benutzerdefiniert
TP_ICM_INPUTCUSTOM_TOOLTIP;Eigene ICC-Farbprofildatei für die Kamera verwenden
TP_ICM_INPUTDLGLABEL;Wähle Eingabe-ICC-Profil...
TP_ICM_INPUTCUSTOM_TOOLTIP;Eigene DCP/ICC-Farbprofildatei für die Kamera verwenden
TP_ICM_INPUTDLGLABEL;Wähle Eingabe-DCP/ICC-Profil...
TP_ICM_INPUTEMBEDDED;Eingebettetes verwenden, falls möglich
TP_ICM_INPUTEMBEDDED_TOOLTIP;Farbprofil verwenden, das ggf. in nicht-RAW Dateien eingebettet ist
TP_ICM_INPUTNONE;Kein Profil

View File

@ -917,14 +917,14 @@ TP_HSVEQUALIZER_VAL;V
TP_ICM_BLENDCMSMATRIX;Blend highlights with matrix
TP_ICM_BLENDCMSMATRIX_TOOLTIP;Enable to recover blown highlights when using LUT based ICC profiles
TP_ICM_FILEDLGFILTERANY;Any files
TP_ICM_FILEDLGFILTERICM;ICC Profile Files
TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific ICC
TP_ICM_INPUTCAMERAICC_TOOLTIP;Use RawTherapee's camera-specific ICC input profile that is more precise than a simpler matrix. Available for some cameras, these profiles are stored in /iccprofiles/input directory and are automatically retrieved based on file name matching the exact model name of the camera.
TP_ICM_FILEDLGFILTERICM;Profile files
TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific profile
TP_ICM_INPUTCAMERAICC_TOOLTIP;Use RawTherapee's camera-specific DCP or ICC input profile that is more precise than a simpler matrix. Available for some cameras, these profiles are stored in /iccprofiles/input directory and are automatically retrieved based on file name matching the exact model name of the camera.
TP_ICM_INPUTCAMERA;Camera standard
TP_ICM_INPUTCAMERA_TOOLTIP;Use simple color matrix by DCRAW, enhanced RawTherapee version (whichever is available based on camera model) or embedded in DNG.
TP_ICM_INPUTCUSTOM;Custom
TP_ICM_INPUTCUSTOM_TOOLTIP;Select your own ICC color profile file for the camera
TP_ICM_INPUTDLGLABEL;Select Input ICC Profile...
TP_ICM_INPUTCUSTOM_TOOLTIP;Select your own DCP/ICC color profile file for the camera
TP_ICM_INPUTDLGLABEL;Select Input DCP/ICC Profile...
TP_ICM_INPUTEMBEDDED;Use Embedded, if possible
TP_ICM_INPUTEMBEDDED_TOOLTIP;Use color profile embedded in non-raw files
TP_ICM_INPUTNONE;No profile

View File

@ -14,7 +14,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona
jpeg_memsrc.cc jdatasrc.cc
PF_correct_RT.cc
dirpyrLab_denoise.cc dirpyrLab_equalizer.cc dirpyr_equalizer.cc
calc_distort.cc
calc_distort.cc dcp.cc
klt/convolve.cc klt/error.cc klt/klt.cc klt/klt_util.cc klt/pnmio.cc klt/pyramid.cc klt/selectGoodFeatures.cc
klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc
)

458
rtengine/dcp.cc Normal file
View File

@ -0,0 +1,458 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2012 Oliver Duis <www.oliverduis.de>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dcp.h"
#include <cstring>
#include "safegtk.h"
#include "iccmatrices.h"
#include "iccstore.h"
#include "rawimagesource.h"
#include "improcfun.h"
using namespace rtengine;
using namespace rtexif;
#undef CLIP
#define MAXVAL 0xffff
#define CLIP(a) ((a)>0?((a)<MAXVAL?(a):MAXVAL):0)
DCPProfile::DCPProfile(Glib::ustring fname) {
const int TagColorMatrix1=50721, TagColorMatrix2=50722, TagProfileHueSatMapDims=50937;
const int TagProfileHueSatMapData1=50938, TagProfileHueSatMapData2=50939;
const int TagCalibrationIlluminant1=50778, TagCalibrationIlluminant2=50779;
const int TagProfileLookTableData=50982, TagProfileLookTableDims=50981; // ProfileLookup is the low quality variant
aDeltas=NULL; iHueDivisions=iSatDivisions=iValDivisions=iArrayCount=0;
FILE *pFile = safe_g_fopen(fname, "rb");
TagDirectory *tagDir=ExifManager::parseTIFF(pFile, false);
// If there are two profiles, check what is the best target to take
// We don't mix the profiles as adobe does, since with more and more non-tungsten light
// it makes no sense. Take the daylight reference light
Tag* tag = tagDir->getTag(TagCalibrationIlluminant2);
bool use2nd = (tag!=NULL && tag->toInt(0,SHORT)>=20 && tag->toInt(0,SHORT)<=23);
// Color Matrix
tag = tagDir->getTag( use2nd ? TagColorMatrix2 : TagColorMatrix1);
for (int row=0;row<3;row++) {
for (int col=0;col<3;col++) {
mColorMatrix[col][row]=(float)tag->toDouble((col+row*3)*8);
}
}
// LUT profile? Divisions counts
bool useSimpleLookup=false;
tag = tagDir->getTag(TagProfileHueSatMapDims);
if (tag==NULL) {
tag=tagDir->getTag(TagProfileLookTableDims);
useSimpleLookup=true;
}
if (tag!=NULL) {
iHueDivisions=tag->toInt(0); iSatDivisions=tag->toInt(4); iValDivisions=tag->toInt(8);
// Saturation maps. Need to be unwinded.
tag = tagDir->getTag(useSimpleLookup ? TagProfileLookTableData : ( use2nd ? TagProfileHueSatMapData2 : TagProfileHueSatMapData1));
iArrayCount = tag->getCount()/3;
aDeltas=new HSBModify[iArrayCount];
const int TIFFFloatSize=4;
for (int i=0;i<iArrayCount;i++) {
aDeltas[i].fHueShift=tag->toDouble((i*3)*TIFFFloatSize);
aDeltas[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize);
aDeltas[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize);
}
}
if (pFile!=NULL) fclose(pFile);
delete tagDir;
if (iArrayCount>0) {
// Convert DNG color matrix to xyz_cam compatible matrix
int i,j,k;
double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (i=0; i<3; i++)
for (j=0; j<3; j++)
for (k=0; k<3; k++)
cam_xyz[i][j] += mColorMatrix[j][k] * (i==k);
// Multiply out XYZ colorspace
double cam_rgb[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (i=0; i < 3; i++)
for (j=0; j < 3; j++)
for (k=0; k < 3; k++)
cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j];
// Normalize cam_rgb so that: cam_rgb * (1,1,1) is (1,1,1,1)
double num;
for (i=0; i<3; i++) {
for (num=j=0; j<3; j++) num += cam_rgb[i][j];
for (j=0; j<3; j++) cam_rgb[i][j] /= num;
}
double rgb_cam[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
RawImageSource::inverse33 (cam_rgb, rgb_cam);
memset(mXYZCAM,0,sizeof(mXYZCAM));
for (i=0; i<3; i++)
for (j=0; j<3; j++)
for (k=0; k<3; k++)
mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j];
}
}
DCPProfile::~DCPProfile() {
delete[] aDeltas;
}
void DCPProfile::Apply(Imagefloat *pImg, Glib::ustring workingSpace) const {
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
if (iArrayCount==0) {
//===== No LUT- Calculate matrix for direct conversion raw>working space
double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<3; k++)
mat[i][j] += mWork[i][k] * mXYZCAM[k][j];
// Apply the matrix part
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb;
for (int x=0; x<pImg->width; x++) {
newr = mat[0][0]*pImg->r[y][x] + mat[0][1]*pImg->g[y][x] + mat[0][2]*pImg->b[y][x];
newg = mat[1][0]*pImg->r[y][x] + mat[1][1]*pImg->g[y][x] + mat[1][2]*pImg->b[y][x];
newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x];
pImg->r[y][x] = newr; pImg->g[y][x] = newg; pImg->b[y][x] = newb;
}
}
}
else {
//===== LUT available- Calculate matrix for conversion raw>ProPhoto
double m2ProPhoto[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<3; k++)
m2ProPhoto[i][j] += prophoto_xyz[i][k] * mXYZCAM[k][j];
double m2Work[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<3; k++)
m2Work[i][j] += mWork[i][k] * xyz_prophoto[k][j];
// Preperations for LUT
float hScale = (iHueDivisions < 2) ? 0.0f : (iHueDivisions * (1.0f / 6.0f));
float sScale = (float) (iSatDivisions - 1);
float vScale = (float) (iValDivisions - 1);
int maxHueIndex0 = iHueDivisions - 1;
int maxSatIndex0 = iSatDivisions - 2;
int maxValIndex0 = iValDivisions - 2;
const HSBModify *tableBase = aDeltas;
int hueStep = iSatDivisions;
int valStep = iHueDivisions * hueStep;
// Convert to prophoto and apply LUT
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb, h,s,v;
for (int x=0; x<pImg->width; x++) {
newr = m2ProPhoto[0][0]*pImg->r[y][x] + m2ProPhoto[0][1]*pImg->g[y][x] + m2ProPhoto[0][2]*pImg->b[y][x];
newg = m2ProPhoto[1][0]*pImg->r[y][x] + m2ProPhoto[1][1]*pImg->g[y][x] + m2ProPhoto[1][2]*pImg->b[y][x];
newb = m2ProPhoto[2][0]*pImg->r[y][x] + m2ProPhoto[2][1]*pImg->g[y][x] + m2ProPhoto[2][2]*pImg->b[y][x];
ImProcFunctions::rgb2hsv(newr, newg, newb, h , s, v);
h*=6.f; // RT calculates in [0,1]
// Apply the HueSatMap. Ported from Adobes reference implementation
float hueShift, satScale, valScale;
if (iValDivisions < 2) // Optimize most common case of "2.5D" table.
{
float hScaled = h * hScale;
float sScaled = s * sScale;
int hIndex0 = (int) hScaled;
int sIndex0 = (int) sScaled;
sIndex0 = MIN (sIndex0, maxSatIndex0);
int hIndex1 = hIndex0 + 1;
if (hIndex0 >= maxHueIndex0)
{
hIndex0 = maxHueIndex0;
hIndex1 = 0;
}
float hFract1 = hScaled - (float) hIndex0;
float sFract1 = sScaled - (float) sIndex0;
float hFract0 = 1.0f - hFract1;
float sFract0 = 1.0f - sFract1;
const HSBModify *entry00 = tableBase + hIndex0 * hueStep +
sIndex0;
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
float hueShift0 = hFract0 * entry00->fHueShift +
hFract1 * entry01->fHueShift;
float satScale0 = hFract0 * entry00->fSatScale +
hFract1 * entry01->fSatScale;
float valScale0 = hFract0 * entry00->fValScale +
hFract1 * entry01->fValScale;
entry00++;
entry01++;
float hueShift1 = hFract0 * entry00->fHueShift +
hFract1 * entry01->fHueShift;
float satScale1 = hFract0 * entry00->fSatScale +
hFract1 * entry01->fSatScale;
float valScale1 = hFract0 * entry00->fValScale +
hFract1 * entry01->fValScale;
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
satScale = sFract0 * satScale0 + sFract1 * satScale1;
valScale = sFract0 * valScale0 + sFract1 * valScale1;
} else {
float hScaled = h * hScale;
float sScaled = s * sScale;
float vScaled = v * vScale;
int hIndex0 = (int) hScaled;
int sIndex0 = (int) sScaled;
int vIndex0 = (int) vScaled;
sIndex0 = MIN (sIndex0, maxSatIndex0);
vIndex0 = MIN (vIndex0, maxValIndex0);
int hIndex1 = hIndex0 + 1;
if (hIndex0 >= maxHueIndex0)
{
hIndex0 = maxHueIndex0;
hIndex1 = 0;
}
float hFract1 = hScaled - (float) hIndex0;
float sFract1 = sScaled - (float) sIndex0;
float vFract1 = vScaled - (float) vIndex0;
float hFract0 = 1.0f - hFract1;
float sFract0 = 1.0f - sFract1;
float vFract0 = 1.0f - vFract1;
const HSBModify *entry00 = tableBase + vIndex0 * valStep +
hIndex0 * hueStep +
sIndex0;
const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep;
const HSBModify *entry10 = entry00 + valStep;
const HSBModify *entry11 = entry01 + valStep;
float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift +
hFract1 * entry01->fHueShift) +
vFract1 * (hFract0 * entry10->fHueShift +
hFract1 * entry11->fHueShift);
float satScale0 = vFract0 * (hFract0 * entry00->fSatScale +
hFract1 * entry01->fSatScale) +
vFract1 * (hFract0 * entry10->fSatScale +
hFract1 * entry11->fSatScale);
float valScale0 = vFract0 * (hFract0 * entry00->fValScale +
hFract1 * entry01->fValScale) +
vFract1 * (hFract0 * entry10->fValScale +
hFract1 * entry11->fValScale);
entry00++;
entry01++;
entry10++;
entry11++;
float hueShift1 = vFract0 * (hFract0 * entry00->fHueShift +
hFract1 * entry01->fHueShift) +
vFract1 * (hFract0 * entry10->fHueShift +
hFract1 * entry11->fHueShift);
float satScale1 = vFract0 * (hFract0 * entry00->fSatScale +
hFract1 * entry01->fSatScale) +
vFract1 * (hFract0 * entry10->fSatScale +
hFract1 * entry11->fSatScale);
float valScale1 = vFract0 * (hFract0 * entry00->fValScale +
hFract1 * entry01->fValScale) +
vFract1 * (hFract0 * entry10->fValScale +
hFract1 * entry11->fValScale);
hueShift = sFract0 * hueShift0 + sFract1 * hueShift1;
satScale = sFract0 * satScale0 + sFract1 * satScale1;
valScale = sFract0 * valScale0 + sFract1 * valScale1;
}
hueShift *= (6.0f / 360.0f); // Convert to internal hue range.
h += hueShift;
s *= satScale; // no clipping here, we are RT float :-)
v *= valScale;
// RT range correction
if (h < 0.0f) h += 6.0f;
if (h >= 6.0f) h -= 6.0f;
h/=6.f;
ImProcFunctions::hsv2rgb( h, s, v, newr, newg, newb);
pImg->r[y][x] = m2Work[0][0]*newr + m2Work[0][1]*newg + m2Work[0][2]*newb;
pImg->g[y][x] = m2Work[1][0]*newr + m2Work[1][1]*newg + m2Work[1][2]*newb;
pImg->b[y][x] = m2Work[2][0]*newr + m2Work[2][1]*newg + m2Work[2][2]*newb;
}
}
}
}
// Integer variant is legacy, only used for thumbs. Simply take the matrix here
void DCPProfile::Apply(Image16 *pImg, Glib::ustring workingSpace) const {
TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace);
// Calculate matrix for direct conversion raw>working space
double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<3; k++)
mat[i][j] += mWork[i][k] * mXYZCAM[k][j];
// Apply the matrix part
#pragma omp parallel for
for (int y=0; y<pImg->height; y++) {
float newr, newg, newb;
for (int x=0; x<pImg->width; x++) {
newr = mat[0][0]*pImg->r[y][x] + mat[0][1]*pImg->g[y][x] + mat[0][2]*pImg->b[y][x];
newg = mat[1][0]*pImg->r[y][x] + mat[1][1]*pImg->g[y][x] + mat[1][2]*pImg->b[y][x];
newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x];
pImg->r[y][x] = CLIP((int)newr); pImg->g[y][x] = CLIP((int)newg); pImg->b[y][x] = CLIP((int)newb);
}
}
}
// Generates as singleton
DCPStore* DCPStore::getInstance()
{
static DCPStore* instance_ = 0;
if ( instance_ == 0 )
{
static Glib::Mutex smutex_;
Glib::Mutex::Lock lock(smutex_);
if ( instance_ == 0 )
{
instance_ = new DCPStore();
}
}
return instance_;
}
// Reads all profiles from the given profiles dir
void DCPStore::init (Glib::ustring rtProfileDir) {
Glib::Mutex::Lock lock(mtx);
fileStdProfiles.clear();
Glib::ustring rootDirName=rtProfileDir;
if (rootDirName!="") {
std::deque<Glib::ustring> qDirs;
qDirs.push_front(rootDirName);
while (qDirs.size()) {
// process directory
Glib::ustring dirname = qDirs.back();
qDirs.pop_back();
Glib::Dir* dir = NULL;
try {
if (!safe_file_test (dirname, Glib::FILE_TEST_IS_DIR)) return;
dir = new Glib::Dir (dirname);
}
catch (Glib::Exception& fe) {
return;
}
dirname = dirname + "/";
for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) {
Glib::ustring fname = dirname + *i;
Glib::ustring sname = *i;
// ignore directories
if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) {
int lastdot = sname.find_last_of ('.');
if (lastdot!=Glib::ustring::npos && lastdot<=(int)sname.size()-4 && (!sname.casefold().compare (lastdot, 4, ".dcp"))) {
Glib::ustring camShortName = sname.substr(0,lastdot).uppercase();
fileStdProfiles[camShortName]=fname; // they will be loaded and cached on demand
}
} else qDirs.push_front(fname); // for later scanning
}
delete dir;
}
}
}
DCPProfile* DCPStore::getProfile (Glib::ustring filename) {
Glib::Mutex::Lock lock(mtx);
std::map<Glib::ustring, DCPProfile*>::iterator r = profileCache.find (filename);
if (r!=profileCache.end()) return r->second;
// Add profile
profileCache[filename]=new DCPProfile(filename);
return profileCache[filename];
}
DCPProfile* DCPStore::getStdProfile(Glib::ustring camShortName) {
std::map<Glib::ustring, Glib::ustring>::iterator r = fileStdProfiles.find (camShortName);
if (r==fileStdProfiles.end()) return NULL;
return getProfile(r->second);
}
bool DCPStore::isValidDCPFileName(Glib::ustring filename) const {
return safe_file_test (filename, Glib::FILE_TEST_EXISTS)
&& !filename.casefold().compare (filename.find_last_of ('.'), 4, ".dcp");
}

76
rtengine/dcp.h Normal file
View File

@ -0,0 +1,76 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2012 Oliver Duis <www.oliverduis.de>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _DCP_
#define _DCP_
#include "imagefloat.h"
#include <glibmm.h>
#include <map>
#include <string>
namespace rtengine {
class DCPProfile {
struct HSBModify
{
float fHueShift;
float fSatScale;
float fValScale;
};
double mColorMatrix[3][3];
double mXYZCAM[3][3]; // compatible to RTs xyz_cam
HSBModify *aDeltas;
int iHueDivisions, iSatDivisions, iValDivisions;
int iHueStep, iValStep, iArrayCount;
public:
DCPProfile(Glib::ustring fname);
~DCPProfile();
void Apply(Imagefloat *pImg, Glib::ustring workingSpace) const;
void Apply(Image16 *pImg, Glib::ustring workingSpace) const;
};
class DCPStore {
Glib::Mutex mtx;
// these contain standard profiles from RT. keys are all in uppercase, file path is value
std::map<Glib::ustring, Glib::ustring> fileStdProfiles;
// Maps file name to profile as cache
std::map<Glib::ustring, DCPProfile*> profileCache;
public:
void init(Glib::ustring rtProfileDir);
bool isValidDCPFileName(Glib::ustring filename) const;
DCPProfile* getProfile(Glib::ustring filename);
DCPProfile* getStdProfile(Glib::ustring camShortName);
static DCPStore* getInstance();
};
#define dcpStore DCPStore::getInstance()
};
#endif

View File

@ -944,7 +944,7 @@ fclose(f);*/
void ImProcFunctions::hsv2rgb (float h, float s, float v, float &r, float &g, float &b) {
float h1 = h*6; // sector 0 to 5
int i = floor( h1 );
int i = (int)h1; // floor() is very slow, and h1 is always >0
float f = h1 - i; // fractional part of h
float p = v * ( 1 - s );

View File

@ -162,8 +162,8 @@ class ImProcFunctions {
static double getAutoDistor (const Glib::ustring& fname, int thumb_size);
double getTransformAutoFill (int oW, int oH);
void rgb2hsv (float r, float g, float b, float &h, float &s, float &v);
void hsv2rgb (float h, float s, float v, float &r, float &g, float &b);
static void rgb2hsv (float r, float g, float b, float &h, float &s, float &v);
static void hsv2rgb (float h, float s, float v, float &r, float &g, float &b);
void xyz2srgb (float x, float y, float z, float &r, float &g, float &b);
void xyz2rgb (float x, float y, float z, float &r, float &g, float &b, float rgb_xyz[3][3]);
void Lab2XYZ(float L, float a, float b, float &x, float &y, float &z);

View File

@ -18,6 +18,7 @@
*/
#include "rtengine.h"
#include "iccstore.h"
#include "dcp.h"
#include "improcfun.h"
#include "improccoordinator.h"
#include "curves.h"
@ -37,6 +38,8 @@ int init (const Settings* s, Glib::ustring baseDir) {
iccStore->init (s->iccDirectory, baseDir + "/iccprofiles");
iccStore->findDefaultMonitorProfile();
dcpStore->init (baseDir + "/dcpprofiles");
ProcParams::init ();
CurveFactory::init ();
ImProcFunctions::initMunsell();

View File

@ -31,6 +31,7 @@
#include "slicer.h"
#include <iostream>
#include "../rtgui/options.h"
#include "dcp.h"
@ -1680,10 +1681,16 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams
//MyTime t1, t2, t3;
//t1.set ();
cmsHPROFILE in;
if (!findInputProfile(cmp.input, embedded, camName, in)) return;
DCPProfile *dcpProf;
if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return;
if (dcpProf!=NULL) {
dcpProf->Apply(im, cmp.working);
} else {
// Calculate matrix for direct conversion raw>working space
TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working);
float mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<3; k++)
@ -1887,7 +1894,7 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams
if (imgPreLCMS!=NULL) delete imgPreLCMS;
}
}
//t3.set ();
// printf ("ICM TIME: %d\n", t3.etime(t1));
}
@ -1897,8 +1904,13 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams
// Converts raw image including ICC input profile to working space - 16bit int version
void RawImageSource::colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double camMatrix[3][3], std::string camName, double& defgain) {
cmsHPROFILE in;
if (!findInputProfile(cmp.input, embedded, camName, in)) return;
DCPProfile *dcpProf;
if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return;
if (dcpProf!=NULL) {
dcpProf->Apply(im, cmp.working);
} else {
if (in==NULL) {
// Take camprofile from DCRAW
// in this case we avoid using the slllllooooooowwww lcms
@ -1970,23 +1982,30 @@ TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working);
cmsDeleteTransform(hTransform);
}
}
//t3.set ();
// printf ("ICM TIME: %d\n", t3.etime(t1));
}
// Determine RAW input and output profiles. Returns TRUE on success
bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, cmsHPROFILE& in) {
bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in) {
in=NULL; // cam will be taken on NULL
*dcpProf=NULL;
if (inProfile == "(none)") return false;
if (inProfile == "(embedded)" && embedded) {
in = embedded;
} else if (inProfile=="(cameraICC)") {
in = iccStore->getStdProfile(camName);
// DCPs have higher quality, so use them first
*dcpProf=dcpStore->getStdProfile(camName);
if (*dcpProf==NULL) in = iccStore->getStdProfile(camName);
} else if (inProfile!="(camera)" && inProfile!="") {
in = iccStore->getProfile (inProfile);
Glib::ustring normalName=inProfile;
if (!inProfile.compare (0, 5, "file:")) normalName=inProfile.substr(5);
if (dcpStore->isValidDCPFileName(normalName)) *dcpProf=dcpStore->getProfile(normalName);
if (*dcpProf==NULL) in = iccStore->getProfile (inProfile);
}
// "in" might be NULL because of "not found". That's ok, we take the cam profile then
@ -2561,7 +2580,7 @@ void RawImageSource::transformPosition (int x, int y, int tran, int& ttx, int& t
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void RawImageSource::inverse33 (double (*rgb_cam)[3], double (*cam_rgb)[3]) {
void RawImageSource::inverse33 (const double (*rgb_cam)[3], double (*cam_rgb)[3]) {
double nom = (rgb_cam[0][2]*rgb_cam[1][1]*rgb_cam[2][0] - rgb_cam[0][1]*rgb_cam[1][2]*rgb_cam[2][0] -
rgb_cam[0][2]*rgb_cam[1][0]*rgb_cam[2][1] + rgb_cam[0][0]*rgb_cam[1][2]*rgb_cam[2][1] +
rgb_cam[0][1]*rgb_cam[1][0]*rgb_cam[2][2] - rgb_cam[0][0]*rgb_cam[1][1]*rgb_cam[2][2] );

View File

@ -21,6 +21,7 @@
#include "imagesource.h"
#include <lcms2.h>
#include "dcp.h"
#include "array2D.h"
#include "curves.h"
#include "../rtgui/cacheimagedata.h"
@ -60,7 +61,7 @@ class RawImageSource : public ImageSource {
private:
static LUTf invGrad; // for fast_demosaic
static LUTf initInvGrad ();
static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, cmsHPROFILE& in);
static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in);
protected:
Glib::Mutex getImageMutex; // locks getImage
@ -167,7 +168,7 @@ class RawImageSource : public ImageSource {
static void colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName, double& defgain);
static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName, double& defgain);
static void inverse33 (double (*coeff)[3], double (*icoeff)[3]);
static void inverse33 (const double (*coeff)[3], double (*icoeff)[3]);
void boxblur2(float** src, float** dst, int H, int W, int box );
void boxblur_resamp(float **src, float **dst, float & max, int H, int W, int box, int samp );

View File

@ -24,6 +24,7 @@
#include <ctime>
#include <algorithm>
#include <sstream>
#include <stdint.h>
#include "rtexif.h"
@ -45,7 +46,7 @@ TagDirectory::TagDirectory ()
TagDirectory::TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border)
: attribs(ta), order(border), parent(p) {}
TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border) {
TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored) {
attribs = ta;
order = border;
@ -66,6 +67,7 @@ TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib*
continue;
}
if (skipIgnored) {
int id = newTag->getID();
// detect and possibly ignore tags of directories belonging to the embedded thumbnail image
@ -78,6 +80,7 @@ TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib*
delete newTag;
else
addTag (newTag);
} else addTag (newTag);
}
}
@ -759,7 +762,7 @@ int Tag::toInt (int ofs, TagType astype) {
case LONG: return (int)sget4 (value+ofs, getOrder());
case SRATIONAL:
case RATIONAL: a = (int)sget4 (value+ofs+4, getOrder()); return a==0 ? 0 : (int)sget4 (value+ofs, getOrder()) / a;
case FLOAT: return (int)((float) sget4 (value+ofs, getOrder()));
case FLOAT: return (int)toDouble(ofs);
case UNDEFINED: return 0;
default: return 0; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
}
@ -767,6 +770,7 @@ int Tag::toInt (int ofs, TagType astype) {
}
double Tag::toDouble (int ofs) {
union IntFloat { uint32_t i; float f; } conv;
double ud, dd;
switch (type) {
@ -778,7 +782,10 @@ double Tag::toDouble (int ofs) {
case LONG: return (double)((int)sget4 (value+ofs, getOrder()));
case SRATIONAL:
case RATIONAL: ud = (int)sget4 (value+ofs, getOrder()); dd = (int)sget4 (value+ofs+4, getOrder()); return dd==0. ? 0. : (double)ud / (double)dd;
case FLOAT: return (float) sget4 (value+ofs, getOrder());
case FLOAT:
conv.i=sget4 (value+ofs, getOrder());
return conv.f; // IEEE FLOATs are already C format, they just need a recast
case UNDEFINED: return 0.;
default: return 0.; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
}
@ -1302,7 +1309,7 @@ void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root)
}
}
TagDirectory* ExifManager::parse (FILE* f, int base) {
TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf
// read tiff header
fseek (f, base, SEEK_SET);
@ -1316,7 +1323,7 @@ TagDirectory* ExifManager::parse (FILE* f, int base) {
fseek (f, base+firstifd, SEEK_SET);
// first read the IFD directory
TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order);
TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order, skipIgnored);
// fix ISO issue with nikon and panasonic cameras
Tag* exif = root->getTag ("Exif");
@ -1373,9 +1380,9 @@ TagDirectory* ExifManager::parseJPEG (FILE* f) {
return NULL;
}
TagDirectory* ExifManager::parseTIFF (FILE* f) {
TagDirectory* ExifManager::parseTIFF (FILE* f, bool skipIgnored) {
return parse (f, 0);
return parse (f, 0, skipIgnored);
}
std::vector<Tag*> ExifManager::defTags;

View File

@ -69,7 +69,7 @@ class TagDirectory {
public:
TagDirectory ();
TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border);
TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored=true);
TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border);
virtual ~TagDirectory ();
@ -197,9 +197,9 @@ class ExifManager {
static Tag* saveCIFFMNTag (FILE* f, TagDirectory* root, int len, const char* name);
public:
static TagDirectory* parse (FILE*f, int base);
static TagDirectory* parse (FILE*f, int base, bool skipIgnored=true);
static TagDirectory* parseJPEG (FILE*f);
static TagDirectory* parseTIFF (FILE*f);
static TagDirectory* parseTIFF (FILE*f, bool skipIgnored=true);
static TagDirectory* parseCIFF (FILE* f, int base, int length);
static void parseCIFF (FILE* f, int base, int length, TagDirectory* root);

View File

@ -22,6 +22,7 @@
#include "guiutils.h"
#include "../rtengine/safegtk.h"
#include "../rtengine/iccstore.h"
#include "../rtengine/dcp.h"
#include "rtimage.h"
using namespace rtengine;
@ -160,6 +161,8 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL),
Gtk::FileFilter filter_icc;
filter_icc.set_name(M("TP_ICM_FILEDLGFILTERICM"));
filter_icc.add_pattern("*.dcp");
filter_icc.add_pattern("*.DCP");
filter_icc.add_pattern("*.icc");
filter_icc.add_pattern("*.icm");
filter_icc.add_pattern("*.ICC");
@ -437,7 +440,7 @@ void ICMPanel::setRawMeta (bool raw, const rtengine::ImageData* pMeta) {
icamera->set_active (raw);
iembedded->set_active (!raw);
icamera->set_sensitive (raw);
icameraICC->set_sensitive (raw && iccStore->getStdProfile(pMeta->getCamera()) != NULL);
icameraICC->set_sensitive (raw && (iccStore->getStdProfile(pMeta->getCamera()) != NULL || dcpStore->getStdProfile(pMeta->getCamera()) != NULL));
iembedded->set_sensitive (!raw);
enableListener ();