XMP/IPTC metadata tunneling option, plus new EXIF tags; see issue #460

This commit is contained in:
Oliver Duis
2011-01-05 21:12:11 +01:00
parent b89e9d5760
commit 13dc744e9b
15 changed files with 83 additions and 25 deletions

View File

@@ -472,6 +472,7 @@ PREFERENCES_INTENT_RELATIVE;Relative Colorimetric
PREFERENCES_INTENT_SATURATION;Saturation
PREFERENCES_LINEDENOISE;Line noise filter
PREFERENCES_LIVETHUMBNAILS;Live Thumbnails (slower)
PREFERENCES_METADATA;Metadata
PREFERENCES_MONITORICC;Monitor Profile
PREFERENCES_MULTITAB;Multiple tabs mode
PREFERENCES_MULTITABDUALMON;Multiple tabs mode, if available on second monitor
@@ -490,10 +491,10 @@ PREFERENCES_PARSEDEXTDELHINT;Delete selected extension from the list
PREFERENCES_PRESER;Exposure before interpolation\n :preserve highlights (EV)
PREFERENCES_PROFILEHANDLING;Processing Profile Handling
PREFERENCES_PROFILELOADPR;Profile Loading Priority
PREFERENCES_PROFILEPRCACHE;Profile in Cache
PREFERENCES_PROFILEPRFILE;Profile Next to the Input File
PREFERENCES_PROFILESAVECACHE;Save Processing Parameters to the Cache
PREFERENCES_PROFILESAVEINPUT;Save Processing Parameters Next to the Input File
PREFERENCES_PROFILEPRCACHE;Profile in cache
PREFERENCES_PROFILEPRFILE;Profile next to the input file
PREFERENCES_PROFILESAVECACHE;Save processing parameters to the cache
PREFERENCES_PROFILESAVEINPUT;Save processing parameters next to the input file
PREFERENCES_PROPERTY;Property
PREFERENCES_PSPATH;Adobe Photoshop installation directory
PREFERENCES_SELECTFONT;Select font
@@ -520,6 +521,7 @@ PREFERENCES_TAB_IMPROC;Image Processing
PREFERENCES_TAB_OUTPUT;Output Options
PREFERENCES_TAB_SOUND;Sounds
PREFERENCES_THUMBSIZE;Thumbnail Size
PREFERENCES_TUNNELMETADATA;Tunnel IPTC/XMP unchanged to output file
PREFERENCES_USESYSTEMTHEME; Use System Theme
PREFERENCES_WORKFLOW;Workflow
PROFILEPANEL_FILEDLGFILTERANY;Any files

View File

@@ -45,6 +45,22 @@ using namespace rtengine::procparams;
Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.","Error while reading header.","File reading error", "Image format not supported."};
// For only copying the raw input data
void ImageIO::setMetadata (const rtexif::TagDirectory* eroot) {
if (exifRoot!=NULL) { delete exifRoot; exifRoot = NULL; }
if (eroot) {
rtexif::TagDirectory* td = ((rtexif::TagDirectory*)eroot)->clone (NULL);
// make IPTC and XMP pass through
td->keepTag(0x83bb); // IPTC
td->keepTag(0x02bc); // XMP
exifRoot=td;
}
}
// For merging with RT specific data
void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const std::vector<ExifPair>& exif, const std::vector<IPTCPair>& iptcc) {
// store exif info
@@ -53,15 +69,13 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const std::vector<
exifChange[i].first = exif[i].field;
exifChange[i].second = exif[i].value;
}
delete exifRoot;
exifRoot = NULL;
if (exifRoot!=NULL) { delete exifRoot; exifRoot = NULL; }
if (eroot)
exifRoot = ((rtexif::TagDirectory*)eroot)->clone (NULL);
if (iptc)
iptc_data_free (iptc);
iptc = NULL;
if (iptc!=NULL) { iptc_data_free (iptc); iptc = NULL; }
// build iptc structures for libiptcdata
if (iptcc.size()==0)

View File

