16-bit floating-point support for TIFF output image (see #2357)

This commit is contained in:
Hombre
2018-05-07 09:57:53 +02:00
parent 5ac11ef1b9
commit 30efa5930d
16 changed files with 83 additions and 42 deletions

View File

@@ -1216,6 +1216,7 @@ SAMPLEFORMAT_8;LogLuv 32 bits
SAMPLEFORMAT_16;32 bits à virgule flottante
SAVEDLG_AUTOSUFFIX;Ajouter automatiquement un suffixe si le fichier existe déjà
SAVEDLG_FILEFORMAT;Format de fichier
SAVEDLG_FILEFORMAT_FLOAT; virgule flottante
SAVEDLG_FORCEFORMATOPTS;Forcer les options d'enregistrement
SAVEDLG_JPEGQUAL;Qualité JPEG
SAVEDLG_PNGCOMPR;Compression PNG

View File

@@ -1219,6 +1219,7 @@ SAMPLEFORMAT_8;LogLuv 32 bits
SAMPLEFORMAT_16;32 bits floating point
SAVEDLG_AUTOSUFFIX;Automatically add a suffix if the file already exists
SAVEDLG_FILEFORMAT;File format
SAVEDLG_FILEFORMAT_FLOAT; floating-point
SAVEDLG_FORCEFORMATOPTS;Force saving options
SAVEDLG_JPEGQUAL;JPEG quality
SAVEDLG_PUTTOQUEUE;Put into processing queue

View File

@@ -1765,8 +1765,9 @@ public:
/** @brief Saves the image to file in a tif format.
* @param fname is the name of the file
* @param bps can be 8 or 16 depending on the bits per pixels the output file will have
* @param isFloat is true for saving float images. Will be ignored by file format not supporting float data
@return the error code, 0 if none */
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false) = 0;
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, float isFloat = false, bool uncompressed = false) = 0;
/** @brief Sets the progress listener if you want to follow the progress of the image saving operations (optional).
* @param pl is the pointer to the class implementing the ProgressListener interface */
virtual void setSaveProgressListener (ProgressListener* pl) = 0;

View File

