Bugfix and Enhancement for Clut, Issue 2584, Kudos to Fl?ssie

This commit is contained in:
Ingo
2014-11-22 01:16:24 +01:00
parent 882d3be3e6
commit 77177deb61
9 changed files with 145 additions and 103 deletions

View File

@@ -739,6 +739,8 @@ PREFERENCES_CIEART;CIECAM02 optimization
PREFERENCES_CIEART_LABEL;Use float precision instead of double
PREFERENCES_CIEART_TOOLTIP;If enabled, CIECAM02 calculations are performed in the single-precision floating-point format instead of the double-precision one. This provides a small increase in speed at the expense of a negligible loss of quality.
PREFERENCES_CLIPPINGIND;Clipping Indication
PREFERENCES_CLUTSCACHE;HaldCLUT cache
PREFERENCES_CLUTSCACHE_LABEL;Max number of cached Cluts
PREFERENCES_CLUTSDIR;HaldCLUT directory
PREFERENCES_CMETRICINTENT;Colorimetric intent
PREFERENCES_CUSTPROFBUILDHINT;Executable (or script) file called when a new initial processing profile should be generated for an image.\n\nThe path of the communication file (*.ini style, a.k.a. "Keyfile") is added as a command line parameter. It contains various parameters required for the scripts and image Exif to allow a rules-based processing profile generation.\n\n<b>WARNING:</b> You are responsible for using double quotes where necessary if you're using paths containing spaces.

View File