@@ -82,6 +82,7 @@ class ImageIO {
cmsHPROFILE getEmbeddedProfile () { return embProfile; }
void getEmbeddedProfileData (int& length, unsigned char*& pdata) { length = loadedProfileLength; pdata = (unsigned char*)loadedProfileData; }
void setMetadata (const rtexif::TagDirectory* eroot);
void setMetadata (const rtexif::TagDirectory* eroot, const std::vector<rtengine::procparams::ExifPair>& exif, const std::vector<rtengine::procparams::IPTCPair>& iptcc);
void setOutputProfile (char* pdata, int plen);
Glib::Mutex& mutex () { return imutex; }

View File

@@ -348,8 +348,9 @@ namespace rtengine {
* @param job the ProcessingJob to cancel.
* @param errorCode is the error code if an error occured (e.g. the input image could not be loaded etc.)
* @param pl is an optional ProgressListener if you want to keep track of the progress
* @param tunnelMetaData tunnels IPTC and XMP to output without change
* @return the resulting image, with the output profile applied, exif and iptc data set. You have to save it or you can access the pixel data directly. */
IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = NULL);
IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = NULL, bool tunnelMetaData=false);
/** This class is used to control the batch processing. The class implementing this interface will be called when the full processing of an
* image is ready and the next job to process is needed. */
@@ -366,8 +367,9 @@ namespace rtengine {
* with processing. If no new job is given, it finishes.
* The ProcessingJob passed becomes invalid, you can not use it any more.
* @param job the ProcessingJob to cancel.
* @param bpl is the BatchProcessingListener that is called when the image is ready or the next job is needed. It also acts as a ProgressListener. */
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl);
* @param bpl is the BatchProcessingListener that is called when the image is ready or the next job is needed. It also acts as a ProgressListener.
* @param tunnelMetaData tunnels IPTC and XMP to output without change */
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData);
extern Glib::Mutex* lcmsMutex;

View File

@@ -2,6 +2,7 @@
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2010 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
@@ -31,7 +32,8 @@
namespace rtengine {
IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl) {
// tunnelMetaData copies IPTC and XMP untouched to output
IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData) {
errorCode = 0;
@@ -275,12 +277,14 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
}
}
if (tunnelMetaData)
readyImg->setMetadata (ii->getMetaData()->getExifData ());
else
readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc);
if (pl)
pl->setProgress (1.0);
readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc);
ProfileContent pc;
if (params.icm.output.compare (0, 6, "No ICM") && params.icm.output!="")
pc = iccStore->getContent (params.icm.output);
@@ -302,23 +306,23 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
return readyImg;
}
void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl) {
void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) {
ProcessingJob* currentJob = job;
while (currentJob) {
int errorCode;
IImage16* img = processImage (currentJob, errorCode, bpl);
IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData);
if (errorCode)
bpl->error ("Can not load input image.");
currentJob = bpl->imageReady (img);
}
}
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl) {
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) {
if (bpl)
Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl), 0, true, true, Glib::THREAD_PRIORITY_LOW);
Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl, tunnelMetaData), 0, true, true, Glib::THREAD_PRIORITY_LOW);
}

View File