@@ -83,9 +83,9 @@ public:
{
return saveJPEG (fname, quality, subSamp);
}
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false)
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, float isFloat = false, bool uncompressed = false)
{
return saveTIFF (fname, bps, uncompressed);
return saveTIFF (fname, bps, isFloat, uncompressed);
}
virtual void setSaveProgressListener (ProgressListener* pl)
{

View File

@@ -78,9 +78,9 @@ public:
{
return saveJPEG (fname, quality, subSamp);
}
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false)
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, float isFloat = false, bool uncompressed = false)
{
return saveTIFF (fname, bps, uncompressed);
return saveTIFF (fname, bps, isFloat, uncompressed);
}
virtual void setSaveProgressListener (ProgressListener* pl)
{

View File

@@ -87,9 +87,9 @@ public:
{
return saveJPEG (fname, quality, subSamp);
}
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false)
virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, float isFloat = false, bool uncompressed = false)
{
return saveTIFF (fname, bps, uncompressed);
return saveTIFF (fname, bps, isFloat, uncompressed);
}
virtual void setSaveProgressListener (ProgressListener* pl)
{

View File

@@ -1300,13 +1300,12 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp)
return IMIO_SUCCESS;
}
int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
int ImageIO::saveTIFF (Glib::ustring fname, int bps, float isFloat, bool uncompressed)
{
if (getWidth() < 1 || getHeight() < 1) {
return IMIO_HEADERERROR;
}
//TODO: Handling 32 bits floating point output images!
bool writeOk = true;
int width = getWidth ();
int height = getHeight ();
@@ -1458,10 +1457,10 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_ADOBE_DEFLATE);
TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, (bps == 16 || bps == 32) && isFloat ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
if (!uncompressed) {
TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL);
TIFFSetField (out, TIFFTAG_PREDICTOR, (bps == 16 || bps == 32) && isFloat ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL);
}
if (profileData) {
TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData);
@@ -1470,14 +1469,24 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
for (int row = 0; row < height; row++) {
getScanline (row, linebuffer, bps);
if(needsReverse && !uncompressed && bps == 32) {
for(int i = 0; i < lineWidth; i += 4) {
char temp = linebuffer[i];
linebuffer[i] = linebuffer[i + 3];
linebuffer[i + 3] = temp;
temp = linebuffer[i + 1];
linebuffer[i + 1] = linebuffer[i + 2];
linebuffer[i + 2] = temp;
/*if (bps == 16) {
if(needsReverse && !uncompressed) {
for(int i = 0; i < lineWidth; i += 2) {
char temp = linebuffer[i];
linebuffer[i] = linebuffer[i + 1];
linebuffer[i + 1] = temp;
}
}
} else */ if (bps == 32) {
if(needsReverse && !uncompressed) {
for(int i = 0; i < lineWidth; i += 4) {
char temp = linebuffer[i];
linebuffer[i] = linebuffer[i + 3];
linebuffer[i + 3] = temp;
temp = linebuffer[i + 1];
linebuffer[i + 1] = linebuffer[i + 2];
linebuffer[i + 2] = temp;
}
}
}

View File

@@ -138,7 +138,7 @@ public:
int savePNG (Glib::ustring fname, volatile int bps = -1);
int saveJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3);
int saveTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false);
int saveTIFF (Glib::ustring fname, int bps = -1, float isFloat = false, bool uncompressed = false);
cmsHPROFILE getEmbeddedProfile ()
{

View File

@@ -228,7 +228,7 @@ bool BatchQueue::saveBatchQueue ()
// The column's header is mandatory (the first line will be skipped when loaded)
file << "input image full path|param file full path|output image full path|file format|jpeg quality|jpeg subsampling|"
<< "png bit depth|png compression|tiff bit depth|uncompressed tiff|save output params|force format options|fast export|<end of line>"
<< "png bit depth|png compression|tiff bit depth|tiff is float|uncompressed tiff|save output params|force format options|fast export|<end of line>"
<< std::endl;
// method is already running with entryLock, so no need to lock again
@@ -246,7 +246,7 @@ bool BatchQueue::saveBatchQueue ()
#endif
<< saveFormat.jpegQuality << '|' << saveFormat.jpegSubSamp << '|'
<< saveFormat.pngBits << '|'
<< saveFormat.tiffBits << '|' << saveFormat.tiffUncompressed << '|'
<< saveFormat.tiffBits << '|' << (saveFormat.tiffFloat ? 1 : 0) << '|' << saveFormat.tiffUncompressed << '|'
<< saveFormat.saveParams << '|' << entry->forceFormatOpts << '|'
<< entry->fast_pipeline << '|'
<< std::endl;
@@ -311,6 +311,7 @@ bool BatchQueue::loadBatchQueue ()
const auto jpegSubSamp = nextIntOr (options.saveFormat.jpegSubSamp);
const auto pngBits = nextIntOr (options.saveFormat.pngBits);
const auto tiffBits = nextIntOr (options.saveFormat.tiffBits);
const auto tiffFloat = nextIntOr (options.saveFormat.tiffFloat);
const auto tiffUncompressed = nextIntOr (options.saveFormat.tiffUncompressed);
const auto saveParams = nextIntOr (options.saveFormat.saveParams);
const auto forceFormatOpts = nextIntOr (options.forceFormatOpts);
@@ -352,6 +353,7 @@ bool BatchQueue::loadBatchQueue ()
saveFormat.jpegSubSamp = jpegSubSamp;
saveFormat.pngBits = pngBits;
saveFormat.tiffBits = tiffBits;
saveFormat.tiffFloat = tiffFloat == 1;
saveFormat.tiffUncompressed = tiffUncompressed != 0;
saveFormat.saveParams = saveParams != 0;
entry->forceFormatOpts = forceFormatOpts != 0;
@@ -608,7 +610,7 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImagefloat* img)
int err = 0;
if (saveFormat.format == "tif") {
err = img->saveAsTIFF (fname, saveFormat.tiffBits, saveFormat.tiffUncompressed);
err = img->saveAsTIFF (fname, saveFormat.tiffBits, saveFormat.tiffFloat, saveFormat.tiffUncompressed);
} else if (saveFormat.format == "png") {
err = img->saveAsPNG (fname, saveFormat.pngBits);
} else if (saveFormat.format == "jpg") {

View File

@@ -175,9 +175,10 @@ Glib::ustring BatchQueueEntry::getToolTip (int x, int y)
tooltip += Glib::ustring::compose("\n\n%1: %2", M("BATCHQUEUE_DESTFILENAME"), outFileName);
if (forceFormatOpts) {
tooltip += Glib::ustring::compose("\n\n%1: %2 (%3 bits)", M("SAVEDLG_FILEFORMAT"), saveFormat.format,
tooltip += Glib::ustring::compose("\n\n%1: %2 (%3-bits%4)", M("SAVEDLG_FILEFORMAT"), saveFormat.format,
saveFormat.format == "png" ? saveFormat.pngBits :
saveFormat.format == "tif" ? saveFormat.tiffBits : 8);
saveFormat.format == "tif" ? saveFormat.tiffBits : 8,
saveFormat.format == "tif" && saveFormat.tiffFloat ? M("SAVEDLG_FILEFORMAT_FLOAT") : "");
if (saveFormat.format == "jpg") {
tooltip += Glib::ustring::compose("\n%1: %2\n%3: %4",

View File

@@ -1763,7 +1763,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector<rtengine::IImagefloat*> *pc,
img->setSaveProgressListener (parent->getProgressListener());
if (sf.format == "tif")
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fname, sf.tiffBits, sf.tiffUncompressed),
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fname, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed),
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams));
else if (sf.format == "png")
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsPNG), fname, sf.pngBits),
@@ -1980,9 +1980,9 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm
int err = 0;
if (gimpPlugin) {
err = img->saveAsTIFF (filename, 32, true);
err = img->saveAsTIFF (filename, 32, true, true);
} else if (sf.format == "tif") {
err = img->saveAsTIFF (filename, sf.tiffBits, sf.tiffUncompressed);
err = img->saveAsTIFF (filename, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed);
} else if (sf.format == "png") {
err = img->saveAsPNG (filename, sf.pngBits);
} else if (sf.format == "jpg") {
@@ -2038,6 +2038,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *p
SaveFormat sf;
sf.format = "tif";
sf.tiffBits = 16;
sf.tiffFloat = false;
sf.tiffUncompressed = true;
sf.saveParams = true;
@@ -2058,7 +2059,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *p
ProgressConnector<int> *ld = new ProgressConnector<int>();
img->setSaveProgressListener (parent->getProgressListener());
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fileName, sf.tiffBits, sf.tiffUncompressed),
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fileName, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed),
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_sentToGimp), ld, img, fileName));
} else {
Glib::ustring msg_ = Glib::ustring ("<b> Error during image processing\n</b>");

View File

@@ -267,6 +267,7 @@ int processLineParams ( int argc, char **argv )
int compression = 92;
int subsampling = 3;
int bits = -1;
bool isFloat = false;
std::string outputType = "";
unsigned errors = 0;
@@ -392,11 +393,14 @@ int processLineParams ( int argc, char **argv )
bits = atoi (currParam.substr (2).c_str());
if (bits != 8 && bits != 16 && bits != 32) {
std::cerr << "Error: specify -b8 for 8-bit/integer or -b16 for 16-bit/integer or -b32 for 32-bit/float output." << std::endl;
std::cerr << "Error: specify -b8 for 8-bit/integer or -b16 for 16-bit/integer or -b16f for 16-bit/float or -b32 for 32-bit/float output." << std::endl;
deleteProcParams (processingParams);
return -3;
}
isFloat = (bits == 16 && currParam.length() == 3 && currParam.at(2) == 'f') || bits == 32;
printf("Float output detected (%d bits)!\n", bits);
break;
case 't':
@@ -550,10 +554,11 @@ int processLineParams ( int argc, char **argv )
std::cout << " Chroma halved horizontally." << std::endl;
std::cout << " 3 = Best quality: 1x1, 1x1, 1x1 (4:4:4)" << std::endl;
std::cout << " No chroma subsampling." << std::endl;
std::cout << " -b<8|16|32> Specify bit depth per channel (default value: 16 for TIFF, 8 for PNG)." << std::endl;
std::cout << " TIFF can be 8-bit/int, 16-bit/int or 32-bit/float" << std::endl;
std::cout << " PNG can be 8-bit/int or 16-bit/int." << std::endl;
std::cout << " JPEG is only 8-bit/int." << std::endl;
std::cout << " -b<8|16|16f|32> Specify bit depth per channel (default value: 16 for TIFF, 8 for PNG and JPEG)." << std::endl;
std::cout << " 8 = 8 bits integer, applies to JPEG, PNG and TIFF" << std::endl;
std::cout << " 16 = 16 bits integer, applies to PNG and TIFF" << std::endl;
std::cout << " 16f = 16 bits float, applies to TIFF" << std::endl;
std::cout << " 32 = 32 bits float, applies to TIFF" << std::endl;
std::cout << " -t[z] Specify output to be TIFF." << std::endl;
std::cout << " Uncompressed by default, or deflate compression with 'z'." << std::endl;
std::cout << " -n Specify output to be compressed PNG." << std::endl;
@@ -781,7 +786,7 @@ int processLineParams ( int argc, char **argv )
if ( outputType == "jpg" ) {
errorCode = resultImage->saveAsJPEG ( outputFile, compression, subsampling );
} else if ( outputType == "tif" ) {
errorCode = resultImage->saveAsTIFF ( outputFile, bits, compression == 0 );
errorCode = resultImage->saveAsTIFF ( outputFile, bits, isFloat, compression == 0 );
} else if ( outputType == "png" ) {
errorCode = resultImage->saveAsPNG ( outputFile, bits );
} else {

View File

@@ -298,6 +298,7 @@ void Options::setDefaults ()
saveFormat.jpegSubSamp = 2;
saveFormat.pngBits = 8;
saveFormat.tiffBits = 16;
saveFormat.tiffFloat = false;
saveFormat.tiffUncompressed = true;
saveFormat.saveParams = true;
@@ -306,6 +307,7 @@ void Options::setDefaults ()
saveFormatBatch.jpegSubSamp = 2;
saveFormatBatch.pngBits = 8;
saveFormatBatch.tiffBits = 16;
saveFormatBatch.tiffFloat = false;
saveFormatBatch.tiffUncompressed = true;
saveFormatBatch.saveParams = true;
@@ -736,6 +738,10 @@ void Options::readFromFile (Glib::ustring fname)
saveFormat.tiffBits = keyFile.get_integer ("Output", "TiffBps");
}
if (keyFile.has_key ("Output", "TiffFloat")) {
saveFormat.tiffFloat = keyFile.get_boolean ("Output", "TiffFloat");
}
if (keyFile.has_key ("Output", "TiffUncompressed")) {
saveFormat.tiffUncompressed = keyFile.get_boolean ("Output", "TiffUncompressed");
}
@@ -765,6 +771,10 @@ void Options::readFromFile (Glib::ustring fname)
saveFormatBatch.tiffBits = keyFile.get_integer ("Output", "TiffBpsBatch");
}
if (keyFile.has_key ("Output", "TiffFloatBatch")) {
saveFormatBatch.tiffFloat = keyFile.get_boolean ("Output", "TiffFloatBatch");
}
if (keyFile.has_key ("Output", "TiffUncompressedBatch")) {
saveFormatBatch.tiffUncompressed = keyFile.get_boolean ("Output", "TiffUncompressedBatch");
}
@@ -1852,6 +1862,7 @@ void Options::saveToFile (Glib::ustring fname)
keyFile.set_integer ("Output", "JpegSubSamp", saveFormat.jpegSubSamp);
keyFile.set_integer ("Output", "PngBps", saveFormat.pngBits);
keyFile.set_integer ("Output", "TiffBps", saveFormat.tiffBits);
keyFile.set_boolean ("Output", "TiffFloat", saveFormat.tiffFloat);
keyFile.set_boolean ("Output", "TiffUncompressed", saveFormat.tiffUncompressed);
keyFile.set_boolean ("Output", "SaveProcParams", saveFormat.saveParams);
@@ -1860,6 +1871,7 @@ void Options::saveToFile (Glib::ustring fname)
keyFile.set_integer ("Output", "JpegSubSampBatch", saveFormatBatch.jpegSubSamp);
keyFile.set_integer ("Output", "PngBpsBatch", saveFormatBatch.pngBits);
keyFile.set_integer ("Output", "TiffBpsBatch", saveFormatBatch.tiffBits);
keyFile.set_boolean ("Output", "TiffFloatBatch", saveFormatBatch.tiffFloat);
keyFile.set_boolean ("Output", "TiffUncompressedBatch", saveFormatBatch.tiffUncompressed);
keyFile.set_boolean ("Output", "SaveProcParamsBatch", saveFormatBatch.saveParams);