@@ -2,6 +2,7 @@
#include "rt_math.h"
#include "stdimagesource.h"
#include "safegtk.h"
#include "../rtgui/options.h"
rtengine::CLUTStore clutStore;
@@ -10,49 +11,67 @@ using namespace rtengine;
const float MAXVAL8 = 255.;
CLUTStore::CLUTStore()
: m_refCount( 0 )
{
}
CLUT* CLUTStore::getClut( Glib::ustring filename )
CLUT* CLUTStore::getClut( const Glib::ustring& filename )
{
//MyMutex::MyLock lock(m_mutex); // copypasted from iccstore
CLUT *result = 0;
if ( !m_lastHaldClut.isValid() || m_lastFilename != filename )
{
takeUpClut();
m_lastHaldClut.load( filename );
m_mutex.lock();
Cluts::iterator cluts_it = m_cluts.find(filename);
if (cluts_it == m_cluts.end()) {
if (m_cluts.size() >= options.clutCacheSize) {
// Evict a "random" entry from cache
Cluts::iterator victim_it = m_cluts.begin();
if (--victim_it->second.first == -1) {
delete victim_it->second.second;
m_cluts.erase(victim_it);
}
if ( m_lastHaldClut.isValid() )
{
result = &m_lastHaldClut;
m_lastFilename = filename;
++m_refCount;
}
else
{
cluts_it = m_cluts.insert(std::make_pair(filename, std::make_pair(0, new HaldCLUT))).first;
cluts_it->second.second->load( filename );
}
if (cluts_it->second.second->isValid()) {
result = cluts_it->second.second;
++cluts_it->second.first;
} else {
delete cluts_it->second.second;
m_cluts.erase(cluts_it);
}
m_mutex.unlock();
}
return result;
}
void CLUTStore::takeUpClut()
void CLUTStore::releaseClut( const CLUT* clut )
{
m_mutex.lock();
for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end(); ++cluts_it) {
if (cluts_it->second.second == clut) {
if (--cluts_it->second.first == -1) {
delete cluts_it->second.second;
m_cluts.erase(cluts_it);
}
break;
}
}
void CLUTStore::releaseClut( CLUT *clut )
{
if ( clut && --m_refCount == 0 )
{
m_mutex.unlock();
}
}
void CLUTStore::clearCache()
{
m_lastHaldClut.clear();
m_lastFilename.clear();
m_mutex.lock();
for (Cluts::iterator cluts_it = m_cluts.begin(); cluts_it != m_cluts.end();) {
if (--cluts_it->second.first == -1) {
delete cluts_it->second.second;
Cluts::iterator tmp = cluts_it;
++cluts_it;
m_cluts.erase(tmp);
} else {
++cluts_it;
}
}
m_mutex.unlock();
}
void rtengine::splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName )
@@ -61,26 +80,21 @@ void rtengine::splitClutFilename( Glib::ustring filename, Glib::ustring &name, G
name = filename;
//remove dirs
size_t lastSlashPos = filename.find_last_of( "/" );
if ( lastSlashPos == Glib::ustring::npos )
{
if ( lastSlashPos == Glib::ustring::npos ) {
lastSlashPos = filename.find_last_of( "\\" );
}
size_t lastDotPos = filename.find_last_of( '.' );
if ( lastDotPos != Glib::ustring::npos )
{
if ( lastDotPos != Glib::ustring::npos ) {
name = filename.substr( 0, lastDotPos );
extension = filename.substr( lastDotPos + 1, Glib::ustring::npos );
}
profileName = "sRGB"; // sRGB by default
static std::vector<Glib::ustring> workingProfiles = rtengine::getWorkingProfiles();
for ( std::vector<Glib::ustring>::iterator it = workingProfiles.begin();
it != workingProfiles.end();
++it )
{
for ( std::vector<Glib::ustring>::iterator it = workingProfiles.begin(); it != workingProfiles.end(); ++it ) {
Glib::ustring &currentProfile = *it;
if ( std::search( name.rbegin(), name.rend(), currentProfile.rbegin(), currentProfile.rend() ) == name.rbegin() )
{
if ( std::search( name.rbegin(), name.rend(), currentProfile.rbegin(), currentProfile.rend() ) == name.rbegin() ) {
profileName = currentProfile;
name = name.substr( 0, name.size() - currentProfile.size() );
break;
@@ -94,36 +108,26 @@ HaldCLUT::HaldCLUT()
: m_clutImage( 0 ),
m_level (0),
m_profile( "sRGB" )
{}
{
}
HaldCLUT::~HaldCLUT()
{
}
void HaldCLUT::clear()
{
if ( m_clutImage )
{
if ( m_clutImage ) {
m_clutImage->free();
m_clutImage = 0;
}
m_filename.clear();
}
void HaldCLUT::load( Glib::ustring filename )
{
if ( m_filename != filename )
{
clear();
m_clutImage = loadFile( filename, "", m_level );
Glib::ustring name, ext;
splitClutFilename( filename, name, ext, m_profile );
if ( m_clutImage )
{
if ( m_clutImage ) {
m_filename = filename;
}
}
}
Glib::ustring HaldCLUT::profile() const
{
@@ -134,8 +138,7 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol
{
Imagefloat *result = 0;
StdImageSource imgSrc;
if ( !safe_file_test( filename, Glib::FILE_TEST_EXISTS ) || imgSrc.load(filename) )
{
if ( !safe_file_test( filename, Glib::FILE_TEST_EXISTS ) || imgSrc.load(filename) ) {
return result;
}
@@ -144,18 +147,15 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol
bool valid = false;
//test on Hald format, copypasted from http://www.quelsolaar.com/technology/clut.html
if ( fw == fh )
{
if ( fw == fh ) {
outLevel = 1;
for(; outLevel * outLevel * outLevel < fw; outLevel++);
if( !( outLevel * outLevel * outLevel > fw ) )
{
if( !( outLevel * outLevel * outLevel > fw ) ) {
valid = true;
}
}
if ( valid )
{
if ( valid ) {
ColorTemp currWB = imgSrc.getWB();
Imagefloat* baseImg = new Imagefloat (fw, fh);
PreviewProps pp (0, 0, fw, fh, 1);
@@ -164,8 +164,7 @@ Imagefloat* HaldCLUT::loadFile( Glib::ustring filename, Glib::ustring workingCol
icm.working = workingColorSpace;
imgSrc.getImage (currWB, TR_NONE, baseImg, pp, procparams::ToneCurveParams(), icm, procparams::RAWParams());
if ( !workingColorSpace.empty() )
{
if ( !workingColorSpace.empty() ) {
imgSrc.convertColorSpace(baseImg, icm, currWB, procparams::RAWParams());
}
result = baseImg;
@@ -181,10 +180,8 @@ void HaldCLUT::loadClut( Imagefloat *img, RawClut &outClut )
outClut.resize( x_size * y_size * 3 );
int clutIdx = 0;
//int level = m_level * m_level; (unused)
for(int y = 0; y < y_size; y++)
{
for(int x = 0; x < x_size; x++)
{
for(int y = 0; y < y_size; y++) {
for(int x = 0; x < x_size; x++) {
outClut[ clutIdx * 3 ] = img->r( y, x ) * MAXVAL8;
outClut[ clutIdx * 3 + 1 ] = img->g( y, x ) * MAXVAL8;
outClut[ clutIdx * 3 + 2 ] = img->b( y, x ) * MAXVAL8;
@@ -202,12 +199,9 @@ Imagefloat* HaldCLUT::generateIdentImage( int level )
int cubeSideSize = level * level;
float step = MAXVALF / (cubeSideSize - 1);
int pos = 0;
for( int b = 0; b < cubeSideSize; ++b )
{
for ( int g = 0; g < cubeSideSize; ++g )
{
for ( int r = 0; r < cubeSideSize; ++r )
{
for( int b = 0; b < cubeSideSize; ++b ) {
for ( int g = 0; g < cubeSideSize; ++g ) {
for ( int r = 0; r < cubeSideSize; ++r ) {
int x = pos / imageWidth;
int y = pos % imageWidth;
resultImg->r( x, y ) = step * r;

View File

@@ -5,6 +5,7 @@
#include "../rtgui/threadutils.h"
#include "imagefloat.h"
#include <vector>
#include <map>
namespace rtengine {
@@ -25,7 +26,6 @@ public:
~HaldCLUT();
void load( Glib::ustring filename );
bool isValid() const;
void clear();
void getRGB( float r, float g, float b, float &outR, float &outG, float &outB ) const;
Glib::ustring profile() const;
@@ -51,17 +51,17 @@ class CLUTStore
{
public:
CLUTStore();
CLUT* getClut( Glib::ustring filename );
void takeUpClut();
void releaseClut( CLUT *clut );
CLUT* getClut( const Glib::ustring& filename );
void releaseClut( const CLUT* clut );
void clearCache();
private:
int m_refCount;
MyMutex m_mutex;
typedef std::map<Glib::ustring, std::pair<int, HaldCLUT*> > Cluts;
HaldCLUT m_lastHaldClut;
Glib::ustring m_lastFilename;
Cluts m_cluts;
MyMutex m_mutex;
};
void splitClutFilename( Glib::ustring filename, Glib::ustring &name, Glib::ustring &extension, Glib::ustring &profileName );
@@ -79,12 +79,12 @@ public:
ClutPtr() : m_point( 0 ) {}
explicit ClutPtr(CLUT *p) : m_point( p ) {}
~ClutPtr() { clutStore.releaseClut( m_point ); }
CLUT* operator-> () const { return m_point; }
const CLUT* operator-> () const { return m_point; }
operator bool() const { return m_point != 0; }
void set( CLUT *p ) { m_point = p; }
private:
ClutPtr& operator=(ClutPtr const& cp ) { /*only for clean warning messages*/return *this; }
ClutPtr& operator=(ClutPtr const& cp );
CLUT *m_point;
};

View File

@@ -22,6 +22,7 @@
#include "improcfun.h"
#include "curves.h"
#include "iccstore.h"
#include "clutstore.h"
#include "processingjob.h"
#include <glibmm.h>
#include "../rtgui/options.h"
@@ -678,6 +679,10 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
if (settings->verbose)
printf("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", autor, autog, autob);
// if clut was used and size of clut cache == 1 we free the memory used by the clutstore (default clut cache size = 1 for 32 bit OS)
if ( params.filmSimulation.enabled && !params.filmSimulation.clutFilename.empty() && options.clutCacheSize == 1)
clutStore.clearCache();
// freeing up some memory
customToneCurve1.Reset();
customToneCurve2.Reset();

View File

@@ -114,6 +114,7 @@ void FilmSimulation::read( const rtengine::procparams::ProcParams* pp, const Par
}
if ( !m_enabled->get_inconsistent() && !pp->filmSimulation.enabled )
{
if (options.clutCacheSize == 1)
clutStore.clearCache();
}

View File

@@ -27,6 +27,11 @@
#include "../rtengine/safegtk.h"
#include "version.h"
#ifdef _OPENMP
#include <omp.h>
#endif
#ifdef WIN32
#include <windows.h>
// for GCC32
@@ -354,7 +359,11 @@ void Options::setDefaults () {
histogramFullMode = false;
rgbDenoiseThreadLimit = 0;
#if defined( _OPENMP ) && defined( __x86_64__ )
clutCacheSize = omp_get_num_procs();
#else
clutCacheSize = 1;
#endif
filledProfile = false;
showProfileSelector = true;
@@ -736,8 +745,7 @@ if (keyFile.has_group ("Performance")) {
if (keyFile.has_key ("Performance", "LevNRAUT")) rtSettings.leveldnaut = keyFile.get_integer("Performance", "LevNRAUT");
if (keyFile.has_key ("Performance", "LevNRLISS")) rtSettings.leveldnliss = keyFile.get_integer("Performance", "LevNRLISS");
if (keyFile.has_key ("Performance", "SIMPLNRAUT")) rtSettings.leveldnautsimpl = keyFile.get_integer("Performance", "SIMPLNRAUT");
if (keyFile.has_key ("Performance", "ClutCacheSize")) clutCacheSize = keyFile.get_integer ("Performance", "ClutCacheSize");
}
if (keyFile.has_group ("GUI")) {
@@ -1019,6 +1027,7 @@ int Options::saveToFile (Glib::ustring fname) {
keyFile.set_integer ("Performance", "LevNRAUT", rtSettings.leveldnaut);
keyFile.set_integer ("Performance", "LevNRLISS", rtSettings.leveldnliss);
keyFile.set_integer ("Performance", "SIMPLNRAUT", rtSettings.leveldnautsimpl);
keyFile.set_integer ("Performance", "ClutCacheSize", clutCacheSize);
keyFile.set_string ("Output", "Format", saveFormat.format);
keyFile.set_integer ("Output", "JpegQuality", saveFormat.jpegQuality);

View File

@@ -219,7 +219,7 @@ class Options {
// Performance options
int rgbDenoiseThreadLimit; // maximum number of threads for the denoising tool ; 0 = use the maximum available
int clutCacheSize;
bool filledProfile; // Used as reminder for the ProfilePanel "mode"
bool menuGroupRank;

View File

@@ -27,6 +27,9 @@
#include <sstream>
#include "../rtengine/safegtk.h"
#include "rtimage.h"
#ifdef _OPENMP
#include <omp.h>
#endif
extern Options options;
extern Glib::ustring argv0;
@@ -595,6 +598,32 @@ Gtk::Widget* Preferences::getPerformancePanel () {
fdenoise->add (*mainContainer);
mvbsd->pack_start (*fdenoise, Gtk::PACK_SHRINK, 4);
Gtk::Frame* fclut = Gtk::manage( new Gtk::Frame (M("PREFERENCES_CLUTSCACHE")) );
Gtk::HBox* clutCacheSizeHB = Gtk::manage( new Gtk::HBox () );
clutCacheSizeHB->set_border_width(4);
clutCacheSizeHB->set_spacing(4);
// clutCacheSizeHB->set_tooltip_text(M("PREFERENCES_CLUTCACHESIZE_TOOLTIP"));
Gtk::Label* CLUTLl = Gtk::manage( new Gtk::Label (M("PREFERENCES_CLUTSCACHE_LABEL") + ":", Gtk::ALIGN_LEFT));
clutCacheSizeSB = Gtk::manage( new Gtk::SpinButton () );
clutCacheSizeSB->set_digits (0);
clutCacheSizeSB->set_increments (1, 5);
clutCacheSizeSB->set_max_length(2); // Will this be sufficient? :)
#ifdef _OPENMP
clutCacheSizeSB->set_range (1, 2*omp_get_num_procs());
#else
clutCacheSizeSB->set_range (1, 8);
#endif
clutCacheSizeHB->pack_start (*CLUTLl, Gtk::PACK_SHRINK, 0);
clutCacheSizeHB->pack_end (*clutCacheSizeSB, Gtk::PACK_SHRINK, 0);
fclut->add (*clutCacheSizeHB);
mvbsd->pack_start (*fclut, Gtk::PACK_SHRINK, 4);
// return mainContainer;
return mvbsd;
@@ -1377,6 +1406,7 @@ void Preferences::storePreferences () {
moptions.UseIconNoText = ckbUseIconNoText->get_active();
moptions.rgbDenoiseThreadLimit = rgbDenoiseTreadLimitSB->get_value_as_int();
moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int();
// Sounds only on Windows and Linux
#if defined(WIN32) || defined(__linux__)
@@ -1522,7 +1552,7 @@ void Preferences::fillPreferences () {
ckbUseIconNoText->set_active(moptions.UseIconNoText);
rgbDenoiseTreadLimitSB->set_value(moptions.rgbDenoiseThreadLimit);
clutCacheSizeSB->set_value(moptions.clutCacheSize);
//darkFrameDir->set_filename( moptions.rtSettings.darkFramesPath );
//updateDFinfos();
darkFrameDir->set_current_folder( moptions.rtSettings.darkFramesPath );

View File

@@ -124,6 +124,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener {
Gtk::CheckButton* sameThumbSize;
Gtk::SpinButton* rgbDenoiseTreadLimitSB;
Gtk::SpinButton* clutCacheSizeSB;
Gtk::CheckButton* ckbmenuGroupRank;
Gtk::CheckButton* ckbmenuGroupLabel;