@@ -208,6 +208,11 @@ Tag* TagDirectory::findTag (const char* name) const {
return NULL;
}
void TagDirectory::keepTag (int ID) {
for (int i=0; i<tags.size(); i++)
if (tags[i]->getID()==ID) tags[i]->setKeep(true);
}
int TagDirectory::calculateSize () {
int size = 2; // space to store the number of tags

View File

@@ -80,6 +80,8 @@ class TagDirectory {
virtual Tag* getTag (const char* name) const;
virtual Tag* getTag (int ID) const;
virtual Tag* findTag (const char* name) const;
void keepTag (int ID);
virtual void addTag (Tag* a);
virtual void addTagFront (Tag* a);
virtual void replaceTag (Tag* a);

View File

@@ -2,6 +2,7 @@
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Copyright (c) 2010 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
@@ -374,6 +375,12 @@ public:
};
UnitsInterpreter unitsInterpreter;
class UTF8BinInterpreter : public Interpreter {
public:
UTF8BinInterpreter () {}
};
UTF8BinInterpreter utf8BinInterpreter;
const TagAttrib exifAttribs[] = {
{0, 2, 0, 0, 0x0100, "ImageWidth", &stdInterpreter},
{0, 2, 0, 0, 0x0101, "ImageHeight", &stdInterpreter},
@@ -442,7 +449,6 @@ const TagAttrib exifAttribs[] = {
{0, 1, 0, 0, 0xA420, "ImageUniqueID", &stdInterpreter},
{0, 1, 0, 0, 0xa432, "LensInfo", &stdInterpreter},
{0, 1, 0, 0, 0xa434, "LensModel", &stdInterpreter},
{0, 1, 0, 0, 0xc630, "DNGLensInfo", &stdInterpreter},
{-1, 0, 0, 0, 0, "", NULL }};
@@ -517,6 +523,7 @@ const TagAttrib iopAttribs[] = {
{0, 2, 0, 0, 0x0212, "YCbCrSubSampling", &stdInterpreter},
{0, 2, 0, 0, 0x0213, "YCbCrPositioning", &stdInterpreter},
{0, 2, 0, 0, 0x0214, "ReferenceBlackWhite", &stdInterpreter},
{0, 2, 0, 0, 0x02bc, "ApplicationNotes", &utf8BinInterpreter}, // XMP
{0, 1, 0, 0, 0x4746, "Rating",&stdInterpreter},
{0, 1, 0, 0, 0x4749, "RatingPercent",&stdInterpreter},
{0, 1, 0, 0, 0x828d, "CFAPatternDim", &stdInterpreter},
@@ -528,8 +535,13 @@ const TagAttrib iopAttribs[] = {
{0, 1, 0, gpsAttribs, 0x8825, "GPSInfo", &stdInterpreter},
{0, 1, 0, 0, 0x9003, "DateTimeOriginal", &stdInterpreter},
{0, 1, 0, 0, 0x9004, "DateTimeDigitized", &stdInterpreter},
{0, 1, 0, 0, 0x9211, "ImageNumber", &stdInterpreter},
{0, 1, 0, iopAttribs, 0xA005, "Interoperability", &stdInterpreter},
{0, 0, 0, 0, 0xC4A5, "PrintIMInformation", &stdInterpreter},
{0, 1, 0, 0, 0xc62f, "CameraSerialNumber", &stdInterpreter},
{0, 2, 0, 0, 0xc630, "DNGLensInfo", &stdInterpreter},
{0, 1, 0, 0, 0xc65d, "RawDataUniqueID", &stdInterpreter},
{0, 0, 0, 0, 0xc761, "NoiseProfile", &stdInterpreter},
{0, 2, 0, 0, 0x00fe, "NewSubFileType", &stdInterpreter},
{-1, 0, 0, 0, 0, "", NULL}};

View File

@@ -281,7 +281,7 @@ void BatchQueue::startProcessing () {
// remove button set
next->removeButtonSet ();
// start batch processing
rtengine::startBatchProcessing (next->job, this);
rtengine::startBatchProcessing (next->job, this, options.tunnelMetaData);
queue_draw ();
}
}

View File

@@ -900,7 +900,7 @@ void EditorPanel::saveAsPressed () {
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
fname = removeExtension (fname);
ProgressConnector<rtengine::IImage16*> *ld = new ProgressConnector<rtengine::IImage16*>();
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener() ),
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData ),
sigc::bind(sigc::mem_fun( *this,&EditorPanel::idle_saveImage ),ld,fname,sf,false ));
saveimgas->set_sensitive(false);
sendtogimp->set_sensitive(false);
@@ -929,7 +929,7 @@ void EditorPanel::sendToGimpPressed () {
ipc->getParams (&pparams);
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
ProgressConnector<rtengine::IImage16*> *ld = new ProgressConnector<rtengine::IImage16*>();
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener() ),
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData ),
sigc::bind(sigc::mem_fun( *this,&EditorPanel::idle_sendToGimp ),ld ));
saveimgas->set_sensitive(false);
sendtogimp->set_sensitive(false);

View File