View File

@@ -50,6 +50,7 @@ struct SaveFormat {
jpegQuality (90),
jpegSubSamp (2),
tiffBits (8),
tiffFloat(false),
tiffUncompressed (true),
saveParams (true)
{
@@ -60,6 +61,7 @@ struct SaveFormat {
int jpegQuality;
int jpegSubSamp; // 1=best compression, 3=best quality
int tiffBits;
bool tiffFloat;
bool tiffUncompressed;
bool saveParams;
};

View File

@@ -40,6 +40,7 @@ SaveFormatPanel::SaveFormatPanel () : listener (nullptr)
format->append ("JPEG (8-bit)");
format->append ("TIFF (8-bit)");
format->append ("TIFF (16-bit)");
format->append ("TIFF (16-bit float)");
format->append ("TIFF (32-bit float)");
format->append ("PNG (8-bit)");
format->append ("PNG (16-bit)");
@@ -48,8 +49,9 @@ SaveFormatPanel::SaveFormatPanel () : listener (nullptr)
fstr[1] = "tif";
fstr[2] = "tif";
fstr[3] = "tif";
fstr[4] = "png";
fstr[4] = "tif";
fstr[5] = "png";
fstr[6] = "png";
hb1->attach (*flab, 0, 0, 1, 1);
hb1->attach (*format, 1, 0, 1, 1);
@@ -123,10 +125,12 @@ void SaveFormatPanel::init (SaveFormat &sf)
if (sf.format == "jpg") {
format->set_active (0);
} else if (sf.format == "png" && sf.pngBits == 16) {
format->set_active (5);
format->set_active (6);
} else if (sf.format == "png" && sf.pngBits == 8) {
format->set_active (4);
format->set_active (5);
} else if (sf.format == "tif" && sf.tiffBits == 32) {
format->set_active (4);
} else if (sf.format == "tif" && sf.tiffBits == 16 && sf.tiffFloat) {
format->set_active (3);
} else if (sf.format == "tif" && sf.tiffBits == 16) {
format->set_active (2);
@@ -150,20 +154,22 @@ SaveFormat SaveFormatPanel::getFormat ()
int sel = format->get_active_row_number();
sf.format = fstr[sel];
if (sel == 5) {
if (sel == 6) {
sf.pngBits = 16;
} else {
sf.pngBits = 8;
}
if (sel == 2) {
if (sel == 2 || sel == 3) {
sf.tiffBits = 16;
} else if (sel == 3) {
} else if (sel == 4) {
sf.tiffBits = 32;
} else {
sf.tiffBits = 8;
}
sf.tiffFloat = sel == 4 || sel == 3;
sf.jpegQuality = (int) jpegQual->getValue ();
sf.jpegSubSamp = jpegSubSamp->get_active_row_number() + 1;
sf.tiffUncompressed = tiffUncompressed->get_active();

View File

@@ -44,7 +44,7 @@ protected:
Gtk::Grid* jpegOpts;
Gtk::Label* jpegSubSampLabel;
FormatChangeListener* listener;
Glib::ustring fstr[6];
Glib::ustring fstr[7];
Gtk::CheckButton* savesPP;