@@ -349,7 +349,7 @@ int processLineParams( int argc, char **argv )
}
// Process image
rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, NULL);
rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, NULL, options.tunnelMetaData);
if( !resultImage ){
errors++;
std::cerr << "Error processing:"<< inputFile << std::endl;

View File

@@ -123,6 +123,7 @@ void Options::setDefaults () {
showFileNames = true;
tabbedUI = false;
multiDisplayMode = 0;
tunnelMetaData = false;
cutOverlayBrush = std::vector<double> (4);
cutOverlayBrush[3] = 0.667;
@@ -214,6 +215,7 @@ if (keyFile.has_group ("Output")) {
if (keyFile.has_key ("Output", "UsePathTemplate")) saveUsePathTemplate = keyFile.get_boolean("Output", "UsePathTemplate");
if (keyFile.has_key ("Output", "LastSaveAsPath")) lastSaveAsPath = keyFile.get_string ("Output", "LastSaveAsPath");
if (keyFile.has_key ("Output", "OverwriteOutputFile")) overwriteOutputFile = keyFile.get_boolean("Output", "OverwriteOutputFile");
if (keyFile.has_key ("Output", "TunnelMetaData")) tunnelMetaData = keyFile.get_boolean("Output", "TunnelMetaData");
}
if (keyFile.has_group ("Profiles")) {
@@ -387,6 +389,7 @@ int Options::saveToFile (Glib::ustring fname) {
keyFile.set_boolean ("Output", "UsePathTemplate", saveUsePathTemplate);
keyFile.set_string ("Output", "LastSaveAsPath", lastSaveAsPath);
keyFile.set_boolean ("Output", "OverwriteOutputFile", overwriteOutputFile);
keyFile.set_boolean ("Output", "TunnelMetaData", tunnelMetaData);
keyFile.set_string ("Profiles", "Directory", profilePath);
keyFile.set_string ("Profiles", "RawDefault", defProfRaw);

View File

@@ -139,7 +139,7 @@ class Options {
Glib::ustring sndBatchQueueDone;
Glib::ustring sndLngEditProcDone;
double sndLngEditProcDoneSecs; // Minimum processing time seconds till the sound is played
bool tunnelMetaData; // Pass through IPTC and XMP unchanged
Options ();

View File

@@ -286,6 +286,13 @@ Gtk::Widget* Preferences::getProcParamsPanel () {
dfconn = darkFrameDir->signal_file_set().connect ( sigc::mem_fun(*this, &Preferences::darkFrameChanged), true);
Gtk::Frame* fmd = Gtk::manage (new Gtk::Frame (M("PREFERENCES_METADATA")));
Gtk::VBox* vbmd = Gtk::manage (new Gtk::VBox ());
ckbTunnelMetaData = Gtk::manage (new Gtk::CheckButton (M("PREFERENCES_TUNNELMETADATA")));
vbmd->pack_start (*ckbTunnelMetaData, Gtk::PACK_SHRINK, 4);
fmd->add (*vbmd);
mvbpp->pack_start (*fmd, Gtk::PACK_SHRINK, 4);
return mvbpp;
}
@@ -816,6 +823,8 @@ void Preferences::storePreferences () {
moptions.saveParamsCache = saveParamsCache->get_active ();
moptions.paramsLoadLocation = (PPLoadLocation)loadParamsPreference->get_active_row_number ();
moptions.tunnelMetaData = ckbTunnelMetaData->get_active ();
moptions.rtSettings.darkFramesPath = darkFrameDir->get_filename();
int i = 0;
@@ -914,6 +923,8 @@ void Preferences::fillPreferences () {
saveParamsCache->set_active (moptions.saveParamsCache);
loadParamsPreference->set_active (moptions.paramsLoadLocation);
ckbTunnelMetaData->set_active (moptions.tunnelMetaData);
if (!moptions.tabbedUI)
editorLayout->set_active(moptions.mainNBVertical ? 1 : 0);
else

View File

@@ -111,6 +111,8 @@ class Preferences : public Gtk::Dialog {
Gtk::Entry* txtSndLngEditProcDone;
Gtk::SpinButton* spbSndLngEditProcDoneSecs;
Gtk::CheckButton* ckbTunnelMetaData;
Options moptions;
sigc::connection tconn, fconn, usethcon, addc, setc, dfconn;
Glib::ustring initialTheme;