From 3f13a98135d7bfe8d03c6603b435cc99e2051355 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 1 Nov 2016 02:20:43 +0100 Subject: [PATCH 001/181] Prepare rtengine to allow extraction of individual frames from multi frame raw files (i.e. Pentax pixel shift) --- rtengine/rawimage.cc | 4 +++- rtengine/rawimage.h | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 382ebe0a7..2d778a3b5 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -398,7 +398,7 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistener, double progressRange) +int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistener, double progressRange, unsigned int frameNum) { ifname = filename.c_str(); image = nullptr; @@ -435,6 +435,8 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene return 2; } + setFrameNumber(frameNum); + if (flip == 5) { this->rotate_deg = 270; } else if (flip == 3) { diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 88ef5d710..c3da24f0d 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -106,7 +106,7 @@ public: explicit RawImage( const Glib::ustring &name ); ~RawImage(); - int loadRaw (bool loadData = true, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); + int loadRaw (bool loadData = true, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0, unsigned int frameNum = 0); void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); void set_prefilters() { @@ -307,6 +307,13 @@ public: return zero_is_bad == 1 ? true : false; } + void setFrameNumber(unsigned int frameNum) { + // a single raw file can contain more than one frame. + // Variable is_raw holds the number of frames in the raw file + // use setFrameNumber() to set the number of the frame [0; is_raw - 1] to be processed + shot_select = std::min(std::max(is_raw, 1) - 1, frameNum); + } + public: // dcraw functions void scale_colors() From a6876e258ab10d7138f844e11252ae876f940ac2 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 1 Nov 2016 14:25:17 +0100 Subject: [PATCH 002/181] Fixed compilation error --- rtengine/rawimage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index c3da24f0d..474b9b8ee 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -311,7 +311,7 @@ public: // a single raw file can contain more than one frame. // Variable is_raw holds the number of frames in the raw file // use setFrameNumber() to set the number of the frame [0; is_raw - 1] to be processed - shot_select = std::min(std::max(is_raw, 1) - 1, frameNum); + shot_select = std::min(std::max(is_raw, 1u) - 1, frameNum); } public: From 405e9a29a288afacaa6fb65ad190bd68b7fbbea2 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 1 Nov 2016 15:09:36 +0100 Subject: [PATCH 003/181] set frame number before identify --- rtengine/rawimage.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 2d778a3b5..8c09dbc50 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -13,7 +13,7 @@ #else #include #endif - +#include namespace rtengine { @@ -422,6 +422,8 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene raw_image = nullptr; //***************** Read ALL raw file info + setFrameNumber(frameNum); + identify (); if (!is_raw) { @@ -435,8 +437,6 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene return 2; } - setFrameNumber(frameNum); - if (flip == 5) { this->rotate_deg = 270; } else if (flip == 3) { From aca3b5aa825eb450bc9fe26b28b5c15255533857 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 1 Nov 2016 16:33:59 +0100 Subject: [PATCH 004/181] Fix bug when requesting a nonexistant frame from Fuji RAF --- rtengine/dcraw.cc | 2 +- rtengine/dcraw.patch | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index dcfe66ee5..d4a9811d0 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -8572,7 +8572,7 @@ void CLASS identify() parse_fuji (i); } load_raw = &CLASS unpacked_load_raw; - fseek (ifp, 100+28*(shot_select > 0), SEEK_SET); + fseek (ifp, 100+28*(shot_select > 0 && shot_select < is_raw), SEEK_SET); parse_tiff (data_offset = get4()); parse_tiff (thumb_offset+12); /*RT*/ exif_base = thumb_offset+12; diff --git a/rtengine/dcraw.patch b/rtengine/dcraw.patch index 22c53d831..43f9e12eb 100644 --- a/rtengine/dcraw.patch +++ b/rtengine/dcraw.patch @@ -1,5 +1,5 @@ ---- dcraw.c 2016-10-28 13:45:27 +0000 -+++ dcraw.cc 2016-10-31 13:35:15 +0000 +--- dcraw.c 2016-11-01 01:07:55 +0000 ++++ dcraw.cc 2016-11-01 14:54:02 +0000 @@ -1,3 +1,16 @@ +/*RT*/#include +/*RT*/#include @@ -2892,8 +2892,12 @@ parse_ciff (hlen, flen-hlen, 0); load_raw = &CLASS canon_load_raw; } else if (parse_tiff(0)) apply_tiff(); -@@ -8494,6 +8575,7 @@ - fseek (ifp, 100+28*(shot_select > 0), SEEK_SET); +@@ -8491,9 +8572,10 @@ + parse_fuji (i); + } + load_raw = &CLASS unpacked_load_raw; +- fseek (ifp, 100+28*(shot_select > 0), SEEK_SET); ++ fseek (ifp, 100+28*(shot_select > 0 && shot_select < is_raw), SEEK_SET); parse_tiff (data_offset = get4()); parse_tiff (thumb_offset+12); +/*RT*/ exif_base = thumb_offset+12; From 742d7ee0374c8b001dd4664f562bd467aa79ecef Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 1 Nov 2016 16:46:57 +0100 Subject: [PATCH 005/181] Small corrections to set the number of the frame to be extracted from raw file --- rtengine/rawimage.cc | 6 +++++- rtengine/rawimage.h | 7 ------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 8c09dbc50..b4add328b 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -422,10 +422,14 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene raw_image = nullptr; //***************** Read ALL raw file info - setFrameNumber(frameNum); + // set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly + shot_select = frameNum; identify (); + // in case dcraw didn't handle the above mentioned case... + shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); + if (!is_raw) { fclose(ifp); ifp = nullptr; diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 474b9b8ee..2ba434960 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -307,13 +307,6 @@ public: return zero_is_bad == 1 ? true : false; } - void setFrameNumber(unsigned int frameNum) { - // a single raw file can contain more than one frame. - // Variable is_raw holds the number of frames in the raw file - // use setFrameNumber() to set the number of the frame [0; is_raw - 1] to be processed - shot_select = std::min(std::max(is_raw, 1u) - 1, frameNum); - } - public: // dcraw functions void scale_colors() From d8593469eeccb04c6eb7871df8179acef4b28b04 Mon Sep 17 00:00:00 2001 From: Hombre Date: Wed, 2 Nov 2016 02:06:40 +0100 Subject: [PATCH 006/181] Adding 'Image Number' to the Bayer Raw tool --- rtdata/languages/default | 4 ++ rtengine/imagesource.h | 2 +- rtengine/improccoordinator.cc | 2 +- rtengine/improcfun.cc | 2 +- rtengine/procevents.h | 1 + rtengine/procparams.cc | 14 ++++++ rtengine/procparams.h | 1 + rtengine/rawimage.cc | 4 +- rtengine/rawimage.h | 2 +- rtengine/rawimagesource.cc | 4 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 3 +- rtengine/rtthumbnail.cc | 4 +- rtengine/rtthumbnail.h | 2 +- rtengine/stdimagesource.cc | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/bayerprocess.cc | 92 ++++++++++++++++++++++++----------- rtgui/bayerprocess.h | 7 ++- rtgui/paramsedited.cc | 8 ++- rtgui/paramsedited.h | 1 + rtgui/partialpastedlg.cc | 12 ++++- rtgui/partialpastedlg.h | 3 +- rtgui/thumbnail.cc | 2 +- 23 files changed, 126 insertions(+), 50 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index df9aaec7c..c66c3a64d 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -675,6 +675,7 @@ HISTORY_MSG_440;CbDL - Method HISTORY_MSG_441;Retinex - Gain transmission HISTORY_MSG_442;Retinex - Scale HISTORY_MSG_443;Output Black Point Compensation +HISTORY_MSG_444;Raw Sub-Image HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -864,6 +865,7 @@ PARTIALPASTE_RAW_DCBENHANCE;DCB enhancement PARTIALPASTE_RAW_DCBITERATIONS;DCB iterations PARTIALPASTE_RAW_DMETHOD;Demosaic method PARTIALPASTE_RAW_FALSECOLOR;False color suppression +PARTIALPASTE_RAW_IMAGENUM;Sub-image PARTIALPASTE_RAW_LMMSEITERATIONS;LMMSE enhancement steps PARTIALPASTE_RESIZE;Resize PARTIALPASTE_RETINEX;Retinex @@ -1647,6 +1649,8 @@ TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to a TP_RAW_FALSECOLOR;False color suppression steps TP_RAW_HD;Threshold TP_RAW_HD_TOOLTIP;Lower values make hot/dead pixel detection more aggressive, but false positives may lead to artifacts. If you notice any artifacts appearing when enabling the Hot/Dead Pixel Filters, gradually increase the threshold value until they disappear. +TP_RAW_IMAGENUM;Sub-image +TP_RAW_IMAGENUM;Some raw files might embed several sub-images (HDR, Pixel-Shift, Dual Sensitivity). Use this button to select the sub-image.\n\nThe last sub-image will be used if you select a value beyond the real sub-image count. TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 59d46035b..4acffef6c 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -66,7 +66,7 @@ public: embProfile(nullptr), idata(nullptr), dirpyrdenoiseExpComp(INFINITY) {} virtual ~ImageSource () {} - virtual int load (const Glib::ustring &fname, bool batch = false) = 0; + virtual int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false) = 0; virtual void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true) {}; virtual void demosaic (const RAWParams &raw) {}; virtual void retinex (ColorManagementParams cmp, RetinexParams deh, ToneCurveParams Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI) {}; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index ca1d44cbf..741370813 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -214,7 +214,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) if (settings->verbose) { if (imgsrc->getSensorType() == ST_BAYER) { - printf("Demosaic Bayer image using method: %s\n", rp.bayersensor.method.c_str()); + printf("Demosaic Bayer image n.%d using method: %s\n", rp.bayersensor.imageNum + 1, rp.bayersensor.method.c_str()); } else if (imgsrc->getSensorType() == ST_FUJI_XTRANS) { printf("Demosaic X-Trans image with using method: %s\n", rp.xtranssensor.method.c_str()); } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 586243391..4860648da 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -6902,7 +6902,7 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si return 0.0; } - Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, w_raw, h_raw, 1, 1.0, FALSE); + Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, w_raw, h_raw, 1, 1.0, FALSE, 0); if (!raw) { delete thumb; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 52517e527..69cda228a 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -470,6 +470,7 @@ enum ProcEvent { EvRetinexgaintransmission = 440, EvLskal = 441, EvOBPCompens = 442, + EvRawImageNum = 443, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 5a8ee39a6..fe0d450c9 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -879,6 +879,7 @@ void CoarseTransformParams::setDefaults() void RAWParams::setDefaults() { bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze]; + bayersensor.imageNum = 0; bayersensor.ccSteps = 0; bayersensor.dcb_iterations = 2; bayersensor.dcb_enhance = true; @@ -3315,6 +3316,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_string ("RAW Bayer", "Method", raw.bayersensor.method ); } + if (!pedited || pedited->raw.bayersensor.imageNum) { + keyFile.set_integer ("RAW Bayer", "ImageNum", raw.bayersensor.imageNum + 1 ); + } + if (!pedited || pedited->raw.bayersensor.ccSteps) { keyFile.set_integer ("RAW Bayer", "CcSteps", raw.bayersensor.ccSteps); } @@ -7321,6 +7326,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "ImageNum")) { + raw.bayersensor.imageNum = keyFile.get_integer ("RAW Bayer", "ImageNum") - 1; + + if (pedited) { + pedited->raw.bayersensor.imageNum = true; + } + } + if (keyFile.has_key ("RAW Bayer", "CcSteps")) { raw.bayersensor.ccSteps = keyFile.get_integer ("RAW Bayer", "CcSteps"); @@ -7837,6 +7850,7 @@ bool ProcParams::operator== (const ProcParams& other) && resize.width == other.resize.width && resize.height == other.resize.height && raw.bayersensor.method == other.raw.bayersensor.method + && raw.bayersensor.imageNum == other.raw.bayersensor.imageNum && raw.bayersensor.ccSteps == other.raw.bayersensor.ccSteps && raw.bayersensor.black0 == other.raw.bayersensor.black0 && raw.bayersensor.black1 == other.raw.bayersensor.black1 diff --git a/rtengine/procparams.h b/rtengine/procparams.h index c65e89e24..fc539144b 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1171,6 +1171,7 @@ public: static const char *methodstring[numMethods]; Glib::ustring method; + int imageNum; int ccSteps; double black0; double black1; diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index b4add328b..0ec9f548c 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -398,7 +398,7 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistener, double progressRange, unsigned int frameNum) +int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) { ifname = filename.c_str(); image = nullptr; @@ -423,7 +423,7 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene //***************** Read ALL raw file info // set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly - shot_select = frameNum; + shot_select = imageNum; identify (); diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 2ba434960..4f39ac855 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -106,7 +106,7 @@ public: explicit RawImage( const Glib::ustring &name ); ~RawImage(); - int loadRaw (bool loadData = true, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0, unsigned int frameNum = 0); + int loadRaw (bool loadData = true, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); void set_prefilters() { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index b2e3e5e56..737e733cd 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1494,7 +1494,7 @@ void RawImageSource::vflip (Imagefloat* image) //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -int RawImageSource::load (const Glib::ustring &fname, bool batch) +int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) { MyTime t1, t2; @@ -1507,7 +1507,7 @@ int RawImageSource::load (const Glib::ustring &fname, bool batch) } ri = new RawImage(fname); - int errCode = ri->loadRaw (true, true, plistener, 0.8); + int errCode = ri->loadRaw (true, imageNum, true, plistener, 0.8); if (errCode) { return errCode; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 151edf959..cec3d78b7 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -107,7 +107,7 @@ public: RawImageSource (); ~RawImageSource (); - int load (const Glib::ustring &fname, bool batch = false); + int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false); void preprocess (const RAWParams &raw, const LensProfParams &lensProf, const CoarseTransformParams& coarse, bool prepareDenoise = true); void demosaic (const RAWParams &raw); void retinex (ColorManagementParams cmp, RetinexParams deh, ToneCurveParams Tc, LUTf & cdcurve, LUTf & mapcurve, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, multi_array2D &conversionBuffer, bool dehacontlutili, bool mapcontlutili, bool useHsl, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, LUTu &histLRETI); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 025265e0a..8325ac17d 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -469,7 +469,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { ALLNORAW, // EvcbdlMethod RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal - OUTPUTPROFILE // EvOBPCompens + OUTPUTPROFILE, // EvOBPCompens + DEMOSAIC // EvRawImageNum }; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index c135acc39..afe60d5a0 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -281,10 +281,10 @@ RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname) return rml; } -Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate) +Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate, int imageNum) { RawImage *ri = new RawImage (fname); - int r = ri->loadRaw(1, 0); + int r = ri->loadRaw(1, imageNum, 0); if( r ) { delete ri; diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index 18e72fc19..da1c96f8d 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -78,7 +78,7 @@ public: void getDimensions (int& w, int& h, double& scaleFac); static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode = false); - static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate); + static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate, int imageNum); static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode = false); static RawMetaDataLocation loadMetaDataFromRaw (const Glib::ustring& fname); diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 6017a1ca6..2eda4b512 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -110,7 +110,7 @@ void StdImageSource::getSampleFormat (const Glib::ustring &fname, IIOSampleForma * and RT's image data type (Image8, Image16 and Imagefloat), then it will * load the image into it */ -int StdImageSource::load (const Glib::ustring &fname, bool batch) +int StdImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) { fileName = fname; diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 0ef487a75..38b6952b0 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -42,7 +42,7 @@ public: StdImageSource (); ~StdImageSource (); - int load (const Glib::ustring &fname, bool batch = false); + int load (const Glib::ustring &fname, int imageNum = 0, bool batch = false); void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const ColorManagementParams &cmp, const RAWParams &raw); ColorTemp getWB () const { diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 14c542172..3a4f2898a 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -38,6 +38,18 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA hb1->pack_end (*method, Gtk::PACK_EXPAND_WIDGET, 4); pack_start( *hb1, Gtk::PACK_SHRINK, 4); + imageNumberBox = Gtk::manage (new Gtk::HBox ()); + hb1->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_IMAGENUM") + ": ")), Gtk::PACK_SHRINK, 4); + imageNumber = Gtk::manage (new MyComboBoxText ()); + imageNumber->append_text("1"); + imageNumber->append_text("2"); + imageNumber->append_text("3"); + imageNumber->append_text("4"); + imageNumber->set_active(0); + imageNumberBox->set_tooltip_text(M("TP_RAW_IMAGENUM_TOOLTIP")); + imageNumberBox->pack_end (*imageNumber, Gtk::PACK_EXPAND_WIDGET, 4); + pack_start( *imageNumberBox, Gtk::PACK_SHRINK, 4); + dcbOptions = Gtk::manage (new Gtk::VBox ()); dcbOptions->set_border_width(4); @@ -88,6 +100,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA //pack_start( *allOptions, Gtk::PACK_SHRINK, 4); methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) ); + imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -98,27 +111,33 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params disableListener (); methodconn.block (true); dcbEnhconn.block (true); + imagenumberconn.block (true); //allEnhconn.block (true); method->set_active(procparams::RAWParams::BayerSensor::numMethods); + imageNumber->set_active(pp->raw.bayersensor.imageNum); - for( size_t i = 0; i < procparams::RAWParams::BayerSensor::numMethods; i++) + for( size_t i = 0; i < procparams::RAWParams::BayerSensor::numMethods; i++) { if( pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[i]) { method->set_active(i); - oldSelection = i; + oldMethod = i; break; } + } - if(pedited ) { + if(pedited) { ccSteps->setEditedState (pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); - if( !pedited->raw.bayersensor.method ) { + if(!pedited->raw.bayersensor.method) { method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name } + if(!pedited->raw.bayersensor.imageNum) { + imageNumber->set_active(4); + } } //allEnhance->set_active(pp->raw.bayersensor.all_enhance); @@ -126,35 +145,36 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); ccSteps->setValue (pp->raw.bayersensor.ccSteps); - - if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || - method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { - dcbOptions->show(); - } else { - dcbOptions->hide(); - } - lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); - if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::lmmse] || - method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { - lmmseOptions->show(); - } else { - lmmseOptions->hide(); - } + if (!batchMode) { + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + dcbOptions->show(); + } else { + dcbOptions->hide(); + } + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::lmmse] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + lmmseOptions->show(); + } else { + lmmseOptions->hide(); + } - // Flase color suppression is applied to all demozaicing method, so don't hide anything - /*if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::eahd] || - pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::hphd] || - pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::vng4]) - ccSteps->show(); - else - ccSteps->hide();*/ + // Flase color suppression is applied to all demozaicing method, so don't hide anything + /*if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::eahd] || + pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::hphd] || + pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::vng4]) + ccSteps->show(); + else + ccSteps->hide();*/ + } lastDCBen = pp->raw.bayersensor.dcb_enhance; //lastALLen = pp->raw.bayersensor.all_enhance; methodconn.block (false); + imagenumberconn.block (false); dcbEnhconn.block (false); //allEnhconn.block (false); @@ -170,19 +190,24 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); int currentRow = method->get_active_row_number(); - if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { pp->raw.bayersensor.method = procparams::RAWParams::BayerSensor::methodstring[currentRow]; } + currentRow = imageNumber->get_active_row_number(); + if (currentRow < 4) { + pp->raw.bayersensor.imageNum = currentRow; + } + + if (pedited) { pedited->raw.bayersensor.ccSteps = ccSteps->getEditedState (); pedited->raw.bayersensor.method = method->get_active_row_number() != procparams::RAWParams::BayerSensor::numMethods; + pedited->raw.bayersensor.imageNum = imageNumber->get_active_row_number() < 4; pedited->raw.bayersensor.dcbIterations = dcbIterations->getEditedState (); pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); - } } @@ -190,6 +215,8 @@ void BayerProcess::setBatchMode(bool batchMode) { method->append_text (M("GENERAL_UNCHANGED")); method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name + imageNumber->append_text (M("GENERAL_UNCHANGED")); + imageNumber->set_active(4); dcbOptions->hide(); lmmseOptions->hide(); ToolPanel::setBatchMode (batchMode); @@ -250,18 +277,25 @@ void BayerProcess::methodChanged () if( curSelection >= 0 && curSelection < procparams::RAWParams::BayerSensor::numMethods) { methodName = procparams::RAWParams::BayerSensor::methodstring[curSelection]; - if (curSelection == procparams::RAWParams::BayerSensor::mono || oldSelection == procparams::RAWParams::BayerSensor::mono) { + if (curSelection == procparams::RAWParams::BayerSensor::mono || oldMethod == procparams::RAWParams::BayerSensor::mono) { ppreq = true; } } - oldSelection = curSelection; + oldMethod = curSelection; if (listener) { listener->panelChanged (ppreq ? EvDemosaicMethodPreProc : EvDemosaicMethod, methodName); } } +void BayerProcess::imageNumberChanged () +{ + if (listener) { + listener->panelChanged (EvRawImageNum, imageNumber->get_active_text()); + } +} + void BayerProcess::dcbEnhanceChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index b78002ea3..bba4bd51d 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -31,6 +31,8 @@ class BayerProcess : public ToolParamBlock, public AdjusterListener, public Fold protected: MyComboBoxText* method; + Gtk::HBox *imageNumberBox; + MyComboBoxText* imageNumber; Adjuster* ccSteps; Gtk::VBox *dcbOptions; Adjuster* dcbIterations; @@ -41,9 +43,9 @@ protected: Adjuster* lmmseIterations; bool lastDCBen; - int oldSelection; + int oldMethod; //bool lastALLen; - sigc::connection methodconn, dcbEnhconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn; //,allEnhconn; public: BayerProcess (); @@ -54,6 +56,7 @@ public: void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); void methodChanged (); + void imageNumberChanged (); void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); //void allEnhanceChanged(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ff3e4c0b1..ebb31ef1d 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -359,6 +359,7 @@ void ParamsEdited::set (bool v) icm.gampos = v; icm.slpos = v; raw.bayersensor.method = v; + raw.bayersensor.imageNum = v; raw.bayersensor.ccSteps = v; raw.bayersensor.exBlack0 = v; raw.bayersensor.exBlack1 = v; @@ -854,6 +855,7 @@ void ParamsEdited::initFrom (const std::vector icm.gampos = icm.gampos && p.icm.gampos == other.icm.gampos; icm.slpos = icm.slpos && p.icm.slpos == other.icm.slpos; raw.bayersensor.method = raw.bayersensor.method && p.raw.bayersensor.method == other.raw.bayersensor.method; + raw.bayersensor.imageNum = raw.bayersensor.imageNum && p.raw.bayersensor.imageNum == other.raw.bayersensor.imageNum; raw.bayersensor.ccSteps = raw.bayersensor.ccSteps && p.raw.bayersensor.ccSteps == other.raw.bayersensor.ccSteps; raw.bayersensor.exBlack0 = raw.bayersensor.exBlack0 && p.raw.bayersensor.black0 == other.raw.bayersensor.black0; raw.bayersensor.exBlack1 = raw.bayersensor.exBlack1 && p.raw.bayersensor.black1 == other.raw.bayersensor.black1; @@ -2236,6 +2238,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.method = mods.raw.bayersensor.method; } + if (raw.bayersensor.imageNum) { + toEdit.raw.bayersensor.imageNum = mods.raw.bayersensor.imageNum; + } + if (raw.bayersensor.ccSteps) { toEdit.raw.bayersensor.ccSteps = mods.raw.bayersensor.ccSteps; } @@ -2782,7 +2788,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten bool RAWParamsEdited::BayerSensor::isUnchanged() const { - return method && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq + return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 19d143398..4e9fa4eb4 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -682,6 +682,7 @@ public: public: bool method; + bool imageNum; bool ccSteps; bool exBlack0; bool exBlack1; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index 78631b725..19a83381e 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -112,6 +112,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title) raw_linenoise = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_LINEDENOISE"))); raw_greenthresh = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_GREENEQUIL"))); raw_method = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DMETHOD"))); + raw_imagenum = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_IMAGENUM"))); raw_ccSteps = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_FALSECOLOR"))); raw_dcb_iterations = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DCBITERATIONS"))); raw_dcb_enhance = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DCBENHANCE"))); @@ -201,6 +202,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title) vboxes[6]->pack_start (*raw, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*hseps[6], Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_method, Gtk::PACK_SHRINK, 2); + vboxes[6]->pack_start (*raw_imagenum, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_ccSteps, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_dcb_iterations, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*raw_dcb_enhance, Gtk::PACK_SHRINK, 2); @@ -340,7 +342,8 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title) exifchConn = exifch->signal_toggled().connect (sigc::bind (sigc::mem_fun(*meta, &Gtk::CheckButton::set_inconsistent), true)); iptcConn = iptc->signal_toggled().connect (sigc::bind (sigc::mem_fun(*meta, &Gtk::CheckButton::set_inconsistent), true)); - raw_methodConn = raw_method->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); + raw_methodConn = raw_method->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); + raw_imagenumConn = raw_imagenum->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_ccStepsConn = raw_ccSteps->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_dcb_iterationsConn = raw_dcb_iterations->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); raw_dcb_enhanceConn = raw_dcb_enhance->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true)); @@ -420,6 +423,7 @@ void PartialPasteDlg::rawToggled () { raw_methodConn.block (true); + raw_imagenumConn.block (true); raw_ccStepsConn.block (true); raw_dcb_iterationsConn.block (true); raw_dcb_enhanceConn.block (true); @@ -446,6 +450,7 @@ void PartialPasteDlg::rawToggled () raw->set_inconsistent (false); raw_method->set_active (raw->get_active ()); + raw_imagenum->set_active (raw->get_active ()); raw_ccSteps->set_active (raw->get_active ()); raw_dcb_iterations->set_active (raw->get_active ()); raw_dcb_enhance->set_active (raw->get_active ()); @@ -470,6 +475,7 @@ void PartialPasteDlg::rawToggled () ff_ClipControl->set_active (raw->get_active ()); raw_methodConn.block (false); + raw_imagenumConn.block (false); raw_ccStepsConn.block (false); raw_dcb_iterationsConn.block (false); raw_dcb_enhanceConn.block (false); @@ -849,6 +855,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param filterPE.raw.xtranssensor.method = falsePE.raw.xtranssensor.method; } + if (!raw_imagenum->get_active ()) { + filterPE.raw.bayersensor.imageNum = falsePE.raw.bayersensor.imageNum; + } + if (!raw_ccSteps->get_active ()) { filterPE.raw.bayersensor.ccSteps = falsePE.raw.bayersensor.ccSteps; filterPE.raw.xtranssensor.ccSteps = falsePE.raw.xtranssensor.ccSteps; diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h index d22f50fad..8c9118755 100644 --- a/rtgui/partialpastedlg.h +++ b/rtgui/partialpastedlg.h @@ -107,6 +107,7 @@ public: Gtk::CheckButton* raw_linenoise; Gtk::CheckButton* raw_greenthresh; Gtk::CheckButton* raw_method; + Gtk::CheckButton* raw_imagenum; Gtk::CheckButton* raw_ccSteps; Gtk::CheckButton* raw_dcb_iterations; Gtk::CheckButton* raw_dcb_enhance; @@ -129,7 +130,7 @@ public: sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn; sigc::connection exifchConn, iptcConn, icmConn; sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_BlurRadiusConn, ff_BlurTypeConn, ff_ClipControlConn; - sigc::connection raw_caredConn, raw_cablueConn, raw_ca_autocorrectConn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn; + sigc::connection raw_caredConn, raw_cablueConn, raw_ca_autocorrectConn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_imagenumConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn; public: explicit PartialPasteDlg (const Glib::ustring &title); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 71aeab0ab..300f61e09 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -137,7 +137,7 @@ void Thumbnail::_generateThumbnailImage () if ( tpp == nullptr ) { quick = false; - tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1, pparams.wb.equal, TRUE); + tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1, pparams.wb.equal, TRUE, pparams.raw.bayersensor.imageNum); } if (tpp) { From 6276b17be29f1e21c41f9e6d270c3d0b35898c88 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 2 Nov 2016 02:47:54 +0100 Subject: [PATCH 007/181] Fix segfault --- rtengine/rtthumbnail.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index afe60d5a0..6321f0dcf 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -156,7 +156,7 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode) { RawImage *ri = new RawImage(fname); - int r = ri->loadRaw(false, false); + int r = ri->loadRaw(false, 0, false); if( r ) { delete ri; From 89901f4b367f205087cc88857cfe66d04cca38b3 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 3 Nov 2016 00:01:47 +0100 Subject: [PATCH 008/181] reworked pixelshift code and temporary removed Fuji S5 dual frame support --- rtengine/dfmanager.cc | 11 +++++++---- rtengine/ffmanager.cc | 12 +++++++----- rtengine/imagesource.h | 3 +++ rtengine/improccoordinator.cc | 1 + rtengine/rawimage.cc | 4 +++- rtengine/rawimage.h | 2 +- rtengine/rawimagesource.cc | 30 ++++++++++++++++++++++-------- rtengine/rawimagesource.h | 11 +++++++++-- rtengine/refreshmap.cc | 2 +- rtengine/rtthumbnail.cc | 11 ++++++++--- rtengine/simpleprocess.cc | 1 + rtengine/stdimagesource.h | 2 ++ 12 files changed, 65 insertions(+), 25 deletions(-) diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc index ab76ff854..1dedf661f 100644 --- a/rtengine/dfmanager.cc +++ b/rtengine/dfmanager.cc @@ -138,7 +138,8 @@ void dfInfo::updateRawImage() std::list::iterator iName = pathNames.begin(); ri = new RawImage(*iName); // First file used also for extra pixels informations (width,height, shutter, filters etc.. ) - if( ri->loadRaw(true)) { + unsigned int imageNum = 0; + if( ri->loadRaw(true, imageNum)) { delete ri; ri = nullptr; } else { @@ -163,7 +164,7 @@ void dfInfo::updateRawImage() for( ++iName; iName != pathNames.end(); ++iName) { RawImage* temp = new RawImage(*iName); - if( !temp->loadRaw(true)) { + if( !temp->loadRaw(true,imageNum)) { temp->compress_image(); //\ TODO would be better working on original, because is temporary nFiles++; @@ -199,8 +200,9 @@ void dfInfo::updateRawImage() } } else { ri = new RawImage(pathname); + unsigned int imageNum = 0; - if( ri->loadRaw(true)) { + if( ri->loadRaw(true,imageNum)) { delete ri; ri = nullptr; } else { @@ -365,7 +367,8 @@ dfInfo* DFManager::addFileInfo (const Glib::ustring& filename, bool pool) } RawImage ri (filename); - int res = ri.loadRaw (false); // Read informations about shot + unsigned int imageNum = 0; + int res = ri.loadRaw (false, imageNum); // Read informations about shot if (res != 0) { return nullptr; diff --git a/rtengine/ffmanager.cc b/rtengine/ffmanager.cc index 069bbf563..42a46802b 100644 --- a/rtengine/ffmanager.cc +++ b/rtengine/ffmanager.cc @@ -130,8 +130,8 @@ void ffInfo::updateRawImage() if( !pathNames.empty() ) { std::list::iterator iName = pathNames.begin(); ri = new RawImage(*iName); // First file used also for extra pixels informations (width,height, shutter, filters etc.. ) - - if( ri->loadRaw(true)) { + unsigned int imageNum = 0; + if( ri->loadRaw(true, imageNum)) { delete ri; ri = nullptr; } else { @@ -156,7 +156,7 @@ void ffInfo::updateRawImage() for( ++iName; iName != pathNames.end(); ++iName) { RawImage* temp = new RawImage(*iName); - if( !temp->loadRaw(true)) { + if( !temp->loadRaw(true,imageNum)) { temp->compress_image(); //\ TODO would be better working on original, because is temporary nFiles++; @@ -192,8 +192,9 @@ void ffInfo::updateRawImage() } } else { ri = new RawImage(pathname); + unsigned int imageNum = 0; - if( ri->loadRaw(true)) { + if( ri->loadRaw(true, imageNum)) { delete ri; ri = nullptr; } else { @@ -326,7 +327,8 @@ ffInfo* FFManager::addFileInfo (const Glib::ustring& filename, bool pool) RawImage ri (filename); - int res = ri.loadRaw (false); // Read informations about shot + unsigned int imageNum = 0; + int res = ri.loadRaw (false, imageNum); // Read informations about shot if (res != 0) { return nullptr; diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 4acffef6c..98e5446a1 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -80,6 +80,9 @@ public: virtual bool IsrgbSourceModified() const = 0; // tracks whether cached rgb output of demosaic has been modified + virtual void setCurrentFrame(unsigned int frameNum) = 0; + + // use right after demosaicing image, add coarse transformation and put the result in the provided Imagefloat* virtual void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hlp, const ColorManagementParams &cmp, const RAWParams &raw) = 0; virtual eSensorType getSensorType () diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 741370813..afa791b4f 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -185,6 +185,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) // raw auto CA is bypassed if no high detail is needed, so we have to compute it when high detail is needed if ( (todo & M_PREPROC) || (!highDetailPreprocessComputed && highDetailNeeded)) { + imgsrc->setCurrentFrame(params.raw.bayersensor.imageNum); imgsrc->preprocess( rp, params.lensProf, params.coarse ); imgsrc->getRAWHistogram( histRedRaw, histGreenRaw, histBlueRaw ); diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 0ec9f548c..85d6a00b5 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -398,7 +398,7 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) +int RawImage::loadRaw (bool loadData, unsigned int &imageNum, bool closeFile, ProgressListener *plistener, double progressRange) { ifname = filename.c_str(); image = nullptr; @@ -427,8 +427,10 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro identify (); + std::cout << "israw : " << is_raw << std::endl; // in case dcraw didn't handle the above mentioned case... shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); + imageNum = shot_select; if (!is_raw) { fclose(ifp); diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 4f39ac855..4080702a6 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -106,7 +106,7 @@ public: explicit RawImage( const Glib::ustring &name ); ~RawImage(); - int loadRaw (bool loadData = true, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); + int loadRaw (bool loadData, unsigned int &imageNum, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); void set_prefilters() { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 737e733cd..1d30706aa 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -475,8 +475,8 @@ RawImageSource::~RawImageSource () delete idata; - if (ri) { - delete ri; + for(size_t i = 0; i < numFrames; ++i) { + delete riFrames[i]; } flushRGB(); @@ -1506,14 +1506,29 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgress (0.0); } - ri = new RawImage(fname); - int errCode = ri->loadRaw (true, imageNum, true, plistener, 0.8); + unsigned int tempImageNum = 4; + do { + tempImageNum --; + numFrames ++; + ri = new RawImage(fname); + int errCode = ri->loadRaw (true, tempImageNum, true, plistener, 0.8); + std::cout << "imagenum : " << tempImageNum << " width : " << ri->get_width() << " height : " << ri->get_height() << std::endl; - if (errCode) { - return errCode; + if (errCode) { + return errCode; + } + riFrames[tempImageNum] = ri; + ri->compress_image(); + ri->set_prefilters(); + } while (tempImageNum); + + if(numFrames > 1 ) { + if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { + numFrames = 1; + } } - ri->compress_image(); +std::cout << "numframes : " << numFrames << std::endl; if (plistener) { plistener->setProgress (0.9); @@ -1627,7 +1642,6 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) }*/ - ri->set_prefilters(); //Load complete Exif informations RawMetaDataLocation rml; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index cec3d78b7..79d89be88 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -25,7 +25,7 @@ #include "curves.h" #include "color.h" #include "iimage.h" - +#include #define HR_SCALE 2 namespace rtengine @@ -71,6 +71,9 @@ protected: bool rgbSourceModified; RawImage* ri; // Copy of raw pixels, NOT corrected for initial gain, blackpoint etc. + RawImage* riFrames[16] = {nullptr}; + unsigned int currFrame = 0; + unsigned int numFrames = 0; // to accelerate CIELAB conversion: double lc00, lc01, lc02, lc10, lc11, lc12, lc20, lc21, lc22; @@ -195,7 +198,11 @@ public: static void HLRecovery_blend (float* rin, float* gin, float* bin, int width, float maxval, float* hlmax); static void init (); static void cleanup (); - + void setCurrentFrame(unsigned int frameNum) { + currFrame = std::min(numFrames - 1, frameNum); + ri = riFrames[currFrame]; + std::cout << "currFrame : " << currFrame << std::endl; + } protected: typedef unsigned short ushort; void processFalseColorCorrection (Imagefloat* i, const int steps); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 8325ac17d..33e988674 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -470,7 +470,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens - DEMOSAIC // EvRawImageNum + DARKFRAME // EvRawImageNum }; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 6321f0dcf..9b71d2156 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -156,7 +156,8 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode) { RawImage *ri = new RawImage(fname); - int r = ri->loadRaw(false, 0, false); + unsigned int imageNum = 0; + int r = ri->loadRaw(false, imageNum, false); if( r ) { delete ri; @@ -270,7 +271,9 @@ RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname) rml.ciffLength = -1; RawImage ri(fname); - int r = ri.loadRaw(false); + unsigned int imageNum = 0; + + int r = ri.loadRaw(false, imageNum); if( !r ) { rml.exifBase = ri.get_exifBase(); @@ -284,7 +287,9 @@ RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname) Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate, int imageNum) { RawImage *ri = new RawImage (fname); - int r = ri->loadRaw(1, imageNum, 0); + unsigned int tempImageNum = 0; + + int r = ri->loadRaw(1, tempImageNum, 0); if( r ) { delete ri; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 9e7d5f58f..250875b6a 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -102,6 +102,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p ImProcFunctions ipf (¶ms, true); PreviewProps pp (0, 0, fw, fh, 1); + imgsrc->setCurrentFrame(params.raw.bayersensor.imageNum); imgsrc->preprocess( params.raw, params.lensProf, params.coarse, params.dirpyrDenoise.enabled); if (params.toneCurve.autoexp) {// this enabled HLRecovery diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 38b6952b0..733a44c42 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -95,6 +95,8 @@ public: { return rgbSourceModified; } + void setCurrentFrame(unsigned int frameNum) {} + }; } #endif From 81fa149570649031f53f1d56341696c29bb35c03 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 3 Nov 2016 00:41:33 +0100 Subject: [PATCH 009/181] small change for Hombre --- rtengine/rawimage.cc | 7 +++++-- rtengine/rawimagesource.cc | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 85d6a00b5..395a8bc55 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -423,13 +423,16 @@ int RawImage::loadRaw (bool loadData, unsigned int &imageNum, bool closeFile, Pr //***************** Read ALL raw file info // set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly - shot_select = imageNum; identify (); + fclose(ifp); + ifp = gfopen (ifname); // Maps to either file map or direct fopen + shot_select = imageNum; + shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); + identify(); std::cout << "israw : " << is_raw << std::endl; // in case dcraw didn't handle the above mentioned case... - shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); imageNum = shot_select; if (!is_raw) { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 1d30706aa..9cef132ae 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1506,7 +1506,7 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgress (0.0); } - unsigned int tempImageNum = 4; + unsigned int tempImageNum = 16; do { tempImageNum --; numFrames ++; From 2d730572f58ec1c51f74b24496698c8c9d2c108a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 3 Nov 2016 01:27:56 +0100 Subject: [PATCH 010/181] Fixed gui (thanks to Hombre) and cleaned code a bit --- rtengine/rawimage.cc | 6 +----- rtengine/rawimagesource.cc | 25 ++++++++++++++++++++----- rtgui/bayerprocess.cc | 2 +- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 395a8bc55..43b829148 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -424,15 +424,11 @@ int RawImage::loadRaw (bool loadData, unsigned int &imageNum, bool closeFile, Pr //***************** Read ALL raw file info // set the number of the frame to extract. If the number is larger then number of existing frames - 1, dcraw will handle that correctly - identify (); - fclose(ifp); - ifp = gfopen (ifname); // Maps to either file map or direct fopen - shot_select = imageNum; - shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); identify(); std::cout << "israw : " << is_raw << std::endl; // in case dcraw didn't handle the above mentioned case... + shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); imageNum = shot_select; if (!is_raw) { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 9cef132ae..6e71e771f 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1506,9 +1506,21 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgress (0.0); } - unsigned int tempImageNum = 16; - do { - tempImageNum --; + unsigned int tempImageNum = 256; + // requesting a subimage which is not available gives subimage 0. We use 256 to access a non existent frame resulting in frame 0 + ri = new RawImage(fname); + int errCode = ri->loadRaw (true, tempImageNum, true, plistener, 0.8); + // now tempImageNum is adjusted to the number of last frame + + if (errCode) { + return errCode; + } + ri->compress_image(); + ri->set_prefilters(); + riFrames[0] = ri; + numFrames ++; + + while (tempImageNum) { numFrames ++; ri = new RawImage(fname); int errCode = ri->loadRaw (true, tempImageNum, true, plistener, 0.8); @@ -1520,9 +1532,10 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) riFrames[tempImageNum] = ri; ri->compress_image(); ri->set_prefilters(); - } while (tempImageNum); + tempImageNum --; + } - if(numFrames > 1 ) { + if(numFrames > 1 ) { // this disables multi frame support for Fuji S5 until I found a solution to handle different dimensions if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { numFrames = 1; } @@ -1530,6 +1543,8 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) std::cout << "numframes : " << numFrames << std::endl; + ri = riFrames[0]; + if (plistener) { plistener->setProgress (0.9); } diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 3a4f2898a..639eabc02 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -39,7 +39,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pack_start( *hb1, Gtk::PACK_SHRINK, 4); imageNumberBox = Gtk::manage (new Gtk::HBox ()); - hb1->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_IMAGENUM") + ": ")), Gtk::PACK_SHRINK, 4); + imageNumberBox->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_IMAGENUM") + ": ")), Gtk::PACK_SHRINK, 4); imageNumber = Gtk::manage (new MyComboBoxText ()); imageNumber->append_text("1"); imageNumber->append_text("2"); From f27241a745eda1c510ac6a818e0c6db660c50735 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 4 Nov 2016 18:27:21 +0100 Subject: [PATCH 011/181] Pentax pixelshift v0.0 --- rtengine/CMakeLists.txt | 1 + rtengine/procparams.cc | 2 +- rtengine/procparams.h | 2 +- rtengine/rawimagesource.cc | 76 +++++++++++++++++++++++++++++++++++++- rtengine/rawimagesource.h | 3 ++ rtexif/pentaxattribs.cc | 1 + 6 files changed, 82 insertions(+), 3 deletions(-) diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 1f4f3375f..49db10faa 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -24,6 +24,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc clutstore.cc ciecam02.cc + pixelshift.cc ) include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index fe0d450c9..2a7b41847 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -40,7 +40,7 @@ const int br = (int) options.rtSettings.bot_right; const int tl = (int) options.rtSettings.top_left; const int bl = (int) options.rtSettings.bot_left; -const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none" }; +const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none", "pixelshift simple" }; const char *RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::numMethods] = {"3-pass (best)", "1-pass (medium)", "fast", "mono", "none" }; const char *RAWParams::ff_BlurTypestring[RAWParams::numFlatFileBlurTypes] = {/*"Parametric",*/ "Area Flatfield", "Vertical Flatfield", "Horizontal Flatfield", "V+H Flatfield"}; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index fc539144b..35a4f1932 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1165,7 +1165,7 @@ public: public: //enum eMethod{ eahd,hphd,vng4,dcb,amaze,ahd,IGV_noise,fast, //numMethods }; // This MUST be the last enum - enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, + enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, pixelshift_simple, numMethods }; // This MUST be the last enum static const char *methodstring[numMethods]; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 6e71e771f..4ebbd5dc6 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1506,6 +1506,7 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgress (0.0); } +StopWatch Stop1("loadraw"); unsigned int tempImageNum = 256; // requesting a subimage which is not available gives subimage 0. We use 256 to access a non existent frame resulting in frame 0 ri = new RawImage(fname); @@ -1544,7 +1545,7 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) std::cout << "numframes : " << numFrames << std::endl; ri = riFrames[0]; - +Stop1.stop(); if (plistener) { plistener->setProgress (0.9); } @@ -1948,6 +1949,13 @@ void RawImageSource::demosaic(const RAWParams &raw) ahd_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); + } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { + if(numFrames == 4) { + pixelshift_simple(0, 0, W, H); + scaleColors_pixelshift( 0, 0, W, H, raw); + } else { + amaze_demosaic_RT (0, 0, W, H); + } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::eahd]) { @@ -3498,6 +3506,72 @@ void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const R } +void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw) +{ + chmax[0] = chmax[1] = chmax[2] = chmax[3] = 0; //channel maxima + float black_lev[4] = {0.f};//black level + + //adjust black level (eg Canon) + bool isMono = false; + + black_lev[0] = raw.bayersensor.black1; //R + black_lev[1] = raw.bayersensor.black0; //G1 + black_lev[2] = raw.bayersensor.black2; //B + black_lev[3] = raw.bayersensor.black3; //G2 + + for(int i = 0; i < 4 ; i++) { + cblacksom[i] = max( c_black[i] + black_lev[i], 0.0f ); // adjust black level + } + + initialGain = calculate_scale_mul(scale_mul, ref_pre_mul, c_white, cblacksom, isMono, ri->get_colors()); // recalculate scale colors with adjusted levels + + //fprintf(stderr, "recalc: %f [%f %f %f %f]\n", initialGain, scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]); + for(int i = 0; i < 4 ; i++) { + clmax[i] = (c_white[i] - cblacksom[i]) * scale_mul[i]; // raw clip level + } + + // this seems strange, but it works + + // scale image colors + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + float tmpchmax[3]; + tmpchmax[0] = tmpchmax[1] = tmpchmax[2] = 0.0f; +#ifdef _OPENMP + #pragma omp for nowait +#endif + + for (int row = winy; row < winy + winh; row ++) + { + for (int col = winx; col < winx + winw; col++) { + float redval = (red[row][col] - cblacksom[0]) * scale_mul[0]; + tmpchmax[0] = max(tmpchmax[0], redval); + red[row][col] = redval; + + float greenval = (green[row][col] - cblacksom[1]) * scale_mul[1]; + tmpchmax[1] = max(tmpchmax[1], greenval); + green[row][col] = greenval; + + float blueval = (blue[row][col] - cblacksom[2]) * scale_mul[2]; + tmpchmax[2] = max(tmpchmax[2], blueval); + blue[row][col] = blueval; + } + } + +#ifdef _OPENMP + #pragma omp critical +#endif + { + chmax[0] = max(tmpchmax[0], chmax[0]); + chmax[1] = max(tmpchmax[1], chmax[1]); + chmax[2] = max(tmpchmax[2], chmax[2]); + } + } +} + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int RawImageSource::defTransform (int tran) diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 79d89be88..abf1f6b97 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -131,6 +131,8 @@ public: void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile ); void cfaboxblur (RawImage *riFlatFile, float* cfablur, int boxH, int boxW); void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw); // raw for cblack + void scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw); + void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const ColorManagementParams &cmp, const RAWParams &raw); eSensorType getSensorType () const @@ -260,6 +262,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); + void pixelshift_simple(int winx, int winy, int winw, int winh); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtexif/pentaxattribs.cc b/rtexif/pentaxattribs.cc index 9fc6b6fb6..13dca86b1 100644 --- a/rtexif/pentaxattribs.cc +++ b/rtexif/pentaxattribs.cc @@ -42,6 +42,7 @@ public: choices[3] = "TIFF"; choices[4] = "RAW"; choices[5] = "Premium"; + choices[6] = "RAW (HDR enabled)"; choices[7] = "RAW (pixel shift enabled)"; choices[65535] = "n/a"; } From eaaf841f27e7b00dbf639e3ce31cd69890c2a743 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 4 Nov 2016 18:52:06 +0100 Subject: [PATCH 012/181] Forgot to add one file --- rtengine/pixelshift.cc | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 rtengine/pixelshift.cc diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc new file mode 100644 index 000000000..944fc9790 --- /dev/null +++ b/rtengine/pixelshift.cc @@ -0,0 +1,93 @@ +//////////////////////////////////////////////////////////////// +// +// pixelshift +// +// +// pixelshift.cc 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. +// +// This program 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 this program. If not, see . +// +//////////////////////////////////////////////////////////////// + +#include +#include "rawimagesource.h" +#include "../rtgui/multilangmgr.h" +#include "procparams.h" +#include "opthelper.h" +#define BENCHMARK +#include "StopWatch.h" +using namespace std; +using namespace rtengine; + +void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh) +{ + +BENCHFUN + double progress = 0.0; + const bool plistenerActive = plistener; + + //int winx=0, winy=0; + //int winw=W, winh=H; + + if (plistener) { + plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); + plistener->setProgress (progress); + } + + + const int bord = 2; + + #pragma omp parallel for + for(int i = bord; i < winh - bord; ++i) { + for(int j = bord; j< winw - bord; ++j) { + int c = FC(i,j); + if(c == 0) { + red[i][j] = riFrames[0]->data[i][j]; + green[i][j] = riFrames[3]->data[i][j+1]; + blue[i][j] = riFrames[2]->data[i+1][j+1]; + } else if(c & 1) { + green[i][j] = riFrames[0]->data[i][j]; + if(FC(i,j+1) == 0) { + red[i][j] = riFrames[3]->data[i][j+1]; + blue[i][j] = riFrames[1]->data[i+1][j]; + } else { + blue[i][j] = riFrames[3]->data[i][j+1]; + red[i][j] = riFrames[1]->data[i+1][j]; + } + } else { + blue[i][j] = riFrames[0]->data[i][j]; + red[i][j] = riFrames[2]->data[i+1][j+1]; + green[i][j] = riFrames[3]->data[i][j+1]; + } + } + } + +// if(plistenerActive && ((++progressCounter) % 16 == 0)) { +//#ifdef _OPENMP +// #pragma omp critical (updateprogress) +//#endif +// { +// progress += progressInc; +// progress = min(1.0, progress); +// plistener->setProgress (progress); +// } +// } + + if(plistenerActive) { + plistener->setProgress(1.00); + } + + + +} +#undef TS +#undef CLF From 5b455702a2ca597e83a87dcd2b0ba4e4f0751b25 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 6 Nov 2016 16:42:20 +0100 Subject: [PATCH 013/181] pixelshift: Fixed a bug, cleaned code, parallel decode of frames --- rtengine/dfmanager.cc | 11 +++---- rtengine/ffmanager.cc | 12 +++----- rtengine/pixelshift.cc | 24 +++------------ rtengine/rawimage.cc | 4 +-- rtengine/rawimage.h | 3 +- rtengine/rawimagesource.cc | 62 +++++++++++++++++++++----------------- rtengine/rawimagesource.h | 1 - 7 files changed, 51 insertions(+), 66 deletions(-) diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc index 1dedf661f..ab76ff854 100644 --- a/rtengine/dfmanager.cc +++ b/rtengine/dfmanager.cc @@ -138,8 +138,7 @@ void dfInfo::updateRawImage() std::list::iterator iName = pathNames.begin(); ri = new RawImage(*iName); // First file used also for extra pixels informations (width,height, shutter, filters etc.. ) - unsigned int imageNum = 0; - if( ri->loadRaw(true, imageNum)) { + if( ri->loadRaw(true)) { delete ri; ri = nullptr; } else { @@ -164,7 +163,7 @@ void dfInfo::updateRawImage() for( ++iName; iName != pathNames.end(); ++iName) { RawImage* temp = new RawImage(*iName); - if( !temp->loadRaw(true,imageNum)) { + if( !temp->loadRaw(true)) { temp->compress_image(); //\ TODO would be better working on original, because is temporary nFiles++; @@ -200,9 +199,8 @@ void dfInfo::updateRawImage() } } else { ri = new RawImage(pathname); - unsigned int imageNum = 0; - if( ri->loadRaw(true,imageNum)) { + if( ri->loadRaw(true)) { delete ri; ri = nullptr; } else { @@ -367,8 +365,7 @@ dfInfo* DFManager::addFileInfo (const Glib::ustring& filename, bool pool) } RawImage ri (filename); - unsigned int imageNum = 0; - int res = ri.loadRaw (false, imageNum); // Read informations about shot + int res = ri.loadRaw (false); // Read informations about shot if (res != 0) { return nullptr; diff --git a/rtengine/ffmanager.cc b/rtengine/ffmanager.cc index 42a46802b..7dd9140a1 100644 --- a/rtengine/ffmanager.cc +++ b/rtengine/ffmanager.cc @@ -130,8 +130,7 @@ void ffInfo::updateRawImage() if( !pathNames.empty() ) { std::list::iterator iName = pathNames.begin(); ri = new RawImage(*iName); // First file used also for extra pixels informations (width,height, shutter, filters etc.. ) - unsigned int imageNum = 0; - if( ri->loadRaw(true, imageNum)) { + if( ri->loadRaw(true)) { delete ri; ri = nullptr; } else { @@ -156,7 +155,7 @@ void ffInfo::updateRawImage() for( ++iName; iName != pathNames.end(); ++iName) { RawImage* temp = new RawImage(*iName); - if( !temp->loadRaw(true,imageNum)) { + if( !temp->loadRaw(true)) { temp->compress_image(); //\ TODO would be better working on original, because is temporary nFiles++; @@ -192,9 +191,7 @@ void ffInfo::updateRawImage() } } else { ri = new RawImage(pathname); - unsigned int imageNum = 0; - - if( ri->loadRaw(true, imageNum)) { + if( ri->loadRaw(true)) { delete ri; ri = nullptr; } else { @@ -327,8 +324,7 @@ ffInfo* FFManager::addFileInfo (const Glib::ustring& filename, bool pool) RawImage ri (filename); - unsigned int imageNum = 0; - int res = ri.loadRaw (false, imageNum); // Read informations about shot + int res = ri.loadRaw (false); // Read informations about shot if (res != 0) { return nullptr; diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 944fc9790..20f27294a 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -1,6 +1,7 @@ //////////////////////////////////////////////////////////////// // -// pixelshift +// simple pentax pixelshift algorithm +// copyright (c) Ingo Weyrich 2016 // // // pixelshift.cc is free software: you can redistribute it and/or modify @@ -23,7 +24,7 @@ #include "../rtgui/multilangmgr.h" #include "procparams.h" #include "opthelper.h" -#define BENCHMARK +//#define BENCHMARK #include "StopWatch.h" using namespace std; using namespace rtengine; @@ -35,18 +36,16 @@ BENCHFUN double progress = 0.0; const bool plistenerActive = plistener; - //int winx=0, winy=0; - //int winw=W, winh=H; - if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); plistener->setProgress (progress); } - const int bord = 2; +#ifdef _OPENMP #pragma omp parallel for +#endif for(int i = bord; i < winh - bord; ++i) { for(int j = bord; j< winw - bord; ++j) { int c = FC(i,j); @@ -71,23 +70,10 @@ BENCHFUN } } -// if(plistenerActive && ((++progressCounter) % 16 == 0)) { -//#ifdef _OPENMP -// #pragma omp critical (updateprogress) -//#endif -// { -// progress += progressInc; -// progress = min(1.0, progress); -// plistener->setProgress (progress); -// } -// } - if(plistenerActive) { plistener->setProgress(1.00); } - - } #undef TS #undef CLF diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 43b829148..f4a412ca5 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -398,7 +398,7 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, unsigned int &imageNum, bool closeFile, ProgressListener *plistener, double progressRange) +int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) { ifname = filename.c_str(); image = nullptr; @@ -426,10 +426,8 @@ int RawImage::loadRaw (bool loadData, unsigned int &imageNum, bool closeFile, Pr shot_select = imageNum; identify(); - std::cout << "israw : " << is_raw << std::endl; // in case dcraw didn't handle the above mentioned case... shot_select = std::min(shot_select, std::max(is_raw, 1u) - 1); - imageNum = shot_select; if (!is_raw) { fclose(ifp); diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 4080702a6..cb9e9100e 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -106,7 +106,7 @@ public: explicit RawImage( const Glib::ustring &name ); ~RawImage(); - int loadRaw (bool loadData, unsigned int &imageNum, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); + int loadRaw (bool loadData, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); void set_prefilters() { @@ -122,6 +122,7 @@ public: float** compress_image(); // revert to compressed pixels format and release image data float** data; // holds pixel values, data[i][j] corresponds to the ith row and jth column unsigned prefilters; // original filters saved ( used for 4 color processing ) + unsigned int getFrameCount() const { return is_raw; } protected: Glib::ustring filename; // complete filename int rotate_deg; // 0,90,180,270 degree of rotation: info taken by dcraw from exif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 4ebbd5dc6..37cf2618e 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1505,47 +1505,52 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgressStr ("Decoding..."); plistener->setProgress (0.0); } - -StopWatch Stop1("loadraw"); - unsigned int tempImageNum = 256; - // requesting a subimage which is not available gives subimage 0. We use 256 to access a non existent frame resulting in frame 0 +StopWatch Stop1("decode"); ri = new RawImage(fname); - int errCode = ri->loadRaw (true, tempImageNum, true, plistener, 0.8); - // now tempImageNum is adjusted to the number of last frame + int errCode = ri->loadRaw (false, 0, false); if (errCode) { return errCode; } - ri->compress_image(); - ri->set_prefilters(); - riFrames[0] = ri; - numFrames ++; + numFrames = ri->getFrameCount(); - while (tempImageNum) { - numFrames ++; - ri = new RawImage(fname); - int errCode = ri->loadRaw (true, tempImageNum, true, plistener, 0.8); - std::cout << "imagenum : " << tempImageNum << " width : " << ri->get_width() << " height : " << ri->get_height() << std::endl; - - if (errCode) { - return errCode; + errCode = 0; +#ifdef _OPENMP +#pragma omp parallel if(numFrames > 1) +#endif +{ + int errCodeThr = 0; +#ifdef _OPENMP +#pragma omp for +#endif + for(unsigned int i = 0; i < numFrames; ++i) { + if(i == 0) { + riFrames[i] = ri; + errCodeThr = riFrames[i]->loadRaw (true, i, true, plistener, 0.8); + } else { + riFrames[i] = new RawImage(fname); + errCodeThr = riFrames[i]->loadRaw (true, i); } - riFrames[tempImageNum] = ri; - ri->compress_image(); - ri->set_prefilters(); - tempImageNum --; + riFrames[i]->compress_image(); } +#ifdef _OPENMP +#pragma omp critical +#endif +{ + errCode = errCodeThr ? errCodeThr : errCode; +} +} + if(errCode) { + return errCode; + } +Stop1.stop(); if(numFrames > 1 ) { // this disables multi frame support for Fuji S5 until I found a solution to handle different dimensions if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { numFrames = 1; } } -std::cout << "numframes : " << numFrames << std::endl; - - ri = riFrames[0]; -Stop1.stop(); if (plistener) { plistener->setProgress (0.9); } @@ -1657,6 +1662,9 @@ Stop1.stop(); initialGain = 1.0 / min(pre_mul[0], pre_mul[1], pre_mul[2]); }*/ + for(unsigned int i=0;i < numFrames; ++i) { + riFrames[i]->set_prefilters(); + } //Load complete Exif informations @@ -1953,7 +1961,7 @@ void RawImageSource::demosaic(const RAWParams &raw) if(numFrames == 4) { pixelshift_simple(0, 0, W, H); scaleColors_pixelshift( 0, 0, W, H, raw); - } else { + } else { // for non pixelshift files use amaze if pixelshift is selected amaze_demosaic_RT (0, 0, W, H); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index abf1f6b97..560314802 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -203,7 +203,6 @@ public: void setCurrentFrame(unsigned int frameNum) { currFrame = std::min(numFrames - 1, frameNum); ri = riFrames[currFrame]; - std::cout << "currFrame : " << currFrame << std::endl; } protected: typedef unsigned short ushort; From 1c2f94a1b667d44443408e397b9f606eabc9c480 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 6 Nov 2016 22:43:08 +0100 Subject: [PATCH 014/181] pixelshift: simplified combination of frames --- rtengine/pixelshift.cc | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 20f27294a..a68157b65 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -24,7 +24,7 @@ #include "../rtgui/multilangmgr.h" #include "procparams.h" #include "opthelper.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" using namespace std; using namespace rtengine; @@ -41,32 +41,33 @@ BENCHFUN plistener->setProgress (progress); } - const int bord = 2; + const int bord = 4; #ifdef _OPENMP #pragma omp parallel for #endif for(int i = bord; i < winh - bord; ++i) { - for(int j = bord; j< winw - bord; ++j) { - int c = FC(i,j); - if(c == 0) { - red[i][j] = riFrames[0]->data[i][j]; - green[i][j] = riFrames[3]->data[i][j+1]; - blue[i][j] = riFrames[2]->data[i+1][j+1]; - } else if(c & 1) { - green[i][j] = riFrames[0]->data[i][j]; - if(FC(i,j+1) == 0) { - red[i][j] = riFrames[3]->data[i][j+1]; - blue[i][j] = riFrames[1]->data[i+1][j]; - } else { - blue[i][j] = riFrames[3]->data[i][j+1]; - red[i][j] = riFrames[1]->data[i+1][j]; - } - } else { - blue[i][j] = riFrames[0]->data[i][j]; - red[i][j] = riFrames[2]->data[i+1][j+1]; - green[i][j] = riFrames[3]->data[i][j+1]; - } + float *greenDest = green[i]; + float *nonGreenDest0 = red[i]; + float *nonGreenDest1 = blue[i]; + int j = bord; + int c = FC(i,j); + if (c == 2 || ((c&1) && FC(i,j+1) == 2)) { + std::swap(nonGreenDest0, nonGreenDest1); + } + if(c&1) { + greenDest[j] = (riFrames[0]->data[i][j] + riFrames[2]->data[i+1][j+1]) / 2.f; + nonGreenDest0[j] = riFrames[3]->data[i][j+1]; + nonGreenDest1[j] = riFrames[1]->data[i+1][j]; + j++; + } + for(; j< winw - bord; j+=2) { + nonGreenDest0[j] = riFrames[0]->data[i][j]; + greenDest[j] = (riFrames[3]->data[i][j+1] + riFrames[1]->data[i+1][j] ) / 2.f; + nonGreenDest1[j] = riFrames[2]->data[i+1][j+1]; + greenDest[j+1] = (riFrames[0]->data[i][j+1] + riFrames[2]->data[i+1][j+2]) / 2.f; + nonGreenDest0[j+1] = riFrames[3]->data[i][j+2]; + nonGreenDest1[j+1] = riFrames[1]->data[i+1][j+1]; } } From e92303705387a0bc8728236cba3ed27ea8105ff3 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 16 Nov 2016 09:31:35 +0100 Subject: [PATCH 015/181] Commited current state to allow tests. Expect colour casts when changin demosaic method or pixelshift frame. In this case, just reload the image --- rtdata/languages/default | 5 + rtdata/rt_splash_5.png | Bin 0 -> 78671 bytes rtdata/rt_splash_5.svg | 1772 ++++++++++++++++++++++++++++++++++++ rtengine/pixelshift.cc | 199 +++- rtengine/procevents.h | 1 + rtengine/procparams.cc | 39 + rtengine/procparams.h | 3 + rtengine/rawimagesource.cc | 38 +- rtengine/rawimagesource.h | 8 +- rtengine/refreshmap.cc | 3 +- rtengine/rt_math.h | 7 + rtgui/bayerprocess.cc | 86 ++ rtgui/bayerprocess.h | 8 +- rtgui/paramsedited.cc | 18 + rtgui/paramsedited.h | 3 + 15 files changed, 2132 insertions(+), 58 deletions(-) create mode 100644 rtdata/rt_splash_5.png create mode 100644 rtdata/rt_splash_5.svg diff --git a/rtdata/languages/default b/rtdata/languages/default index c66c3a64d..9399982d8 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1654,6 +1654,11 @@ TP_RAW_IMAGENUM;Some raw files might embed several sub-images (HDR, Pixel-Shift, TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. +TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection +TP_RAW_PIXELSHIFTMOTION_TOOLTIP;No tooltip available atm +TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction +TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid +TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster. TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix diff --git a/rtdata/rt_splash_5.png b/rtdata/rt_splash_5.png new file mode 100644 index 0000000000000000000000000000000000000000..be996e1197805b83afb2b264a826671de829a341 GIT binary patch literal 78671 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 z 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

k?dC+o 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 b0`LC`dr8$8Cv7*pK>ifv)F8Do=Ar)s@QPk} literal 0 HcmV?d00001 diff --git a/rtdata/rt_splash_5.svg b/rtdata/rt_splash_5.svg new file mode 100644 index 000000000..d5f7ac4f3 --- /dev/null +++ b/rtdata/rt_splash_5.svg @@ -0,0 +1,1772 @@ + + + + + RawTherapee logo white font white glow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + RawTherapee logo white font white glow + + + RawTherapee + + + + + rawtherapee + logo + white + + + www.rawtherapee.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Select the desired element and apply one of the effects in the Filter Editor. You might need to ungroup the element before applying. For example to set the RT ring to have a colorful glow, select it and enable the "ring glow". You can change the flood color of the "ring shadow" effect to make it white if you want to make the logo usable on a dark background.For logo specifics, refer to rt_logo.svg Raw + Therapee + "Raw" font Eras-UltraBlk, 69px, -3px spacing between characters, skewed 2° to the right."Therapee" font Eras-Medium, 68px, 4px spacing between characters, skewed 2° to the right.Both have a dropshadow with an opacity of 0.40 and Gaussian blur standard deviation of 3.5.Version number Eras bold 64 or less.Eras font from "freefonts-0.10":ftp://ftp.gimp.org/pub/gimp/fonts/ RawTherapee splash screen design version 1.0 from 2014-05-03 | www.rawtherapee.com + GNU GPLv3 + 5 + + + diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index a68157b65..7e25f907f 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -1,6 +1,13 @@ //////////////////////////////////////////////////////////////// // -// simple pentax pixelshift algorithm +// pentax pixelshift algorithm with motion detection +// +// derived from dcrawps (https://github.com/tomtor/dcrawps), but with additional motion correction methods and adapted for RawTherapee data structures +// +// If motion correction is enabled only the pixels which are not detected as motion are set +// That means for a complete image you have to demosaic one of the frames with a bayer demosaicer to fill red, green and blue +// before calling pixelshift in case motion correction is enabled. +// // copyright (c) Ingo Weyrich 2016 // // @@ -23,58 +30,184 @@ #include "rawimagesource.h" #include "../rtgui/multilangmgr.h" #include "procparams.h" -#include "opthelper.h" #define BENCHMARK #include "StopWatch.h" + +namespace +{ + +float greenDiff(float a, float b) +{ + // calculate the difference between to green samples + // add a small epsilon to avoid division by zero + return std::fabs(a - b) / (std::max(a, b) + 0.0001f); + +} + +} + using namespace std; using namespace rtengine; -void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh) +void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize) { -BENCHFUN - double progress = 0.0; - const bool plistenerActive = plistener; + BENCHFUN if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); - plistener->setProgress (progress); + plistener->setProgress(0.0); } - const int bord = 4; + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel + const float motionThreshold = 1.f - (motion / 100.f); + + unsigned int offsX = 0, offsY = 0; + if(detectMotion && !showMotion) { + // if motion correction is enabled we have to adjust the offsets for the selected subframe we use for areas with motion + switch (frame) { + case 0: + offsX = offsY = 0; + break; + + case 1: + offsX = 0; + offsY = 1; + break; + + case 2: + offsX = offsY = 1; + break; + + case 3: + offsX = 1; + offsY = 0; + } + } #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic,16) #endif - for(int i = bord; i < winh - bord; ++i) { - float *greenDest = green[i]; - float *nonGreenDest0 = red[i]; - float *nonGreenDest1 = blue[i]; - int j = bord; - int c = FC(i,j); - if (c == 2 || ((c&1) && FC(i,j+1) == 2)) { + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *nonGreenDest0 = red[i + offsY]; + float *nonGreenDest1 = blue[i + offsY]; + int j = winx + border - offsX; + int c = FC(i, j); + + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels std::swap(nonGreenDest0, nonGreenDest1); } - if(c&1) { - greenDest[j] = (riFrames[0]->data[i][j] + riFrames[2]->data[i+1][j+1]) / 2.f; - nonGreenDest0[j] = riFrames[3]->data[i][j+1]; - nonGreenDest1[j] = riFrames[1]->data[i+1][j]; - j++; + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); + + + float greenDifMax[gridSize]; + // motion detection checks the grid around the pixel for differences in green channels + if(detectMotion) { + if(gridSize == 3) { + // compute maximum of differences for first two columns of 3x3 grid + greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j]), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j]), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j]) + ); + greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1]), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1]) + ); + } else if(gridSize == 5) { + // compute maximum of differences for first four columns of 5x5 grid + greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1]), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1]), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1]), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1]), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1]) + ); + greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j]), + greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j]), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j]), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j]), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j]) + ); + greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1]), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1]), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1]), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1]) + ); + greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2]), + greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2]), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2]), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2]), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2]) + ); + } } - for(; j< winw - bord; j+=2) { - nonGreenDest0[j] = riFrames[0]->data[i][j]; - greenDest[j] = (riFrames[3]->data[i][j+1] + riFrames[1]->data[i+1][j] ) / 2.f; - nonGreenDest1[j] = riFrames[2]->data[i+1][j+1]; - greenDest[j+1] = (riFrames[0]->data[i][j+1] + riFrames[2]->data[i+1][j+2]) / 2.f; - nonGreenDest0[j+1] = riFrames[3]->data[i][j+2]; - nonGreenDest1[j+1] = riFrames[1]->data[i+1][j+1]; + + offset ^= 1; // 0 => 1 or 1 => 0 + + // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 + int lastIndex = gridSize - 1; + + for(; j < winw - (border + offsX); ++j) { + offset ^= 1; // 0 => 1 or 1 => 0 + + if(detectMotion) { + bool skipNext = false; + float gridMax; + if(gridSize == 1) { + // compute difference for current pixel and skip next pixel, that's the method from dcrawps + gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]); + skipNext = !showMotion; + } else if(gridSize == 3) { + // compute maximum of differences for third column of 3x3 grid and save at position lastIndex + greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2]), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2]), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2]) + ); + gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); + } else if(gridSize == 5) { + // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex + greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3]), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3]), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3]), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3]), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3]) + ); + gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); + } + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + + if (gridMax > motionThreshold) { + // at least one of the tested pixels of the grid is detected as motion + if(showMotion) { + // if showMotion is enabled make the pixel green + greenDest[j] = 10000.f; + nonGreenDest0[j] = nonGreenDest1[j] = 0.f; + } + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + offset ^= 1; + } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + + // motion correction disabled or no motion detected => combine the values from the four pixelshift frames + greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; + nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; + nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; } } - if(plistenerActive) { - plistener->setProgress(1.00); + if(plistener) { + plistener->setProgress(1.0); } - } -#undef TS -#undef CLF diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 69cda228a..24469cead 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -471,6 +471,7 @@ enum ProcEvent { EvLskal = 441, EvOBPCompens = 442, EvRawImageNum = 443, + EvDemosaicPixelshiftMotion = 444, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 2a7b41847..466b25d82 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -885,6 +885,9 @@ void RAWParams::setDefaults() bayersensor.dcb_enhance = true; //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; + bayersensor.pixelshiftMotion = 70; + bayersensor.pixelshiftMotionCorrection = 3; + bayersensor.pixelshiftShowMotion = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3364,6 +3367,18 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations ); } + if (!pedited || pedited->raw.bayersensor.pixelshiftMotion) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotion", raw.bayersensor.pixelshiftMotion ); + } + + if (!pedited || pedited->raw.bayersensor.pixelshiftMotionCorrection) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelshiftMotionCorrection ); + } + + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotion) { + keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); + } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { @@ -7422,6 +7437,30 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotion")) { + raw.bayersensor.pixelshiftMotion = keyFile.get_integer("RAW Bayer", "PixelShiftMotion"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftMotion = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotionCorrection")) { + raw.bayersensor.pixelshiftMotionCorrection = keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrection"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftMotionCorrection = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotion")) { + raw.bayersensor.pixelshiftShowMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotion"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftShowMotion = true; + } + } + //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 35a4f1932..3f5951cf8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1182,6 +1182,9 @@ public: int greenthresh; int dcb_iterations; int lmmse_iterations; + int pixelshiftMotion; + int pixelshiftMotionCorrection; + bool pixelshiftShowMotion; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 37cf2618e..f4760d44e 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1549,6 +1549,7 @@ Stop1.stop(); if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { numFrames = 1; } + pixelShiftColoursScaled = false; } if (plistener) { @@ -1757,7 +1758,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le printf( "Flat Field Correction:%s\n", rif->get_filename().c_str()); } - copyOriginalPixels(raw, ri, rid, rif); + copyOriginalPixels(raw, ri, rid, rif, rawData); //FLATFIELD end @@ -1798,7 +1799,10 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } - scaleColors( 0, 0, W, H, raw); //+ + raw parameters for black level(raw.blackxx) + scaleColors( 0, 0, W, H, raw, rawData); //+ + raw parameters for black level(raw.blackxx) + if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] && !pixelShiftColoursScaled) { + scaleColors_pixelshift( 0, 0, W, H, raw); + } // Correct vignetting of lens profile if (!hasFlatField && lensProf.useVign) { @@ -1958,11 +1962,11 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { + if(raw.bayersensor.pixelshiftMotion > 0) { + amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction + } if(numFrames == 4) { - pixelshift_simple(0, 0, W, H); - scaleColors_pixelshift( 0, 0, W, H, raw); - } else { // for non pixelshift files use amaze if pixelshift is selected - amaze_demosaic_RT (0, 0, W, H); + pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); @@ -3010,7 +3014,7 @@ void RawImageSource::processFlatField(const RAWParams &raw, RawImage *riFlatFile /* Copy original pixel data and * subtract dark frame (if present) from current image and apply flat field correction (if present) */ -void RawImageSource::copyOriginalPixels(const RAWParams &raw, RawImage *src, RawImage *riDark, RawImage *riFlatFile ) +void RawImageSource::copyOriginalPixels(const RAWParams &raw, RawImage *src, RawImage *riDark, RawImage *riFlatFile, array2D &rawData ) { // TODO: Change type of black[] to float to avoid conversions unsigned short black[4] = { @@ -3341,7 +3345,7 @@ SSEFUNCTION void RawImageSource::cfaboxblur(RawImage *riFlatFile, float* cfablur // Scale original pixels into the range 0 65535 using black offsets and multipliers -void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const RAWParams &raw) +void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const RAWParams &raw, array2D &rawData) { chmax[0] = chmax[1] = chmax[2] = chmax[3] = 0; //channel maxima float black_lev[4] = {0.f};//black level @@ -3555,17 +3559,12 @@ void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int wi for (int row = winy; row < winy + winh; row ++) { for (int col = winx; col < winx + winw; col++) { - float redval = (red[row][col] - cblacksom[0]) * scale_mul[0]; - tmpchmax[0] = max(tmpchmax[0], redval); - red[row][col] = redval; - - float greenval = (green[row][col] - cblacksom[1]) * scale_mul[1]; - tmpchmax[1] = max(tmpchmax[1], greenval); - green[row][col] = greenval; - - float blueval = (blue[row][col] - cblacksom[2]) * scale_mul[2]; - tmpchmax[2] = max(tmpchmax[2], blueval); - blue[row][col] = blueval; + int c = FC(row,col); + for(int frame = 0; frame < 4; ++frame) { + float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; + tmpchmax[c] = max(tmpchmax[c], val); + riFrames[frame]->data[row][col] = val; + } } } @@ -3578,6 +3577,7 @@ void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int wi chmax[2] = max(tmpchmax[2], chmax[2]); } } + pixelShiftColoursScaled = true; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 560314802..648e5ec77 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -81,6 +81,7 @@ protected: int threshold; array2D rawData; // holds preprocessed pixel values, rowData[i][j] corresponds to the ith row and jth column + array2D *rawDataFrames[16] = {nullptr}; // the interpolated green plane: array2D green; @@ -128,9 +129,9 @@ public: } void processFlatField(const RAWParams &raw, RawImage *riFlatFile, unsigned short black[4]); - void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile ); + void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile, array2D &rawData ); void cfaboxblur (RawImage *riFlatFile, float* cfablur, int boxH, int boxW); - void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw); // raw for cblack + void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw, array2D &rawData); // raw for cblack void scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw); @@ -205,6 +206,7 @@ public: ri = riFrames[currFrame]; } protected: + bool pixelShiftColoursScaled = false; typedef unsigned short ushort; void processFalseColorCorrection (Imagefloat* i, const int steps); inline void convert_row_to_YIQ (const float* const r, const float* const g, const float* const b, float* Y, float* I, float* Q, const int W); @@ -261,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift_simple(int winx, int winy, int winw, int winh); + void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int method); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 33e988674..643c9e075 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -470,7 +470,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens - DARKFRAME // EvRawImageNum + DARKFRAME, // EvRawImageNum + DEMOSAIC // EvDemosaicPixelshiftMotion }; diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 67d883080..5e2f04d05 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -74,6 +74,13 @@ inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d) return std::max(d, std::max(c, std::max(a, b))); } +template +inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d, const _Tp& e) +{ + return max(max(a,b,c),std::max(d,e)); +} + + template inline _Tp intp(_Tp a, _Tp b, _Tp c) { diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 639eabc02..92351be2b 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -81,6 +81,34 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA lmmseOptions->pack_start(*lmmseIterations); pack_start( *lmmseOptions, Gtk::PACK_SHRINK, 4); + pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); + pixelShiftOptions->set_border_width(4); + pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); + pixelShiftOptions->pack_start(*pixelShiftShowMotion); + + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); + pixelShiftMotion->setAdjusterListener (this); + pixelShiftMotion->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); + + if (pixelShiftMotion->delay < options.adjusterMaxDelay) { + pixelShiftMotion->delay = options.adjusterMaxDelay; + } + pixelShiftMotion->show(); + pixelShiftOptions->pack_start(*pixelShiftMotion); + + pixelShiftMotionCorrection = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION"), 1, 5, 2, 3)); + pixelShiftMotionCorrection->setAdjusterListener (this); + pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); + + if (pixelShiftMotionCorrection->delay < options.adjusterMaxDelay) { + pixelShiftMotionCorrection->delay = options.adjusterMaxDelay; + } + + pixelShiftMotionCorrection->show(); + pixelShiftOptions->pack_start(*pixelShiftMotionCorrection); + pack_start( *pixelShiftOptions, Gtk::PACK_SHRINK, 4); + + pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); ccSteps = Gtk::manage (new Adjuster (M("TP_RAW_FALSECOLOR"), 0, 5, 1, 0 )); ccSteps->setAdjusterListener (this); @@ -102,6 +130,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) ); imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); + pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -129,8 +158,11 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params ccSteps->setEditedState (pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); + pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); + pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); + pixelShiftMotionCorrection->setEditedState ( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); if(!pedited->raw.bayersensor.method) { method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name @@ -144,8 +176,11 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); + pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); + pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); + pixelShiftMotionCorrection->setValue (pp->raw.bayersensor.pixelshiftMotionCorrection); if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || @@ -160,6 +195,12 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } else { lmmseOptions->hide(); } + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::pixelshift_simple] || + method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } // Flase color suppression is applied to all demozaicing method, so don't hide anything /*if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::eahd] || @@ -188,6 +229,9 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.dcb_enhance = dcbEnhance->get_active(); //pp->raw.bayersensor.all_enhance = allEnhance->get_active(); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); + pp->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getIntValue(); + pp->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getIntValue(); + pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -208,6 +252,9 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); + pedited->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getEditedState (); + pedited->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getEditedState (); + pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); } } @@ -219,25 +266,34 @@ void BayerProcess::setBatchMode(bool batchMode) imageNumber->set_active(4); dcbOptions->hide(); lmmseOptions->hide(); + pixelShiftOptions->hide(); ToolPanel::setBatchMode (batchMode); ccSteps->showEditedCB (); dcbIterations->showEditedCB (); lmmseIterations->showEditedCB (); + pixelShiftMotion->showEditedCB (); + pixelShiftMotionCorrection->showEditedCB (); } void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) { dcbIterations->setDefault( defParams->raw.bayersensor.dcb_iterations); lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); + pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelshiftMotion); + pixelShiftMotionCorrection->setDefault( defParams->raw.bayersensor.pixelshiftMotionCorrection); ccSteps->setDefault (defParams->raw.bayersensor.ccSteps); if (pedited) { dcbIterations->setDefaultEditedState( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); + pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); + pixelShiftMotionCorrection->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); ccSteps->setDefaultEditedState(pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); } else { dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); + pixelShiftMotion->setDefaultEditedState( Irrelevant ); + pixelShiftMotionCorrection->setDefaultEditedState( Irrelevant ); ccSteps->setDefaultEditedState(Irrelevant ); } } @@ -251,6 +307,10 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvDemosaicFalseColorIter, a->getTextValue() ); } else if (a == lmmseIterations) { listener->panelChanged (EvDemosaicLMMSEIter, a->getTextValue() ); + } else if (a == pixelShiftMotion) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + } else if (a == pixelShiftMotionCorrection) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); } } } @@ -271,6 +331,12 @@ void BayerProcess::methodChanged () lmmseOptions->hide(); } + if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift_simple) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + Glib::ustring methodName = ""; bool ppreq = false; @@ -316,6 +382,26 @@ void BayerProcess::dcbEnhanceChanged () } } +void BayerProcess::pixelShiftShowMotionChanged () +{ + if (batchMode) { + if (pixelShiftShowMotion->get_inconsistent()) { + pixelShiftShowMotion->set_inconsistent (false); + pixelShiftShowMotionconn.block (true); + pixelShiftShowMotion->set_active (false); + pixelShiftShowMotionconn.block (false); + } else if (lastDCBen) { + pixelShiftShowMotion->set_inconsistent (true); + } + + lastDCBen = pixelShiftShowMotion->get_active (); + } + + if (listener) { + listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftShowMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + /*void BayerProcess::allEnhanceChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index bba4bd51d..404324ed6 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -41,11 +41,14 @@ protected: //Gtk::CheckButton* allEnhance; Gtk::VBox *lmmseOptions; Adjuster* lmmseIterations; - + Gtk::VBox *pixelShiftOptions; + Adjuster* pixelShiftMotion; + Adjuster* pixelShiftMotionCorrection; + Gtk::CheckButton* pixelShiftShowMotion; bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn; //,allEnhconn; public: BayerProcess (); @@ -59,6 +62,7 @@ public: void imageNumberChanged (); void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); + void pixelShiftShowMotionChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ebb31ef1d..6a020cd8b 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -370,6 +370,9 @@ void ParamsEdited::set (bool v) raw.bayersensor.dcbEnhance = v; //raw.bayersensor.allEnhance = v; raw.bayersensor.lmmseIterations = v; + raw.bayersensor.pixelshiftMotion = v; + raw.bayersensor.pixelshiftMotionCorrection = v; + raw.bayersensor.pixelshiftShowMotion = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -866,6 +869,9 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.dcbEnhance = raw.bayersensor.dcbEnhance && p.raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance; //raw.bayersensor.allEnhance = raw.bayersensor.allEnhance && p.raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance; raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; + raw.bayersensor.pixelshiftMotion = raw.bayersensor.pixelshiftMotion && p.raw.bayersensor.pixelshiftMotion == other.raw.bayersensor.pixelshiftMotion; + raw.bayersensor.pixelshiftMotionCorrection = raw.bayersensor.pixelshiftMotionCorrection && p.raw.bayersensor.pixelshiftMotionCorrection == other.raw.bayersensor.pixelshiftMotionCorrection; + raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2278,6 +2284,18 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.lmmse_iterations = mods.raw.bayersensor.lmmse_iterations; } + if (raw.bayersensor.pixelshiftMotion) { + toEdit.raw.bayersensor.pixelshiftMotion = mods.raw.bayersensor.pixelshiftMotion; + } + + if (raw.bayersensor.pixelshiftMotionCorrection) { + toEdit.raw.bayersensor.pixelshiftMotionCorrection = mods.raw.bayersensor.pixelshiftMotionCorrection; + } + + if (raw.bayersensor.pixelshiftShowMotion) { + toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; + } + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 4e9fa4eb4..5546c9857 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -692,6 +692,9 @@ public: bool dcbIterations; bool dcbEnhance; bool lmmseIterations; + bool pixelshiftMotion; + bool pixelshiftMotionCorrection; + bool pixelshiftShowMotion; //bool allEnhance; bool greenEq; bool linenoise; From 53bc43459e581d8f206fcc0f52a2d7007695dc8f Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 16 Nov 2016 09:34:46 +0100 Subject: [PATCH 016/181] removed splash files --- rtdata/rt_splash_5.png | Bin 78671 -> 0 bytes rtdata/rt_splash_5.svg | 1772 ---------------------------------------- 2 files changed, 1772 deletions(-) delete mode 100644 rtdata/rt_splash_5.png delete mode 100644 rtdata/rt_splash_5.svg diff --git a/rtdata/rt_splash_5.png b/rtdata/rt_splash_5.png deleted file mode 100644 index be996e1197805b83afb2b264a826671de829a341..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78671 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 z 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

k?dC+o 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 b0`LC`dr8$8Cv7*pK>ifv)F8Do=Ar)s@QPk} diff --git a/rtdata/rt_splash_5.svg b/rtdata/rt_splash_5.svg deleted file mode 100644 index d5f7ac4f3..000000000 --- a/rtdata/rt_splash_5.svg +++ /dev/null @@ -1,1772 +0,0 @@ - - - - - RawTherapee logo white font white glow - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - RawTherapee logo white font white glow - - - RawTherapee - - - - - rawtherapee - logo - white - - - www.rawtherapee.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the desired element and apply one of the effects in the Filter Editor. You might need to ungroup the element before applying. For example to set the RT ring to have a colorful glow, select it and enable the "ring glow". You can change the flood color of the "ring shadow" effect to make it white if you want to make the logo usable on a dark background.For logo specifics, refer to rt_logo.svg Raw - Therapee - "Raw" font Eras-UltraBlk, 69px, -3px spacing between characters, skewed 2° to the right."Therapee" font Eras-Medium, 68px, 4px spacing between characters, skewed 2° to the right.Both have a dropshadow with an opacity of 0.40 and Gaussian blur standard deviation of 3.5.Version number Eras bold 64 or less.Eras font from "freefonts-0.10":ftp://ftp.gimp.org/pub/gimp/fonts/ RawTherapee splash screen design version 1.0 from 2014-05-03 | www.rawtherapee.com - GNU GPLv3 - 5 - - - From d711015ae092ad9d23cac3a87935df811dda2f0a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 17 Nov 2016 15:54:55 +0100 Subject: [PATCH 017/181] Correction for pixelshift 'show motion' when selected frame > 1 --- rtengine/pixelshift.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 7e25f907f..9430315ac 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -40,7 +40,7 @@ float greenDiff(float a, float b) { // calculate the difference between to green samples // add a small epsilon to avoid division by zero - return std::fabs(a - b) / (std::max(a, b) + 0.0001f); + return std::fabs(a - b) / (std::max(a, b) + 0.01f); } @@ -63,7 +63,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b const float motionThreshold = 1.f - (motion / 100.f); unsigned int offsX = 0, offsY = 0; - if(detectMotion && !showMotion) { + if(detectMotion) { // if motion correction is enabled we have to adjust the offsets for the selected subframe we use for areas with motion switch (frame) { case 0: @@ -187,8 +187,8 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b // at least one of the tested pixels of the grid is detected as motion if(showMotion) { // if showMotion is enabled make the pixel green - greenDest[j] = 10000.f; - nonGreenDest0[j] = nonGreenDest1[j] = 0.f; + greenDest[j + offsX] = 10000.f; + nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; } if(skipNext) { // treat the horizontally next pixel also as motion From 5683a1b9b15a6e0995af6a68d4c9eb87198dc2c7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 17 Nov 2016 15:55:37 +0100 Subject: [PATCH 018/181] Pixelshift, added a tooltip --- rtdata/languages/default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 9399982d8..997d3ea84 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1655,7 +1655,7 @@ TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection -TP_RAW_PIXELSHIFTMOTION_TOOLTIP;No tooltip available atm +TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion From 8bb958eb190fc91f9188f151ce892cbdd06cebf0 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 21 Nov 2016 01:33:59 +0100 Subject: [PATCH 019/181] pixelshift: committed some experimental changes for testing --- rtdata/languages/default | 1 + rtengine/pixelshift.cc | 147 +++++++++++++++++++++++++++++++++---- rtengine/procparams.cc | 13 ++++ rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 4 +- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 30 ++++++++ rtgui/bayerprocess.h | 4 +- rtgui/paramsedited.cc | 6 ++ rtgui/paramsedited.h | 1 + 10 files changed, 189 insertions(+), 20 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 997d3ea84..44d540315 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1654,6 +1654,7 @@ TP_RAW_IMAGENUM;Some raw files might embed several sub-images (HDR, Pixel-Shift, TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. +TP_RAW_PIXELSHIFTADAPTIVE;Automatic detection TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 9430315ac..7869daa6d 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -49,19 +49,32 @@ float greenDiff(float a, float b) using namespace std; using namespace rtengine; -void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize) +void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool blendMotion) { BENCHFUN + printf("%f\t%f\t%f\t%f\n",scale_mul[0],scale_mul[1],scale_mul[2],scale_mul[3]); + if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); plistener->setProgress(0.0); } - // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel - const float motionThreshold = 1.f - (motion / 100.f); + LUTf log2Lut(65536); + log2Lut[0] = 0; + const float lutStrength = 2.f; + const float scaleGreen = 1.f / scale_mul[1]; + for(int i=2; i < 65536; i+=2) + log2Lut[i>>1] = 2.f * log2(i) / 100.f; + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel + float motionThreshold = 1.f - (motion / 100.f); + // For 'blend' mode + const float blendFactor = 1.f / (1.f - motionThreshold); + + bool checkRedBlue = (gridSize == 5); +// bool checkRedBlue = false; unsigned int offsX = 0, offsY = 0; if(detectMotion) { // if motion correction is enabled we have to adjust the offsets for the selected subframe we use for areas with motion @@ -155,7 +168,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b for(; j < winw - (border + offsX); ++j) { offset ^= 1; // 0 => 1 or 1 => 0 - if(detectMotion) { + if(detectMotion || blendMotion) { bool skipNext = false; float gridMax; if(gridSize == 1) { @@ -183,23 +196,125 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b lastIndex ++; lastIndex = lastIndex == gridSize ? 0 : lastIndex; - if (gridMax > motionThreshold) { - // at least one of the tested pixels of the grid is detected as motion - if(showMotion) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = 10000.f; - nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + // increase motion detection dependent on brightness +// float korr = 2.f * log2Lut[((int)riFrames[1 - offset]->data[i - offset + 1][j])>>1]; + float centerVal = riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen; + if(true || centerVal > 32) { + float korr; + float thresh; + if(blendMotion) { + korr = 0.f; + float average = scaleGreen * (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; + thresh = (5.f / sqrtf(average)) / 0.75f; + } else { + korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; + thresh = motionThreshold; } - if(skipNext) { - // treat the horizontally next pixel also as motion - j++; - offset ^= 1; +// float korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; +// float korr = 0.f; +// float average = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1])/2.f; +// float motionThreshold = 5.f * sqrtf(3.f*average/(scale_mul[1])) / (average * scaleGreen * 3.f); + if (gridMax > thresh - korr) { + float blend = (gridMax - thresh + korr) * blendFactor; + // at least one of the tested pixels of the grid is detected as motion + if(showMotion) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + } else if(false) { + greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); + nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); + nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); + } + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + offset ^= 1; + } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; } } + if(false && detectMotion && checkRedBlue) { + float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; + float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; + float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; + float diff0 = ng1 - ng0; + float diff2 = ng1 - ng2; + float gridMax; + if(diff0 * diff2 > 0.f) { +// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { + gridMax = greenDiff(ng1, std::max(ng0, ng2)); +// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); + if(gridMax > motionThreshold ) { + float factor = 1.f / (1.f - motionThreshold); + float blend = (gridMax - motionThreshold) * factor; + if(showMotion) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + nonGreenDest0[j + offsX] = 20000.f; + continue; +// greenDest[j + offsX+1] = nonGreenDest1[j + offsX+1] = 0.f; +// nonGreenDest0[j + offsX+1] = 20000.f; + } + greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; + +// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); +// nonGreenDest0[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; + nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); +// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); + nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; + +// nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); + +// if(skipNext) { +// // treat the horizontally next pixel also as motion +// j++; +// offset ^= 1; +// } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; + ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; + ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; + diff0 = ng1 - ng0; + diff2 = ng1 - ng2; + if(signbit(diff0) == signbit(diff2)) { +// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { + gridMax = greenDiff(ng1, std::max(ng0, ng2)); +// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); + if(gridMax > motionThreshold ) { + float factor = 1.f / (1.f - motionThreshold); + float blend = (gridMax - motionThreshold) * factor; + if(showMotion) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = nonGreenDest0[j + offsX] = 0.f; + nonGreenDest1[j + offsX] = 20000.f; +// greenDest[j + offsX+1] = nonGreenDest0[j + offsX+1] = 0.f; +// nonGreenDest1[j + offsX+1] = 20000.f; +continue; + } + greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; +// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); +// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); + nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; + +// nonGreenDest1[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; + nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); +// if(skipNext) { +// // treat the horizontally next pixel also as motion +// j++; +// offset ^= 1; +// } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + } // motion correction disabled or no motion detected => combine the values from the four pixelshift frames greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 466b25d82..eba1e36f4 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -888,6 +888,7 @@ void RAWParams::setDefaults() bayersensor.pixelshiftMotion = 70; bayersensor.pixelshiftMotionCorrection = 3; bayersensor.pixelshiftShowMotion = false; + bayersensor.pixelshiftBlendMotion = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3379,6 +3380,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); } + if (!pedited || pedited->raw.bayersensor.pixelshiftBlendMotion) { + keyFile.set_boolean ("RAW Bayer", "PixelShiftBlendMotion", raw.bayersensor.pixelshiftBlendMotion ); + } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { @@ -7461,6 +7466,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftBlendMotion")) { + raw.bayersensor.pixelshiftBlendMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftBlendMotion"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftBlendMotion = true; + } + } + //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 3f5951cf8..a6308454c 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1185,6 +1185,7 @@ public: int pixelshiftMotion; int pixelshiftMotionCorrection; bool pixelshiftShowMotion; + bool pixelshiftBlendMotion; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index f4760d44e..ed6efdae0 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1966,7 +1966,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection); + pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelshiftBlendMotion); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); @@ -3561,7 +3561,7 @@ void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int wi for (int col = winx; col < winx + winw; col++) { int c = FC(row,col); for(int frame = 0; frame < 4; ++frame) { - float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; + float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; tmpchmax[c] = max(tmpchmax[c], val); riFrames[frame]->data[row][col] = val; } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 648e5ec77..08e1643ed 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -263,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int method); + void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool blendMotion); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 92351be2b..5c80b93c1 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -86,6 +86,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); pixelShiftOptions->pack_start(*pixelShiftShowMotion); + pixelShiftBlendMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); + pixelShiftOptions->pack_start(*pixelShiftBlendMotion); + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); pixelShiftMotion->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); @@ -131,6 +134,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); + pixelShiftBlendMotionconn = pixelShiftBlendMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlendMotionChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -159,6 +163,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); + pixelShiftBlendMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftBlendMotion); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); @@ -177,6 +182,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); + pixelShiftBlendMotion->set_active(pp->raw.bayersensor.pixelshiftBlendMotion); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); @@ -232,6 +238,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getIntValue(); pp->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getIntValue(); pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); + pp->raw.bayersensor.pixelshiftBlendMotion = pixelShiftBlendMotion->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -255,6 +262,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getEditedState (); pedited->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getEditedState (); pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); + pedited->raw.bayersensor.pixelshiftBlendMotion = !pixelShiftBlendMotion->get_inconsistent(); } } @@ -402,6 +410,28 @@ void BayerProcess::pixelShiftShowMotionChanged () } } +void BayerProcess::pixelShiftBlendMotionChanged () +{ + if (batchMode) { + if (pixelShiftBlendMotion->get_inconsistent()) { + pixelShiftBlendMotion->set_inconsistent (false); + pixelShiftBlendMotionconn.block (true); + pixelShiftBlendMotion->set_active (false); + pixelShiftBlendMotionconn.block (false); + } else if (lastDCBen) { + pixelShiftBlendMotion->set_inconsistent (true); + } + + lastDCBen = pixelShiftBlendMotion->get_active (); + } + pixelShiftMotion->set_sensitive(!pixelShiftBlendMotion->get_active ()); + + if (listener) { + listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftBlendMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + /*void BayerProcess::allEnhanceChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 404324ed6..383adeb43 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -45,10 +45,11 @@ protected: Adjuster* pixelShiftMotion; Adjuster* pixelShiftMotionCorrection; Gtk::CheckButton* pixelShiftShowMotion; + Gtk::CheckButton* pixelShiftBlendMotion; bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftBlendMotionconn; //,allEnhconn; public: BayerProcess (); @@ -63,6 +64,7 @@ public: void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); void pixelShiftShowMotionChanged(); + void pixelShiftBlendMotionChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 6a020cd8b..5b045c2ce 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -373,6 +373,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelshiftMotion = v; raw.bayersensor.pixelshiftMotionCorrection = v; raw.bayersensor.pixelshiftShowMotion = v; + raw.bayersensor.pixelshiftBlendMotion = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -872,6 +873,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelshiftMotion = raw.bayersensor.pixelshiftMotion && p.raw.bayersensor.pixelshiftMotion == other.raw.bayersensor.pixelshiftMotion; raw.bayersensor.pixelshiftMotionCorrection = raw.bayersensor.pixelshiftMotionCorrection && p.raw.bayersensor.pixelshiftMotionCorrection == other.raw.bayersensor.pixelshiftMotionCorrection; raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; + raw.bayersensor.pixelshiftBlendMotion = raw.bayersensor.pixelshiftBlendMotion && p.raw.bayersensor.pixelshiftBlendMotion == other.raw.bayersensor.pixelshiftBlendMotion; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2296,6 +2298,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; } + if (raw.bayersensor.pixelshiftBlendMotion) { + toEdit.raw.bayersensor.pixelshiftBlendMotion = mods.raw.bayersensor.pixelshiftBlendMotion; + } + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 5546c9857..9d9bc3fb9 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -695,6 +695,7 @@ public: bool pixelshiftMotion; bool pixelshiftMotionCorrection; bool pixelshiftShowMotion; + bool pixelshiftBlendMotion; //bool allEnhance; bool greenEq; bool linenoise; From fa1717fddfb1b240819b6132443d7e4756fb4b63 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 21 Nov 2016 12:35:18 +0100 Subject: [PATCH 020/181] pixelshift bugfix --- rtengine/pixelshift.cc | 4 ++-- rtengine/rawimagesource.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 7869daa6d..333f22bd4 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -76,7 +76,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b bool checkRedBlue = (gridSize == 5); // bool checkRedBlue = false; unsigned int offsX = 0, offsY = 0; - if(detectMotion) { + if(detectMotion || blendMotion) { // if motion correction is enabled we have to adjust the offsets for the selected subframe we use for areas with motion switch (frame) { case 0: @@ -120,7 +120,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b float greenDifMax[gridSize]; // motion detection checks the grid around the pixel for differences in green channels - if(detectMotion) { + if(detectMotion || blendMotion) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j]), diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index ed6efdae0..2c07aee3a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1962,7 +1962,7 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { - if(raw.bayersensor.pixelshiftMotion > 0) { + if(raw.bayersensor.pixelshiftMotion > 0 || raw.bayersensor.pixelshiftBlendMotion) { amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { From efde91c226c78b1add01e48f08ba19e29e05e0a7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 21 Nov 2016 14:08:18 +0100 Subject: [PATCH 021/181] pixelshift bugfix --- rtengine/pixelshift.cc | 338 ++++++++++++++++++++--------------------- 1 file changed, 164 insertions(+), 174 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 333f22bd4..7394cb237 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -36,12 +36,14 @@ namespace { -float greenDiff(float a, float b) +float greenDiff(float a, float b, bool adaptive, float scale) { // calculate the difference between to green samples // add a small epsilon to avoid division by zero - return std::fabs(a - b) / (std::max(a, b) + 0.01f); - + float diff = std::fabs(a - b) / (std::max(a, b) + 0.01f); + if(adaptive) + diff -= ((5.f / sqrtf(scale * (a+b) / 2.f)) / 0.75f); + return diff; } } @@ -49,53 +51,52 @@ float greenDiff(float a, float b) using namespace std; using namespace rtengine; -void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool blendMotion) +void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive) { BENCHFUN - printf("%f\t%f\t%f\t%f\n",scale_mul[0],scale_mul[1],scale_mul[2],scale_mul[3]); - if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); plistener->setProgress(0.0); } - LUTf log2Lut(65536); + // Lookup table for non adaptive (slider) mode + LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); log2Lut[0] = 0; const float lutStrength = 2.f; const float scaleGreen = 1.f / scale_mul[1]; for(int i=2; i < 65536; i+=2) log2Lut[i>>1] = 2.f * log2(i) / 100.f; + + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); - // For 'blend' mode - const float blendFactor = 1.f / (1.f - motionThreshold); + // For shades of green motion indicators + const float blendFactor = (motion == 0.f ? 1.f : 1.f / (1.f - motionThreshold)); bool checkRedBlue = (gridSize == 5); // bool checkRedBlue = false; unsigned int offsX = 0, offsY = 0; - if(detectMotion || blendMotion) { - // if motion correction is enabled we have to adjust the offsets for the selected subframe we use for areas with motion - switch (frame) { - case 0: - offsX = offsY = 0; - break; + // We have to adjust the offsets for the selected subframe we use for areas with motion + switch (frame) { + case 0: + offsX = offsY = 0; + break; - case 1: - offsX = 0; - offsY = 1; - break; + case 1: + offsX = 0; + offsY = 1; + break; - case 2: - offsX = offsY = 1; - break; + case 2: + offsX = offsY = 1; + break; - case 3: - offsX = 1; - offsY = 0; - } + case 3: + offsX = 1; + offsY = 0; } #ifdef _OPENMP @@ -120,42 +121,42 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b float greenDifMax[gridSize]; // motion detection checks the grid around the pixel for differences in green channels - if(detectMotion || blendMotion) { + if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j]), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j]), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j]) + greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen) ); - greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1]), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1]) + greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1]), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1]), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1]), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1]), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1]) + greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, scaleGreen) ); - greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j]), - greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j]), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j]), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j]), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j]) + greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen) ); - greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1]), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1]), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1]), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1]) + greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen) ); - greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2]), - greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2]), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2]), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2]), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2]) + greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, scaleGreen) ); } } @@ -168,27 +169,27 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b for(; j < winw - (border + offsX); ++j) { offset ^= 1; // 0 => 1 or 1 => 0 - if(detectMotion || blendMotion) { + if(detectMotion || adaptive) { bool skipNext = false; float gridMax; if(gridSize == 1) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1]); + gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen); skipNext = !showMotion; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2]), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2]), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2]) + greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, scaleGreen) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3]), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3]), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3]), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3]), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3]) + greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, scaleGreen), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, scaleGreen), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, scaleGreen) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } @@ -197,124 +198,113 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b lastIndex = lastIndex == gridSize ? 0 : lastIndex; // increase motion detection dependent on brightness -// float korr = 2.f * log2Lut[((int)riFrames[1 - offset]->data[i - offset + 1][j])>>1]; - float centerVal = riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen; - if(true || centerVal > 32) { - float korr; - float thresh; - if(blendMotion) { - korr = 0.f; - float average = scaleGreen * (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; - thresh = (5.f / sqrtf(average)) / 0.75f; - } else { - korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; - thresh = motionThreshold; + float korr; + float thresh; + if(adaptive) { + korr = 0.f; + thresh = 0; + } else { + korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; + thresh = motionThreshold; + } + + if (gridMax > thresh - korr) { + float blend = (gridMax - thresh + korr) * blendFactor; + // at least one of the tested pixels of the grid is detected as motion + if(showMotion) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; } -// float korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; -// float korr = 0.f; -// float average = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1])/2.f; -// float motionThreshold = 5.f * sqrtf(3.f*average/(scale_mul[1])) / (average * scaleGreen * 3.f); - if (gridMax > thresh - korr) { - float blend = (gridMax - thresh + korr) * blendFactor; - // at least one of the tested pixels of the grid is detected as motion - if(showMotion) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - } else if(false) { - greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); - nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); - nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); - } - if(skipNext) { - // treat the horizontally next pixel also as motion - j++; - offset ^= 1; - } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; + + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + offset ^= 1; } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; } } - if(false && detectMotion && checkRedBlue) { - float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; - float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; - float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; - float diff0 = ng1 - ng0; - float diff2 = ng1 - ng2; - float gridMax; - if(diff0 * diff2 > 0.f) { -// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { - gridMax = greenDiff(ng1, std::max(ng0, ng2)); -// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); - if(gridMax > motionThreshold ) { - float factor = 1.f / (1.f - motionThreshold); - float blend = (gridMax - motionThreshold) * factor; - if(showMotion) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - nonGreenDest0[j + offsX] = 20000.f; - continue; -// greenDest[j + offsX+1] = nonGreenDest1[j + offsX+1] = 0.f; -// nonGreenDest0[j + offsX+1] = 20000.f; - } - greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; - -// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); -// nonGreenDest0[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; - nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); +// if(false && detectMotion && checkRedBlue) { +// float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; +// float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; +// float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; +// float diff0 = ng1 - ng0; +// float diff2 = ng1 - ng2; +// float gridMax; +// if(diff0 * diff2 > 0.f) { +//// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { +// gridMax = greenDiff(ng1, std::max(ng0, ng2)); +//// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); +// if(gridMax > motionThreshold ) { +// float factor = 1.f / (1.f - motionThreshold); +// float blend = (gridMax - motionThreshold) * factor; +// if(showMotion) { +// // if showMotion is enabled make the pixel green +// greenDest[j + offsX] = nonGreenDest1[j + offsX] = 0.f; +// nonGreenDest0[j + offsX] = 20000.f; +// continue; +//// greenDest[j + offsX+1] = nonGreenDest1[j + offsX+1] = 0.f; +//// nonGreenDest0[j + offsX+1] = 20000.f; +// } +// greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; +// +//// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); +//// nonGreenDest0[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; // nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); - nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; - +//// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); +// nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; +// +//// nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); +// +//// if(skipNext) { +//// // treat the horizontally next pixel also as motion +//// j++; +//// offset ^= 1; +//// } +// // do not set the motion pixel values. They have already been set by demosaicer or showMotion +// continue; +// } +// } +// ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; +// ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; +// ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; +// diff0 = ng1 - ng0; +// diff2 = ng1 - ng2; +// if(signbit(diff0) == signbit(diff2)) { +//// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { +// gridMax = greenDiff(ng1, std::max(ng0, ng2)); +//// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); +// if(gridMax > motionThreshold ) { +// float factor = 1.f / (1.f - motionThreshold); +// float blend = (gridMax - motionThreshold) * factor; +// if(showMotion) { +// // if showMotion is enabled make the pixel green +// greenDest[j + offsX] = nonGreenDest0[j + offsX] = 0.f; +// nonGreenDest1[j + offsX] = 20000.f; +//// greenDest[j + offsX+1] = nonGreenDest0[j + offsX+1] = 0.f; +//// nonGreenDest1[j + offsX+1] = 20000.f; +//continue; +// } +// greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; +//// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); +//// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); +// nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; +// +//// nonGreenDest1[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; // nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); - -// if(skipNext) { -// // treat the horizontally next pixel also as motion -// j++; -// offset ^= 1; -// } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; - } - } - ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; - ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; - ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; - diff0 = ng1 - ng0; - diff2 = ng1 - ng2; - if(signbit(diff0) == signbit(diff2)) { -// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { - gridMax = greenDiff(ng1, std::max(ng0, ng2)); -// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); - if(gridMax > motionThreshold ) { - float factor = 1.f / (1.f - motionThreshold); - float blend = (gridMax - motionThreshold) * factor; - if(showMotion) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = nonGreenDest0[j + offsX] = 0.f; - nonGreenDest1[j + offsX] = 20000.f; -// greenDest[j + offsX+1] = nonGreenDest0[j + offsX+1] = 0.f; -// nonGreenDest1[j + offsX+1] = 20000.f; -continue; - } - greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; -// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); -// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); - nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; - -// nonGreenDest1[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; - nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); -// if(skipNext) { -// // treat the horizontally next pixel also as motion -// j++; -// offset ^= 1; -// } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; - } - } - } +//// if(skipNext) { +//// // treat the horizontally next pixel also as motion +//// j++; +//// offset ^= 1; +//// } +// // do not set the motion pixel values. They have already been set by demosaicer or showMotion +// continue; +// } +// } +// } // motion correction disabled or no motion detected => combine the values from the four pixelshift frames greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; From fb043e9f5d3f890afbce7b497e06058202250c8c Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 21 Nov 2016 21:38:14 +0100 Subject: [PATCH 022/181] pixelshift: experimental changes for Ilias :) --- rtdata/languages/default | 6 +- rtengine/pixelshift.cc | 94 +++++++++++++++------------- rtengine/procparams.cc | 64 +++++++++++++++++-- rtengine/procparams.h | 6 +- rtengine/rawimagesource.cc | 4 +- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 124 ++++++++++++++++++++++++++++++++----- rtgui/bayerprocess.h | 10 ++- rtgui/paramsedited.cc | 32 ++++++++-- rtgui/paramsedited.h | 6 +- 10 files changed, 270 insertions(+), 78 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 44d540315..8520b8c36 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1654,12 +1654,16 @@ TP_RAW_IMAGENUM;Some raw files might embed several sub-images (HDR, Pixel-Shift, TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. -TP_RAW_PIXELSHIFTADAPTIVE;Automatic detection +TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion +TP_RAW_PIXELSHIFTSTDDEVFACTOR;StdDev factor +TP_RAW_PIXELSHIFTEPERISO;e per ISO +TP_RAW_PIXELSHIFTNREADISO;nRead per ISO +TP_RAW_PIXELSHIFTPRNU;PRNU TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster. TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 7394cb237..e88c4bfeb 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -36,13 +36,18 @@ namespace { -float greenDiff(float a, float b, bool adaptive, float scale) +float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor, float eperIso, float nreadIso, float prnu) { // calculate the difference between to green samples // add a small epsilon to avoid division by zero float diff = std::fabs(a - b) / (std::max(a, b) + 0.01f); - if(adaptive) - diff -= ((5.f / sqrtf(scale * (a+b) / 2.f)) / 0.75f); + if(adaptive) { + float avg = (a+b)/2.f; + avg *= scale; + float stddev = sqrtf(avg * eperIso + nreadIso * nreadIso + prnu * prnu); + float korr = stddevFactor * stddev / (a * scale); + diff -= korr; + } return diff; } @@ -51,7 +56,7 @@ float greenDiff(float a, float b, bool adaptive, float scale) using namespace std; using namespace rtengine; -void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive) +void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu) { BENCHFUN @@ -62,6 +67,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b } + gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); log2Lut[0] = 0; @@ -70,13 +76,18 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b for(int i=2; i < 65536; i+=2) log2Lut[i>>1] = 2.f * log2(i) / 100.f; - +// const float eperIso = 0.75f * idata->getISOSpeed() / 100; + eperIso *= (idata->getISOSpeed() / 100); + nreadIso *= (idata->getISOSpeed() / 100); +// const float nreadIso = 5.f * idata->getISOSpeed() / 100; +// const float prnu = 1.f; +// const float stddevFactor = 4.f; // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); // For shades of green motion indicators const float blendFactor = (motion == 0.f ? 1.f : 1.f / (1.f - motionThreshold)); - bool checkRedBlue = (gridSize == 5); +// bool checkRedBlue = (gridSize == 5); // bool checkRedBlue = false; unsigned int offsX = 0, offsY = 0; // We have to adjust the offsets for the selected subframe we use for areas with motion @@ -118,45 +129,44 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop unsigned int offset = (c & 1); - float greenDifMax[gridSize]; // motion detection checks the grid around the pixel for differences in green channels if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen) + greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); - greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen) + greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, scaleGreen) + greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); - greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen) + greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); - greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen) + greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); - greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, scaleGreen) + greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); } } @@ -174,22 +184,22 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b float gridMax; if(gridSize == 1) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen); + gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu); skipNext = !showMotion; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, scaleGreen) + greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, scaleGreen), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, scaleGreen), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, scaleGreen) + greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index eba1e36f4..42e31b3ed 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -887,8 +887,12 @@ void RAWParams::setDefaults() bayersensor.lmmse_iterations = 2; bayersensor.pixelshiftMotion = 70; bayersensor.pixelshiftMotionCorrection = 3; + bayersensor.pixelShiftStddevFactor = 5.0; + bayersensor.pixelShiftEperIso = 0.75; + bayersensor.pixelShiftNreadIso = 5.0; + bayersensor.pixelShiftPrnu = 1.0; bayersensor.pixelshiftShowMotion = false; - bayersensor.pixelshiftBlendMotion = false; + bayersensor.pixelShiftAutomatic = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3376,12 +3380,28 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelshiftMotionCorrection ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactor) { + keyFile.set_double ("RAW Bayer", "PixelShiftStddevFactor", raw.bayersensor.pixelShiftStddevFactor ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftEperIso) { + keyFile.set_double ("RAW Bayer", "PixelShiftEperIso", raw.bayersensor.pixelShiftEperIso ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNreadIso) { + keyFile.set_double ("RAW Bayer", "PixelShiftNreadIso", raw.bayersensor.pixelShiftNreadIso ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftPrnu) { + keyFile.set_double ("RAW Bayer", "PixelShiftPrnu", raw.bayersensor.pixelShiftPrnu ); + } + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotion) { keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); } - if (!pedited || pedited->raw.bayersensor.pixelshiftBlendMotion) { - keyFile.set_boolean ("RAW Bayer", "PixelShiftBlendMotion", raw.bayersensor.pixelshiftBlendMotion ); + if (!pedited || pedited->raw.bayersensor.pixelShiftAutomatic) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftAutomatic", raw.bayersensor.pixelShiftAutomatic ); } //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); @@ -7458,6 +7478,38 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftStddevFactor")) { + raw.bayersensor.pixelShiftStddevFactor = keyFile.get_double("RAW Bayer", "PixelShiftStddevFactor"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactor = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftEperIso")) { + raw.bayersensor.pixelShiftEperIso = keyFile.get_double("RAW Bayer", "PixelShiftEperIso"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftEperIso = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftNreadIso")) { + raw.bayersensor.pixelShiftNreadIso = keyFile.get_double("RAW Bayer", "PixelShiftNreadIso"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNreadIso = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftPrnu")) { + raw.bayersensor.pixelShiftPrnu = keyFile.get_double("RAW Bayer", "PixelShiftPrnu"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftPrnu = true; + } + } + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotion")) { raw.bayersensor.pixelshiftShowMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotion"); @@ -7466,11 +7518,11 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - if (keyFile.has_key ("RAW Bayer", "PixelShiftBlendMotion")) { - raw.bayersensor.pixelshiftBlendMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftBlendMotion"); + if (keyFile.has_key ("RAW Bayer", "pixelShiftAutomatic")) { + raw.bayersensor.pixelShiftAutomatic = keyFile.get_boolean("RAW Bayer", "pixelShiftAutomatic"); if (pedited) { - pedited->raw.bayersensor.pixelshiftBlendMotion = true; + pedited->raw.bayersensor.pixelShiftAutomatic = true; } } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index a6308454c..5bd3e4646 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1184,8 +1184,12 @@ public: int lmmse_iterations; int pixelshiftMotion; int pixelshiftMotionCorrection; + double pixelShiftStddevFactor; + double pixelShiftEperIso; + double pixelShiftNreadIso; + double pixelShiftPrnu; bool pixelshiftShowMotion; - bool pixelshiftBlendMotion; + bool pixelShiftAutomatic; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 2c07aee3a..d3e6b1881 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1962,11 +1962,11 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { - if(raw.bayersensor.pixelshiftMotion > 0 || raw.bayersensor.pixelshiftBlendMotion) { + if(raw.bayersensor.pixelshiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) { amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelshiftBlendMotion); + pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 08e1643ed..75037710b 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -263,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool blendMotion); + void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 5c80b93c1..867d911e5 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -86,8 +86,8 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); pixelShiftOptions->pack_start(*pixelShiftShowMotion); - pixelShiftBlendMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); - pixelShiftOptions->pack_start(*pixelShiftBlendMotion); + pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); + pixelShiftOptions->pack_start(*pixelShiftAutomatic); pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); @@ -109,6 +109,52 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->show(); pixelShiftOptions->pack_start(*pixelShiftMotionCorrection); + + pixelShiftStddevFactor = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR"), 2, 8, 0.1, 5)); + pixelShiftStddevFactor->setAdjusterListener (this); +// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); + + if (pixelShiftStddevFactor->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactor->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactor->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactor); + + pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), 0.3, 1.0, 0.05, 0.75)); + pixelShiftEperIso->setAdjusterListener (this); +// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); + + if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { + pixelShiftEperIso->delay = options.adjusterMaxDelay; + } + + pixelShiftEperIso->show(); + pixelShiftOptions->pack_start(*pixelShiftEperIso); + + pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), 1.0, 10.0, 0.5, 5.0)); + pixelShiftNreadIso->setAdjusterListener (this); +// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); + + if (pixelShiftNreadIso->delay < options.adjusterMaxDelay) { + pixelShiftNreadIso->delay = options.adjusterMaxDelay; + } + + pixelShiftNreadIso->show(); + pixelShiftOptions->pack_start(*pixelShiftNreadIso); + + + pixelShiftPrnu = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTPRNU"), 0.3, 2.0, 0.1, 1.0)); + pixelShiftPrnu->setAdjusterListener (this); +// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); + + if (pixelShiftPrnu->delay < options.adjusterMaxDelay) { + pixelShiftPrnu->delay = options.adjusterMaxDelay; + } + + pixelShiftPrnu->show(); + pixelShiftOptions->pack_start(*pixelShiftPrnu); + pack_start( *pixelShiftOptions, Gtk::PACK_SHRINK, 4); @@ -134,7 +180,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); - pixelShiftBlendMotionconn = pixelShiftBlendMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlendMotionChanged), true); + pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -163,11 +209,15 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); - pixelShiftBlendMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftBlendMotion); + pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); pixelShiftMotionCorrection->setEditedState ( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); + pixelShiftStddevFactor->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); + pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); + pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); if(!pedited->raw.bayersensor.method) { method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name @@ -182,11 +232,15 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); - pixelShiftBlendMotion->set_active(pp->raw.bayersensor.pixelshiftBlendMotion); + pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); pixelShiftMotionCorrection->setValue (pp->raw.bayersensor.pixelshiftMotionCorrection); + pixelShiftStddevFactor->setValue (pp->raw.bayersensor.pixelShiftStddevFactor); + pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); + pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || @@ -237,8 +291,12 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); pp->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getIntValue(); pp->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getIntValue(); + pp->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getValue(); + pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); + pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); + pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); - pp->raw.bayersensor.pixelshiftBlendMotion = pixelShiftBlendMotion->get_active(); + pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -261,8 +319,12 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); pedited->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getEditedState (); pedited->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getEditedState (); + pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); + pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); + pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); - pedited->raw.bayersensor.pixelshiftBlendMotion = !pixelShiftBlendMotion->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); } } @@ -281,6 +343,10 @@ void BayerProcess::setBatchMode(bool batchMode) lmmseIterations->showEditedCB (); pixelShiftMotion->showEditedCB (); pixelShiftMotionCorrection->showEditedCB (); + pixelShiftStddevFactor->showEditedCB (); + pixelShiftEperIso->showEditedCB (); + pixelShiftNreadIso->showEditedCB (); + pixelShiftPrnu->showEditedCB (); } void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) @@ -289,6 +355,10 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelshiftMotion); pixelShiftMotionCorrection->setDefault( defParams->raw.bayersensor.pixelshiftMotionCorrection); + pixelShiftStddevFactor->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactor); + pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); + pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setDefault( defParams->raw.bayersensor.pixelShiftPrnu); ccSteps->setDefault (defParams->raw.bayersensor.ccSteps); if (pedited) { @@ -296,12 +366,20 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); pixelShiftMotionCorrection->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); + pixelShiftStddevFactor->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); + pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); + pixelShiftPrnu->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); ccSteps->setDefaultEditedState(pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); } else { dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); pixelShiftMotion->setDefaultEditedState( Irrelevant ); pixelShiftMotionCorrection->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactor->setDefaultEditedState( Irrelevant ); + pixelShiftEperIso->setDefaultEditedState( Irrelevant ); + pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); + pixelShiftPrnu->setDefaultEditedState( Irrelevant ); ccSteps->setDefaultEditedState(Irrelevant ); } } @@ -319,6 +397,14 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); } else if (a == pixelShiftMotionCorrection) { listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + } else if (a == pixelShiftStddevFactor) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + } else if (a == pixelShiftEperIso) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + } else if (a == pixelShiftNreadIso) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + } else if (a == pixelShiftPrnu) { + listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); } } } @@ -410,24 +496,28 @@ void BayerProcess::pixelShiftShowMotionChanged () } } -void BayerProcess::pixelShiftBlendMotionChanged () +void BayerProcess::pixelShiftAutomaticChanged () { if (batchMode) { - if (pixelShiftBlendMotion->get_inconsistent()) { - pixelShiftBlendMotion->set_inconsistent (false); - pixelShiftBlendMotionconn.block (true); - pixelShiftBlendMotion->set_active (false); - pixelShiftBlendMotionconn.block (false); + if (pixelShiftAutomatic->get_inconsistent()) { + pixelShiftAutomatic->set_inconsistent (false); + pixelShiftAutomaticconn.block (true); + pixelShiftAutomatic->set_active (false); + pixelShiftAutomaticconn.block (false); } else if (lastDCBen) { - pixelShiftBlendMotion->set_inconsistent (true); + pixelShiftAutomatic->set_inconsistent (true); } - lastDCBen = pixelShiftBlendMotion->get_active (); + lastDCBen = pixelShiftAutomatic->get_active (); } - pixelShiftMotion->set_sensitive(!pixelShiftBlendMotion->get_active ()); + pixelShiftMotion->set_sensitive(!pixelShiftAutomatic->get_active ()); + pixelShiftEperIso->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNreadIso->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftStddevFactor->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftBlendMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 383adeb43..b490f8c42 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -45,11 +45,15 @@ protected: Adjuster* pixelShiftMotion; Adjuster* pixelShiftMotionCorrection; Gtk::CheckButton* pixelShiftShowMotion; - Gtk::CheckButton* pixelShiftBlendMotion; + Gtk::CheckButton* pixelShiftAutomatic; + Adjuster* pixelShiftStddevFactor; + Adjuster* pixelShiftEperIso; + Adjuster* pixelShiftNreadIso; + Adjuster* pixelShiftPrnu; bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftBlendMotionconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftAutomaticconn; //,allEnhconn; public: BayerProcess (); @@ -64,7 +68,7 @@ public: void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); void pixelShiftShowMotionChanged(); - void pixelShiftBlendMotionChanged(); + void pixelShiftAutomaticChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 5b045c2ce..87dab2311 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -372,8 +372,12 @@ void ParamsEdited::set (bool v) raw.bayersensor.lmmseIterations = v; raw.bayersensor.pixelshiftMotion = v; raw.bayersensor.pixelshiftMotionCorrection = v; + raw.bayersensor.pixelShiftStddevFactor = v; + raw.bayersensor.pixelShiftEperIso = v; + raw.bayersensor.pixelShiftNreadIso = v; + raw.bayersensor.pixelShiftPrnu = v; raw.bayersensor.pixelshiftShowMotion = v; - raw.bayersensor.pixelshiftBlendMotion = v; + raw.bayersensor.pixelShiftAutomatic = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -872,8 +876,12 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; raw.bayersensor.pixelshiftMotion = raw.bayersensor.pixelshiftMotion && p.raw.bayersensor.pixelshiftMotion == other.raw.bayersensor.pixelshiftMotion; raw.bayersensor.pixelshiftMotionCorrection = raw.bayersensor.pixelshiftMotionCorrection && p.raw.bayersensor.pixelshiftMotionCorrection == other.raw.bayersensor.pixelshiftMotionCorrection; + raw.bayersensor.pixelShiftStddevFactor = raw.bayersensor.pixelShiftStddevFactor && p.raw.bayersensor.pixelShiftStddevFactor == other.raw.bayersensor.pixelShiftStddevFactor; + raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; + raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; + raw.bayersensor.pixelShiftPrnu = raw.bayersensor.pixelShiftPrnu && p.raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu; raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; - raw.bayersensor.pixelshiftBlendMotion = raw.bayersensor.pixelshiftBlendMotion && p.raw.bayersensor.pixelshiftBlendMotion == other.raw.bayersensor.pixelshiftBlendMotion; + raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2294,12 +2302,28 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelshiftMotionCorrection = mods.raw.bayersensor.pixelshiftMotionCorrection; } + if (raw.bayersensor.pixelShiftStddevFactor) { + toEdit.raw.bayersensor.pixelShiftStddevFactor = mods.raw.bayersensor.pixelShiftStddevFactor; + } + + if (raw.bayersensor.pixelShiftEperIso) { + toEdit.raw.bayersensor.pixelShiftEperIso = mods.raw.bayersensor.pixelShiftEperIso; + } + + if (raw.bayersensor.pixelShiftNreadIso) { + toEdit.raw.bayersensor.pixelShiftNreadIso = mods.raw.bayersensor.pixelShiftNreadIso; + } + + if (raw.bayersensor.pixelShiftPrnu) { + toEdit.raw.bayersensor.pixelShiftPrnu = mods.raw.bayersensor.pixelShiftPrnu; + } + if (raw.bayersensor.pixelshiftShowMotion) { toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; } - if (raw.bayersensor.pixelshiftBlendMotion) { - toEdit.raw.bayersensor.pixelshiftBlendMotion = mods.raw.bayersensor.pixelshiftBlendMotion; + if (raw.bayersensor.pixelShiftAutomatic) { + toEdit.raw.bayersensor.pixelShiftAutomatic = mods.raw.bayersensor.pixelShiftAutomatic; } //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 9d9bc3fb9..c116350a8 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -694,8 +694,12 @@ public: bool lmmseIterations; bool pixelshiftMotion; bool pixelshiftMotionCorrection; + bool pixelShiftStddevFactor; + bool pixelShiftEperIso; + bool pixelShiftNreadIso; + bool pixelShiftPrnu; bool pixelshiftShowMotion; - bool pixelshiftBlendMotion; + bool pixelShiftAutomatic; //bool allEnhance; bool greenEq; bool linenoise; From 520557912b8f2358b649058fcfe826cdb8cf37c8 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 22 Nov 2016 16:35:11 +0100 Subject: [PATCH 023/181] pixelshift: changed formula for adaptive motion detection --- rtdata/languages/default | 4 ++-- rtengine/pixelshift.cc | 6 ++++-- rtgui/bayerprocess.cc | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 8520b8c36..01d5270e1 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1662,8 +1662,8 @@ TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 gr TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_PIXELSHIFTSTDDEVFACTOR;StdDev factor TP_RAW_PIXELSHIFTEPERISO;e per ISO -TP_RAW_PIXELSHIFTNREADISO;nRead per ISO -TP_RAW_PIXELSHIFTPRNU;PRNU +TP_RAW_PIXELSHIFTNREADISO;nRead +TP_RAW_PIXELSHIFTPRNU;PRNU (%) TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster. TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index e88c4bfeb..c4c7d2c7d 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -43,7 +43,8 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor float diff = std::fabs(a - b) / (std::max(a, b) + 0.01f); if(adaptive) { float avg = (a+b)/2.f; - avg *= scale; + avg *= scale; // revert the colour scaling + prnu *= (avg * eperIso); float stddev = sqrtf(avg * eperIso + nreadIso * nreadIso + prnu * prnu); float korr = stddevFactor * stddev / (a * scale); diff -= korr; @@ -78,7 +79,8 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b // const float eperIso = 0.75f * idata->getISOSpeed() / 100; eperIso *= (idata->getISOSpeed() / 100); - nreadIso *= (idata->getISOSpeed() / 100); +// nreadIso *= (idata->getISOSpeed() / 100); + prnu /= 100.f; // const float nreadIso = 5.f * idata->getISOSpeed() / 100; // const float prnu = 1.f; // const float stddevFactor = 4.f; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 867d911e5..63705e472 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -132,7 +132,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftEperIso->show(); pixelShiftOptions->pack_start(*pixelShiftEperIso); - pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), 1.0, 10.0, 0.5, 5.0)); + pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), 1.0, 10.0, 0.05, 3.45)); pixelShiftNreadIso->setAdjusterListener (this); // pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); From 622fddb15cbcd3e4713549344fc53037eb8d8381 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 23 Nov 2016 00:28:26 +0100 Subject: [PATCH 024/181] changed name from 'pixelshift simple' to 'pixelshift' --- rtengine/procparams.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 42e31b3ed..5da9f05fd 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -40,7 +40,7 @@ const int br = (int) options.rtSettings.bot_right; const int tl = (int) options.rtSettings.top_left; const int bl = (int) options.rtSettings.bot_left; -const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none", "pixelshift simple" }; +const char *RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::numMethods] = {"amaze", "igv", "lmmse", "eahd", "hphd", "vng4", "dcb", "ahd", "fast", "mono", "none", "pixelshift" }; const char *RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::numMethods] = {"3-pass (best)", "1-pass (medium)", "fast", "mono", "none" }; const char *RAWParams::ff_BlurTypestring[RAWParams::numFlatFileBlurTypes] = {/*"Parametric",*/ "Area Flatfield", "Vertical Flatfield", "Horizontal Flatfield", "V+H Flatfield"}; From fe5f862c092449423a2ff972f5cf11ae0612a7aa Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 23 Nov 2016 17:45:32 +0100 Subject: [PATCH 025/181] use camconst.json from master --- rtengine/camconst.json | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/rtengine/camconst.json b/rtengine/camconst.json index cbef6d132..ed28618ab 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -1003,8 +1003,8 @@ Camera constants: "ranges": { "white": 16300 } }, - { // Quality B - "make_model": [ "Canon PowerShot G7 X", "Canon PowerShot G7 X Mark II" ], + { // Quality B, + "make_model": "Canon PowerShot G7 X", "dcraw_matrix": [ 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 ], // DNG_V8.7 D65 //"raw_crop": [ 116, 24, 5504, 3680 ], // Sensor size 5632x3710. Largest useful frame 120-5616X28-3702 = 5504x3682, 4pix RTborders, Left Border 120-4, Top border 28-4 "raw_crop": [ 128, 36, 5480, 3656 ], // Default official 3/2 frame 5472X3648, 4pix borders, Left Border 132-4, Top border 40-4 @@ -1012,8 +1012,8 @@ Camera constants: "ranges": { "white": 4080 } }, - { // Quality B - "make_model": [ "Canon PowerShot G5 X", "Canon PowerShot G9 X" ], + { // Quality B, + "make_model": [ "Canon PowerShot G5 X", "Canon PowerShot G9 X", "Canon PowerShot G7 X Mark II" ], "dcraw_matrix": [ 9602,-3823,-937,-2984,11495,1675,-407,1415,5049 ], // DNG_V8.7 D65 //"raw_crop": [ 116, 24, 5504, 3680 ], // Sensor size 5632x3710. Largest useful frame 120-5616X28-3702 = 5504x3682, 4pix RTborders, Left Border 120-4, Top border 28-4 "raw_crop": [ 128, 36, 5480, 3656 ], // Default official 3/2 frame 5472X3648, 4pix borders, Left Border 132-4, Top border 40-4 @@ -1332,6 +1332,20 @@ Camera constants: } }, + { // Quality C, 20Mp and 80Mp raw frames, Color matrix copied from EM5MKII which looks to be close. + "make_model": "OLYMPUS E-M1MarkII", + "dcraw_matrix": [ 9422,-3258,-711,-2655,10898,2015,-512,1354,5512 ], // E-M5II dng_v9.5 D65 + "raw_crop": [ 8, 8, -16, -8 ], // full raw 5240X3912, jpeg top12,left12,5184x3888, full hires 10400X7792, jpeg crop 8,8,10368x7776 + "ranges": { + "white": [ + { "iso": [ 64, 100, 125, 160, 200, 250, 320, 400 ], "levels": 4090 }, // normal 4095 + { "iso": [ 500, 640, 800, 1000, 1600 ], "levels": 4080 }, // 4085-4095 + { "iso": [ 2000, 2500, 3200, 4000, 5000, 6400 ], "levels": 4060 }, // 4085-4095 + { "iso": [ 12800, 25600, 51200 ], "levels": 4060 } // guess + ] + } + }, + { // Quality B, missing per ISO samples "make_model": "OLYMPUS E-M1", "dcraw_matrix": [ 7687,-1984,-606,-4327,11928,2721,-1381,2339,6452 ], // dng d65 @@ -1702,6 +1716,22 @@ Camera constants: } }, + { // Quality B, Intemediate ISO samples missing, Pentax_DNG WLtags are after BL sutraction and not valid + "make_model": [ "RICOH PENTAX K-1", "PENTAX K-1" ], + "dcraw_matrix": [ 8596,-2981,-639,-4202,12046,2431,-685,1424,6122 ], // adobe DNG v9.7 D65 + //"dcraw_matrix": [ 8566,-2746,-1201,-3612,12204,1550,-893,1680,6264 ], // PENTAX DNG + "raw_crop": [ 6, 18, 7376, 4932 ], // full frame 7392x4950, cropped to official DNG raw_crop 6,18,7382,4950, official jpeg crop 8,10,7360x4912 + "ranges": { + "white": [ + { "iso": [ 100, 200, 400, 800 ], "levels": 16300 }, // 16380 + { "iso": [ 1600, 3200 ], "levels": 16250 }, // 16360 + { "iso": [ 6400, 12800 ], "levels": 16200 }, // 16330 + { "iso": [ 25600, 51200 ], "levels": 16100 }, // 16300 + { "iso": 102400, "levels": 16000 } // 16200 + ] + } + }, + { // Quality B, intermediate ISOs info missing "make_model": [ "RICOH PENTAX K-3", "PENTAX K-3" ], "dcraw_matrix": [ 7415,-2052,-721,-5186,12788,2682,-1446,2157,6773 ], // adobe dcp d65 From d83834d4e6a5eb9ab2791b61d03f34f56f6595f7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 24 Nov 2016 15:11:07 +0100 Subject: [PATCH 026/181] pixelshift: Correction for adaptive mode --- rtengine/pixelshift.cc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index c4c7d2c7d..68393da38 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -40,13 +40,14 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor { // calculate the difference between to green samples // add a small epsilon to avoid division by zero - float diff = std::fabs(a - b) / (std::max(a, b) + 0.01f); + float maxVal = std::max(a, b) + 0.01f; + float diff = std::fabs(a - b) / maxVal; if(adaptive) { float avg = (a+b)/2.f; avg *= scale; // revert the colour scaling prnu *= (avg * eperIso); float stddev = sqrtf(avg * eperIso + nreadIso * nreadIso + prnu * prnu); - float korr = stddevFactor * stddev / (a * scale); + float korr = stddevFactor * stddev / (maxVal * scale); diff -= korr; } return diff; @@ -77,13 +78,9 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b for(int i=2; i < 65536; i+=2) log2Lut[i>>1] = 2.f * log2(i) / 100.f; -// const float eperIso = 0.75f * idata->getISOSpeed() / 100; eperIso *= (idata->getISOSpeed() / 100); -// nreadIso *= (idata->getISOSpeed() / 100); prnu /= 100.f; -// const float nreadIso = 5.f * idata->getISOSpeed() / 100; -// const float prnu = 1.f; -// const float stddevFactor = 4.f; + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); // For shades of green motion indicators From 1fcbdd8966a97ee70494df2b3bff7d56540922aa Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 24 Nov 2016 19:01:33 +0100 Subject: [PATCH 027/181] pixelshift: new version of adapaptive motion detection --- rtengine/pixelshift.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 68393da38..4a3d9e7e6 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -41,13 +41,17 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor // calculate the difference between to green samples // add a small epsilon to avoid division by zero float maxVal = std::max(a, b) + 0.01f; - float diff = std::fabs(a - b) / maxVal; + float gDiff = std::fabs(a - b); + float diff = gDiff / maxVal; if(adaptive) { float avg = (a+b)/2.f; avg *= scale; // revert the colour scaling - prnu *= (avg * eperIso); - float stddev = sqrtf(avg * eperIso + nreadIso * nreadIso + prnu * prnu); - float korr = stddevFactor * stddev / (maxVal * scale); + avg *= eperIso; + prnu *= avg; + float stddev = sqrtf(avg + nreadIso * nreadIso + prnu * prnu); +// float korr = stddevFactor * stddev / (a * scale); // V0: use G1 not scaled by eperIso +// float korr = stddevFactor * stddev / (maxVal * scale); // V1: use max(G1,G2) not scaled by eperIso + float korr = stddevFactor * stddev / (gDiff / (eperIso * scale)); // V2: use absolute difference abs(G1-G2) scaled by eperISo diff -= korr; } return diff; @@ -72,12 +76,14 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); - log2Lut[0] = 0; - const float lutStrength = 2.f; + + if(detectMotion && !adaptive) { + const float lutStrength = 2.f; + log2Lut[0] = 0; + for(int i=2; i < 65536; i+=2) + log2Lut[i>>1] = lutStrength * log2(i) / 100.f; + } const float scaleGreen = 1.f / scale_mul[1]; - for(int i=2; i < 65536; i+=2) - log2Lut[i>>1] = 2.f * log2(i) / 100.f; - eperIso *= (idata->getISOSpeed() / 100); prnu /= 100.f; From 7eda37a081ae4410faaeead2994c0f37c0509658 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 24 Nov 2016 19:28:11 +0100 Subject: [PATCH 028/181] pixelshift: adaptice method V3 (worse than V2 imho) --- rtengine/pixelshift.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 4a3d9e7e6..4c1c1f122 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -51,7 +51,8 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor float stddev = sqrtf(avg + nreadIso * nreadIso + prnu * prnu); // float korr = stddevFactor * stddev / (a * scale); // V0: use G1 not scaled by eperIso // float korr = stddevFactor * stddev / (maxVal * scale); // V1: use max(G1,G2) not scaled by eperIso - float korr = stddevFactor * stddev / (gDiff / (eperIso * scale)); // V2: use absolute difference abs(G1-G2) scaled by eperISo +// float korr = stddevFactor * stddev / (gDiff / (eperIso * scale)); // V2: use absolute difference abs(G1-G2) scaled by eperISo + float korr = stddevFactor * stddev / (gDiff * eperIso * scale); // V3: corrected version of V2 diff -= korr; } return diff; From 28358d6bf2d3d6826ef4b03ec71537f4ecae3054 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 24 Nov 2016 20:01:16 +0100 Subject: [PATCH 029/181] pixelshift: added V4 for tests --- rtengine/pixelshift.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 4c1c1f122..c3f2cb986 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -50,9 +50,10 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor prnu *= avg; float stddev = sqrtf(avg + nreadIso * nreadIso + prnu * prnu); // float korr = stddevFactor * stddev / (a * scale); // V0: use G1 not scaled by eperIso -// float korr = stddevFactor * stddev / (maxVal * scale); // V1: use max(G1,G2) not scaled by eperIso + float korr = stddevFactor * stddev / (maxVal * scale); // V1: use max(G1,G2) not scaled by eperIso // float korr = stddevFactor * stddev / (gDiff / (eperIso * scale)); // V2: use absolute difference abs(G1-G2) scaled by eperISo - float korr = stddevFactor * stddev / (gDiff * eperIso * scale); // V3: corrected version of V2 +// float korr = stddevFactor * stddev / (gDiff * eperIso * scale); // V3: corrected version of V2 +// float korr = stddevFactor * stddev / (maxVal * scale * eperIso); // V4: use max(G1,G2) scaled by eperIso diff -= korr; } return diff; From d84b54cfe0e65e021500bbe90ccc1aa7dc71510e Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 24 Nov 2016 21:17:40 +0100 Subject: [PATCH 030/181] pixelshift: Fixed wrong ISO dependency --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index c3f2cb986..8e7a38fcd 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -86,7 +86,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b log2Lut[i>>1] = lutStrength * log2(i) / 100.f; } const float scaleGreen = 1.f / scale_mul[1]; - eperIso *= (idata->getISOSpeed() / 100); + eperIso *= (100 / idata->getISOSpeed()); prnu /= 100.f; // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel From 39a02ea8278c1ada2f46b1b6a9b751cc1d5725bd Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 25 Nov 2016 01:57:22 +0100 Subject: [PATCH 031/181] pixelshift: added some verbose console output and reverted inverse ISO scaling --- rtengine/pixelshift.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 8e7a38fcd..be0308908 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -75,6 +75,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b } + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %f\tnread %f\tprnu %f\n",gridSize, adaptive, stddevFactor, eperIso, nreadIso, prnu); gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); @@ -86,7 +87,7 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b log2Lut[i>>1] = lutStrength * log2(i) / 100.f; } const float scaleGreen = 1.f / scale_mul[1]; - eperIso *= (100 / idata->getISOSpeed()); + eperIso *= (idata->getISOSpeed() / 100.f); prnu /= 100.f; // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel From 802e7986616993fb2d24c107d7072b23e8e1225e Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 25 Nov 2016 13:56:40 +0100 Subject: [PATCH 032/181] pixelshift: simplified calculation of adaptive detection --- rtengine/pixelshift.cc | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index be0308908..e2a59c314 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -39,24 +39,21 @@ namespace float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor, float eperIso, float nreadIso, float prnu) { // calculate the difference between to green samples - // add a small epsilon to avoid division by zero - float maxVal = std::max(a, b) + 0.01f; float gDiff = std::fabs(a - b); - float diff = gDiff / maxVal; if(adaptive) { - float avg = (a+b)/2.f; + float avg = (a + b) / 2.f; avg *= scale; // revert the colour scaling avg *= eperIso; prnu *= avg; - float stddev = sqrtf(avg + nreadIso * nreadIso + prnu * prnu); -// float korr = stddevFactor * stddev / (a * scale); // V0: use G1 not scaled by eperIso - float korr = stddevFactor * stddev / (maxVal * scale); // V1: use max(G1,G2) not scaled by eperIso -// float korr = stddevFactor * stddev / (gDiff / (eperIso * scale)); // V2: use absolute difference abs(G1-G2) scaled by eperISo -// float korr = stddevFactor * stddev / (gDiff * eperIso * scale); // V3: corrected version of V2 -// float korr = stddevFactor * stddev / (maxVal * scale * eperIso); // V4: use max(G1,G2) scaled by eperIso - diff -= korr; + float stddev = stddevFactor * sqrtf(avg + nreadIso * nreadIso + prnu * prnu); + gDiff *= scale; + gDiff *= eperIso; + return gDiff - stddev; + } else { + // add a small epsilon to avoid division by zero + float maxVal = std::max(a, b) + 0.01f; + return gDiff / maxVal; } - return diff; } } @@ -87,7 +84,9 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b log2Lut[i>>1] = lutStrength * log2(i) / 100.f; } const float scaleGreen = 1.f / scale_mul[1]; - eperIso *= (idata->getISOSpeed() / 100.f); + + eperIso *= (100.f / idata->getISOSpeed()); + prnu /= 100.f; // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel From b4ef423acd943c972d1231d95a9a360e0db8ec38 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 25 Nov 2016 18:22:26 +0100 Subject: [PATCH 033/181] pixelshift: show mask only, Speedup for adaptive motion detection --- rtdata/languages/default | 1 + rtengine/pixelshift.cc | 114 +++++++++++++++++++++---------------- rtengine/procevents.h | 1 + rtengine/procparams.cc | 17 +++++- rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 3 +- rtgui/bayerprocess.cc | 30 +++++++++- rtgui/bayerprocess.h | 4 +- rtgui/paramsedited.cc | 6 ++ rtgui/paramsedited.h | 1 + 12 files changed, 127 insertions(+), 55 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 01d5270e1..2137092f2 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1660,6 +1660,7 @@ TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion +TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only TP_RAW_PIXELSHIFTSTDDEVFACTOR;StdDev factor TP_RAW_PIXELSHIFTEPERISO;e per ISO TP_RAW_PIXELSHIFTNREADISO;nRead diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index e2a59c314..215506811 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -36,20 +36,27 @@ namespace { -float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor, float eperIso, float nreadIso, float prnu) +float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between to green samples - float gDiff = std::fabs(a - b); if(adaptive) { + float gDiff = a - b; + gDiff *= eperIso; + gDiff *= gDiff; float avg = (a + b) / 2.f; - avg *= scale; // revert the colour scaling avg *= eperIso; prnu *= avg; - float stddev = stddevFactor * sqrtf(avg + nreadIso * nreadIso + prnu * prnu); - gDiff *= scale; - gDiff *= eperIso; - return gDiff - stddev; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = gDiff - stddev; + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / std::max(a, b) + 0.01f; + } else { + return 0.f; + } } else { + float gDiff = std::fabs(a - b); // add a small epsilon to avoid division by zero float maxVal = std::max(a, b) + 0.01f; return gDiff / maxVal; @@ -61,7 +68,7 @@ float greenDiff(float a, float b, bool adaptive, float scale, float stddevFactor using namespace std; using namespace rtengine; -void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu) { BENCHFUN @@ -86,14 +93,18 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b const float scaleGreen = 1.f / scale_mul[1]; eperIso *= (100.f / idata->getISOSpeed()); + eperIso *= scaleGreen; prnu /= 100.f; - + stddevFactor *= stddevFactor; + nreadIso *= nreadIso; + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); // For shades of green motion indicators const float blendFactor = (motion == 0.f ? 1.f : 1.f / (1.f - motionThreshold)); +// bool showOnlyMask = showMotion; // bool checkRedBlue = (gridSize == 5); // bool checkRedBlue = false; unsigned int offsX = 0, offsY = 0; @@ -141,39 +152,39 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); - greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); - greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); } } @@ -191,22 +202,22 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b float gridMax; if(gridSize == 1) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu); + gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion); skipNext = !showMotion; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, scaleGreen, stddevFactor, eperIso, nreadIso, prnu) + greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), + greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } @@ -226,12 +237,16 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b } if (gridMax > thresh - korr) { - float blend = (gridMax - thresh + korr) * blendFactor; // at least one of the tested pixels of the grid is detected as motion if(showMotion) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + float blend = (gridMax - thresh + korr) * blendFactor; + if(!showOnlyMask) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } } if(skipNext) { @@ -241,6 +256,9 @@ void RawImageSource::pixelshift_simple(int winx, int winy, int winw, int winh, b } // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; + } else if(showOnlyMask) { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + continue; } } diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 24469cead..72cbf781f 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -472,6 +472,7 @@ enum ProcEvent { EvOBPCompens = 442, EvRawImageNum = 443, EvDemosaicPixelshiftMotion = 444, + EvDemosaicPixelshiftMotionMaskOnly = 445, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 5da9f05fd..01f3f665b 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -889,10 +889,11 @@ void RAWParams::setDefaults() bayersensor.pixelshiftMotionCorrection = 3; bayersensor.pixelShiftStddevFactor = 5.0; bayersensor.pixelShiftEperIso = 0.75; - bayersensor.pixelShiftNreadIso = 5.0; + bayersensor.pixelShiftNreadIso = 3.45; bayersensor.pixelShiftPrnu = 1.0; bayersensor.pixelshiftShowMotion = false; - bayersensor.pixelShiftAutomatic = false; + bayersensor.pixelshiftShowMotionMaskOnly = false; + bayersensor.pixelShiftAutomatic = true; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3400,6 +3401,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); } + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly) { + keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotionMaskOnly", raw.bayersensor.pixelshiftShowMotionMaskOnly ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftAutomatic) { keyFile.set_boolean ("RAW Bayer", "pixelShiftAutomatic", raw.bayersensor.pixelShiftAutomatic ); } @@ -7518,6 +7523,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotionMaskOnly")) { + raw.bayersensor.pixelshiftShowMotionMaskOnly = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotionMaskOnly"); + + if (pedited) { + pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftAutomatic")) { raw.bayersensor.pixelShiftAutomatic = keyFile.get_boolean("RAW Bayer", "pixelShiftAutomatic"); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 5bd3e4646..80c0af080 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1189,6 +1189,7 @@ public: double pixelShiftNreadIso; double pixelShiftPrnu; bool pixelshiftShowMotion; + bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; bool dcb_enhance; //bool all_enhance; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index d3e6b1881..a838b5a0a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1966,7 +1966,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift_simple(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu); + pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 75037710b..3529a28ec 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -263,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift_simple(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 643c9e075..4452ae623 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -471,7 +471,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens DARKFRAME, // EvRawImageNum - DEMOSAIC // EvDemosaicPixelshiftMotion + DEMOSAIC, // EvDemosaicPixelshiftMotion + DEMOSAIC // EvDemosaicPixelshiftMotionMaskOnly }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 63705e472..0878403cf 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -86,6 +86,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); pixelShiftOptions->pack_start(*pixelShiftShowMotion); + pixelShiftShowMotionMaskOnly = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); + pixelShiftOptions->pack_start(*pixelShiftShowMotionMaskOnly); + pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); @@ -180,6 +183,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); + pixelShiftShowMotionMaskOnlyconn = pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true); pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -209,6 +213,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); + pixelShiftShowMotionMaskOnly->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); @@ -232,6 +237,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); + pixelShiftShowMotionMaskOnly->set_active(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); @@ -296,6 +302,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); + pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); int currentRow = method->get_active_row_number(); @@ -324,6 +331,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); + pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); } } @@ -490,12 +498,32 @@ void BayerProcess::pixelShiftShowMotionChanged () lastDCBen = pixelShiftShowMotion->get_active (); } - + pixelShiftShowMotionMaskOnly->set_sensitive(pixelShiftShowMotion->get_active ()); if (listener) { listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftShowMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } +void BayerProcess::pixelShiftShowMotionMaskOnlyChanged () +{ + if (batchMode) { + if (pixelShiftShowMotionMaskOnly->get_inconsistent()) { + pixelShiftShowMotionMaskOnly->set_inconsistent (false); + pixelShiftShowMotionMaskOnlyconn.block (true); + pixelShiftShowMotionMaskOnly->set_active (false); + pixelShiftShowMotionMaskOnlyconn.block (false); + } else if (lastDCBen) { + pixelShiftShowMotionMaskOnly->set_inconsistent (true); + } + + lastDCBen = pixelShiftShowMotionMaskOnly->get_active (); + } + + if (listener) { + listener->panelChanged (EvDemosaicPixelshiftMotionMaskOnly, pixelShiftShowMotionMaskOnly->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + void BayerProcess::pixelShiftAutomaticChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index b490f8c42..f13a0fb66 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -45,6 +45,7 @@ protected: Adjuster* pixelShiftMotion; Adjuster* pixelShiftMotionCorrection; Gtk::CheckButton* pixelShiftShowMotion; + Gtk::CheckButton* pixelShiftShowMotionMaskOnly; Gtk::CheckButton* pixelShiftAutomatic; Adjuster* pixelShiftStddevFactor; Adjuster* pixelShiftEperIso; @@ -53,7 +54,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftAutomaticconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn; //,allEnhconn; public: BayerProcess (); @@ -68,6 +69,7 @@ public: void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); void pixelShiftShowMotionChanged(); + void pixelShiftShowMotionMaskOnlyChanged(); void pixelShiftAutomaticChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 87dab2311..3859ca1e6 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -377,6 +377,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftNreadIso = v; raw.bayersensor.pixelShiftPrnu = v; raw.bayersensor.pixelshiftShowMotion = v; + raw.bayersensor.pixelshiftShowMotionMaskOnly = v; raw.bayersensor.pixelShiftAutomatic = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; @@ -881,6 +882,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; raw.bayersensor.pixelShiftPrnu = raw.bayersensor.pixelShiftPrnu && p.raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu; raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; + raw.bayersensor.pixelshiftShowMotionMaskOnly = raw.bayersensor.pixelshiftShowMotionMaskOnly && p.raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly; raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; @@ -2322,6 +2324,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; } + if (raw.bayersensor.pixelshiftShowMotionMaskOnly) { + toEdit.raw.bayersensor.pixelshiftShowMotionMaskOnly = mods.raw.bayersensor.pixelshiftShowMotionMaskOnly; + } + if (raw.bayersensor.pixelShiftAutomatic) { toEdit.raw.bayersensor.pixelShiftAutomatic = mods.raw.bayersensor.pixelShiftAutomatic; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index c116350a8..ae1fae218 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -699,6 +699,7 @@ public: bool pixelShiftNreadIso; bool pixelShiftPrnu; bool pixelshiftShowMotion; + bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; //bool allEnhance; bool greenEq; From 9bcb347a3b30517980c7396e4871ba9f65450b05 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 25 Nov 2016 18:55:11 +0100 Subject: [PATCH 034/181] pixelshift: Fixed a bug in gui --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 215506811..5c40d2019 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -256,7 +256,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; - } else if(showOnlyMask) { + } else if(showMotion && showOnlyMask) { greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; continue; } From ac5bad2de1eb7eedc044c288c81da0b1585a49b4 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 27 Nov 2016 18:42:58 +0100 Subject: [PATCH 035/181] pixelshift: optional red/blue check --- rtdata/languages/default | 1 + rtengine/dfmanager.cc | 6 +- rtengine/ffmanager.cc | 6 +- rtengine/pixelshift.cc | 190 ++++++++++++++++++++++++++----------- rtengine/procparams.cc | 14 ++- rtengine/procparams.h | 1 + rtengine/rawimage.cc | 7 +- rtengine/rawimage.h | 2 +- rtengine/rawimagesource.cc | 68 +++---------- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 30 ++++++ rtgui/bayerprocess.h | 4 +- rtgui/paramsedited.cc | 6 ++ rtgui/paramsedited.h | 2 + 14 files changed, 218 insertions(+), 121 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2137092f2..5fe1a7f30 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1655,6 +1655,7 @@ TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection +TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc index ab76ff854..4ef2c3358 100644 --- a/rtengine/dfmanager.cc +++ b/rtengine/dfmanager.cc @@ -144,7 +144,7 @@ void dfInfo::updateRawImage() } else { int H = ri->get_height(); int W = ri->get_width(); - ri->compress_image(); + ri->compress_image(0); int rSize = W * ((ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) ? 1 : 3); acc_t **acc = new acc_t*[H]; @@ -164,7 +164,7 @@ void dfInfo::updateRawImage() RawImage* temp = new RawImage(*iName); if( !temp->loadRaw(true)) { - temp->compress_image(); //\ TODO would be better working on original, because is temporary + temp->compress_image(0); //\ TODO would be better working on original, because is temporary nFiles++; if( ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS ) { @@ -204,7 +204,7 @@ void dfInfo::updateRawImage() delete ri; ri = nullptr; } else { - ri->compress_image(); + ri->compress_image(0); } } } diff --git a/rtengine/ffmanager.cc b/rtengine/ffmanager.cc index 7dd9140a1..87ae98905 100644 --- a/rtengine/ffmanager.cc +++ b/rtengine/ffmanager.cc @@ -136,7 +136,7 @@ void ffInfo::updateRawImage() } else { int H = ri->get_height(); int W = ri->get_width(); - ri->compress_image(); + ri->compress_image(0); int rSize = W * ((ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) ? 1 : 3); acc_t **acc = new acc_t*[H]; @@ -156,7 +156,7 @@ void ffInfo::updateRawImage() RawImage* temp = new RawImage(*iName); if( !temp->loadRaw(true)) { - temp->compress_image(); //\ TODO would be better working on original, because is temporary + temp->compress_image(0); //\ TODO would be better working on original, because is temporary nFiles++; if( ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS ) { @@ -195,7 +195,7 @@ void ffInfo::updateRawImage() delete ri; ri = nullptr; } else { - ri->compress_image(); + ri->compress_image(0); } } diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 5c40d2019..df183a5e3 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -36,7 +36,7 @@ namespace { -float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between to green samples if(adaptive) { @@ -68,7 +68,7 @@ float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperI using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal) { BENCHFUN @@ -93,7 +93,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det const float scaleGreen = 1.f / scale_mul[1]; eperIso *= (100.f / idata->getISOSpeed()); - eperIso *= scaleGreen; + + float eperIsoGreen = eperIso * scaleGreen; prnu /= 100.f; stddevFactor *= stddevFactor; @@ -104,9 +105,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // For shades of green motion indicators const float blendFactor = (motion == 0.f ? 1.f : 1.f / (1.f - motionThreshold)); -// bool showOnlyMask = showMotion; -// bool checkRedBlue = (gridSize == 5); -// bool checkRedBlue = false; + bool checkNonGreen = true; unsigned int offsX = 0, offsY = 0; // We have to adjust the offsets for the selected subframe we use for areas with motion switch (frame) { @@ -128,6 +127,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det offsY = 0; } + const float thresh = adaptive ? 0.f : motionThreshold; + #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif @@ -138,10 +139,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float *nonGreenDest1 = blue[i + offsY]; int j = winx + border - offsX; int c = FC(i, j); + float scaleNonGreen0 = 1.f / scale_mul[0]; + float scaleNonGreen2 = 1.f / scale_mul[2]; + float eperIsoNonGreen0 = eperIso / scale_mul[0]; + float eperIsoNonGreen2 = eperIso / scale_mul[2]; if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels std::swap(nonGreenDest0, nonGreenDest1); + std::swap(scaleNonGreen0, scaleNonGreen2); + std::swap(eperIsoNonGreen0, eperIsoNonGreen2); } // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop @@ -152,39 +159,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[2] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[2] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[3] = max(greenDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[3] = max(colourDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); } } @@ -193,6 +200,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 int lastIndex = gridSize - 1; + float korr = 0.f; for(; j < winw - (border + offsX); ++j) { offset ^= 1; // 0 => 1 or 1 => 0 @@ -202,22 +210,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax; if(gridSize == 1) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion); + gridMax = colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion); skipNext = !showMotion; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion), - greenDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, stddevFactor, eperIso, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } @@ -226,14 +234,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det lastIndex = lastIndex == gridSize ? 0 : lastIndex; // increase motion detection dependent on brightness - float korr; - float thresh; - if(adaptive) { - korr = 0.f; - thresh = 0; - } else { + if(!adaptive) { korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; - thresh = motionThreshold; } if (gridMax > thresh - korr) { @@ -260,6 +262,86 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; continue; } + + if(adaptive && checkNonGreenHorizontal) { + float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; + float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; + float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; + float diff0 = ng0 - ng1; + float diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { +// float val = std::abs(diff0) > std::abs(diff2) ? ng0 : ng2; + float val = (ng0 + ng2) / 2.f; + float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + if(!showOnlyMask) { + // if showMotion is enabled make the pixel green + nonGreenDest0[j + offsX] = 1000.f + 25000.f; + nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; + } + } + continue; + } + } + ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; + ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; + ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; + diff0 = ng0 - ng1; + diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { +// float val = std::abs(diff0) > std::abs(diff2) ? ng0 : ng2; + float val = (ng0 + ng2) / 2.f; + float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + if(!showOnlyMask) { + nonGreenDest1[j + offsX] = 1000.f + 25000.f; + nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; + } + } + continue; + } + } +// float ngDiff = nonGreenDiff(ng0,ng1,ng2); +// float korr = log2Lut[((int)(riFrames[(offset << 1) + offset]->data[i][j + offset] * scaleNonGreen0))>>1]; +// if(ngDiff > 0.5f - korr) { +// if(showMotion) { +// if(!showOnlyMask) { +// // if showMotion is enabled make the pixel green +// nonGreenDest0[j + offsX] = 1000.f + 25000.f; +// nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; +// } else { +// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; +// } +// } +// continue; +// } +// +// ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; +// ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; +// ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; +// ngDiff = nonGreenDiff(ng0,ng1,ng2); +// korr = log2Lut[((int)(riFrames[2 - offset]->data[i + 1][j - offset + 1] * scaleNonGreen2))>>1]; +// if(ngDiff > 0.5f - korr) { +// if(showMotion) { +// if(!showOnlyMask) { +// // if showMotion is enabled make the pixel green +// nonGreenDest0[j + offsX] = 1000.f + 25000.f; +// nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; +// } else { +// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; +// } +// } +// continue; +// } + } } // if(false && detectMotion && checkRedBlue) { @@ -270,9 +352,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // float diff2 = ng1 - ng2; // float gridMax; // if(diff0 * diff2 > 0.f) { -//// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { -// gridMax = greenDiff(ng1, std::max(ng0, ng2)); -//// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); +//// if(colourDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { +// gridMax = colourDiff(ng1, std::max(ng0, ng2)); +//// gridMax = colourDiff(ng1, ((ng0 + ng2) / 2.f)); // if(gridMax > motionThreshold ) { // float factor = 1.f / (1.f - motionThreshold); // float blend = (gridMax - motionThreshold) * factor; @@ -309,9 +391,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // diff0 = ng1 - ng0; // diff2 = ng1 - ng2; // if(signbit(diff0) == signbit(diff2)) { -//// if(greenDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { -// gridMax = greenDiff(ng1, std::max(ng0, ng2)); -//// gridMax = greenDiff(ng1, ((ng0 + ng2) / 2.f)); +//// if(colourDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { +// gridMax = colourDiff(ng1, std::max(ng0, ng2)); +//// gridMax = colourDiff(ng1, ((ng0 + ng2) / 2.f)); // if(gridMax > motionThreshold ) { // float factor = 1.f / (1.f - motionThreshold); // float blend = (gridMax - motionThreshold) * factor; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 01f3f665b..b8c475be4 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -885,7 +885,7 @@ void RAWParams::setDefaults() bayersensor.dcb_enhance = true; //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; - bayersensor.pixelshiftMotion = 70; + bayersensor.pixelshiftMotion = 0; bayersensor.pixelshiftMotionCorrection = 3; bayersensor.pixelShiftStddevFactor = 5.0; bayersensor.pixelShiftEperIso = 0.75; @@ -894,6 +894,7 @@ void RAWParams::setDefaults() bayersensor.pixelshiftShowMotion = false; bayersensor.pixelshiftShowMotionMaskOnly = false; bayersensor.pixelShiftAutomatic = true; + bayersensor.pixelShiftNonGreenHorizontal = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3409,6 +3410,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftAutomatic", raw.bayersensor.pixelShiftAutomatic ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenHorizontal) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenHorizontal", raw.bayersensor.pixelShiftNonGreenHorizontal ); + } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { @@ -7539,6 +7544,13 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenHorizontal")) { + raw.bayersensor.pixelShiftNonGreenHorizontal = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenHorizontal"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = true; + } + } //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 80c0af080..20fda6c88 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1191,6 +1191,7 @@ public: bool pixelshiftShowMotion; bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; + bool pixelShiftNonGreenHorizontal; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index f4a412ca5..a9ff42de5 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -659,7 +659,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro return 0; } -float** RawImage::compress_image() +float** RawImage::compress_image(int frameNum) { if( !image ) { return nullptr; @@ -667,11 +667,12 @@ float** RawImage::compress_image() if (isBayer() || isXtrans()) { if (!allocation) { - allocation = new float[height * width]; + // shift the beginning of all frames but the first by 32 floats to avoid cache miss conflicts on CPUs which have <= 4-way associative L1-Cache + allocation = new float[height * width + frameNum * 32]; data = new float*[height]; for (int i = 0; i < height; i++) { - data[i] = allocation + i * width; + data[i] = allocation + i * width + frameNum * 32; } } } else if (colors == 1) { diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index cb9e9100e..4644f7dca 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -119,7 +119,7 @@ public: { return image; } - float** compress_image(); // revert to compressed pixels format and release image data + float** compress_image(int frameNum); // revert to compressed pixels format and release image data float** data; // holds pixel values, data[i][j] corresponds to the ith row and jth column unsigned prefilters; // original filters saved ( used for 4 color processing ) unsigned int getFrameCount() const { return is_raw; } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index a838b5a0a..44b1221fa 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -37,7 +37,7 @@ #include #endif #include "opthelper.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" #define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) #undef CLIPD @@ -1521,7 +1521,7 @@ StopWatch Stop1("decode"); { int errCodeThr = 0; #ifdef _OPENMP -#pragma omp for +#pragma omp for nowait #endif for(unsigned int i = 0; i < numFrames; ++i) { if(i == 0) { @@ -1531,7 +1531,7 @@ StopWatch Stop1("decode"); riFrames[i] = new RawImage(fname); errCodeThr = riFrames[i]->loadRaw (true, i); } - riFrames[i]->compress_image(); + riFrames[i]->compress_image(i); } #ifdef _OPENMP #pragma omp critical @@ -1966,7 +1966,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu); + pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.bayersensor.pixelShiftNonGreenHorizontal); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); @@ -3520,64 +3520,24 @@ void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const R void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw) { - chmax[0] = chmax[1] = chmax[2] = chmax[3] = 0; //channel maxima - float black_lev[4] = {0.f};//black level - - //adjust black level (eg Canon) - bool isMono = false; - - black_lev[0] = raw.bayersensor.black1; //R - black_lev[1] = raw.bayersensor.black0; //G1 - black_lev[2] = raw.bayersensor.black2; //B - black_lev[3] = raw.bayersensor.black3; //G2 - - for(int i = 0; i < 4 ; i++) { - cblacksom[i] = max( c_black[i] + black_lev[i], 0.0f ); // adjust black level - } - - initialGain = calculate_scale_mul(scale_mul, ref_pre_mul, c_white, cblacksom, isMono, ri->get_colors()); // recalculate scale colors with adjusted levels - - //fprintf(stderr, "recalc: %f [%f %f %f %f]\n", initialGain, scale_mul[0], scale_mul[1], scale_mul[2], scale_mul[3]); - for(int i = 0; i < 4 ; i++) { - clmax[i] = (c_white[i] - cblacksom[i]) * scale_mul[i]; // raw clip level - } - - // this seems strange, but it works + BENCHFUN // scale image colors #ifdef _OPENMP - #pragma omp parallel + #pragma omp parallel for schedule(dynamic,64) collapse(2) #endif + for(int frame = 0; frame < 4; ++frame) { + for (int row = winy; row < winy + winh; row ++) { - float tmpchmax[3]; - tmpchmax[0] = tmpchmax[1] = tmpchmax[2] = 0.0f; -#ifdef _OPENMP - #pragma omp for nowait -#endif - - for (int row = winy; row < winy + winh; row ++) - { - for (int col = winx; col < winx + winw; col++) { - int c = FC(row,col); - for(int frame = 0; frame < 4; ++frame) { - float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; - tmpchmax[c] = max(tmpchmax[c], val); - riFrames[frame]->data[row][col] = val; - } - } - } - -#ifdef _OPENMP - #pragma omp critical -#endif - { - chmax[0] = max(tmpchmax[0], chmax[0]); - chmax[1] = max(tmpchmax[1], chmax[1]); - chmax[2] = max(tmpchmax[2], chmax[2]); + for (int col = winx; col < winx + winw; col++) { + int c = FC(row,col); + float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; + riFrames[frame]->data[row][col] = val; } } - pixelShiftColoursScaled = true; + } + pixelShiftColoursScaled = true; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 3529a28ec..3bf44ad8c 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -263,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 0878403cf..6e9f34549 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -92,6 +92,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); + pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); pixelShiftMotion->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); @@ -185,6 +188,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); pixelShiftShowMotionMaskOnlyconn = pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true); pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); + pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -215,6 +219,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); pixelShiftShowMotionMaskOnly->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); @@ -239,6 +244,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); pixelShiftShowMotionMaskOnly->set_active(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); @@ -304,6 +310,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -333,6 +340,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); } } @@ -543,6 +551,7 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftNreadIso->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftStddevFactor->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -550,6 +559,27 @@ void BayerProcess::pixelShiftAutomaticChanged () } +void BayerProcess::pixelShiftNonGreenHorizontalChanged () +{ + if (batchMode) { + if (pixelShiftNonGreenHorizontal->get_inconsistent()) { + pixelShiftNonGreenHorizontal->set_inconsistent (false); + pixelShiftNonGreenHorizontalconn.block (true); + pixelShiftNonGreenHorizontal->set_active (false); + pixelShiftNonGreenHorizontalconn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenHorizontal->set_inconsistent (true); + } + + lastDCBen = pixelShiftNonGreenHorizontal->get_active (); + } + + if (listener) { + listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftNonGreenHorizontal->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + /*void BayerProcess::allEnhanceChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index f13a0fb66..de1c99cdf 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -47,6 +47,7 @@ protected: Gtk::CheckButton* pixelShiftShowMotion; Gtk::CheckButton* pixelShiftShowMotionMaskOnly; Gtk::CheckButton* pixelShiftAutomatic; + Gtk::CheckButton* pixelShiftNonGreenHorizontal; Adjuster* pixelShiftStddevFactor; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; @@ -54,7 +55,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn; //,allEnhconn; public: BayerProcess (); @@ -71,6 +72,7 @@ public: void pixelShiftShowMotionChanged(); void pixelShiftShowMotionMaskOnlyChanged(); void pixelShiftAutomaticChanged(); + void pixelShiftNonGreenHorizontalChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 3859ca1e6..ad3e11d69 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -379,6 +379,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelshiftShowMotion = v; raw.bayersensor.pixelshiftShowMotionMaskOnly = v; raw.bayersensor.pixelShiftAutomatic = v; + raw.bayersensor.pixelShiftNonGreenHorizontal = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -884,6 +885,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; raw.bayersensor.pixelshiftShowMotionMaskOnly = raw.bayersensor.pixelshiftShowMotionMaskOnly && p.raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly; raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; + raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2332,6 +2334,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftAutomatic = mods.raw.bayersensor.pixelShiftAutomatic; } + if (raw.bayersensor.pixelShiftNonGreenHorizontal) { + toEdit.raw.bayersensor.pixelShiftNonGreenHorizontal = mods.raw.bayersensor.pixelShiftNonGreenHorizontal; + } + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index ae1fae218..305c99880 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -701,6 +701,8 @@ public: bool pixelshiftShowMotion; bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; + bool pixelShiftNonGreenHorizontal; + //bool allEnhance; bool greenEq; bool linenoise; From b9fb8cf29294b7711db96097a6982c2c19e30f4f Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 27 Nov 2016 19:12:03 +0100 Subject: [PATCH 036/181] pixelshift: Correction for mask-only mode when red/blue detection is enabled --- rtengine/pixelshift.cc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index df183a5e3..186dc8827 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -258,10 +258,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; - } else if(showMotion && showOnlyMask) { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - continue; } +// } else if(showMotion && showOnlyMask) { +// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; +// continue; +// } if(adaptive && checkNonGreenHorizontal) { float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; @@ -278,10 +279,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float blend = gridMax * blendFactor; if(!showOnlyMask) { // if showMotion is enabled make the pixel green - nonGreenDest0[j + offsX] = 1000.f + 25000.f; + nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } continue; @@ -300,15 +301,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(showMotion) { float blend = gridMax * blendFactor; if(!showOnlyMask) { - nonGreenDest1[j + offsX] = 1000.f + 25000.f; + nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } continue; } } + // float ngDiff = nonGreenDiff(ng0,ng1,ng2); // float korr = log2Lut[((int)(riFrames[(offset << 1) + offset]->data[i][j + offset] * scaleNonGreen0))>>1]; // if(ngDiff > 0.5f - korr) { @@ -422,6 +424,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // } // } // } + + if(showMotion && showOnlyMask) { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + continue; + } + // motion correction disabled or no motion detected => combine the values from the four pixelshift frames greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; From 13bd6752f6652f40523f75bc66d7a40092b9ecf5 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 27 Nov 2016 20:07:02 +0100 Subject: [PATCH 037/181] pixelshift: Fixed a crash caused by division by zero when Show motion was enabled --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 186dc8827..5483afdf3 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -51,7 +51,7 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper if(!showMotion) { return result; } else if(result > 0.f) { // for the motion mask - return std::fabs(a - b) / std::max(a, b) + 0.01f; + return std::fabs(a - b) / (std::max(a, b) + 0.01f); } else { return 0.f; } From 8afb267fe9e3f566889b456c67395ff3824d21aa Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 27 Nov 2016 21:55:27 +0100 Subject: [PATCH 038/181] pixelshift: changed behaviour of correction amount, now 0 is 1x1 for green, 1 is 1x2 for green, no changes for other values --- rtengine/pixelshift.cc | 7 ++++--- rtgui/bayerprocess.cc | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 5483afdf3..16757057d 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -80,6 +80,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %f\tnread %f\tprnu %f\n",gridSize, adaptive, stddevFactor, eperIso, nreadIso, prnu); + const bool skip = (gridSize != 1 ? false : true); gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); @@ -157,7 +158,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float greenDifMax[gridSize]; // motion detection checks the grid around the pixel for differences in green channels if(detectMotion || adaptive) { - if(gridSize == 3) { + if(gridSize < 2) { // compute maximum of differences for first two columns of 3x3 grid greenDifMax[0] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), colourDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), @@ -208,10 +209,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { bool skipNext = false; float gridMax; - if(gridSize == 1) { + if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps gridMax = colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion); - skipNext = !showMotion; + skipNext = skip && !showMotion ; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex greenDifMax[lastIndex] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 6e9f34549..db34ba8f9 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -105,7 +105,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotion->show(); pixelShiftOptions->pack_start(*pixelShiftMotion); - pixelShiftMotionCorrection = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION"), 1, 5, 2, 3)); + pixelShiftMotionCorrection = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION"), 0, 5, 1, 3)); pixelShiftMotionCorrection->setAdjusterListener (this); pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); From 313e770cfe19cf189c13b5b465466391633ba863 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 28 Nov 2016 15:21:45 +0100 Subject: [PATCH 039/181] pixelshift: optional vertical red/blue check --- rtdata/languages/default | 1 + rtengine/pixelshift.cc | 171 +++++++++++-------------------------- rtengine/procparams.cc | 14 +++ rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 28 ++++++ rtgui/bayerprocess.h | 4 +- rtgui/paramsedited.cc | 6 ++ rtgui/paramsedited.h | 1 + 10 files changed, 108 insertions(+), 122 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 5fe1a7f30..f15ee1a38 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1656,6 +1656,7 @@ TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal +TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 16757057d..09a1cb8d0 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -68,7 +68,7 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal, bool checkNonGreenVertical) { BENCHFUN @@ -260,10 +260,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; } -// } else if(showMotion && showOnlyMask) { -// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; -// continue; -// } if(adaptive && checkNonGreenHorizontal) { float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; @@ -272,14 +268,13 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diff0 = ng0 - ng1; float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { -// float val = std::abs(diff0) > std::abs(diff2) ? ng0 : ng2; float val = (ng0 + ng2) / 2.f; float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; if(!showOnlyMask) { - // if showMotion is enabled make the pixel green + // if showMotion is enabled colourize the pixel nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; } else { @@ -289,19 +284,20 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det continue; } } + ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; diff0 = ng0 - ng1; diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { -// float val = std::abs(diff0) > std::abs(diff2) ? ng0 : ng2; float val = (ng0 + ng2) / 2.f; float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; } else { @@ -311,121 +307,58 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det continue; } } + } + if(adaptive && checkNonGreenVertical) { + float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; + float ng0 = riFrames[((offset << 1) + offset)^1]->data[i][j + offset]; + float ng2 = riFrames[((offset << 1) + offset)^1]->data[i+2][j + offset]; -// float ngDiff = nonGreenDiff(ng0,ng1,ng2); -// float korr = log2Lut[((int)(riFrames[(offset << 1) + offset]->data[i][j + offset] * scaleNonGreen0))>>1]; -// if(ngDiff > 0.5f - korr) { -// if(showMotion) { -// if(!showOnlyMask) { -// // if showMotion is enabled make the pixel green -// nonGreenDest0[j + offsX] = 1000.f + 25000.f; -// nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; -// } else { -// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; -// } -// } -// continue; -// } -// -// ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; -// ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; -// ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; -// ngDiff = nonGreenDiff(ng0,ng1,ng2); -// korr = log2Lut[((int)(riFrames[2 - offset]->data[i + 1][j - offset + 1] * scaleNonGreen2))>>1]; -// if(ngDiff > 0.5f - korr) { -// if(showMotion) { -// if(!showOnlyMask) { -// // if showMotion is enabled make the pixel green -// nonGreenDest0[j + offsX] = 1000.f + 25000.f; -// nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; -// } else { -// greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f; -// } -// } -// continue; -// } + float diff0 = ng0 - ng1; + float diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { + float val = (ng0 + ng2) / 2.f; + float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } + } + continue; + } + } + + ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; + ng0 = riFrames[3 - ((offset<<1) + offset)]->data[i - 1][j - offset + 1]; + ng2 = riFrames[3 - ((offset<<1) + offset)]->data[i + 1][j - offset + 1]; + + diff0 = ng0 - ng1; + diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { + float val = (ng0 + ng2) / 2.f; + float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } + } + continue; + } + } } } -// if(false && detectMotion && checkRedBlue) { -// float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; -// float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; -// float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; -// float diff0 = ng1 - ng0; -// float diff2 = ng1 - ng2; -// float gridMax; -// if(diff0 * diff2 > 0.f) { -//// if(colourDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { -// gridMax = colourDiff(ng1, std::max(ng0, ng2)); -//// gridMax = colourDiff(ng1, ((ng0 + ng2) / 2.f)); -// if(gridMax > motionThreshold ) { -// float factor = 1.f / (1.f - motionThreshold); -// float blend = (gridMax - motionThreshold) * factor; -// if(showMotion) { -// // if showMotion is enabled make the pixel green -// greenDest[j + offsX] = nonGreenDest1[j + offsX] = 0.f; -// nonGreenDest0[j + offsX] = 20000.f; -// continue; -//// greenDest[j + offsX+1] = nonGreenDest1[j + offsX+1] = 0.f; -//// nonGreenDest0[j + offsX+1] = 20000.f; -// } -// greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; -// -//// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); -//// nonGreenDest0[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; -// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); -//// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); -// nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; -// -//// nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); -// -//// if(skipNext) { -//// // treat the horizontally next pixel also as motion -//// j++; -//// offset ^= 1; -//// } -// // do not set the motion pixel values. They have already been set by demosaicer or showMotion -// continue; -// } -// } -// ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; -// ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; -// ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; -// diff0 = ng1 - ng0; -// diff2 = ng1 - ng2; -// if(signbit(diff0) == signbit(diff2)) { -//// if(colourDiff(ng1, fabsf(diff0) < fabsf(diff2) ? ng0 : ng2) > motionThreshold ) { -// gridMax = colourDiff(ng1, std::max(ng0, ng2)); -//// gridMax = colourDiff(ng1, ((ng0 + ng2) / 2.f)); -// if(gridMax > motionThreshold ) { -// float factor = 1.f / (1.f - motionThreshold); -// float blend = (gridMax - motionThreshold) * factor; -// if(showMotion) { -// // if showMotion is enabled make the pixel green -// greenDest[j + offsX] = nonGreenDest0[j + offsX] = 0.f; -// nonGreenDest1[j + offsX] = 20000.f; -//// greenDest[j + offsX+1] = nonGreenDest0[j + offsX+1] = 0.f; -//// nonGreenDest1[j + offsX+1] = 20000.f; -//continue; -// } -// greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; -//// greenDest[j + offsX] = intp(blend, greenDest[j + offsX],(riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f); -//// nonGreenDest0[j + offsX] = intp(blend, nonGreenDest0[j + offsX], riFrames[(offset << 1) + offset]->data[i][j + offset]); -// nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; -// -//// nonGreenDest1[j + offsX] = (ng1 + (diff0 < diff2 ? ng0 : ng2)) / 2.f; -// nonGreenDest1[j + offsX] = intp(blend, nonGreenDest1[j + offsX], riFrames[2 - offset]->data[i + 1][j - offset + 1]); -//// if(skipNext) { -//// // treat the horizontally next pixel also as motion -//// j++; -//// offset ^= 1; -//// } -// // do not set the motion pixel values. They have already been set by demosaicer or showMotion -// continue; -// } -// } -// } - if(showMotion && showOnlyMask) { greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; continue; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index b8c475be4..3671b2c71 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -895,6 +895,7 @@ void RAWParams::setDefaults() bayersensor.pixelshiftShowMotionMaskOnly = false; bayersensor.pixelShiftAutomatic = true; bayersensor.pixelShiftNonGreenHorizontal = false; + bayersensor.pixelShiftNonGreenVertical = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3414,6 +3415,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenHorizontal", raw.bayersensor.pixelShiftNonGreenHorizontal ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenVertical) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenVertical", raw.bayersensor.pixelShiftNonGreenVertical ); + } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { @@ -7551,6 +7556,15 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = true; } } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenVertical")) { + raw.bayersensor.pixelShiftNonGreenVertical = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenVertical"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenVertical = true; + } + } + //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 20fda6c88..747afc250 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1192,6 +1192,7 @@ public: bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; + bool pixelShiftNonGreenVertical; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 44b1221fa..35a24085e 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1966,7 +1966,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 3bf44ad8c..ce0e75f49 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -263,7 +263,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal, bool checkNonGreenVertical); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index db34ba8f9..87900cead 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -95,6 +95,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenVertical); + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); pixelShiftMotion->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); @@ -189,6 +192,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftShowMotionMaskOnlyconn = pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true); pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); + pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -220,6 +224,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftShowMotionMaskOnly->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); @@ -245,6 +250,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftShowMotionMaskOnly->set_active(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); @@ -311,6 +317,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -341,6 +348,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); } } @@ -552,6 +560,7 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftStddevFactor->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -579,6 +588,25 @@ void BayerProcess::pixelShiftNonGreenHorizontalChanged () } } +void BayerProcess::pixelShiftNonGreenVerticalChanged () +{ + if (batchMode) { + if (pixelShiftNonGreenVertical->get_inconsistent()) { + pixelShiftNonGreenVertical->set_inconsistent (false); + pixelShiftNonGreenVerticalconn.block (true); + pixelShiftNonGreenVertical->set_active (false); + pixelShiftNonGreenVerticalconn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenVertical->set_inconsistent (true); + } + + lastDCBen = pixelShiftNonGreenVertical->get_active (); + } + + if (listener) { + listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftNonGreenVertical->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} /*void BayerProcess::allEnhanceChanged () { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index de1c99cdf..68dab3f95 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -48,6 +48,7 @@ protected: Gtk::CheckButton* pixelShiftShowMotionMaskOnly; Gtk::CheckButton* pixelShiftAutomatic; Gtk::CheckButton* pixelShiftNonGreenHorizontal; + Gtk::CheckButton* pixelShiftNonGreenVertical; Adjuster* pixelShiftStddevFactor; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; @@ -55,7 +56,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn; //,allEnhconn; public: BayerProcess (); @@ -73,6 +74,7 @@ public: void pixelShiftShowMotionMaskOnlyChanged(); void pixelShiftAutomaticChanged(); void pixelShiftNonGreenHorizontalChanged(); + void pixelShiftNonGreenVerticalChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ad3e11d69..d7d101f0a 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -380,6 +380,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelshiftShowMotionMaskOnly = v; raw.bayersensor.pixelShiftAutomatic = v; raw.bayersensor.pixelShiftNonGreenHorizontal = v; + raw.bayersensor.pixelShiftNonGreenVertical = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -886,6 +887,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelshiftShowMotionMaskOnly = raw.bayersensor.pixelshiftShowMotionMaskOnly && p.raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly; raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; + raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2338,6 +2340,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftNonGreenHorizontal = mods.raw.bayersensor.pixelShiftNonGreenHorizontal; } + if (raw.bayersensor.pixelShiftNonGreenVertical) { + toEdit.raw.bayersensor.pixelShiftNonGreenVertical = mods.raw.bayersensor.pixelShiftNonGreenVertical; + } + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 305c99880..9fe0d6b9b 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -702,6 +702,7 @@ public: bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; + bool pixelShiftNonGreenVertical; //bool allEnhance; bool greenEq; From 6496e34e4bcbb30a3f78df95a473f7d7e8bee9a3 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 28 Nov 2016 21:01:26 +0100 Subject: [PATCH 040/181] pixelshift: switch subframe without need to reload the image; enabled CA-correction --- rtengine/CA_correct_RT.cc | 2 +- rtengine/pixelshift.cc | 102 ++++++++++++++++++------------------- rtengine/rawimagesource.cc | 42 ++++++++++++--- rtengine/rawimagesource.h | 7 +-- 4 files changed, 91 insertions(+), 62 deletions(-) diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc index 7c28801ae..97450a61a 100644 --- a/rtengine/CA_correct_RT.cc +++ b/rtengine/CA_correct_RT.cc @@ -112,7 +112,7 @@ bool LinEqSolve(int nDim, double* pfMatr, double* pfVect, double* pfSolution) using namespace std; using namespace rtengine; -void RawImageSource::CA_correct_RT(const double cared, const double cablue, const double caautostrength) +void RawImageSource::CA_correct_RT(const double cared, const double cablue, const double caautostrength, array2D &rawData) { // multithreaded and partly vectorized by Ingo Weyrich constexpr int ts = 128; diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 09a1cb8d0..abc5e2e0b 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -160,39 +160,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize < 2) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j-2], riFrames[3 - offset]->data[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 1][j-2], riFrames[3 - offset]->data[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 3][j-2], riFrames[3 - offset]->data[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset - 1][j-2], riFrames[2 + offset]->data[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset + 1][j-2], riFrames[2 + offset]->data[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j-2], (*rawDataFrames[3 - offset])[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j-2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j-2], (*rawDataFrames[3 - offset])[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j-2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j-2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff(riFrames[0 + offset]->data[i + offset-2][j - 1], riFrames[2 + offset]->data[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset][j - 1], riFrames[2 + offset]->data[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset+2][j - 1], riFrames[2 + offset]->data[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset][j - 1], riFrames[3 - offset]->data[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 2][j - 1], riFrames[3 - offset]->data[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[2] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j], riFrames[3 - offset]->data[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 3][j], riFrames[3 - offset]->data[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset - 1][j], riFrames[2 + offset]->data[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset + 1][j], riFrames[2 + offset]->data[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); - greenDifMax[3] = max(colourDiff(riFrames[0 + offset]->data[i + offset-2][j + 1], riFrames[2 + offset]->data[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset+2][j + 1], riFrames[2 + offset]->data[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 2][j +- 1], riFrames[3 - offset]->data[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j +- 1], (*rawDataFrames[3 - offset])[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); } } @@ -211,22 +211,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax; if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = colourDiff(riFrames[1 - offset]->data[i - offset + 1][j], riFrames[3 - offset]->data[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion); + gridMax = colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion); skipNext = skip && !showMotion ; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff(riFrames[0 + offset]->data[i + offset][j + 1], riFrames[2 + offset]->data[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset][j + 1], riFrames[3 - offset]->data[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 2][j + 1], riFrames[3 - offset]->data[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff(riFrames[1 - offset]->data[i - offset - 1][j+2], riFrames[3 - offset]->data[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 1][j+2], riFrames[3 - offset]->data[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[1 - offset]->data[i - offset + 3][j+2], riFrames[3 - offset]->data[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset - 1][j+2], riFrames[2 + offset]->data[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff(riFrames[0 + offset]->data[i + offset + 1][j+2], riFrames[2 + offset]->data[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j+2], (*rawDataFrames[3 - offset])[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j+2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j+2], (*rawDataFrames[3 - offset])[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j+2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j+2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } @@ -236,7 +236,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // increase motion detection dependent on brightness if(!adaptive) { - korr = log2Lut[((int)(riFrames[1 - offset]->data[i - offset + 1][j] * scaleGreen))>>1]; + korr = log2Lut[((int)((*rawDataFrames)[1 - offset][i - offset + 1][j] * scaleGreen))>>1]; } if (gridMax > thresh - korr) { @@ -262,9 +262,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } if(adaptive && checkNonGreenHorizontal) { - float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; - float ng0 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)+1]; - float ng2 = riFrames[((offset^1) << 1) + (offset^1)]->data[i][j + (offset^1)-1]; + float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ng0 = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1)+1]; + float ng2 = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1)-1]; float diff0 = ng0 - ng1; float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { @@ -285,9 +285,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } - ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; - ng0 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1) + 2]; - ng2 = riFrames[2 - (offset^1)]->data[i + 1][j - (offset^1)]; + ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ng0 = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; + ng2 = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; diff0 = ng0 - ng1; diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { @@ -309,9 +309,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } if(adaptive && checkNonGreenVertical) { - float ng1 = riFrames[(offset << 1) + offset]->data[i][j + offset]; - float ng0 = riFrames[((offset << 1) + offset)^1]->data[i][j + offset]; - float ng2 = riFrames[((offset << 1) + offset)^1]->data[i+2][j + offset]; + float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ng0 = (*rawDataFrames[((offset << 1) + offset)^1])[i][j + offset]; + float ng2 = (*rawDataFrames[((offset << 1) + offset)^1])[i+2][j + offset]; float diff0 = ng0 - ng1; float diff2 = ng2 - ng1; @@ -333,9 +333,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } - ng1 = riFrames[2 - offset]->data[i + 1][j - offset + 1]; - ng0 = riFrames[3 - ((offset<<1) + offset)]->data[i - 1][j - offset + 1]; - ng2 = riFrames[3 - ((offset<<1) + offset)]->data[i + 1][j - offset + 1]; + ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ng0 = (*rawDataFrames[3 - ((offset<<1) + offset)])[i - 1][j - offset + 1]; + ng2 = (*rawDataFrames[3 - ((offset<<1) + offset)])[i + 1][j - offset + 1]; diff0 = ng0 - ng1; diff2 = ng2 - ng1; @@ -365,9 +365,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } // motion correction disabled or no motion detected => combine the values from the four pixelshift frames - greenDest[j + offsX] = (riFrames[1 - offset]->data[i - offset + 1][j] + riFrames[3 - offset]->data[i + offset][j + 1]) / 2.f; - nonGreenDest0[j + offsX] = riFrames[(offset << 1) + offset]->data[i][j + offset]; - nonGreenDest1[j + offsX] = riFrames[2 - offset]->data[i + 1][j - offset + 1]; + greenDest[j + offsX] = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; + nonGreenDest0[j + offsX] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + nonGreenDest1[j + offsX] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; } } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 35a24085e..7e725f92a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -479,6 +479,10 @@ RawImageSource::~RawImageSource () delete riFrames[i]; } + for(size_t i = 0; i < numFrames - 1; ++i) { + delete rawDataBuffer[i]; + } + flushRGB(); flushRawData(); @@ -1758,7 +1762,24 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le printf( "Flat Field Correction:%s\n", rif->get_filename().c_str()); } - copyOriginalPixels(raw, ri, rid, rif, rawData); + if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + int bufferNumber = 0; + for(int i=0; i<4; ++i) { + if(i==currFrame) { + copyOriginalPixels(raw, ri, rid, rif, rawData); + rawDataFrames[i] = &rawData; + } else { + if(!rawDataBuffer[bufferNumber]) { + rawDataBuffer[bufferNumber] = new array2D; + } + rawDataFrames[i] = rawDataBuffer[bufferNumber]; + ++bufferNumber; + copyOriginalPixels(raw, riFrames[i], rid, rif, *rawDataFrames[i]); + } + } + } else { + copyOriginalPixels(raw, ri, rid, rif, rawData); + } //FLATFIELD end @@ -1798,10 +1819,12 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } } - - scaleColors( 0, 0, W, H, raw, rawData); //+ + raw parameters for black level(raw.blackxx) - if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] && !pixelShiftColoursScaled) { - scaleColors_pixelshift( 0, 0, W, H, raw); + if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + for(int i=0; i<4; ++i) { + scaleColors( 0, 0, W, H, raw, *rawDataFrames[i]); + } + } else { + scaleColors( 0, 0, W, H, raw, rawData); //+ + raw parameters for black level(raw.blackxx) } // Correct vignetting of lens profile @@ -1916,8 +1939,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgressStr ("CA Auto Correction..."); plistener->setProgress (0.0); } - - CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength); + if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + for(int i=0; i<4; ++i) { + CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); + } + } else { + CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength, rawData); + } } if ( raw.expos != 1 ) { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index ce0e75f49..445b531d6 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -71,7 +71,7 @@ protected: bool rgbSourceModified; RawImage* ri; // Copy of raw pixels, NOT corrected for initial gain, blackpoint etc. - RawImage* riFrames[16] = {nullptr}; + RawImage* riFrames[4] = {nullptr}; unsigned int currFrame = 0; unsigned int numFrames = 0; @@ -81,7 +81,8 @@ protected: int threshold; array2D rawData; // holds preprocessed pixel values, rowData[i][j] corresponds to the ith row and jth column - array2D *rawDataFrames[16] = {nullptr}; + array2D *rawDataFrames[4] = {nullptr}; + array2D *rawDataBuffer[3] = {nullptr}; // the interpolated green plane: array2D green; @@ -218,7 +219,7 @@ protected: inline void interpolate_row_rb (float* ar, float* ab, float* pg, float* cg, float* ng, int i); inline void interpolate_row_rb_mul_pp (float* ar, float* ab, float* pg, float* cg, float* ng, int i, float r_mul, float g_mul, float b_mul, int x1, int width, int skip); - void CA_correct_RT (const double cared, const double cablue, const double caautostrength); + void CA_correct_RT (const double cared, const double cablue, const double caautostrength, array2D &rawData); void ddct8x8s(int isgn, float a[8][8]); void processRawWhitepoint (float expos, float preser); // exposure before interpolation From 5231b442c70673439e5e4e4dbc3d0f6cc650df62 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 28 Nov 2016 21:51:51 +0100 Subject: [PATCH 041/181] pixelshift: fix a crash in non-adaptive mode --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index abc5e2e0b..0abdd5524 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -236,7 +236,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // increase motion detection dependent on brightness if(!adaptive) { - korr = log2Lut[((int)((*rawDataFrames)[1 - offset][i - offset + 1][j] * scaleGreen))>>1]; + korr = log2Lut[((int)((*rawDataFrames[1 - offset])[i - offset + 1][j] * scaleGreen))>>1]; } if (gridMax > thresh - korr) { From 2b09ad6ecc65f21482a57a215a04befbe4b114c4 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 28 Nov 2016 23:40:04 +0100 Subject: [PATCH 042/181] pixelshift: fixed another crash --- rtengine/rawimagesource.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 7e725f92a..3ad0d2ed2 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1762,7 +1762,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le printf( "Flat Field Correction:%s\n", rif->get_filename().c_str()); } - if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + if(numFrames == 4) { int bufferNumber = 0; for(int i=0; i<4; ++i) { if(i==currFrame) { @@ -1819,7 +1819,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } } - if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + if(numFrames == 4) { for(int i=0; i<4; ++i) { scaleColors( 0, 0, W, H, raw, *rawDataFrames[i]); } From 389d2ebdddec2a03f7277153ada6f0be3dece593 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 29 Nov 2016 15:22:33 +0100 Subject: [PATCH 043/181] pixelshift: Added support for .badpixels file and raw white point correction --- rtengine/expo_before_b.cc | 2 +- rtengine/pixelshift.cc | 4 ++-- rtengine/rawimagesource.cc | 48 ++++++++++++++------------------------ rtengine/rawimagesource.h | 9 +++---- 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/rtengine/expo_before_b.cc b/rtengine/expo_before_b.cc index 0854647c5..98fed04f7 100644 --- a/rtengine/expo_before_b.cc +++ b/rtengine/expo_before_b.cc @@ -44,7 +44,7 @@ namespace rtengine extern const Settings* settings; -void RawImageSource::processRawWhitepoint(float expos, float preser) +void RawImageSource::processRawWhitepoint(float expos, float preser, array2D &rawData) { MyTime t1e, t2e; diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 0abdd5524..a8fbd7bcb 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -68,7 +68,7 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal, bool checkNonGreenVertical) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) { BENCHFUN @@ -93,7 +93,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } const float scaleGreen = 1.f / scale_mul[1]; - eperIso *= (100.f / idata->getISOSpeed()); + eperIso *= (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 3ad0d2ed2..deda1fbc8 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -916,7 +916,7 @@ void RawImageSource::convertColorSpace(Imagefloat* image, const ColorManagementP /* interpolateBadPixelsBayer: correct raw pixels looking at the bitmap * takes into consideration if there are multiple bad pixels in the neighbourhood */ -int RawImageSource::interpolateBadPixelsBayer( PixelsMap &bitmapBads ) +int RawImageSource::interpolateBadPixelsBayer( PixelsMap &bitmapBads, array2D &rawData ) { static const float eps = 1.f; int counter = 0; @@ -1509,7 +1509,6 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) plistener->setProgressStr ("Decoding..."); plistener->setProgress (0.0); } -StopWatch Stop1("decode"); ri = new RawImage(fname); int errCode = ri->loadRaw (false, 0, false); @@ -1548,12 +1547,11 @@ StopWatch Stop1("decode"); if(errCode) { return errCode; } -Stop1.stop(); + if(numFrames > 1 ) { // this disables multi frame support for Fuji S5 until I found a solution to handle different dimensions if(riFrames[0]->get_width() != riFrames[1]->get_width() || riFrames[0]->get_height() != riFrames[1]->get_height()) { numFrames = 1; } - pixelShiftColoursScaled = false; } if (plistener) { @@ -1667,7 +1665,7 @@ Stop1.stop(); initialGain = 1.0 / min(pre_mul[0], pre_mul[1], pre_mul[2]); }*/ - for(unsigned int i=0;i < numFrames; ++i) { + for(unsigned int i = 0;i < numFrames; ++i) { riFrames[i]->set_prefilters(); } @@ -1918,7 +1916,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if( totBP ) if ( ri->getSensorType() == ST_BAYER ) { - interpolateBadPixelsBayer( *bitmapBads ); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + interpolateBadPixelsBayer( *bitmapBads, *rawDataFrames[i] ); + } + } else { + interpolateBadPixelsBayer( *bitmapBads, rawData ); + } } else if ( ri->getSensorType() == ST_FUJI_XTRANS ) { interpolateBadPixelsXtrans( *bitmapBads ); } else { @@ -1949,7 +1953,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } if ( raw.expos != 1 ) { - processRawWhitepoint(raw.expos, raw.preser); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + processRawWhitepoint(raw.expos, raw.preser, *rawDataFrames[i]); + } + } else { + processRawWhitepoint(raw.expos, raw.preser, rawData); + } } if(prepareDenoise && dirpyrdenoiseExpComp == INFINITY) { @@ -1994,7 +2004,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); + pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); @@ -3546,28 +3556,6 @@ void RawImageSource::scaleColors(int winx, int winy, int winw, int winh, const R } -void RawImageSource::scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw) -{ - BENCHFUN - - // scale image colors - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,64) collapse(2) -#endif - for(int frame = 0; frame < 4; ++frame) { - for (int row = winy; row < winy + winh; row ++) - { - for (int col = winx; col < winx + winw; col++) { - int c = FC(row,col); - float val = (riFrames[frame]->data[row][col] - cblacksom[c]) * scale_mul[c]; - riFrames[frame]->data[row][col] = val; - } - } - } - pixelShiftColoursScaled = true; -} - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int RawImageSource::defTransform (int tran) diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 445b531d6..2c389a6d9 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -133,8 +133,6 @@ public: void copyOriginalPixels(const RAWParams &raw, RawImage *ri, RawImage *riDark, RawImage *riFlatFile, array2D &rawData ); void cfaboxblur (RawImage *riFlatFile, float* cfablur, int boxH, int boxW); void scaleColors (int winx, int winy, int winw, int winh, const RAWParams &raw, array2D &rawData); // raw for cblack - void scaleColors_pixelshift(int winx, int winy, int winw, int winh, const RAWParams &raw); - void getImage (const ColorTemp &ctemp, int tran, Imagefloat* image, const PreviewProps &pp, const ToneCurveParams &hrp, const ColorManagementParams &cmp, const RAWParams &raw); eSensorType getSensorType () const @@ -207,7 +205,6 @@ public: ri = riFrames[currFrame]; } protected: - bool pixelShiftColoursScaled = false; typedef unsigned short ushort; void processFalseColorCorrection (Imagefloat* i, const int steps); inline void convert_row_to_YIQ (const float* const r, const float* const g, const float* const b, float* Y, float* I, float* Q, const int W); @@ -221,9 +218,9 @@ protected: void CA_correct_RT (const double cared, const double cablue, const double caautostrength, array2D &rawData); void ddct8x8s(int isgn, float a[8][8]); - void processRawWhitepoint (float expos, float preser); // exposure before interpolation + void processRawWhitepoint (float expos, float preser, array2D &rawData); // exposure before interpolation - int interpolateBadPixelsBayer( PixelsMap &bitmapBads ); + int interpolateBadPixelsBayer( PixelsMap &bitmapBads, array2D &rawData ); int interpolateBadPixelsNColours( PixelsMap &bitmapBads, const int colours ); int interpolateBadPixelsXtrans( PixelsMap &bitmapBads ); int findHotDeadPixels( PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels ); @@ -264,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool checkNonGreenHorizontal, bool checkNonGreenVertical); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); void hflip (Imagefloat* im); void vflip (Imagefloat* im); From 675b2cc7fc6eeddc0d08c2dd12acc97f7f562605 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 29 Nov 2016 17:01:14 +0100 Subject: [PATCH 044/181] pixelshift: Added support for lcp vignetting correction and green equilibration --- rtengine/green_equil_RT.cc | 17 ++++++++--------- rtengine/rawimagesource.cc | 35 +++++++++++++++++++++++++++++------ rtengine/rawimagesource.h | 2 +- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc index a9183509d..8b1136359 100644 --- a/rtengine/green_equil_RT.cc +++ b/rtengine/green_equil_RT.cc @@ -30,12 +30,11 @@ #include "rt_math.h" #include "rawimagesource.h" - namespace rtengine { //void green_equilibrate()//for dcraw implementation -void RawImageSource::green_equilibrate(float thresh) +void RawImageSource::green_equilibrate(float thresh, array2D &rawData) { // thresh = threshold for performing green equilibration; max percentage difference of G1 vs G2 // G1-G2 differences larger than this will be assumed to be Nyquist texture, and left untouched @@ -79,7 +78,7 @@ void RawImageSource::green_equilibrate(float thresh) */ //now smooth the cfa data #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic,16) #endif for (int rr = 4; rr < height - 4; rr++) @@ -99,8 +98,8 @@ void RawImageSource::green_equilibrate(float thresh) float d1 = (o1_1 + o1_2 + o1_3 + o1_4) * 0.25f; float d2 = (o2_1 + o2_2 + o2_3 + o2_4) * 0.25f; - float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.0; - float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.0; + float c1 = (fabs(o1_1 - o1_2) + fabs(o1_1 - o1_3) + fabs(o1_1 - o1_4) + fabs(o1_2 - o1_3) + fabs(o1_3 - o1_4) + fabs(o1_2 - o1_4)) / 6.f; + float c2 = (fabs(o2_1 - o2_2) + fabs(o2_1 - o2_3) + fabs(o2_1 - o2_4) + fabs(o2_2 - o2_3) + fabs(o2_3 - o2_4) + fabs(o2_2 - o2_4)) / 6.f; //%%%%%%%%%%%%%%%%%%%%%% //vote1=(checker[rr-2][cc]+checker[rr][cc-2]+checker[rr][cc+2]+checker[rr+2][cc]); @@ -111,10 +110,10 @@ void RawImageSource::green_equilibrate(float thresh) //pixel interpolation float gin = cfa[rr][cc]; - float gse = (cfa[rr + 1][cc + 1]) + 0.5 * (cfa[rr][cc] - cfa[rr + 2][cc + 2]); - float gnw = (cfa[rr - 1][cc - 1]) + 0.5 * (cfa[rr][cc] - cfa[rr - 2][cc - 2]); - float gne = (cfa[rr - 1][cc + 1]) + 0.5 * (cfa[rr][cc] - cfa[rr - 2][cc + 2]); - float gsw = (cfa[rr + 1][cc - 1]) + 0.5 * (cfa[rr][cc] - cfa[rr + 2][cc - 2]); + float gse = (cfa[rr + 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc + 2]); + float gnw = (cfa[rr - 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc - 2]); + float gne = (cfa[rr - 1][cc + 1]) + 0.5f * (cfa[rr][cc] - cfa[rr - 2][cc + 2]); + float gsw = (cfa[rr + 1][cc - 1]) + 0.5f * (cfa[rr][cc] - cfa[rr + 2][cc - 2]); diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index deda1fbc8..ca3090b50 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1832,14 +1832,31 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); + if(numFrames == 4) { #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for #endif - for (int y = 0; y < H; y++) { - for (int x = 0; x < W; x++) { - if (rawData[y][x] > 0) { - rawData[y][x] *= map.calcVignetteFac(x, y); + for (int y = 0; y < H; y++) { + for (int x = 0; x < W; x++) { + float val = map.calcVignetteFac(x, y); + for(int i = 0; i < 4; ++i) { + if ((*rawDataFrames[i])[y][x] > 0) { + (*rawDataFrames[i])[y][x] *= val; + } + } + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H; y++) { + for (int x = 0; x < W; x++) { + if (rawData[y][x] > 0) { + rawData[y][x] *= map.calcVignetteFac(x, y); + } } } } @@ -1910,7 +1927,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgress (0.0); } - green_equilibrate(0.01 * (raw.bayersensor.greenthresh)); + if(numFrames == 4) { + for(int i = 0; i < 4; ++i) { + green_equilibrate(0.01 * (raw.bayersensor.greenthresh), *rawDataFrames[i]); + } + } else { + green_equilibrate(0.01 * (raw.bayersensor.greenthresh), rawData); + } } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 2c389a6d9..9045f0a6c 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -227,7 +227,7 @@ protected: void cfa_linedn (float linenoiselevel);//Emil's line denoise - void green_equilibrate (float greenthresh);//Emil's green equilibration + void green_equilibrate (float greenthresh, array2D &rawData);//Emil's green equilibration void nodemosaic(bool bw); void eahd_demosaic(); From 1f365425588985453f24a4d2556e15d25bd277fa Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 30 Nov 2016 01:17:50 +0100 Subject: [PATCH 045/181] pixelshift Diversified green and non green motion detection --- rtengine/pixelshift.cc | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index a8fbd7bcb..491ae882b 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -63,6 +63,34 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper } } +float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +{ + // calculate the difference between to green samples + if(adaptive) { + float gDiff = a - b; + gDiff *= eperIso; + gDiff *= gDiff; + float avg = (a + b) / 2.f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = gDiff - stddev; + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / (std::max(a, b) + 0.01f); + } else { + return 0.f; + } + } else { + float gDiff = std::fabs(a - b); + // add a small epsilon to avoid division by zero + float maxVal = std::max(a, b) + 0.01f; + return gDiff / maxVal; + } +} + + } using namespace std; @@ -269,7 +297,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -292,7 +320,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -317,7 +345,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -341,7 +369,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = colourDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; From 4ba6e7d5f7e6e6990ecf34ab2a909a47c3ef8ef5 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 1 Dec 2016 19:41:38 +0100 Subject: [PATCH 046/181] pixelshift: model specific default values. Caution: you need to press reset buttons for pixelshift adjusters of existing profiles once! --- rtdata/languages/default | 4 +- rtengine/pixelshift.cc | 210 +++++++++++++++++++++++++++++-------- rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 4 +- 5 files changed, 170 insertions(+), 52 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index f15ee1a38..a9359b839 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1645,12 +1645,12 @@ TP_RAW_DCBITERATIONS;Number of DCB iterations TP_RAW_DMETHOD;Method TP_RAW_DMETHOD_PROGRESSBAR;%1 demosaicing... TP_RAW_DMETHOD_PROGRESSBAR_REFINE;Demosaicing refinement... -TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to aid in noise reduction without leading to maze patterns, posterization or a washed-out look. +TP_RAW_DMETHOD_TOOLTIP;Note: IGV and LMMSE are dedicated to high ISO images to aid in noise reduction without leading to maze patterns, posterization or a washed-out look.\nPixelshift is for Pentax pixelshift files. It falls back to Amaze for non pixelshift files. TP_RAW_FALSECOLOR;False color suppression steps TP_RAW_HD;Threshold TP_RAW_HD_TOOLTIP;Lower values make hot/dead pixel detection more aggressive, but false positives may lead to artifacts. If you notice any artifacts appearing when enabling the Hot/Dead Pixel Filters, gradually increase the threshold value until they disappear. TP_RAW_IMAGENUM;Sub-image -TP_RAW_IMAGENUM;Some raw files might embed several sub-images (HDR, Pixel-Shift, Dual Sensitivity). Use this button to select the sub-image.\n\nThe last sub-image will be used if you select a value beyond the real sub-image count. +TP_RAW_IMAGENUM_TOOLTIP;Some raw files might embed several sub-images (HDR, Pixel-Shift, Dual Sensitivity). Use this button to select the sub-image.\n\nThe last sub-image will be used if you select a value beyond the real sub-image count. TP_RAW_LABEL;Demosaicing TP_RAW_LMMSEITERATIONS;LMMSE enhancement steps TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (steps 5-6) to reduce artifacts and improve the signal-to-noise ratio. diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 491ae882b..c5254a5dc 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -38,7 +38,7 @@ namespace float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { - // calculate the difference between to green samples + // calculate the difference between two green samples if(adaptive) { float gDiff = a - b; gDiff *= eperIso; @@ -65,7 +65,7 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { - // calculate the difference between to green samples + // calculate the difference between two nongreen samples if(adaptive) { float gDiff = a - b; gDiff *= eperIso; @@ -96,18 +96,117 @@ float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float ep using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) { BENCHFUN + + static const float nReadK3II[] = { 3.4f, // ISO 100 + 3.1f, // ISO 125 + 2.5f, // ISO 160 + 2.5f, // ISO 200 + 2.5f, // ISO 250 + 2.5f, // ISO 320 + 2.3f, // ISO 400 + 2.5f, // ISO 500 + 2.3f, // ISO 640 + 2.3f, // ISO 800 + 2.4f, // ISO 1000 + 2.3f, // ISO 1250 + 1.75f, // ISO 1600 + 1.75f, // ISO 2000 + 1.75f, // ISO 2500 + 1.75f, // ISO 3200 + 1.75f, // ISO 4000 + 1.75f, // ISO 5000 + 1.75f, // ISO 6400 + 1.75f, // ISO 8000 + 1.75f, // ISO 10000 + 1.5f, // ISO 12800 + 1.5f, // ISO 16000 + 1.5f, // ISO 20000 + 1.5f, // ISO 25600 + 1.5f, // ISO 32000 + 1.5f, // ISO 40000 + 1.5f, // ISO 51200 + 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK3II = 0.35f; + + static const float nReadK1[] = { 3.45f, // ISO 100 + 3.15f, // ISO 125 + 3.45f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 2.7f, // ISO 400 + 2.7f, // ISO 500 + 2.7f, // ISO 640 + 2.5f, // ISO 800 + 2.5f, // ISO 1000 + 2.5f, // ISO 1250 + 2.4f, // ISO 1600 + 2.4f, // ISO 2000 + 2.4f, // ISO 2500 + 2.4f, // ISO 3200 + 2.4f, // ISO 4000 + 2.4f, // ISO 5000 + 2.4f, // ISO 6400 + 2.4f, // ISO 8000 + 2.4f, // ISO 10000 + 2.4f, // ISO 12800 + 2.4f, // ISO 16000 + 2.4f, // ISO 20000 + 2.4f, // ISO 25600 + 2.4f, // ISO 32000 + 2.4f, // ISO 40000 + 2.4f, // ISO 51200 + 2.4f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK1 = 0.75f; + + static const float nReadK70[] = { 3.0f, // ISO 100 + 3.0f, // ISO 125 + 3.0f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 3.0f, // ISO 400 + 3.0f, // ISO 500 + 3.0f, // ISO 640 + 3.0f, // ISO 800 + 3.0f, // ISO 1000 + 3.0f, // ISO 1250 + 3.0f, // ISO 1600 + 3.0f, // ISO 2000 + 3.0f, // ISO 2500 + 3.0f, // ISO 3200 + 3.0f, // ISO 4000 + 3.0f, // ISO 5000 + 3.0f, // ISO 6400 + 3.0f, // ISO 8000 + 3.0f, // ISO 10000 + 3.0f, // ISO 12800 + 3.0f, // ISO 16000 + 3.0f, // ISO 20000 + 3.0f, // ISO 25600 + 3.0f, // ISO 32000 + 3.0f, // ISO 40000 + 3.0f, // ISO 51200 + 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK70 = 0.5f; + if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); plistener->setProgress(0.0); } - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %f\tnread %f\tprnu %f\n",gridSize, adaptive, stddevFactor, eperIso, nreadIso, prnu); const bool skip = (gridSize != 1 ? false : true); gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode @@ -121,18 +220,36 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } const float scaleGreen = 1.f / scale_mul[1]; - eperIso *= (100.f / (rawWpCorrection * idata->getISOSpeed())); + float nRead; + float eperIsoModel; + if(model.find("K-3") != string::npos) { + nRead = nReadK3II[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + eperIsoModel = ePerIsoK3II; + } else if(model.find("K-1") != string::npos) { + nRead = nReadK1[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + eperIsoModel = ePerIsoK1; + } else { + nRead = nReadK70[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + eperIsoModel = ePerIsoK70; + } + nRead *= pow(2.f, nreadIso); + eperIsoModel *= pow(2.f, eperIso); + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n",gridSize, adaptive, stddevFactor, eperIso, nRead, prnu); + prnu /= 100.f; stddevFactor *= stddevFactor; - nreadIso *= nreadIso; + + + nRead *= nRead; // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); // For shades of green motion indicators - const float blendFactor = (motion == 0.f ? 1.f : 1.f / (1.f - motionThreshold)); + const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); bool checkNonGreen = true; unsigned int offsX = 0, offsY = 0; @@ -188,39 +305,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize < 2) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j-2], (*rawDataFrames[3 - offset])[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j-2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j-2], (*rawDataFrames[3 - offset])[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j-2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j-2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j-2], (*rawDataFrames[3 - offset])[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j-2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j-2], (*rawDataFrames[3 - offset])[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j-2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j-2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j +- 1], (*rawDataFrames[3 - offset])[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j +- 1], (*rawDataFrames[3 - offset])[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); } } @@ -239,22 +356,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax; if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion); + gridMax = colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion); skipNext = skip && !showMotion ; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j+2], (*rawDataFrames[3 - offset])[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j+2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j+2], (*rawDataFrames[3 - offset])[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j+2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j+2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nreadIso, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j+2], (*rawDataFrames[3 - offset])[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j+2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j+2], (*rawDataFrames[3 - offset])[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j+2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j+2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); } @@ -297,7 +414,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -320,7 +437,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -336,6 +453,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } } + if(adaptive && checkNonGreenVertical) { float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ng0 = (*rawDataFrames[((offset << 1) + offset)^1])[i][j + offset]; @@ -345,7 +463,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; @@ -369,7 +487,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diff2 = ng2 - ng1; if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nreadIso, prnu, showMotion); + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index ca3090b50..4199a3023 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2027,7 +2027,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); + pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 9045f0a6c..5cb98cd64 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 87900cead..0781b9b4b 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -130,7 +130,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftStddevFactor->show(); pixelShiftOptions->pack_start(*pixelShiftStddevFactor); - pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), 0.3, 1.0, 0.05, 0.75)); + pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -2.0, 2.0, 0.05, 0.0)); pixelShiftEperIso->setAdjusterListener (this); // pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); @@ -141,7 +141,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftEperIso->show(); pixelShiftOptions->pack_start(*pixelShiftEperIso); - pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), 1.0, 10.0, 0.05, 3.45)); + pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), -2.0, 2.0, 0.05, 0.0)); pixelShiftNreadIso->setAdjusterListener (this); // pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); From 98cd7af963d16219f8311d8624ea2f5b02517a75 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 1 Dec 2016 19:57:15 +0100 Subject: [PATCH 047/181] pixelshift: Set new default values for images without existing pp3 file --- rtengine/procparams.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 3671b2c71..8ca050cec 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -888,8 +888,8 @@ void RAWParams::setDefaults() bayersensor.pixelshiftMotion = 0; bayersensor.pixelshiftMotionCorrection = 3; bayersensor.pixelShiftStddevFactor = 5.0; - bayersensor.pixelShiftEperIso = 0.75; - bayersensor.pixelShiftNreadIso = 3.45; + bayersensor.pixelShiftEperIso = 0.0; + bayersensor.pixelShiftNreadIso = 0.0; bayersensor.pixelShiftPrnu = 1.0; bayersensor.pixelshiftShowMotion = false; bayersensor.pixelshiftShowMotionMaskOnly = false; From b1e7dcbf4ad605ee2c857dd1411023d82111ceaf Mon Sep 17 00:00:00 2001 From: Hombre Date: Fri, 2 Dec 2016 02:58:09 +0100 Subject: [PATCH 048/181] Updated GUI --- rtdata/languages/default | 11 ++++ rtengine/pixelshift.cc | 4 +- rtengine/procevents.h | 13 ++++- rtengine/procparams.cc | 35 +++++++++---- rtengine/procparams.h | 7 ++- rtengine/rawimagesource.cc | 4 +- rtengine/rawimagesource.h | 2 +- rtgui/bayerprocess.cc | 100 ++++++++++++++++++++++++------------- rtgui/bayerprocess.h | 5 +- rtgui/paramsedited.cc | 19 ++++--- rtgui/paramsedited.h | 4 +- 11 files changed, 137 insertions(+), 67 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index a9359b839..23a7d5d68 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -676,6 +676,17 @@ HISTORY_MSG_441;Retinex - Gain transmission HISTORY_MSG_442;Retinex - Scale HISTORY_MSG_443;Output Black Point Compensation HISTORY_MSG_444;Raw Sub-Image +HISTORY_MSG_445;EvPixelShiftMotion +HISTORY_MSG_446;EvPixelShiftMotionCorrection +HISTORY_MSG_447;EvPixelShiftStddevFactor +HISTORY_MSG_448;EvPixelShiftEperIso +HISTORY_MSG_449;EvPixelShiftNreadIso +HISTORY_MSG_450;EvPixelShiftPrnu +HISTORY_MSG_451;EvPixelshiftShowMotion +HISTORY_MSG_452;EvPixelshiftShowMotionMaskOnly +HISTORY_MSG_453;EvPixelShiftAutomatic +HISTORY_MSG_454;EvPixelShiftNonGreenHorizontal +HISTORY_MSG_455;EvPixelShiftNonGreenVertical HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index c5254a5dc..63b670ebc 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -96,7 +96,7 @@ float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float ep using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) { BENCHFUN @@ -207,6 +207,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } + int gridSize = (int)gridSize_; + const bool skip = (gridSize != 1 ? false : true); gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 72cbf781f..a82a2d028 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -471,8 +471,17 @@ enum ProcEvent { EvLskal = 441, EvOBPCompens = 442, EvRawImageNum = 443, - EvDemosaicPixelshiftMotion = 444, - EvDemosaicPixelshiftMotionMaskOnly = 445, + EvPixelShiftMotion = 444, + EvPixelShiftMotionCorrection = 445, + EvPixelShiftStddevFactor = 446, + EvPixelShiftEperIso = 447, + EvPixelShiftNreadIso = 448, + EvPixelShiftPrnu = 449, + EvPixelshiftShowMotion = 450, + EvPixelshiftShowMotionMaskOnly = 451, + EvPixelShiftAutomatic = 452, + EvPixelShiftNonGreenHorizontal = 453, + EvPixelShiftNonGreenVertical = 454, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 8ca050cec..57958a6f5 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -885,8 +885,8 @@ void RAWParams::setDefaults() bayersensor.dcb_enhance = true; //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; - bayersensor.pixelshiftMotion = 0; - bayersensor.pixelshiftMotionCorrection = 3; + bayersensor.pixelShiftMotion = 0; + bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid5x5; bayersensor.pixelShiftStddevFactor = 5.0; bayersensor.pixelShiftEperIso = 0.0; bayersensor.pixelShiftNreadIso = 0.0; @@ -3375,12 +3375,12 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "LMMSEIterations", raw.bayersensor.lmmse_iterations ); } - if (!pedited || pedited->raw.bayersensor.pixelshiftMotion) { - keyFile.set_integer ("RAW Bayer", "PixelShiftMotion", raw.bayersensor.pixelshiftMotion ); + if (!pedited || pedited->raw.bayersensor.pixelShiftMotion) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotion", raw.bayersensor.pixelShiftMotion ); } - if (!pedited || pedited->raw.bayersensor.pixelshiftMotionCorrection) { - keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelshiftMotionCorrection ); + if (!pedited || pedited->raw.bayersensor.pixelShiftMotionCorrection) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelShiftMotionCorrection ); } if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactor) { @@ -7478,18 +7478,18 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } if (keyFile.has_key ("RAW Bayer", "PixelShiftMotion")) { - raw.bayersensor.pixelshiftMotion = keyFile.get_integer("RAW Bayer", "PixelShiftMotion"); + raw.bayersensor.pixelShiftMotion = keyFile.get_integer("RAW Bayer", "PixelShiftMotion"); if (pedited) { - pedited->raw.bayersensor.pixelshiftMotion = true; + pedited->raw.bayersensor.pixelShiftMotion = true; } } if (keyFile.has_key ("RAW Bayer", "PixelShiftMotionCorrection")) { - raw.bayersensor.pixelshiftMotionCorrection = keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrection"); + raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrection"); if (pedited) { - pedited->raw.bayersensor.pixelshiftMotionCorrection = true; + pedited->raw.bayersensor.pixelShiftMotionCorrection = true; } } @@ -8002,8 +8002,21 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.twogreen == other.raw.bayersensor.twogreen && raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh && raw.bayersensor.linenoise == other.raw.bayersensor.linenoise - && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance && raw.bayersensor.dcb_iterations == other.raw.bayersensor.dcb_iterations + && raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations + && raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion + && raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection + && raw.bayersensor.pixelShiftStddevFactor == other.raw.bayersensor.pixelShiftStddevFactor + && raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso + && raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso + && raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu + && raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion + && raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly + && raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic + && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal + && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical + && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance + //&& raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method && raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps && raw.xtranssensor.blackred == other.raw.xtranssensor.blackred diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 747afc250..8d3dfc541 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1168,6 +1168,9 @@ public: enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, pixelshift_simple, numMethods }; // This MUST be the last enum + enum ePSMotionCorrection { + Grid1x1, Grid1x2, Grid3x3, Grid5x5 + }; static const char *methodstring[numMethods]; Glib::ustring method; @@ -1182,8 +1185,8 @@ public: int greenthresh; int dcb_iterations; int lmmse_iterations; - int pixelshiftMotion; - int pixelshiftMotionCorrection; + int pixelShiftMotion; + ePSMotionCorrection pixelShiftMotionCorrection; double pixelShiftStddevFactor; double pixelShiftEperIso; double pixelShiftNreadIso; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 4199a3023..6b97eb0b6 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2023,11 +2023,11 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { - if(raw.bayersensor.pixelshiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) { + if(raw.bayersensor.pixelShiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) { amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelshiftMotion > 0, raw.bayersensor.pixelshiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelshiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); + pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 5cb98cd64..a6877f6fd 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, unsigned int gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 0781b9b4b..2d2f2902b 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -108,16 +108,18 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotion->show(); pixelShiftOptions->pack_start(*pixelShiftMotion); - pixelShiftMotionCorrection = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION"), 0, 5, 1, 3)); - pixelShiftMotionCorrection->setAdjusterListener (this); + Gtk::HBox* hb2 = Gtk::manage (new Gtk::HBox ()); + hb2->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONCORRECTION") + ": ")), Gtk::PACK_SHRINK, 4); + pixelShiftMotionCorrection = Gtk::manage (new MyComboBoxText ()); + pixelShiftMotionCorrection->append_text("1x1"); + pixelShiftMotionCorrection->append_text("1x2"); + pixelShiftMotionCorrection->append_text("3x3"); + pixelShiftMotionCorrection->append_text("5x5"); + pixelShiftMotionCorrection->set_active(0); pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); - - if (pixelShiftMotionCorrection->delay < options.adjusterMaxDelay) { - pixelShiftMotionCorrection->delay = options.adjusterMaxDelay; - } - pixelShiftMotionCorrection->show(); - pixelShiftOptions->pack_start(*pixelShiftMotionCorrection); + hb2->pack_start(*pixelShiftMotionCorrection); + pixelShiftOptions->pack_start(*hb2); pixelShiftStddevFactor = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR"), 2, 8, 0.1, 5)); pixelShiftStddevFactor->setAdjusterListener (this); @@ -186,6 +188,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA //pack_start( *allOptions, Gtk::PACK_SHRINK, 4); methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) ); + psmcconn = pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) ); imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); @@ -203,6 +206,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params methodconn.block (true); dcbEnhconn.block (true); imagenumberconn.block (true); + psmcconn.block (true); //allEnhconn.block (true); method->set_active(procparams::RAWParams::BayerSensor::numMethods); @@ -227,8 +231,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); - pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); - pixelShiftMotionCorrection->setEditedState ( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); + pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftStddevFactor->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); @@ -238,7 +241,10 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name } if(!pedited->raw.bayersensor.imageNum) { - imageNumber->set_active(4); + imageNumber->set_active_text(M("GENERAL_UNCHANGED")); + } + if(!pedited->raw.bayersensor.pixelShiftMotionCorrection) { + pixelShiftMotionCorrection->set_active_text(M("GENERAL_UNCHANGED")); } } @@ -253,8 +259,8 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); - pixelShiftMotion->setValue (pp->raw.bayersensor.pixelshiftMotion); - pixelShiftMotionCorrection->setValue (pp->raw.bayersensor.pixelshiftMotionCorrection); + pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); + pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); pixelShiftStddevFactor->setValue (pp->raw.bayersensor.pixelShiftStddevFactor); pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); @@ -293,6 +299,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params //lastALLen = pp->raw.bayersensor.all_enhance; methodconn.block (false); + psmcconn.block (false); imagenumberconn.block (false); dcbEnhconn.block (false); //allEnhconn.block (false); @@ -307,8 +314,8 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.dcb_enhance = dcbEnhance->get_active(); //pp->raw.bayersensor.all_enhance = allEnhance->get_active(); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); - pp->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getIntValue(); - pp->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getIntValue(); + pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); + pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); pp->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getValue(); pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); @@ -333,13 +340,13 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe if (pedited) { pedited->raw.bayersensor.ccSteps = ccSteps->getEditedState (); pedited->raw.bayersensor.method = method->get_active_row_number() != procparams::RAWParams::BayerSensor::numMethods; - pedited->raw.bayersensor.imageNum = imageNumber->get_active_row_number() < 4; + pedited->raw.bayersensor.imageNum = imageNumber->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.dcbIterations = dcbIterations->getEditedState (); pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); - pedited->raw.bayersensor.pixelshiftMotion = pixelShiftMotion->getEditedState (); - pedited->raw.bayersensor.pixelshiftMotionCorrection = pixelShiftMotionCorrection->getEditedState (); + pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); + pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getEditedState (); pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); @@ -356,6 +363,8 @@ void BayerProcess::setBatchMode(bool batchMode) { method->append_text (M("GENERAL_UNCHANGED")); method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name + pixelShiftMotionCorrection->append_text (M("GENERAL_UNCHANGED")); + pixelShiftMotionCorrection->set_active (4); imageNumber->append_text (M("GENERAL_UNCHANGED")); imageNumber->set_active(4); dcbOptions->hide(); @@ -366,7 +375,6 @@ void BayerProcess::setBatchMode(bool batchMode) dcbIterations->showEditedCB (); lmmseIterations->showEditedCB (); pixelShiftMotion->showEditedCB (); - pixelShiftMotionCorrection->showEditedCB (); pixelShiftStddevFactor->showEditedCB (); pixelShiftEperIso->showEditedCB (); pixelShiftNreadIso->showEditedCB (); @@ -377,8 +385,7 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams { dcbIterations->setDefault( defParams->raw.bayersensor.dcb_iterations); lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); - pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelshiftMotion); - pixelShiftMotionCorrection->setDefault( defParams->raw.bayersensor.pixelshiftMotionCorrection); + pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelShiftMotion); pixelShiftStddevFactor->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactor); pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); @@ -388,8 +395,7 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams if (pedited) { dcbIterations->setDefaultEditedState( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); - pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotion ? Edited : UnEdited); - pixelShiftMotionCorrection->setDefaultEditedState( pedited->raw.bayersensor.pixelshiftMotionCorrection ? Edited : UnEdited); + pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftStddevFactor->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); @@ -399,7 +405,6 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); pixelShiftMotion->setDefaultEditedState( Irrelevant ); - pixelShiftMotionCorrection->setDefaultEditedState( Irrelevant ); pixelShiftStddevFactor->setDefaultEditedState( Irrelevant ); pixelShiftEperIso->setDefaultEditedState( Irrelevant ); pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); @@ -418,21 +423,44 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) } else if (a == lmmseIterations) { listener->panelChanged (EvDemosaicLMMSEIter, a->getTextValue() ); } else if (a == pixelShiftMotion) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); - } else if (a == pixelShiftMotionCorrection) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + listener->panelChanged (EvPixelShiftMotion, a->getTextValue() ); } else if (a == pixelShiftStddevFactor) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + listener->panelChanged (EvPixelShiftStddevFactor, a->getTextValue() ); } else if (a == pixelShiftEperIso) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + listener->panelChanged (EvPixelShiftEperIso, a->getTextValue() ); } else if (a == pixelShiftNreadIso) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + listener->panelChanged (EvPixelShiftNreadIso, a->getTextValue() ); } else if (a == pixelShiftPrnu) { - listener->panelChanged (EvDemosaicPixelshiftMotion, a->getTextValue() ); + listener->panelChanged (EvPixelShiftPrnu, a->getTextValue() ); } } } +void BayerProcess::psMotionCorrectionChanged () +{ + int curSelection = pixelShiftMotionCorrection->get_active_row_number(); + Glib::ustring sGrid; + switch (curSelection) { + case 0: + sGrid = "1x1"; + break; + case 1: + sGrid = "1x2"; + break; + case 2: + sGrid = "3x3"; + break; + case 3: + default: + sGrid = "5x5"; + break; + } + + if (listener) { + listener->panelChanged (EvPixelShiftMotionCorrection, sGrid); + } +} + void BayerProcess::methodChanged () { int curSelection = method->get_active_row_number(); @@ -516,7 +544,7 @@ void BayerProcess::pixelShiftShowMotionChanged () } pixelShiftShowMotionMaskOnly->set_sensitive(pixelShiftShowMotion->get_active ()); if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftShowMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPixelshiftShowMotion, pixelShiftShowMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } @@ -536,7 +564,7 @@ void BayerProcess::pixelShiftShowMotionMaskOnlyChanged () } if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotionMaskOnly, pixelShiftShowMotionMaskOnly->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPixelshiftShowMotionMaskOnly, pixelShiftShowMotionMaskOnly->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } @@ -563,7 +591,7 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } @@ -584,7 +612,7 @@ void BayerProcess::pixelShiftNonGreenHorizontalChanged () } if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftNonGreenHorizontal->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPixelShiftNonGreenHorizontal, pixelShiftNonGreenHorizontal->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } @@ -604,7 +632,7 @@ void BayerProcess::pixelShiftNonGreenVerticalChanged () } if (listener) { - listener->panelChanged (EvDemosaicPixelshiftMotion, pixelShiftNonGreenVertical->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPixelShiftNonGreenVertical, pixelShiftNonGreenVertical->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 68dab3f95..a54637f13 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -43,7 +43,7 @@ protected: Adjuster* lmmseIterations; Gtk::VBox *pixelShiftOptions; Adjuster* pixelShiftMotion; - Adjuster* pixelShiftMotionCorrection; + MyComboBoxText* pixelShiftMotionCorrection; Gtk::CheckButton* pixelShiftShowMotion; Gtk::CheckButton* pixelShiftShowMotionMaskOnly; Gtk::CheckButton* pixelShiftAutomatic; @@ -56,7 +56,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn; //,allEnhconn; public: BayerProcess (); @@ -67,6 +67,7 @@ public: void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); void methodChanged (); + void psMotionCorrectionChanged (); void imageNumberChanged (); void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index d7d101f0a..ac0257fa1 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -370,8 +370,8 @@ void ParamsEdited::set (bool v) raw.bayersensor.dcbEnhance = v; //raw.bayersensor.allEnhance = v; raw.bayersensor.lmmseIterations = v; - raw.bayersensor.pixelshiftMotion = v; - raw.bayersensor.pixelshiftMotionCorrection = v; + raw.bayersensor.pixelShiftMotion = v; + raw.bayersensor.pixelShiftMotionCorrection = v; raw.bayersensor.pixelShiftStddevFactor = v; raw.bayersensor.pixelShiftEperIso = v; raw.bayersensor.pixelShiftNreadIso = v; @@ -877,8 +877,8 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.dcbEnhance = raw.bayersensor.dcbEnhance && p.raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance; //raw.bayersensor.allEnhance = raw.bayersensor.allEnhance && p.raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance; raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; - raw.bayersensor.pixelshiftMotion = raw.bayersensor.pixelshiftMotion && p.raw.bayersensor.pixelshiftMotion == other.raw.bayersensor.pixelshiftMotion; - raw.bayersensor.pixelshiftMotionCorrection = raw.bayersensor.pixelshiftMotionCorrection && p.raw.bayersensor.pixelshiftMotionCorrection == other.raw.bayersensor.pixelshiftMotionCorrection; + raw.bayersensor.pixelShiftMotion = raw.bayersensor.pixelShiftMotion && p.raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion; + raw.bayersensor.pixelShiftMotionCorrection = raw.bayersensor.pixelShiftMotionCorrection && p.raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection; raw.bayersensor.pixelShiftStddevFactor = raw.bayersensor.pixelShiftStddevFactor && p.raw.bayersensor.pixelShiftStddevFactor == other.raw.bayersensor.pixelShiftStddevFactor; raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; @@ -2300,12 +2300,12 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.lmmse_iterations = mods.raw.bayersensor.lmmse_iterations; } - if (raw.bayersensor.pixelshiftMotion) { - toEdit.raw.bayersensor.pixelshiftMotion = mods.raw.bayersensor.pixelshiftMotion; + if (raw.bayersensor.pixelShiftMotion) { + toEdit.raw.bayersensor.pixelShiftMotion = mods.raw.bayersensor.pixelShiftMotion; } - if (raw.bayersensor.pixelshiftMotionCorrection) { - toEdit.raw.bayersensor.pixelshiftMotionCorrection = mods.raw.bayersensor.pixelshiftMotionCorrection; + if (raw.bayersensor.pixelShiftMotionCorrection) { + toEdit.raw.bayersensor.pixelShiftMotionCorrection = mods.raw.bayersensor.pixelShiftMotionCorrection; } if (raw.bayersensor.pixelShiftStddevFactor) { @@ -2855,6 +2855,9 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten bool RAWParamsEdited::BayerSensor::isUnchanged() const { return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq + && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactor && pixelShiftEperIso + && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 9fe0d6b9b..4f9979d1f 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -692,8 +692,8 @@ public: bool dcbIterations; bool dcbEnhance; bool lmmseIterations; - bool pixelshiftMotion; - bool pixelshiftMotionCorrection; + bool pixelShiftMotion; + bool pixelShiftMotionCorrection; bool pixelShiftStddevFactor; bool pixelShiftEperIso; bool pixelShiftNreadIso; From 2b7d3fb591dcc4af1be276898e7dde571fc08755 Mon Sep 17 00:00:00 2001 From: Hombre Date: Fri, 2 Dec 2016 03:09:55 +0100 Subject: [PATCH 049/181] Forgot to update one file, see issue #3489 --- rtengine/refreshmap.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 4452ae623..cfea668c1 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -472,7 +472,18 @@ int refreshmap[rtengine::NUMOFEVENTS] = { OUTPUTPROFILE, // EvOBPCompens DARKFRAME, // EvRawImageNum DEMOSAIC, // EvDemosaicPixelshiftMotion - DEMOSAIC // EvDemosaicPixelshiftMotionMaskOnly + DEMOSAIC, // EvDemosaicPixelshiftMotionMaskOnly + DEMOSAIC, // EvPixelShiftMotion + DEMOSAIC, // EvPixelShiftMotionCorrection + DEMOSAIC, // EvPixelShiftStddevFactor + DEMOSAIC, // EvPixelShiftEperIso + DEMOSAIC, // EvPixelShiftNreadIso + DEMOSAIC, // EvPixelShiftPrnu + DEMOSAIC, // EvPixelshiftShowMotion + DEMOSAIC, // EvPixelshiftShowMotionMaskOnly + DEMOSAIC, // EvPixelShiftAutomatic + DEMOSAIC, // EvPixelShiftNonGreenHorizontal + DEMOSAIC // EvPixelShiftNonGreenVertical }; From 6a437c90b3c33f9e2616c920f4a39bc4aafbd055 Mon Sep 17 00:00:00 2001 From: Hombre Date: Fri, 2 Dec 2016 03:22:37 +0100 Subject: [PATCH 050/181] Bugfix Too much confidence... never commit w/o building first! Sorry --- rtengine/refreshmap.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index cfea668c1..6aa73c67a 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -471,8 +471,6 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens DARKFRAME, // EvRawImageNum - DEMOSAIC, // EvDemosaicPixelshiftMotion - DEMOSAIC, // EvDemosaicPixelshiftMotionMaskOnly DEMOSAIC, // EvPixelShiftMotion DEMOSAIC, // EvPixelShiftMotionCorrection DEMOSAIC, // EvPixelShiftStddevFactor From 27a537aba44b245868f2b2ad62c4276bea185443 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 2 Dec 2016 14:57:14 +0100 Subject: [PATCH 051/181] pixelshift: fixed possible crash bug in 1x1 and 1x2 mode --- rtengine/pixelshift.cc | 129 +++++++++++++++++++++++++++-------------- rtengine/procparams.cc | 4 +- rtgui/bayerprocess.cc | 4 +- 3 files changed, 89 insertions(+), 48 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 63b670ebc..301e36dae 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -48,6 +48,7 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); float result = gDiff - stddev; + if(!showMotion) { return result; } else if(result > 0.f) { // for the motion mask @@ -75,6 +76,7 @@ float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float ep prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); float result = gDiff - stddev; + if(!showMotion) { return result; } else if(result > 0.f) { // for the motion mask @@ -131,7 +133,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det 1.5f, // ISO 40000 1.5f, // ISO 51200 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; + }; static const float ePerIsoK3II = 0.35f; @@ -164,7 +166,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det 2.4f, // ISO 40000 2.4f, // ISO 51200 2.4f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; + }; static const float ePerIsoK1 = 0.75f; @@ -197,7 +199,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det 3.0f, // ISO 40000 3.0f, // ISO 51200 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; + }; static const float ePerIsoK70 = 0.5f; @@ -207,23 +209,40 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } - int gridSize = (int)gridSize_; + const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); + int gridSize = 1; + + switch (gridSize_) { + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: + gridSize = 1; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: + gridSize = 3; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: + gridSize = 5; + } - const bool skip = (gridSize != 1 ? false : true); - gridSize += ((gridSize & 1) == 0 ? 1 : 0); // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); - + if(detectMotion && !adaptive) { const float lutStrength = 2.f; log2Lut[0] = 0; - for(int i=2; i < 65536; i+=2) - log2Lut[i>>1] = lutStrength * log2(i) / 100.f; + + for(int i = 2; i < 65536; i += 2) { + log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; + } } + const float scaleGreen = 1.f / scale_mul[1]; float nRead; float eperIsoModel; + if(model.find("K-3") != string::npos) { nRead = nReadK3II[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; eperIsoModel = ePerIsoK3II; @@ -234,27 +253,28 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det nRead = nReadK70[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; eperIsoModel = ePerIsoK70; } - + nRead *= pow(2.f, nreadIso); eperIsoModel *= pow(2.f, eperIso); eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n",gridSize, adaptive, stddevFactor, eperIso, nRead, prnu); + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n", gridSize, adaptive, stddevFactor, eperIso, nRead, prnu); prnu /= 100.f; stddevFactor *= stddevFactor; nRead *= nRead; - + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel float motionThreshold = 1.f - (motion / 100.f); - // For shades of green motion indicators + // For shades of green motion indicators const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); bool checkNonGreen = true; unsigned int offsX = 0, offsY = 0; + // We have to adjust the offsets for the selected subframe we use for areas with motion switch (frame) { case 0: @@ -303,9 +323,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det unsigned int offset = (c & 1); float greenDifMax[gridSize]; + // motion detection checks the grid around the pixel for differences in green channels if(detectMotion || adaptive) { - if(gridSize < 2) { + if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid greenDifMax[0] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), @@ -317,29 +338,29 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j-2], (*rawDataFrames[3 - offset])[i + offset -2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j-2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j-2], (*rawDataFrames[3 - offset])[i + offset +2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j-2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j-2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset -2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset +2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset-2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset+2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j +- 1], (*rawDataFrames[3 - offset])[i + offset + 1][j+2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); } } @@ -356,6 +377,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { bool skipNext = false; float gridMax; + if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps gridMax = colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion); @@ -366,30 +388,32 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2]); + gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j+2], (*rawDataFrames[3 - offset])[i + offset -2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j+2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j+2], (*rawDataFrames[3 - offset])[i + offset +2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j+2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j+2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), + colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); - gridMax = max(greenDifMax[0],greenDifMax[1],greenDifMax[2],greenDifMax[3],greenDifMax[4]); + gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]); } + // adjust index for next column lastIndex ++; lastIndex = lastIndex == gridSize ? 0 : lastIndex; // increase motion detection dependent on brightness if(!adaptive) { - korr = log2Lut[((int)((*rawDataFrames[1 - offset])[i - offset + 1][j] * scaleGreen))>>1]; + korr = log2Lut[((int)((*rawDataFrames[1 - offset])[i - offset + 1][j] * scaleGreen)) >> 1]; } if (gridMax > thresh - korr) { // at least one of the tested pixels of the grid is detected as motion if(showMotion) { float blend = (gridMax - thresh + korr) * blendFactor; + if(!showOnlyMask) { // if showMotion is enabled make the pixel green greenDest[j + offsX] = 1000.f + 25000.f * blend; @@ -404,22 +428,26 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det j++; offset ^= 1; } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; } if(adaptive && checkNonGreenHorizontal) { float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ng0 = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1)+1]; - float ng2 = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1)-1]; + float ng0 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; + float ng2 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; float diff0 = ng0 - ng1; float diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; + if(!showOnlyMask) { // if showMotion is enabled colourize the pixel nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; @@ -428,21 +456,25 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } + continue; } } ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ng0 = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; - ng2 = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; + ng0 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; + ng2 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; diff0 = ng0 - ng1; diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; + if(!showOnlyMask) { // if showMotion is enabled colourize the pixel nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; @@ -451,6 +483,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } + continue; } } @@ -458,17 +491,20 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(adaptive && checkNonGreenVertical) { float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ng0 = (*rawDataFrames[((offset << 1) + offset)^1])[i][j + offset]; - float ng2 = (*rawDataFrames[((offset << 1) + offset)^1])[i+2][j + offset]; + float ng0 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; + float ng2 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; float diff0 = ng0 - ng1; float diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; + if(!showOnlyMask) { // if showMotion is enabled colourize the pixel nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; @@ -477,22 +513,26 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } + continue; } } ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ng0 = (*rawDataFrames[3 - ((offset<<1) + offset)])[i - 1][j - offset + 1]; - ng2 = (*rawDataFrames[3 - ((offset<<1) + offset)])[i + 1][j - offset + 1]; + ng0 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; + ng2 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; diff0 = ng0 - ng1; diff2 = ng2 - ng1; + if(diff0 * diff2 >= 0.f) { float val = (ng0 + ng2) / 2.f; float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + if(gridMax > 0.f) { if(showMotion) { float blend = gridMax * blendFactor; + if(!showOnlyMask) { // if showMotion is enabled colourize the pixel nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; @@ -501,6 +541,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } } + continue; } } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 57958a6f5..66c924fc1 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -886,8 +886,8 @@ void RAWParams::setDefaults() //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; bayersensor.pixelShiftMotion = 0; - bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid5x5; - bayersensor.pixelShiftStddevFactor = 5.0; + bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid1x2; + bayersensor.pixelShiftStddevFactor = 3.0; bayersensor.pixelShiftEperIso = 0.0; bayersensor.pixelShiftNreadIso = 0.0; bayersensor.pixelShiftPrnu = 1.0; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 2d2f2902b..f5e81de75 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -116,12 +116,12 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->append_text("3x3"); pixelShiftMotionCorrection->append_text("5x5"); pixelShiftMotionCorrection->set_active(0); - pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); +// pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); pixelShiftMotionCorrection->show(); hb2->pack_start(*pixelShiftMotionCorrection); pixelShiftOptions->pack_start(*hb2); - pixelShiftStddevFactor = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR"), 2, 8, 0.1, 5)); + pixelShiftStddevFactor = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR"), 2, 8, 0.1, 3)); pixelShiftStddevFactor->setAdjusterListener (this); // pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); From 4820437c469dfff03706099d58728f3ca8fdd6f5 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 2 Dec 2016 22:41:09 +0100 Subject: [PATCH 052/181] pixelshift: added checkbox to test red/blue cross check --- rtdata/languages/default | 2 ++ rtengine/pixelshift.cc | 65 ++++++++++++++++++++++++++++++++++++-- rtengine/procevents.h | 1 + rtengine/procparams.cc | 14 ++++++++ rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 3 +- rtgui/bayerprocess.cc | 30 ++++++++++++++++++ rtgui/bayerprocess.h | 4 ++- rtgui/paramsedited.cc | 8 ++++- rtgui/paramsedited.h | 1 + 12 files changed, 126 insertions(+), 7 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 23a7d5d68..d10756b2b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -687,6 +687,7 @@ HISTORY_MSG_452;EvPixelshiftShowMotionMaskOnly HISTORY_MSG_453;EvPixelShiftAutomatic HISTORY_MSG_454;EvPixelShiftNonGreenHorizontal HISTORY_MSG_455;EvPixelShiftNonGreenVertical +HISTORY_MSG_456;EvPixelShiftNonGreenCross HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1668,6 +1669,7 @@ TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (ste TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical +TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 301e36dae..06dfb3b70 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -98,7 +98,7 @@ float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float ep using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) { BENCHFUN @@ -272,7 +272,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det // For shades of green motion indicators const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); - bool checkNonGreen = true; unsigned int offsX = 0, offsY = 0; // We have to adjust the offsets for the selected subframe we use for areas with motion @@ -433,6 +432,68 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det continue; } + if(adaptive && checkNonGreenCross) { + float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ng0 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; + float ng2 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; + float ng3 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; + float ng4 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; + float diff0 = ng0 - ng1; + float diff2 = ng2 - ng1; + float diff3 = ng3 - ng1; + float diff4 = ng4 - ng1; + if(diff0 * diff2 >= 0.f && diff3 * diff4 >= 0.f && diff0 * diff3 >= 0.f) { + float val = (ng0 + ng2 + ng3 + ng4) / 4.f; + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } + } + + continue; + } + } + + ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ng0 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; + ng2 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; + ng3 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; + ng4 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; + diff0 = ng0 - ng1; + diff2 = ng2 - ng1; + diff3 = ng3 - ng1; + diff4 = ng4 - ng1; + if(diff0 * diff2 >= 0.f && diff3 * diff4 >= 0.f && diff0 * diff3 >= 0.f) { + float val = (ng0 + ng2 + ng3 + ng4) / 4.f; + float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; + + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } + } + + continue; + } + } + } + if(adaptive && checkNonGreenHorizontal) { float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ng0 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index a82a2d028..7d9d41436 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -482,6 +482,7 @@ enum ProcEvent { EvPixelShiftAutomatic = 452, EvPixelShiftNonGreenHorizontal = 453, EvPixelShiftNonGreenVertical = 454, + EvPixelShiftNonGreenCross = 455, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 66c924fc1..86f02f26d 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -896,6 +896,7 @@ void RAWParams::setDefaults() bayersensor.pixelShiftAutomatic = true; bayersensor.pixelShiftNonGreenHorizontal = false; bayersensor.pixelShiftNonGreenVertical = false; + bayersensor.pixelShiftNonGreenCross = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3419,6 +3420,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenVertical", raw.bayersensor.pixelShiftNonGreenVertical ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); + } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { @@ -7565,6 +7570,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross")) { + raw.bayersensor.pixelShiftNonGreenCross = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenCross = true; + } + } + //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } @@ -8015,6 +8028,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical + && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance //&& raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 8d3dfc541..1b866b09d 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1196,6 +1196,7 @@ public: bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; + bool pixelShiftNonGreenCross; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 6b97eb0b6..6d4f744d1 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2027,7 +2027,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical); + pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index a6877f6fd..df284e8c9 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 6aa73c67a..f0fdcb69f 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -481,7 +481,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelshiftShowMotionMaskOnly DEMOSAIC, // EvPixelShiftAutomatic DEMOSAIC, // EvPixelShiftNonGreenHorizontal - DEMOSAIC // EvPixelShiftNonGreenVertical + DEMOSAIC, // EvPixelShiftNonGreenVertical + DEMOSAIC // EvPixelShiftNonGreenCross }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index f5e81de75..611314fc0 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -92,6 +92,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); + pixelShiftNonGreenCross = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); + pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); @@ -196,6 +199,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); + pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -229,6 +233,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); @@ -257,6 +262,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); @@ -325,6 +331,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -356,6 +363,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); } } @@ -589,6 +597,7 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftStddevFactor->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -636,6 +645,27 @@ void BayerProcess::pixelShiftNonGreenVerticalChanged () } } +void BayerProcess::pixelShiftNonGreenCrossChanged () +{ + if (batchMode) { + if (pixelShiftNonGreenCross->get_inconsistent()) { + pixelShiftNonGreenCross->set_inconsistent (false); + pixelShiftNonGreenCrossconn.block (true); + pixelShiftNonGreenCross->set_active (false); + pixelShiftNonGreenCrossconn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenCross->set_inconsistent (true); + } + + lastDCBen = pixelShiftNonGreenCross->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenCross, pixelShiftNonGreenCross->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + + /*void BayerProcess::allEnhanceChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index a54637f13..136298e80 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -49,6 +49,7 @@ protected: Gtk::CheckButton* pixelShiftAutomatic; Gtk::CheckButton* pixelShiftNonGreenHorizontal; Gtk::CheckButton* pixelShiftNonGreenVertical; + Gtk::CheckButton* pixelShiftNonGreenCross; Adjuster* pixelShiftStddevFactor; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; @@ -56,7 +57,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn; //,allEnhconn; public: BayerProcess (); @@ -76,6 +77,7 @@ public: void pixelShiftAutomaticChanged(); void pixelShiftNonGreenHorizontalChanged(); void pixelShiftNonGreenVerticalChanged(); + void pixelShiftNonGreenCrossChanged(); //void allEnhanceChanged(); }; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ac0257fa1..f4b41b784 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -381,6 +381,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftAutomatic = v; raw.bayersensor.pixelShiftNonGreenHorizontal = v; raw.bayersensor.pixelShiftNonGreenVertical = v; + raw.bayersensor.pixelShiftNonGreenCross = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -888,6 +889,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; + raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2344,6 +2346,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftNonGreenVertical = mods.raw.bayersensor.pixelShiftNonGreenVertical; } + if (raw.bayersensor.pixelShiftNonGreenCross) { + toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; + } + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; @@ -2857,7 +2863,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactor && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 4f9979d1f..bf66afd5d 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -703,6 +703,7 @@ public: bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; + bool pixelShiftNonGreenCross; //bool allEnhance; bool greenEq; From 4384819eeefe56df91d96c119ed15419301421f6 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 4 Dec 2016 22:35:29 +0100 Subject: [PATCH 053/181] pixelshift: changed red/blue cross check --- rtengine/pixelshift.cc | 89 +++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 06dfb3b70..c5ead46c3 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -2,7 +2,7 @@ // // pentax pixelshift algorithm with motion detection // -// derived from dcrawps (https://github.com/tomtor/dcrawps), but with additional motion correction methods and adapted for RawTherapee data structures +// non adaptive mode is derived from dcrawps (https://github.com/tomtor/dcrawps), but with additional motion correction methods and adapted for RawTherapee data structures // // If motion correction is enabled only the pixels which are not detected as motion are set // That means for a complete image you have to demosaic one of the frames with a bayer demosaicer to fill red, green and blue @@ -92,6 +92,29 @@ float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float ep } } +float nonGreenDiffCross(float a, float b, float c, float d, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +{ + // calculate the difference between two nongreen samples + float hDiff = (a + b) / 2.f - centre; + hDiff *= eperIso; + hDiff *= hDiff; + float vDiff = (c + d) / 2.f - centre; + vDiff *= eperIso; + vDiff *= vDiff; + float avg = (a + b + c + d) / 4.f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = std::min(hDiff - stddev, vDiff - stddev); + + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / (std::max(a, b) + 0.01f); + } else { + return 0.f; + } +} } @@ -359,7 +382,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) ); } } @@ -438,29 +461,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ng2 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; float ng3 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; float ng4 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float diff0 = ng0 - ng1; - float diff2 = ng2 - ng1; - float diff3 = ng3 - ng1; - float diff4 = ng4 - ng1; - if(diff0 * diff2 >= 0.f && diff3 * diff4 >= 0.f && diff0 * diff3 >= 0.f) { - float val = (ng0 + ng2 + ng3 + ng4) / 4.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + float gridMax = nonGreenDiffCross(ng0, ng2, ng3, ng4, ng1, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); - if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } - - continue; } + + continue; } ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; @@ -468,29 +484,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det ng2 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; ng3 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; ng4 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - diff0 = ng0 - ng1; - diff2 = ng2 - ng1; - diff3 = ng3 - ng1; - diff4 = ng4 - ng1; - if(diff0 * diff2 >= 0.f && diff3 * diff4 >= 0.f && diff0 * diff3 >= 0.f) { - float val = (ng0 + ng2 + ng3 + ng4) / 4.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + gridMax = nonGreenDiffCross(ng0, ng2, ng3, ng4, ng1, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); - if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; + if(gridMax > 0.f) { + if(showMotion) { + float blend = gridMax * blendFactor; - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; } - - continue; } + + continue; } } From d498e5f5c5a3be8de1ff13b8bb2badba2bb75995 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 4 Dec 2016 23:59:42 +0100 Subject: [PATCH 054/181] pixelshift: partly fixed wrong colouring of red/blue cross check motion mask. Not good, but better than last one --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index c5ead46c3..2d808bb2e 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -110,7 +110,7 @@ float nonGreenDiffCross(float a, float b, float c, float d, float centre, float if(!showMotion) { return result; } else if(result > 0.f) { // for the motion mask - return std::fabs(a - b) / (std::max(a, b) + 0.01f); + return std::sqrt((result / (stddev + result + 0.01f))); //1.f; //std::fabs(a - b) / (std::max(a, b) + 0.01f); } else { return 0.f; } From 49aa9f0c4e1edca645fc3fcc9c72118a409a3114 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 5 Dec 2016 15:34:05 +0100 Subject: [PATCH 055/181] pixelshift: Added new red/blue stddev adjusters for test --- rtdata/languages/default | 8 +- rtengine/pixelshift.cc | 218 ++++++++++++++++++------------------- rtengine/procevents.h | 4 +- rtengine/procparams.cc | 42 +++++-- rtengine/procparams.h | 6 +- rtengine/rawimagesource.cc | 6 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 6 +- rtgui/bayerprocess.cc | 84 ++++++++++---- rtgui/bayerprocess.h | 4 +- rtgui/paramsedited.cc | 22 +++- rtgui/paramsedited.h | 4 +- 12 files changed, 248 insertions(+), 158 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index d10756b2b..af0c0525b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -678,7 +678,7 @@ HISTORY_MSG_443;Output Black Point Compensation HISTORY_MSG_444;Raw Sub-Image HISTORY_MSG_445;EvPixelShiftMotion HISTORY_MSG_446;EvPixelShiftMotionCorrection -HISTORY_MSG_447;EvPixelShiftStddevFactor +HISTORY_MSG_447;EvPixelShiftStddevFactorGreen HISTORY_MSG_448;EvPixelShiftEperIso HISTORY_MSG_449;EvPixelShiftNreadIso HISTORY_MSG_450;EvPixelShiftPrnu @@ -688,6 +688,8 @@ HISTORY_MSG_453;EvPixelShiftAutomatic HISTORY_MSG_454;EvPixelShiftNonGreenHorizontal HISTORY_MSG_455;EvPixelShiftNonGreenVertical HISTORY_MSG_456;EvPixelShiftNonGreenCross +HISTORY_MSG_457;EvPixelShiftStddevFactorRed +HISTORY_MSG_458;EvPixelShiftStddevFactorBlue HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1676,7 +1678,9 @@ TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only -TP_RAW_PIXELSHIFTSTDDEVFACTOR;StdDev factor +TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN;StdDev factor Green +TP_RAW_PIXELSHIFTSTDDEVFACTORRED;StdDev factor Red +TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE;StdDev factor Blue TP_RAW_PIXELSHIFTEPERISO;e per ISO TP_RAW_PIXELSHIFTNREADISO;nRead TP_RAW_PIXELSHIFTPRNU;PRNU (%) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 2d808bb2e..62b47e41b 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -36,7 +36,7 @@ namespace { -float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between two green samples if(adaptive) { @@ -64,44 +64,37 @@ float colourDiff(float a, float b, bool adaptive, float stddevFactor, float eper } } -float nonGreenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between two nongreen samples - if(adaptive) { - float gDiff = a - b; - gDiff *= eperIso; - gDiff *= gDiff; - float avg = (a + b) / 2.f; - avg *= eperIso; - prnu *= avg; - float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); - float result = gDiff - stddev; + float gDiff = a - b; + gDiff *= eperIso; + gDiff *= gDiff; + float avg = (a + b) / 2.f; + avg *= eperIso; + prnu *= avg; + float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + float result = gDiff - stddev; - if(!showMotion) { - return result; - } else if(result > 0.f) { // for the motion mask - return std::fabs(a - b) / (std::max(a, b) + 0.01f); - } else { - return 0.f; - } + if(!showMotion) { + return result; + } else if(result > 0.f) { // for the motion mask + return std::fabs(a - b) / (std::max(a, b) + 0.01f); } else { - float gDiff = std::fabs(a - b); - // add a small epsilon to avoid division by zero - float maxVal = std::max(a, b) + 0.01f; - return gDiff / maxVal; + return 0.f; } } -float nonGreenDiffCross(float a, float b, float c, float d, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between two nongreen samples - float hDiff = (a + b) / 2.f - centre; + float hDiff = (right + left) / 2.f - centre; hDiff *= eperIso; hDiff *= hDiff; - float vDiff = (c + d) / 2.f - centre; + float vDiff = (top + bottom) / 2.f - centre; vDiff *= eperIso; vDiff *= vDiff; - float avg = (a + b + c + d) / 4.f; + float avg = (right + left + top + bottom) / 4.f; avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); @@ -110,7 +103,7 @@ float nonGreenDiffCross(float a, float b, float c, float d, float centre, float if(!showMotion) { return result; } else if(result > 0.f) { // for the motion mask - return std::sqrt((result / (stddev + result + 0.01f))); //1.f; //std::fabs(a - b) / (std::max(a, b) + 0.01f); + return std::sqrt((result / (stddev + result + 0.01f))); } else { return 0.f; } @@ -121,7 +114,7 @@ float nonGreenDiffCross(float a, float b, float c, float d, float centre, float using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) { BENCHFUN @@ -227,7 +220,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det static const float ePerIsoK70 = 0.5f; if (plistener) { - plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple])); + plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); plistener->setProgress(0.0); } @@ -282,10 +275,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactor %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n", gridSize, adaptive, stddevFactor, eperIso, nRead, prnu); + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); prnu /= 100.f; - stddevFactor *= stddevFactor; + stddevFactorGreen *= stddevFactorGreen; + stddevFactorRed *= stddevFactorRed; + stddevFactorBlue *= stddevFactorBlue; nRead *= nRead; @@ -333,12 +328,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float scaleNonGreen2 = 1.f / scale_mul[2]; float eperIsoNonGreen0 = eperIso / scale_mul[0]; float eperIsoNonGreen2 = eperIso / scale_mul[2]; + float stddevFactorNonGreen0 = stddevFactorRed; + float stddevFactorNonGreen2 = stddevFactorBlue; if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels std::swap(nonGreenDest0, nonGreenDest1); std::swap(scaleNonGreen0, scaleNonGreen2); std::swap(eperIsoNonGreen0, eperIsoNonGreen2); + std::swap(stddevFactorNonGreen0, stddevFactorNonGreen2); } // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop @@ -350,39 +348,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[0] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[1] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[2] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[2] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[3] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[3] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); } } @@ -402,22 +400,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion); + gridMax = greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); skipNext = skip && !showMotion ; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(colourDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion), - colourDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactor, eperIsoGreen, nRead, prnu, showMotion) + greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]); } @@ -456,12 +454,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } if(adaptive && checkNonGreenCross) { - float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ng0 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; - float ng2 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float ng3 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; - float ng4 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float gridMax = nonGreenDiffCross(ng0, ng2, ng3, ng4, ng1, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; + float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; + float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; + float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; + float gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { @@ -479,12 +477,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det continue; } - ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ng0 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; - ng2 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - ng3 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; - ng4 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - gridMax = nonGreenDiffCross(ng0, ng2, ng3, ng4, ng1, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; + ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; + ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; + ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; + gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { @@ -504,15 +502,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } if(adaptive && checkNonGreenHorizontal) { - float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ng0 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; - float ng2 = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float diff0 = ng0 - ng1; - float diff2 = ng2 - ng1; + float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; + float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; + float diffRight = ngRight - ngCentre; + float diff2 = ngLeft - ngCentre; - if(diff0 * diff2 >= 0.f) { - float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + if(diffRight * diff2 >= 0.f) { + float val = (ngRight + ngLeft) / 2.f; + float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { @@ -531,15 +529,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } - ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ng0 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; - ng2 = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - diff0 = ng0 - ng1; - diff2 = ng2 - ng1; + ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; + ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; + diffRight = ngRight - ngCentre; + diff2 = ngLeft - ngCentre; - if(diff0 * diff2 >= 0.f) { - float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + if(diffRight * diff2 >= 0.f) { + float val = (ngRight + ngLeft) / 2.f; + float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { @@ -560,16 +558,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } if(adaptive && checkNonGreenVertical) { - float ng1 = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ng0 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; - float ng2 = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; + float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; + float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float diff0 = ng0 - ng1; - float diff2 = ng2 - ng1; + float diffTop = ngTop - ngCentre; + float diffBottom = ngBottom - ngCentre; - if(diff0 * diff2 >= 0.f) { - float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen0, nRead, prnu, showMotion); + if(diffTop * diffBottom >= 0.f) { + float val = (ngTop + ngBottom) / 2.f; + float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { @@ -588,16 +586,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } } - ng1 = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ng0 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; - ng2 = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; + ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; + ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - diff0 = ng0 - ng1; - diff2 = ng2 - ng1; + diffTop = ngTop - ngCentre; + diffBottom = ngBottom - ngCentre; - if(diff0 * diff2 >= 0.f) { - float val = (ng0 + ng2) / 2.f; - float gridMax = nonGreenDiff(ng1, val, true, stddevFactor, eperIsoNonGreen2, nRead, prnu, showMotion); + if(diffTop * diffBottom >= 0.f) { + float val = (ngTop + ngBottom) / 2.f; + float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { if(showMotion) { diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 7d9d41436..747d60cb4 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -473,7 +473,7 @@ enum ProcEvent { EvRawImageNum = 443, EvPixelShiftMotion = 444, EvPixelShiftMotionCorrection = 445, - EvPixelShiftStddevFactor = 446, + EvPixelShiftStddevFactorGreen = 446, EvPixelShiftEperIso = 447, EvPixelShiftNreadIso = 448, EvPixelShiftPrnu = 449, @@ -483,6 +483,8 @@ enum ProcEvent { EvPixelShiftNonGreenHorizontal = 453, EvPixelShiftNonGreenVertical = 454, EvPixelShiftNonGreenCross = 455, + EvPixelShiftStddevFactorRed = 456, + EvPixelShiftStddevFactorBlue = 457, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 86f02f26d..0a09995fe 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -887,7 +887,9 @@ void RAWParams::setDefaults() bayersensor.lmmse_iterations = 2; bayersensor.pixelShiftMotion = 0; bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid1x2; - bayersensor.pixelShiftStddevFactor = 3.0; + bayersensor.pixelShiftStddevFactorGreen = 3.0; + bayersensor.pixelShiftStddevFactorRed = 3.0; + bayersensor.pixelShiftStddevFactorBlue = 3.0; bayersensor.pixelShiftEperIso = 0.0; bayersensor.pixelShiftNreadIso = 0.0; bayersensor.pixelShiftPrnu = 1.0; @@ -3384,8 +3386,16 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelShiftMotionCorrection ); } - if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactor) { - keyFile.set_double ("RAW Bayer", "PixelShiftStddevFactor", raw.bayersensor.pixelShiftStddevFactor ); + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorGreen) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorGreen", raw.bayersensor.pixelShiftStddevFactorGreen ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorRed) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorRed", raw.bayersensor.pixelShiftStddevFactorRed ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorBlue) { + keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorBlue", raw.bayersensor.pixelShiftStddevFactorBlue ); } if (!pedited || pedited->raw.bayersensor.pixelShiftEperIso) { @@ -7498,11 +7508,27 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - if (keyFile.has_key ("RAW Bayer", "PixelShiftStddevFactor")) { - raw.bayersensor.pixelShiftStddevFactor = keyFile.get_double("RAW Bayer", "PixelShiftStddevFactor"); + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorGreen")) { + raw.bayersensor.pixelShiftStddevFactorGreen = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorGreen"); if (pedited) { - pedited->raw.bayersensor.pixelShiftStddevFactor = true; + pedited->raw.bayersensor.pixelShiftStddevFactorGreen = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorRed")) { + raw.bayersensor.pixelShiftStddevFactorRed = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorRed"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactorRed = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorBlue")) { + raw.bayersensor.pixelShiftStddevFactorBlue = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorBlue"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftStddevFactorBlue = true; } } @@ -8019,7 +8045,9 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations && raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion && raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection - && raw.bayersensor.pixelShiftStddevFactor == other.raw.bayersensor.pixelShiftStddevFactor + && raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen + && raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed + && raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue && raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso && raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso && raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 1b866b09d..d38824559 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1165,7 +1165,7 @@ public: public: //enum eMethod{ eahd,hphd,vng4,dcb,amaze,ahd,IGV_noise,fast, //numMethods }; // This MUST be the last enum - enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, pixelshift_simple, + enum eMethod { amaze, igv, lmmse, eahd, hphd, vng4, dcb, ahd, fast, mono, none, pixelshift, numMethods }; // This MUST be the last enum enum ePSMotionCorrection { @@ -1187,7 +1187,9 @@ public: int lmmse_iterations; int pixelShiftMotion; ePSMotionCorrection pixelShiftMotionCorrection; - double pixelShiftStddevFactor; + double pixelShiftStddevFactorGreen; + double pixelShiftStddevFactorRed; + double pixelShiftStddevFactorBlue; double pixelShiftEperIso; double pixelShiftNreadIso; double pixelShiftPrnu; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 6d4f744d1..dab8e342c 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1966,7 +1966,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgressStr ("CA Auto Correction..."); plistener->setProgress (0.0); } - if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple]) { + if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift]) { for(int i=0; i<4; ++i) { CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); } @@ -2022,12 +2022,12 @@ void RawImageSource::demosaic(const RAWParams &raw) ahd_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { amaze_demosaic_RT (0, 0, W, H); - } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift_simple] ) { + } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift] ) { if(raw.bayersensor.pixelShiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) { amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactor, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); + pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index df284e8c9..f80efeacf 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index f0fdcb69f..08e3d2520 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -473,7 +473,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DARKFRAME, // EvRawImageNum DEMOSAIC, // EvPixelShiftMotion DEMOSAIC, // EvPixelShiftMotionCorrection - DEMOSAIC, // EvPixelShiftStddevFactor + DEMOSAIC, // EvPixelShiftStddevFactorGreen DEMOSAIC, // EvPixelShiftEperIso DEMOSAIC, // EvPixelShiftNreadIso DEMOSAIC, // EvPixelShiftPrnu @@ -482,7 +482,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftAutomatic DEMOSAIC, // EvPixelShiftNonGreenHorizontal DEMOSAIC, // EvPixelShiftNonGreenVertical - DEMOSAIC // EvPixelShiftNonGreenCross + DEMOSAIC, // EvPixelShiftNonGreenCross + DEMOSAIC, // EvPixelShiftStddevFactorRed + DEMOSAIC // EvPixelShiftStddevFactorBlue }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 611314fc0..87a433e38 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -124,20 +124,38 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA hb2->pack_start(*pixelShiftMotionCorrection); pixelShiftOptions->pack_start(*hb2); - pixelShiftStddevFactor = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR"), 2, 8, 0.1, 3)); - pixelShiftStddevFactor->setAdjusterListener (this); -// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); + pixelShiftStddevFactorGreen = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN"), 2, 8, 0.1, 3)); + pixelShiftStddevFactorGreen->setAdjusterListener (this); - if (pixelShiftStddevFactor->delay < options.adjusterMaxDelay) { - pixelShiftStddevFactor->delay = options.adjusterMaxDelay; + if (pixelShiftStddevFactorGreen->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorGreen->delay = options.adjusterMaxDelay; } - pixelShiftStddevFactor->show(); - pixelShiftOptions->pack_start(*pixelShiftStddevFactor); + pixelShiftStddevFactorGreen->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorGreen); + + pixelShiftStddevFactorRed = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORRED"), 1, 8, 0.1, 3)); + pixelShiftStddevFactorRed->setAdjusterListener (this); + + if (pixelShiftStddevFactorRed->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorRed->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactorRed->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorRed); + + pixelShiftStddevFactorBlue = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE"), 1, 8, 0.1, 3)); + pixelShiftStddevFactorBlue->setAdjusterListener (this); + + if (pixelShiftStddevFactorBlue->delay < options.adjusterMaxDelay) { + pixelShiftStddevFactorBlue->delay = options.adjusterMaxDelay; + } + + pixelShiftStddevFactorBlue->show(); + pixelShiftOptions->pack_start(*pixelShiftStddevFactorBlue); pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -2.0, 2.0, 0.05, 0.0)); pixelShiftEperIso->setAdjusterListener (this); -// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { pixelShiftEperIso->delay = options.adjusterMaxDelay; @@ -148,7 +166,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), -2.0, 2.0, 0.05, 0.0)); pixelShiftNreadIso->setAdjusterListener (this); -// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); if (pixelShiftNreadIso->delay < options.adjusterMaxDelay) { pixelShiftNreadIso->delay = options.adjusterMaxDelay; @@ -160,7 +177,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftPrnu = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTPRNU"), 0.3, 2.0, 0.1, 1.0)); pixelShiftPrnu->setAdjusterListener (this); -// pixelShiftStddevFactor->set_tooltip_markup (M("TP_RAW_PIXELSHIFTSTDDEVFACTOR_TOOLTIP")); if (pixelShiftPrnu->delay < options.adjusterMaxDelay) { pixelShiftPrnu->delay = options.adjusterMaxDelay; @@ -237,7 +253,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); - pixelShiftStddevFactor->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); + pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); + pixelShiftStddevFactorRed->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); + pixelShiftStddevFactorBlue->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); @@ -267,7 +285,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); - pixelShiftStddevFactor->setValue (pp->raw.bayersensor.pixelShiftStddevFactor); + pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); + pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); + pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); @@ -285,7 +305,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } else { lmmseOptions->hide(); } - if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::pixelshift_simple] || + if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::pixelshift] || method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { pixelShiftOptions->show(); } else { @@ -322,7 +342,9 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); - pp->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); @@ -354,7 +376,9 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); - pedited->raw.bayersensor.pixelShiftStddevFactor = pixelShiftStddevFactor->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getEditedState (); pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); @@ -383,7 +407,9 @@ void BayerProcess::setBatchMode(bool batchMode) dcbIterations->showEditedCB (); lmmseIterations->showEditedCB (); pixelShiftMotion->showEditedCB (); - pixelShiftStddevFactor->showEditedCB (); + pixelShiftStddevFactorGreen->showEditedCB (); + pixelShiftStddevFactorRed->showEditedCB (); + pixelShiftStddevFactorBlue->showEditedCB (); pixelShiftEperIso->showEditedCB (); pixelShiftNreadIso->showEditedCB (); pixelShiftPrnu->showEditedCB (); @@ -394,7 +420,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams dcbIterations->setDefault( defParams->raw.bayersensor.dcb_iterations); lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelShiftMotion); - pixelShiftStddevFactor->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactor); + pixelShiftStddevFactorGreen->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorGreen); + pixelShiftStddevFactorRed->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorRed); + pixelShiftStddevFactorBlue->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorBlue); pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); pixelShiftPrnu->setDefault( defParams->raw.bayersensor.pixelShiftPrnu); @@ -404,7 +432,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams dcbIterations->setDefaultEditedState( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); - pixelShiftStddevFactor->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactor ? Edited : UnEdited); + pixelShiftStddevFactorGreen->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); + pixelShiftStddevFactorRed->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); + pixelShiftStddevFactorBlue->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); pixelShiftPrnu->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); @@ -413,7 +443,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); pixelShiftMotion->setDefaultEditedState( Irrelevant ); - pixelShiftStddevFactor->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorGreen->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorRed->setDefaultEditedState( Irrelevant ); + pixelShiftStddevFactorBlue->setDefaultEditedState( Irrelevant ); pixelShiftEperIso->setDefaultEditedState( Irrelevant ); pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); pixelShiftPrnu->setDefaultEditedState( Irrelevant ); @@ -432,8 +464,12 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvDemosaicLMMSEIter, a->getTextValue() ); } else if (a == pixelShiftMotion) { listener->panelChanged (EvPixelShiftMotion, a->getTextValue() ); - } else if (a == pixelShiftStddevFactor) { - listener->panelChanged (EvPixelShiftStddevFactor, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorGreen) { + listener->panelChanged (EvPixelShiftStddevFactorGreen, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorRed) { + listener->panelChanged (EvPixelShiftStddevFactorRed, a->getTextValue() ); + } else if (a == pixelShiftStddevFactorBlue) { + listener->panelChanged (EvPixelShiftStddevFactorBlue, a->getTextValue() ); } else if (a == pixelShiftEperIso) { listener->panelChanged (EvPixelShiftEperIso, a->getTextValue() ); } else if (a == pixelShiftNreadIso) { @@ -485,7 +521,7 @@ void BayerProcess::methodChanged () lmmseOptions->hide(); } - if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift_simple) { + if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { pixelShiftOptions->show(); } else { pixelShiftOptions->hide(); @@ -594,7 +630,9 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftEperIso->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNreadIso->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftStddevFactor->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftStddevFactorGreen->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftStddevFactorRed->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftStddevFactorBlue->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 136298e80..ca560ad89 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -50,7 +50,9 @@ protected: Gtk::CheckButton* pixelShiftNonGreenHorizontal; Gtk::CheckButton* pixelShiftNonGreenVertical; Gtk::CheckButton* pixelShiftNonGreenCross; - Adjuster* pixelShiftStddevFactor; + Adjuster* pixelShiftStddevFactorGreen; + Adjuster* pixelShiftStddevFactorRed; + Adjuster* pixelShiftStddevFactorBlue; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; Adjuster* pixelShiftPrnu; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index f4b41b784..f433ec639 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -372,7 +372,9 @@ void ParamsEdited::set (bool v) raw.bayersensor.lmmseIterations = v; raw.bayersensor.pixelShiftMotion = v; raw.bayersensor.pixelShiftMotionCorrection = v; - raw.bayersensor.pixelShiftStddevFactor = v; + raw.bayersensor.pixelShiftStddevFactorGreen = v; + raw.bayersensor.pixelShiftStddevFactorRed = v; + raw.bayersensor.pixelShiftStddevFactorBlue = v; raw.bayersensor.pixelShiftEperIso = v; raw.bayersensor.pixelShiftNreadIso = v; raw.bayersensor.pixelShiftPrnu = v; @@ -880,7 +882,9 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; raw.bayersensor.pixelShiftMotion = raw.bayersensor.pixelShiftMotion && p.raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion; raw.bayersensor.pixelShiftMotionCorrection = raw.bayersensor.pixelShiftMotionCorrection && p.raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection; - raw.bayersensor.pixelShiftStddevFactor = raw.bayersensor.pixelShiftStddevFactor && p.raw.bayersensor.pixelShiftStddevFactor == other.raw.bayersensor.pixelShiftStddevFactor; + raw.bayersensor.pixelShiftStddevFactorGreen = raw.bayersensor.pixelShiftStddevFactorGreen && p.raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen; + raw.bayersensor.pixelShiftStddevFactorRed = raw.bayersensor.pixelShiftStddevFactorRed && p.raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed; + raw.bayersensor.pixelShiftStddevFactorBlue = raw.bayersensor.pixelShiftStddevFactorBlue && p.raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue; raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; raw.bayersensor.pixelShiftPrnu = raw.bayersensor.pixelShiftPrnu && p.raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu; @@ -2310,8 +2314,16 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftMotionCorrection = mods.raw.bayersensor.pixelShiftMotionCorrection; } - if (raw.bayersensor.pixelShiftStddevFactor) { - toEdit.raw.bayersensor.pixelShiftStddevFactor = mods.raw.bayersensor.pixelShiftStddevFactor; + if (raw.bayersensor.pixelShiftStddevFactorGreen) { + toEdit.raw.bayersensor.pixelShiftStddevFactorGreen = mods.raw.bayersensor.pixelShiftStddevFactorGreen; + } + + if (raw.bayersensor.pixelShiftStddevFactorRed) { + toEdit.raw.bayersensor.pixelShiftStddevFactorRed = mods.raw.bayersensor.pixelShiftStddevFactorRed; + } + + if (raw.bayersensor.pixelShiftStddevFactorBlue) { + toEdit.raw.bayersensor.pixelShiftStddevFactorBlue = mods.raw.bayersensor.pixelShiftStddevFactorBlue; } if (raw.bayersensor.pixelShiftEperIso) { @@ -2861,7 +2873,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten bool RAWParamsEdited::BayerSensor::isUnchanged() const { return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq - && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactor && pixelShiftEperIso + && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index bf66afd5d..b4af2ad05 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -694,7 +694,9 @@ public: bool lmmseIterations; bool pixelShiftMotion; bool pixelShiftMotionCorrection; - bool pixelShiftStddevFactor; + bool pixelShiftStddevFactorGreen; + bool pixelShiftStddevFactorRed; + bool pixelShiftStddevFactorBlue; bool pixelShiftEperIso; bool pixelShiftNreadIso; bool pixelShiftPrnu; From 755a43f4ac338a4957448dcf2eb8d1f78a015a03 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 5 Dec 2016 19:17:58 +0100 Subject: [PATCH 056/181] pixelshift: reordered code for better readability, corrected behaviour of green 1x2 motion mask --- rtdata/languages/default | 2 +- rtengine/pixelshift.cc | 59 ++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index af0c0525b..db55dde96 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1674,7 +1674,7 @@ TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used -TP_RAW_PIXELSHIFTMOTIONCORRECTION;Pixelshift motion correction +TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 62b47e41b..8774e1cd5 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -87,7 +87,7 @@ float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nr float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { - // calculate the difference between two nongreen samples + // check non green cross float hDiff = (right + left) / 2.f - centre; hDiff *= eperIso; hDiff *= hDiff; @@ -275,7 +275,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f\%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); prnu /= 100.f; stddevFactorGreen *= stddevFactorGreen; @@ -348,39 +348,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); - greenDifMax[1] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); greenDifMax[2] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); greenDifMax[3] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); } } @@ -401,21 +401,21 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps gridMax = greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); - skipNext = skip && !showMotion ; + skipNext = skip; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2]); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]); } @@ -446,6 +446,17 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(skipNext) { // treat the horizontally next pixel also as motion j++; + if(showMotion) { + float blend = (gridMax - thresh + korr) * blendFactor; + + if(!showOnlyMask) { + // if showMotion is enabled make the pixel green + greenDest[j + offsX] = 1000.f + 25000.f * blend; + nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; + } else { + greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; + } + } offset ^= 1; } From 98b405368f4a2b0bfcc9daf44dba3f639541a0ac Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 5 Dec 2016 20:39:28 +0100 Subject: [PATCH 057/181] pixelshift: Added green 7x7 correction grid --- rtengine/pixelshift.cc | 68 ++++++++++++++++++++++++++++++++++++++++++ rtengine/procparams.h | 2 +- rtengine/rt_math.h | 6 ++++ rtgui/bayerprocess.cc | 1 + 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 8774e1cd5..d8a683bb3 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -240,6 +240,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: gridSize = 5; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: + gridSize = 7; + break; } // Lookup table for non adaptive (slider) mode @@ -382,7 +387,58 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); + } else if(gridSize == 7) { + // compute maximum of differences for first six columns of 7x7 grid + greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + greenDifMax[2] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + greenDifMax[3] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + greenDifMax[4] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + greenDifMax[5] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); } + } offset ^= 1; // 0 => 1 or 1 => 0 @@ -418,8 +474,20 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) ); gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]); + } else if(gridSize == 7) { + // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex + greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + ); + gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]); } + // adjust index for next column lastIndex ++; lastIndex = lastIndex == gridSize ? 0 : lastIndex; diff --git a/rtengine/procparams.h b/rtengine/procparams.h index d38824559..f9d6a60fd 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1169,7 +1169,7 @@ public: numMethods }; // This MUST be the last enum enum ePSMotionCorrection { - Grid1x1, Grid1x2, Grid3x3, Grid5x5 + Grid1x1, Grid1x2, Grid3x3, Grid5x5, Grid7x7 }; static const char *methodstring[numMethods]; diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 5e2f04d05..3abeae843 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -80,6 +80,12 @@ inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d, co return max(max(a,b,c),std::max(d,e)); } +template +inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d, const _Tp& e, const _Tp& f, const _Tp& g) +{ + return max(max(a,b,c,d),max(e,f,g)); +} + template inline _Tp intp(_Tp a, _Tp b, _Tp c) diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 87a433e38..297a9b65b 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -118,6 +118,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->append_text("1x2"); pixelShiftMotionCorrection->append_text("3x3"); pixelShiftMotionCorrection->append_text("5x5"); + pixelShiftMotionCorrection->append_text("7x7"); pixelShiftMotionCorrection->set_active(0); // pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); pixelShiftMotionCorrection->show(); From 49b5dbd595e3bb0403f7c7f23b0987b6555b545a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 6 Dec 2016 01:42:19 +0100 Subject: [PATCH 058/181] pixelshift: deduped code to paint motion mask --- rtengine/pixelshift.cc | 127 +++++++++-------------------------------- 1 file changed, 26 insertions(+), 101 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index d8a683bb3..b30918e37 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -109,6 +109,19 @@ float nonGreenDiffCross(float right, float left, float top, float bottom, float } } +void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMask, float *maskDest, float *nonMaskDest0, float *nonMaskDest1) +{ + if(showMotion) { + if(!showOnlyMask) { + // if showMotion is enabled colourize the pixel + maskDest[index] = 1000.f + 25000.f * gridMax; + nonMaskDest1[index] = nonMaskDest0[index] = 0.f; + } else { + maskDest[index] = nonMaskDest0[index] = nonMaskDest1[index] = 1000.f + 25000.f * gridMax; + } + } +} + } using namespace std; @@ -119,7 +132,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det BENCHFUN - static const float nReadK3II[] = { 3.4f, // ISO 100 3.1f, // ISO 125 2.5f, // ISO 160 @@ -244,7 +256,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: gridSize = 7; - break; } // Lookup table for non adaptive (slider) mode @@ -349,7 +360,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float greenDifMax[gridSize]; - // motion detection checks the grid around the pixel for differences in green channels + // green channel motion detection checks the grid around the pixel for differences in green channels if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid @@ -499,32 +510,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if (gridMax > thresh - korr) { // at least one of the tested pixels of the grid is detected as motion - if(showMotion) { - float blend = (gridMax - thresh + korr) * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, nonGreenDest0, nonGreenDest1); if(skipNext) { // treat the horizontally next pixel also as motion j++; - if(showMotion) { - float blend = (gridMax - thresh + korr) * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled make the pixel green - greenDest[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, nonGreenDest0, nonGreenDest1); offset ^= 1; } @@ -541,18 +532,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } @@ -564,18 +544,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } @@ -585,25 +554,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; float diffRight = ngRight - ngCentre; - float diff2 = ngLeft - ngCentre; + float diffLeft = ngLeft - ngCentre; - if(diffRight * diff2 >= 0.f) { + if(diffRight * diffLeft >= 0.f) { float val = (ngRight + ngLeft) / 2.f; float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } } @@ -612,25 +570,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; diffRight = ngRight - ngCentre; - diff2 = ngLeft - ngCentre; + diffLeft = ngLeft - ngCentre; - if(diffRight * diff2 >= 0.f) { + if(diffRight * diffLeft >= 0.f) { float val = (ngRight + ngLeft) / 2.f; float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } @@ -649,18 +596,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest0[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest1[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } } @@ -677,18 +613,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); if(gridMax > 0.f) { - if(showMotion) { - float blend = gridMax * blendFactor; - - if(!showOnlyMask) { - // if showMotion is enabled colourize the pixel - nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - nonGreenDest0[j + offsX] = greenDest[j + offsX] = 0.f; - } else { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 1000.f + 25000.f * blend; - } - } - + paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } From 9bd874eb8c76cce79ebce96d0a60f9fc9c3279a4 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 6 Dec 2016 22:52:36 +0100 Subject: [PATCH 059/181] pixelshift: simplified and increased readability of code --- rtengine/pixelshift.cc | 117 +++++++++++++++++++++-------------------- rtengine/rt_math.h | 13 ----- 2 files changed, 59 insertions(+), 71 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index b30918e37..e3ababc85 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -275,14 +275,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float nRead; float eperIsoModel; + int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); if(model.find("K-3") != string::npos) { - nRead = nReadK3II[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + nRead = nReadK3II[nReadIndex]; eperIsoModel = ePerIsoK3II; } else if(model.find("K-1") != string::npos) { - nRead = nReadK1[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + nRead = nReadK1[nReadIndex]; eperIsoModel = ePerIsoK1; } else { - nRead = nReadK70[static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f))]; + nRead = nReadK70[nReadIndex]; eperIsoModel = ePerIsoK70; } @@ -364,90 +365,90 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); + }); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[2] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[3] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); + }); } else if(gridSize == 7) { // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[1] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[2] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[3] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[4] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - greenDifMax[5] = max(greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); + }); } } @@ -471,31 +472,31 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det skipNext = skip; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2]); + }); + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]); + }); + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = max(greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - ); - gridMax = max(greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]); + }); + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); } @@ -529,10 +530,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } @@ -541,10 +542,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - gridMax = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } @@ -557,11 +558,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diffLeft = ngLeft - ngCentre; if(diffRight * diffLeft >= 0.f) { - float val = (ngRight + ngLeft) / 2.f; - float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + float avg = (ngRight + ngLeft) / 2.f; + float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } } @@ -573,11 +574,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diffLeft = ngLeft - ngCentre; if(diffRight * diffLeft >= 0.f) { - float val = (ngRight + ngLeft) / 2.f; - float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + float avg = (ngRight + ngLeft) / 2.f; + float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } @@ -592,11 +593,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float diffBottom = ngBottom - ngCentre; if(diffTop * diffBottom >= 0.f) { - float val = (ngTop + ngBottom) / 2.f; - float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + float avg = (ngTop + ngBottom) / 2.f; + float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); continue; } } @@ -609,11 +610,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det diffBottom = ngBottom - ngCentre; if(diffTop * diffBottom >= 0.f) { - float val = (ngTop + ngBottom) / 2.f; - float gridMax = nonGreenDiff(ngCentre, val, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + float avg = (ngTop + ngBottom) / 2.f; + float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - if(gridMax > 0.f) { - paintMotionMask(j + offsX, showMotion, gridMax, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; } } diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 3abeae843..67d883080 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -74,19 +74,6 @@ inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d) return std::max(d, std::max(c, std::max(a, b))); } -template -inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d, const _Tp& e) -{ - return max(max(a,b,c),std::max(d,e)); -} - -template -inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d, const _Tp& e, const _Tp& f, const _Tp& g) -{ - return max(max(a,b,c,d),max(e,f,g)); -} - - template inline _Tp intp(_Tp a, _Tp b, _Tp c) { From 3870f6d35d0d4098d232edae42412354cf9f2144 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 15 Dec 2016 18:45:34 +0100 Subject: [PATCH 060/181] pixelshift: Simplified code and added 2 checkboxes --- rtdata/languages/default | 4 + rtengine/pixelshift.cc | 857 ++++++++++++++++++++++++++++++++----- rtengine/procevents.h | 2 + rtengine/procparams.cc | 29 +- rtengine/procparams.h | 2 + rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 4 +- rtgui/bayerprocess.cc | 67 ++- rtgui/bayerprocess.h | 10 +- rtgui/paramsedited.cc | 15 +- rtgui/paramsedited.h | 2 + 12 files changed, 875 insertions(+), 121 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index db55dde96..a6ec3c098 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -690,6 +690,8 @@ HISTORY_MSG_455;EvPixelShiftNonGreenVertical HISTORY_MSG_456;EvPixelShiftNonGreenCross HISTORY_MSG_457;EvPixelShiftStddevFactorRed HISTORY_MSG_458;EvPixelShiftStddevFactorBlue +HISTORY_MSG_459;EvPixelShiftNonGreenCross2 +HISTORY_MSG_460;EvPixelShiftNonGreenAmaze HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1672,6 +1674,8 @@ TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross +TP_RAW_PIXELSHIFTNONGREENCROSS2;Check red/blue experimental +TP_RAW_PIXELSHIFTNONGREENAMAZE;Check red/blue amaze TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index e3ababc85..ebb9d2c64 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -126,7 +126,7 @@ void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMas using namespace std; using namespace rtengine; - +#ifdef __OLDPS__ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) { @@ -276,6 +276,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoModel; int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); + if(model.find("K-3") != string::npos) { nRead = nReadK3II[nReadIndex]; eperIsoModel = ePerIsoK3II; @@ -347,9 +348,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoNonGreen2 = eperIso / scale_mul[2]; float stddevFactorNonGreen0 = stddevFactorRed; float stddevFactorNonGreen2 = stddevFactorBlue; + bool blueRow = false; if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; std::swap(nonGreenDest0, nonGreenDest1); std::swap(scaleNonGreen0, scaleNonGreen2); std::swap(eperIsoNonGreen0, eperIsoNonGreen2); @@ -366,89 +369,89 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } else if(gridSize == 7) { // compute maximum of differences for first six columns of 7x7 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } } @@ -473,29 +476,29 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); } @@ -528,29 +531,102 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; - float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + float diffRight = ngRight - ngCentre; + float diffLeft = ngLeft - ngCentre; + float diffHorNg0 = -1.f; - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); - continue; + if(diffRight * diffLeft >= 0.f) { + float avg = (ngRight + ngLeft) / 2.f; + diffHorNg0 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + +// if(diff > 0.f) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } } + float diffHorNg1 = -1.f; ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; - ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + diffRight = ngRight - ngCentre; + diffLeft = ngLeft - ngCentre; - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); - continue; + if(diffRight * diffLeft >= 0.f) { + float avg = (ngRight + ngLeft) / 2.f; + float diffHorNg1 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + +// if(diff > 0.f) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); +// continue; +// } } + + if( diffHorNg0 * diffHorNg1 < 0.f) { + paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + continue; + + } + +// bool motion = false; +// float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; +// float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; +// float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; +// float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; +// float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; +// float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); +// +// if(diff > 0.f) { +// motion = true; +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } +// +// ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; +// ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; +// ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; +// ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; +// ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; +// diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); +// +//// if(diff > 0.f) { +// if((diff > 0.f && !motion) || (diff <= 0.f && motion) ) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); +// continue; +// } } if(adaptive && checkNonGreenHorizontal) { +// float lg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j - 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j]) / 2.f; +// float cg = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; +// float rg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j + 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j + 2]) / 2.f; +// +// float lr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) - 1]; +// float cr = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; +// float rr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) + 1]; +// +// float lb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; +// float cb = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; +// float rb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; +// +// if(blueRow) { +// std::swap(lr, lb); +// std::swap(cr, cb); +// std::swap(rr, rb); +// } +// +// float lh = Color::rgb2h(lr, lg, lb); +// float ch = Color::rgb2h(cr, cg, cb); +// float rh = Color::rgb2h(rr, rg, rb); +// +// float lHueDiff = lh - ch; +// float rHueDiff = rh - ch; +// if(lHueDiff * rHueDiff > 0.f) { +// if(std::fabs(lHueDiff) > 0.5f && std::fabs(rHueDiff) > 0.5f/* && std::fabs(lHueDiff) < 3.f && std::fabs(rHueDiff) < 3.f*/) { +// paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } +// } float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; @@ -637,3 +713,592 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det plistener->setProgress(1.0); } } +#else +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross, bool checkNonGreenAmaze, bool checkNonGreenCross2) +{ + + BENCHFUN + + static const float nReadK3II[] = { 3.4f, // ISO 100 + 3.1f, // ISO 125 + 2.5f, // ISO 160 + 2.5f, // ISO 200 + 2.5f, // ISO 250 + 2.5f, // ISO 320 + 2.3f, // ISO 400 + 2.5f, // ISO 500 + 2.3f, // ISO 640 + 2.3f, // ISO 800 + 2.4f, // ISO 1000 + 2.3f, // ISO 1250 + 1.75f, // ISO 1600 + 1.75f, // ISO 2000 + 1.75f, // ISO 2500 + 1.75f, // ISO 3200 + 1.75f, // ISO 4000 + 1.75f, // ISO 5000 + 1.75f, // ISO 6400 + 1.75f, // ISO 8000 + 1.75f, // ISO 10000 + 1.5f, // ISO 12800 + 1.5f, // ISO 16000 + 1.5f, // ISO 20000 + 1.5f, // ISO 25600 + 1.5f, // ISO 32000 + 1.5f, // ISO 40000 + 1.5f, // ISO 51200 + 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK3II = 0.35f; + + static const float nReadK1[] = { 3.45f, // ISO 100 + 3.15f, // ISO 125 + 3.45f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 2.7f, // ISO 400 + 2.7f, // ISO 500 + 2.7f, // ISO 640 + 2.5f, // ISO 800 + 2.5f, // ISO 1000 + 2.5f, // ISO 1250 + 2.4f, // ISO 1600 + 2.4f, // ISO 2000 + 2.4f, // ISO 2500 + 2.4f, // ISO 3200 + 2.4f, // ISO 4000 + 2.4f, // ISO 5000 + 2.4f, // ISO 6400 + 2.4f, // ISO 8000 + 2.4f, // ISO 10000 + 2.4f, // ISO 12800 + 2.4f, // ISO 16000 + 2.4f, // ISO 20000 + 2.4f, // ISO 25600 + 2.4f, // ISO 32000 + 2.4f, // ISO 40000 + 2.4f, // ISO 51200 + 2.4f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK1 = 0.75f; + + static const float nReadK70[] = { 3.0f, // ISO 100 + 3.0f, // ISO 125 + 3.0f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 3.0f, // ISO 400 + 3.0f, // ISO 500 + 3.0f, // ISO 640 + 3.0f, // ISO 800 + 3.0f, // ISO 1000 + 3.0f, // ISO 1250 + 3.0f, // ISO 1600 + 3.0f, // ISO 2000 + 3.0f, // ISO 2500 + 3.0f, // ISO 3200 + 3.0f, // ISO 4000 + 3.0f, // ISO 5000 + 3.0f, // ISO 6400 + 3.0f, // ISO 8000 + 3.0f, // ISO 10000 + 3.0f, // ISO 12800 + 3.0f, // ISO 16000 + 3.0f, // ISO 20000 + 3.0f, // ISO 25600 + 3.0f, // ISO 32000 + 3.0f, // ISO 40000 + 3.0f, // ISO 51200 + 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK70 = 0.5f; + + if (plistener) { + plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); + plistener->setProgress(0.0); + } + + + const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); + int gridSize = 1; + + switch (gridSize_) { + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: + gridSize = 1; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: + gridSize = 3; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: + gridSize = 5; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: + gridSize = 7; + } + + // Lookup table for non adaptive (slider) mode + LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); + + if(detectMotion && !adaptive) { + const float lutStrength = 2.f; + log2Lut[0] = 0; + + for(int i = 2; i < 65536; i += 2) { + log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; + } + } + + const float scaleGreen = 1.f / scale_mul[1]; + + float nRead; + float eperIsoModel; + + int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); + + if(model.find("K-3") != string::npos) { + nRead = nReadK3II[nReadIndex]; + eperIsoModel = ePerIsoK3II; + } else if(model.find("K-1") != string::npos) { + nRead = nReadK1[nReadIndex]; + eperIsoModel = ePerIsoK1; + } else { + nRead = nReadK70[nReadIndex]; + eperIsoModel = ePerIsoK70; + } + + nRead *= pow(2.f, nreadIso); + eperIsoModel *= pow(2.f, eperIso); + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); + + float eperIsoRed = eperIso / scale_mul[0]; + float eperIsoGreen = eperIso * scaleGreen; + float eperIsoBlue = eperIso / scale_mul[2]; + + printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); + + prnu /= 100.f; + stddevFactorGreen *= stddevFactorGreen; + stddevFactorRed *= stddevFactorRed; + stddevFactorBlue *= stddevFactorBlue; + + + nRead *= nRead; + + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel + float motionThreshold = 1.f - (motion / 100.f); + // For shades of green motion indicators + const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); + + unsigned int offsX = 0, offsY = 0; + + // We have to adjust the offsets for the selected subframe we use for areas with motion + switch (frame) { + case 0: + offsX = offsY = 0; + break; + + case 1: + offsX = 0; + offsY = 1; + break; + + case 2: + offsX = offsY = 1; + break; + + case 3: + offsX = 1; + offsY = 0; + } + + const float thresh = adaptive ? 0.f : motionThreshold; + array2D psRed(winw, winh); + array2D psG1(winw, winh); + array2D psG2(winw, winh); + array2D psBlue(winw, winh); + +// fill channels psRed, psG1, psG2 and psBlue +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + float *greenDest1 = psG1[i]; + float *greenDest2 = psG2[i]; + float *nonGreenDest0 = psRed[i]; + float *nonGreenDest1 = psBlue[i]; + int j = winx + 1; + int c = FC(i, j); + + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + std::swap(nonGreenDest0, nonGreenDest1); + std::swap(greenDest1, greenDest2); + } + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); + offset ^= 1; // 0 => 1 or 1 => 0 + + for(; j < winw - 1; ++j) { + offset ^= 1; // 0 => 1 or 1 => 0 + + // store the values from the 4 frames into 4 different temporary planes + greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j]; + greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1]; + nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + } + } + +// now that the temporary planes are filled for easy access we do the motion detection + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + int j = winx + border - offsX; + + float greenDifMax[gridSize]; // Here we store the maximum differences per Column + + // green channel motion detection checks the grid around the pixel for differences in green channels + if(detectMotion || adaptive) { + if(gridSize == 3) { + // compute maximum of differences for first two columns of 3x3 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } else if(gridSize == 5) { + // compute maximum of differences for first four columns of 5x5 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } else if(gridSize == 7) { + // compute maximum of differences for first six columns of 7x7 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + } + + } + + + // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 + int lastIndex = gridSize - 1; + float korr = 0.f; + int c = FC(i, j); + bool blueRow = false; + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; + } + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); +// offset ^= 1; // 0 => 1 or 1 => 0 + + for(; j < winw - (border + offsX); ++j) { + bool greenFromPs = false; + offset ^= 1; // 0 => 1 or 1 => 0 + + if(detectMotion || adaptive) { + bool skipNext = false; + float gridMax; + + if(gridSize < 2) { + // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps + gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); + skipNext = skip; + } else if(gridSize == 3) { + // compute maximum of differences for third column of 3x3 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); + } else if(gridSize == 5) { + // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); + } else if(gridSize == 7) { + // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); + } + + + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + + // increase motion detection dependent on brightness + if(!adaptive) { + korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; + } + + if (gridMax > thresh - korr) { + if((offset == (frame&1)) && checkNonGreenVertical) { + if(frame > 1) { + green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; + } else { + green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; + } + continue; + } else { + // at least one of the tested green pixels of the grid is detected as motion + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + } + + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + } + + if(adaptive && checkNonGreenCross) { + // check red cross + float redTop = psRed[i - 1][ j ]; + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + float redBottom = psRed[i + 1][ j ]; + float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + continue; + } + + // check blue cross + float blueTop = psBlue[i - 1][ j ]; + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + float blueBottom = psBlue[i + 1][ j ]; + float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + continue; + } + } + + if(adaptive && checkNonGreenHorizontal) { + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + + float redDiffLeft = redLeft - redCentre; + float redDiffRight = redRight - redCentre; + + if(redDiffLeft * redDiffRight >= 0.f) { + float redAvg = (redRight + redLeft) / 2.f; + float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + if(redDiffHor > 0.f) { + paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); + continue; + } + } + + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + + float blueDiffLeft = blueLeft - blueCentre; + float blueDiffRight = blueRight - blueCentre; + + if(blueDiffLeft * blueDiffRight >= 0.f) { + float blueAvg = (blueRight + blueLeft) / 2.f; + float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffHor > 0.f) { + paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, redDest, blueDest, greenDest); + continue; + } + } + } + + if(adaptive && checkNonGreenVertical) { + // check red vertically + float redTop = psRed[i - 1][ j ]; + float redCentre = psRed[ i ][ j ]; + float redBottom = psRed[i + 1][ j ]; + + float redDiffTop = redTop - redCentre; + float redDiffBottom = redBottom - redCentre; + + if(redDiffTop * redDiffBottom >= 0.f) { + float redAvg = (redTop + redBottom) / 2.f; + float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + continue; + } + } + + // check blue vertically + float blueTop = psBlue[i - 1][ j ]; + float blueCentre = psBlue[ i ][ j ]; + float blueBottom = psBlue[i + 1][ j ]; + + float blueDiffTop = blueTop - blueCentre; + float blueDiffBottom = blueBottom - blueCentre; + + if(blueDiffTop * blueDiffBottom >= 0.f) { + float blueAvg = (blueTop + blueBottom) / 2.f; + float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + continue; + } + } + } + + if(adaptive && checkNonGreenAmaze) { + // check current pixel against amaze + float redCentre = psRed[ i ][ j ]; + float redAmaze = red[i + offsY][j + offsX]; + + float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + if(redDiffAmaze > 0.f) { + paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); + continue; + } + + float blueCentre = psBlue[ i ][ j ]; + float blueAmaze = blue[i + offsY][j + offsX]; + + float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + if(blueDiffAmaze > 0.f) { + paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); + continue; + } + } + + if(adaptive && checkNonGreenCross2) { // for experiments + + } + + + if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else { + // no motion detected, replace the a priori demosaiced values by the pixelshift combined values + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } + } + } + + if(plistener) { + plistener->setProgress(1.0); + } +} +#endif \ No newline at end of file diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 747d60cb4..43a9ae020 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -485,6 +485,8 @@ enum ProcEvent { EvPixelShiftNonGreenCross = 455, EvPixelShiftStddevFactorRed = 456, EvPixelShiftStddevFactorBlue = 457, + EvPixelShiftNonGreenCross2 = 458, + EvPixelShiftNonGreenAmaze = 459, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 5fd8b17ff..65fffb175 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -899,6 +899,8 @@ void RAWParams::setDefaults() bayersensor.pixelShiftNonGreenHorizontal = false; bayersensor.pixelShiftNonGreenVertical = false; bayersensor.pixelShiftNonGreenCross = false; + bayersensor.pixelShiftNonGreenCross2 = false; + bayersensor.pixelShiftNonGreenAmaze = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3434,7 +3436,13 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); } - //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross2) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross2", raw.bayersensor.pixelShiftNonGreenCross2 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenAmaze) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenAmaze", raw.bayersensor.pixelShiftNonGreenAmaze ); + } if (!pedited || pedited->raw.xtranssensor.method) { keyFile.set_string ("RAW X-Trans", "Method", raw.xtranssensor.method ); @@ -7604,7 +7612,21 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross2")) { + raw.bayersensor.pixelShiftNonGreenCross2 = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross2"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenAmaze")) { + raw.bayersensor.pixelShiftNonGreenAmaze = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenAmaze"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = true; + } + } } // load X-Trans sensors' raw settings @@ -8057,8 +8079,9 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross + && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 + && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance - //&& raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method && raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps && raw.xtranssensor.blackred == other.raw.xtranssensor.blackred diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 0c06fa315..fa46f3ac8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1218,6 +1218,8 @@ public: bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 36b78aa20..c95009a80 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2028,7 +2028,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); + pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross, raw.bayersensor.pixelShiftNonGreenAmaze, raw.bayersensor.pixelShiftNonGreenCross2); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index f80efeacf..0b42a37a1 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross, bool checkNonGreenAmaze, bool checkNonGreenCross2); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 08e3d2520..d61065cb8 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -484,7 +484,9 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftNonGreenVertical DEMOSAIC, // EvPixelShiftNonGreenCross DEMOSAIC, // EvPixelShiftStddevFactorRed - DEMOSAIC // EvPixelShiftStddevFactorBlue + DEMOSAIC, // EvPixelShiftStddevFactorBlue + DEMOSAIC, // EvPixelShiftNonGreenCross2 + DEMOSAIC // EvPixelShiftNonGreenAmaze }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 297a9b65b..98054f3fa 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -95,6 +95,12 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNonGreenCross = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); + pixelShiftNonGreenCross2 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); + + pixelShiftNonGreenAmaze = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); + pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); @@ -217,7 +223,8 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); - //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); + pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); + pixelShiftNonGreenAmazeconn = pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true); } @@ -251,7 +258,8 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); - //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); + pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); @@ -282,6 +290,8 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); + pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); @@ -355,6 +365,8 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -389,6 +401,8 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); } } @@ -637,6 +651,8 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenAmaze->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -704,21 +720,42 @@ void BayerProcess::pixelShiftNonGreenCrossChanged () } } - -/*void BayerProcess::allEnhanceChanged () +void BayerProcess::pixelShiftNonGreenCross2Changed () { if (batchMode) { - if (allEnhance->get_inconsistent()) { - allEnhance->set_inconsistent (false); - allEnhconn.block (true); - allEnhance->set_active (false); - allEnhconn.block (false); + if (pixelShiftNonGreenCross2->get_inconsistent()) { + pixelShiftNonGreenCross2->set_inconsistent (false); + pixelShiftNonGreenCross2conn.block (true); + pixelShiftNonGreenCross2->set_active (false); + pixelShiftNonGreenCross2conn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenCross2->set_inconsistent (true); } - else if (lastALLen) - allEnhance->set_inconsistent (true); - lastALLen = allEnhance->get_active (); + lastDCBen = pixelShiftNonGreenCross2->get_active (); } - if (listener) - listener->panelChanged (EvDemosaicALLEnhanced, allEnhance->get_active()?M("GENERAL_ENABLED"):M("GENERAL_DISABLED")); -}*/ + + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenCross2, pixelShiftNonGreenCross2->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftNonGreenAmazeChanged () +{ + if (batchMode) { + if (pixelShiftNonGreenAmaze->get_inconsistent()) { + pixelShiftNonGreenAmaze->set_inconsistent (false); + pixelShiftNonGreenAmazeconn.block (true); + pixelShiftNonGreenAmaze->set_active (false); + pixelShiftNonGreenAmazeconn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenAmaze->set_inconsistent (true); + } + + lastDCBen = pixelShiftNonGreenAmaze->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index ca560ad89..5911a1267 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -50,6 +50,8 @@ protected: Gtk::CheckButton* pixelShiftNonGreenHorizontal; Gtk::CheckButton* pixelShiftNonGreenVertical; Gtk::CheckButton* pixelShiftNonGreenCross; + Gtk::CheckButton* pixelShiftNonGreenCross2; + Gtk::CheckButton* pixelShiftNonGreenAmaze; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; @@ -59,7 +61,10 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, + pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, + pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn, + pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn; public: BayerProcess (); @@ -80,7 +85,8 @@ public: void pixelShiftNonGreenHorizontalChanged(); void pixelShiftNonGreenVerticalChanged(); void pixelShiftNonGreenCrossChanged(); - //void allEnhanceChanged(); + void pixelShiftNonGreenCross2Changed(); + void pixelShiftNonGreenAmazeChanged(); }; #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index f433ec639..e91ea6c26 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -384,6 +384,8 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftNonGreenHorizontal = v; raw.bayersensor.pixelShiftNonGreenVertical = v; raw.bayersensor.pixelShiftNonGreenCross = v; + raw.bayersensor.pixelShiftNonGreenCross2 = v; + raw.bayersensor.pixelShiftNonGreenAmaze = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -894,6 +896,8 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; + raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; + raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2362,7 +2366,14 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; } - //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; + if (raw.bayersensor.pixelShiftNonGreenCross2) { + toEdit.raw.bayersensor.pixelShiftNonGreenCross2 = mods.raw.bayersensor.pixelShiftNonGreenCross2; + } + + if (raw.bayersensor.pixelShiftNonGreenAmaze) { + toEdit.raw.bayersensor.pixelShiftNonGreenAmaze = mods.raw.bayersensor.pixelShiftNonGreenAmaze; + } + if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; } @@ -2875,7 +2886,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b4af2ad05..b4da65ac1 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -706,6 +706,8 @@ public: bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; //bool allEnhance; bool greenEq; From bbabe9bca88fa70a0a1b96e9cb0e84629f6001df Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 15 Dec 2016 18:54:42 +0100 Subject: [PATCH 061/181] Revert "pixelshift: Simplified code and added 2 checkboxes" This reverts commit 3870f6d35d0d4098d232edae42412354cf9f2144. --- rtdata/languages/default | 4 - rtengine/pixelshift.cc | 855 +++++-------------------------------- rtengine/procevents.h | 2 - rtengine/procparams.cc | 29 +- rtengine/procparams.h | 2 - rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 4 +- rtgui/bayerprocess.cc | 67 +-- rtgui/bayerprocess.h | 10 +- rtgui/paramsedited.cc | 15 +- rtgui/paramsedited.h | 2 - 12 files changed, 120 insertions(+), 874 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index a6ec3c098..db55dde96 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -690,8 +690,6 @@ HISTORY_MSG_455;EvPixelShiftNonGreenVertical HISTORY_MSG_456;EvPixelShiftNonGreenCross HISTORY_MSG_457;EvPixelShiftStddevFactorRed HISTORY_MSG_458;EvPixelShiftStddevFactorBlue -HISTORY_MSG_459;EvPixelShiftNonGreenCross2 -HISTORY_MSG_460;EvPixelShiftNonGreenAmaze HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1674,8 +1672,6 @@ TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross -TP_RAW_PIXELSHIFTNONGREENCROSS2;Check red/blue experimental -TP_RAW_PIXELSHIFTNONGREENAMAZE;Check red/blue amaze TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index ebb9d2c64..e3ababc85 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -126,7 +126,7 @@ void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMas using namespace std; using namespace rtengine; -#ifdef __OLDPS__ + void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) { @@ -276,7 +276,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoModel; int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); - if(model.find("K-3") != string::npos) { nRead = nReadK3II[nReadIndex]; eperIsoModel = ePerIsoK3II; @@ -348,11 +347,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoNonGreen2 = eperIso / scale_mul[2]; float stddevFactorNonGreen0 = stddevFactorRed; float stddevFactorNonGreen2 = stddevFactorBlue; - bool blueRow = false; if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels - blueRow = true; std::swap(nonGreenDest0, nonGreenDest1); std::swap(scaleNonGreen0, scaleNonGreen2); std::swap(eperIsoNonGreen0, eperIsoNonGreen2); @@ -369,89 +366,89 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } else if(gridSize == 7) { // compute maximum of differences for first six columns of 7x7 grid greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); } } @@ -476,29 +473,29 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); } @@ -531,102 +528,29 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float diffRight = ngRight - ngCentre; - float diffLeft = ngLeft - ngCentre; - float diffHorNg0 = -1.f; + float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; + float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; + float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - diffHorNg0 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - -// if(diff > 0.f) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + continue; } - float diffHorNg1 = -1.f; ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - diffRight = ngRight - ngCentre; - diffLeft = ngLeft - ngCentre; + ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; + ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; + diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - float diffHorNg1 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - -// if(diff > 0.f) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); -// continue; -// } - } - - if( diffHorNg0 * diffHorNg1 < 0.f) { - paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + if(diff > 0.f) { + paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); continue; - } - -// bool motion = false; -// float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; -// float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; -// float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; -// float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; -// float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; -// float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); -// -// if(diff > 0.f) { -// motion = true; -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } -// -// ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; -// ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; -// ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; -// ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; -// ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; -// diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); -// -//// if(diff > 0.f) { -// if((diff > 0.f && !motion) || (diff <= 0.f && motion) ) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); -// continue; -// } } if(adaptive && checkNonGreenHorizontal) { -// float lg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j - 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j]) / 2.f; -// float cg = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; -// float rg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j + 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j + 2]) / 2.f; -// -// float lr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) - 1]; -// float cr = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; -// float rr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) + 1]; -// -// float lb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; -// float cb = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; -// float rb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; -// -// if(blueRow) { -// std::swap(lr, lb); -// std::swap(cr, cb); -// std::swap(rr, rb); -// } -// -// float lh = Color::rgb2h(lr, lg, lb); -// float ch = Color::rgb2h(cr, cg, cb); -// float rh = Color::rgb2h(rr, rg, rb); -// -// float lHueDiff = lh - ch; -// float rHueDiff = rh - ch; -// if(lHueDiff * rHueDiff > 0.f) { -// if(std::fabs(lHueDiff) > 0.5f && std::fabs(rHueDiff) > 0.5f/* && std::fabs(lHueDiff) < 3.f && std::fabs(rHueDiff) < 3.f*/) { -// paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } -// } float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; @@ -713,592 +637,3 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det plistener->setProgress(1.0); } } -#else -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross, bool checkNonGreenAmaze, bool checkNonGreenCross2) -{ - - BENCHFUN - - static const float nReadK3II[] = { 3.4f, // ISO 100 - 3.1f, // ISO 125 - 2.5f, // ISO 160 - 2.5f, // ISO 200 - 2.5f, // ISO 250 - 2.5f, // ISO 320 - 2.3f, // ISO 400 - 2.5f, // ISO 500 - 2.3f, // ISO 640 - 2.3f, // ISO 800 - 2.4f, // ISO 1000 - 2.3f, // ISO 1250 - 1.75f, // ISO 1600 - 1.75f, // ISO 2000 - 1.75f, // ISO 2500 - 1.75f, // ISO 3200 - 1.75f, // ISO 4000 - 1.75f, // ISO 5000 - 1.75f, // ISO 6400 - 1.75f, // ISO 8000 - 1.75f, // ISO 10000 - 1.5f, // ISO 12800 - 1.5f, // ISO 16000 - 1.5f, // ISO 20000 - 1.5f, // ISO 25600 - 1.5f, // ISO 32000 - 1.5f, // ISO 40000 - 1.5f, // ISO 51200 - 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK3II = 0.35f; - - static const float nReadK1[] = { 3.45f, // ISO 100 - 3.15f, // ISO 125 - 3.45f, // ISO 160 - 3.0f, // ISO 200 - 3.0f, // ISO 250 - 3.0f, // ISO 320 - 2.7f, // ISO 400 - 2.7f, // ISO 500 - 2.7f, // ISO 640 - 2.5f, // ISO 800 - 2.5f, // ISO 1000 - 2.5f, // ISO 1250 - 2.4f, // ISO 1600 - 2.4f, // ISO 2000 - 2.4f, // ISO 2500 - 2.4f, // ISO 3200 - 2.4f, // ISO 4000 - 2.4f, // ISO 5000 - 2.4f, // ISO 6400 - 2.4f, // ISO 8000 - 2.4f, // ISO 10000 - 2.4f, // ISO 12800 - 2.4f, // ISO 16000 - 2.4f, // ISO 20000 - 2.4f, // ISO 25600 - 2.4f, // ISO 32000 - 2.4f, // ISO 40000 - 2.4f, // ISO 51200 - 2.4f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK1 = 0.75f; - - static const float nReadK70[] = { 3.0f, // ISO 100 - 3.0f, // ISO 125 - 3.0f, // ISO 160 - 3.0f, // ISO 200 - 3.0f, // ISO 250 - 3.0f, // ISO 320 - 3.0f, // ISO 400 - 3.0f, // ISO 500 - 3.0f, // ISO 640 - 3.0f, // ISO 800 - 3.0f, // ISO 1000 - 3.0f, // ISO 1250 - 3.0f, // ISO 1600 - 3.0f, // ISO 2000 - 3.0f, // ISO 2500 - 3.0f, // ISO 3200 - 3.0f, // ISO 4000 - 3.0f, // ISO 5000 - 3.0f, // ISO 6400 - 3.0f, // ISO 8000 - 3.0f, // ISO 10000 - 3.0f, // ISO 12800 - 3.0f, // ISO 16000 - 3.0f, // ISO 20000 - 3.0f, // ISO 25600 - 3.0f, // ISO 32000 - 3.0f, // ISO 40000 - 3.0f, // ISO 51200 - 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK70 = 0.5f; - - if (plistener) { - plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); - plistener->setProgress(0.0); - } - - - const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); - int gridSize = 1; - - switch (gridSize_) { - case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: - case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: - gridSize = 1; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: - gridSize = 3; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: - gridSize = 5; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: - gridSize = 7; - } - - // Lookup table for non adaptive (slider) mode - LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); - - if(detectMotion && !adaptive) { - const float lutStrength = 2.f; - log2Lut[0] = 0; - - for(int i = 2; i < 65536; i += 2) { - log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; - } - } - - const float scaleGreen = 1.f / scale_mul[1]; - - float nRead; - float eperIsoModel; - - int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); - - if(model.find("K-3") != string::npos) { - nRead = nReadK3II[nReadIndex]; - eperIsoModel = ePerIsoK3II; - } else if(model.find("K-1") != string::npos) { - nRead = nReadK1[nReadIndex]; - eperIsoModel = ePerIsoK1; - } else { - nRead = nReadK70[nReadIndex]; - eperIsoModel = ePerIsoK70; - } - - nRead *= pow(2.f, nreadIso); - eperIsoModel *= pow(2.f, eperIso); - eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); - - float eperIsoRed = eperIso / scale_mul[0]; - float eperIsoGreen = eperIso * scaleGreen; - float eperIsoBlue = eperIso / scale_mul[2]; - - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); - - prnu /= 100.f; - stddevFactorGreen *= stddevFactorGreen; - stddevFactorRed *= stddevFactorRed; - stddevFactorBlue *= stddevFactorBlue; - - - nRead *= nRead; - - // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel - float motionThreshold = 1.f - (motion / 100.f); - // For shades of green motion indicators - const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); - - unsigned int offsX = 0, offsY = 0; - - // We have to adjust the offsets for the selected subframe we use for areas with motion - switch (frame) { - case 0: - offsX = offsY = 0; - break; - - case 1: - offsX = 0; - offsY = 1; - break; - - case 2: - offsX = offsY = 1; - break; - - case 3: - offsX = 1; - offsY = 0; - } - - const float thresh = adaptive ? 0.f : motionThreshold; - array2D psRed(winw, winh); - array2D psG1(winw, winh); - array2D psG2(winw, winh); - array2D psBlue(winw, winh); - -// fill channels psRed, psG1, psG2 and psBlue -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - - for(int i = winy + 1; i < winh - 1; ++i) { - float *greenDest1 = psG1[i]; - float *greenDest2 = psG2[i]; - float *nonGreenDest0 = psRed[i]; - float *nonGreenDest1 = psBlue[i]; - int j = winx + 1; - int c = FC(i, j); - - if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { - // row with blue pixels => swap destination pointers for non green pixels - std::swap(nonGreenDest0, nonGreenDest1); - std::swap(greenDest1, greenDest2); - } - - // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); - offset ^= 1; // 0 => 1 or 1 => 0 - - for(; j < winw - 1; ++j) { - offset ^= 1; // 0 => 1 or 1 => 0 - - // store the values from the 4 frames into 4 different temporary planes - greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j]; - greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1]; - nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - } - } - -// now that the temporary planes are filled for easy access we do the motion detection - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - - for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { - float *greenDest = green[i + offsY]; - float *redDest = red[i + offsY]; - float *blueDest = blue[i + offsY]; - int j = winx + border - offsX; - - float greenDifMax[gridSize]; // Here we store the maximum differences per Column - - // green channel motion detection checks the grid around the pixel for differences in green channels - if(detectMotion || adaptive) { - if(gridSize == 3) { - // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - } else if(gridSize == 5) { - // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - } else if(gridSize == 7) { - // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - } - - } - - - // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 - int lastIndex = gridSize - 1; - float korr = 0.f; - int c = FC(i, j); - bool blueRow = false; - if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { - // row with blue pixels => swap destination pointers for non green pixels - blueRow = true; - } - - // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); -// offset ^= 1; // 0 => 1 or 1 => 0 - - for(; j < winw - (border + offsX); ++j) { - bool greenFromPs = false; - offset ^= 1; // 0 => 1 or 1 => 0 - - if(detectMotion || adaptive) { - bool skipNext = false; - float gridMax; - - if(gridSize < 2) { - // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps - gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); - skipNext = skip; - } else if(gridSize == 3) { - // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); - } else if(gridSize == 5) { - // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); - } else if(gridSize == 7) { - // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); - } - - - // adjust index for next column - lastIndex ++; - lastIndex = lastIndex == gridSize ? 0 : lastIndex; - - // increase motion detection dependent on brightness - if(!adaptive) { - korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; - } - - if (gridMax > thresh - korr) { - if((offset == (frame&1)) && checkNonGreenVertical) { - if(frame > 1) { - green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; - } else { - green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; - } - continue; - } else { - // at least one of the tested green pixels of the grid is detected as motion - paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); - - if(skipNext) { - // treat the horizontally next pixel also as motion - j++; - paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); - } - - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; - } - } - } - - if(adaptive && checkNonGreenCross) { - // check red cross - float redTop = psRed[i - 1][ j ]; - float redLeft = psRed[ i ][j - 1]; - float redCentre = psRed[ i ][ j ]; - float redRight = psRed[ i ][j + 1]; - float redBottom = psRed[i + 1][ j ]; - float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - - if(redDiff > 0.f) { - paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); - continue; - } - - // check blue cross - float blueTop = psBlue[i - 1][ j ]; - float blueLeft = psBlue[ i ][j - 1]; - float blueCentre = psBlue[ i ][ j ]; - float blueRight = psBlue[ i ][j + 1]; - float blueBottom = psBlue[i + 1][ j ]; - float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - - if(blueDiff > 0.f) { - paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); - continue; - } - } - - if(adaptive && checkNonGreenHorizontal) { - float redLeft = psRed[ i ][j - 1]; - float redCentre = psRed[ i ][ j ]; - float redRight = psRed[ i ][j + 1]; - - float redDiffLeft = redLeft - redCentre; - float redDiffRight = redRight - redCentre; - - if(redDiffLeft * redDiffRight >= 0.f) { - float redAvg = (redRight + redLeft) / 2.f; - float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(redDiffHor > 0.f) { - paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); - continue; - } - } - - float blueLeft = psBlue[ i ][j - 1]; - float blueCentre = psBlue[ i ][ j ]; - float blueRight = psBlue[ i ][j + 1]; - - float blueDiffLeft = blueLeft - blueCentre; - float blueDiffRight = blueRight - blueCentre; - - if(blueDiffLeft * blueDiffRight >= 0.f) { - float blueAvg = (blueRight + blueLeft) / 2.f; - float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - - if(blueDiffHor > 0.f) { - paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, redDest, blueDest, greenDest); - continue; - } - } - } - - if(adaptive && checkNonGreenVertical) { - // check red vertically - float redTop = psRed[i - 1][ j ]; - float redCentre = psRed[ i ][ j ]; - float redBottom = psRed[i + 1][ j ]; - - float redDiffTop = redTop - redCentre; - float redDiffBottom = redBottom - redCentre; - - if(redDiffTop * redDiffBottom >= 0.f) { - float redAvg = (redTop + redBottom) / 2.f; - float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - - if(redDiff > 0.f) { - paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); - continue; - } - } - - // check blue vertically - float blueTop = psBlue[i - 1][ j ]; - float blueCentre = psBlue[ i ][ j ]; - float blueBottom = psBlue[i + 1][ j ]; - - float blueDiffTop = blueTop - blueCentre; - float blueDiffBottom = blueBottom - blueCentre; - - if(blueDiffTop * blueDiffBottom >= 0.f) { - float blueAvg = (blueTop + blueBottom) / 2.f; - float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - - if(blueDiff > 0.f) { - paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); - continue; - } - } - } - - if(adaptive && checkNonGreenAmaze) { - // check current pixel against amaze - float redCentre = psRed[ i ][ j ]; - float redAmaze = red[i + offsY][j + offsX]; - - float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(redDiffAmaze > 0.f) { - paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); - continue; - } - - float blueCentre = psBlue[ i ][ j ]; - float blueAmaze = blue[i + offsY][j + offsX]; - - float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - if(blueDiffAmaze > 0.f) { - paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); - continue; - } - } - - if(adaptive && checkNonGreenCross2) { // for experiments - - } - - - if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black - red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; - } else { - // no motion detected, replace the a priori demosaiced values by the pixelshift combined values - red[i + offsY][j + offsX] = psRed[i][j]; - green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; - blue[i + offsY][j + offsX] = psBlue[i][j]; - } - } - } - - if(plistener) { - plistener->setProgress(1.0); - } -} -#endif \ No newline at end of file diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 43a9ae020..747d60cb4 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -485,8 +485,6 @@ enum ProcEvent { EvPixelShiftNonGreenCross = 455, EvPixelShiftStddevFactorRed = 456, EvPixelShiftStddevFactorBlue = 457, - EvPixelShiftNonGreenCross2 = 458, - EvPixelShiftNonGreenAmaze = 459, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 65fffb175..5fd8b17ff 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -899,8 +899,6 @@ void RAWParams::setDefaults() bayersensor.pixelShiftNonGreenHorizontal = false; bayersensor.pixelShiftNonGreenVertical = false; bayersensor.pixelShiftNonGreenCross = false; - bayersensor.pixelShiftNonGreenCross2 = false; - bayersensor.pixelShiftNonGreenAmaze = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3436,13 +3434,7 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); } - if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross2) { - keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross2", raw.bayersensor.pixelShiftNonGreenCross2 ); - } - - if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenAmaze) { - keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenAmaze", raw.bayersensor.pixelShiftNonGreenAmaze ); - } + //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); if (!pedited || pedited->raw.xtranssensor.method) { keyFile.set_string ("RAW X-Trans", "Method", raw.xtranssensor.method ); @@ -7612,21 +7604,7 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross2")) { - raw.bayersensor.pixelShiftNonGreenCross2 = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross2"); - - if (pedited) { - pedited->raw.bayersensor.pixelShiftNonGreenCross2 = true; - } - } - - if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenAmaze")) { - raw.bayersensor.pixelShiftNonGreenAmaze = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenAmaze"); - - if (pedited) { - pedited->raw.bayersensor.pixelShiftNonGreenAmaze = true; - } - } + //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } } // load X-Trans sensors' raw settings @@ -8079,9 +8057,8 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross - && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 - && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance + //&& raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method && raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps && raw.xtranssensor.blackred == other.raw.xtranssensor.blackred diff --git a/rtengine/procparams.h b/rtengine/procparams.h index fa46f3ac8..0c06fa315 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1218,8 +1218,6 @@ public: bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; bool pixelShiftNonGreenCross; - bool pixelShiftNonGreenCross2; - bool pixelShiftNonGreenAmaze; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index c95009a80..36b78aa20 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2028,7 +2028,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross, raw.bayersensor.pixelShiftNonGreenAmaze, raw.bayersensor.pixelShiftNonGreenCross2); + pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 0b42a37a1..f80efeacf 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross, bool checkNonGreenAmaze, bool checkNonGreenCross2); + void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index d61065cb8..08e3d2520 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -484,9 +484,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftNonGreenVertical DEMOSAIC, // EvPixelShiftNonGreenCross DEMOSAIC, // EvPixelShiftStddevFactorRed - DEMOSAIC, // EvPixelShiftStddevFactorBlue - DEMOSAIC, // EvPixelShiftNonGreenCross2 - DEMOSAIC // EvPixelShiftNonGreenAmaze + DEMOSAIC // EvPixelShiftStddevFactorBlue }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 98054f3fa..297a9b65b 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -95,12 +95,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNonGreenCross = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); - pixelShiftNonGreenCross2 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); - pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); - - pixelShiftNonGreenAmaze = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); - pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); - pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); @@ -223,8 +217,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); - pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); - pixelShiftNonGreenAmazeconn = pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true); + //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); } @@ -258,8 +251,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); - pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); + //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); @@ -290,8 +282,6 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); - pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); @@ -365,8 +355,6 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -401,8 +389,6 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); } } @@ -651,8 +637,6 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenAmaze->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -720,42 +704,21 @@ void BayerProcess::pixelShiftNonGreenCrossChanged () } } -void BayerProcess::pixelShiftNonGreenCross2Changed () + +/*void BayerProcess::allEnhanceChanged () { if (batchMode) { - if (pixelShiftNonGreenCross2->get_inconsistent()) { - pixelShiftNonGreenCross2->set_inconsistent (false); - pixelShiftNonGreenCross2conn.block (true); - pixelShiftNonGreenCross2->set_active (false); - pixelShiftNonGreenCross2conn.block (false); - } else if (lastDCBen) { - pixelShiftNonGreenCross2->set_inconsistent (true); + if (allEnhance->get_inconsistent()) { + allEnhance->set_inconsistent (false); + allEnhconn.block (true); + allEnhance->set_active (false); + allEnhconn.block (false); } + else if (lastALLen) + allEnhance->set_inconsistent (true); - lastDCBen = pixelShiftNonGreenCross2->get_active (); + lastALLen = allEnhance->get_active (); } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenCross2, pixelShiftNonGreenCross2->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftNonGreenAmazeChanged () -{ - if (batchMode) { - if (pixelShiftNonGreenAmaze->get_inconsistent()) { - pixelShiftNonGreenAmaze->set_inconsistent (false); - pixelShiftNonGreenAmazeconn.block (true); - pixelShiftNonGreenAmaze->set_active (false); - pixelShiftNonGreenAmazeconn.block (false); - } else if (lastDCBen) { - pixelShiftNonGreenAmaze->set_inconsistent (true); - } - - lastDCBen = pixelShiftNonGreenAmaze->get_active (); - } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} + if (listener) + listener->panelChanged (EvDemosaicALLEnhanced, allEnhance->get_active()?M("GENERAL_ENABLED"):M("GENERAL_DISABLED")); +}*/ diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 5911a1267..ca560ad89 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -50,8 +50,6 @@ protected: Gtk::CheckButton* pixelShiftNonGreenHorizontal; Gtk::CheckButton* pixelShiftNonGreenVertical; Gtk::CheckButton* pixelShiftNonGreenCross; - Gtk::CheckButton* pixelShiftNonGreenCross2; - Gtk::CheckButton* pixelShiftNonGreenAmaze; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; @@ -61,10 +59,7 @@ protected: bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, - pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, - pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn, - pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn; + sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn; //,allEnhconn; public: BayerProcess (); @@ -85,8 +80,7 @@ public: void pixelShiftNonGreenHorizontalChanged(); void pixelShiftNonGreenVerticalChanged(); void pixelShiftNonGreenCrossChanged(); - void pixelShiftNonGreenCross2Changed(); - void pixelShiftNonGreenAmazeChanged(); + //void allEnhanceChanged(); }; #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index e91ea6c26..f433ec639 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -384,8 +384,6 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftNonGreenHorizontal = v; raw.bayersensor.pixelShiftNonGreenVertical = v; raw.bayersensor.pixelShiftNonGreenCross = v; - raw.bayersensor.pixelShiftNonGreenCross2 = v; - raw.bayersensor.pixelShiftNonGreenAmaze = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -896,8 +894,6 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; - raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; - raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2366,14 +2362,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; } - if (raw.bayersensor.pixelShiftNonGreenCross2) { - toEdit.raw.bayersensor.pixelShiftNonGreenCross2 = mods.raw.bayersensor.pixelShiftNonGreenCross2; - } - - if (raw.bayersensor.pixelShiftNonGreenAmaze) { - toEdit.raw.bayersensor.pixelShiftNonGreenAmaze = mods.raw.bayersensor.pixelShiftNonGreenAmaze; - } - + //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; } @@ -2886,7 +2875,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b4da65ac1..b4af2ad05 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -706,8 +706,6 @@ public: bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; bool pixelShiftNonGreenCross; - bool pixelShiftNonGreenCross2; - bool pixelShiftNonGreenAmaze; //bool allEnhance; bool greenEq; From 37243d9cd6675dca1592c2c3c4ede7e08f51fdc5 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 13 Jan 2017 21:35:11 +0100 Subject: [PATCH 062/181] Pixelshift: new beta realease --- rtdata/languages/default | 29 +- rtengine/amaze_demosaic_RT.cc | 2 +- rtengine/median.h | 9 + rtengine/pixelshift.cc | 1227 ++++++++++++++++++++++++++++++--- rtengine/procevents.h | 11 + rtengine/procparams.cc | 165 ++++- rtengine/procparams.h | 13 +- rtengine/rawimagesource.cc | 106 ++- rtengine/rawimagesource.h | 4 +- rtengine/refreshmap.cc | 13 +- rtgui/bayerprocess.cc | 322 ++++++++- rtgui/bayerprocess.h | 25 +- rtgui/paramsedited.cc | 71 +- rtgui/paramsedited.h | 11 + 14 files changed, 1850 insertions(+), 158 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index db55dde96..4b1b329e9 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -690,6 +690,16 @@ HISTORY_MSG_455;EvPixelShiftNonGreenVertical HISTORY_MSG_456;EvPixelShiftNonGreenCross HISTORY_MSG_457;EvPixelShiftStddevFactorRed HISTORY_MSG_458;EvPixelShiftStddevFactorBlue +HISTORY_MSG_459;EvPixelShiftGreenAmaze +HISTORY_MSG_460;EvPixelShiftNonGreenAmaze +HISTORY_MSG_461;EvPixelShiftGreen +HISTORY_MSG_462;EvPixelShiftRedBlueWeight +HISTORY_MSG_463;EvPixelShiftBlur +HISTORY_MSG_464;EvPixelShiftSigma +HISTORY_MSG_465;EvPixelShiftSum +HISTORY_MSG_466;EvPixelShiftExp0 +HISTORY_MSG_467;EvPixelShiftHoleFill +HISTORY_MSG_468;EvPixelShiftMedian HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1671,9 +1681,23 @@ TP_RAW_LMMSE_TOOLTIP;Adds gamma (step 1), median (steps 2-4) and refinement (ste TP_RAW_PIXELSHIFTADAPTIVE;Adaptive detection TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical +TP_RAW_PIXELSHIFTMEDIAN;Median +TP_RAW_PIXELSHIFTMEDIAN3;Exclude selected frame from median +TP_RAW_PIXELSHIFTHOLEFILL;3x3 new: Fill holes +TP_RAW_PIXELSHIFTBLUR;3x3 new: Blur +TP_RAW_PIXELSHIFTEXP0;Experimental +TP_RAW_PIXELSHIFTGREEN;Check dual green TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross -TP_RAW_PIXELSHIFTMOTION;Pixelshift motion detection +TP_RAW_PIXELSHIFTNONGREENCROSS2;Check green amaze +TP_RAW_PIXELSHIFTNONGREENAMAZE;Check red/blue amaze +TP_RAW_PIXELSHIFTMOTION;Motion detection level (deprecated) TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used +TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP;Fill holes in motion mask +TP_RAW_PIXELSHIFTBLUR_TOOLTIP;Blur motion mask +TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP;Use median of all frames instead of selected frame for regions with motion.\nRemoves objects which are at different places in all frames.\nGives motion effect on slow moving (overlapping) objects. +TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP;Excludes selected frame from median.\nUseful if moving objects overlap in frame 2 and 3 +TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the regions with motion +TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP;Shows the motion mask without the image TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTSHOWMOTION;Show motion @@ -1684,6 +1708,9 @@ TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE;StdDev factor Blue TP_RAW_PIXELSHIFTEPERISO;e per ISO TP_RAW_PIXELSHIFTNREADISO;nRead TP_RAW_PIXELSHIFTPRNU;PRNU (%) +TP_RAW_PIXELSHIFTSIGMA;Blur sigma +TP_RAW_PIXELSHIFTMASKTHRESHOLD;3x3 new threshold +TP_RAW_PIXELSHIFTREDBLUEWEIGHT;Red&Blue weight TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;3-pass gives best results (recommended for low ISO images).\n1-pass is almost undistinguishable from 3-pass for high ISO images and is faster. TP_RAW_SENSOR_XTRANS_LABEL;Sensor with X-Trans Matrix diff --git a/rtengine/amaze_demosaic_RT.cc b/rtengine/amaze_demosaic_RT.cc index c5535493c..3cb0ee8de 100644 --- a/rtengine/amaze_demosaic_RT.cc +++ b/rtengine/amaze_demosaic_RT.cc @@ -38,7 +38,7 @@ namespace rtengine { -SSEFUNCTION void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh) +SSEFUNCTION void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue) { BENCHFUN diff --git a/rtengine/median.h b/rtengine/median.h index 971452bc3..30adbacb3 100644 --- a/rtengine/median.h +++ b/rtengine/median.h @@ -64,6 +64,15 @@ inline vfloat median(std::array array) } #endif + +template +inline T median(std::array array) +{ + float val1 = std::max(std::min(array[0], array[1]), std::min(array[2], array[3])); + float val2 = std::min(std::max(array[0], array[1]), std::max(array[2], array[3])); + return (val1 + val2) / 2.f; +} + template inline T median(std::array array) { diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index e3ababc85..ae2f08fdb 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -30,13 +30,14 @@ #include "rawimagesource.h" #include "../rtgui/multilangmgr.h" #include "procparams.h" +#include "gauss.h" #define BENCHMARK #include "StopWatch.h" namespace { -float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion, int x, int y) { // calculate the difference between two green samples if(adaptive) { @@ -47,6 +48,12 @@ float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperI avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); + +// if(x >= 4294 && x <= 4303 && y >= 3056 && y <= 3058) { +// #pragma omp critical +// std::cout << "x : " << x << " y : " << y << " stddev : " << stddev << " avg : " << avg << " gDiff : " << gDiff << std::endl; +// } + float result = gDiff - stddev; if(!showMotion) { @@ -122,11 +129,166 @@ void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMas } } +void invertMask(int xStart, int xEnd, int yStart, int yEnd, array2D &maskIn, array2D &maskOut) +{ + #pragma omp parallel for schedule(dynamic,16) + + for(int i = yStart; i < yEnd; ++i) { + #pragma omp simd + + for(int j = xStart; j < xEnd; ++j) { + maskOut[i][j] = ~maskIn[i][j]; + } + } +} + +void xorMasks(int xStart, int xEnd, int yStart, int yEnd, array2D &maskIn, array2D &maskOut) +{ + #pragma omp parallel for schedule(dynamic,16) + + for(int i = yStart; i < yEnd; ++i) { + #pragma omp simd + + for(int j = xStart; j < xEnd; ++j) { + maskOut[i][j] ^= maskIn[i][j]; + } + } +} + +void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, array2D &mask, std::stack, std::vector>> &coordStack) +{ + coordStack.emplace(x, y); + + while(!coordStack.empty()) { + auto coord = coordStack.top(); + coordStack.pop(); + auto x = coord.first, y = coord.second; + + if (mask[y][x] == 255) { + auto yUp = y - 1, yDown = y + 1; + bool lastXUp = false, lastXDown = false, firstXUp = false, firstXDown = false; + mask[y][x] = 0; + if(yUp >= yStart && mask[yUp][x] == 255) { + coordStack.emplace(x, yUp); + firstXUp = lastXUp = true; + } + if(yDown < yEnd && mask[yDown][x] == 255) { + coordStack.emplace(x, yDown); + firstXDown = lastXDown = true; + } + auto xr = x + 1; + + while(xr < xEnd && mask[y][xr] == 255) { + mask[y][xr] = 0; + if(yUp >= yStart && mask[yUp][xr] == 255) { + if(!lastXUp) { + coordStack.emplace(xr, yUp); + lastXUp = true; + } + } else { + lastXUp = false; + } + if(yDown < yEnd && mask[yDown][xr] == 255) { + if(!lastXDown) { + coordStack.emplace(xr, yDown); + lastXDown = true; + } + } else { + lastXDown = false; + } + xr++; + } + + auto xl = x - 1; + lastXUp = firstXUp; + lastXDown = firstXDown; + while(xl >= xStart && mask[y][xl] == 255) { + mask[y][xl] = 0; + if(yUp >= yStart && mask[yUp][xl] == 255) { + if(!lastXUp) { + coordStack.emplace(xl, yUp); + lastXUp = true; + } + } else { + lastXUp = false; + } + if(yDown < yEnd && mask[yDown][xl] == 255) { + if(!lastXDown) { + coordStack.emplace(xl, yDown); + lastXDown = true; + } + } else { + lastXDown = false; + } + xl--; + } + mask[y][x] = 0; + } + } +} + +void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &mask) +{ + #pragma omp parallel + { + std::stack, std::vector>> coordStack; + + #pragma omp for schedule(dynamic,128) nowait + for(uint16_t i = yStart;i= 0 ;i--) + floodFill4Impl(i, xEnd - 1, xStart, xEnd, yStart, yEnd, mask, coordStack); + + #pragma omp sections nowait + { + #pragma omp section + { + uint16_t i = yStart; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yStart; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + } + } +} + + } using namespace std; using namespace rtengine; - +#ifdef __OLDPS__ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) { @@ -276,6 +438,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoModel; int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); + if(model.find("K-3") != string::npos) { nRead = nReadK3II[nReadIndex]; eperIsoModel = ePerIsoK3II; @@ -292,7 +455,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); float eperIsoGreen = eperIso * scaleGreen; - printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); +// printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); prnu /= 100.f; stddevFactorGreen *= stddevFactorGreen; @@ -347,9 +510,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float eperIsoNonGreen2 = eperIso / scale_mul[2]; float stddevFactorNonGreen0 = stddevFactorRed; float stddevFactorNonGreen2 = stddevFactorBlue; + bool blueRow = false; if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; std::swap(nonGreenDest0, nonGreenDest1); std::swap(scaleNonGreen0, scaleNonGreen2); std::swap(eperIsoNonGreen0, eperIsoNonGreen2); @@ -365,90 +530,90 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(detectMotion || adaptive) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); } else if(gridSize == 7) { // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); - greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); } } @@ -468,34 +633,34 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); + gridMax = greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); skipNext = skip; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) - }); + greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); } @@ -528,29 +693,102 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; - float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + float diffRight = ngRight - ngCentre; + float diffLeft = ngLeft - ngCentre; + float diffHorNg0 = -1.f; - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); - continue; + if(diffRight * diffLeft >= 0.f) { + float avg = (ngRight + ngLeft) / 2.f; + diffHorNg0 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); + +// if(diff > 0.f) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } } + float diffHorNg1 = -1.f; ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; - ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + diffRight = ngRight - ngCentre; + diffLeft = ngLeft - ngCentre; - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); - continue; + if(diffRight * diffLeft >= 0.f) { + float avg = (ngRight + ngLeft) / 2.f; + float diffHorNg1 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); + +// if(diff > 0.f) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); +// continue; +// } } + + if( diffHorNg0 * diffHorNg1 < 0.f) { + paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); + continue; + + } + +// bool motion = false; +// float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; +// float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; +// float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; +// float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; +// float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; +// float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); +// +// if(diff > 0.f) { +// motion = true; +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } +// +// ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; +// ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; +// ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; +// ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; +// ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; +// diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); +// +//// if(diff > 0.f) { +// if((diff > 0.f && !motion) || (diff <= 0.f && motion) ) { +// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); +// continue; +// } } if(adaptive && checkNonGreenHorizontal) { +// float lg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j - 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j]) / 2.f; +// float cg = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; +// float rg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j + 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j + 2]) / 2.f; +// +// float lr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) - 1]; +// float cr = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; +// float rr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) + 1]; +// +// float lb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; +// float cb = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; +// float rb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; +// +// if(blueRow) { +// std::swap(lr, lb); +// std::swap(cr, cb); +// std::swap(rr, rb); +// } +// +// float lh = Color::rgb2h(lr, lg, lb); +// float ch = Color::rgb2h(cr, cg, cb); +// float rh = Color::rgb2h(rr, rg, rb); +// +// float lHueDiff = lh - ch; +// float rHueDiff = rh - ch; +// if(lHueDiff * rHueDiff > 0.f) { +// if(std::fabs(lHueDiff) > 0.5f && std::fabs(rHueDiff) > 0.5f/* && std::fabs(lHueDiff) < 3.f && std::fabs(rHueDiff) < 3.f*/) { +// paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); +// continue; +// } +// } float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; @@ -637,3 +875,764 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det plistener->setProgress(1.0); } } +#else +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection) +{ + + BENCHFUN + + const bool detectMotion = bayerParams.pixelShiftMotion > 0; + const int motion = bayerParams.pixelShiftMotion; + const bool showMotion = bayerParams.pixelshiftShowMotion; + const bool showOnlyMask = bayerParams.pixelshiftShowMotionMaskOnly; + const RAWParams::BayerSensor::ePSMotionCorrection gridSize_ = bayerParams.pixelShiftMotionCorrection; + const bool adaptive = bayerParams.pixelShiftAutomatic; + float stddevFactorGreen = bayerParams.pixelShiftStddevFactorGreen; + float stddevFactorRed = bayerParams.pixelShiftStddevFactorRed; + float stddevFactorBlue = bayerParams.pixelShiftStddevFactorBlue; + float eperIso = bayerParams.pixelShiftEperIso; + float nreadIso = bayerParams.pixelShiftNreadIso; + float prnu = bayerParams.pixelShiftPrnu; + const bool checkNonGreenHorizontal = bayerParams.pixelShiftNonGreenHorizontal; + const bool checkNonGreenVertical = bayerParams.pixelShiftNonGreenVertical; + const bool checkNonGreenCross = bayerParams.pixelShiftNonGreenCross; + const bool checkNonGreenAmaze = bayerParams.pixelShiftNonGreenAmaze; + const bool checkNonGreenCross2 = bayerParams.pixelShiftNonGreenCross2; + const bool checkGreen = bayerParams.pixelShiftGreen; + const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight; + const bool blurMap = bayerParams.pixelShiftBlur; + const float sigma = bayerParams.pixelShiftSigma; + const float threshold = bayerParams.pixelShiftSum; + const bool experimental0 = bayerParams.pixelShiftExp0; + const bool holeFill = bayerParams.pixelShiftHoleFill; + + static const float nReadK3II[] = { 3.4f, // ISO 100 + 3.1f, // ISO 125 + 2.5f, // ISO 160 + 2.5f, // ISO 200 + 2.5f, // ISO 250 + 2.5f, // ISO 320 + 2.3f, // ISO 400 + 2.5f, // ISO 500 + 2.3f, // ISO 640 + 2.3f, // ISO 800 + 2.4f, // ISO 1000 + 2.3f, // ISO 1250 + 1.75f, // ISO 1600 + 1.75f, // ISO 2000 + 1.75f, // ISO 2500 + 1.75f, // ISO 3200 + 1.75f, // ISO 4000 + 1.75f, // ISO 5000 + 1.75f, // ISO 6400 + 1.75f, // ISO 8000 + 1.75f, // ISO 10000 + 1.5f, // ISO 12800 + 1.5f, // ISO 16000 + 1.5f, // ISO 20000 + 1.5f, // ISO 25600 + 1.5f, // ISO 32000 + 1.5f, // ISO 40000 + 1.5f, // ISO 51200 + 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK3II = 4 * 0.35f; + + static const float nReadK1[] = { 3.45f, // ISO 100 + 3.15f, // ISO 125 + 3.45f, // ISO 160 + 3.0f, // ISO 200 + 3.0f, // ISO 250 + 3.0f, // ISO 320 + 2.7f, // ISO 400 + 2.7f, // ISO 500 + 2.7f, // ISO 640 + 2.5f, // ISO 800 + 2.5f, // ISO 1000 + 2.5f, // ISO 1250 + 2.4f, // ISO 1600 + 2.4f, // ISO 2000 + 2.4f, // ISO 2500 + 2.4f, // ISO 3200 + 2.4f, // ISO 4000 + 2.4f, // ISO 5000 + 2.4f, // ISO 6400 + 2.4f, // ISO 8000 + 2.4f, // ISO 10000 + 2.4f, // ISO 12800 + 2.4f, // ISO 16000 + 2.4f, // ISO 20000 + 2.4f, // ISO 25600 + 2.4f, // ISO 32000 + 2.4f, // ISO 40000 + 2.4f, // ISO 51200 + 2.4f, // ISO 64000 + 2.4f, // ISO 80000 + 2.4f, // ISO 102400 + 2.4f, // ISO 128000 + 2.4f, // ISO 160000 + 2.4f // ISO 204800 + }; + + static const float ePerIsoK1 = 4 * 0.75f; + + static const float nReadK70[] = { 4.0f, // ISO 100 + 4.0f, // ISO 125 + 4.0f, // ISO 160 + 4.0f, // ISO 200 + 4.0f, // ISO 250 + 4.0f, // ISO 320 + 4.0f, // ISO 400 + 4.0f, // ISO 500 + 4.0f, // ISO 640 + 3.0f, // ISO 800 + 3.0f, // ISO 1000 + 3.0f, // ISO 1250 + 3.0f, // ISO 1600 + 3.0f, // ISO 2000 + 3.0f, // ISO 2500 + 3.0f, // ISO 3200 + 3.0f, // ISO 4000 + 3.0f, // ISO 5000 + 3.0f, // ISO 6400 + 3.0f, // ISO 8000 + 3.0f, // ISO 10000 + 3.0f, // ISO 12800 + 3.0f, // ISO 16000 + 3.0f, // ISO 20000 + 3.0f, // ISO 25600 + 3.0f, // ISO 32000 + 3.0f, // ISO 40000 + 3.0f, // ISO 51200 + 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) + }; + + static const float ePerIsoK70 = 4 * 0.5f; + + if (plistener) { + plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); + plistener->setProgress(0.0); + } + + + const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); + int gridSize = 1; + bool nOf3x3 = false; + + switch (gridSize_) { + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: + case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: + gridSize = 1; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: + gridSize = 3; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: + gridSize = 5; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: + gridSize = 7; + break; + + case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3New: + gridSize = 1; + nOf3x3 = true; + } + + // Lookup table for non adaptive (slider) mode + LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); + + if(detectMotion && !adaptive) { + const float lutStrength = 2.f; + log2Lut[0] = 0; + + for(int i = 2; i < 65536; i += 2) { + log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; + } + } + + const float scaleGreen = 1.f / scale_mul[1]; + + float nRead; + float eperIsoModel; + + int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); + + if(model.find("K-3") != string::npos) { + nRead = nReadK3II[nReadIndex]; + eperIsoModel = ePerIsoK3II; + } else if(model.find("K-1") != string::npos) { + nRead = nReadK1[nReadIndex]; + eperIsoModel = ePerIsoK1; + } else { + nRead = nReadK70[nReadIndex]; + eperIsoModel = ePerIsoK70; + } + + nRead *= pow(2.f, nreadIso); + eperIsoModel *= pow(2.f, eperIso); + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); + + float eperIsoRed = eperIso / scale_mul[0]; + float eperIsoGreen = eperIso * scaleGreen; + float eperIsoBlue = eperIso / scale_mul[2]; + +// printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); + + prnu /= 100.f; + stddevFactorGreen *= stddevFactorGreen; + stddevFactorRed *= stddevFactorRed; + stddevFactorBlue *= stddevFactorBlue; + + + nRead *= nRead; + + // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel + float motionThreshold = 1.f - (motion / 100.f); + // For shades of green motion indicators + const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); + + unsigned int offsX = 0, offsY = 0; + + if(!bayerParams.pixelShiftMedian || !adaptive) { + // We have to adjust the offsets for the selected subframe we use for areas with motion + switch (frame) { + case 0: + offsX = offsY = 0; + break; + + case 1: + offsX = 0; + offsY = 1; + break; + + case 2: + offsX = offsY = 1; + break; + + case 3: + offsX = 1; + offsY = 0; + } + } + + const float thresh = adaptive ? 0.f : motionThreshold; + array2D psRed(winw + 32, winh); // increase width to avoid cache conflicts + array2D psG1(winw + 32, winh); + array2D psG2(winw + 32, winh); + array2D psBlue(winw + 32, winh); + + array2D psMask(winw, winh, ARRAY2D_CLEAR_DATA); +// fill channels psRed, psG1, psG2 and psBlue +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + float *greenDest1 = psG1[i]; + float *greenDest2 = psG2[i]; + float *nonGreenDest0 = psRed[i]; + float *nonGreenDest1 = psBlue[i]; + int j = winx + 1; + int c = FC(i, j); + + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + std::swap(nonGreenDest0, nonGreenDest1); + std::swap(greenDest1, greenDest2); + } + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); + offset ^= 1; // 0 => 1 or 1 => 0 + + for(; j < winw - 1; ++j) { + offset ^= 1; // 0 => 1 or 1 => 0 + + // store the values from the 4 frames into 4 different temporary planes + greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j]; + greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1]; + nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + } + } + +// now that the temporary planes are filled for easy access we do the motion detection + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + int j = winx + border - offsX; + + float greenDifMax[gridSize]; // Here we store the maximum differences per Column + + // green channel motion detection checks the grid around the pixel for differences in green channels + if(detectMotion || (adaptive && checkGreen)) { + if(gridSize == 3) { + // compute maximum of differences for first two columns of 3x3 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } else if(gridSize == 5) { + // compute maximum of differences for first four columns of 5x5 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } else if(gridSize == 7) { + // compute maximum of differences for first six columns of 7x7 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } + + } + + + // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 + int lastIndex = gridSize - 1; + float korr = 0.f; + int c = FC(i, j); + bool blueRow = false; + + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; + } + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); +// offset ^= 1; // 0 => 1 or 1 => 0 + + for(; j < winw - (border + offsX); ++j) { + bool greenFromPs = false; + offset ^= 1; // 0 => 1 or 1 => 0 + + if(detectMotion || (adaptive && checkGreen)) { + bool skipNext = false; + float gridMax; + + if(gridSize < 2) { + // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps + gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); + skipNext = skip; + } else if(gridSize == 3) { + // compute maximum of differences for third column of 3x3 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); + } else if(gridSize == 5) { + // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); + } else if(gridSize == 7) { + // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); + } + + + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + + // increase motion detection dependent on brightness + if(!adaptive) { + korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; + } + + if (gridMax > thresh - korr) { + if(nOf3x3) { + psMask[i][j] = 1.f; + } else if((offset == (frame & 1)) && checkNonGreenVertical) { + if(frame > 1) { + green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; + } else { + green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; + } + + continue; + } else { + // at least one of the tested green pixels of the grid is detected as motion + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + } + + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; + } + } + } + + if(adaptive && checkNonGreenCross) { + // check red cross + float redTop = psRed[i - 1][ j ]; + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + float redBottom = psRed[i + 1][ j ]; + float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + + // check blue cross + float blueTop = psBlue[i - 1][ j ]; + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + float blueBottom = psBlue[i + 1][ j ]; + float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + + } + } + + if(adaptive && checkNonGreenHorizontal) { + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + + float redDiffLeft = redLeft - redCentre; + float redDiffRight = redRight - redCentre; + + if(redDiffLeft * redDiffRight >= 0.f) { + float redAvg = (redRight + redLeft) / 2.f; + float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + } + + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + + float blueDiffLeft = blueLeft - blueCentre; + float blueDiffRight = blueRight - blueCentre; + + if(blueDiffLeft * blueDiffRight >= 0.f) { + float blueAvg = (blueRight + blueLeft) / 2.f; + float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + } + + if(adaptive && checkNonGreenVertical) { + // check red vertically + float redTop = psRed[i - 1][ j ]; + float redCentre = psRed[ i ][ j ]; + float redBottom = psRed[i + 1][ j ]; + + float redDiffTop = redTop - redCentre; + float redDiffBottom = redBottom - redCentre; + + if(redDiffTop * redDiffBottom >= 0.f) { + float redAvg = (redTop + redBottom) / 2.f; + float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + } + + // check blue vertically + float blueTop = psBlue[i - 1][ j ]; + float blueCentre = psBlue[ i ][ j ]; + float blueBottom = psBlue[i + 1][ j ]; + + float blueDiffTop = blueTop - blueCentre; + float blueDiffBottom = blueBottom - blueCentre; + + if(blueDiffTop * blueDiffBottom >= 0.f) { + float blueAvg = (blueTop + blueBottom) / 2.f; + float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + } + + if(adaptive && checkNonGreenAmaze) { + // check current pixel against amaze + float redCentre = psRed[ i ][ j ]; + float redAmaze = red[i + offsY][j + offsX]; + + float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + + if(redDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } + + float blueCentre = psBlue[ i ][ j ]; + float blueAmaze = blue[i + offsY][j + offsX]; + + float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } + + if(adaptive && checkNonGreenCross2) { // for green amaze + float greenCentre = (psG1[ i ][ j ] + psG2[ i ][ j ]) / 2.f; + float greenAmaze = green[i + offsY][j + offsX]; + float greenDiffAmaze = nonGreenDiff(greenCentre, greenAmaze, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); + + if(greenDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = 1.f; + } else { + paintMotionMask(j + offsX, showMotion, greenDiffAmaze, showOnlyMask, greenDest, redDest, blueDest); + } + + continue; + } + } + + if(adaptive && experimental0) { // for experiments + + } + + if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else if(!(adaptive && nOf3x3)) { + // no motion detected, replace the a priori demosaiced values by the pixelshift combined values + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } + } + } + + if(adaptive && nOf3x3) { + if(blurMap) { + #pragma omp parallel + { + gaussianBlur(psMask, psMask, winw, winh, sigma); + } + } + + array2D mask(W, H, ARRAY2D_CLEAR_DATA); + array2D maskInv(W, H, ARRAY2D_CLEAR_DATA); + #pragma omp parallel for schedule(dynamic,16) + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + int j = winx + border - offsX; + float v3sum[3] = {0.f}; + + for(int v = -1; v <= 1; v++) { + for(int h = -1; h < 1; h++) { + v3sum[1 + h] += psMask[i + v][j + h]; + } + } + + float blocksum = v3sum[0] + v3sum[1]; + + for(int voffset = 2; j < winw - (border + offsX); ++j, ++voffset) { + float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1]; + voffset = voffset == 3 ? 0 : voffset; // faster than voffset %= 3; + blocksum -= v3sum[voffset]; + blocksum += colSum; + v3sum[voffset] = colSum; + + if(blocksum >= threshold) { + mask[i][j] = 255; + } + } + } + + if(holeFill) { + invertMask(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), mask, maskInv); + floodFill4(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), maskInv); + xorMasks(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), maskInv, mask); + } + + #pragma omp parallel for schedule(dynamic,16) + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { + if(mask[i][j] == 255 ) { + paintMotionMask(j + offsX, showMotion, 0.5f, showOnlyMask, greenDest, redDest, blueDest); + continue; + } + + if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else { + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } + } + } + } + + if(plistener) { + plistener->setProgress(1.0); + } +} +#endif \ No newline at end of file diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 747d60cb4..4526b13f3 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -485,6 +485,17 @@ enum ProcEvent { EvPixelShiftNonGreenCross = 455, EvPixelShiftStddevFactorRed = 456, EvPixelShiftStddevFactorBlue = 457, + EvPixelShiftGreenAmaze = 458, + EvPixelShiftNonGreenAmaze = 459, + EvPixelShiftGreen = 460, + EvPixelShiftRedBlueWeight = 461, + EvPixelShiftBlur = 462, + EvPixelShiftSigma = 463, + EvPixelShiftSum = 464, + EvPixelShiftExp0 = 465, + EvPixelShiftHoleFill = 466, + EvPixelShiftMedian = 467, + EvPixelShiftMedian3 = 468, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 5fd8b17ff..a7443a87c 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -886,19 +886,30 @@ void RAWParams::setDefaults() //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; bayersensor.pixelShiftMotion = 0; - bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid1x2; - bayersensor.pixelShiftStddevFactorGreen = 3.0; - bayersensor.pixelShiftStddevFactorRed = 3.0; - bayersensor.pixelShiftStddevFactorBlue = 3.0; + bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid3x3New; + bayersensor.pixelShiftStddevFactorGreen = 5.0; + bayersensor.pixelShiftStddevFactorRed = 5.0; + bayersensor.pixelShiftStddevFactorBlue = 5.0; bayersensor.pixelShiftEperIso = 0.0; bayersensor.pixelShiftNreadIso = 0.0; bayersensor.pixelShiftPrnu = 1.0; + bayersensor.pixelShiftSigma = 1.0; + bayersensor.pixelShiftSum = 3.0; + bayersensor.pixelShiftRedBlueWeight = 0.7; bayersensor.pixelshiftShowMotion = false; bayersensor.pixelshiftShowMotionMaskOnly = false; bayersensor.pixelShiftAutomatic = true; bayersensor.pixelShiftNonGreenHorizontal = false; bayersensor.pixelShiftNonGreenVertical = false; - bayersensor.pixelShiftNonGreenCross = false; + bayersensor.pixelShiftHoleFill = true; + bayersensor.pixelShiftMedian = false; + bayersensor.pixelShiftMedian3 = false; + bayersensor.pixelShiftGreen = true; + bayersensor.pixelShiftBlur = true; + bayersensor.pixelShiftExp0 = false; + bayersensor.pixelShiftNonGreenCross = true; + bayersensor.pixelShiftNonGreenCross2 = false; + bayersensor.pixelShiftNonGreenAmaze = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3410,6 +3421,18 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_double ("RAW Bayer", "PixelShiftPrnu", raw.bayersensor.pixelShiftPrnu ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftSigma) { + keyFile.set_double ("RAW Bayer", "PixelShiftSigma", raw.bayersensor.pixelShiftSigma ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftSum) { + keyFile.set_double ("RAW Bayer", "PixelShiftSum", raw.bayersensor.pixelShiftSum ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftRedBlueWeight) { + keyFile.set_double ("RAW Bayer", "PixelShiftRedBlueWeight", raw.bayersensor.pixelShiftRedBlueWeight ); + } + if (!pedited || pedited->raw.bayersensor.pixelshiftShowMotion) { keyFile.set_boolean ("RAW Bayer", "PixelShiftShowMotion", raw.bayersensor.pixelshiftShowMotion ); } @@ -3430,11 +3453,41 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenVertical", raw.bayersensor.pixelShiftNonGreenVertical ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftHoleFill) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftHoleFill", raw.bayersensor.pixelShiftHoleFill ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMedian) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftMedian3) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian3 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftGreen) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftGreen", raw.bayersensor.pixelShiftGreen ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftBlur) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftBlur", raw.bayersensor.pixelShiftBlur ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftExp0) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftExp0", raw.bayersensor.pixelShiftExp0 ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross) { keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); } - //if (!pedited || pedited->raw.bayersensor.allEnhance) keyFile.set_boolean ("RAW Bayer", "ALLEnhance", raw.bayersensor.all_enhance ); + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross2) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross2", raw.bayersensor.pixelShiftNonGreenCross2 ); + } + + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenAmaze) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenAmaze", raw.bayersensor.pixelShiftNonGreenAmaze ); + } if (!pedited || pedited->raw.xtranssensor.method) { keyFile.set_string ("RAW X-Trans", "Method", raw.xtranssensor.method ); @@ -7556,6 +7609,30 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftSigma")) { + raw.bayersensor.pixelShiftSigma = keyFile.get_double("RAW Bayer", "PixelShiftSigma"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSigma = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftSum")) { + raw.bayersensor.pixelShiftSum = keyFile.get_double("RAW Bayer", "PixelShiftSum"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSum = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "PixelShiftRedBlueWeight")) { + raw.bayersensor.pixelShiftRedBlueWeight = keyFile.get_double("RAW Bayer", "PixelShiftRedBlueWeight"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftRedBlueWeight = true; + } + } + if (keyFile.has_key ("RAW Bayer", "PixelShiftShowMotion")) { raw.bayersensor.pixelshiftShowMotion = keyFile.get_boolean("RAW Bayer", "PixelShiftShowMotion"); @@ -7596,6 +7673,54 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftHoleFill")) { + raw.bayersensor.pixelShiftHoleFill = keyFile.get_boolean("RAW Bayer", "pixelShiftHoleFill"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftHoleFill = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftMedian")) { + raw.bayersensor.pixelShiftMedian = keyFile.get_boolean("RAW Bayer", "pixelShiftMedian"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMedian = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftMedian3")) { + raw.bayersensor.pixelShiftMedian3 = keyFile.get_boolean("RAW Bayer", "pixelShiftMedian3"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMedian3 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftGreen")) { + raw.bayersensor.pixelShiftGreen = keyFile.get_boolean("RAW Bayer", "pixelShiftGreen"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftGreen = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftBlur")) { + raw.bayersensor.pixelShiftBlur = keyFile.get_boolean("RAW Bayer", "pixelShiftBlur"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftBlur = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftExp0")) { + raw.bayersensor.pixelShiftExp0 = keyFile.get_boolean("RAW Bayer", "pixelShiftExp0"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftExp0 = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross")) { raw.bayersensor.pixelShiftNonGreenCross = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross"); @@ -7604,7 +7729,21 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - //if (keyFile.has_key ("RAW Bayer", "ALLEnhance")) { raw.bayersensor.all_enhance = keyFile.get_boolean("RAW Bayer", "ALLEnhance"); if (pedited) pedited->raw.bayersensor.allEnhance = true; } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross2")) { + raw.bayersensor.pixelShiftNonGreenCross2 = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross2"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = true; + } + } + + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenAmaze")) { + raw.bayersensor.pixelShiftNonGreenAmaze = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenAmaze"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = true; + } + } } // load X-Trans sensors' raw settings @@ -8051,14 +8190,24 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso && raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso && raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu + && raw.bayersensor.pixelShiftSigma == other.raw.bayersensor.pixelShiftSigma + && raw.bayersensor.pixelShiftSum == other.raw.bayersensor.pixelShiftSum + && raw.bayersensor.pixelShiftRedBlueWeight == other.raw.bayersensor.pixelShiftRedBlueWeight && raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion && raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly && raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic && raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal && raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical + && raw.bayersensor.pixelShiftHoleFill == other.raw.bayersensor.pixelShiftHoleFill + && raw.bayersensor.pixelShiftMedian == other.raw.bayersensor.pixelShiftMedian + && raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3 + && raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen + && raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur + && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross + && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 + && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze && raw.bayersensor.dcb_enhance == other.raw.bayersensor.dcb_enhance - //&& raw.bayersensor.all_enhance == other.raw.bayersensor.all_enhance && raw.xtranssensor.method == other.raw.xtranssensor.method && raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps && raw.xtranssensor.blackred == other.raw.xtranssensor.blackred diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 0c06fa315..c3c8296b7 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1188,7 +1188,7 @@ public: numMethods }; // This MUST be the last enum enum ePSMotionCorrection { - Grid1x1, Grid1x2, Grid3x3, Grid5x5, Grid7x7 + Grid1x1, Grid1x2, Grid3x3, Grid5x5, Grid7x7, Grid3x3New }; static const char *methodstring[numMethods]; @@ -1212,12 +1212,23 @@ public: double pixelShiftEperIso; double pixelShiftNreadIso; double pixelShiftPrnu; + double pixelShiftSigma; + double pixelShiftSum; + double pixelShiftRedBlueWeight; bool pixelshiftShowMotion; bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; + bool pixelShiftHoleFill; + bool pixelShiftMedian; + bool pixelShiftMedian3; + bool pixelShiftGreen; + bool pixelShiftBlur; + bool pixelShiftExp0; bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; bool dcb_enhance; //bool all_enhance; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 36b78aa20..566fc77a3 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2022,13 +2022,107 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::ahd] ) { ahd_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze] ) { - amaze_demosaic_RT (0, 0, W, H); + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift] ) { - if(raw.bayersensor.pixelShiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) { - amaze_demosaic_RT (0, 0, W, H); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction - } - if(numFrames == 4) { - pixelshift(0, 0, W, H, raw.bayersensor.pixelShiftMotion > 0, raw.bayersensor.pixelShiftMotion, raw.bayersensor.pixelshiftShowMotion, raw.bayersensor.pixelshiftShowMotionMaskOnly, currFrame, raw.bayersensor.pixelShiftMotionCorrection, raw.bayersensor.pixelShiftAutomatic, raw.bayersensor.pixelShiftStddevFactorGreen, raw.bayersensor.pixelShiftStddevFactorRed, raw.bayersensor.pixelShiftStddevFactorBlue, raw.bayersensor.pixelShiftEperIso, raw.bayersensor.pixelShiftNreadIso, raw.bayersensor.pixelShiftPrnu, ri->get_model(), raw.expos, raw.bayersensor.pixelShiftNonGreenHorizontal, raw.bayersensor.pixelShiftNonGreenVertical, raw.bayersensor.pixelShiftNonGreenCross); + if(numFrames != 4) { // fallback for non pixelshift files + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); + } else { + if(!raw.bayersensor.pixelshiftShowMotion || raw.bayersensor.pixelShiftNonGreenAmaze || raw.bayersensor.pixelShiftNonGreenCross2) { + if((raw.bayersensor.pixelShiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) && numFrames == 4) { + if(raw.bayersensor.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction + if(!raw.bayersensor.pixelShiftMedian3) { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[0]), red, green, blue); + multi_array2D redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0;i<3;i++) { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); + } + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;i redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0, frameIndex = 0;i<4;++i) { + if(i != currFrame) { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); + ++frameIndex; + } + } + unsigned int offsX0 = 0, offsY0 = 0; + unsigned int offsX1 = 0, offsY1 = 0; + unsigned int offsX2 = 0, offsY2 = 0; + + // We have to adjust the offsets for the selected subframe we exclude from median + switch (currFrame) { + case 0: + offsY0 = 1; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 1: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 2: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 0; + offsX2 = 1; + break; + + case 3: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 1; + offsX2 = 1; + } + + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;iget_model(), raw.expos); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index f80efeacf..d54bcd49d 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -237,7 +237,7 @@ protected: void jdl_interpolate_omp(); void igv_interpolate(int winw, int winh); void lmmse_interpolate_omp(int winw, int winh, int iterations); - void amaze_demosaic_RT(int winx, int winy, int winw, int winh);//Emil's code for AMaZE + void amaze_demosaic_RT(int winx, int winy, int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue);//Emil's code for AMaZE void fast_demosaic(int winx, int winy, int winw, int winh );//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(int winx, int winy, int winw, int winh); @@ -261,7 +261,7 @@ protected: void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); void fast_xtrans_interpolate (); - void pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross); + void pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); void vflip (Imagefloat* im); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 08e3d2520..3030a4319 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -484,7 +484,18 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftNonGreenVertical DEMOSAIC, // EvPixelShiftNonGreenCross DEMOSAIC, // EvPixelShiftStddevFactorRed - DEMOSAIC // EvPixelShiftStddevFactorBlue + DEMOSAIC, // EvPixelShiftStddevFactorBlue + DEMOSAIC, // EvPixelShiftNonGreenCross2 + DEMOSAIC, // EvPixelShiftNonGreenAmaze + DEMOSAIC, // EvPixelShiftGreen + DEMOSAIC, // EvPixelShiftRedBlueWeight + DEMOSAIC, // EvPixelShiftBlur + DEMOSAIC, // EvPixelShiftSigma + DEMOSAIC, // EvPixelShiftSum + DEMOSAIC, // EvPixelShiftExp0 + DEMOSAIC, // EvPixelShiftHoleFill + DEMOSAIC, // EvPixelShiftMedian + DEMOSAIC // EvPixelShiftMedian3 }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 297a9b65b..701463ac4 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -83,27 +83,58 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); pixelShiftOptions->set_border_width(4); + pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); + pixelShiftShowMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftShowMotion); pixelShiftShowMotionMaskOnly = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); + pixelShiftShowMotionMaskOnly->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftShowMotionMaskOnly); pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); + pixelShiftGreen = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTGREEN"))); + pixelShiftOptions->pack_start(*pixelShiftGreen); + + pixelShiftBlur = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTBLUR"))); + pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTBLUR_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftBlur); + + pixelShiftHoleFill = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); + pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftHoleFill); + + pixelShiftMedian = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTMEDIAN"))); + pixelShiftMedian->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftMedian); + + pixelShiftMedian3 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTMEDIAN3"))); + pixelShiftMedian3->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftMedian3); + pixelShiftNonGreenCross = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); + pixelShiftNonGreenCross2 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); + + pixelShiftNonGreenAmaze = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); + pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenVertical); + pixelShiftExp0 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTEXP0"))); + pixelShiftOptions->pack_start(*pixelShiftExp0); + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); - pixelShiftMotion->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); + pixelShiftMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); if (pixelShiftMotion->delay < options.adjusterMaxDelay) { pixelShiftMotion->delay = options.adjusterMaxDelay; @@ -119,13 +150,14 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->append_text("3x3"); pixelShiftMotionCorrection->append_text("5x5"); pixelShiftMotionCorrection->append_text("7x7"); + pixelShiftMotionCorrection->append_text("3x3 new"); pixelShiftMotionCorrection->set_active(0); // pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); pixelShiftMotionCorrection->show(); hb2->pack_start(*pixelShiftMotionCorrection); pixelShiftOptions->pack_start(*hb2); - pixelShiftStddevFactorGreen = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN"), 2, 8, 0.1, 3)); + pixelShiftStddevFactorGreen = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN"), 2, 8, 0.1, 5)); pixelShiftStddevFactorGreen->setAdjusterListener (this); if (pixelShiftStddevFactorGreen->delay < options.adjusterMaxDelay) { @@ -135,7 +167,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftStddevFactorGreen->show(); pixelShiftOptions->pack_start(*pixelShiftStddevFactorGreen); - pixelShiftStddevFactorRed = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORRED"), 1, 8, 0.1, 3)); + pixelShiftStddevFactorRed = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORRED"), 1, 8, 0.1, 5)); pixelShiftStddevFactorRed->setAdjusterListener (this); if (pixelShiftStddevFactorRed->delay < options.adjusterMaxDelay) { @@ -145,7 +177,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftStddevFactorRed->show(); pixelShiftOptions->pack_start(*pixelShiftStddevFactorRed); - pixelShiftStddevFactorBlue = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE"), 1, 8, 0.1, 3)); + pixelShiftStddevFactorBlue = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE"), 1, 8, 0.1, 5)); pixelShiftStddevFactorBlue->setAdjusterListener (this); if (pixelShiftStddevFactorBlue->delay < options.adjusterMaxDelay) { @@ -186,6 +218,36 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftPrnu->show(); pixelShiftOptions->pack_start(*pixelShiftPrnu); + pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 2.5, 0.1, 1.0)); + pixelShiftSigma->setAdjusterListener (this); + + if (pixelShiftSigma->delay < options.adjusterMaxDelay) { + pixelShiftSigma->delay = options.adjusterMaxDelay; + } + + pixelShiftSigma->show(); + pixelShiftOptions->pack_start(*pixelShiftSigma); + + pixelShiftSum = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMASKTHRESHOLD"), 1.0, 8.0, 0.1, 3.0)); + pixelShiftSum->setAdjusterListener (this); + + if (pixelShiftSum->delay < options.adjusterMaxDelay) { + pixelShiftSum->delay = options.adjusterMaxDelay; + } + + pixelShiftSum->show(); + pixelShiftOptions->pack_start(*pixelShiftSum); + + pixelShiftRedBlueWeight = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTREDBLUEWEIGHT"), 0.1, 1.0, 0.1, 0.7)); + pixelShiftRedBlueWeight->setAdjusterListener (this); + + if (pixelShiftRedBlueWeight->delay < options.adjusterMaxDelay) { + pixelShiftRedBlueWeight->delay = options.adjusterMaxDelay; + } + + pixelShiftRedBlueWeight->show(); + pixelShiftOptions->pack_start(*pixelShiftRedBlueWeight); + pack_start( *pixelShiftOptions, Gtk::PACK_SHRINK, 4); @@ -216,8 +278,15 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); + pixelShiftHoleFillconn = pixelShiftHoleFill->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftHoleFillChanged), true); + pixelShiftMedianconn = pixelShiftMedian->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedianChanged), true); + pixelShiftMedian3conn = pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true); + pixelShiftGreenconn = pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true); + pixelShiftBlurconn = pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true); + pixelShiftExp0conn = pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); - //allEnhconn = allEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::allEnhanceChanged), true); + pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); + pixelShiftNonGreenAmazeconn = pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true); } @@ -250,8 +319,15 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftHoleFill->set_inconsistent(!pedited->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian); + pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); + pixelShiftGreen->set_inconsistent(!pedited->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); + pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); - //allEnhance->set_inconsistent(!pedited->raw.bayersensor.allEnhance); + pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); @@ -260,6 +336,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); + pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); + pixelShiftSum->setEditedState ( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); + pixelShiftRedBlueWeight->setEditedState ( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); if(!pedited->raw.bayersensor.method) { method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name @@ -281,7 +360,15 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftHoleFill->set_active(pp->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->set_active(pp->raw.bayersensor.pixelShiftMedian); + pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); + pixelShiftGreen->set_active(pp->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); + pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); + pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); @@ -292,6 +379,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); + pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); + pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); + pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || @@ -349,12 +439,23 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); + pp->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getValue(); + pp->raw.bayersensor.pixelShiftSum = pixelShiftSum->getValue(); + pp->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getValue(); pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); + pp->raw.bayersensor.pixelShiftHoleFill = pixelShiftHoleFill->get_active(); + pp->raw.bayersensor.pixelShiftMedian = pixelShiftMedian->get_active(); + pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); + pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->get_active(); + pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); + pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -383,12 +484,23 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); + pedited->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getEditedState (); + pedited->raw.bayersensor.pixelShiftSum = pixelShiftSum->getEditedState (); + pedited->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getEditedState (); pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftHoleFill = !pixelShiftHoleFill->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftMedian = !pixelShiftMedian->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftGreen = !pixelShiftGreen->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); } } @@ -414,6 +526,9 @@ void BayerProcess::setBatchMode(bool batchMode) pixelShiftEperIso->showEditedCB (); pixelShiftNreadIso->showEditedCB (); pixelShiftPrnu->showEditedCB (); + pixelShiftSigma->showEditedCB (); + pixelShiftSum->showEditedCB (); + pixelShiftRedBlueWeight->showEditedCB (); } void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) @@ -427,6 +542,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); pixelShiftPrnu->setDefault( defParams->raw.bayersensor.pixelShiftPrnu); + pixelShiftSigma->setDefault( defParams->raw.bayersensor.pixelShiftSigma); + pixelShiftSum->setDefault( defParams->raw.bayersensor.pixelShiftSum); + pixelShiftRedBlueWeight->setDefault( defParams->raw.bayersensor.pixelShiftRedBlueWeight); ccSteps->setDefault (defParams->raw.bayersensor.ccSteps); if (pedited) { @@ -439,6 +557,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); pixelShiftPrnu->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); + pixelShiftSigma->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); + pixelShiftSum->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); + pixelShiftRedBlueWeight->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); ccSteps->setDefaultEditedState(pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); } else { dcbIterations->setDefaultEditedState( Irrelevant ); @@ -450,6 +571,9 @@ void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams pixelShiftEperIso->setDefaultEditedState( Irrelevant ); pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); pixelShiftPrnu->setDefaultEditedState( Irrelevant ); + pixelShiftSigma->setDefaultEditedState( Irrelevant ); + pixelShiftSum->setDefaultEditedState( Irrelevant ); + pixelShiftRedBlueWeight->setDefaultEditedState( Irrelevant ); ccSteps->setDefaultEditedState(Irrelevant ); } } @@ -477,6 +601,12 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvPixelShiftNreadIso, a->getTextValue() ); } else if (a == pixelShiftPrnu) { listener->panelChanged (EvPixelShiftPrnu, a->getTextValue() ); + } else if (a == pixelShiftSigma) { + listener->panelChanged (EvPixelShiftSigma, a->getTextValue() ); + } else if (a == pixelShiftSum) { + listener->panelChanged (EvPixelShiftSum, a->getTextValue() ); + } else if (a == pixelShiftRedBlueWeight) { + listener->panelChanged (EvPixelShiftRedBlueWeight, a->getTextValue() ); } } } @@ -631,12 +761,23 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftEperIso->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNreadIso->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftSigma->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftSum->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftRedBlueWeight->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftStddevFactorGreen->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftStddevFactorRed->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftStddevFactorBlue->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftMedian->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftMedian3->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMedian->get_active()); + pixelShiftGreen->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftExp0->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftNonGreenAmaze->set_sensitive(pixelShiftAutomatic->get_active ()); if (listener) { listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); @@ -684,6 +825,128 @@ void BayerProcess::pixelShiftNonGreenVerticalChanged () } } +void BayerProcess::pixelShiftHoleFillChanged () +{ + if (batchMode) { + if (pixelShiftHoleFill->get_inconsistent()) { + pixelShiftHoleFill->set_inconsistent (false); + pixelShiftHoleFillconn.block (true); + pixelShiftHoleFill->set_active (false); + pixelShiftHoleFillconn.block (false); + } else if (lastDCBen) { + pixelShiftHoleFill->set_inconsistent (true); + } + + lastDCBen = pixelShiftHoleFill->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftHoleFill, pixelShiftHoleFill->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftMedianChanged () +{ + if (batchMode) { + if (pixelShiftMedian->get_inconsistent()) { + pixelShiftMedian->set_inconsistent (false); + pixelShiftMedianconn.block (true); + pixelShiftMedian->set_active (false); + pixelShiftMedianconn.block (false); + } else if (lastDCBen) { + pixelShiftMedian->set_inconsistent (true); + } + + lastDCBen = pixelShiftMedian->get_active (); + } + + pixelShiftMedian3->set_sensitive(pixelShiftMedian->get_active ()); + + if (listener) { + listener->panelChanged (EvPixelShiftMedian, pixelShiftMedian->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftMedian3Changed () +{ + if (batchMode) { + if (pixelShiftMedian3->get_inconsistent()) { + pixelShiftMedian3->set_inconsistent (false); + pixelShiftMedian3conn.block (true); + pixelShiftMedian3->set_active (false); + pixelShiftMedian3conn.block (false); + } else if (lastDCBen) { + pixelShiftMedian3->set_inconsistent (true); + } + + lastDCBen = pixelShiftMedian3->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftMedian3, pixelShiftMedian3->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftGreenChanged () +{ + if (batchMode) { + if (pixelShiftGreen->get_inconsistent()) { + pixelShiftGreen->set_inconsistent (false); + pixelShiftGreenconn.block (true); + pixelShiftGreen->set_active (false); + pixelShiftGreenconn.block (false); + } else if (lastDCBen) { + pixelShiftGreen->set_inconsistent (true); + } + + lastDCBen = pixelShiftGreen->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftGreen, pixelShiftGreen->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftBlurChanged () +{ + if (batchMode) { + if (pixelShiftBlur->get_inconsistent()) { + pixelShiftBlur->set_inconsistent (false); + pixelShiftBlurconn.block (true); + pixelShiftBlur->set_active (false); + pixelShiftBlurconn.block (false); + } else if (lastDCBen) { + pixelShiftBlur->set_inconsistent (true); + } + + lastDCBen = pixelShiftBlur->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftExp0Changed () +{ + if (batchMode) { + if (pixelShiftExp0->get_inconsistent()) { + pixelShiftExp0->set_inconsistent (false); + pixelShiftExp0conn.block (true); + pixelShiftExp0->set_active (false); + pixelShiftExp0conn.block (false); + } else if (lastDCBen) { + pixelShiftExp0->set_inconsistent (true); + } + + lastDCBen = pixelShiftExp0->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftExp0, pixelShiftExp0->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + void BayerProcess::pixelShiftNonGreenCrossChanged () { if (batchMode) { @@ -704,21 +967,42 @@ void BayerProcess::pixelShiftNonGreenCrossChanged () } } - -/*void BayerProcess::allEnhanceChanged () +void BayerProcess::pixelShiftNonGreenCross2Changed () { if (batchMode) { - if (allEnhance->get_inconsistent()) { - allEnhance->set_inconsistent (false); - allEnhconn.block (true); - allEnhance->set_active (false); - allEnhconn.block (false); + if (pixelShiftNonGreenCross2->get_inconsistent()) { + pixelShiftNonGreenCross2->set_inconsistent (false); + pixelShiftNonGreenCross2conn.block (true); + pixelShiftNonGreenCross2->set_active (false); + pixelShiftNonGreenCross2conn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenCross2->set_inconsistent (true); } - else if (lastALLen) - allEnhance->set_inconsistent (true); - lastALLen = allEnhance->get_active (); + lastDCBen = pixelShiftNonGreenCross2->get_active (); } - if (listener) - listener->panelChanged (EvDemosaicALLEnhanced, allEnhance->get_active()?M("GENERAL_ENABLED"):M("GENERAL_DISABLED")); -}*/ + + if (listener) { + listener->panelChanged (EvPixelShiftGreenAmaze, pixelShiftNonGreenCross2->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + +void BayerProcess::pixelShiftNonGreenAmazeChanged () +{ + if (batchMode) { + if (pixelShiftNonGreenAmaze->get_inconsistent()) { + pixelShiftNonGreenAmaze->set_inconsistent (false); + pixelShiftNonGreenAmazeconn.block (true); + pixelShiftNonGreenAmaze->set_active (false); + pixelShiftNonGreenAmazeconn.block (false); + } else if (lastDCBen) { + pixelShiftNonGreenAmaze->set_inconsistent (true); + } + + lastDCBen = pixelShiftNonGreenAmaze->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index ca560ad89..79593debf 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -50,16 +50,30 @@ protected: Gtk::CheckButton* pixelShiftNonGreenHorizontal; Gtk::CheckButton* pixelShiftNonGreenVertical; Gtk::CheckButton* pixelShiftNonGreenCross; + Gtk::CheckButton* pixelShiftNonGreenCross2; + Gtk::CheckButton* pixelShiftNonGreenAmaze; + Gtk::CheckButton* pixelShiftGreen; + Gtk::CheckButton* pixelShiftBlur; + Gtk::CheckButton* pixelShiftExp0; + Gtk::CheckButton* pixelShiftHoleFill; + Gtk::CheckButton* pixelShiftMedian; + Gtk::CheckButton* pixelShiftMedian3; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; Adjuster* pixelShiftPrnu; + Adjuster* pixelShiftSigma; + Adjuster* pixelShiftSum; + Adjuster* pixelShiftRedBlueWeight; bool lastDCBen; int oldMethod; //bool lastALLen; - sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftNonGreenCrossconn; //,allEnhconn; + sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, + pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, + pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftHoleFillconn, pixelShiftMedianconn, pixelShiftMedian3conn, pixelShiftNonGreenCrossconn, + pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftExp0conn; public: BayerProcess (); @@ -79,8 +93,15 @@ public: void pixelShiftAutomaticChanged(); void pixelShiftNonGreenHorizontalChanged(); void pixelShiftNonGreenVerticalChanged(); + void pixelShiftHoleFillChanged(); + void pixelShiftMedianChanged(); + void pixelShiftMedian3Changed(); + void pixelShiftGreenChanged(); + void pixelShiftBlurChanged(); + void pixelShiftExp0Changed(); void pixelShiftNonGreenCrossChanged(); - //void allEnhanceChanged(); + void pixelShiftNonGreenCross2Changed(); + void pixelShiftNonGreenAmazeChanged(); }; #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index f433ec639..b6127b7d2 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -378,12 +378,23 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftEperIso = v; raw.bayersensor.pixelShiftNreadIso = v; raw.bayersensor.pixelShiftPrnu = v; + raw.bayersensor.pixelShiftSigma = v; + raw.bayersensor.pixelShiftSum = v; + raw.bayersensor.pixelShiftRedBlueWeight = v; raw.bayersensor.pixelshiftShowMotion = v; raw.bayersensor.pixelshiftShowMotionMaskOnly = v; raw.bayersensor.pixelShiftAutomatic = v; raw.bayersensor.pixelShiftNonGreenHorizontal = v; raw.bayersensor.pixelShiftNonGreenVertical = v; + raw.bayersensor.pixelShiftHoleFill = v; + raw.bayersensor.pixelShiftMedian = v; + raw.bayersensor.pixelShiftMedian3 = v; + raw.bayersensor.pixelShiftGreen = v; + raw.bayersensor.pixelShiftBlur = v; + raw.bayersensor.pixelShiftExp0 = v; raw.bayersensor.pixelShiftNonGreenCross = v; + raw.bayersensor.pixelShiftNonGreenCross2 = v; + raw.bayersensor.pixelShiftNonGreenAmaze = v; raw.bayersensor.greenEq = v; raw.bayersensor.linenoise = v; raw.xtranssensor.method = v; @@ -888,12 +899,23 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftEperIso = raw.bayersensor.pixelShiftEperIso && p.raw.bayersensor.pixelShiftEperIso == other.raw.bayersensor.pixelShiftEperIso; raw.bayersensor.pixelShiftNreadIso = raw.bayersensor.pixelShiftNreadIso && p.raw.bayersensor.pixelShiftNreadIso == other.raw.bayersensor.pixelShiftNreadIso; raw.bayersensor.pixelShiftPrnu = raw.bayersensor.pixelShiftPrnu && p.raw.bayersensor.pixelShiftPrnu == other.raw.bayersensor.pixelShiftPrnu; + raw.bayersensor.pixelShiftSigma = raw.bayersensor.pixelShiftSigma && p.raw.bayersensor.pixelShiftSigma == other.raw.bayersensor.pixelShiftSigma; + raw.bayersensor.pixelShiftSum = raw.bayersensor.pixelShiftSum && p.raw.bayersensor.pixelShiftSum == other.raw.bayersensor.pixelShiftSum; + raw.bayersensor.pixelShiftRedBlueWeight = raw.bayersensor.pixelShiftRedBlueWeight && p.raw.bayersensor.pixelShiftRedBlueWeight == other.raw.bayersensor.pixelShiftRedBlueWeight; raw.bayersensor.pixelshiftShowMotion = raw.bayersensor.pixelshiftShowMotion && p.raw.bayersensor.pixelshiftShowMotion == other.raw.bayersensor.pixelshiftShowMotion; raw.bayersensor.pixelshiftShowMotionMaskOnly = raw.bayersensor.pixelshiftShowMotionMaskOnly && p.raw.bayersensor.pixelshiftShowMotionMaskOnly == other.raw.bayersensor.pixelshiftShowMotionMaskOnly; raw.bayersensor.pixelShiftAutomatic = raw.bayersensor.pixelShiftAutomatic && p.raw.bayersensor.pixelShiftAutomatic == other.raw.bayersensor.pixelShiftAutomatic; raw.bayersensor.pixelShiftNonGreenHorizontal = raw.bayersensor.pixelShiftNonGreenHorizontal && p.raw.bayersensor.pixelShiftNonGreenHorizontal == other.raw.bayersensor.pixelShiftNonGreenHorizontal; raw.bayersensor.pixelShiftNonGreenVertical = raw.bayersensor.pixelShiftNonGreenVertical && p.raw.bayersensor.pixelShiftNonGreenVertical == other.raw.bayersensor.pixelShiftNonGreenVertical; + raw.bayersensor.pixelShiftHoleFill = raw.bayersensor.pixelShiftHoleFill && p.raw.bayersensor.pixelShiftHoleFill == other.raw.bayersensor.pixelShiftHoleFill; + raw.bayersensor.pixelShiftMedian = raw.bayersensor.pixelShiftMedian && p.raw.bayersensor.pixelShiftMedian == other.raw.bayersensor.pixelShiftMedian; + raw.bayersensor.pixelShiftMedian3 = raw.bayersensor.pixelShiftMedian3 && p.raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3; + raw.bayersensor.pixelShiftGreen = raw.bayersensor.pixelShiftGreen && p.raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen; + raw.bayersensor.pixelShiftBlur = raw.bayersensor.pixelShiftBlur && p.raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur; + raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; + raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; + raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; raw.bayersensor.greenEq = raw.bayersensor.greenEq && p.raw.bayersensor.greenthresh == other.raw.bayersensor.greenthresh; raw.bayersensor.linenoise = raw.bayersensor.linenoise && p.raw.bayersensor.linenoise == other.raw.bayersensor.linenoise; raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method; @@ -2338,6 +2360,18 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftPrnu = mods.raw.bayersensor.pixelShiftPrnu; } + if (raw.bayersensor.pixelShiftSigma) { + toEdit.raw.bayersensor.pixelShiftSigma = mods.raw.bayersensor.pixelShiftSigma; + } + + if (raw.bayersensor.pixelShiftSum) { + toEdit.raw.bayersensor.pixelShiftSum = mods.raw.bayersensor.pixelShiftSum; + } + + if (raw.bayersensor.pixelShiftRedBlueWeight) { + toEdit.raw.bayersensor.pixelShiftRedBlueWeight = mods.raw.bayersensor.pixelShiftRedBlueWeight; + } + if (raw.bayersensor.pixelshiftShowMotion) { toEdit.raw.bayersensor.pixelshiftShowMotion = mods.raw.bayersensor.pixelshiftShowMotion; } @@ -2358,11 +2392,42 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftNonGreenVertical = mods.raw.bayersensor.pixelShiftNonGreenVertical; } + if (raw.bayersensor.pixelShiftHoleFill) { + toEdit.raw.bayersensor.pixelShiftHoleFill = mods.raw.bayersensor.pixelShiftHoleFill; + } + + if (raw.bayersensor.pixelShiftMedian) { + toEdit.raw.bayersensor.pixelShiftMedian = mods.raw.bayersensor.pixelShiftMedian; + } + + if (raw.bayersensor.pixelShiftMedian3) { + toEdit.raw.bayersensor.pixelShiftMedian3 = mods.raw.bayersensor.pixelShiftMedian3; + } + + if (raw.bayersensor.pixelShiftGreen) { + toEdit.raw.bayersensor.pixelShiftGreen = mods.raw.bayersensor.pixelShiftGreen; + } + + if (raw.bayersensor.pixelShiftBlur) { + toEdit.raw.bayersensor.pixelShiftBlur = mods.raw.bayersensor.pixelShiftBlur; + } + + if (raw.bayersensor.pixelShiftExp0) { + toEdit.raw.bayersensor.pixelShiftExp0 = mods.raw.bayersensor.pixelShiftExp0; + } + if (raw.bayersensor.pixelShiftNonGreenCross) { toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; } - //if (raw.bayersensor.allEnhance) toEdit.raw.bayersensor.all_enhance = mods.raw.bayersensor.all_enhance; + if (raw.bayersensor.pixelShiftNonGreenCross2) { + toEdit.raw.bayersensor.pixelShiftNonGreenCross2 = mods.raw.bayersensor.pixelShiftNonGreenCross2; + } + + if (raw.bayersensor.pixelShiftNonGreenAmaze) { + toEdit.raw.bayersensor.pixelShiftNonGreenAmaze = mods.raw.bayersensor.pixelShiftNonGreenAmaze; + } + if (raw.bayersensor.greenEq) { toEdit.raw.bayersensor.greenthresh = dontforceSet && options.baBehav[ADDSET_PREPROCESS_GREENEQUIL] ? toEdit.raw.bayersensor.greenthresh + mods.raw.bayersensor.greenthresh : mods.raw.bayersensor.greenthresh; } @@ -2874,8 +2939,8 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const { return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso - && pixelShiftNreadIso && pixelShiftPrnu && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftNonGreenCross + && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftExp0 && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index b4af2ad05..6a72f9ffa 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -700,12 +700,23 @@ public: bool pixelShiftEperIso; bool pixelShiftNreadIso; bool pixelShiftPrnu; + bool pixelShiftSigma; + bool pixelShiftSum; + bool pixelShiftRedBlueWeight; bool pixelshiftShowMotion; bool pixelshiftShowMotionMaskOnly; bool pixelShiftAutomatic; bool pixelShiftNonGreenHorizontal; bool pixelShiftNonGreenVertical; + bool pixelShiftHoleFill; + bool pixelShiftMedian; + bool pixelShiftMedian3; + bool pixelShiftGreen; + bool pixelShiftBlur; + bool pixelShiftExp0; bool pixelShiftNonGreenCross; + bool pixelShiftNonGreenCross2; + bool pixelShiftNonGreenAmaze; //bool allEnhance; bool greenEq; From fba1cff9a006cfeed3a0531b6cd8af48b50b4ab7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 13 Jan 2017 21:59:14 +0100 Subject: [PATCH 063/181] Pixelshift: Fix bug saving pixelshift median parameter in pp3 --- rtengine/procparams.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index a7443a87c..d7cbad13c 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -3462,7 +3462,7 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b } if (!pedited || pedited->raw.bayersensor.pixelShiftMedian3) { - keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian", raw.bayersensor.pixelShiftMedian3 ); + keyFile.set_boolean ("RAW Bayer", "pixelShiftMedian3", raw.bayersensor.pixelShiftMedian3 ); } if (!pedited || pedited->raw.bayersensor.pixelShiftGreen) { From dfd7c29b0648f67fda933aad3bfa2d8b8a83238f Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 14 Jan 2017 00:36:22 +0100 Subject: [PATCH 064/181] Forgot to add AUTHORS.txt with last commit --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 6aebfcf6e..3aa555bc0 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -56,5 +56,6 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati Alberto Righetto Kostia (Kildor) Romanov Johan Thor + Vitalis Tiknius TooWaBoo Colin Walker From 4ecdf8dca9cde1d78ff9971418c006350d7f31af Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 15 Jan 2017 23:11:53 +0100 Subject: [PATCH 065/181] Added one file which I forgot with last commit --- rtgui/cropwindow.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 20dd3eb9d..2dbdf0e8b 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -61,9 +61,10 @@ ZoomStep zoomSteps[] = { {"500%", 5.0, 5000}, {"600%", 6.0, 6000}, {"700%", 7.0, 7000}, - {"800%", 8.0, 8000} + {"800%", 8.0, 8000}, + {"1600%", 16.0, 16000} }; -#define MAXZOOMSTEPS 20 +#define MAXZOOMSTEPS 21 #define ZOOM11INDEX 13 CropWindow::CropWindow (ImageArea* parent, bool isLowUpdatePriority_, bool isDetailWindow) From 70415fdfe5ef87e70ee91b76536c834ad27248cc Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 18 Jan 2017 19:36:35 +0100 Subject: [PATCH 066/181] pixelshift: cleaned gui --- rtdata/languages/default | 2 + rtengine/pixelshift.cc | 45 ++++++++++++-- rtengine/procevents.h | 1 + rtengine/procparams.cc | 45 ++++++++++++++ rtengine/procparams.h | 7 +++ rtengine/rawimagesource.cc | 18 ++++-- rtengine/refreshmap.cc | 3 +- rtgui/bayerprocess.cc | 124 +++++++++++++++++++++++++------------ rtgui/bayerprocess.h | 5 +- rtgui/paramsedited.cc | 8 ++- rtgui/paramsedited.h | 1 + 11 files changed, 206 insertions(+), 53 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 4b1b329e9..2a4943424 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -700,6 +700,7 @@ HISTORY_MSG_465;EvPixelShiftSum HISTORY_MSG_466;EvPixelShiftExp0 HISTORY_MSG_467;EvPixelShiftHoleFill HISTORY_MSG_468;EvPixelShiftMedian +HISTORY_MSG_469;EvPixelShiftMotionMethod HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1700,6 +1701,7 @@ TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the r TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP;Shows the motion mask without the image TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid +TP_RAW_PIXELSHIFTMOTIONMETHOD;Motion Correction TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN;StdDev factor Green diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index ae2f08fdb..1fde4156d 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -31,6 +31,7 @@ #include "../rtgui/multilangmgr.h" #include "procparams.h" #include "gauss.h" +#include "median.h" #define BENCHMARK #include "StopWatch.h" @@ -1162,9 +1163,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } // now that the temporary planes are filled for easy access we do the motion detection - + int sum0 = 0; + int sum1 = 0; + float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; #ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) + #pragma omp parallel for reduction(+:sum0,sum1) schedule(dynamic,16) #endif for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { @@ -1338,6 +1341,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } if (gridMax > thresh - korr) { + if(offset == 0) { + sum0 ++; + } else { + sum1 ++; + } + if(nOf3x3) { psMask[i][j] = 1.f; } else if((offset == (frame & 1)) && checkNonGreenVertical) { @@ -1357,7 +1366,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA j++; paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; } @@ -1547,6 +1555,20 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } if(adaptive && experimental0) { // for experiments +// float green1Median, green2Median; +// green1Median = median(psG1[ i - 1 ][ j - 1 ],psG1[ i - 1 ][ j + 1 ],psG1[ i ][ j ],psG1[ i + 1 ][ j -1 ],psG1[ i + 1 ][ j + 1 ]); +// green2Median = median(psG2[ i - 1 ][ j - 1 ],psG2[ i - 1 ][ j + 1 ],psG2[ i ][ j ],psG2[ i + 1 ][ j -1 ],psG2[ i + 1 ][ j + 1 ]); +// float greenDiffMedian = nonGreenDiff(green1Median, green2Median, stddevFactorGreen * 0.36f, eperIsoGreen, nRead, prnu, showMotion); +// +// if(greenDiffMedian > 0.f) { +// if(nOf3x3) { +// psMask[i][j] = 1.f; +// } else { +// paintMotionMask(j + offsX, showMotion, greenDiffMedian, showOnlyMask, greenDest, redDest, blueDest); +// } +// +// continue; +// } } @@ -1561,6 +1583,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } + float percent0 = 100.f * sum0 / pixelcount; + float percent1 = 100.f * sum1 / pixelcount; + + std::cout << fileName << " : Green detections at stddev " << std::setprecision( 2 ) << bayerParams.pixelShiftStddevFactorGreen << " : Frame 1/3 : " << std::setprecision( 6 ) << sum0 << " (" << percent0 << "%)" << " Frame 2/4 : " << sum1 << " (" << percent1 << "%)" << std::endl; + if(adaptive && nOf3x3) { if(blurMap) { #pragma omp parallel @@ -1623,9 +1650,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else { - red[i + offsY][j + offsX] = psRed[i][j]; - green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; - blue[i + offsY][j + offsX] = psBlue[i][j]; + if(blurMap && experimental0) { + red[i + offsY][j + offsX] = intp(psMask[i][j], red[i + offsY][j + offsX], psRed[i][j] ); + green[i + offsY][j + offsX] = intp(psMask[i][j],green[i + offsY][j + offsX],(psG1[i][j] + psG2[i][j]) / 2.f); + blue[i + offsY][j + offsX] = intp(psMask[i][j],blue[i + offsY][j + offsX], psBlue[i][j]); + } else { + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } } } } diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 4526b13f3..75b64d30d 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -496,6 +496,7 @@ enum ProcEvent { EvPixelShiftHoleFill = 466, EvPixelShiftMedian = 467, EvPixelShiftMedian3 = 468, + EvPixelShiftMotionMethod = 469, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index d7cbad13c..0597be182 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -875,6 +875,33 @@ void CoarseTransformParams::setDefaults() hflip = false; vflip = false; } +void RAWParams::BayerSensor::setPixelShiftDefaults() +{ + pixelShiftMotion = 0; + pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid3x3New; + pixelShiftMotionCorrectionMethod = RAWParams::BayerSensor::Automatic; + pixelShiftStddevFactorGreen = 5.0; + pixelShiftStddevFactorRed = 5.0; + pixelShiftStddevFactorBlue = 5.0; + pixelShiftEperIso = 0.0; + pixelShiftNreadIso = 0.0; + pixelShiftPrnu = 1.0; + pixelShiftSigma = 1.0; + pixelShiftSum = 3.0; + pixelShiftRedBlueWeight = 0.7; + pixelShiftAutomatic = true; + pixelShiftNonGreenHorizontal = false; + pixelShiftNonGreenVertical = false; + pixelShiftHoleFill = true; + pixelShiftMedian = false; + pixelShiftMedian3 = false; + pixelShiftGreen = true; + pixelShiftBlur = true; + pixelShiftExp0 = false; + pixelShiftNonGreenCross = true; + pixelShiftNonGreenCross2 = false; + pixelShiftNonGreenAmaze = false; +} void RAWParams::setDefaults() { @@ -887,6 +914,7 @@ void RAWParams::setDefaults() bayersensor.lmmse_iterations = 2; bayersensor.pixelShiftMotion = 0; bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid3x3New; + bayersensor.pixelShiftMotionCorrectionMethod = RAWParams::BayerSensor::Automatic; bayersensor.pixelShiftStddevFactorGreen = 5.0; bayersensor.pixelShiftStddevFactorRed = 5.0; bayersensor.pixelShiftStddevFactorBlue = 5.0; @@ -939,6 +967,10 @@ void RAWParams::setDefaults() hotPixelFilter = false; deadPixelFilter = false; hotdeadpix_thresh = 100; + bayersensor.setPixelShiftDefaults(); + bayersensor.pixelshiftShowMotion = false; + bayersensor.pixelshiftShowMotionMaskOnly = false; + } void ColorManagementParams::setDefaults() @@ -3397,6 +3429,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrection", raw.bayersensor.pixelShiftMotionCorrection ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod) { + keyFile.set_integer ("RAW Bayer", "PixelShiftMotionCorrectionMethod", raw.bayersensor.pixelShiftMotionCorrectionMethod ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftStddevFactorGreen) { keyFile.set_double ("RAW Bayer", "pixelShiftStddevFactorGreen", raw.bayersensor.pixelShiftStddevFactorGreen ); } @@ -7561,6 +7597,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "PixelShiftMotionCorrectionMethod")) { + raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)keyFile.get_integer("RAW Bayer", "PixelShiftMotionCorrectionMethod"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftStddevFactorGreen")) { raw.bayersensor.pixelShiftStddevFactorGreen = keyFile.get_double("RAW Bayer", "pixelShiftStddevFactorGreen"); @@ -8184,6 +8228,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations && raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion && raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection + && raw.bayersensor.pixelShiftMotionCorrectionMethod == other.raw.bayersensor.pixelShiftMotionCorrectionMethod && raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen && raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed && raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue diff --git a/rtengine/procparams.h b/rtengine/procparams.h index c3c8296b7..51d55bedc 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1190,6 +1190,9 @@ public: enum ePSMotionCorrection { Grid1x1, Grid1x2, Grid3x3, Grid5x5, Grid7x7, Grid3x3New }; + enum ePSMotionCorrectionMethod { + Off, Automatic, Custom + }; static const char *methodstring[numMethods]; Glib::ustring method; @@ -1206,6 +1209,7 @@ public: int lmmse_iterations; int pixelShiftMotion; ePSMotionCorrection pixelShiftMotionCorrection; + ePSMotionCorrectionMethod pixelShiftMotionCorrectionMethod; double pixelShiftStddevFactorGreen; double pixelShiftStddevFactorRed; double pixelShiftStddevFactorBlue; @@ -1231,6 +1235,9 @@ public: bool pixelShiftNonGreenAmaze; bool dcb_enhance; //bool all_enhance; + + void setPixelShiftDefaults(); + }; /** diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 566fc77a3..11d7a2af2 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2027,10 +2027,18 @@ void RawImageSource::demosaic(const RAWParams &raw) if(numFrames != 4) { // fallback for non pixelshift files amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); } else { - if(!raw.bayersensor.pixelshiftShowMotion || raw.bayersensor.pixelShiftNonGreenAmaze || raw.bayersensor.pixelShiftNonGreenCross2) { - if((raw.bayersensor.pixelShiftMotion > 0 || raw.bayersensor.pixelShiftAutomatic) && numFrames == 4) { - if(raw.bayersensor.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction - if(!raw.bayersensor.pixelShiftMedian3) { + RAWParams::BayerSensor bayerParams = raw.bayersensor; + if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic) { + bayerParams.setPixelShiftDefaults(); + } else if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Off) { + bayerParams.pixelShiftMotion = 0; + bayerParams.pixelShiftAutomatic = false; + bayerParams.pixelshiftShowMotion = false; + } + if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftExp0)) { + if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { + if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction + if(!bayerParams.pixelShiftMedian3) { amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[0]), red, green, blue); multi_array2D redTmp(W,H); multi_array2D greenTmp(W,H); @@ -2122,7 +2130,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } } - pixelshift(0, 0, W, H, raw.bayersensor, raw.bayersensor.pixelShiftExp0 ? 0: currFrame, ri->get_model(), raw.expos); + pixelshift(0, 0, W, H, bayerParams, currFrame, ri->get_model(), raw.expos); } } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 3030a4319..72fe3b76a 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -495,7 +495,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftExp0 DEMOSAIC, // EvPixelShiftHoleFill DEMOSAIC, // EvPixelShiftMedian - DEMOSAIC // EvPixelShiftMedian3 + DEMOSAIC, // EvPixelShiftMedian3 + DEMOSAIC // EvPixelShiftMotionMethod }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 701463ac4..1fcb2911e 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -50,6 +50,18 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA imageNumberBox->pack_end (*imageNumber, Gtk::PACK_EXPAND_WIDGET, 4); pack_start( *imageNumberBox, Gtk::PACK_SHRINK, 4); + pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); + ccSteps = Gtk::manage (new Adjuster (M("TP_RAW_FALSECOLOR"), 0, 5, 1, 0 )); + ccSteps->setAdjusterListener (this); + + if (ccSteps->delay < options.adjusterMaxDelay) { + ccSteps->delay = options.adjusterMaxDelay; + } + + ccSteps->show(); + pack_start( *ccSteps, Gtk::PACK_SHRINK, 4); + + dcbOptions = Gtk::manage (new Gtk::VBox ()); dcbOptions->set_border_width(4); @@ -81,16 +93,30 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA lmmseOptions->pack_start(*lmmseIterations); pack_start( *lmmseOptions, Gtk::PACK_SHRINK, 4); + pixelShiftFrame = Gtk::manage (new Gtk::VBox ()); + pixelShiftFrame->set_border_width(0); + + Gtk::HBox* hb3 = Gtk::manage (new Gtk::HBox ()); + hb3->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONMETHOD") + ": ")), Gtk::PACK_SHRINK, 4); + pixelShiftMotionMethod = Gtk::manage (new MyComboBoxText ()); + pixelShiftMotionMethod->append_text("Off"); + pixelShiftMotionMethod->append_text("Automatic"); + pixelShiftMotionMethod->append_text("Custom"); + pixelShiftMotionMethod->set_active(1); + pixelShiftMotionMethod->show(); + hb3->pack_start(*pixelShiftMotionMethod); + pixelShiftFrame->pack_start(*hb3); + pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); - pixelShiftOptions->set_border_width(4); + pixelShiftOptions->set_border_width(0); pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); pixelShiftShowMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP")); - pixelShiftOptions->pack_start(*pixelShiftShowMotion); + pixelShiftFrame->pack_start(*pixelShiftShowMotion); pixelShiftShowMotionMaskOnly = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); pixelShiftShowMotionMaskOnly->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP")); - pixelShiftOptions->pack_start(*pixelShiftShowMotionMaskOnly); + pixelShiftFrame->pack_start(*pixelShiftShowMotionMaskOnly); pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); @@ -142,8 +168,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotion->show(); pixelShiftOptions->pack_start(*pixelShiftMotion); + Gtk::HBox* hb2 = Gtk::manage (new Gtk::HBox ()); - hb2->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONCORRECTION") + ": ")), Gtk::PACK_SHRINK, 4); + hb2->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONCORRECTION") + ": ")), Gtk::PACK_SHRINK, 0); pixelShiftMotionCorrection = Gtk::manage (new MyComboBoxText ()); pixelShiftMotionCorrection->append_text("1x1"); pixelShiftMotionCorrection->append_text("1x2"); @@ -152,7 +179,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->append_text("7x7"); pixelShiftMotionCorrection->append_text("3x3 new"); pixelShiftMotionCorrection->set_active(0); -// pixelShiftMotionCorrection->set_tooltip_markup (M("TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP")); pixelShiftMotionCorrection->show(); hb2->pack_start(*pixelShiftMotionCorrection); pixelShiftOptions->pack_start(*hb2); @@ -218,7 +244,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftPrnu->show(); pixelShiftOptions->pack_start(*pixelShiftPrnu); - pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 2.5, 0.1, 1.0)); + pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 25, 0.1, 1.0)); pixelShiftSigma->setAdjusterListener (this); if (pixelShiftSigma->delay < options.adjusterMaxDelay) { @@ -248,19 +274,10 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftRedBlueWeight->show(); pixelShiftOptions->pack_start(*pixelShiftRedBlueWeight); - pack_start( *pixelShiftOptions, Gtk::PACK_SHRINK, 4); + pixelShiftFrame->pack_start(*pixelShiftOptions); + pixelShiftOptions->hide(); - - pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); - ccSteps = Gtk::manage (new Adjuster (M("TP_RAW_FALSECOLOR"), 0, 5, 1, 0 )); - ccSteps->setAdjusterListener (this); - - if (ccSteps->delay < options.adjusterMaxDelay) { - ccSteps->delay = options.adjusterMaxDelay; - } - - ccSteps->show(); - pack_start( *ccSteps, Gtk::PACK_SHRINK, 4); + pack_start( *pixelShiftFrame, Gtk::PACK_SHRINK, 4); //pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); //allOptions = Gtk::manage (new Gtk::VBox ()); @@ -273,6 +290,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA psmcconn = pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) ); imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); + pixelShiftMotionMethodConn = pixelShiftMotionMethod->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::pixelShiftMotionMethodChanged) ); pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); pixelShiftShowMotionMaskOnlyconn = pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true); pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); @@ -349,6 +367,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params if(!pedited->raw.bayersensor.pixelShiftMotionCorrection) { pixelShiftMotionCorrection->set_active_text(M("GENERAL_UNCHANGED")); } + if(!pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod) { + pixelShiftMotionMethod->set_active_text(M("GENERAL_UNCHANGED")); + } } //allEnhance->set_active(pp->raw.bayersensor.all_enhance); @@ -373,6 +394,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); + pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); @@ -398,9 +420,14 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::pixelshift] || method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { - pixelShiftOptions->show(); + if(pp->raw.bayersensor.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Custom) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + pixelShiftFrame->show(); } else { - pixelShiftOptions->hide(); + pixelShiftFrame->hide(); } // Flase color suppression is applied to all demozaicing method, so don't hide anything @@ -433,6 +460,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); + pp->raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)pixelShiftMotionMethod->get_active_row_number(); pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); @@ -478,6 +506,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); + pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = pixelShiftMotionMethod->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getEditedState (); pedited->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getEditedState (); pedited->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getEditedState (); @@ -510,6 +539,8 @@ void BayerProcess::setBatchMode(bool batchMode) method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name pixelShiftMotionCorrection->append_text (M("GENERAL_UNCHANGED")); pixelShiftMotionCorrection->set_active (4); + pixelShiftMotionMethod->append_text (M("GENERAL_UNCHANGED")); + pixelShiftMotionMethod->set_active (4); imageNumber->append_text (M("GENERAL_UNCHANGED")); imageNumber->set_active(4); dcbOptions->hide(); @@ -613,26 +644,16 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) void BayerProcess::psMotionCorrectionChanged () { - int curSelection = pixelShiftMotionCorrection->get_active_row_number(); - Glib::ustring sGrid; - switch (curSelection) { - case 0: - sGrid = "1x1"; - break; - case 1: - sGrid = "1x2"; - break; - case 2: - sGrid = "3x3"; - break; - case 3: - default: - sGrid = "5x5"; - break; + if(pixelShiftMotionCorrection->get_active_row_number() == 5) { + pixelShiftBlur->set_sensitive(true); + pixelShiftHoleFill->set_sensitive(true); + } else { + pixelShiftBlur->set_sensitive(false); + pixelShiftHoleFill->set_sensitive(false); } if (listener) { - listener->panelChanged (EvPixelShiftMotionCorrection, sGrid); + listener->panelChanged (EvPixelShiftMotionCorrection, pixelShiftMotionCorrection->get_active_text()); } } @@ -653,9 +674,14 @@ void BayerProcess::methodChanged () } if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { - pixelShiftOptions->show(); + if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + pixelShiftFrame->show(); } else { - pixelShiftOptions->hide(); + pixelShiftFrame->hide(); } Glib::ustring methodName = ""; @@ -703,6 +729,26 @@ void BayerProcess::dcbEnhanceChanged () } } +void BayerProcess::pixelShiftMotionMethodChanged () +{ + if(pixelShiftMotionMethod->get_active_row_number() == 0) { + pixelShiftOptions->hide(); + pixelShiftShowMotion->hide(); + pixelShiftShowMotionMaskOnly->hide(); + } else if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } else { + pixelShiftOptions->hide(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } + if (listener) { + listener->panelChanged (EvPixelShiftMotionMethod, pixelShiftMotionMethod->get_active_text()); + } +} + void BayerProcess::pixelShiftShowMotionChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 79593debf..e53cb6816 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -41,9 +41,11 @@ protected: //Gtk::CheckButton* allEnhance; Gtk::VBox *lmmseOptions; Adjuster* lmmseIterations; + Gtk::VBox *pixelShiftFrame; Gtk::VBox *pixelShiftOptions; Adjuster* pixelShiftMotion; MyComboBoxText* pixelShiftMotionCorrection; + MyComboBoxText* pixelShiftMotionMethod; Gtk::CheckButton* pixelShiftShowMotion; Gtk::CheckButton* pixelShiftShowMotionMaskOnly; Gtk::CheckButton* pixelShiftAutomatic; @@ -73,7 +75,7 @@ protected: sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftHoleFillconn, pixelShiftMedianconn, pixelShiftMedian3conn, pixelShiftNonGreenCrossconn, - pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftExp0conn; + pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftExp0conn, pixelShiftMotionMethodConn; public: BayerProcess (); @@ -102,6 +104,7 @@ public: void pixelShiftNonGreenCrossChanged(); void pixelShiftNonGreenCross2Changed(); void pixelShiftNonGreenAmazeChanged(); + void pixelShiftMotionMethodChanged(); }; #endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index b6127b7d2..185341e32 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -372,6 +372,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.lmmseIterations = v; raw.bayersensor.pixelShiftMotion = v; raw.bayersensor.pixelShiftMotionCorrection = v; + raw.bayersensor.pixelShiftMotionCorrectionMethod = v; raw.bayersensor.pixelShiftStddevFactorGreen = v; raw.bayersensor.pixelShiftStddevFactorRed = v; raw.bayersensor.pixelShiftStddevFactorBlue = v; @@ -893,6 +894,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.lmmseIterations = raw.bayersensor.lmmseIterations && p.raw.bayersensor.lmmse_iterations == other.raw.bayersensor.lmmse_iterations; raw.bayersensor.pixelShiftMotion = raw.bayersensor.pixelShiftMotion && p.raw.bayersensor.pixelShiftMotion == other.raw.bayersensor.pixelShiftMotion; raw.bayersensor.pixelShiftMotionCorrection = raw.bayersensor.pixelShiftMotionCorrection && p.raw.bayersensor.pixelShiftMotionCorrection == other.raw.bayersensor.pixelShiftMotionCorrection; + raw.bayersensor.pixelShiftMotionCorrectionMethod = raw.bayersensor.pixelShiftMotionCorrectionMethod && p.raw.bayersensor.pixelShiftMotionCorrectionMethod == other.raw.bayersensor.pixelShiftMotionCorrectionMethod; raw.bayersensor.pixelShiftStddevFactorGreen = raw.bayersensor.pixelShiftStddevFactorGreen && p.raw.bayersensor.pixelShiftStddevFactorGreen == other.raw.bayersensor.pixelShiftStddevFactorGreen; raw.bayersensor.pixelShiftStddevFactorRed = raw.bayersensor.pixelShiftStddevFactorRed && p.raw.bayersensor.pixelShiftStddevFactorRed == other.raw.bayersensor.pixelShiftStddevFactorRed; raw.bayersensor.pixelShiftStddevFactorBlue = raw.bayersensor.pixelShiftStddevFactorBlue && p.raw.bayersensor.pixelShiftStddevFactorBlue == other.raw.bayersensor.pixelShiftStddevFactorBlue; @@ -2336,6 +2338,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftMotionCorrection = mods.raw.bayersensor.pixelShiftMotionCorrection; } + if (raw.bayersensor.pixelShiftMotionCorrectionMethod) { + toEdit.raw.bayersensor.pixelShiftMotionCorrectionMethod = mods.raw.bayersensor.pixelShiftMotionCorrectionMethod; + } + if (raw.bayersensor.pixelShiftStddevFactorGreen) { toEdit.raw.bayersensor.pixelShiftStddevFactorGreen = mods.raw.bayersensor.pixelShiftStddevFactorGreen; } @@ -2938,7 +2944,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten bool RAWParamsEdited::BayerSensor::isUnchanged() const { return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq - && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso + && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftMotionCorrectionMethod && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftExp0 && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 6a72f9ffa..ceb8b82fd 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -694,6 +694,7 @@ public: bool lmmseIterations; bool pixelShiftMotion; bool pixelShiftMotionCorrection; + bool pixelShiftMotionCorrectionMethod; bool pixelShiftStddevFactorGreen; bool pixelShiftStddevFactorRed; bool pixelShiftStddevFactorBlue; From e231d6c06d497721ee80059cf9972ec8640fcd67 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 18 Jan 2017 20:33:40 +0100 Subject: [PATCH 067/181] pixelshift: minor gui fixes --- rtgui/bayerprocess.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 1fcb2911e..8b8cc9be8 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -405,6 +405,9 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); + pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); + if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { @@ -815,11 +818,11 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftStddevFactorBlue->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); pixelShiftMedian->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftMedian3->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMedian->get_active()); pixelShiftGreen->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active ()); + pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); pixelShiftExp0->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); From 8d40dbdc0b3c78fe1d9c2d79c23a4ef6f1ecec5a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 19 Jan 2017 18:46:41 +0100 Subject: [PATCH 068/181] pixelshift: option to smooth transitions between regions with and without motion --- rtdata/languages/default | 3 +++ rtengine/pixelshift.cc | 3 ++- rtengine/procevents.h | 1 + rtengine/procparams.cc | 40 ++++++++++++++-------------------------- rtengine/procparams.h | 1 + rtengine/refreshmap.cc | 3 ++- rtgui/bayerprocess.cc | 35 +++++++++++++++++++++++++++++++++++ rtgui/bayerprocess.h | 4 +++- rtgui/paramsedited.cc | 8 +++++++- rtgui/paramsedited.h | 1 + 10 files changed, 69 insertions(+), 30 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 2a4943424..064c4dbdb 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -701,6 +701,7 @@ HISTORY_MSG_466;EvPixelShiftExp0 HISTORY_MSG_467;EvPixelShiftHoleFill HISTORY_MSG_468;EvPixelShiftMedian HISTORY_MSG_469;EvPixelShiftMotionMethod +HISTORY_MSG_470;EvPixelShiftSmooth HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1686,6 +1687,7 @@ TP_RAW_PIXELSHIFTMEDIAN;Median TP_RAW_PIXELSHIFTMEDIAN3;Exclude selected frame from median TP_RAW_PIXELSHIFTHOLEFILL;3x3 new: Fill holes TP_RAW_PIXELSHIFTBLUR;3x3 new: Blur +TP_RAW_PIXELSHIFTSMOOTH;3x3 new: Smooth transitions TP_RAW_PIXELSHIFTEXP0;Experimental TP_RAW_PIXELSHIFTGREEN;Check dual green TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross @@ -1695,6 +1697,7 @@ TP_RAW_PIXELSHIFTMOTION;Motion detection level (deprecated) TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP;Fill holes in motion mask TP_RAW_PIXELSHIFTBLUR_TOOLTIP;Blur motion mask +TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions (requires 3x3 new Blur) TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP;Use median of all frames instead of selected frame for regions with motion.\nRemoves objects which are at different places in all frames.\nGives motion effect on slow moving (overlapping) objects. TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP;Excludes selected frame from median.\nUseful if moving objects overlap in frame 2 and 3 TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the regions with motion diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 1fde4156d..91aa972ee 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -906,6 +906,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const float threshold = bayerParams.pixelShiftSum; const bool experimental0 = bayerParams.pixelShiftExp0; const bool holeFill = bayerParams.pixelShiftHoleFill; + const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmooth; static const float nReadK3II[] = { 3.4f, // ISO 100 3.1f, // ISO 125 @@ -1650,7 +1651,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else { - if(blurMap && experimental0) { + if(smoothTransitions) { red[i + offsY][j + offsX] = intp(psMask[i][j], red[i + offsY][j + offsX], psRed[i][j] ); green[i + offsY][j + offsX] = intp(psMask[i][j],green[i + offsY][j + offsX],(psG1[i][j] + psG2[i][j]) / 2.f); blue[i + offsY][j + offsX] = intp(psMask[i][j],blue[i + offsY][j + offsX], psBlue[i][j]); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 75b64d30d..86deb7d6d 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -497,6 +497,7 @@ enum ProcEvent { EvPixelShiftMedian = 467, EvPixelShiftMedian3 = 468, EvPixelShiftMotionMethod = 469, + EvPixelShiftSmooth = 470, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 0597be182..2edf7c77e 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -897,6 +897,7 @@ void RAWParams::BayerSensor::setPixelShiftDefaults() pixelShiftMedian3 = false; pixelShiftGreen = true; pixelShiftBlur = true; + pixelShiftSmooth = true; pixelShiftExp0 = false; pixelShiftNonGreenCross = true; pixelShiftNonGreenCross2 = false; @@ -912,32 +913,6 @@ void RAWParams::setDefaults() bayersensor.dcb_enhance = true; //bayersensor.all_enhance = false; bayersensor.lmmse_iterations = 2; - bayersensor.pixelShiftMotion = 0; - bayersensor.pixelShiftMotionCorrection = RAWParams::BayerSensor::Grid3x3New; - bayersensor.pixelShiftMotionCorrectionMethod = RAWParams::BayerSensor::Automatic; - bayersensor.pixelShiftStddevFactorGreen = 5.0; - bayersensor.pixelShiftStddevFactorRed = 5.0; - bayersensor.pixelShiftStddevFactorBlue = 5.0; - bayersensor.pixelShiftEperIso = 0.0; - bayersensor.pixelShiftNreadIso = 0.0; - bayersensor.pixelShiftPrnu = 1.0; - bayersensor.pixelShiftSigma = 1.0; - bayersensor.pixelShiftSum = 3.0; - bayersensor.pixelShiftRedBlueWeight = 0.7; - bayersensor.pixelshiftShowMotion = false; - bayersensor.pixelshiftShowMotionMaskOnly = false; - bayersensor.pixelShiftAutomatic = true; - bayersensor.pixelShiftNonGreenHorizontal = false; - bayersensor.pixelShiftNonGreenVertical = false; - bayersensor.pixelShiftHoleFill = true; - bayersensor.pixelShiftMedian = false; - bayersensor.pixelShiftMedian3 = false; - bayersensor.pixelShiftGreen = true; - bayersensor.pixelShiftBlur = true; - bayersensor.pixelShiftExp0 = false; - bayersensor.pixelShiftNonGreenCross = true; - bayersensor.pixelShiftNonGreenCross2 = false; - bayersensor.pixelShiftNonGreenAmaze = false; bayersensor.black0 = 0.0; bayersensor.black1 = 0.0; bayersensor.black2 = 0.0; @@ -3509,6 +3484,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftBlur", raw.bayersensor.pixelShiftBlur ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftSmooth) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftSmooth", raw.bayersensor.pixelShiftSmooth ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftExp0) { keyFile.set_boolean ("RAW Bayer", "pixelShiftExp0", raw.bayersensor.pixelShiftExp0 ); } @@ -7757,6 +7736,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftSmooth")) { + raw.bayersensor.pixelShiftSmooth = keyFile.get_boolean("RAW Bayer", "pixelShiftSmooth"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftSmooth = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftExp0")) { raw.bayersensor.pixelShiftExp0 = keyFile.get_boolean("RAW Bayer", "pixelShiftExp0"); @@ -8248,6 +8235,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3 && raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen && raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur + && raw.bayersensor.pixelShiftSmooth == other.raw.bayersensor.pixelShiftSmooth && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 51d55bedc..dbf285fb2 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1229,6 +1229,7 @@ public: bool pixelShiftMedian3; bool pixelShiftGreen; bool pixelShiftBlur; + bool pixelShiftSmooth; bool pixelShiftExp0; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 72fe3b76a..891f191b2 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -496,7 +496,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftHoleFill DEMOSAIC, // EvPixelShiftMedian DEMOSAIC, // EvPixelShiftMedian3 - DEMOSAIC // EvPixelShiftMotionMethod + DEMOSAIC, // EvPixelShiftMotionMethod + DEMOSAIC // EvPixelShiftSmooth }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 8b8cc9be8..5d33ffe18 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -128,6 +128,10 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTBLUR_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftBlur); + pixelShiftSmooth = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSMOOTH"))); + pixelShiftSmooth->set_tooltip_text (M("TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftSmooth); + pixelShiftHoleFill = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftHoleFill); @@ -301,6 +305,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMedian3conn = pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true); pixelShiftGreenconn = pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true); pixelShiftBlurconn = pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true); + pixelShiftSmoothconn = pixelShiftSmooth->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftSmoothChanged), true); pixelShiftExp0conn = pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); @@ -342,6 +347,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_inconsistent(!pedited->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); + pixelShiftSmooth->set_inconsistent(!pedited->raw.bayersensor.pixelShiftSmooth); pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); @@ -386,6 +392,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_active(pp->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); + pixelShiftSmooth->set_active(pp->raw.bayersensor.pixelShiftSmooth); pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); @@ -407,6 +414,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->get_active()); if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || @@ -483,6 +491,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->get_active(); pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); + pp->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->get_active(); pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); @@ -529,6 +538,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); pedited->raw.bayersensor.pixelShiftGreen = !pixelShiftGreen->get_inconsistent(); pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftSmooth = !pixelShiftSmooth->get_inconsistent(); pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); @@ -650,9 +660,11 @@ void BayerProcess::psMotionCorrectionChanged () if(pixelShiftMotionCorrection->get_active_row_number() == 5) { pixelShiftBlur->set_sensitive(true); pixelShiftHoleFill->set_sensitive(true); + pixelShiftSmooth->set_sensitive(pixelShiftBlur->get_active()); } else { pixelShiftBlur->set_sensitive(false); pixelShiftHoleFill->set_sensitive(false); + pixelShiftSmooth->set_sensitive(false); } if (listener) { @@ -823,6 +835,7 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftMedian3->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMedian->get_active()); pixelShiftGreen->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->get_active()); pixelShiftExp0->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); @@ -971,11 +984,33 @@ void BayerProcess::pixelShiftBlurChanged () lastDCBen = pixelShiftBlur->get_active (); } + pixelShiftSmooth->set_sensitive(pixelShiftBlur->get_active ()); + if (listener) { listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } +void BayerProcess::pixelShiftSmoothChanged () +{ + if (batchMode) { + if (pixelShiftSmooth->get_inconsistent()) { + pixelShiftSmooth->set_inconsistent (false); + pixelShiftSmoothconn.block (true); + pixelShiftSmooth->set_active (false); + pixelShiftSmoothconn.block (false); + } else if (lastDCBen) { + pixelShiftSmooth->set_inconsistent (true); + } + + lastDCBen = pixelShiftSmooth->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftSmooth, pixelShiftSmooth->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + void BayerProcess::pixelShiftExp0Changed () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index e53cb6816..135c02df5 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -56,6 +56,7 @@ protected: Gtk::CheckButton* pixelShiftNonGreenAmaze; Gtk::CheckButton* pixelShiftGreen; Gtk::CheckButton* pixelShiftBlur; + Gtk::CheckButton* pixelShiftSmooth; Gtk::CheckButton* pixelShiftExp0; Gtk::CheckButton* pixelShiftHoleFill; Gtk::CheckButton* pixelShiftMedian; @@ -75,7 +76,7 @@ protected: sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftHoleFillconn, pixelShiftMedianconn, pixelShiftMedian3conn, pixelShiftNonGreenCrossconn, - pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftExp0conn, pixelShiftMotionMethodConn; + pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftSmoothconn, pixelShiftExp0conn, pixelShiftMotionMethodConn; public: BayerProcess (); @@ -100,6 +101,7 @@ public: void pixelShiftMedian3Changed(); void pixelShiftGreenChanged(); void pixelShiftBlurChanged(); + void pixelShiftSmoothChanged(); void pixelShiftExp0Changed(); void pixelShiftNonGreenCrossChanged(); void pixelShiftNonGreenCross2Changed(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 185341e32..061424acf 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -392,6 +392,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftMedian3 = v; raw.bayersensor.pixelShiftGreen = v; raw.bayersensor.pixelShiftBlur = v; + raw.bayersensor.pixelShiftSmooth = v; raw.bayersensor.pixelShiftExp0 = v; raw.bayersensor.pixelShiftNonGreenCross = v; raw.bayersensor.pixelShiftNonGreenCross2 = v; @@ -914,6 +915,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftMedian3 = raw.bayersensor.pixelShiftMedian3 && p.raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3; raw.bayersensor.pixelShiftGreen = raw.bayersensor.pixelShiftGreen && p.raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen; raw.bayersensor.pixelShiftBlur = raw.bayersensor.pixelShiftBlur && p.raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur; + raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmooth == other.raw.bayersensor.pixelShiftSmooth; raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; @@ -2418,6 +2420,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftBlur = mods.raw.bayersensor.pixelShiftBlur; } + if (raw.bayersensor.pixelShiftSmooth) { + toEdit.raw.bayersensor.pixelShiftSmooth = mods.raw.bayersensor.pixelShiftSmooth; + } + if (raw.bayersensor.pixelShiftExp0) { toEdit.raw.bayersensor.pixelShiftExp0 = mods.raw.bayersensor.pixelShiftExp0; } @@ -2946,7 +2952,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftMotionCorrectionMethod && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftExp0 + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index ceb8b82fd..23b0e240b 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -714,6 +714,7 @@ public: bool pixelShiftMedian3; bool pixelShiftGreen; bool pixelShiftBlur; + bool pixelShiftSmooth; bool pixelShiftExp0; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; From c0988beb3312b53756ece02e964e91a16c07b158 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 21 Jan 2017 13:44:10 +0100 Subject: [PATCH 069/181] Pixelshift: fixed two small bugs --- rtengine/rawimagesource.cc | 2 +- rtgui/bayerprocess.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 11d7a2af2..f5bea64d3 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2035,7 +2035,7 @@ void RawImageSource::demosaic(const RAWParams &raw) bayerParams.pixelShiftAutomatic = false; bayerParams.pixelshiftShowMotion = false; } - if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftExp0)) { + if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftSmooth)) { if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction if(!bayerParams.pixelShiftMedian3) { diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 5d33ffe18..89b92ebd2 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -831,7 +831,6 @@ void BayerProcess::pixelShiftAutomaticChanged () pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); - pixelShiftMedian->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftMedian3->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMedian->get_active()); pixelShiftGreen->set_sensitive(pixelShiftAutomatic->get_active ()); pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); From 208c179921c35c574ec725a6db8ec400e09fd93c Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 22 Jan 2017 17:44:58 +0100 Subject: [PATCH 070/181] pixelshift: adjuster for smooth transitions --- rtdata/languages/default | 7 ++++--- rtengine/pixelshift.cc | 38 +++++++++++++++++++++++++++++++----- rtengine/procparams.cc | 10 +++++----- rtengine/procparams.h | 2 +- rtengine/rawimagesource.cc | 2 +- rtgui/bayerprocess.cc | 40 +++++++++++++------------------------- rtgui/bayerprocess.h | 3 +-- rtgui/paramsedited.cc | 4 ++-- 8 files changed, 61 insertions(+), 45 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index dcd6fd3da..68386d1bd 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -700,8 +700,9 @@ HISTORY_MSG_465;EvPixelShiftSum HISTORY_MSG_466;EvPixelShiftExp0 HISTORY_MSG_467;EvPixelShiftHoleFill HISTORY_MSG_468;EvPixelShiftMedian -HISTORY_MSG_469;EvPixelShiftMotionMethod -HISTORY_MSG_470;EvPixelShiftSmooth +HISTORY_MSG_469;EvPixelShiftMedian3 +HISTORY_MSG_470;EvPixelShiftMotionMethod +HISTORY_MSG_471;EvPixelShiftSmooth HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1702,7 +1703,7 @@ TP_RAW_PIXELSHIFTMOTION;Motion detection level (deprecated) TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP;Fill holes in motion mask TP_RAW_PIXELSHIFTBLUR_TOOLTIP;Blur motion mask -TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions (requires 3x3 new Blur) +TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions (requires 3x3 new Blur)\nSet to 0 to disable smooth transitions\nSet to 1 to get Amaze or Median TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP;Use median of all frames instead of selected frame for regions with motion.\nRemoves objects which are at different places in all frames.\nGives motion effect on slow moving (overlapping) objects. TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP;Excludes selected frame from median.\nUseful if moving objects overlap in frame 2 and 3 TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the regions with motion diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 91aa972ee..188b5f4bf 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -906,7 +906,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const float threshold = bayerParams.pixelShiftSum; const bool experimental0 = bayerParams.pixelShiftExp0; const bool holeFill = bayerParams.pixelShiftHoleFill; - const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmooth; + const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmoothFactor > 0.; + const float smoothFactor = 1.0 - bayerParams.pixelShiftSmoothFactor; static const float nReadK3II[] = { 3.4f, // ISO 100 3.1f, // ISO 125 @@ -1638,12 +1639,34 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #pragma omp parallel for schedule(dynamic,16) for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { +#ifdef __SSE2__ + if(!(showMotion && showOnlyMask) && smoothTransitions) { + if(smoothFactor == 0.f) { + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { + psMask[i][j] = 1.f; + } + } else { + vfloat zerov = F2V(0.f); + vfloat smoothv = F2V(smoothFactor); + int j = winx + border - offsX; + for(; j < winw - (border + offsX)- 3; j += 4) { + vfloat blendv = LVFU(psMask[i][j]); + blendv = vmaxf(blendv, zerov); + blendv = pow_F(blendv, smoothv); + STVFU(psMask[i][j], blendv); + } + for(; j < winw - (border + offsX); ++j) { + psMask[i][j] = pow_F(std::max(psMask[i][j],0.f),smoothFactor); + } + } + } +#endif float *greenDest = green[i + offsY]; float *redDest = red[i + offsY]; float *blueDest = blue[i + offsY]; for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { - if(mask[i][j] == 255 ) { + if(mask[i][j] == 255) { paintMotionMask(j + offsX, showMotion, 0.5f, showOnlyMask, greenDest, redDest, blueDest); continue; } @@ -1652,9 +1675,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else { if(smoothTransitions) { - red[i + offsY][j + offsX] = intp(psMask[i][j], red[i + offsY][j + offsX], psRed[i][j] ); - green[i + offsY][j + offsX] = intp(psMask[i][j],green[i + offsY][j + offsX],(psG1[i][j] + psG2[i][j]) / 2.f); - blue[i + offsY][j + offsX] = intp(psMask[i][j],blue[i + offsY][j + offsX], psBlue[i][j]); +#ifdef __SSE2__ + float blend = psMask[i][j]; +#else + float blend = pow_F(std::max(psMask[i][j],0.f),smoothFactor); +#endif + red[i + offsY][j + offsX] = intp(blend, red[i + offsY][j + offsX], psRed[i][j] ); + green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX],(psG1[i][j] + psG2[i][j]) / 2.f); + blue[i + offsY][j + offsX] = intp(blend, blue[i + offsY][j + offsX], psBlue[i][j]); } else { red[i + offsY][j + offsX] = psRed[i][j]; green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 2edf7c77e..d0a79e6a8 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -897,7 +897,7 @@ void RAWParams::BayerSensor::setPixelShiftDefaults() pixelShiftMedian3 = false; pixelShiftGreen = true; pixelShiftBlur = true; - pixelShiftSmooth = true; + pixelShiftSmoothFactor = 0.7; pixelShiftExp0 = false; pixelShiftNonGreenCross = true; pixelShiftNonGreenCross2 = false; @@ -3485,7 +3485,7 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b } if (!pedited || pedited->raw.bayersensor.pixelShiftSmooth) { - keyFile.set_boolean ("RAW Bayer", "pixelShiftSmooth", raw.bayersensor.pixelShiftSmooth ); + keyFile.set_double ("RAW Bayer", "pixelShiftSmoothFactor", raw.bayersensor.pixelShiftSmoothFactor ); } if (!pedited || pedited->raw.bayersensor.pixelShiftExp0) { @@ -7736,8 +7736,8 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } - if (keyFile.has_key ("RAW Bayer", "pixelShiftSmooth")) { - raw.bayersensor.pixelShiftSmooth = keyFile.get_boolean("RAW Bayer", "pixelShiftSmooth"); + if (keyFile.has_key ("RAW Bayer", "pixelShiftSmoothFactor")) { + raw.bayersensor.pixelShiftSmoothFactor = keyFile.get_double("RAW Bayer", "pixelShiftSmoothFactor"); if (pedited) { pedited->raw.bayersensor.pixelShiftSmooth = true; @@ -8235,7 +8235,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3 && raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen && raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur - && raw.bayersensor.pixelShiftSmooth == other.raw.bayersensor.pixelShiftSmooth + && raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 diff --git a/rtengine/procparams.h b/rtengine/procparams.h index dbf285fb2..21fe97855 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1229,7 +1229,7 @@ public: bool pixelShiftMedian3; bool pixelShiftGreen; bool pixelShiftBlur; - bool pixelShiftSmooth; + double pixelShiftSmoothFactor; bool pixelShiftExp0; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index f5bea64d3..e5cce8f57 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2035,7 +2035,7 @@ void RawImageSource::demosaic(const RAWParams &raw) bayerParams.pixelShiftAutomatic = false; bayerParams.pixelshiftShowMotion = false; } - if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftSmooth)) { + if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftSmoothFactor > 0.0)) { if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction if(!bayerParams.pixelShiftMedian3) { diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 89b92ebd2..8c2b8da48 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -128,8 +128,15 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTBLUR_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftBlur); - pixelShiftSmooth = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSMOOTH"))); + pixelShiftSmooth = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSMOOTH"), 0, 1, 0.05, 0.7)); pixelShiftSmooth->set_tooltip_text (M("TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP")); + pixelShiftSmooth->setAdjusterListener (this); + + if (pixelShiftSmooth->delay < options.adjusterMaxDelay) { + pixelShiftSmooth->delay = options.adjusterMaxDelay; + } + + pixelShiftSmooth->show(); pixelShiftOptions->pack_start(*pixelShiftSmooth); pixelShiftHoleFill = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); @@ -305,7 +312,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMedian3conn = pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true); pixelShiftGreenconn = pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true); pixelShiftBlurconn = pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true); - pixelShiftSmoothconn = pixelShiftSmooth->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftSmoothChanged), true); pixelShiftExp0conn = pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); @@ -347,7 +353,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_inconsistent(!pedited->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); - pixelShiftSmooth->set_inconsistent(!pedited->raw.bayersensor.pixelShiftSmooth); + pixelShiftSmooth->setEditedState ( pedited->raw.bayersensor.pixelShiftSmooth ? Edited : UnEdited); pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); @@ -392,7 +398,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_active(pp->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); - pixelShiftSmooth->set_active(pp->raw.bayersensor.pixelShiftSmooth); + pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); @@ -491,7 +497,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->get_active(); pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); - pp->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->get_active(); + pp->raw.bayersensor.pixelShiftSmoothFactor = pixelShiftSmooth->getValue(); pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); @@ -538,7 +544,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); pedited->raw.bayersensor.pixelShiftGreen = !pixelShiftGreen->get_inconsistent(); pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftSmooth = !pixelShiftSmooth->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->getEditedState(); pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); @@ -651,6 +657,8 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvPixelShiftSum, a->getTextValue() ); } else if (a == pixelShiftRedBlueWeight) { listener->panelChanged (EvPixelShiftRedBlueWeight, a->getTextValue() ); + } else if (a == pixelShiftSmooth) { + listener->panelChanged (EvPixelShiftSmooth, a->getTextValue() ); } } } @@ -990,26 +998,6 @@ void BayerProcess::pixelShiftBlurChanged () } } -void BayerProcess::pixelShiftSmoothChanged () -{ - if (batchMode) { - if (pixelShiftSmooth->get_inconsistent()) { - pixelShiftSmooth->set_inconsistent (false); - pixelShiftSmoothconn.block (true); - pixelShiftSmooth->set_active (false); - pixelShiftSmoothconn.block (false); - } else if (lastDCBen) { - pixelShiftSmooth->set_inconsistent (true); - } - - lastDCBen = pixelShiftSmooth->get_active (); - } - - if (listener) { - listener->panelChanged (EvPixelShiftSmooth, pixelShiftSmooth->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - void BayerProcess::pixelShiftExp0Changed () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 135c02df5..def4021ce 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -56,11 +56,11 @@ protected: Gtk::CheckButton* pixelShiftNonGreenAmaze; Gtk::CheckButton* pixelShiftGreen; Gtk::CheckButton* pixelShiftBlur; - Gtk::CheckButton* pixelShiftSmooth; Gtk::CheckButton* pixelShiftExp0; Gtk::CheckButton* pixelShiftHoleFill; Gtk::CheckButton* pixelShiftMedian; Gtk::CheckButton* pixelShiftMedian3; + Adjuster* pixelShiftSmooth; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; @@ -101,7 +101,6 @@ public: void pixelShiftMedian3Changed(); void pixelShiftGreenChanged(); void pixelShiftBlurChanged(); - void pixelShiftSmoothChanged(); void pixelShiftExp0Changed(); void pixelShiftNonGreenCrossChanged(); void pixelShiftNonGreenCross2Changed(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 061424acf..e1303095a 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -915,7 +915,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftMedian3 = raw.bayersensor.pixelShiftMedian3 && p.raw.bayersensor.pixelShiftMedian3 == other.raw.bayersensor.pixelShiftMedian3; raw.bayersensor.pixelShiftGreen = raw.bayersensor.pixelShiftGreen && p.raw.bayersensor.pixelShiftGreen == other.raw.bayersensor.pixelShiftGreen; raw.bayersensor.pixelShiftBlur = raw.bayersensor.pixelShiftBlur && p.raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur; - raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmooth == other.raw.bayersensor.pixelShiftSmooth; + raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor; raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; @@ -2421,7 +2421,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten } if (raw.bayersensor.pixelShiftSmooth) { - toEdit.raw.bayersensor.pixelShiftSmooth = mods.raw.bayersensor.pixelShiftSmooth; + toEdit.raw.bayersensor.pixelShiftSmoothFactor = mods.raw.bayersensor.pixelShiftSmoothFactor; } if (raw.bayersensor.pixelShiftExp0) { From 46e078c7396017694fb3047c5ccb827cb1284439 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 27 Jan 2017 14:26:27 +0100 Subject: [PATCH 071/181] pixelshift: speedup and new experimental ISO adaption --- rtengine/pixelshift.cc | 878 ++++++++++++++++++++++------------------- 1 file changed, 464 insertions(+), 414 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 188b5f4bf..26ac2418f 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -169,18 +169,22 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar auto yUp = y - 1, yDown = y + 1; bool lastXUp = false, lastXDown = false, firstXUp = false, firstXDown = false; mask[y][x] = 0; + if(yUp >= yStart && mask[yUp][x] == 255) { coordStack.emplace(x, yUp); firstXUp = lastXUp = true; } + if(yDown < yEnd && mask[yDown][x] == 255) { coordStack.emplace(x, yDown); firstXDown = lastXDown = true; } + auto xr = x + 1; - + while(xr < xEnd && mask[y][xr] == 255) { mask[y][xr] = 0; + if(yUp >= yStart && mask[yUp][xr] == 255) { if(!lastXUp) { coordStack.emplace(xr, yUp); @@ -189,6 +193,7 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar } else { lastXUp = false; } + if(yDown < yEnd && mask[yDown][xr] == 255) { if(!lastXDown) { coordStack.emplace(xr, yDown); @@ -197,14 +202,17 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar } else { lastXDown = false; } + xr++; } auto xl = x - 1; lastXUp = firstXUp; lastXDown = firstXDown; + while(xl >= xStart && mask[y][xl] == 255) { mask[y][xl] = 0; + if(yUp >= yStart && mask[yUp][xl] == 255) { if(!lastXUp) { coordStack.emplace(xl, yUp); @@ -213,6 +221,7 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar } else { lastXUp = false; } + if(yDown < yEnd && mask[yDown][xl] == 255) { if(!lastXDown) { coordStack.emplace(xl, yDown); @@ -221,8 +230,10 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar } else { lastXDown = false; } + xl--; } + mask[y][x] = 0; } } @@ -232,55 +243,61 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma { #pragma omp parallel { - std::stack, std::vector>> coordStack; + std::stack, std::vector>> coordStack; - #pragma omp for schedule(dynamic,128) nowait - for(uint16_t i = yStart;i= 0 ;i--) - floodFill4Impl(i, xEnd - 1, xStart, xEnd, yStart, yEnd, mask, coordStack); - - #pragma omp sections nowait - { - #pragma omp section + for(uint16_t i = yStart; i < yEnd; i++) { - uint16_t i = yStart; + floodFill4Impl(i, xStart, xStart, xEnd, yStart, yEnd, mask, coordStack); + } - for(uint16_t j = xStart; j < xEnd; j++) + #pragma omp for schedule(dynamic,128) nowait + + for(int16_t i = yEnd - 1; i >= 0 ; i--) + { + floodFill4Impl(i, xEnd - 1, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + + #pragma omp sections nowait + { + #pragma omp section { - floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + uint16_t i = yStart; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yStart; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xStart; j < xEnd; j++) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } + } + #pragma omp section + { + uint16_t i = yEnd; + + for(uint16_t j = xEnd - 1; j >= xStart; j--) + { + floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); + } } } - #pragma omp section - { - uint16_t i = yStart; - - for(uint16_t j = xEnd - 1; j >= xStart; j--) - { - floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); - } - } - #pragma omp section - { - uint16_t i = yEnd; - - for(uint16_t j = xStart; j < xEnd; j++) - { - floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); - } - } - #pragma omp section - { - uint16_t i = yEnd; - - for(uint16_t j = xEnd - 1; j >= xStart; j--) - { - floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); - } - } - } } } @@ -900,7 +917,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const bool checkNonGreenAmaze = bayerParams.pixelShiftNonGreenAmaze; const bool checkNonGreenCross2 = bayerParams.pixelShiftNonGreenCross2; const bool checkGreen = bayerParams.pixelShiftGreen; - const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight; + const float greenWeight = 2.f; + const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight + 1.f; const bool blurMap = bayerParams.pixelShiftBlur; const float sigma = bayerParams.pixelShiftSigma; const float threshold = bayerParams.pixelShiftSum; @@ -1077,8 +1095,13 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } nRead *= pow(2.f, nreadIso); - eperIsoModel *= pow(2.f, eperIso); - eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); + eperIsoModel *= pow(2.f, eperIso * 0.5f); + + if(adaptive && experimental0) { + eperIso = eperIsoModel * sqrtf(100.f / (rawWpCorrection * idata->getISOSpeed())); + } else { + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); + } float eperIsoRed = eperIso / scale_mul[0]; float eperIsoGreen = eperIso * scaleGreen; @@ -1128,8 +1151,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA array2D psG1(winw + 32, winh); array2D psG2(winw + 32, winh); array2D psBlue(winw + 32, winh); + array2D psMask(winw, winh); + + // Fill the mask with value 1.0 + // We work in 1.0 to 2.0 range to avoid performance issues caused by denormal numbers in gaussian blur + for(int i = 0; i < winh; ++i) { + for(int j = 0; j < winw; ++j) { + psMask[i][j] = 1.f; + } + } - array2D psMask(winw, winh, ARRAY2D_CLEAR_DATA); // fill channels psRed, psG1, psG2 and psBlue #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) @@ -1165,433 +1196,447 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } // now that the temporary planes are filled for easy access we do the motion detection - int sum0 = 0; - int sum1 = 0; + int sum[2] = {0}; + float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; + #ifdef _OPENMP - #pragma omp parallel for reduction(+:sum0,sum1) schedule(dynamic,16) + #pragma omp parallel +#endif + { + int sumThr[2] = {0}; +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) nowait #endif - for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { - float *greenDest = green[i + offsY]; - float *redDest = red[i + offsY]; - float *blueDest = blue[i + offsY]; - int j = winx + border - offsX; + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + float *greenDest = green[i + offsY]; + float *redDest = red[i + offsY]; + float *blueDest = blue[i + offsY]; + int j = winx + border - offsX; - float greenDifMax[gridSize]; // Here we store the maximum differences per Column + float greenDifMax[gridSize]; // Here we store the maximum differences per Column + + // green channel motion detection checks the grid around the pixel for differences in green channels + if(detectMotion || (adaptive && checkGreen)) { + if(gridSize == 3) { + // compute maximum of differences for first two columns of 3x3 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } else if(gridSize == 5) { + // compute maximum of differences for first four columns of 5x5 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } else if(gridSize == 7) { + // compute maximum of differences for first six columns of 7x7 grid + greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + } - // green channel motion detection checks the grid around the pixel for differences in green channels - if(detectMotion || (adaptive && checkGreen)) { - if(gridSize == 3) { - // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - } else if(gridSize == 5) { - // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - } else if(gridSize == 7) { - // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); } - } + // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 + int lastIndex = gridSize - 1; + float korr = 0.f; + int c = FC(i, j); + bool blueRow = false; - // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 - int lastIndex = gridSize - 1; - float korr = 0.f; - int c = FC(i, j); - bool blueRow = false; + if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + // row with blue pixels => swap destination pointers for non green pixels + blueRow = true; + } - if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { - // row with blue pixels => swap destination pointers for non green pixels - blueRow = true; - } - - // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); // offset ^= 1; // 0 => 1 or 1 => 0 - for(; j < winw - (border + offsX); ++j) { - bool greenFromPs = false; - offset ^= 1; // 0 => 1 or 1 => 0 + for(; j < winw - (border + offsX); ++j) { + bool greenFromPs = false; + offset ^= 1; // 0 => 1 or 1 => 0 - if(detectMotion || (adaptive && checkGreen)) { - bool skipNext = false; - float gridMax; + if(detectMotion || (adaptive && checkGreen)) { + bool skipNext = false; + float gridMax; - if(gridSize < 2) { - // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps - gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); - skipNext = skip; - } else if(gridSize == 3) { - // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); - } else if(gridSize == 5) { - // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); - } else if(gridSize == 7) { - // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - }); - // calculate maximum of whole grid by calculating maximum of grid column max values - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); - } - - - // adjust index for next column - lastIndex ++; - lastIndex = lastIndex == gridSize ? 0 : lastIndex; - - // increase motion detection dependent on brightness - if(!adaptive) { - korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; - } - - if (gridMax > thresh - korr) { - if(offset == 0) { - sum0 ++; - } else { - sum1 ++; + if(gridSize < 2) { + // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps + gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); + skipNext = skip; + } else if(gridSize == 3) { + // compute maximum of differences for third column of 3x3 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); + } else if(gridSize == 5) { + // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); + } else if(gridSize == 7) { + // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + }); + // calculate maximum of whole grid by calculating maximum of grid column max values + gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); } - if(nOf3x3) { - psMask[i][j] = 1.f; - } else if((offset == (frame & 1)) && checkNonGreenVertical) { - if(frame > 1) { - green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; + + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; + + if(!adaptive) { + // increase motion detection dependent on brightness + korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; + } + + if (gridMax > thresh - korr) { + sumThr[offset] ++; + + if(nOf3x3) { + psMask[i][j] = greenWeight; + } else if((offset == (frame & 1)) && checkNonGreenVertical) { + if(frame > 1) { + green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; + } else { + green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; + } + + continue; } else { - green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; - } - - continue; - } else { - // at least one of the tested green pixels of the grid is detected as motion - paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); - - if(skipNext) { - // treat the horizontally next pixel also as motion - j++; + // at least one of the tested green pixels of the grid is detected as motion paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + + if(skipNext) { + // treat the horizontally next pixel also as motion + j++; + paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); + } + + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; } - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; } } - } - if(adaptive && checkNonGreenCross) { - // check red cross - float redTop = psRed[i - 1][ j ]; - float redLeft = psRed[ i ][j - 1]; - float redCentre = psRed[ i ][ j ]; - float redRight = psRed[ i ][j + 1]; - float redBottom = psRed[i + 1][ j ]; - float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + if(adaptive) { + if(checkNonGreenCross) { + // check red cross + float redTop = psRed[i - 1][ j ]; + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; + float redBottom = psRed[i + 1][ j ]; + float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(redDiff > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); - } + if(redDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } - continue; - } - - // check blue cross - float blueTop = psBlue[i - 1][ j ]; - float blueLeft = psBlue[ i ][j - 1]; - float blueCentre = psBlue[ i ][ j ]; - float blueRight = psBlue[ i ][j + 1]; - float blueBottom = psBlue[i + 1][ j ]; - float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - - if(blueDiff > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); - } - - continue; - - } - } - - if(adaptive && checkNonGreenHorizontal) { - float redLeft = psRed[ i ][j - 1]; - float redCentre = psRed[ i ][ j ]; - float redRight = psRed[ i ][j + 1]; - - float redDiffLeft = redLeft - redCentre; - float redDiffRight = redRight - redCentre; - - if(redDiffLeft * redDiffRight >= 0.f) { - float redAvg = (redRight + redLeft) / 2.f; - float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - - if(redDiffHor > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); + continue; } - continue; + // check blue cross + float blueTop = psBlue[i - 1][ j ]; + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + float blueBottom = psBlue[i + 1][ j ]; + float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + + } } - } - float blueLeft = psBlue[ i ][j - 1]; - float blueCentre = psBlue[ i ][ j ]; - float blueRight = psBlue[ i ][j + 1]; + if(checkNonGreenHorizontal) { + float redLeft = psRed[ i ][j - 1]; + float redCentre = psRed[ i ][ j ]; + float redRight = psRed[ i ][j + 1]; - float blueDiffLeft = blueLeft - blueCentre; - float blueDiffRight = blueRight - blueCentre; + float redDiffLeft = redLeft - redCentre; + float redDiffRight = redRight - redCentre; - if(blueDiffLeft * blueDiffRight >= 0.f) { - float blueAvg = (blueRight + blueLeft) / 2.f; - float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + if(redDiffLeft * redDiffRight >= 0.f) { + float redAvg = (redRight + redLeft) / 2.f; + float redDiffHor = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(blueDiffHor > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, blueDest, redDest, greenDest); + if(redDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffHor, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } } - continue; + float blueLeft = psBlue[ i ][j - 1]; + float blueCentre = psBlue[ i ][ j ]; + float blueRight = psBlue[ i ][j + 1]; + + float blueDiffLeft = blueLeft - blueCentre; + float blueDiffRight = blueRight - blueCentre; + + if(blueDiffLeft * blueDiffRight >= 0.f) { + float blueAvg = (blueRight + blueLeft) / 2.f; + float blueDiffHor = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiffHor > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffHor, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } } - } - } - if(adaptive && checkNonGreenVertical) { - // check red vertically - float redTop = psRed[i - 1][ j ]; - float redCentre = psRed[ i ][ j ]; - float redBottom = psRed[i + 1][ j ]; + if(checkNonGreenVertical) { + // check red vertically + float redTop = psRed[i - 1][ j ]; + float redCentre = psRed[ i ][ j ]; + float redBottom = psRed[i + 1][ j ]; - float redDiffTop = redTop - redCentre; - float redDiffBottom = redBottom - redCentre; + float redDiffTop = redTop - redCentre; + float redDiffBottom = redBottom - redCentre; - if(redDiffTop * redDiffBottom >= 0.f) { - float redAvg = (redTop + redBottom) / 2.f; - float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + if(redDiffTop * redDiffBottom >= 0.f) { + float redAvg = (redTop + redBottom) / 2.f; + float redDiff = nonGreenDiff(redCentre, redAvg, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(redDiff > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + if(redDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); + } + + continue; + } } - continue; + // check blue vertically + float blueTop = psBlue[i - 1][ j ]; + float blueCentre = psBlue[ i ][ j ]; + float blueBottom = psBlue[i + 1][ j ]; + + float blueDiffTop = blueTop - blueCentre; + float blueDiffBottom = blueBottom - blueCentre; + + if(blueDiffTop * blueDiffBottom >= 0.f) { + float blueAvg = (blueTop + blueBottom) / 2.f; + float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + + if(blueDiff > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + } + + continue; + } + } } - } - // check blue vertically - float blueTop = psBlue[i - 1][ j ]; - float blueCentre = psBlue[ i ][ j ]; - float blueBottom = psBlue[i + 1][ j ]; + if(checkNonGreenAmaze) { + // check current pixel against amaze + float redCentre = psRed[ i ][ j ]; + float redAmaze = red[i + offsY][j + offsX]; - float blueDiffTop = blueTop - blueCentre; - float blueDiffBottom = blueBottom - blueCentre; + float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); - if(blueDiffTop * blueDiffBottom >= 0.f) { - float blueAvg = (blueTop + blueBottom) / 2.f; - float blueDiff = nonGreenDiff(blueCentre, blueAvg, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + if(redDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); + } - if(blueDiff > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); + continue; } - continue; - } - } - } + float blueCentre = psBlue[ i ][ j ]; + float blueAmaze = blue[i + offsY][j + offsX]; - if(adaptive && checkNonGreenAmaze) { - // check current pixel against amaze - float redCentre = psRed[ i ][ j ]; - float redAmaze = red[i + offsY][j + offsX]; + float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - float redDiffAmaze = nonGreenDiff(redCentre, redAmaze, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + if(blueDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = redBlueWeight; + } else { + paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); + } - if(redDiffAmaze > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, redDiffAmaze, showOnlyMask, redDest, blueDest, greenDest); + continue; + } } - continue; - } + if(checkNonGreenCross2) { // for green amaze + float greenCentre = (psG1[ i ][ j ] + psG2[ i ][ j ]) / 2.f; + float greenAmaze = green[i + offsY][j + offsX]; + float greenDiffAmaze = nonGreenDiff(greenCentre, greenAmaze, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); - float blueCentre = psBlue[ i ][ j ]; - float blueAmaze = blue[i + offsY][j + offsX]; + if(greenDiffAmaze > 0.f) { + if(nOf3x3) { + psMask[i][j] = greenWeight; + } else { + paintMotionMask(j + offsX, showMotion, greenDiffAmaze, showOnlyMask, greenDest, redDest, blueDest); + } - float blueDiffAmaze = nonGreenDiff(blueCentre, blueAmaze, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); - - if(blueDiffAmaze > 0.f) { - if(nOf3x3) { - psMask[i][j] = redBlueWeight; - } else { - paintMotionMask(j + offsX, showMotion, blueDiffAmaze, showOnlyMask, blueDest, redDest, greenDest); + continue; + } } - continue; - } - } + if(experimental0) { // for experiments + // float green1Median, green2Median; + // green1Median = median(psG1[ i - 1 ][ j - 1 ],psG1[ i - 1 ][ j + 1 ],psG1[ i ][ j ],psG1[ i + 1 ][ j -1 ],psG1[ i + 1 ][ j + 1 ]); + // green2Median = median(psG2[ i - 1 ][ j - 1 ],psG2[ i - 1 ][ j + 1 ],psG2[ i ][ j ],psG2[ i + 1 ][ j -1 ],psG2[ i + 1 ][ j + 1 ]); + // float greenDiffMedian = nonGreenDiff(green1Median, green2Median, stddevFactorGreen * 0.36f, eperIsoGreen, nRead, prnu, showMotion); + // + // if(greenDiffMedian > 0.f) { + // if(nOf3x3) { + // psMask[i][j] = 1.f; + // } else { + // paintMotionMask(j + offsX, showMotion, greenDiffMedian, showOnlyMask, greenDest, redDest, blueDest); + // } + // + // continue; + // } - if(adaptive && checkNonGreenCross2) { // for green amaze - float greenCentre = (psG1[ i ][ j ] + psG2[ i ][ j ]) / 2.f; - float greenAmaze = green[i + offsY][j + offsX]; - float greenDiffAmaze = nonGreenDiff(greenCentre, greenAmaze, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); - - if(greenDiffAmaze > 0.f) { - if(nOf3x3) { - psMask[i][j] = 1.f; - } else { - paintMotionMask(j + offsX, showMotion, greenDiffAmaze, showOnlyMask, greenDest, redDest, blueDest); } - - continue; } - } - if(adaptive && experimental0) { // for experiments -// float green1Median, green2Median; -// green1Median = median(psG1[ i - 1 ][ j - 1 ],psG1[ i - 1 ][ j + 1 ],psG1[ i ][ j ],psG1[ i + 1 ][ j -1 ],psG1[ i + 1 ][ j + 1 ]); -// green2Median = median(psG2[ i - 1 ][ j - 1 ],psG2[ i - 1 ][ j + 1 ],psG2[ i ][ j ],psG2[ i + 1 ][ j -1 ],psG2[ i + 1 ][ j + 1 ]); -// float greenDiffMedian = nonGreenDiff(green1Median, green2Median, stddevFactorGreen * 0.36f, eperIsoGreen, nRead, prnu, showMotion); -// -// if(greenDiffMedian > 0.f) { -// if(nOf3x3) { -// psMask[i][j] = 1.f; -// } else { -// paintMotionMask(j + offsX, showMotion, greenDiffMedian, showOnlyMask, greenDest, redDest, blueDest); -// } -// -// continue; -// } - - } - - if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black - red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; - } else if(!(adaptive && nOf3x3)) { - // no motion detected, replace the a priori demosaiced values by the pixelshift combined values - red[i + offsY][j + offsX] = psRed[i][j]; - green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; - blue[i + offsY][j + offsX] = psBlue[i][j]; + if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; + } else if(!(adaptive && nOf3x3)) { + // no motion detected, replace the a priori demosaiced values by the pixelshift combined values + red[i + offsY][j + offsX] = psRed[i][j]; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + blue[i + offsY][j + offsX] = psBlue[i][j]; + } } } + +#ifdef _OPENMP + #pragma omp critical +#endif + { + sum[0] += sumThr[0]; + sum[1] += sumThr[0]; + } } + float percent0 = 100.f * sum[0] / pixelcount; + float percent1 = 100.f * sum[1] / pixelcount; - float percent0 = 100.f * sum0 / pixelcount; - float percent1 = 100.f * sum1 / pixelcount; - - std::cout << fileName << " : Green detections at stddev " << std::setprecision( 2 ) << bayerParams.pixelShiftStddevFactorGreen << " : Frame 1/3 : " << std::setprecision( 6 ) << sum0 << " (" << percent0 << "%)" << " Frame 2/4 : " << sum1 << " (" << percent1 << "%)" << std::endl; + std::cout << fileName << " : Green detections at stddev " << std::setprecision( 2 ) << bayerParams.pixelShiftStddevFactorGreen << " : Frame 1/3 : " << std::setprecision( 6 ) << sum[0] << " (" << percent0 << "%)" << " Frame 2/4 : " << sum[1] << " (" << percent1 << "%)" << std::endl; if(adaptive && nOf3x3) { if(blurMap) { + StopWatch Stop1("Blur"); #pragma omp parallel { gaussianBlur(psMask, psMask, winw, winh, sigma); @@ -1611,14 +1656,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int v = -1; v <= 1; v++) { for(int h = -1; h < 1; h++) { - v3sum[1 + h] += psMask[i + v][j + h]; + v3sum[1 + h] += (psMask[i + v][j + h] - 1.f); } } float blocksum = v3sum[0] + v3sum[1]; for(int voffset = 2; j < winw - (border + offsX); ++j, ++voffset) { - float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1]; + float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1] - 3.f; voffset = voffset == 3 ? 0 : voffset; // faster than voffset %= 3; blocksum -= v3sum[voffset]; blocksum += colSum; @@ -1640,26 +1685,31 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { #ifdef __SSE2__ + if(!(showMotion && showOnlyMask) && smoothTransitions) { if(smoothFactor == 0.f) { for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { - psMask[i][j] = 1.f; + psMask[i][j] = greenWeight - 1.f; } } else { vfloat zerov = F2V(0.f); + vfloat onev = F2V(1.f); vfloat smoothv = F2V(smoothFactor); int j = winx + border - offsX; - for(; j < winw - (border + offsX)- 3; j += 4) { - vfloat blendv = LVFU(psMask[i][j]); + + for(; j < winw - (border + offsX) - 3; j += 4) { + vfloat blendv = LVFU(psMask[i][j]) - onev; blendv = vmaxf(blendv, zerov); blendv = pow_F(blendv, smoothv); STVFU(psMask[i][j], blendv); } + for(; j < winw - (border + offsX); ++j) { - psMask[i][j] = pow_F(std::max(psMask[i][j],0.f),smoothFactor); + psMask[i][j] = pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); } } } + #endif float *greenDest = green[i + offsY]; float *redDest = red[i + offsY]; @@ -1678,10 +1728,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #ifdef __SSE2__ float blend = psMask[i][j]; #else - float blend = pow_F(std::max(psMask[i][j],0.f),smoothFactor); + float blend = pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); #endif red[i + offsY][j + offsX] = intp(blend, red[i + offsY][j + offsX], psRed[i][j] ); - green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX],(psG1[i][j] + psG2[i][j]) / 2.f); + green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX], (psG1[i][j] + psG2[i][j]) / 2.f); blue[i + offsY][j + offsX] = intp(blend, blue[i + offsY][j + offsX], psBlue[i][j]); } else { red[i + offsY][j + offsX] = psRed[i][j]; From 262e9291115647ba5d6fc5641480a8099f072a06 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 27 Jan 2017 16:39:39 +0100 Subject: [PATCH 072/181] pxelshift: small optimization for 3x3new --- rtengine/pixelshift.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 26ac2418f..d933c361b 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -921,7 +921,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight + 1.f; const bool blurMap = bayerParams.pixelShiftBlur; const float sigma = bayerParams.pixelShiftSigma; - const float threshold = bayerParams.pixelShiftSum; + const float threshold = bayerParams.pixelShiftSum + 9.f; const bool experimental0 = bayerParams.pixelShiftExp0; const bool holeFill = bayerParams.pixelShiftHoleFill; const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmoothFactor > 0.; @@ -1656,14 +1656,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int v = -1; v <= 1; v++) { for(int h = -1; h < 1; h++) { - v3sum[1 + h] += (psMask[i + v][j + h] - 1.f); + v3sum[1 + h] += (psMask[i + v][j + h]); } } float blocksum = v3sum[0] + v3sum[1]; for(int voffset = 2; j < winw - (border + offsX); ++j, ++voffset) { - float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1] - 3.f; + float colSum = psMask[i - 1][j + 1] + psMask[i][j + 1] + psMask[i + 1][j + 1]; voffset = voffset == 3 ? 0 : voffset; // faster than voffset %= 3; blocksum -= v3sum[voffset]; blocksum += colSum; From bd78989b9cd5f67b84fbc6741304592d5c1f843e Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 27 Jan 2017 22:06:07 +0100 Subject: [PATCH 073/181] pixelshift: fixed accidently commited change to eperiso adjuster --- rtengine/pixelshift.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index d933c361b..ed967c074 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -1095,7 +1095,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } nRead *= pow(2.f, nreadIso); - eperIsoModel *= pow(2.f, eperIso * 0.5f); + eperIsoModel *= pow(2.f, eperIso); if(adaptive && experimental0) { eperIso = eperIsoModel * sqrtf(100.f / (rawWpCorrection * idata->getISOSpeed())); From 3f2f1fb8ef6216920390b733ea4a3944fba65d37 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 29 Jan 2017 15:16:16 +0100 Subject: [PATCH 074/181] pixelshift: fixed wrong weight of red/blue in 3x3new --- rtengine/pixelshift.cc | 97 +++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 58 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index ed967c074..1b9689137 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -1040,7 +1040,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); int gridSize = 1; bool nOf3x3 = false; - switch (gridSize_) { case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: @@ -1064,6 +1063,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA nOf3x3 = true; } + if(adaptive && blurMap && nOf3x3 && smoothFactor == 0.f && !showMotion) { + if(plistener) { + plistener->setProgress(1.0); + } + return; + } + + // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); @@ -1151,15 +1158,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA array2D psG1(winw + 32, winh); array2D psG2(winw + 32, winh); array2D psBlue(winw + 32, winh); - array2D psMask(winw, winh); - - // Fill the mask with value 1.0 - // We work in 1.0 to 2.0 range to avoid performance issues caused by denormal numbers in gaussian blur - for(int i = 0; i < winh; ++i) { - for(int j = 0; j < winw; ++j) { - psMask[i][j] = 1.f; - } - } // fill channels psRed, psG1, psG2 and psBlue #ifdef _OPENMP @@ -1200,6 +1198,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; + array2D psMask(winw, winh); + + #ifdef _OPENMP #pragma omp parallel #endif @@ -1323,10 +1324,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop unsigned int offset = (c & 1); -// offset ^= 1; // 0 => 1 or 1 => 0 for(; j < winw - (border + offsX); ++j) { - bool greenFromPs = false; + psMask[i][j] = 1.f; + offset ^= 1; // 0 => 1 or 1 => 0 if(detectMotion || (adaptive && checkGreen)) { @@ -1345,6 +1346,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), @@ -1355,6 +1359,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), @@ -1367,13 +1374,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); + // adjust index for next column + lastIndex ++; + lastIndex = lastIndex == gridSize ? 0 : lastIndex; } - - // adjust index for next column - lastIndex ++; - lastIndex = lastIndex == gridSize ? 0 : lastIndex; - if(!adaptive) { // increase motion detection dependent on brightness korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; @@ -1391,7 +1396,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA green[i + offsY][j + offsX] = blueRow ? psG2[i][j] : psG1[i][j];; } - continue; } else { // at least one of the tested green pixels of the grid is detected as motion paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); @@ -1401,10 +1405,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA j++; paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); } - - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; } + // do not set the motion pixel values. They have already been set by demosaicer or showMotion + continue; } } @@ -1592,20 +1595,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } if(experimental0) { // for experiments - // float green1Median, green2Median; - // green1Median = median(psG1[ i - 1 ][ j - 1 ],psG1[ i - 1 ][ j + 1 ],psG1[ i ][ j ],psG1[ i + 1 ][ j -1 ],psG1[ i + 1 ][ j + 1 ]); - // green2Median = median(psG2[ i - 1 ][ j - 1 ],psG2[ i - 1 ][ j + 1 ],psG2[ i ][ j ],psG2[ i + 1 ][ j -1 ],psG2[ i + 1 ][ j + 1 ]); - // float greenDiffMedian = nonGreenDiff(green1Median, green2Median, stddevFactorGreen * 0.36f, eperIsoGreen, nRead, prnu, showMotion); - // - // if(greenDiffMedian > 0.f) { - // if(nOf3x3) { - // psMask[i][j] = 1.f; - // } else { - // paintMotionMask(j + offsX, showMotion, greenDiffMedian, showOnlyMask, greenDest, redDest, blueDest); - // } - // - // continue; - // } } } @@ -1629,6 +1618,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA sum[1] += sumThr[0]; } } + + float percent0 = 100.f * sum[0] / pixelcount; float percent1 = 100.f * sum[1] / pixelcount; @@ -1636,7 +1627,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(adaptive && nOf3x3) { if(blurMap) { - StopWatch Stop1("Blur"); #pragma omp parallel { gaussianBlur(psMask, psMask, winw, winh, sigma); @@ -1645,6 +1635,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA array2D mask(W, H, ARRAY2D_CLEAR_DATA); array2D maskInv(W, H, ARRAY2D_CLEAR_DATA); + #pragma omp parallel for schedule(dynamic,16) for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { @@ -1681,32 +1672,25 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA xorMasks(winx + border - offsX, winw - (border + offsX), winy + border - offsY, winh - (border + offsY), maskInv, mask); } + #pragma omp parallel for schedule(dynamic,16) for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { #ifdef __SSE2__ if(!(showMotion && showOnlyMask) && smoothTransitions) { - if(smoothFactor == 0.f) { - for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { - psMask[i][j] = greenWeight - 1.f; - } - } else { - vfloat zerov = F2V(0.f); - vfloat onev = F2V(1.f); - vfloat smoothv = F2V(smoothFactor); - int j = winx + border - offsX; + vfloat onev = F2V(1.f); + vfloat smoothv = F2V(smoothFactor); + int j = winx + border - offsX; - for(; j < winw - (border + offsX) - 3; j += 4) { - vfloat blendv = LVFU(psMask[i][j]) - onev; - blendv = vmaxf(blendv, zerov); - blendv = pow_F(blendv, smoothv); - STVFU(psMask[i][j], blendv); - } + for(; j < winw - (border + offsX) - 3; j += 4) { + vfloat blendv = vmaxf(LVFU(psMask[i][j]), onev) - onev; + blendv = pow_F(blendv, smoothv); + STVFU(psMask[i][j], blendv); + } - for(; j < winw - (border + offsX); ++j) { - psMask[i][j] = pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); - } + for(; j < winw - (border + offsX); ++j) { + psMask[i][j] = pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); } } @@ -1718,10 +1702,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { if(mask[i][j] == 255) { paintMotionMask(j + offsX, showMotion, 0.5f, showOnlyMask, greenDest, redDest, blueDest); - continue; - } - - if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + } else if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else { if(smoothTransitions) { From 291f1ed9667b62ed7d1a832cb936669249352425 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 29 Jan 2017 15:37:15 +0100 Subject: [PATCH 075/181] pixelshift: extended range of eperiso adjuster --- rtgui/bayerprocess.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 8c2b8da48..ef2905f8d 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -224,7 +224,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftStddevFactorBlue->show(); pixelShiftOptions->pack_start(*pixelShiftStddevFactorBlue); - pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -2.0, 2.0, 0.05, 0.0)); + pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -5.0, 5.0, 0.05, 0.0)); pixelShiftEperIso->setAdjusterListener (this); if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { From bcb3ce8164b9d71ecd17f7857b374fb2bbacf6d8 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 29 Jan 2017 17:28:30 +0100 Subject: [PATCH 076/181] pixelshift: changed ePerIso calculation --- rtengine/pixelshift.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 1b9689137..7696c1874 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -958,7 +958,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) }; - static const float ePerIsoK3II = 4 * 0.35f; + static const float ePerIsoK3II = 0.35f; static const float nReadK1[] = { 3.45f, // ISO 100 3.15f, // ISO 125 @@ -996,7 +996,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 2.4f // ISO 204800 }; - static const float ePerIsoK1 = 4 * 0.75f; + static const float ePerIsoK1 = 0.75f; static const float nReadK70[] = { 4.0f, // ISO 100 4.0f, // ISO 125 @@ -1029,7 +1029,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) }; - static const float ePerIsoK70 = 4 * 0.5f; + static const float ePerIsoK70 = 0.5f; if (plistener) { plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); @@ -1110,9 +1110,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); } - float eperIsoRed = eperIso / scale_mul[0]; - float eperIsoGreen = eperIso * scaleGreen; - float eperIsoBlue = eperIso / scale_mul[2]; + std::cout << "WL: " << c_white[0] << " BL: " << c_black[0] << " ePerIso multiplicator: " << (65535.f / (c_white[0] - c_black[0])) << std::endl; + + float eperIsoRed = (eperIso / scale_mul[0]) * (65535.f / (c_white[0]- c_black[0])); + float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1]- c_black[1])); + float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2]- c_black[2])); // printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); From bd492e0ece3de0ab2509b665996ca1b9b4b72132 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 1 Feb 2017 19:35:36 +0100 Subject: [PATCH 077/181] pixelshift: optionally select lmmse for motion in high iso files --- rtdata/languages/default | 2 ++ rtengine/dcraw.cc | 2 +- rtengine/demosaic_algos.cc | 2 +- rtengine/procevents.h | 1 + rtengine/procparams.cc | 14 ++++++++++++++ rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 32 ++++++++++++++++++++++++++------ rtengine/rawimagesource.h | 2 +- rtengine/refreshmap.cc | 3 ++- rtgui/bayerprocess.cc | 28 ++++++++++++++++++++++++++++ rtgui/bayerprocess.h | 4 +++- rtgui/paramsedited.cc | 8 +++++++- rtgui/paramsedited.h | 1 + 13 files changed, 88 insertions(+), 12 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 68386d1bd..7d01c019d 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -703,6 +703,7 @@ HISTORY_MSG_468;EvPixelShiftMedian HISTORY_MSG_469;EvPixelShiftMedian3 HISTORY_MSG_470;EvPixelShiftMotionMethod HISTORY_MSG_471;EvPixelShiftSmooth +HISTORY_MSG_472;EvPixelShiftLmmse HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1694,6 +1695,7 @@ TP_RAW_PIXELSHIFTMEDIAN3;Exclude selected frame from median TP_RAW_PIXELSHIFTHOLEFILL;3x3 new: Fill holes TP_RAW_PIXELSHIFTBLUR;3x3 new: Blur TP_RAW_PIXELSHIFTSMOOTH;3x3 new: Smooth transitions +TP_RAW_PIXELSHIFTLMMSE;Use lmmse for motion parts TP_RAW_PIXELSHIFTEXP0;Experimental TP_RAW_PIXELSHIFTGREEN;Check dual green TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 8e973913e..5a517b107 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -9486,7 +9486,7 @@ dng_skip: adobe_coeff (make, model); if(!strncmp(make, "Samsung", 7) && !strncmp(model, "NX1",3)) adobe_coeff (make, model); - if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4))) + if(!strncmp(make, "Pentax", 6) && (!strncmp(model, "K10D",4) || !strncmp(model, "K-70",4) || !strncmp(model, "K-1",3))) adobe_coeff (make, model); if(!strncmp(make, "Leica", 5) && !strncmp(model, "Q",1)) adobe_coeff (make, model); diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index e641ed777..1d8554792 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -1315,7 +1315,7 @@ void RawImageSource::jdl_interpolate_omp() // from "Lassus" // Adapted to RawTherapee by Jacques Desmis 3/2013 // Improved speed and reduced memory consumption by Ingo Weyrich 2/2015 //TODO Tiles to reduce memory consumption -SSEFUNCTION void RawImageSource::lmmse_interpolate_omp(int winw, int winh, int iterations) +SSEFUNCTION void RawImageSource::lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations) { const int width = winw, height = winh; const int ba = 10; diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 86deb7d6d..13294c73a 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -498,6 +498,7 @@ enum ProcEvent { EvPixelShiftMedian3 = 468, EvPixelShiftMotionMethod = 469, EvPixelShiftSmooth = 470, + EvPixelShiftLmmse = 471, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 18437431d..dc6b804a1 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -899,6 +899,7 @@ void RAWParams::BayerSensor::setPixelShiftDefaults() pixelShiftBlur = true; pixelShiftSmoothFactor = 0.7; pixelShiftExp0 = false; + pixelShiftLmmse = false; pixelShiftNonGreenCross = true; pixelShiftNonGreenCross2 = false; pixelShiftNonGreenAmaze = false; @@ -3492,6 +3493,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftExp0", raw.bayersensor.pixelShiftExp0 ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftLmmse) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftLmmse", raw.bayersensor.pixelShiftLmmse ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross) { keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); } @@ -7752,6 +7757,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftLmmse")) { + raw.bayersensor.pixelShiftLmmse = keyFile.get_boolean("RAW Bayer", "pixelShiftLmmse"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftLmmse = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross")) { raw.bayersensor.pixelShiftNonGreenCross = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross"); @@ -8237,6 +8250,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur && raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 + && raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 21fe97855..096decd90 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1231,6 +1231,7 @@ public: bool pixelShiftBlur; double pixelShiftSmoothFactor; bool pixelShiftExp0; + bool pixelShiftLmmse; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; bool pixelShiftNonGreenAmaze; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index e5cce8f57..327c6e3ba 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2039,12 +2039,20 @@ void RawImageSource::demosaic(const RAWParams &raw) if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction if(!bayerParams.pixelShiftMedian3) { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[0]), red, green, blue); + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(W, H, *(rawDataFrames[0]), red, green, blue, raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[0]), red, green, blue); + } multi_array2D redTmp(W,H); multi_array2D greenTmp(W,H); multi_array2D blueTmp(W,H); for(int i=0;i<3;i++) { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i], raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); + } } #pragma omp parallel for schedule(dynamic,16) for(int i=border;i blueTmp(W,H); for(int i=0, frameIndex = 0;i<4;++i) { if(i != currFrame) { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex], raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); + } ++frameIndex; } } @@ -2124,10 +2136,18 @@ void RawImageSource::demosaic(const RAWParams &raw) } } } else { - amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction + } } } else { - amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction + } } } pixelshift(0, 0, W, H, bayerParams, currFrame, ri->get_model(), raw.expos); @@ -2139,7 +2159,7 @@ void RawImageSource::demosaic(const RAWParams &raw) } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::igv]) { igv_interpolate(W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::lmmse]) { - lmmse_interpolate_omp(W, H, raw.bayersensor.lmmse_iterations); + lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::fast] ) { fast_demosaic (0, 0, W, H); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::mono] ) { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index d54bcd49d..280b546f4 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -236,7 +236,7 @@ protected: void ppg_demosaic(); void jdl_interpolate_omp(); void igv_interpolate(int winw, int winh); - void lmmse_interpolate_omp(int winw, int winh, int iterations); + void lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations); void amaze_demosaic_RT(int winx, int winy, int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue);//Emil's code for AMaZE void fast_demosaic(int winx, int winy, int winw, int winh );//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 891f191b2..d759f6866 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -497,7 +497,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftMedian DEMOSAIC, // EvPixelShiftMedian3 DEMOSAIC, // EvPixelShiftMotionMethod - DEMOSAIC // EvPixelShiftSmooth + DEMOSAIC, // EvPixelShiftSmooth + DEMOSAIC // EvPixelShiftLmmse }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index ef2905f8d..dbf41781a 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -169,6 +169,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftExp0 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTEXP0"))); pixelShiftOptions->pack_start(*pixelShiftExp0); + pixelShiftLmmse = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTLMMSE"))); + pixelShiftOptions->pack_start(*pixelShiftLmmse); + pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); pixelShiftMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); @@ -313,6 +316,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftGreenconn = pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true); pixelShiftBlurconn = pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true); pixelShiftExp0conn = pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true); + pixelShiftLmmseconn = pixelShiftLmmse->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftLmmseChanged), true); pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); pixelShiftNonGreenAmazeconn = pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true); @@ -355,6 +359,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); pixelShiftSmooth->setEditedState ( pedited->raw.bayersensor.pixelShiftSmooth ? Edited : UnEdited); pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); + pixelShiftLmmse->set_inconsistent(!pedited->raw.bayersensor.pixelShiftLmmse); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); @@ -400,6 +405,7 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); + pixelShiftLmmse->set_active(pp->raw.bayersensor.pixelShiftLmmse); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); @@ -499,6 +505,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); pp->raw.bayersensor.pixelShiftSmoothFactor = pixelShiftSmooth->getValue(); pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); + pp->raw.bayersensor.pixelShiftLmmse = pixelShiftLmmse->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); @@ -546,6 +553,7 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); pedited->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->getEditedState(); pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftLmmse = !pixelShiftLmmse->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); @@ -1018,6 +1026,26 @@ void BayerProcess::pixelShiftExp0Changed () } } +void BayerProcess::pixelShiftLmmseChanged () +{ + if (batchMode) { + if (pixelShiftLmmse->get_inconsistent()) { + pixelShiftLmmse->set_inconsistent (false); + pixelShiftLmmseconn.block (true); + pixelShiftLmmse->set_active (false); + pixelShiftLmmseconn.block (false); + } else if (lastDCBen) { + pixelShiftLmmse->set_inconsistent (true); + } + + lastDCBen = pixelShiftLmmse->get_active (); + } + + if (listener) { + listener->panelChanged (EvPixelShiftLmmse, pixelShiftLmmse->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + void BayerProcess::pixelShiftNonGreenCrossChanged () { if (batchMode) { diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index def4021ce..c8658f5bf 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -60,6 +60,7 @@ protected: Gtk::CheckButton* pixelShiftHoleFill; Gtk::CheckButton* pixelShiftMedian; Gtk::CheckButton* pixelShiftMedian3; + Gtk::CheckButton* pixelShiftLmmse; Adjuster* pixelShiftSmooth; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; @@ -76,7 +77,7 @@ protected: sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftHoleFillconn, pixelShiftMedianconn, pixelShiftMedian3conn, pixelShiftNonGreenCrossconn, - pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftSmoothconn, pixelShiftExp0conn, pixelShiftMotionMethodConn; + pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftSmoothconn, pixelShiftExp0conn, pixelShiftLmmseconn, pixelShiftMotionMethodConn; public: BayerProcess (); @@ -102,6 +103,7 @@ public: void pixelShiftGreenChanged(); void pixelShiftBlurChanged(); void pixelShiftExp0Changed(); + void pixelShiftLmmseChanged(); void pixelShiftNonGreenCrossChanged(); void pixelShiftNonGreenCross2Changed(); void pixelShiftNonGreenAmazeChanged(); diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index e1303095a..939413f18 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -394,6 +394,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftBlur = v; raw.bayersensor.pixelShiftSmooth = v; raw.bayersensor.pixelShiftExp0 = v; + raw.bayersensor.pixelShiftLmmse = v; raw.bayersensor.pixelShiftNonGreenCross = v; raw.bayersensor.pixelShiftNonGreenCross2 = v; raw.bayersensor.pixelShiftNonGreenAmaze = v; @@ -917,6 +918,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftBlur = raw.bayersensor.pixelShiftBlur && p.raw.bayersensor.pixelShiftBlur == other.raw.bayersensor.pixelShiftBlur; raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor; raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; + raw.bayersensor.pixelShiftLmmse = raw.bayersensor.pixelShiftLmmse && p.raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; @@ -2428,6 +2430,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftExp0 = mods.raw.bayersensor.pixelShiftExp0; } + if (raw.bayersensor.pixelShiftLmmse) { + toEdit.raw.bayersensor.pixelShiftLmmse = mods.raw.bayersensor.pixelShiftLmmse; + } + if (raw.bayersensor.pixelShiftNonGreenCross) { toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; } @@ -2952,7 +2958,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftMotionCorrectionMethod && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 && pixelShiftLmmse && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 23b0e240b..5f66c3d18 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -716,6 +716,7 @@ public: bool pixelShiftBlur; bool pixelShiftSmooth; bool pixelShiftExp0; + bool pixelShiftLmmse; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; bool pixelShiftNonGreenAmaze; From 812bf40d176b21f412b951610720aad76fd8fa5a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 5 Feb 2017 16:06:57 +0100 Subject: [PATCH 078/181] Show raw values in navigator when demosaic 'none' is used --- rtengine/imagesource.h | 1 + rtengine/rawimagesource.cc | 15 +++++++++++++++ rtengine/rawimagesource.h | 1 + rtengine/stdimagesource.h | 3 +++ rtgui/cropwindow.cc | 14 +++++++++++++- 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index 98e5446a1..e8740edf4 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -160,6 +160,7 @@ public: { return this; } + virtual void getRawValues(int x, int y, int &R, int &G, int &B) = 0; }; } #endif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 327c6e3ba..fa433e98c 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5460,6 +5460,21 @@ void RawImageSource::init () } } +void RawImageSource::getRawValues(int x, int y, int &R, int &G, int &B) +{ + int xnew = x + border; + int ynew = y + border; + int c = FC(ynew,xnew); + int val = rawData[ynew][xnew] / scale_mul[c]; + if(c == 0) { + R = val; G = 0; B = 0; + } else if(c == 2) { + R = 0; G = 0; B = val; + } else { + R = 0; G = val; B = 0; + } +} + void RawImageSource::cleanup () { delete phaseOneIccCurve; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 280b546f4..99363d9e0 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -264,6 +264,7 @@ protected: void pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); void vflip (Imagefloat* im); + void getRawValues(int x, int y, int &R, int &G, int &B); }; } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 733a44c42..775bd484d 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -97,6 +97,9 @@ public: } void setCurrentFrame(unsigned int frameNum) {} + void getRawValues(int x, int y, int &R, int &G, int &B) { R = G = B = 0;} + + }; } #endif diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 2513bb137..c021886fb 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -1035,9 +1035,21 @@ void CropWindow::pointerMoved (int bstate, int x, int y) int imheight = cropHandler.cropPixbuf->get_height(); guint8* pix = cropHandler.cropPixbuftrue->get_pixels() + vy * cropHandler.cropPixbuf->get_rowstride() + vx * 3; + int rval = pix[0]; + int gval = pix[1]; + int bval = pix[2]; if (vx < imwidth && vy < imheight) { + rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator(); + if(ipc) { + procparams::ProcParams params; + ipc->getParams(¶ms); + if(params.raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::none]) { + ImageSource *isrc = static_cast(ipc->getInitialImage()); + isrc->getRawValues(mx, my, rval, gval, bval); + } + } // pmlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); - pmlistener->pointerMoved (true, cropHandler.colorParams.output, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); + pmlistener->pointerMoved (true, cropHandler.colorParams.output, cropHandler.colorParams.working, mx, my, rval, gval, bval); if (pmhlistener) // pmhlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); From b720a673284c64dd2d2d20b844bcaea950b6c1f7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 5 Feb 2017 16:28:25 +0100 Subject: [PATCH 079/181] getRawValues: round instead of truncate --- rtengine/rawimagesource.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index fa433e98c..00ce631b2 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5465,7 +5465,7 @@ void RawImageSource::getRawValues(int x, int y, int &R, int &G, int &B) int xnew = x + border; int ynew = y + border; int c = FC(ynew,xnew); - int val = rawData[ynew][xnew] / scale_mul[c]; + int val = round(rawData[ynew][xnew] / scale_mul[c]); if(c == 0) { R = val; G = 0; B = 0; } else if(c == 2) { From d570459f1e82f6a5ec9090bfb8c9931d36ef4809 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 5 Feb 2017 19:03:34 +0100 Subject: [PATCH 080/181] Fixed bug in last commit when image was rotated --- rtengine/imagesource.h | 2 +- rtengine/rawimagesource.cc | 17 ++++++++++++++++- rtengine/rawimagesource.h | 2 +- rtengine/stdimagesource.h | 2 +- rtgui/cropwindow.cc | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index e8740edf4..8998fa848 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -160,7 +160,7 @@ public: { return this; } - virtual void getRawValues(int x, int y, int &R, int &G, int &B) = 0; + virtual void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) = 0; }; } #endif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 00ce631b2..4fce68bdf 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5460,10 +5460,25 @@ void RawImageSource::init () } } -void RawImageSource::getRawValues(int x, int y, int &R, int &G, int &B) +void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int &B) { int xnew = x + border; int ynew = y + border; + rotate += ri->get_rotateDegree(); + rotate %= 360; + if (rotate == 90) { + std::swap(xnew,ynew); + ynew = H - 1 - ynew; + } else if (rotate == 180) { + xnew = W - 1 - xnew; + ynew = H - 1 - ynew; + } else if (rotate == 270) { + std::swap(xnew,ynew); + ynew = H - 1 - ynew; + xnew = W - 1 - xnew; + ynew = H - 1 - ynew; + } + int c = FC(ynew,xnew); int val = round(rawData[ynew][xnew] / scale_mul[c]); if(c == 0) { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 99363d9e0..948efb355 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -264,7 +264,7 @@ protected: void pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); void vflip (Imagefloat* im); - void getRawValues(int x, int y, int &R, int &G, int &B); + void getRawValues(int x, int y, int rotate, int &R, int &G, int &B); }; } diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 775bd484d..1dbddf325 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -97,7 +97,7 @@ public: } void setCurrentFrame(unsigned int frameNum) {} - void getRawValues(int x, int y, int &R, int &G, int &B) { R = G = B = 0;} + void getRawValues(int x, int y, int rotate, int &R, int &G, int &B) { R = G = B = 0;} }; diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index c021886fb..a6e93c511 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -1045,7 +1045,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y) ipc->getParams(¶ms); if(params.raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::none]) { ImageSource *isrc = static_cast(ipc->getInitialImage()); - isrc->getRawValues(mx, my, rval, gval, bval); + isrc->getRawValues(mx, my, params.coarse.rotate, rval, gval, bval); } } // pmlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); From 2feb43f5d510e511944262b1f893f6394fd30893 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 6 Feb 2017 16:43:49 +0100 Subject: [PATCH 081/181] Fix display of raw values in navigator for xtrans --- rtengine/rawimagesource.cc | 2 +- rtgui/cropwindow.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 4fce68bdf..2cb642285 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -5479,7 +5479,7 @@ void RawImageSource::getRawValues(int x, int y, int rotate, int &R, int &G, int ynew = H - 1 - ynew; } - int c = FC(ynew,xnew); + int c = ri->getSensorType() == ST_FUJI_XTRANS ? ri->XTRANSFC(ynew,xnew) : ri->FC(ynew,xnew); int val = round(rawData[ynew][xnew] / scale_mul[c]); if(c == 0) { R = val; G = 0; B = 0; diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index a6e93c511..71855786e 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -1043,7 +1043,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y) if(ipc) { procparams::ProcParams params; ipc->getParams(¶ms); - if(params.raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::none]) { + if(params.raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::none] || params.raw.xtranssensor.method == RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::none]) { ImageSource *isrc = static_cast(ipc->getInitialImage()); isrc->getRawValues(mx, my, params.coarse.rotate, rval, gval, bval); } From dca0e41f35c5a7e3c0cfe3c7a06d0e410ae7da4e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 12 Feb 2017 17:39:52 +0100 Subject: [PATCH 082/181] added "Auto White Balance temperature bias" feature This new slider in the White Balance tools allows to alter the computation of the "auto white balance" by "biasing" it towards warmer or cooler temperatures. The bias is expressed as a percentage of the computed temperature, so that the resuling temperature is given by "computedTemp + computedTemp * bias". --- rtdata/languages/Catala | 3 + rtdata/languages/Chinese (Simplified) | 3 + rtdata/languages/Chinese (Traditional) | 3 + rtdata/languages/Czech | 7 ++ rtdata/languages/Dansk | 3 + rtdata/languages/Deutsch | 7 ++ rtdata/languages/English (UK) | 3 + rtdata/languages/English (US) | 3 + rtdata/languages/Espanol | 3 + rtdata/languages/Euskara | 3 + rtdata/languages/Francais | 7 ++ rtdata/languages/Greek | 3 + rtdata/languages/Hebrew | 3 + rtdata/languages/Italiano | 3 + rtdata/languages/Japanese | 3 + rtdata/languages/Latvian | 3 + rtdata/languages/Magyar | 3 + rtdata/languages/Nederlands | 3 + rtdata/languages/Norsk BM | 3 + rtdata/languages/Polish | 3 + rtdata/languages/Polish (Latin Characters) | 3 + rtdata/languages/Portugues (Brasil) | 3 + rtdata/languages/Russian | 3 + rtdata/languages/Serbian (Cyrilic Characters) | 3 + rtdata/languages/Serbian (Latin Characters) | 3 + rtdata/languages/Slovak | 3 + rtdata/languages/Suomi | 3 + rtdata/languages/Swedish | 3 + rtdata/languages/Turkish | 3 + rtdata/languages/default | 3 + rtengine/colortemp.h | 5 +- rtengine/improccoordinator.cc | 22 ++++-- rtengine/improccoordinator.h | 3 +- rtengine/procevents.h | 1 + rtengine/procparams.cc | 79 +++++++++++-------- rtengine/procparams.h | 4 +- rtengine/refreshmap.cc | 3 +- rtengine/rtengine.h | 2 +- rtengine/rtthumbnail.cc | 14 +++- rtengine/rtthumbnail.h | 4 +- rtengine/simpleprocess.cc | 2 +- rtgui/addsetids.h | 1 + rtgui/batchtoolpanelcoord.cc | 12 ++- rtgui/batchtoolpanelcoord.h | 2 +- rtgui/options.cc | 1 + rtgui/paramsedited.cc | 6 ++ rtgui/paramsedited.h | 1 + rtgui/preferences.cc | 1 + rtgui/thumbnail.cc | 4 +- rtgui/thumbnail.h | 2 +- rtgui/toolpanelcoord.h | 4 +- rtgui/wbprovider.h | 2 +- rtgui/whitebalance.cc | 68 +++++++++++++--- rtgui/whitebalance.h | 5 +- 54 files changed, 276 insertions(+), 74 deletions(-) diff --git a/rtdata/languages/Catala b/rtdata/languages/Catala index 5fc720cfa..991d99479 100644 --- a/rtdata/languages/Catala +++ b/rtdata/languages/Catala @@ -1280,6 +1280,7 @@ ZOOMPANEL_ZOOMOUT;Allunya\nDrecera: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2033,6 +2034,8 @@ ZOOMPANEL_ZOOMOUT;Allunya\nDrecera: - !TP_WAVELET_TON;Toning !TP_WBALANCE_EQBLUERED;Blue/Red equalizer !TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behavior of "white balance" by modulating the blue/red balance.\nThis can be useful when shooting conditions:\na) are far from the standard illuminant (e.g. underwater),\nb) are far from conditions where calibrations were performed,\nc) where the matrices or ICC profiles are unsuitable. +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 !TP_WBALANCE_WATER_HEADER;UnderWater diff --git a/rtdata/languages/Chinese (Simplified) b/rtdata/languages/Chinese (Simplified) index 64631d2b8..5900ef487 100644 --- a/rtdata/languages/Chinese (Simplified) +++ b/rtdata/languages/Chinese (Simplified) @@ -1197,6 +1197,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2041,5 +2042,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !TP_WBALANCE_LED_LSI;LSI Lumelex 2040 !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Chinese (Traditional) b/rtdata/languages/Chinese (Traditional) index 90933541f..fdc36f91a 100644 --- a/rtdata/languages/Chinese (Traditional) +++ b/rtdata/languages/Chinese (Traditional) @@ -934,6 +934,7 @@ TP_WBALANCE_TEMPERATURE;色溫 !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2030,6 +2031,8 @@ TP_WBALANCE_TEMPERATURE;色溫 !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Czech b/rtdata/languages/Czech index 6fa1fa8f7..08efca6cb 100644 --- a/rtdata/languages/Czech +++ b/rtdata/languages/Czech @@ -2070,3 +2070,10 @@ ZOOMPANEL_ZOOMFITSCREEN;Přizpůsobit obrázek obrazovce\nZkratka: f ZOOMPANEL_ZOOMIN;Přiblížit\nZkratka: + ZOOMPANEL_ZOOMOUT;Oddálit\nZkratka: - +!!!!!!!!!!!!!!!!!!!!!!!!! +! Untranslated keys follow; remove the ! prefix after an entry is translated. +!!!!!!!!!!!!!!!!!!!!!!!!! + +!HISTORY_MSG_444;WB - Temp bias +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Dansk b/rtdata/languages/Dansk index 1aa0d3788..dc28d6c59 100644 --- a/rtdata/languages/Dansk +++ b/rtdata/languages/Dansk @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;Temperatur !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 13da1e9fc..277b6782e 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -2067,3 +2067,10 @@ ZOOMPANEL_ZOOMFITSCREEN;An Bildschirm anpassen\nTaste: f ZOOMPANEL_ZOOMIN;Hineinzoomen\nTaste: + ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - +!!!!!!!!!!!!!!!!!!!!!!!!! +! Untranslated keys follow; remove the ! prefix after an entry is translated. +!!!!!!!!!!!!!!!!!!!!!!!!! + +!HISTORY_MSG_444;WB - Temp bias +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/English (UK) b/rtdata/languages/English (UK) index bf4734453..30e9bffab 100644 --- a/rtdata/languages/English (UK) +++ b/rtdata/languages/English (UK) @@ -758,6 +758,7 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT;Add !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !HISTORY_SNAPSHOT;Snapshot @@ -2023,6 +2024,8 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) !TP_WBALANCE_SPOTWB;Spot WB +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TEMPERATURE;Temperature !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index d5a0e16f2..63fe55143 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -676,6 +676,7 @@ !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT;Add !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !HISTORY_SNAPSHOT;Snapshot @@ -2023,6 +2024,8 @@ !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) !TP_WBALANCE_SPOTWB;Spot WB +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TEMPERATURE;Temperature !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 diff --git a/rtdata/languages/Espanol b/rtdata/languages/Espanol index f9763dadb..3cd36a01f 100644 --- a/rtdata/languages/Espanol +++ b/rtdata/languages/Espanol @@ -1674,6 +1674,7 @@ ZOOMPANEL_ZOOMOUT;Reducir Zoom\nAtajo: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2100,4 +2101,6 @@ ZOOMPANEL_ZOOMOUT;Reducir Zoom\nAtajo: - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Euskara b/rtdata/languages/Euskara index fb1b452dc..5bc77fdd8 100644 --- a/rtdata/languages/Euskara +++ b/rtdata/languages/Euskara @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 937ac0927..d1031f359 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2035,3 +2035,10 @@ ZOOMPANEL_ZOOMFITSCREEN;Affiche l'image entière\nRaccourci: f ZOOMPANEL_ZOOMIN;Zoom Avant\nRaccourci: + ZOOMPANEL_ZOOMOUT;Zoom Arrière\nRaccourci: - +!!!!!!!!!!!!!!!!!!!!!!!!! +! Untranslated keys follow; remove the ! prefix after an entry is translated. +!!!!!!!!!!!!!!!!!!!!!!!!! + +!HISTORY_MSG_444;WB - Temp bias +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Greek b/rtdata/languages/Greek index f57acf41a..6643930de 100644 --- a/rtdata/languages/Greek +++ b/rtdata/languages/Greek @@ -929,6 +929,7 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2028,6 +2029,8 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Hebrew b/rtdata/languages/Hebrew index 0ed0c41bf..1c3a97c16 100644 --- a/rtdata/languages/Hebrew +++ b/rtdata/languages/Hebrew @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;מידת חום !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;מידת חום !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Italiano b/rtdata/languages/Italiano index 613a29301..ae60b9303 100644 --- a/rtdata/languages/Italiano +++ b/rtdata/languages/Italiano @@ -1547,6 +1547,7 @@ ZOOMPANEL_ZOOMOUT;Rimpicciolisci.\nScorciatoia: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2041,4 +2042,6 @@ ZOOMPANEL_ZOOMOUT;Rimpicciolisci.\nScorciatoia: - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Japanese b/rtdata/languages/Japanese index 0ba1b52e4..c879c649c 100644 --- a/rtdata/languages/Japanese +++ b/rtdata/languages/Japanese @@ -1925,6 +1925,7 @@ ZOOMPANEL_ZOOMOUT;ズームアウト\nショートカット: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2071,3 +2072,5 @@ ZOOMPANEL_ZOOMOUT;ズームアウト\nショートカット: - !TP_RETINEX_VIEW_TRAN;Transmission - Auto !TP_RETINEX_VIEW_TRAN2;Transmission - Fixed !TP_RETINEX_VIEW_UNSHARP;Unsharp mask +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Latvian b/rtdata/languages/Latvian index bb9c63ea4..bc3546cba 100644 --- a/rtdata/languages/Latvian +++ b/rtdata/languages/Latvian @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Magyar b/rtdata/languages/Magyar index b2ae35fbe..1188f5344 100644 --- a/rtdata/languages/Magyar +++ b/rtdata/languages/Magyar @@ -1209,6 +1209,7 @@ ZOOMPANEL_ZOOMOUT;Kicsinyítés - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2036,6 +2037,8 @@ ZOOMPANEL_ZOOMOUT;Kicsinyítés - !TP_WAVELET_TON;Toning !TP_WBALANCE_EQBLUERED;Blue/Red equalizer !TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behavior of "white balance" by modulating the blue/red balance.\nThis can be useful when shooting conditions:\na) are far from the standard illuminant (e.g. underwater),\nb) are far from conditions where calibrations were performed,\nc) where the matrices or ICC profiles are unsuitable. +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 !TP_WBALANCE_WATER_HEADER;UnderWater diff --git a/rtdata/languages/Nederlands b/rtdata/languages/Nederlands index adb37a61f..636efe1ad 100644 --- a/rtdata/languages/Nederlands +++ b/rtdata/languages/Nederlands @@ -2004,6 +2004,7 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2058,3 +2059,5 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: - !TP_RETINEX_GAINTRANSMISSION_TOOLTIP;Amplify or reduce transmission map to achieve luminance.\nAbscissa: transmission -min from 0, mean, and values (max).\nOrdinate: gain. !TP_RETINEX_SKAL;Scale !TP_WAVELET_CB_TOOLTIP;For strong values product color-toning by combining it or not with levels decomposition 'toning'\nFor low values you can change the white balance of the background (sky, ...) without changing that of the front plane, generally more contrasted +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Norsk BM b/rtdata/languages/Norsk BM index 40ed600c9..79ad3ebfd 100644 --- a/rtdata/languages/Norsk BM +++ b/rtdata/languages/Norsk BM @@ -929,6 +929,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2028,6 +2029,8 @@ TP_WBALANCE_TEMPERATURE;Temperatur !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Polish b/rtdata/languages/Polish index bbe23f2fd..6493ba694 100644 --- a/rtdata/languages/Polish +++ b/rtdata/languages/Polish @@ -1631,6 +1631,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrót: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2047,3 +2048,5 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrót: - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Polish (Latin Characters) b/rtdata/languages/Polish (Latin Characters) index fd1205a7d..ce490653c 100644 --- a/rtdata/languages/Polish (Latin Characters) +++ b/rtdata/languages/Polish (Latin Characters) @@ -1631,6 +1631,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrot: - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2047,3 +2048,5 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrot: - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Portugues (Brasil) b/rtdata/languages/Portugues (Brasil) index 4643008f1..b7ab9b7bf 100644 --- a/rtdata/languages/Portugues (Brasil) +++ b/rtdata/languages/Portugues (Brasil) @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;Temperatura !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;Temperatura !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Russian b/rtdata/languages/Russian index 120d17530..9ed2b940b 100644 --- a/rtdata/languages/Russian +++ b/rtdata/languages/Russian @@ -1490,6 +1490,7 @@ ZOOMPANEL_ZOOMOUT;Удалить - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2043,4 +2044,6 @@ ZOOMPANEL_ZOOMOUT;Удалить - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Serbian (Cyrilic Characters) b/rtdata/languages/Serbian (Cyrilic Characters) index 6797ab195..648a123e2 100644 --- a/rtdata/languages/Serbian (Cyrilic Characters) +++ b/rtdata/languages/Serbian (Cyrilic Characters) @@ -1657,6 +1657,7 @@ ZOOMPANEL_ZOOMOUT;Умањује приказ слике - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2176,4 +2177,6 @@ ZOOMPANEL_ZOOMOUT;Умањује приказ слике - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Serbian (Latin Characters) b/rtdata/languages/Serbian (Latin Characters) index 1a86a552a..49f5eee27 100644 --- a/rtdata/languages/Serbian (Latin Characters) +++ b/rtdata/languages/Serbian (Latin Characters) @@ -1657,6 +1657,7 @@ ZOOMPANEL_ZOOMOUT;Umanjuje prikaz slike - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2176,4 +2177,6 @@ ZOOMPANEL_ZOOMOUT;Umanjuje prikaz slike - !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TMTYPE;Compression method !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f diff --git a/rtdata/languages/Slovak b/rtdata/languages/Slovak index 8e7957cf4..070c2f4a1 100644 --- a/rtdata/languages/Slovak +++ b/rtdata/languages/Slovak @@ -993,6 +993,7 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2037,6 +2038,8 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Suomi b/rtdata/languages/Suomi index d92c71c88..d14a02808 100644 --- a/rtdata/languages/Suomi +++ b/rtdata/languages/Suomi @@ -931,6 +931,7 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2029,6 +2030,8 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/Swedish b/rtdata/languages/Swedish index d98dabc24..b25ebf2a5 100644 --- a/rtdata/languages/Swedish +++ b/rtdata/languages/Swedish @@ -1918,6 +1918,7 @@ ZOOMPANEL_ZOOMOUT;Förminska.\nKortkommando: - !HISTORY_MSG_425;Retinex - Log base !HISTORY_MSG_427;Output rendering intent !HISTORY_MSG_428;Monitor rendering intent +!HISTORY_MSG_444;WB - Temp bias !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. !IPTCPANEL_COPYRIGHT;Copyright notice @@ -2043,3 +2044,5 @@ ZOOMPANEL_ZOOMOUT;Förminska.\nKortkommando: - !TP_WAVELET_TILES_TOOLTIP;Processing the full image leads to better quality and is the recommended option, while using tiles is a fall-back solution for users with little RAM. Refer to RawPedia for memory requirements. !TP_WAVELET_TMSTRENGTH_TOOLTIP;Control the strength of tone mapping or contrast compression of the residual image. When the value is different from 0, the Strength and Gamma sliders of the Tone Mapping tool in the Exposure tab will become grayed out. !TP_WAVELET_TON;Toning +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Turkish b/rtdata/languages/Turkish index 2e64516f1..03eb15ca3 100644 --- a/rtdata/languages/Turkish +++ b/rtdata/languages/Turkish @@ -930,6 +930,7 @@ TP_WBALANCE_TEMPERATURE;Isı !HISTORY_MSG_441;Retinex - Gain transmission !HISTORY_MSG_442;Retinex - Scale !HISTORY_MSG_443;Output Black Point Compensation +!HISTORY_MSG_444;WB - Temp bias !HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s !IPTCPANEL_CATEGORYHINT;Identifies the subject of the image in the opinion of the provider. !IPTCPANEL_CITYHINT;Enter the name of the city pictured in this image. @@ -2028,6 +2029,8 @@ TP_WBALANCE_TEMPERATURE;Isı !TP_WBALANCE_SOLUX41;Solux 4100K !TP_WBALANCE_SOLUX47;Solux 4700K (vendor) !TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) +!TP_WBALANCE_TEMPBIAS;AWB temperature bias +!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". !TP_WBALANCE_TUNGSTEN;Tungsten !TP_WBALANCE_WATER1;UnderWater 1 !TP_WBALANCE_WATER2;UnderWater 2 diff --git a/rtdata/languages/default b/rtdata/languages/default index fc5aa8881..5bcfc2892 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -675,6 +675,7 @@ HISTORY_MSG_440;CbDL - Method HISTORY_MSG_441;Retinex - Gain transmission HISTORY_MSG_442;Retinex - Scale HISTORY_MSG_443;Output Black Point Compensation +HISTORY_MSG_444;WB - Temp bias HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -2022,6 +2023,8 @@ TP_WBALANCE_SOLUX41;Solux 4100K TP_WBALANCE_SOLUX47;Solux 4700K (vendor) TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) TP_WBALANCE_SPOTWB;Spot WB +TP_WBALANCE_TEMPBIAS;AWB temperature bias +TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". TP_WBALANCE_TEMPERATURE;Temperature TP_WBALANCE_TUNGSTEN;Tungsten TP_WBALANCE_WATER1;UnderWater 1 diff --git a/rtengine/colortemp.h b/rtengine/colortemp.h index 33a2b1b04..2d346dd81 100644 --- a/rtengine/colortemp.h +++ b/rtengine/colortemp.h @@ -57,10 +57,13 @@ public: ColorTemp (double t, double g, double e, const Glib::ustring &m); ColorTemp (double mulr, double mulg, double mulb, double e); - void update (const double rmul, const double gmul, const double bmul, const double equal) + void update (const double rmul, const double gmul, const double bmul, const double equal, const double tempBias=0.0) { this->equal = equal; mul2temp (rmul, gmul, bmul, this->equal, temp, green); + if (tempBias != 0.0 && tempBias >= -1.0 && tempBias <= 1.0) { + temp += temp * tempBias; + } } void useDefaults (const double equal) { diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 46cf031bd..83e4ce40f 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -34,7 +34,7 @@ extern const Settings* settings; ImProcCoordinator::ImProcCoordinator () : orig_prev(nullptr), oprevi(nullptr), oprevl(nullptr), nprevl(nullptr), previmg(nullptr), workimg(nullptr), - ncie(nullptr), imgsrc(nullptr), shmap(nullptr), lastAwbEqual(0.), ipf(¶ms, true), monitorIntent(RI_RELATIVE), + ncie(nullptr), imgsrc(nullptr), shmap(nullptr), lastAwbEqual(0.), lastAwbTempBias(0.0), ipf(¶ms, true), monitorIntent(RI_RELATIVE), softProof(false), gamutCheck(false), scale(10), highDetailPreprocessComputed(false), highDetailRawComputed(false), allocated(false), bwAutoR(-9000.f), bwAutoG(-9000.f), bwAutoB(-9000.f), CAMMean(NAN), @@ -284,15 +284,17 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) if (params.wb.method == "Camera") { currWB = imgsrc->getWB (); } else if (params.wb.method == "Auto") { - if (lastAwbEqual != params.wb.equal) { + if (lastAwbEqual != params.wb.equal || lastAwbTempBias != params.wb.tempBias) { double rm, gm, bm; imgsrc->getAutoWBMultipliers(rm, gm, bm); if (rm != -1.) { - autoWB.update(rm, gm, bm, params.wb.equal); + autoWB.update(rm, gm, bm, params.wb.equal, params.wb.tempBias); lastAwbEqual = params.wb.equal; + lastAwbTempBias = params.wb.tempBias; } else { lastAwbEqual = -1.; + lastAwbTempBias = 0.0; autoWB.useDefaults(params.wb.equal); } @@ -1011,21 +1013,23 @@ void ImProcCoordinator::progress (Glib::ustring str, int pr) }*/ } -bool ImProcCoordinator::getAutoWB (double& temp, double& green, double equal) +bool ImProcCoordinator::getAutoWB (double& temp, double& green, double equal, double tempBias) { if (imgsrc) { - if (lastAwbEqual != equal) { + if (lastAwbEqual != equal || lastAwbTempBias != tempBias) { // Issue 2500 MyMutex::MyLock lock(minit); // Also used in crop window double rm, gm, bm; imgsrc->getAutoWBMultipliers(rm, gm, bm); if (rm != -1) { - autoWB.update(rm, gm, bm, equal); + autoWB.update(rm, gm, bm, equal, tempBias); lastAwbEqual = equal; + lastAwbTempBias = tempBias; } else { lastAwbEqual = -1.; autoWB.useDefaults(equal); + lastAwbTempBias = 0.0; } } @@ -1162,15 +1166,17 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool if (params.wb.method == "Camera") { currWB = imgsrc->getWB (); } else if (params.wb.method == "Auto") { - if (lastAwbEqual != params.wb.equal) { + if (lastAwbEqual != params.wb.equal || lastAwbTempBias != params.wb.tempBias) { double rm, gm, bm; imgsrc->getAutoWBMultipliers(rm, gm, bm); if (rm != -1.) { - autoWB.update(rm, gm, bm, params.wb.equal); + autoWB.update(rm, gm, bm, params.wb.equal, params.wb.tempBias); lastAwbEqual = params.wb.equal; + lastAwbTempBias = params.wb.tempBias; } else { lastAwbEqual = -1.; + lastAwbTempBias = 0.0; autoWB.useDefaults(params.wb.equal); } } diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 2cc767b39..80ad0dd99 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -69,6 +69,7 @@ protected: ColorTemp autoWB; double lastAwbEqual; + double lastAwbTempBias; ImProcFunctions ipf; @@ -256,7 +257,7 @@ public: DetailedCrop* createCrop (::EditDataProvider *editDataProvider, bool isDetailWindow); - bool getAutoWB (double& temp, double& green, double equal); + bool getAutoWB (double& temp, double& green, double equal, double tempBias); void getCamWB (double& temp, double& green); void getSpotWB (int x, int y, int rectSize, double& temp, double& green); void getAutoCrop (double ratio, int &x, int &y, int &w, int &h); diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 52517e527..bca679bdb 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -470,6 +470,7 @@ enum ProcEvent { EvRetinexgaintransmission = 440, EvLskal = 441, EvOBPCompens = 442, + EvWBtempBias = 443, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 788c422d6..7f4ae11f8 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -58,40 +58,40 @@ bool ToneCurveParams::HLReconstructionNecessary(LUTu &histRedRaw, LUTu &histGree void WBParams::init() { // Creation of the different methods and its associated temperature value - wbEntries.push_back(new WBEntry("Camera" , WBT_CAMERA, M("TP_WBALANCE_CAMERA"), 0, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Auto" , WBT_AUTO, M("TP_WBALANCE_AUTO"), 0, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Daylight" , WBT_DAYLIGHT, M("TP_WBALANCE_DAYLIGHT"), 5300, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Cloudy" , WBT_CLOUDY, M("TP_WBALANCE_CLOUDY"), 6200, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Shade" , WBT_SHADE, M("TP_WBALANCE_SHADE"), 7600, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Water 1" , WBT_WATER, M("TP_WBALANCE_WATER1"), 35000, 0.3f, 1.1f)); - wbEntries.push_back(new WBEntry("Water 2" , WBT_WATER, M("TP_WBALANCE_WATER2"), 48000, 0.63f, 1.38f)); - wbEntries.push_back(new WBEntry("Tungsten" , WBT_TUNGSTEN, M("TP_WBALANCE_TUNGSTEN"), 2856, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F1" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO1"), 6430, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F2" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO2"), 4230, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F3" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO3"), 3450, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F4" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO4"), 2940, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F5" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO5"), 6350, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F6" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO6"), 4150, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F7" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO7"), 6500, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F8" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO8"), 5020, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F9" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO9"), 4330, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F10" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO10"), 5300, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F11" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO11"), 4000, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Fluo F12" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO12"), 3000, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("HMI Lamp" , WBT_LAMP, M("TP_WBALANCE_HMI"), 4800, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("GTI Lamp" , WBT_LAMP, M("TP_WBALANCE_GTI"), 5000, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("JudgeIII Lamp" , WBT_LAMP, M("TP_WBALANCE_JUDGEIII"), 5100, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Solux Lamp 3500K" , WBT_LAMP, M("TP_WBALANCE_SOLUX35"), 3480, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Solux Lamp 4100K" , WBT_LAMP, M("TP_WBALANCE_SOLUX41"), 3930, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Solux Lamp 4700K" , WBT_LAMP, M("TP_WBALANCE_SOLUX47"), 4700, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("NG Solux Lamp 4700K" , WBT_LAMP, M("TP_WBALANCE_SOLUX47_NG"), 4480, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("LED LSI Lumelex 2040", WBT_LED, M("TP_WBALANCE_LED_LSI"), 2970, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("LED CRS SP12 WWMR16" , WBT_LED, M("TP_WBALANCE_LED_CRS"), 3050, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Flash 5500K" , WBT_FLASH, M("TP_WBALANCE_FLASH55"), 5500, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Flash 6000K" , WBT_FLASH, M("TP_WBALANCE_FLASH60"), 6000, 1.f, 1.f)); - wbEntries.push_back(new WBEntry("Flash 6500K" , WBT_FLASH, M("TP_WBALANCE_FLASH65"), 6500, 1.f, 1.f)); + wbEntries.push_back(new WBEntry("Camera" , WBT_CAMERA, M("TP_WBALANCE_CAMERA"), 0, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Auto" , WBT_AUTO, M("TP_WBALANCE_AUTO"), 0, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Daylight" , WBT_DAYLIGHT, M("TP_WBALANCE_DAYLIGHT"), 5300, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Cloudy" , WBT_CLOUDY, M("TP_WBALANCE_CLOUDY"), 6200, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Shade" , WBT_SHADE, M("TP_WBALANCE_SHADE"), 7600, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Water 1" , WBT_WATER, M("TP_WBALANCE_WATER1"), 35000, 0.3f, 1.1f, 0.f)); + wbEntries.push_back(new WBEntry("Water 2" , WBT_WATER, M("TP_WBALANCE_WATER2"), 48000, 0.63f, 1.38f, 0.f)); + wbEntries.push_back(new WBEntry("Tungsten" , WBT_TUNGSTEN, M("TP_WBALANCE_TUNGSTEN"), 2856, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F1" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO1"), 6430, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F2" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO2"), 4230, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F3" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO3"), 3450, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F4" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO4"), 2940, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F5" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO5"), 6350, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F6" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO6"), 4150, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F7" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO7"), 6500, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F8" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO8"), 5020, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F9" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO9"), 4330, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F10" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO10"), 5300, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F11" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO11"), 4000, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Fluo F12" , WBT_FLUORESCENT, M("TP_WBALANCE_FLUO12"), 3000, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("HMI Lamp" , WBT_LAMP, M("TP_WBALANCE_HMI"), 4800, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("GTI Lamp" , WBT_LAMP, M("TP_WBALANCE_GTI"), 5000, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("JudgeIII Lamp" , WBT_LAMP, M("TP_WBALANCE_JUDGEIII"), 5100, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Solux Lamp 3500K" , WBT_LAMP, M("TP_WBALANCE_SOLUX35"), 3480, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Solux Lamp 4100K" , WBT_LAMP, M("TP_WBALANCE_SOLUX41"), 3930, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Solux Lamp 4700K" , WBT_LAMP, M("TP_WBALANCE_SOLUX47"), 4700, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("NG Solux Lamp 4700K" , WBT_LAMP, M("TP_WBALANCE_SOLUX47_NG"), 4480, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("LED LSI Lumelex 2040", WBT_LED, M("TP_WBALANCE_LED_LSI"), 2970, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("LED CRS SP12 WWMR16" , WBT_LED, M("TP_WBALANCE_LED_CRS"), 3050, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Flash 5500K" , WBT_FLASH, M("TP_WBALANCE_FLASH55"), 5500, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Flash 6000K" , WBT_FLASH, M("TP_WBALANCE_FLASH60"), 6000, 1.f, 1.f, 0.f)); + wbEntries.push_back(new WBEntry("Flash 6500K" , WBT_FLASH, M("TP_WBALANCE_FLASH65"), 6500, 1.f, 1.f, 0.f)); // Should remain the last one - wbEntries.push_back(new WBEntry("Custom" , WBT_CUSTOM, M("TP_WBALANCE_CUSTOM"), 0, 1.f, 1.f)); + wbEntries.push_back(new WBEntry("Custom" , WBT_CUSTOM, M("TP_WBALANCE_CUSTOM"), 0, 1.f, 1.f, 0.f)); } void WBParams::cleanup() @@ -1059,6 +1059,7 @@ void ProcParams::setDefaults () wb.temperature = 6504; wb.green = 1.0; wb.equal = 1.0; + wb.tempBias = 0.0; colorappearance.enabled = false; colorappearance.degree = 90; colorappearance.autodegree = true; @@ -1985,6 +1986,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_double ("White Balance", "Equal", wb.equal); } + if (!pedited || pedited->wb.tempBias) { + keyFile.set_double ("White Balance", "TemperatureBias", wb.tempBias); + } + /* // save colorShift if (!pedited || pedited->colorShift.a) keyFile.set_double ("Color Shift", "ChannelA", colorShift.a); @@ -4664,6 +4669,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) pedited->wb.equal = true; } } + + if (keyFile.has_key ("White Balance", "TemperatureBias")) { + wb.tempBias = keyFile.get_double ("White Balance", "TemperatureBias"); + + if (pedited) { + pedited->wb.tempBias = true; + } + } } // load colorShift diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 7c2d71aaa..7cdc0894f 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -546,8 +546,9 @@ public: int temperature; double green; double equal; + double tempBias; - WBEntry(const Glib::ustring &p, enum WBTypes t, const Glib::ustring &l, int temp, double green, double equal) : ppLabel(p), type(t), GUILabel(l), temperature(temp), green(green), equal(equal) {}; + WBEntry(const Glib::ustring &p, enum WBTypes t, const Glib::ustring &l, int temp, double green, double equal, double bias) : ppLabel(p), type(t), GUILabel(l), temperature(temp), green(green), equal(equal), tempBias(bias) {}; }; class WBParams @@ -559,6 +560,7 @@ public: int temperature; double green; double equal; + double tempBias; static void init(); static void cleanup(); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index 025265e0a..8e832a734 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -469,7 +469,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { ALLNORAW, // EvcbdlMethod RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal - OUTPUTPROFILE // EvOBPCompens + OUTPUTPROFILE, // EvOBPCompens + ALLNORAW // EvWBtempBias }; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 61c779fb7..ef3a1be70 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -396,7 +396,7 @@ public: * @return a pointer to the Crop object that handles the image data trough its own pipeline */ virtual DetailedCrop* createCrop (::EditDataProvider *editDataProvider, bool isDetailWindow) = 0; - virtual bool getAutoWB (double& temp, double& green, double equal) = 0; + virtual bool getAutoWB (double& temp, double& green, double equal, double tempBias) = 0; virtual void getCamWB (double& temp, double& green) = 0; virtual void getSpotWB (int x, int y, int rectSize, double& temp, double& green) = 0; virtual void getAutoCrop (double ratio, int &x, int &y, int &w, int &h) = 0; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 257a3eeb3..f34e906b9 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -162,6 +162,7 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, tpp->greenAWBMul = avg_g / double(n); tpp->blueAWBMul = avg_b / double(n); tpp->wbEqual = wbEq; + tpp->wbTempBias = 0.0; cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen); } @@ -735,6 +736,7 @@ Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocati tpp->greenAWBMul = ri->get_rgb_cam(1, 0) * reds + ri->get_rgb_cam(1, 1) * greens + ri->get_rgb_cam(1, 2) * blues; tpp->blueAWBMul = ri->get_rgb_cam(2, 0) * reds + ri->get_rgb_cam(2, 1) * greens + ri->get_rgb_cam(2, 2) * blues; tpp->wbEqual = wbEq; + tpp->wbTempBias = 0.0; ColorTemp cTemp; cTemp.mul2temp(tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen); @@ -776,7 +778,7 @@ Thumbnail::Thumbnail () : camProfile(nullptr), thumbImg(nullptr), camwbRed(1.0), camwbGreen(1.0), camwbBlue(1.0), redAWBMul(-1.0), greenAWBMul(-1.0), blueAWBMul(-1.0), - autoWBTemp(2700), autoWBGreen(1.0), wbEqual(-1.0), + autoWBTemp(2700), autoWBGreen(1.0), wbEqual(-1.0), wbTempBias(0.0), embProfileLength(0), embProfileData(nullptr), embProfile(nullptr), redMultiplier(1.0), greenMultiplier(1.0), blueMultiplier(1.0), defGain(1.0), @@ -839,11 +841,13 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei BENCHFUN // check if the WB's equalizer value has changed - if (wbEqual < (params.wb.equal - 5e-4) || wbEqual > (params.wb.equal + 5e-4)) { + if (wbEqual < (params.wb.equal - 5e-4) || wbEqual > (params.wb.equal + 5e-4) || wbTempBias < (params.wb.tempBias - 5e-4) || wbTempBias > (params.wb.tempBias + 5e-4)) { wbEqual = params.wb.equal; + wbTempBias = params.wb.tempBias; // recompute the autoWB ColorTemp cTemp; cTemp.mul2temp (redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen); + autoWBTemp += autoWBTemp * wbTempBias; } // compute WB multipliers @@ -1272,15 +1276,17 @@ void Thumbnail::getCamWB (double& temp, double& green) green = currWB.getGreen (); } -void Thumbnail::getAutoWB (double& temp, double& green, double equal) +void Thumbnail::getAutoWB (double& temp, double& green, double equal, double tempBias) { - if (equal != wbEqual) { + if (equal != wbEqual || tempBias != wbTempBias) { // compute the values depending on equal ColorTemp cTemp; wbEqual = equal; + wbTempBias = tempBias; // compute autoWBTemp and autoWBGreen cTemp.mul2temp(redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen); + autoWBTemp += autoWBTemp * tempBias; } temp = autoWBTemp; diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index 18e72fc19..56e68f815 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -47,7 +47,7 @@ class Thumbnail double camwbGreen; double camwbBlue; double redAWBMul, greenAWBMul, blueAWBMul; // multipliers for auto WB - double autoWBTemp, autoWBGreen, wbEqual; // autoWBTemp and autoWBGreen are updated each time autoWB is requested and if wbEqual has been modified + double autoWBTemp, autoWBGreen, wbEqual, wbTempBias; // autoWBTemp and autoWBGreen are updated each time autoWB is requested and if wbEqual has been modified LUTu aeHistogram; int aeHistCompression; int embProfileLength; @@ -83,7 +83,7 @@ public: static RawMetaDataLocation loadMetaDataFromRaw (const Glib::ustring& fname); void getCamWB (double& temp, double& green); - void getAutoWB (double& temp, double& green, double equal); + void getAutoWB (double& temp, double& green, double equal, double tempBias); void getAutoWBMultipliers (double& rm, double& gm, double& bm); void getSpotWB (const procparams::ProcParams& params, int x, int y, int rect, double& temp, double& green); void applyAutoExp (procparams::ProcParams& pparams); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 4fb193c2b..9f1ca3c5c 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -160,7 +160,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p } else if (params.wb.method == "Auto") { double rm, gm, bm; imgsrc->getAutoWBMultipliers(rm, gm, bm); - currWB.update(rm, gm, bm, params.wb.equal); + currWB.update(rm, gm, bm, params.wb.equal, params.wb.tempBias); } NoiseCurve noiseLCurve; diff --git a/rtgui/addsetids.h b/rtgui/addsetids.h index 580874e5a..898ef9528 100644 --- a/rtgui/addsetids.h +++ b/rtgui/addsetids.h @@ -115,6 +115,7 @@ enum { ADDSET_RETI_VART, ADDSET_RETI_GAM, ADDSET_RETI_SLO, + ADDSET_WB_TEMPBIAS, ADDSET_PARAM_NUM // THIS IS USED AS A DELIMITER!! }; diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index 2caa61187..63ece79b2 100644 --- a/rtgui/batchtoolpanelcoord.cc +++ b/rtgui/batchtoolpanelcoord.cc @@ -152,7 +152,7 @@ void BatchToolPanelCoordinator::initSession () toneCurve->setAdjusterBehavior (false, false, false, false, false, false, false, false); lcurve->setAdjusterBehavior (false, false, false); - whitebalance->setAdjusterBehavior (false, false, false); + whitebalance->setAdjusterBehavior (false, false, false, false); vibrance->setAdjusterBehavior (false, false); vignetting->setAdjusterBehavior (false, false, false, false); colorappearance->setAdjusterBehavior (false, false, false, false, false, false, false, false, false, false, false, false, false); @@ -191,7 +191,7 @@ void BatchToolPanelCoordinator::initSession () toneCurve->setAdjusterBehavior (options.baBehav[ADDSET_TC_EXPCOMP], options.baBehav[ADDSET_TC_HLCOMPAMOUNT], options.baBehav[ADDSET_TC_HLCOMPTHRESH], options.baBehav[ADDSET_TC_BRIGHTNESS], options.baBehav[ADDSET_TC_BLACKLEVEL], options.baBehav[ADDSET_TC_SHCOMP], options.baBehav[ADDSET_TC_CONTRAST], options.baBehav[ADDSET_TC_SATURATION]); lcurve->setAdjusterBehavior (options.baBehav[ADDSET_LC_BRIGHTNESS], options.baBehav[ADDSET_LC_CONTRAST], options.baBehav[ADDSET_LC_CHROMATICITY]); - whitebalance->setAdjusterBehavior (options.baBehav[ADDSET_WB_TEMPERATURE], options.baBehav[ADDSET_WB_GREEN], options.baBehav[ADDSET_WB_EQUAL]); + whitebalance->setAdjusterBehavior (options.baBehav[ADDSET_WB_TEMPERATURE], options.baBehav[ADDSET_WB_GREEN], options.baBehav[ADDSET_WB_EQUAL], options.baBehav[ADDSET_WB_TEMPBIAS]); vibrance->setAdjusterBehavior (options.baBehav[ADDSET_VIBRANCE_PASTELS], options.baBehav[ADDSET_VIBRANCE_SATURATED]); vignetting->setAdjusterBehavior (options.baBehav[ADDSET_VIGN_AMOUNT], options.baBehav[ADDSET_VIGN_RADIUS], options.baBehav[ADDSET_VIGN_STRENGTH], options.baBehav[ADDSET_VIGN_CENTER]); colorappearance->setAdjusterBehavior (options.baBehav[ADDSET_CAT_DEGREE], options.baBehav[ADDSET_CAT_ADAPTSCENE], options.baBehav[ADDSET_CAT_ADAPTVIEWING], options.baBehav[ADDSET_CAT_BADPIX], options.baBehav[ADDSET_CAT_LIGHT], options.baBehav[ADDSET_CAT_CHROMA], options.baBehav[ADDSET_CAT_CONTRAST], options.baBehav[ADDSET_CAT_RSTPRO], options.baBehav[ADDSET_CAT_BRIGHT], options.baBehav[ADDSET_CAT_CONTRAST_Q], options.baBehav[ADDSET_CAT_CHROMA_S], options.baBehav[ADDSET_CAT_CHROMA_M], options.baBehav[ADDSET_CAT_HUE]); @@ -325,6 +325,10 @@ void BatchToolPanelCoordinator::initSession () pparams.wb.equal = 0; } + if (options.baBehav[ADDSET_WB_TEMPBIAS]) { + pparams.wb.tempBias = 0; + } + if (options.baBehav[ADDSET_VIBRANCE_PASTELS]) { pparams.vibrance.pastels = 0; } @@ -856,11 +860,11 @@ void BatchToolPanelCoordinator::panelChanged (rtengine::ProcEvent event, const G } } -void BatchToolPanelCoordinator::getAutoWB (double& temp, double& green, double equal) +void BatchToolPanelCoordinator::getAutoWB (double& temp, double& green, double equal, double tempBias) { if (!selected.empty()) { - selected[0]->getAutoWB (temp, green, equal); + selected[0]->getAutoWB (temp, green, equal, tempBias); } } diff --git a/rtgui/batchtoolpanelcoord.h b/rtgui/batchtoolpanelcoord.h index c96a1a329..4efcea6fa 100644 --- a/rtgui/batchtoolpanelcoord.h +++ b/rtgui/batchtoolpanelcoord.h @@ -60,7 +60,7 @@ public: void profileChange (const rtengine::procparams::PartialProfile* nparams, rtengine::ProcEvent event, const Glib::ustring& descr, const ParamsEdited* paramsEdited = nullptr); // wbprovider interface - void getAutoWB (double& temp, double& green, double equal); + void getAutoWB (double& temp, double& green, double equal, double tempBias); void getCamWB (double& temp, double& green); // thumbnaillistener interface diff --git a/rtgui/options.cc b/rtgui/options.cc index 431b3ffb3..5c0c032eb 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -604,6 +604,7 @@ void Options::setDefaults () 0, // ADDSET_RETI_VART 0, // ADDSET_RETI_GAM 0, // ADDSET_RETI_SLO + 0, // ADDSET_WB_TEMPBIAS }; rtSettings.darkFramesPath = ""; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index ff3e4c0b1..7704327bb 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -215,6 +215,7 @@ void ParamsEdited::set (bool v) wb.green = v; wb.temperature = v; wb.equal = v; + wb.tempBias = v; //colorShift.a = v; //colorShift.b = v; //lumaDenoise.enabled = v; @@ -708,6 +709,7 @@ void ParamsEdited::initFrom (const std::vector wb.green = wb.green && p.wb.green == other.wb.green; wb.equal = wb.equal && p.wb.equal == other.wb.equal; wb.temperature = wb.temperature && p.wb.temperature == other.wb.temperature; + wb.tempBias = wb.tempBias && p.wb.tempBias == other.wb.tempBias; //colorShift.a = colorShift.a && p.colorShift.a == other.colorShift.a; //colorShift.b = colorShift.b && p.colorShift.b == other.colorShift.b; //lumaDenoise.enabled = lumaDenoise.enabled && p.lumaDenoise.enabled == other.lumaDenoise.enabled; @@ -1587,6 +1589,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.wb.equal = dontforceSet && options.baBehav[ADDSET_WB_EQUAL] ? toEdit.wb.equal + mods.wb.equal : mods.wb.equal; } + if (wb.tempBias) { + toEdit.wb.tempBias = dontforceSet && options.baBehav[ADDSET_WB_TEMPBIAS] ? toEdit.wb.tempBias + mods.wb.tempBias : mods.wb.tempBias; + } + if (wb.green) { toEdit.wb.green = dontforceSet && options.baBehav[ADDSET_WB_GREEN] ? toEdit.wb.green + mods.wb.green : mods.wb.green; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 38f26658a..fc549d91a 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -234,6 +234,7 @@ public: bool temperature; bool green; bool equal; + bool tempBias; }; /*class ColorShiftParamsEdited { diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 04f3b1fe1..0de355bd2 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -231,6 +231,7 @@ Gtk::Widget* Preferences::getBatchProcPanel () appendBehavList (mi, M("TP_WBALANCE_TEMPERATURE"), ADDSET_WB_TEMPERATURE, true); appendBehavList (mi, M("TP_WBALANCE_GREEN"), ADDSET_WB_GREEN, true); appendBehavList (mi, M("TP_WBALANCE_EQBLUERED"), ADDSET_WB_EQUAL, true); + appendBehavList (mi, M("TP_WBALANCE_TEMPBIAS"), ADDSET_WB_TEMPBIAS, true); mi = behModel->append (); mi->set_value (behavColumns.label, M("TP_COLORAPP_LABEL")); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 71aeab0ab..03ef49a5e 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -184,7 +184,7 @@ const ProcParams& Thumbnail::getProcParamsU () pparams.wb.temperature = ct; } else if (pparams.wb.method == "Auto") { double ct; - getAutoWB (ct, pparams.wb.green, pparams.wb.equal); + getAutoWB (ct, pparams.wb.green, pparams.wb.equal, pparams.wb.tempBias); pparams.wb.temperature = ct; } } @@ -669,7 +669,7 @@ const Glib::ustring& Thumbnail::getDateTimeString () return dateTimeString; } -void Thumbnail::getAutoWB (double& temp, double& green, double equal) +void Thumbnail::getAutoWB (double& temp, double& green, double equal, double tempBias) { if (cfs.redAWBMul != -1.0) { rtengine::ColorTemp ct(cfs.redAWBMul, cfs.greenAWBMul, cfs.blueAWBMul, equal); diff --git a/rtgui/thumbnail.h b/rtgui/thumbnail.h index a7b889d5f..8c7691ed2 100644 --- a/rtgui/thumbnail.h +++ b/rtgui/thumbnail.h @@ -125,7 +125,7 @@ public: temp = green = -1.0; } } - void getAutoWB (double& temp, double& green, double equal); + void getAutoWB (double& temp, double& green, double equal, double tempBias); void getSpotWB (int x, int y, int rect, double& temp, double& green) { if (tpp) { diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index c4d4d3ac2..68a458916 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -252,10 +252,10 @@ public: void writeOptions (); // wbprovider interface - void getAutoWB (double& temp, double& green, double equal) + void getAutoWB (double& temp, double& green, double equal, double tempBias) { if (ipc) { - ipc->getAutoWB (temp, green, equal); + ipc->getAutoWB (temp, green, equal, tempBias); } } void getCamWB (double& temp, double& green) diff --git a/rtgui/wbprovider.h b/rtgui/wbprovider.h index 2e46467f9..df1329c84 100644 --- a/rtgui/wbprovider.h +++ b/rtgui/wbprovider.h @@ -25,7 +25,7 @@ class WBProvider public: virtual ~WBProvider() {} - virtual void getAutoWB (double& temp, double& green, double equal) {} + virtual void getAutoWB (double& temp, double& green, double equal, double tempBias) {} virtual void getCamWB (double& temp, double& green) {} virtual void spotWBRequested (int size) {} }; diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index d0413412b..6b510c471 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -232,6 +232,7 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB custom_green = 1.0; custom_equal = 1.0; + custom_tempBias = 0.0; } //Add the model columns to the Combo (which is a kind of view), @@ -313,13 +314,17 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB temp = Gtk::manage (new Adjuster (M("TP_WBALANCE_TEMPERATURE"), MINTEMP, MAXTEMP, 5, CENTERTEMP, itempL, itempR, &wbSlider2Temp, &wbTemp2Slider)); green = Gtk::manage (new Adjuster (M("TP_WBALANCE_GREEN"), MINGREEN, MAXGREEN, 0.001, 1.0, igreenL, igreenR)); equal = Gtk::manage (new Adjuster (M("TP_WBALANCE_EQBLUERED"), MINEQUAL, MAXEQUAL, 0.001, 1.0, iblueredL, iblueredR)); + tempBias = Gtk::manage (new Adjuster(M("TP_WBALANCE_TEMPBIAS"), -0.5, 0.5, 0.01, 0.0)); cache_customTemp (0); cache_customGreen (0); cache_customEqual (0); + cache_customTempBias (0); equal->set_tooltip_markup (M("TP_WBALANCE_EQBLUERED_TOOLTIP")); + tempBias->set_tooltip_markup (M("TP_WBALANCE_TEMPBIAS_TOOLTIP")); temp->show (); green->show (); equal->show (); + tempBias->show (); /* Gtk::HBox* boxgreen = Gtk::manage (new Gtk::HBox ()); boxgreen->show (); @@ -332,10 +337,12 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB //pack_start (*boxgreen); pack_start (*green); pack_start (*equal); + pack_start (*tempBias); temp->setAdjusterListener (this); green->setAdjusterListener (this); equal->setAdjusterListener (this); + tempBias->setAdjusterListener (this); spotbutton->signal_pressed().connect( sigc::mem_fun(*this, &WhiteBalance::spotPressed) ); methconn = method->signal_changed().connect( sigc::mem_fun(*this, &WhiteBalance::optChanged) ); @@ -348,6 +355,7 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) int tVal = (int)temp->getValue(); double gVal = green->getValue(); double eVal = equal->getValue(); + double tempBiasVal = tempBias->getValue(); Gtk::TreeModel::Row row = getActiveMethod(); if (row == refTreeModel->children().end()) { @@ -358,11 +366,16 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) WBEntry* ppMethod = findWBEntry (row[methodColumns.colLabel], WBLT_GUI); WBEntry* wbCustom = findWBEntry ("Custom", WBLT_PP); - if (!ppMethod || (ppMethod->ppLabel != wbCustom->ppLabel && !(a == equal && ppMethod->type == WBT_AUTO)) ) { + if (!ppMethod || (ppMethod->ppLabel != wbCustom->ppLabel && !((a == equal || a == tempBias) && ppMethod->type == WBT_AUTO)) ) { methconn.block(true); opt = setActiveMethod(wbCustom->GUILabel); cache_customWB (tVal, gVal); - cache_customEqual(eVal); + if (a != equal) { + cache_customEqual(eVal); + } + if (a != tempBias) { + cache_customTempBias(tempBiasVal); + } methconn.block(false); } @@ -373,12 +386,16 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) cache_customGreen (gVal); } else if (a == equal) { cache_customEqual (eVal); + } else if (a == tempBias) { + cache_customTempBias (tempBiasVal); + } + if (a == equal || a == tempBias) { // Recomputing AutoWB if it's the current method if (wbp && ppMethod->type == WBT_AUTO) { double ctemp = -1.0; double cgreen = -1.0; - wbp->getAutoWB (ctemp, cgreen, eVal); + wbp->getAutoWB (ctemp, cgreen, eVal, tempBiasVal); if (ctemp != -1.0) { // Set the automatics temperature value only if in SET mode @@ -401,6 +418,8 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvWBGreen, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(3), a->getValue())); } else if (a == equal) { listener->panelChanged (EvWBequal, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(3), a->getValue())); + } else if (a == tempBias) { + listener->panelChanged (EvWBtempBias, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(3), a->getValue())); } } } @@ -442,11 +461,13 @@ void WhiteBalance::optChanged () temp->setValue (temp->getAddMode() ? 0.0 : (int)ctemp); green->setValue (green->getAddMode() ? 0.0 : cgreen); equal->setValue (equal->getAddMode() ? 0.0 : 1.0); + tempBias->setValue (tempBias->getAddMode() ? 0.0 : 0.0); if (batchMode) { temp->setEditedState (UnEdited); green->setEditedState (UnEdited); equal->setEditedState (UnEdited); + tempBias->setEditedState (UnEdited); } } @@ -460,9 +481,9 @@ void WhiteBalance::optChanged () // equal remain as is } - if (!batchMode || equal->getEditedState()) { + if (!batchMode || equal->getEditedState() || tempBias->getEditedState()) { double ctemp, cgreen; - wbp->getAutoWB (ctemp, cgreen, equal->getValue()); + wbp->getAutoWB (ctemp, cgreen, equal->getValue(), tempBias->getValue()); if (ctemp != -1.0) { temp->setValue (temp->getAddMode() ? 0.0 : (int)ctemp); @@ -478,16 +499,19 @@ void WhiteBalance::optChanged () temp->setValue (temp->getAddMode() ? 0.0 : custom_temp); green->setValue (green->getAddMode() ? 0.0 : custom_green); equal->setValue (equal->getAddMode() ? 0.0 : custom_equal); + tempBias->setValue (tempBias->getAddMode() ? 0.0 : custom_tempBias); } else { cache_customTemp (temp->getValue()); cache_customGreen (green->getValue()); cache_customEqual (equal->getValue()); + cache_customTempBias (tempBias->getValue()); } if (batchMode) { temp->setEditedState (Edited); green->setEditedState (Edited); equal->setEditedState (Edited); + tempBias->setEditedState (Edited); } break; @@ -506,11 +530,13 @@ void WhiteBalance::optChanged () temp->setValue ( temp->getAddMode() ? 0.0 : (double)(currMethod->temperature)); green->setValue (green->getAddMode() ? 0.0 : (double)(currMethod->green)); equal->setValue (equal->getAddMode() ? 0.0 : (double)(currMethod->equal)); + tempBias->setValue (tempBias->getAddMode() ? 0.0 : (double)(currMethod->tempBias)); if (batchMode) { temp->setEditedState (Edited); green->setEditedState (Edited); equal->setEditedState (Edited); + tempBias->setEditedState (Edited); } break; @@ -546,12 +572,14 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) methconn.block (true); equal->setValue (pp->wb.equal); + tempBias->setValue (pp->wb.tempBias); if (pedited) { // By default, temperature and green are said "UnEdited", but it may change later temp->setEditedState (UnEdited); green->setEditedState (UnEdited); equal->setEditedState (pedited->wb.equal ? Edited : UnEdited); + tempBias->setEditedState (pedited->wb.tempBias ? Edited : UnEdited); } if (pedited && !pedited->wb.method) { @@ -571,9 +599,11 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) temp->setValue (temp->getAddMode() ? 0.0 : pp->wb.temperature); green->setValue (green->getAddMode() ? 0.0 : pp->wb.green); equal->setValue (equal->getAddMode() ? 0.0 : pp->wb.equal); + tempBias->setValue (tempBias->getAddMode() ? 0.0 : pp->wb.tempBias); cache_customTemp (pp->wb.temperature); cache_customGreen (pp->wb.green); cache_customEqual (pp->wb.equal); + cache_customTempBias (pp->wb.tempBias); if (pedited) { // The user may have changed the temperature and green value @@ -595,10 +625,12 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) // Set the camera's green value, or 0.0 if in ADD mode green->setValue (green->getAddMode() ? 0.0 : cgreen); equal->setValue (equal->getAddMode() ? 0.0 : 1.); + tempBias->setValue (tempBias->getAddMode() ? 0.0 : 0.0); } else { temp->setValue (temp->getAddMode() ? 0.0 : pp->wb.temperature); green->setValue (green->getAddMode() ? 0.0 : pp->wb.green); equal->setValue (equal->getAddMode() ? 0.0 : pp->wb.equal); + tempBias->setValue (equal->getAddMode() ? 0.0 : pp->wb.tempBias); } } @@ -607,7 +639,8 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) case WBT_AUTO: // the equalizer's value is restored for the AutoWB equal->setValue (equal->getAddMode() ? 0.0 : pp->wb.equal); - + tempBias->setValue (tempBias->getAddMode() ? 0.0 : pp->wb.tempBias); + // set default values first if in ADD mode, otherwise keep the current ones if (temp->getAddMode() ) { temp->setValue (0.0); @@ -621,7 +654,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) if (wbp) { double ctemp = -1.0; double cgreen = -1.0; - wbp->getAutoWB (ctemp, cgreen, pp->wb.equal); + wbp->getAutoWB (ctemp, cgreen, pp->wb.equal, pp->wb.tempBias); if (ctemp != -1.0) { // Set the automatics temperature if in SET mode @@ -663,11 +696,13 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) // Set the stored temperature, or 0.0 if in ADD mode green->setValue(green->getAddMode() ? 0.0 : pp->wb.green); equal->setValue(equal->getAddMode() ? 0.0 : pp->wb.equal); + tempBias->setValue(equal->getAddMode() ? 0.0 : pp->wb.tempBias); // The user may have changed the green value even for predefined WB values if (pedited) { green->setEditedState (pedited->wb.green ? Edited : UnEdited); equal->setEditedState (pedited->wb.equal ? Edited : UnEdited); + tempBias->setEditedState (pedited->wb.tempBias ? Edited : UnEdited); } //cache_customGreen (pp->wb.green); @@ -688,6 +723,7 @@ void WhiteBalance::write (ProcParams* pp, ParamsEdited* pedited) pedited->wb.temperature = temp->getEditedState (); pedited->wb.green = green->getEditedState (); pedited->wb.equal = equal->getEditedState (); + pedited->wb.tempBias = tempBias->getEditedState (); pedited->wb.method = row[methodColumns.colLabel] != M("GENERAL_UNCHANGED"); } @@ -700,11 +736,14 @@ void WhiteBalance::write (ProcParams* pp, ParamsEdited* pedited) pp->wb.temperature = temp->getIntValue (); pp->wb.green = green->getValue (); pp->wb.equal = equal->getValue (); + pp->wb.tempBias = tempBias->getValue (); } void WhiteBalance::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { + equal->setDefault (defParams->wb.equal); + tempBias->setDefault (defParams->wb.tempBias); if (wbp && defParams->wb.method == "Camera") { double ctemp; @@ -721,7 +760,7 @@ void WhiteBalance::setDefaults (const ProcParams* defParams, const ParamsEdited* // but wbp is not ready to provide! double ctemp; double cgreen; - wbp->getAutoWB (ctemp, cgreen, defParams->wb.equal); + wbp->getAutoWB (ctemp, cgreen, defParams->wb.equal, defParams->wb.tempBias); if (ctemp != -1.0) { temp->setDefault (temp->getAddMode() ? 0 : (int)ctemp); @@ -740,10 +779,12 @@ void WhiteBalance::setDefaults (const ProcParams* defParams, const ParamsEdited* temp->setDefaultEditedState (pedited->wb.temperature ? Edited : UnEdited); green->setDefaultEditedState (pedited->wb.green ? Edited : UnEdited); equal->setDefaultEditedState (pedited->wb.equal ? Edited : UnEdited); + tempBias->setDefaultEditedState (pedited->wb.tempBias ? Edited : UnEdited); } else { temp->setDefaultEditedState (Irrelevant); green->setDefaultEditedState (Irrelevant); equal->setDefaultEditedState (Irrelevant); + tempBias->setDefaultEditedState (Irrelevant); } } @@ -754,6 +795,7 @@ void WhiteBalance::setBatchMode (bool batchMode) temp->showEditedCB (); green->showEditedCB (); equal->showEditedCB (); + tempBias->showEditedCB (); Gtk::TreeModel::Row row = *(refTreeModel->append()); row[methodColumns.colId] = WBParams::wbEntries.size(); row[methodColumns.colLabel] = M("GENERAL_UNCHANGED"); @@ -776,6 +818,7 @@ void WhiteBalance::setWB (int vtemp, double vgreen) opt = setActiveMethod(wbValues->GUILabel); cache_customWB (vtemp, vgreen); // sequence in which this call is made is important; must be before "method->set_active (2);" cache_customEqual(equal->getValue()); + cache_customTempBias(tempBias->getValue()); temp->setEditedState (Edited); green->setEditedState (Edited); methconn.block(false); @@ -785,12 +828,13 @@ void WhiteBalance::setWB (int vtemp, double vgreen) } } -void WhiteBalance::setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd) +void WhiteBalance::setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd, bool tempbiasadd) { temp->setAddMode(tempadd); green->setAddMode(greenadd); equal->setAddMode(equaladd); + tempBias->setAddMode(tempbiasadd); } void WhiteBalance::trimValues (rtengine::procparams::ProcParams* pp) @@ -799,6 +843,7 @@ void WhiteBalance::trimValues (rtengine::procparams::ProcParams* pp) temp->trimValue(pp->wb.temperature); green->trimValue(pp->wb.green); equal->trimValue(pp->wb.equal); + tempBias->trimValue(pp->wb.tempBias); } inline void WhiteBalance::cache_customTemp(int temp) @@ -815,6 +860,11 @@ void WhiteBalance::cache_customEqual(double equal) custom_equal = equal; } +void WhiteBalance::cache_customTempBias(double tempBias) +{ + custom_tempBias = tempBias; +} + void WhiteBalance::cache_customWB(int temp, double green) { cache_customTemp (temp); diff --git a/rtgui/whitebalance.h b/rtgui/whitebalance.h index c18b15853..a0d961612 100644 --- a/rtgui/whitebalance.h +++ b/rtgui/whitebalance.h @@ -65,6 +65,7 @@ protected: Adjuster* temp; Adjuster* green; Adjuster* equal; + Adjuster* tempBias; Gtk::Button* spotbutton; int opt; @@ -76,10 +77,12 @@ protected: int custom_temp; double custom_green; double custom_equal; + double custom_tempBias; void cache_customWB (int temp, double green); //cache custom WB setting to allow its recall void cache_customTemp (int temp); //cache Temperature only to allow its recall void cache_customGreen (double green); //cache Green only to allow its recall void cache_customEqual (double equal); //cache Equal only to allow its recall + void cache_customTempBias (double tempBias); //cache TempBias only to allow its recall int setActiveMethod (Glib::ustring label); int _setActiveMethod (Glib::ustring &label, Gtk::TreeModel::Children &children); @@ -115,7 +118,7 @@ public: } void setWB (int temp, double green); - void setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd); + void setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd, bool tempbiasadd); void trimValues (rtengine::procparams::ProcParams* pp); }; From 3329899b37f48450cfad48f53c36e3d226643336 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 12 Feb 2017 21:27:11 +0100 Subject: [PATCH 083/181] Added support for compressed fuji bayer files --- rtengine/dcraw.cc | 5 +- rtengine/dcraw.h | 43 +-- ...{xtranscompressed.cc => fujicompressed.cc} | 337 +++++++++++++++--- rtengine/rawimage.cc | 6 +- 4 files changed, 307 insertions(+), 84 deletions(-) rename rtengine/{xtranscompressed.cc => fujicompressed.cc} (63%) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 3b6d98133..0ccf66684 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -9018,9 +9018,6 @@ canon_a5: if (filters == 9) FORC(36) ((char *)xtrans)[c] = xtrans_abs[(c/6+top_margin) % 6][(c+left_margin) % 6]; - if(filters == 9 && raw_height * raw_width * 2 != raw_size) { - xtransCompressed = true; - } } else if (!strcmp(model,"KD-400Z")) { height = 1712; width = 2312; @@ -9870,7 +9867,7 @@ struct tiff_hdr { char desc[512], make[64], model[64], soft[32], date[20], artist[64]; }; -#include "xtranscompressed.cc" +#include "fujicompressed.cc" /* RT: Delete from here */ /*RT*/#undef SQR diff --git a/rtengine/dcraw.h b/rtengine/dcraw.h index 1ea938cc7..3c5719db8 100644 --- a/rtengine/dcraw.h +++ b/rtengine/dcraw.h @@ -90,7 +90,7 @@ protected: unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; unsigned tile_width, tile_length, gpsdata[32], load_flags; bool xtransCompressed = false; - struct xtrans_params + struct fuji_compressed_params { char *q_table; /* quantization table */ int q_point[5]; /* quantization points */ @@ -115,13 +115,14 @@ protected: _ltotal }; - struct xtrans_block { + struct fuji_compressed_block { int cur_bit; // current bit being read (from left to right) int cur_pos; // current position in a buffer INT64 cur_buf_offset; // offset of this buffer in a file unsigned max_read_size; // Amount of data to be read int cur_buf_size; // buffer size uchar *cur_buf; // currently read block + int fillbytes; // Counter to add extra byte for block size N*16 IMFILE *input; struct int_pair grad_even[3][41]; // tables of gradients struct int_pair grad_odd[3][41]; @@ -129,7 +130,7 @@ protected: ushort *linebuf[_ltotal]; }; - int fuji_total_lines, fuji_total_blocks, fuji_block_width, fuji_bits; + int fuji_total_lines, fuji_total_blocks, fuji_block_width, fuji_bits, fuji_raw_type; ushort raw_height, raw_width, height, width, top_margin, left_margin; ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; @@ -269,25 +270,27 @@ void adobe_copy_pixel (unsigned row, unsigned col, ushort **rp); void lossless_dng_load_raw(); void packed_dng_load_raw(); void deflate_dng_load_raw(); -void init_xtrans(struct xtrans_params* info); -void fuji_fill_buffer(struct xtrans_block *info); -void init_xtrans_block(struct xtrans_block* info, const struct xtrans_params *params, INT64 raw_offset, unsigned dsize); -void copy_line_to_xtrans(struct xtrans_block* info, int cur_line, int cur_block, int cur_block_width); -void fuji_zerobits(struct xtrans_block* info, int *count); -void fuji_read_code(struct xtrans_block* info, int *data, int bits_to_read); +void init_fuji_compr(struct fuji_compressed_params* info); +void fuji_fill_buffer(struct fuji_compressed_block *info); +void init_fuji_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, INT64 raw_offset, unsigned dsize); +void copy_line_to_xtrans(struct fuji_compressed_block* info, int cur_line, int cur_block, int cur_block_width); +void copy_line_to_bayer(struct fuji_compressed_block* info, int cur_line, int cur_block, int cur_block_width); +void fuji_zerobits(struct fuji_compressed_block* info, int *count); +void fuji_read_code(struct fuji_compressed_block* info, int *data, int bits_to_read); int bitDiff(int value1, int value2); -int fuji_decode_sample_even(struct xtrans_block* info, const struct xtrans_params * params, ushort* line_buf, int pos, struct int_pair* grads); -int fuji_decode_sample_odd(struct xtrans_block* info, const struct xtrans_params * params, ushort* line_buf, int pos, struct int_pair* grads); +int fuji_decode_sample_even(struct fuji_compressed_block* info, const struct fuji_compressed_params * params, ushort* line_buf, int pos, struct int_pair* grads); +int fuji_decode_sample_odd(struct fuji_compressed_block* info, const struct fuji_compressed_params * params, ushort* line_buf, int pos, struct int_pair* grads); void fuji_decode_interpolation_even(int line_width, ushort* line_buf, int pos); -void xtrans_extend_generic(ushort *linebuf[_ltotal], int line_width, int start, int end); -void xtrans_extend_red(ushort *linebuf[_ltotal], int line_width); -void xtrans_extend_green(ushort *linebuf[_ltotal], int line_width); -void xtrans_extend_blue(ushort *linebuf[_ltotal], int line_width); -void xtrans_decode_block(struct xtrans_block* info, const struct xtrans_params *params, int cur_line); -void xtrans_decode_strip(const struct xtrans_params* info_common, int cur_block, INT64 raw_offset, unsigned dsize); -void xtrans_compressed_load_raw(); -void xtrans_decode_loop(const struct xtrans_params* common_info, int count, INT64* raw_block_offsets, unsigned *block_sizes); -void parse_xtrans_header(); +void fuji_extend_generic(ushort *linebuf[_ltotal], int line_width, int start, int end); +void fuji_extend_red(ushort *linebuf[_ltotal], int line_width); +void fuji_extend_green(ushort *linebuf[_ltotal], int line_width); +void fuji_extend_blue(ushort *linebuf[_ltotal], int line_width); +void xtrans_decode_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, int cur_line); +void fuji_bayer_decode_block(struct fuji_compressed_block* info, const struct fuji_compressed_params *params, int cur_line); +void fuji_decode_strip(const struct fuji_compressed_params* info_common, int cur_block, INT64 raw_offset, unsigned dsize); +void fuji_compressed_load_raw(); +void fuji_decode_loop(const struct fuji_compressed_params* common_info, int count, INT64* raw_block_offsets, unsigned *block_sizes); +void parse_fuji_compressed_header(); void pentax_load_raw(); void nikon_load_raw(); int nikon_is_compressed(); diff --git a/rtengine/xtranscompressed.cc b/rtengine/fujicompressed.cc similarity index 63% rename from rtengine/xtranscompressed.cc rename to rtengine/fujicompressed.cc index 854ffec2f..2af218756 100644 --- a/rtengine/xtranscompressed.cc +++ b/rtengine/fujicompressed.cc @@ -1,5 +1,5 @@ /* -*- C++ -*- - * File: xtranscompressed.cpp + * File: fujicompressed.cpp * Copyright (C) 2016 Alexey Danilchenko * * Adopted to LibRaw by Alex Tutubalin, lexa@lexa.ru @@ -18,19 +18,23 @@ it under the terms of the one of three licenses as you choose: */ -void CLASS init_xtrans (struct xtrans_params* info) +void CLASS init_fuji_compr (struct fuji_compressed_params* info) { int cur_val, i; char *qt; - if (fuji_block_width % 3) { + if ((fuji_block_width % 3 && fuji_raw_type == 16) || (fuji_block_width & 1 && fuji_raw_type == 0)) { derror(); } info->q_table = (char *) malloc (32768); - merror (info->q_table, "init_xtrans()"); + merror (info->q_table, "init_fuji_compr()"); - info->line_width = (fuji_block_width * 2) / 3; + if (fuji_raw_type == 16) { + info->line_width = (fuji_block_width * 2) / 3; + } else { + info->line_width = fuji_block_width >> 1; + } info->q_point[0] = 0; info->q_point[1] = 0x12; @@ -79,9 +83,9 @@ void CLASS init_xtrans (struct xtrans_params* info) } } -#define XTRANS_BUF_SIZE 0x10000u +#define FUJI_BUF_SIZE 0x10000u -void CLASS fuji_fill_buffer (struct xtrans_block *info) +void CLASS fuji_fill_buffer (struct fuji_compressed_block *info) { if (info->cur_pos >= info->cur_buf_size) { info->cur_pos = 0; @@ -91,23 +95,31 @@ void CLASS fuji_fill_buffer (struct xtrans_block *info) #endif { fseek (info->input, info->cur_buf_offset, SEEK_SET); - info->cur_buf_size = fread (info->cur_buf, 1, std::min (info->max_read_size, XTRANS_BUF_SIZE), info->input); + info->cur_buf_size = fread (info->cur_buf, 1, std::min (info->max_read_size, FUJI_BUF_SIZE), info->input); + } + + if (info->cur_buf_size < 1) { // nothing read + if (info->fillbytes > 0) { + int ls = std::max (1, std::min (info->fillbytes, (int)FUJI_BUF_SIZE)); + memset (info->cur_buf, 0, ls); + info->fillbytes -= ls; + } else + ; } - if (info->cur_buf_size < 1) // nothing read - ;//throw LIBRAW_EXCEPTION_IO_EOF; info->max_read_size -= info->cur_buf_size; } } -void CLASS init_xtrans_block (struct xtrans_block* info, const struct xtrans_params *params, INT64 raw_offset, unsigned dsize) +void CLASS init_fuji_block (struct fuji_compressed_block* info, const struct fuji_compressed_params *params, INT64 raw_offset, unsigned dsize) { info->linealloc = (ushort*)calloc (sizeof (ushort), _ltotal * (params->line_width + 2)); - merror (info->linealloc, "init_xtrans_block()"); + merror (info->linealloc, "init_fuji_block()"); info->input = ifp; INT64 fsize = info->input->size; info->max_read_size = std::min (unsigned (fsize - raw_offset), dsize + 16); // Data size may be incorrect? + info->fillbytes = 1; info->linebuf[_R0] = info->linealloc; @@ -116,8 +128,8 @@ void CLASS init_xtrans_block (struct xtrans_block* info, const struct xtrans_par } // init buffer - info->cur_buf = (uchar*)malloc (XTRANS_BUF_SIZE); - merror (info->cur_buf, "init_xtrans_block()"); + info->cur_buf = (uchar*)malloc (FUJI_BUF_SIZE); + merror (info->cur_buf, "init_fuji_block()"); info->cur_bit = 0; info->cur_pos = 0; info->cur_buf_offset = raw_offset; @@ -134,7 +146,7 @@ void CLASS init_xtrans_block (struct xtrans_block* info, const struct xtrans_par fuji_fill_buffer (info); } -void CLASS copy_line_to_xtrans (struct xtrans_block* info, int cur_line, int cur_block, int cur_block_width) +void CLASS copy_line_to_xtrans (struct fuji_compressed_block* info, int cur_line, int cur_block, int cur_block_width) { ushort *lineBufB[3]; ushort *lineBufG[6]; @@ -185,9 +197,67 @@ void CLASS copy_line_to_xtrans (struct xtrans_block* info, int cur_line, int cur } } +void CLASS copy_line_to_bayer (struct fuji_compressed_block *info, int cur_line, int cur_block, int cur_block_width) +{ + ushort *lineBufB[3]; + ushort *lineBufG[6]; + ushort *lineBufR[3]; + unsigned pixel_count; + ushort *line_buf; + + int fuji_bayer[2][2]; + + for (int r = 0; r < 2; r++) + for (int c = 0; c < 2; c++) { + fuji_bayer[r][c] = FC (r, c); // We'll downgrade G2 to G below + } + + int offset = fuji_block_width * cur_block + 6 * raw_width * cur_line; + ushort *raw_block_data = raw_image + offset; + int row_count = 0; + + for (int i = 0; i < 3; i++) { + lineBufR[i] = info->linebuf[_R2 + i] + 1; + lineBufB[i] = info->linebuf[_B2 + i] + 1; + } + + for (int i = 0; i < 6; i++) { + lineBufG[i] = info->linebuf[_G2 + i] + 1; + } + + while (row_count < 6) { + pixel_count = 0; + + while (pixel_count < cur_block_width) { + switch (fuji_bayer[row_count & 1][pixel_count & 1]) { + case 0: // red + line_buf = lineBufR[row_count >> 1]; + break; + + case 1: // green + case 3: // second green + default: // to make static analyzer happy + line_buf = lineBufG[row_count]; + break; + + case 2: // blue + line_buf = lineBufB[row_count >> 1]; + break; + } + + raw_block_data[pixel_count] = line_buf[pixel_count >> 1]; + ++pixel_count; + } + + ++row_count; + raw_block_data += raw_width; + } +} + + #define fuji_quant_gradient(i,v1,v2) (9*i->q_table[i->q_point[4]+(v1)] + i->q_table[i->q_point[4]+(v2)]) -void CLASS fuji_zerobits (struct xtrans_block* info, int *count) +void CLASS fuji_zerobits (struct fuji_compressed_block* info, int *count) { uchar zero = 0; *count = 0; @@ -210,7 +280,7 @@ void CLASS fuji_zerobits (struct xtrans_block* info, int *count) } } -void CLASS fuji_read_code (struct xtrans_block* info, int *data, int bits_to_read) +void CLASS fuji_read_code (struct fuji_compressed_block* info, int *data, int bits_to_read) { uchar bits_left = bits_to_read; uchar bits_left_in_byte = 8 - (info->cur_bit & 7); @@ -253,7 +323,7 @@ int CLASS bitDiff (int value1, int value2) return decBits; } -int CLASS fuji_decode_sample_even (struct xtrans_block* info, const struct xtrans_params * params, ushort* line_buf, int pos, struct int_pair* grads) +int CLASS fuji_decode_sample_even (struct fuji_compressed_block* info, const struct fuji_compressed_params * params, ushort* line_buf, int pos, struct int_pair* grads) { int interp_val = 0; int errcnt = 0; @@ -333,7 +403,7 @@ int CLASS fuji_decode_sample_even (struct xtrans_block* info, const struct xtran return errcnt; } -int CLASS fuji_decode_sample_odd (struct xtrans_block* info, const struct xtrans_params * params, ushort* line_buf, int pos, struct int_pair* grads) +int CLASS fuji_decode_sample_odd (struct fuji_compressed_block* info, const struct fuji_compressed_params * params, ushort* line_buf, int pos, struct int_pair* grads) { int interp_val = 0; int errcnt = 0; @@ -400,7 +470,7 @@ int CLASS fuji_decode_sample_odd (struct xtrans_block* info, const struct xtrans } if ( interp_val >= 0 ) { - line_buf_cur[0] = std::min(interp_val, params->q_point[4]); + line_buf_cur[0] = std::min (interp_val, params->q_point[4]); } else { line_buf_cur[0] = 0; } @@ -428,7 +498,7 @@ void CLASS fuji_decode_interpolation_even (int line_width, ushort* line_buf, int } } -void CLASS xtrans_extend_generic (ushort *linebuf[_ltotal], int line_width, int start, int end) +void CLASS fuji_extend_generic (ushort *linebuf[_ltotal], int line_width, int start, int end) { for (int i = start; i <= end; i++) { linebuf[i][0] = linebuf[i - 1][1]; @@ -436,22 +506,22 @@ void CLASS xtrans_extend_generic (ushort *linebuf[_ltotal], int line_width, int } } -void CLASS xtrans_extend_red (ushort *linebuf[_ltotal], int line_width) +void CLASS fuji_extend_red (ushort *linebuf[_ltotal], int line_width) { - xtrans_extend_generic (linebuf, line_width, _R2, _R4); + fuji_extend_generic (linebuf, line_width, _R2, _R4); } -void CLASS xtrans_extend_green (ushort *linebuf[_ltotal], int line_width) +void CLASS fuji_extend_green (ushort *linebuf[_ltotal], int line_width) { - xtrans_extend_generic (linebuf, line_width, _G2, _G7); + fuji_extend_generic (linebuf, line_width, _G2, _G7); } -void CLASS xtrans_extend_blue (ushort *linebuf[_ltotal], int line_width) +void CLASS fuji_extend_blue (ushort *linebuf[_ltotal], int line_width) { - xtrans_extend_generic (linebuf, line_width, _B2, _B4); + fuji_extend_generic (linebuf, line_width, _B2, _B4); } -void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_params *params, int cur_line) +void CLASS xtrans_decode_block (struct fuji_compressed_block* info, const struct fuji_compressed_params *params, int cur_line) { int r_even_pos = 0, r_odd_pos = 1; int g_even_pos = 0, g_odd_pos = 1; @@ -477,8 +547,8 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_red (info->linebuf, line_width); - xtrans_extend_green (info->linebuf, line_width); + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); g_even_pos = 0, g_odd_pos = 1; @@ -498,8 +568,8 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_green (info->linebuf, line_width); - xtrans_extend_blue (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); r_even_pos = 0, r_odd_pos = 1; g_even_pos = 0, g_odd_pos = 1; @@ -525,8 +595,8 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_red (info->linebuf, line_width); - xtrans_extend_green (info->linebuf, line_width); + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); g_even_pos = 0, g_odd_pos = 1; b_even_pos = 0, b_odd_pos = 1; @@ -553,8 +623,8 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_green (info->linebuf, line_width); - xtrans_extend_blue (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); r_even_pos = 0, r_odd_pos = 1; g_even_pos = 0, g_odd_pos = 1; @@ -580,8 +650,8 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_red (info->linebuf, line_width); - xtrans_extend_green (info->linebuf, line_width); + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); g_even_pos = 0, g_odd_pos = 1; b_even_pos = 0, b_odd_pos = 1; @@ -608,21 +678,165 @@ void CLASS xtrans_decode_block (struct xtrans_block* info, const struct xtrans_p } } - xtrans_extend_green (info->linebuf, line_width); - xtrans_extend_blue (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); if (errcnt) { derror(); } } -void CLASS xtrans_decode_strip (const struct xtrans_params* info_common, int cur_block, INT64 raw_offset, unsigned dsize) +void CLASS fuji_bayer_decode_block (struct fuji_compressed_block *info, const struct fuji_compressed_params *params, + int cur_line) +{ + int r_even_pos = 0, r_odd_pos = 1; + int g_even_pos = 0, g_odd_pos = 1; + int b_even_pos = 0, b_odd_pos = 1; + + int errcnt = 0; + + const int line_width = params->line_width; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_R2] + 1, r_even_pos, info->grad_even[0]); + r_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G2] + 1, g_even_pos, info->grad_even[0]); + g_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_R2] + 1, r_odd_pos, info->grad_odd[0]); + r_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G2] + 1, g_odd_pos, info->grad_odd[0]); + g_odd_pos += 2; + } + } + + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + + g_even_pos = 0, g_odd_pos = 1; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G3] + 1, g_even_pos, info->grad_even[1]); + g_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_B2] + 1, b_even_pos, info->grad_even[1]); + b_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G3] + 1, g_odd_pos, info->grad_odd[1]); + g_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_B2] + 1, b_odd_pos, info->grad_odd[1]); + b_odd_pos += 2; + } + } + + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); + + r_even_pos = 0, r_odd_pos = 1; + g_even_pos = 0, g_odd_pos = 1; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_R3] + 1, r_even_pos, info->grad_even[2]); + r_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G4] + 1, g_even_pos, info->grad_even[2]); + g_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_R3] + 1, r_odd_pos, info->grad_odd[2]); + r_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G4] + 1, g_odd_pos, info->grad_odd[2]); + g_odd_pos += 2; + } + } + + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + + g_even_pos = 0, g_odd_pos = 1; + b_even_pos = 0, b_odd_pos = 1; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G5] + 1, g_even_pos, info->grad_even[0]); + g_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_B3] + 1, b_even_pos, info->grad_even[0]); + b_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G5] + 1, g_odd_pos, info->grad_odd[0]); + g_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_B3] + 1, b_odd_pos, info->grad_odd[0]); + b_odd_pos += 2; + } + } + + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); + + r_even_pos = 0, r_odd_pos = 1; + g_even_pos = 0, g_odd_pos = 1; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_R4] + 1, r_even_pos, info->grad_even[1]); + r_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G6] + 1, g_even_pos, info->grad_even[1]); + g_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_R4] + 1, r_odd_pos, info->grad_odd[1]); + r_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G6] + 1, g_odd_pos, info->grad_odd[1]); + g_odd_pos += 2; + } + } + + fuji_extend_red (info->linebuf, line_width); + fuji_extend_green (info->linebuf, line_width); + + g_even_pos = 0, g_odd_pos = 1; + b_even_pos = 0, b_odd_pos = 1; + + while (g_even_pos < line_width || g_odd_pos < line_width) { + if (g_even_pos < line_width) { + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_G7] + 1, g_even_pos, info->grad_even[2]); + g_even_pos += 2; + errcnt += fuji_decode_sample_even (info, params, info->linebuf[_B4] + 1, b_even_pos, info->grad_even[2]); + b_even_pos += 2; + } + + if (g_even_pos > 8) { + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_G7] + 1, g_odd_pos, info->grad_odd[2]); + g_odd_pos += 2; + errcnt += fuji_decode_sample_odd (info, params, info->linebuf[_B4] + 1, b_odd_pos, info->grad_odd[2]); + b_odd_pos += 2; + } + } + + fuji_extend_green (info->linebuf, line_width); + fuji_extend_blue (info->linebuf, line_width); + + if (errcnt) { + derror(); + } +} + +void CLASS fuji_decode_strip (const struct fuji_compressed_params* info_common, int cur_block, INT64 raw_offset, unsigned dsize) { int cur_block_width, cur_line; unsigned line_size; - struct xtrans_block info; + struct fuji_compressed_block info; - init_xtrans_block (&info, info_common, raw_offset, dsize); + init_fuji_block (&info, info_common, raw_offset, dsize); line_size = sizeof (ushort) * (info_common->line_width + 2); cur_block_width = fuji_block_width; @@ -639,14 +853,22 @@ void CLASS xtrans_decode_strip (const struct xtrans_params* info_common, int cur ztable[3] = {{_R2, 3}, {_G2, 6}, {_B2, 3}}; for (cur_line = 0; cur_line < fuji_total_lines; cur_line++) { - xtrans_decode_block (&info, info_common, cur_line); + if (fuji_raw_type == 16) { + xtrans_decode_block (&info, info_common, cur_line); + } else { + fuji_bayer_decode_block (&info, info_common, cur_line); + } // copy data from line buffers and advance for (int i = 0; i < 6; i++) { memcpy (info.linebuf[mtable[i].a], info.linebuf[mtable[i].b], line_size); } - copy_line_to_xtrans (&info, cur_line, cur_block, cur_block_width); + if (fuji_raw_type == 16) { + copy_line_to_xtrans (&info, cur_line, cur_block, cur_block_width); + } else { + copy_line_to_bayer (&info, cur_line, cur_block, cur_block_width); + } for (int i = 0; i < 3; i++) { memset (info.linebuf[ztable[i].a], 0, ztable[i].b * line_size); @@ -671,22 +893,22 @@ static unsigned sgetn (int n, uchar *s) return result; } -void CLASS xtrans_compressed_load_raw() +void CLASS fuji_compressed_load_raw() { - struct xtrans_params common_info; + struct fuji_compressed_params common_info; int cur_block; unsigned line_size, *block_sizes; INT64 raw_offset, *raw_block_offsets; - //struct xtrans_block info; + //struct fuji_compressed_block info; - init_xtrans (&common_info); + init_fuji_compr (&common_info); line_size = sizeof (ushort) * (common_info.line_width + 2); // read block sizes block_sizes = (unsigned*) malloc (sizeof (unsigned) * fuji_total_blocks); - merror (block_sizes, "xtrans_load_raw()"); + merror (block_sizes, "fuji_compressed_load_raw()"); raw_block_offsets = (INT64*) malloc (sizeof (INT64) * fuji_total_blocks); - merror (raw_block_offsets, "xtrans_load_raw()"); + merror (raw_block_offsets, "fuji_compressed_load_raw()"); raw_offset = sizeof (unsigned) * fuji_total_blocks; @@ -711,14 +933,14 @@ void CLASS xtrans_compressed_load_raw() raw_block_offsets[cur_block] = raw_block_offsets[cur_block - 1] + block_sizes[cur_block - 1] ; } - xtrans_decode_loop (&common_info, fuji_total_blocks, raw_block_offsets, block_sizes); + fuji_decode_loop (&common_info, fuji_total_blocks, raw_block_offsets, block_sizes); free (block_sizes); free (raw_block_offsets); free (common_info.q_table); } -void CLASS xtrans_decode_loop (const struct xtrans_params* common_info, int count, INT64* raw_block_offsets, unsigned *block_sizes) +void CLASS fuji_decode_loop (const struct fuji_compressed_params* common_info, int count, INT64* raw_block_offsets, unsigned *block_sizes) { #ifdef _OPENMP @@ -726,12 +948,12 @@ void CLASS xtrans_decode_loop (const struct xtrans_params* common_info, int coun #endif for (int cur_block = 0; cur_block < count ; cur_block++) { - xtrans_decode_strip (common_info, cur_block, raw_block_offsets[cur_block], block_sizes[cur_block]); + fuji_decode_strip (common_info, cur_block, raw_block_offsets[cur_block], block_sizes[cur_block]); } } -void CLASS parse_xtrans_header() +void CLASS parse_fuji_compressed_header() { uchar header[16]; @@ -783,7 +1005,7 @@ void CLASS parse_xtrans_header() || h_total_lines == 0 || h_total_lines != h_raw_height / 6 || (h_raw_bits != 12 && h_raw_bits != 14) - || h_raw_type != 16) { + || (h_raw_type != 16 && h_raw_type != 0)) { xtransCompressed = false; return; } @@ -793,8 +1015,9 @@ void CLASS parse_xtrans_header() fuji_total_blocks = h_blocks_in_row; fuji_block_width = h_block_size; fuji_bits = h_raw_bits; + fuji_raw_type = h_raw_type; raw_width = h_raw_width; raw_height = h_raw_height; data_offset += 16; - load_raw = &CLASS xtrans_compressed_load_raw; + load_raw = &CLASS fuji_compressed_load_raw; } diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index bfe7092f2..0fdf82378 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -435,9 +435,9 @@ int RawImage::loadRaw (bool loadData, bool closeFile, ProgressListener *plistene return 2; } - if(xtransCompressed) { - parse_xtrans_header(); - } + if(!strcmp(make,"Fujifilm") && raw_height * raw_width * 2 != raw_size) { + parse_fuji_compressed_header(); + } if (flip == 5) { this->rotate_deg = 270; From 3bf98847f25e3a7e3fcbf27487fcd5bfd006bfbb Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 13 Feb 2017 15:04:45 +0100 Subject: [PATCH 084/181] Added camconst entry for FUJIFILM GFX 50S --- rtengine/camconst.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rtengine/camconst.json b/rtengine/camconst.json index 5d0898ee9..f506a190e 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -1072,6 +1072,14 @@ Camera constants: "ranges": { "white": 4050 } // nominal 4080-4093 }, + { // Quality C, color data guessed to be same with X-A3 + "make_model": "FUJIFILM GFX 50S", + "dcraw_matrix": [ 12407,-5222,-1086,-2971,11116,2120,-294,1029,5284 ], // copy from X-A3 DNGv9.8 D65 + "raw_crop": [ 0, 0, 8280, 6208 ], // full raw 9216X6210 - usefull 8280x6208 + // "raw_crop": [ 6, 6, 8264, 6200 ], // fuji official JPEG 8256X6192 10,11,9,8 - experimental crop to match with official + "ranges": { "white": 16300 } + }, + { // Quality A "make_model": "FUJIFILM S1", "dcraw_matrix": [ 12297,-4882,-1202,-2106,10691,1623,-88,1312,4790 ] // DNG_v8.5 D65 From 67ad3ee24860df277e4dde4c684fa94e50689d45 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 13 Feb 2017 15:39:51 +0100 Subject: [PATCH 085/181] small speedup for fuji_compressed_load_raw() --- rtengine/fujicompressed.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/fujicompressed.cc b/rtengine/fujicompressed.cc index 2af218756..2e0b83485 100644 --- a/rtengine/fujicompressed.cc +++ b/rtengine/fujicompressed.cc @@ -944,7 +944,7 @@ void CLASS fuji_decode_loop (const struct fuji_compressed_params* common_info, i { #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic,1) // dynamic scheduling is faster if count > number of cores (e.g. count for GFX 50S is 12) #endif for (int cur_block = 0; cur_block < count ; cur_block++) { From 9c9ac0d5890e550eb84ce47319138d2f5350a6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Thu, 26 Jan 2017 18:16:41 +0100 Subject: [PATCH 086/181] Change `-std=gnu++11` to `-std=c++11` I propose changing the default `-std=` compiler flag from the non- standard `gnu++11` to `c++11`. Our code is fully C++11 compliant and that should be reflected in the C++ standard we choose as default. Furthermore there's an ambiguity as we make people use `-DCMAKE_CXX_FLAGS="-std=c++11"` when this is already handled (differently) in `CMakeLists.txt`. See the [pixls.us](https://discuss.pixls.us/t/rawtherapee-5-and-dcmake-cxx-flags/3145/3) discussion. You also see it in `AboutThisBuild.txt`: ``` Build flags: -std=c++11 -Wno-deprecated-declarations -Wno-unused-result -std=gnu++11 -march=native -Werror=unused-label -fopenmp -Werror=unknown-pragmas -O3 -DNDEBUG ``` This commit changes `-std=gnu++11` to `-std=c++11` and builds fine without `-DCMAKE_CXX_FLAGS="-std=c++11"`. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2fb88ec9..22c71c269 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,8 @@ endif () string (TOUPPER ${CMAKE_BUILD_TYPE} UPPER_CMAKE_BUILD_TYPE) # Set required C and C++ standards and check GCC version -SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") +SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9") message(FATAL_ERROR "Building RawTherapee requires using GCC version 4.9 or higher!") From 88336cb89782423711380c265dd3ad4f9a6d6988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 28 Jan 2017 19:03:59 +0100 Subject: [PATCH 087/181] Make RT build without `__USE_MISC` and `__USE_XOPEN` --- rtengine/FTblockDN.cc | 10 +-- rtengine/ciecam02.cc | 28 +++---- rtengine/color.h | 30 +++---- rtengine/coord.cc | 4 +- rtengine/dcraw.cc | 13 ++-- rtengine/imagedata.cc | 4 +- rtengine/imageio.cc | 2 +- rtengine/ipresize.cc | 2 +- rtengine/iptransform.cc | 65 ++++++++-------- rtengine/ipwavelet.cc | 2 +- rtengine/rawimage.cc | 10 ++- rtengine/rt_math.h | 16 ++++ rtengine/sleef.c | 138 ++++++++++++++++----------------- rtengine/sleefsseavx.c | 123 ++++++++++++++--------------- rtengine/utils.cc | 15 ++++ rtengine/utils.h | 2 + rtgui/cropwindow.cc | 2 +- rtgui/edit.cc | 8 +- rtgui/filebrowserentry.cc | 2 +- rtgui/gradient.cc | 4 +- rtgui/lockablecolorpicker.cc | 14 ++-- rtgui/mydiagonalcurve.cc | 4 +- rtgui/myflatcurve.cc | 4 +- rtgui/thumbbrowserentrybase.cc | 16 ++-- 24 files changed, 276 insertions(+), 242 deletions(-) diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc index 6bb0c4c3c..f72e56529 100644 --- a/rtengine/FTblockDN.cc +++ b/rtengine/FTblockDN.cc @@ -622,13 +622,13 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef for (int i = 0; i < TS; ++i) { float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); - float vmask = (i1 < border ? SQR(sin((M_PI * i1) / (2 * border))) : 1.0f); - float vmask2 = (i1 < 2 * border ? SQR(sin((M_PI * i1) / (2 * border))) : 1.0f); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI * i1) / (2 * border))) : 1.0f); for (int j = 0; j < TS; ++j) { float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); - tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((M_PI * j1) / (2 * border))) : 1.0f)) + epsilon; - tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((M_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI * j1) / (2 * border))) : 1.0f)) + epsilon; } } @@ -1512,7 +1512,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef } for (int i = 0; i < overlap; ++i) { - float mask = SQR(xsinf((M_PI * i) / (2 * overlap))); + float mask = SQR(xsinf((rtengine::RT_PI * i) / (2 * overlap))); if (tiletop > 0) { Vmask[i] = mask; diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc index f10ae4b31..08a9b3145 100644 --- a/rtengine/ciecam02.cc +++ b/rtengine/ciecam02.cc @@ -577,7 +577,7 @@ void Ciecam02::Aab_to_rgbfloat( vfloat &r, vfloat &g, vfloat &b, vfloat A, vfloa void Ciecam02::calculate_ab( double &aa, double &bb, double h, double e, double t, double nbb, double a ) { - double hrad = (h * M_PI) / 180.0; + double hrad = (h * rtengine::RT_PI) / 180.0; double sinh = sin( hrad ); double cosh = cos( hrad ); double x = (a / nbb) + 0.305; @@ -605,7 +605,7 @@ void Ciecam02::calculate_ab( double &aa, double &bb, double h, double e, double } void Ciecam02::calculate_abfloat( float &aa, float &bb, float h, float e, float t, float nbb, float a ) { - float2 sincosval = xsincosf((h * M_PI) / 180.0f); + float2 sincosval = xsincosf((h * rtengine::RT_PI) / 180.0f); float sinh = sincosval.x; float cosh = sincosval.y; float x = (a / nbb) + 0.305f; @@ -643,7 +643,7 @@ void Ciecam02::calculate_abfloat( float &aa, float &bb, float h, float e, float #ifdef __SSE2__ void Ciecam02::calculate_abfloat( vfloat &aa, vfloat &bb, vfloat h, vfloat e, vfloat t, vfloat nbb, vfloat a ) { - vfloat2 sincosval = xsincosf((h * F2V(M_PI)) / F2V(180.0f)); + vfloat2 sincosval = xsincosf((h * F2V(rtengine::RT_PI)) / F2V(180.0f)); vfloat sinh = sincosval.x; vfloat cosh = sincosval.y; vfloat x = (a / nbb) + F2V(0.305f); @@ -794,7 +794,7 @@ void Ciecam02::xyz2jchqms_ciecam02( double &J, double &C, double &h, double &Q, ca = rpa - ((12.0 * gpa) / 11.0) + (bpa / 11.0); cb = (1.0 / 9.0) * (rpa + gpa - (2.0 * bpa)); - myh = (180.0 / M_PI) * atan2( cb, ca ); + myh = (180.0 / rtengine::RT_PI) * atan2( cb, ca ); if ( myh < 0.0 ) { myh += 360.0; @@ -831,7 +831,7 @@ void Ciecam02::xyz2jchqms_ciecam02( double &J, double &C, double &h, double &Q, J = 100.0 * pow( a / aw, c * cz ); - e = ((12500.0 / 13.0) * nc * ncb) * (cos( ((myh * M_PI) / 180.0) + 2.0 ) + 3.8); + e = ((12500.0 / 13.0) * nc * ncb) * (cos( ((myh * rtengine::RT_PI) / 180.0) + 2.0 ) + 3.8); t = (e * sqrt( (ca * ca) + (cb * cb) )) / (rpa + gpa + ((21.0 / 20.0) * bpa)); C = pow( t, 0.9 ) * sqrt( J / 100.0 ) @@ -881,7 +881,7 @@ void Ciecam02::xyz2jchqms_ciecam02float( float &J, float &C, float &h, float &Q, myh = xatan2f( cb, ca ); if ( myh < 0.0f ) { - myh += (2.f * M_PI); + myh += (2.f * rtengine::RT_PI); } a = ((2.0f * rpa) + gpa + (0.05f * bpa) - 0.305f) * nbb; @@ -902,7 +902,7 @@ void Ciecam02::xyz2jchqms_ciecam02float( float &J, float &C, float &h, float &Q, M = C * pfl; Q = (Q == 0.f ? 0.0001f : Q); // avoid division by zero s = 100.0f * sqrtf( M / Q ); - h = (myh * 180.f) / (float)M_PI; + h = (myh * 180.f) / (float)rtengine::RT_PI; } #ifdef __SSE2__ void Ciecam02::xyz2jchqms_ciecam02float( vfloat &J, vfloat &C, vfloat &h, vfloat &Q, vfloat &M, vfloat &s, vfloat aw, vfloat fl, vfloat wh, @@ -938,7 +938,7 @@ void Ciecam02::xyz2jchqms_ciecam02float( vfloat &J, vfloat &C, vfloat &h, vfloat cb = F2V(0.11111111f) * (rpa + gpa - (bpa + bpa)); vfloat myh = xatan2f( cb, ca ); - vfloat temp = F2V(M_PI); + vfloat temp = F2V(rtengine::RT_PI); temp += temp; temp += myh; myh = vself(vmaskf_lt(myh, ZEROV), temp, myh); @@ -958,7 +958,7 @@ void Ciecam02::xyz2jchqms_ciecam02float( vfloat &J, vfloat &C, vfloat &h, vfloat M = C * pfl; Q = _mm_max_ps(Q, F2V(0.0001f)); // avoid division by zero s = F2V(100.0f) * _mm_sqrt_ps( M / Q ); - h = (myh * F2V(180.f)) / F2V(M_PI); + h = (myh * F2V(180.f)) / F2V(rtengine::RT_PI); } #endif @@ -1000,7 +1000,7 @@ void Ciecam02::xyz2jch_ciecam02float( float &J, float &C, float &h, float aw, fl myh = xatan2f( cb, ca ); if ( myh < 0.0f ) { - myh += (2.f * M_PI); + myh += (2.f * rtengine::RT_PI); } a = ((2.0f * rpa) + gpa + (0.05f * bpa) - 0.305f) * nbb; @@ -1017,7 +1017,7 @@ void Ciecam02::xyz2jch_ciecam02float( float &J, float &C, float &h, float aw, fl C = pow_F( t, 0.9f ) * J * pow1; J *= J * 100.0f; - h = (myh * 180.f) / (float)M_PI; + h = (myh * 180.f) / (float)rtengine::RT_PI; } @@ -1034,7 +1034,7 @@ void Ciecam02::jch2xyz_ciecam02( double &x, double &y, double &z, double J, doub double e, t; gamu = 1; xyz_to_cat02( rw, gw, bw, xw, yw, zw, gamu ); - e = ((12500.0 / 13.0) * nc * ncb) * (cos( ((h * M_PI) / 180.0) + 2.0 ) + 3.8); + e = ((12500.0 / 13.0) * nc * ncb) * (cos( ((h * rtengine::RT_PI) / 180.0) + 2.0 ) + 3.8); a = pow( J / 100.0, 1.0 / (c * cz) ) * aw; t = pow( C / (sqrt( J / 100) * pow( 1.64 - pow( 0.29, n ), 0.73 )), 10.0 / 9.0 ); @@ -1068,7 +1068,7 @@ void Ciecam02::jch2xyz_ciecam02float( float &x, float &y, float &z, float J, flo float e, t; gamu = 1; xyz_to_cat02float( rw, gw, bw, xw, yw, zw, gamu ); - e = ((961.53846f) * nc * ncb) * (xcosf( ((h * M_PI) / 180.0f) + 2.0f ) + 3.8f); + e = ((961.53846f) * nc * ncb) * (xcosf( ((h * rtengine::RT_PI) / 180.0f) + 2.0f ) + 3.8f); a = pow_F( J / 100.0f, 1.0f / (c * cz) ) * aw; t = pow_F( 10.f * C / (sqrtf( J ) * pow1), 1.1111111f ); @@ -1102,7 +1102,7 @@ void Ciecam02::jch2xyz_ciecam02float( vfloat &x, vfloat &y, vfloat &z, vfloat J, vfloat a, ca, cb; vfloat e, t; xyz_to_cat02float( rw, gw, bw, xw, yw, zw); - e = ((F2V(961.53846f)) * nc * ncb) * (xcosf( ((h * F2V(M_PI)) / F2V(180.0f)) + F2V(2.0f) ) + F2V(3.8f)); + e = ((F2V(961.53846f)) * nc * ncb) * (xcosf( ((h * F2V(rtengine::RT_PI)) / F2V(180.0f)) + F2V(2.0f) ) + F2V(3.8f)); a = pow_F( J / F2V(100.0f), reccmcz ) * aw; t = pow_F( F2V(10.f) * C / (_mm_sqrt_ps( J ) * pow1), F2V(1.1111111f) ); diff --git a/rtengine/color.h b/rtengine/color.h index 77db1f4f7..6fda40f12 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -780,18 +780,18 @@ public: static inline T interpolatePolarHue_PI (T h1, T h2, U balance) { if (h1==h2) return h1; - if ((h1 > h2) && (h1-h2 > T(M_PI))){ - h1 -= T(2*M_PI); + if ((h1 > h2) && (h1-h2 > T(rtengine::RT_PI))){ + h1 -= T(2*rtengine::RT_PI); T value = h1 + T(balance) * (h2-h1); - if (value < T(-M_PI)) - value += T(2*M_PI); + if (value < T(-rtengine::RT_PI)) + value += T(2*rtengine::RT_PI); return value; } - else if (h2-h1 > T(M_PI)) { - h2 -= T(2*M_PI); + else if (h2-h1 > T(rtengine::RT_PI)) { + h2 -= T(2*rtengine::RT_PI); T value = h1 + T(balance) * (h2-h1); if (value < T(0)) - value += T(2*M_PI); + value += T(2*rtengine::RT_PI); return value; } else @@ -821,21 +821,21 @@ public: f = 1.f - f; } - if (d < T(-M_PI) || d < T(0) || d > T(M_PI)) { //there was an inversion here !! d > T(M_PI) - h1 += T(2 * M_PI); + if (d < T(-rtengine::RT_PI) || d < T(0) || d > T(rtengine::RT_PI)) { //there was an inversion here !! d > T(rtengine::RT_PI) + h1 += T(2 * rtengine::RT_PI); h = h1 + f * (h2 - h1); - h = std::fmod(h, 2 * M_PI); + h = std::fmod(h, 2 * rtengine::RT_PI); } else { h = h1 + f * d; } // not strictly necessary..but in case of - if(h < T(-M_PI)) { - h = T(2 * M_PI) - h; + if(h < T(-rtengine::RT_PI)) { + h = T(2 * rtengine::RT_PI) - h; } - if(h > T(M_PI)) { - h = h - T(2 * M_PI); + if(h > T(rtengine::RT_PI)) { + h = h - T(2 * rtengine::RT_PI); } return h; @@ -864,7 +864,7 @@ public: f = 1.f - f; } - if (d < T(0) || d < T(0.5) || d > T(1.)) { //there was an inversion here !! d > T(M_PI) + if (d < T(0) || d < T(0.5) || d > T(1.)) { //there was an inversion here !! d > T(rtengine::RT_PI) h1 += T(1.); h = h1 + f * (h2 - h1); h = std::fmod(h, 1.); diff --git a/rtengine/coord.cc b/rtengine/coord.cc index 8a3d7d080..bf9ee816e 100644 --- a/rtengine/coord.cc +++ b/rtengine/coord.cc @@ -27,7 +27,7 @@ namespace rtengine Coord& Coord::operator= (const PolarCoord& other) { const auto radius = other.radius; - const auto angle = other.angle / 180.0 * M_PI; + const auto angle = other.angle / 180.0 * rtengine::RT_PI; x = radius * std::cos (angle); y = radius * std::sin (angle); @@ -41,7 +41,7 @@ PolarCoord& PolarCoord::operator= (const Coord& other) const double y = other.y; radius = rtengine::norm2 (x, y); - angle = std::atan2 (y, x) * 180.0 / M_PI; + angle = std::atan2 (y, x) * 180.0 / rtengine::RT_PI; return *this; } diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 3b6d98133..a76507cce 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -54,6 +54,7 @@ #include #include #include +#include #include #if defined(DJGPP) || defined(__MINGW32__) @@ -326,7 +327,7 @@ void CLASS read_shorts (ushort *pixel, int count) { if (fread (pixel, 2, count, ifp) < count) derror(); if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) - swab ((char*)pixel, (char*)pixel, count*2); + rtengine::swab ((char*)pixel, (char*)pixel, count*2); } void CLASS cubic_spline (const int *x_, const int *y_, const int len) @@ -1047,7 +1048,7 @@ void CLASS ljpeg_idct (struct jhead *jh) 47,55,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63 }; if (!cs[0]) - FORC(106) cs[c] = cos((c & 31)*M_PI/16)/2; + FORC(106) cs[c] = cos((c & 31)*rtengine::RT_PI/16)/2; memset (work, 0, sizeof work); work[0][0][0] = jh->vpred[0] += ljpeg_diff (jh->huff[0]) * jh->quant[0]; for (i=1; i < 64; i++ ) { @@ -1059,8 +1060,8 @@ void CLASS ljpeg_idct (struct jhead *jh) coef -= (1 << len) - 1; ((float *)work)[zigzag[i]] = coef * jh->quant[i]; } - FORC(8) work[0][0][c] *= M_SQRT1_2; - FORC(8) work[0][c][0] *= M_SQRT1_2; + FORC(8) work[0][0][c] *= rtengine::RT_SQRT1_2; + FORC(8) work[0][c][0] *= rtengine::RT_SQRT1_2; for (i=0; i < 8; i++) for (j=0; j < 8; j++) FORC(8) work[1][i][j] += work[0][i][c] * cs[(j*2+1)*c]; @@ -2614,7 +2615,7 @@ fill_input_buffer (j_decompress_ptr cinfo) size_t nbytes; nbytes = fread (jpeg_buffer, 1, 4096, ifp); - swab ((char*)jpeg_buffer, (char*)jpeg_buffer, nbytes); + rtengine::swab ((char*)jpeg_buffer, (char*)jpeg_buffer, nbytes); cinfo->src->next_input_byte = jpeg_buffer; cinfo->src->bytes_in_buffer = nbytes; return TRUE; @@ -3627,7 +3628,7 @@ short * CLASS foveon_make_curve (double max, double mul, double filt) double x; if (!filt) filt = 0.8; - size = 4*M_PI*max / filt; + size = 4*rtengine::RT_PI*max / filt; if (size == UINT_MAX) size--; curve = (short *) calloc (size+1, sizeof *curve); merror (curve, "foveon_make_curve()"); diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index 8dfd90ab3..fa3651571 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -16,9 +16,11 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ +#include +#include + #include "imagedata.h" #include "iptcpairs.h" -#include using namespace rtengine; diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index 05684aaa7..587c4572a 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -895,7 +895,7 @@ int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool s char swapped[line_length]; for ( int row = 0; row < height; ++row ) { - ::swab(((char*)buffer) + (row * line_length), swapped, line_length); + ::rtengine::swab(((char*)buffer) + (row * line_length), swapped, line_length); setScanline(row, (unsigned char*)&swapped[0], bps); } } else { diff --git a/rtengine/ipresize.cc b/rtengine/ipresize.cc index 8bdc0a6cb..07ef6501c 100644 --- a/rtengine/ipresize.cc +++ b/rtengine/ipresize.cc @@ -37,7 +37,7 @@ static inline float Lanc(float x, float a) } else if (x * x > a * a) { return 0.0f; } else { - x = static_cast(M_PI) * x; + x = static_cast(rtengine::RT_PI) * x; return a * xsinf(x) * xsinf(x / a) / (x * x); } } diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 1c0a09929..e953a5c0d 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -90,7 +90,6 @@ namespace rtengine #undef CLIPTOC #define CLIPTOC(a,b,c,d) ((a)>=(b)?((a)<=(c)?(a):(d=true,(c))):(d=true,(b))) -#define RT_PI 3.141592653589 bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef, const LCPMapper *pLCPMap) @@ -122,19 +121,19 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, double distAmount = params->distortion.amount; // auxiliary variables for rotation - double cost = cos(params->rotate.degree * RT_PI / 180.0); - double sint = sin(params->rotate.degree * RT_PI / 180.0); + double cost = cos(params->rotate.degree * rtengine::RT_PI / 180.0); + double sint = sin(params->rotate.degree * rtengine::RT_PI / 180.0); // auxiliary variables for vertical perspective correction double vpdeg = params->perspective.vertical / 100.0 * 45.0; - double vpalpha = (90.0 - vpdeg) / 180.0 * RT_PI; - double vpteta = fabs(vpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-oW * oW * tan(vpalpha) * tan(vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(16 * maxRadius * maxRadius + oW * oW * tan(vpalpha) * tan(vpalpha))) / (maxRadius * maxRadius * 8))); + double vpalpha = (90.0 - vpdeg) / 180.0 * rtengine::RT_PI; + double vpteta = fabs(vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-oW * oW * tan(vpalpha) * tan(vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(16 * maxRadius * maxRadius + oW * oW * tan(vpalpha) * tan(vpalpha))) / (maxRadius * maxRadius * 8))); double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta); // auxiliary variables for horizontal perspective correction double hpdeg = params->perspective.horizontal / 100.0 * 45.0; - double hpalpha = (90.0 - hpdeg) / 180.0 * RT_PI; - double hpteta = fabs(hpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-oH * oH * tan(hpalpha) * tan(hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(16 * maxRadius * maxRadius + oH * oH * tan(hpalpha) * tan(hpalpha))) / (maxRadius * maxRadius * 8))); + double hpalpha = (90.0 - hpdeg) / 180.0 * rtengine::RT_PI; + double hpteta = fabs(hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-oH * oH * tan(hpalpha) * tan(hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(16 * maxRadius * maxRadius + oH * oH * tan(hpalpha) * tan(hpalpha))) / (maxRadius * maxRadius * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); double ascale = ascaleDef > 0 ? ascaleDef : (params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0); @@ -351,14 +350,14 @@ static void calcGradientParams(int oW, int oH, const GradientParams& gradient, s double gradient_span = gradient.feather / 100.0; double gradient_center_x = gradient.centerX / 200.0 + 0.5; double gradient_center_y = gradient.centerY / 200.0 + 0.5; - double gradient_angle = gradient.degree / 180.0 * M_PI; + double gradient_angle = gradient.degree / 180.0 * rtengine::RT_PI; //fprintf(stderr, "%f %f %f %f %f %d %d\n", gradient_stops, gradient_span, gradient_center_x, gradient_center_y, gradient_angle, w, h); - // make 0.0 <= gradient_angle < 2 * M_PI - gradient_angle = fmod(gradient_angle, 2 * M_PI); + // make 0.0 <= gradient_angle < 2 * rtengine::RT_PI + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); if (gradient_angle < 0.0) { - gradient_angle += 2.0 * M_PI; + gradient_angle += 2.0 * rtengine::RT_PI; } gp.bright_top = false; @@ -372,24 +371,24 @@ static void calcGradientParams(int oW, int oH, const GradientParams& gradient, s // (actually we could transpose only for 90 degrees, but this way we avoid // division with extremely small numbers gp.transpose = true; - gradient_angle += 0.5 * M_PI; + gradient_angle += 0.5 * rtengine::RT_PI; cosgrad = cos(gradient_angle); double gxc = gradient_center_x; gradient_center_x = 1.0 - gradient_center_y; gradient_center_y = gxc; } - gradient_angle = fmod(gradient_angle, 2 * M_PI); + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); - if (gradient_angle > 0.5 * M_PI && gradient_angle < M_PI) { - gradient_angle += M_PI; + if (gradient_angle > 0.5 * rtengine::RT_PI && gradient_angle < rtengine::RT_PI) { + gradient_angle += rtengine::RT_PI; gp.bright_top = true; - } else if (gradient_angle >= M_PI && gradient_angle < 1.5 * M_PI) { - gradient_angle -= M_PI; + } else if (gradient_angle >= rtengine::RT_PI && gradient_angle < 1.5 * rtengine::RT_PI) { + gradient_angle -= rtengine::RT_PI; gp.bright_top = true; } - if (fabs(gradient_angle) < 0.001 || fabs(gradient_angle - 2 * M_PI) < 0.001) { + if (fabs(gradient_angle) < 0.001 || fabs(gradient_angle - 2 * rtengine::RT_PI) < 0.001) { gradient_angle = 0; gp.angle_is_zero = true; } @@ -444,7 +443,7 @@ static float calcGradientFactor(const struct grad_params& gp, int x, int y) val = 1.f - val; } - val *= M_PIf_2; + val *= rtengine::RT_PI_F_2; if (gp.scale < 1.f) { val = pow3(xsinf(val)); @@ -468,7 +467,7 @@ static float calcGradientFactor(const struct grad_params& gp, int x, int y) val = gp.bright_top ? 1.f - val : val; - val *= M_PIf_2; + val *= rtengine::RT_PI_F_2; if (gp.scale < 1.f) { val = pow3(xsinf(val)); @@ -623,7 +622,7 @@ static float calcPCVignetteFactor(const struct pcv_params& pcv, int x, int y) if (dist >= dist_oe) { val = pcv.scale; } else { - val = M_PIf_2 * (dist - dist_ie) / (dist_oe - dist_ie); + val = rtengine::RT_PI_F_2 * (dist - dist_ie) / (dist_oe - dist_ie); if (pcv.scale < 1.f) { val = pow4(xcosf(val)); @@ -746,20 +745,20 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr double distAmount = params->distortion.amount; // auxiliary variables for rotation - double cost = cos(params->rotate.degree * RT_PI / 180.0); - double sint = sin(params->rotate.degree * RT_PI / 180.0); + double cost = cos(params->rotate.degree * rtengine::RT_PI / 180.0); + double sint = sin(params->rotate.degree * rtengine::RT_PI / 180.0); // auxiliary variables for vertical perspective correction double vpdeg = params->perspective.vertical / 100.0 * 45.0; - double vpalpha = (90.0 - vpdeg) / 180.0 * RT_PI; - double vpteta = fabs(vpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oW * tan(vpalpha)) + (vpdeg > 0 ? 1.0 : -1.0) * + double vpalpha = (90.0 - vpdeg) / 180.0 * rtengine::RT_PI; + double vpteta = fabs(vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oW * tan(vpalpha)) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(SQR(4 * maxRadius) + SQR(oW * tan(vpalpha)))) / (SQR(maxRadius) * 8))); double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta); // auxiliary variables for horizontal perspective correction double hpdeg = params->perspective.horizontal / 100.0 * 45.0; - double hpalpha = (90.0 - hpdeg) / 180.0 * RT_PI; - double hpteta = fabs(hpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oH * tan(hpalpha)) + (hpdeg > 0 ? 1.0 : -1.0) * + double hpalpha = (90.0 - hpdeg) / 180.0 * rtengine::RT_PI; + double hpteta = fabs(hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oH * tan(hpalpha)) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(SQR(4 * maxRadius) + SQR(oH * tan(hpalpha)))) / (SQR(maxRadius) * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); @@ -934,19 +933,19 @@ void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transf double distAmount = params->distortion.amount; // auxiliary variables for rotation - double cost = cos(params->rotate.degree * RT_PI / 180.0); - double sint = sin(params->rotate.degree * RT_PI / 180.0); + double cost = cos(params->rotate.degree * rtengine::RT_PI / 180.0); + double sint = sin(params->rotate.degree * rtengine::RT_PI / 180.0); // auxiliary variables for vertical perspective correction double vpdeg = params->perspective.vertical / 100.0 * 45.0; - double vpalpha = (90 - vpdeg) / 180.0 * RT_PI; - double vpteta = fabs(vpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-oW * oW * tan(vpalpha) * tan(vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(16 * maxRadius * maxRadius + oW * oW * tan(vpalpha) * tan(vpalpha))) / (maxRadius * maxRadius * 8))); + double vpalpha = (90 - vpdeg) / 180.0 * rtengine::RT_PI; + double vpteta = fabs(vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-oW * oW * tan(vpalpha) * tan(vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(16 * maxRadius * maxRadius + oW * oW * tan(vpalpha) * tan(vpalpha))) / (maxRadius * maxRadius * 8))); double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta); // auxiliary variables for horizontal perspective correction double hpdeg = params->perspective.horizontal / 100.0 * 45.0; - double hpalpha = (90 - hpdeg) / 180.0 * RT_PI; - double hpteta = fabs(hpalpha - RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-oH * oH * tan(hpalpha) * tan(hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(16 * maxRadius * maxRadius + oH * oH * tan(hpalpha) * tan(hpalpha))) / (maxRadius * maxRadius * 8))); + double hpalpha = (90 - hpdeg) / 180.0 * rtengine::RT_PI; + double hpteta = fabs(hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-oH * oH * tan(hpalpha) * tan(hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(16 * maxRadius * maxRadius + oH * oH * tan(hpalpha) * tan(hpalpha))) / (maxRadius * maxRadius * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); double ascale = params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0; diff --git a/rtengine/ipwavelet.cc b/rtengine/ipwavelet.cc index ee8f10ad1..532c562c5 100644 --- a/rtengine/ipwavelet.cc +++ b/rtengine/ipwavelet.cc @@ -1058,7 +1058,7 @@ SSEFUNCTION void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int } for (int i = 0; i < overlap; i++) { - float mask = SQR(sin((M_PI * i) / (2 * overlap))); + float mask = SQR(sin((rtengine::RT_PI * i) / (2 * overlap))); if (tiletop > 0) { Vmask[i] = mask; diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index bfe7092f2..1c129d28a 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -4,16 +4,18 @@ * Created on: 20/nov/2010 */ -#include "rawimage.h" -#include "settings.h" -#include "camconst.h" -#include "utils.h" +#include #ifdef WIN32 #include #else #include #endif +#include "rawimage.h" +#include "settings.h" +#include "camconst.h" +#include "utils.h" + namespace rtengine { diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 67d883080..a7cbbf301 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -11,6 +12,21 @@ constexpr int MAXVAL = 0xffff; constexpr float MAXVALF = static_cast(MAXVAL); // float version of MAXVAL constexpr double MAXVALD = static_cast(MAXVAL); // double version of MAXVAL +constexpr double RT_PI = 3.14159265358979323846; // pi +constexpr double RT_PI_2 = 1.57079632679489661923; // pi/2 +constexpr double RT_1_PI = 0.31830988618379067154; // 1/pi +constexpr double RT_2_PI = 0.63661977236758134308; // 2/pi +constexpr double RT_SQRT1_2 = 0.70710678118654752440; // 1/sqrt(2) + +constexpr double RT_INFINITY = std::numeric_limits::infinity(); +constexpr double RT_NAN = std::numeric_limits::quiet_NaN(); + +constexpr float RT_PI_F = RT_PI; +constexpr float RT_PI_F_2 = RT_PI_2; + +constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); +constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN(); + template inline _Tp SQR (_Tp x) { diff --git a/rtengine/sleef.c b/rtengine/sleef.c index bc38a3cfb..17dfccc0f 100644 --- a/rtengine/sleef.c +++ b/rtengine/sleef.c @@ -12,7 +12,8 @@ #include #include -#include +//#include +#include "rt_math.h" //#include //#include @@ -56,9 +57,9 @@ __inline double mla(double x, double y, double z) { return x * y + z; } __inline double xrint(double x) { return x < 0 ? (int)(x - 0.5) : (int)(x + 0.5); } __inline int xisnan(double x) { return x != x; } -__inline int xisinf(double x) { return x == INFINITY || x == -INFINITY; } -__inline int xisminf(double x) { return x == -INFINITY; } -__inline int xispinf(double x) { return x == INFINITY; } +__inline int xisinf(double x) { return x == rtengine::RT_INFINITY || x == -rtengine::RT_INFINITY; } +__inline int xisminf(double x) { return x == -rtengine::RT_INFINITY; } +__inline int xispinf(double x) { return x == rtengine::RT_INFINITY; } __inline double ldexpk(double x, int q) { double u; @@ -87,7 +88,7 @@ __inline int ilogbp1(double d) { __inline int xilogb(double d) { int e = ilogbp1(xfabs(d)) - 1; e = d == 0 ? (-2147483647 - 1) : e; - e = d == INFINITY || d == -INFINITY ? 2147483647 : e; + e = d == rtengine::RT_INFINITY || d == -rtengine::RT_INFINITY ? 2147483647 : e; return e; } @@ -309,7 +310,7 @@ __inline double atan2k(double y, double x) { u = u * t + (-0.333333333333311110369124); t = u * t * s + s; - t = q * (M_PI/2) + t; + t = q * (rtengine::RT_PI_2) + t; return t; } @@ -318,11 +319,11 @@ __inline double xatan2(double y, double x) { double r = atan2k(xfabs(y), x); r = mulsign(r, x); - if (xisinf(x) || x == 0) r = M_PI/2 - (xisinf(x) ? (sign(x) * (M_PI /2)) : 0); - if (xisinf(y) ) r = M_PI/2 - (xisinf(x) ? (sign(x) * (M_PI*1/4)) : 0); - if ( y == 0) r = (sign(x) == -1 ? M_PI : 0); + if (xisinf(x) || x == 0) r = rtengine::RT_PI_2 - (xisinf(x) ? (sign(x) * (rtengine::RT_PI_2)) : 0); + if (xisinf(y) ) r = rtengine::RT_PI_2 - (xisinf(x) ? (sign(x) * (rtengine::RT_PI*1/4)) : 0); + if ( y == 0) r = (sign(x) == -1 ? rtengine::RT_PI : 0); - return xisnan(x) || xisnan(y) ? NAN : mulsign(r, y); + return xisnan(x) || xisnan(y) ? rtengine::RT_NAN : mulsign(r, y); } __inline double xasin(double d) { @@ -330,7 +331,7 @@ __inline double xasin(double d) { } __inline double xacos(double d) { - return mulsign(atan2k(sqrt((1+d)*(1-d)), xfabs(d)), d) + (d < 0 ? M_PI : 0); + return mulsign(atan2k(sqrt((1+d)*(1-d)), xfabs(d)), d) + (d < 0 ? rtengine::RT_PI : 0); } __inline double xatan(double s) { @@ -374,7 +375,7 @@ __inline double xsin(double d) { int q; double u, s; - q = (int)xrint(d * M_1_PI); + q = (int)xrint(d * rtengine::RT_1_PI); d = mla(q, -PI4_A*4, d); d = mla(q, -PI4_B*4, d); @@ -403,7 +404,7 @@ __inline double xcos(double d) { int q; double u, s; - q = 1 + 2*(int)xrint(d * M_1_PI - 0.5); + q = 1 + 2*(int)xrint(d * rtengine::RT_1_PI - 0.5); d = mla(q, -PI4_A*2, d); d = mla(q, -PI4_B*2, d); @@ -433,7 +434,7 @@ __inline double2 xsincos(double d) { double u, s, t; double2 r; - q = (int)xrint(d * (2 * M_1_PI)); + q = (int)xrint(d * (2 * rtengine::RT_1_PI)); s = d; @@ -469,7 +470,7 @@ __inline double2 xsincos(double d) { if ((q & 2) != 0) { r.x = -r.x; } if (((q+1) & 2) != 0) { r.y = -r.y; } - if (xisinf(d)) { r.x = r.y = NAN; } + if (xisinf(d)) { r.x = r.y = rtengine::RT_NAN; } return r; } @@ -478,7 +479,7 @@ __inline double xtan(double d) { int q; double u, s, x; - q = (int)xrint(d * (2 * M_1_PI)); + q = (int)xrint(d * (2 * rtengine::RT_1_PI)); x = mla(q, -PI4_A*2, d); x = mla(q, -PI4_B*2, x); @@ -508,7 +509,7 @@ __inline double xtan(double d) { if ((q & 1) != 0) u = 1.0 / u; - if (xisinf(d)) u = NAN; + if (xisinf(d)) u = rtengine::RT_NAN; return u; } @@ -534,9 +535,9 @@ __inline double xlog(double d) { x = x * t + 0.693147180559945286226764 * e; - if (xisinf(d)) x = INFINITY; - if (d < 0) x = NAN; - if (d == 0) x = -INFINITY; + if (xisinf(d)) x = rtengine::RT_INFINITY; + if (d < 0) x = rtengine::RT_NAN; + if (d == 0) x = -rtengine::RT_INFINITY; return x; } @@ -625,13 +626,13 @@ __inline double xpow(double x, double y) { double result = expk(mul_ds(logk(xfabs(x)), y)); - result = xisnan(result) ? INFINITY : result; - result *= (x >= 0 ? 1 : (!yisint ? NAN : (yisodd ? -1 : 1))); + result = xisnan(result) ? rtengine::RT_INFINITY : result; + result *= (x >= 0 ? 1 : (!yisint ? rtengine::RT_NAN : (yisodd ? -1 : 1))); double efx = mulsign(xfabs(x) - 1, y); - if (xisinf(y)) result = efx < 0 ? 0.0 : (efx == 0 ? 1.0 : INFINITY); - if (xisinf(x) || x == 0) result = (yisodd ? sign(x) : 1) * ((x == 0 ? -y : y) < 0 ? 0 : INFINITY); - if (xisnan(x) || xisnan(y)) result = NAN; + if (xisinf(y)) result = efx < 0 ? 0.0 : (efx == 0 ? 1.0 : rtengine::RT_INFINITY); + if (xisinf(x) || x == 0) result = (yisodd ? sign(x) : 1) * ((x == 0 ? -y : y) < 0 ? 0 : rtengine::RT_INFINITY); + if (xisnan(x) || xisnan(y)) result = rtengine::RT_NAN; if (y == 0 || x == 1) result = 1; return result; @@ -670,9 +671,9 @@ __inline double xsinh(double x) { d = add2_dd(d, div_dd(dd(-1, 0), d)); y = (d.x + d.y) * 0.5; - y = xisinf(x) || xisnan(y) ? INFINITY : y; + y = xisinf(x) || xisnan(y) ? rtengine::RT_INFINITY : y; y = mulsign(y, x); - y = xisnan(x) ? NAN : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -682,8 +683,8 @@ __inline double xcosh(double x) { d = add2_dd(d, div_dd(dd(1, 0), d)); double y = (d.x + d.y) * 0.5; - y = xisinf(x) || xisnan(y) ? INFINITY : y; - y = xisnan(x) ? NAN : y; + y = xisinf(x) || xisnan(y) ? rtengine::RT_INFINITY : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -697,7 +698,7 @@ __inline double xtanh(double x) { y = xisinf(x) || xisnan(y) ? 1.0 : y; y = mulsign(y, x); - y = xisnan(x) ? NAN : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -732,9 +733,9 @@ __inline double xasinh(double x) { double2 d = logk2(add2_ds(sqrt_d(add2_ds(mul_ss(y, y), 1)), y)); y = d.x + d.y; - y = xisinf(x) || xisnan(y) ? INFINITY : y; + y = xisinf(x) || xisnan(y) ? rtengine::RT_INFINITY : y; y = mulsign(y, x); - y = xisnan(x) ? NAN : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -743,10 +744,10 @@ __inline double xacosh(double x) { double2 d = logk2(add2_ds(sqrt_d(add2_ds(mul_ss(x, x), -1)), x)); double y = d.x + d.y; - y = xisinf(x) || xisnan(y) ? INFINITY : y; + y = xisinf(x) || xisnan(y) ? rtengine::RT_INFINITY : y; y = x == 1.0 ? 0.0 : y; - y = x < 1.0 ? NAN : y; - y = xisnan(x) ? NAN : y; + y = x < 1.0 ? rtengine::RT_NAN : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -754,11 +755,11 @@ __inline double xacosh(double x) { __inline double xatanh(double x) { double y = xfabs(x); double2 d = logk2(div_dd(add2_ss(1, y), add2_ss(1, -y))); - y = y > 1.0 ? NAN : (y == 1.0 ? INFINITY : (d.x + d.y) * 0.5); + y = y > 1.0 ? rtengine::RT_NAN : (y == 1.0 ? rtengine::RT_INFINITY : (d.x + d.y) * 0.5); - y = xisinf(x) || xisnan(y) ? NAN : y; + y = xisinf(x) || xisnan(y) ? rtengine::RT_NAN : y; y = mulsign(y, x); - y = xisnan(x) ? NAN : y; + y = xisnan(x) ? rtengine::RT_NAN : y; return y; } @@ -809,7 +810,7 @@ __inline double xsqrt(double d) { // max error : 0.5 ulp // You can change xfma to fma if fma is correctly implemented x = xfma(d * x, d * x, -d) * (x * -0.5) + d * x; - return d == INFINITY ? INFINITY : x * q; + return d == rtengine::RT_INFINITY ? rtengine::RT_INFINITY : x * q; } __inline double xcbrt(double d) { // max error : 2 ulps @@ -842,14 +843,14 @@ __inline double xcbrt(double d) { // max error : 2 ulps __inline double xexp2(double a) { double u = expk(mul_ds(dd(0.69314718055994528623, 2.3190468138462995584e-17), a)); - if (xispinf(a)) u = INFINITY; + if (xispinf(a)) u = rtengine::RT_INFINITY; if (xisminf(a)) u = 0; return u; } __inline double xexp10(double a) { double u = expk(mul_ds(dd(2.3025850929940459011, -2.1707562233822493508e-16), a)); - if (xispinf(a)) u = INFINITY; + if (xispinf(a)) u = rtengine::RT_INFINITY; if (xisminf(a)) u = 0; return u; } @@ -857,7 +858,7 @@ __inline double xexp10(double a) { __inline double xexpm1(double a) { double2 d = add2_ds(expk2(dd(a, 0)), -1.0); double x = d.x + d.y; - if (xispinf(a)) x = INFINITY; + if (xispinf(a)) x = rtengine::RT_INFINITY; if (xisminf(a)) x = -1; return x; } @@ -866,9 +867,9 @@ __inline double xlog10(double a) { double2 d = mul_dd(logk(a), dd(0.43429448190325176116, 6.6494347733425473126e-17)); double x = d.x + d.y; - if (xisinf(a)) x = INFINITY; - if (a < 0) x = NAN; - if (a == 0) x = -INFINITY; + if (xisinf(a)) x = rtengine::RT_INFINITY; + if (a < 0) x = rtengine::RT_NAN; + if (a == 0) x = -rtengine::RT_INFINITY; return x; } @@ -877,9 +878,9 @@ __inline double xlog1p(double a) { double2 d = logk2(add2_ss(a, 1)); double x = d.x + d.y; - if (xisinf(a)) x = INFINITY; - if (a < -1) x = NAN; - if (a == -1) x = -INFINITY; + if (xisinf(a)) x = rtengine::RT_INFINITY; + if (a < -1) x = rtengine::RT_NAN; + if (a == -1) x = -rtengine::RT_INFINITY; return x; } @@ -895,11 +896,6 @@ __inline double xlog1p(double a) { #define L2Lf 1.428606765330187045e-06f #define R_LN2f 1.442695040888963407359924681001892137426645954152985934135449406931f -#define M_PIf ((float)M_PI) -#define M_PIf_2 ((float)M_PI_2) - -#define INFINITYf ((float)INFINITY) -#define NANf ((float)NAN) __inline int32_t floatToRawIntBits(float d) { union { @@ -932,9 +928,9 @@ __inline float mlaf(float x, float y, float z) { return x * y + z; } __inline float xrintf(float x) { return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f); } __inline int xisnanf(float x) { return x != x; } -__inline int xisinff(float x) { return x == INFINITYf || x == -INFINITYf; } -__inline int xisminff(float x) { return x == -INFINITYf; } -__inline int xispinff(float x) { return x == INFINITYf; } +__inline int xisinff(float x) { return x == rtengine::RT_INFINITY_F || x == -rtengine::RT_INFINITY_F; } +__inline int xisminff(float x) { return x == -rtengine::RT_INFINITY_F; } +__inline int xispinff(float x) { return x == rtengine::RT_INFINITY_F; } __inline int ilogbp1f(float d) { int m = d < 5.421010862427522E-20f; @@ -988,7 +984,7 @@ __inline float xsinf(float d) { int q; float u, s; - q = (int)xrintf(d * (float)M_1_PI); + q = (int)xrintf(d * (float)rtengine::RT_1_PI); d = mlaf(q, -PI4_Af*4, d); d = mlaf(q, -PI4_Bf*4, d); @@ -1013,7 +1009,7 @@ __inline float xcosf(float d) { int q; float u, s; - q = 1 + 2*(int)xrintf(d * (float)M_1_PI - 0.5f); + q = 1 + 2*(int)xrintf(d * (float)rtengine::RT_1_PI - 0.5f); d = mlaf(q, -PI4_Af*2, d); d = mlaf(q, -PI4_Bf*2, d); @@ -1039,7 +1035,7 @@ __inline float2 xsincosf(float d) { float u, s, t; float2 r; - q = (int)rint(d * ((float)(2 * M_1_PI))); + q = (int)rint(d * ((float)(2 * rtengine::RT_1_PI))); s = d; @@ -1071,7 +1067,7 @@ __inline float2 xsincosf(float d) { if ((q & 2) != 0) { r.x = -r.x; } if (((q+1) & 2) != 0) { r.y = -r.y; } - if (xisinff(d)) { r.x = r.y = NANf; } + if (xisinff(d)) { r.x = r.y = rtengine::RT_NAN_F; } return r; } @@ -1080,7 +1076,7 @@ __inline float xtanf(float d) { int q; float u, s, x; - q = (int)xrintf(d * (float)(2 * M_1_PI)); + q = (int)xrintf(d * (float)(2 * rtengine::RT_1_PI)); x = d; @@ -1104,7 +1100,7 @@ __inline float xtanf(float d) { if ((q & 1) != 0) u = 1.0f / u; - if (xisinff(d)) u = NANf; + if (xisinff(d)) u = rtengine::RT_NAN_F; return u; } @@ -1156,18 +1152,18 @@ __inline float atan2kf(float y, float x) { t = u * t; t = mlaf(t,s,s); - return mlaf(q,(float)(M_PIf_2),t); + return mlaf(q,(float)(rtengine::RT_PI_F_2),t); } __inline float xatan2f(float y, float x) { float r = atan2kf(xfabsf(y), x); r = mulsignf(r, x); - if (xisinff(x) || x == 0) r = M_PIf/2 - (xisinff(x) ? (signf(x) * (float)(M_PIf*.5f)) : 0); - if (xisinff(y) ) r = M_PIf/2 - (xisinff(x) ? (signf(x) * (float)(M_PIf*.25f)) : 0); - if ( y == 0) r = (signf(x) == -1 ? M_PIf : 0); + if (xisinff(x) || x == 0) r = rtengine::RT_PI_F/2 - (xisinff(x) ? (signf(x) * (float)(rtengine::RT_PI_F*.5f)) : 0); + if (xisinff(y) ) r = rtengine::RT_PI_F/2 - (xisinff(x) ? (signf(x) * (float)(rtengine::RT_PI_F*.25f)) : 0); + if ( y == 0) r = (signf(x) == -1 ? rtengine::RT_PI_F : 0); - return xisnanf(x) || xisnanf(y) ? NANf : mulsignf(r, y); + return xisnanf(x) || xisnanf(y) ? rtengine::RT_NAN_F : mulsignf(r, y); } __inline float xasinf(float d) { @@ -1175,7 +1171,7 @@ __inline float xasinf(float d) { } __inline float xacosf(float d) { - return mulsignf(atan2kf(sqrtf((1.0f+d)*(1.0f-d)), fabsf(d)), d) + (d < 0 ? (float)M_PI : 0.0f); + return mulsignf(atan2kf(sqrtf((1.0f+d)*(1.0f-d)), fabsf(d)), d) + (d < 0 ? (float)rtengine::RT_PI : 0.0f); } __inline float xlogf(float d) { @@ -1196,9 +1192,9 @@ __inline float xlogf(float d) { x = x * t + 0.693147180559945286226764f * e; - if (xisinff(d)) x = INFINITYf; - if (d < 0) x = NANf; - if (d == 0) x = -INFINITYf; + if (xisinff(d)) x = rtengine::RT_INFINITY_F; + if (d < 0) x = rtengine::RT_NAN_F; + if (d == 0) x = -rtengine::RT_INFINITY_F; return x; } diff --git a/rtengine/sleefsseavx.c b/rtengine/sleefsseavx.c index a55fcf897..e4f587464 100644 --- a/rtengine/sleefsseavx.c +++ b/rtengine/sleefsseavx.c @@ -12,10 +12,11 @@ #define SLEEFSSEAVX #include -#include +//#include //#include //#include //#include "sleefsseavx.h" +#include "rt_math.h" #ifdef __SSE2__ #include "helpersse2.h" @@ -51,8 +52,8 @@ #define L2Lf 1.428606765330187045e-06f #define R_LN2f 1.442695040888963407359924681001892137426645954152985934135449406931f -#define INFINITYf ((float)INFINITY) -#define NANf ((float)NAN) +#define INFINITYf ((float)rtengine::RT_INFINITY) +#define NANf ((float)rtengine::RT_NAN) // @@ -259,7 +260,7 @@ static INLINE vdouble xldexp(vdouble x, vint q) { return vldexp(x, q); } static INLINE vint xilogb(vdouble d) { vdouble e = vcast_vd_vi(vsubi(vilogbp1(vabs(d)), vcast_vi_i(1))); e = vsel(vmask_eq(d, vcast_vd_d(0)), vcast_vd_d(-2147483648.0), e); - e = vsel(vmask_eq(vabs(d), vcast_vd_d(INFINITY)), vcast_vd_d(2147483647), e); + e = vsel(vmask_eq(vabs(d), vcast_vd_d(rtengine::RT_INFINITY)), vcast_vd_d(2147483647), e); return vrint_vi_vd(e); } @@ -267,7 +268,7 @@ static INLINE vdouble xsin(vdouble d) { vint q; vdouble u, s; - q = vrint_vi_vd(vmul(d, vcast_vd_d(M_1_PI))); + q = vrint_vi_vd(vmul(d, vcast_vd_d(rtengine::RT_1_PI))); u = vcast_vd_vi(q); d = vadd(d, vmul(u, vcast_vd_d(-PI4_A*4))); @@ -297,7 +298,7 @@ static INLINE vdouble xcos(vdouble d) { vint q; vdouble u, s; - q = vrint_vi_vd(vsub(vmul(d, vcast_vd_d(M_1_PI)), vcast_vd_d(0.5))); + q = vrint_vi_vd(vsub(vmul(d, vcast_vd_d(rtengine::RT_1_PI)), vcast_vd_d(0.5))); q = vaddi(vaddi(q, q), vcast_vi_i(1)); u = vcast_vd_vi(q); @@ -330,7 +331,7 @@ static INLINE vdouble2 xsincos(vdouble d) { vdouble u, s, t, rx, ry; vdouble2 r; - q = vrint_vi_vd(vmul(d, vcast_vd_d(M_2_PI))); + q = vrint_vi_vd(vmul(d, vcast_vd_d(rtengine::RT_2_PI))); s = d; @@ -374,8 +375,8 @@ static INLINE vdouble2 xsincos(vdouble d) { r.y = vreinterpret_vd_vm(vxorm(vandm(m, vreinterpret_vm_vd(vcast_vd_d(-0.0))), vreinterpret_vm_vd(r.y))); m = vmask_isinf(d); - r.x = vsel(m, vcast_vd_d(NAN), r.x); - r.y = vsel(m, vcast_vd_d(NAN), r.y); + r.x = vsel(m, vcast_vd_d(rtengine::RT_NAN), r.x); + r.y = vsel(m, vcast_vd_d(rtengine::RT_NAN), r.y); return r; } @@ -385,7 +386,7 @@ static INLINE vdouble xtan(vdouble d) { vdouble u, s, x; vmask m; - q = vrint_vi_vd(vmul(d, vcast_vd_d(M_2_PI))); + q = vrint_vi_vd(vmul(d, vcast_vd_d(rtengine::RT_2_PI))); u = vcast_vd_vi(q); x = vadd(d, vmul(u, vcast_vd_d(-PI4_A*2))); @@ -417,7 +418,7 @@ static INLINE vdouble xtan(vdouble d) { u = vsel(m, vrec(u), u); - u = vsel(vmask_isinf(d), vcast_vd_d(NAN), u); + u = vsel(vmask_isinf(d), vcast_vd_d(rtengine::RT_NAN), u); return u; } @@ -459,7 +460,7 @@ static INLINE vdouble atan2k(vdouble y, vdouble x) { u = vmla(u, t, vcast_vd_d(-0.333333333333311110369124)); t = vadd(s, vmul(s, vmul(t, u))); - t = vadd(t, vmul(vcast_vd_vi(q), vcast_vd_d(M_PI/2))); + t = vadd(t, vmul(vcast_vd_vi(q), vcast_vd_d(rtengine::RT_PI/2))); return t; } @@ -468,11 +469,11 @@ static INLINE vdouble xatan2(vdouble y, vdouble x) { vdouble r = atan2k(vabs(y), x); r = vmulsign(r, x); - r = vsel(vorm(vmask_isinf(x), vmask_eq(x, vcast_vd_d(0))), vsub(vcast_vd_d(M_PI/2), visinf2(x, vmulsign(vcast_vd_d(M_PI/2), x))), r); - r = vsel(vmask_isinf(y), vsub(vcast_vd_d(M_PI/2), visinf2(x, vmulsign(vcast_vd_d(M_PI/4), x))), r); - r = vsel(vmask_eq(y, vcast_vd_d(0)), vsel(vmask_eq(vsign(x), vcast_vd_d(-1.0)), vcast_vd_d(M_PI), vcast_vd_d(0)), r); + r = vsel(vorm(vmask_isinf(x), vmask_eq(x, vcast_vd_d(0))), vsub(vcast_vd_d(rtengine::RT_PI/2), visinf2(x, vmulsign(vcast_vd_d(rtengine::RT_PI/2), x))), r); + r = vsel(vmask_isinf(y), vsub(vcast_vd_d(rtengine::RT_PI/2), visinf2(x, vmulsign(vcast_vd_d(rtengine::RT_PI/4), x))), r); + r = vsel(vmask_eq(y, vcast_vd_d(0)), vsel(vmask_eq(vsign(x), vcast_vd_d(-1.0)), vcast_vd_d(rtengine::RT_PI), vcast_vd_d(0)), r); - return vsel(vorm(vmask_isnan(x), vmask_isnan(y)), vcast_vd_d(NAN), vmulsign(r, y)); + return vsel(vorm(vmask_isnan(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_NAN), vmulsign(r, y)); } static INLINE vdouble xasin(vdouble d) { @@ -481,7 +482,7 @@ static INLINE vdouble xasin(vdouble d) { y = vsub(vcast_vd_d(1), d); x = vmul(x, y); x = vsqrt(x); - x = vsel(vmask_isnan(x), vcast_vd_d(NAN), atan2k(vabs(d), x)); + x = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), atan2k(vabs(d), x)); return vmulsign(x, d); } @@ -492,7 +493,7 @@ static INLINE vdouble xacos(vdouble d) { x = vmul(x, y); x = vsqrt(x); x = vmulsign(atan2k(x, vabs(d)), d); - y = (vdouble)vandm(vmask_lt(d, vcast_vd_d(0)), (vmask)vcast_vd_d(M_PI)); + y = (vdouble)vandm(vmask_lt(d, vcast_vd_d(0)), (vmask)vcast_vd_d(rtengine::RT_PI)); x = vadd(x, y); return x; } @@ -531,7 +532,7 @@ static INLINE vdouble xatan(vdouble s) { t = vadd(s, vmul(s, vmul(t, u))); - t = vsel(vmaski_eq(vandi(q, vcast_vi_i(1)), vcast_vi_i(1)), vsub(vcast_vd_d(M_PI/2), t), t); + t = vsel(vmaski_eq(vandi(q, vcast_vi_i(1)), vcast_vi_i(1)), vsub(vcast_vd_d(rtengine::RT_PI/2), t), t); t = vsel(vmaski_eq(vandi(q, vcast_vi_i(2)), vcast_vi_i(2)), vneg(t), t); return t; @@ -559,9 +560,9 @@ static INLINE vdouble xlog(vdouble d) { x = vadd(vmul(x, t), vmul(vcast_vd_d(0.693147180559945286226764), vcast_vd_vi(e))); - x = vsel(vmask_ispinf(d), vcast_vd_d(INFINITY), x); - x = vsel(vmask_gt(vcast_vd_d(0), d), vcast_vd_d(NAN), x); - x = vsel(vmask_eq(d, vcast_vd_d(0)), vcast_vd_d(-INFINITY), x); + x = vsel(vmask_ispinf(d), vcast_vd_d(rtengine::RT_INFINITY), x); + x = vsel(vmask_gt(vcast_vd_d(0), d), vcast_vd_d(rtengine::RT_NAN), x); + x = vsel(vmask_eq(d, vcast_vd_d(0)), vcast_vd_d(-rtengine::RT_INFINITY), x); return x; } @@ -659,7 +660,7 @@ static INLINE vdouble xpow(vdouble x, vdouble y) { vdouble result = expk(mul_ds(logk(vabs(x)), y)); - //result = vsel(vmask_isnan(result), vcast_vd_d(INFINITY), result); + //result = vsel(vmask_isnan(result), vcast_vd_d(rtengine::RT_INFINITY), result); result = vmul(result, vsel(vmask_gt(x, vcast_vd_d(0)), @@ -668,7 +669,7 @@ static INLINE vdouble xpow(vdouble x, vdouble y) { vsel(yisodd, vcast_vd_d(-1), vcast_vd_d(1)), - vcast_vd_d(NAN)))); + vcast_vd_d(rtengine::RT_NAN)))); vdouble efx = vreinterpret_vd_vm(vxorm(vreinterpret_vm_vd(vsub(vabs(x), vcast_vd_d(1))), vsignbit(y))); @@ -677,17 +678,17 @@ static INLINE vdouble xpow(vdouble x, vdouble y) { vcast_vd_d(0), vsel(vmask_eq(efx, vcast_vd_d(0)), vcast_vd_d(1.0), - vcast_vd_d(INFINITY))), + vcast_vd_d(rtengine::RT_INFINITY))), result); result = vsel(vorm(vmask_isinf(x), vmask_eq(x, vcast_vd_d(0))), vmul(vsel(yisodd, vsign(x), vcast_vd_d(1)), vsel(vmask_lt(vsel(vmask_eq(x, vcast_vd_d(0)), vneg(y), y), vcast_vd_d(0)), vcast_vd_d(0), - vcast_vd_d(INFINITY))), + vcast_vd_d(rtengine::RT_INFINITY))), result); - result = vsel(vorm(vmask_isnan(x), vmask_isnan(y)), vcast_vd_d(NAN), result); + result = vsel(vorm(vmask_isnan(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_NAN), result); result = vsel(vorm(vmask_eq(y, vcast_vd_d(0)), vmask_eq(x, vcast_vd_d(1))), vcast_vd_d(1), result); @@ -733,9 +734,9 @@ static INLINE vdouble xsinh(vdouble x) { d = add2_dd(d, div_dd(dd(vcast_vd_d(-1), vcast_vd_d(0)), d)); y = vmul(vadd(d.x, d.y), vcast_vd_d(0.5)); - y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(INFINITY), y); + y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_INFINITY), y); y = vmulsign(y, x); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -745,8 +746,8 @@ static INLINE vdouble xcosh(vdouble x) { d = add2_dd(d, div_dd(dd(vcast_vd_d(1), vcast_vd_d(0)), d)); vdouble y = vmul(vadd(d.x, d.y), vcast_vd_d(0.5)); - y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(INFINITY), y); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_INFINITY), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -760,7 +761,7 @@ static INLINE vdouble xtanh(vdouble x) { y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(1.0), y); y = vmulsign(y, x); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -797,9 +798,9 @@ static INLINE vdouble xasinh(vdouble x) { vdouble2 d = logk2(add2_ds(sqrt_d(add2_ds(mul_ss(y, y), vcast_vd_d(1))), y)); y = vadd(d.x, d.y); - y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(INFINITY), y); + y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_INFINITY), y); y = vmulsign(y, x); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -808,10 +809,10 @@ static INLINE vdouble xacosh(vdouble x) { vdouble2 d = logk2(add2_ds(sqrt_d(add2_ds(mul_ss(x, x), vcast_vd_d(-1))), x)); vdouble y = vadd(d.x, d.y); - y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(INFINITY), y); + y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_INFINITY), y); y = vsel(vmask_eq(x, vcast_vd_d(1.0)), vcast_vd_d(0.0), y); - y = vsel(vmask_lt(x, vcast_vd_d(1.0)), vcast_vd_d(NAN), y); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vmask_lt(x, vcast_vd_d(1.0)), vcast_vd_d(rtengine::RT_NAN), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -819,11 +820,11 @@ static INLINE vdouble xacosh(vdouble x) { static INLINE vdouble xatanh(vdouble x) { vdouble y = vabs(x); vdouble2 d = logk2(div_dd(add2_ss(vcast_vd_d(1), y), add2_ss(vcast_vd_d(1), -y))); - y = vsel(vmask_gt(y, vcast_vd_d(1.0)), vcast_vd_d(NAN), vsel(vmask_eq(y, vcast_vd_d(1.0)), vcast_vd_d(INFINITY), vmul(vadd(d.x, d.y), vcast_vd_d(0.5)))); + y = vsel(vmask_gt(y, vcast_vd_d(1.0)), vcast_vd_d(rtengine::RT_NAN), vsel(vmask_eq(y, vcast_vd_d(1.0)), vcast_vd_d(rtengine::RT_INFINITY), vmul(vadd(d.x, d.y), vcast_vd_d(0.5)))); - y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(NAN), y); + y = vsel(vorm(vmask_isinf(x), vmask_isnan(y)), vcast_vd_d(rtengine::RT_NAN), y); y = vmulsign(y, x); - y = vsel(vmask_isnan(x), vcast_vd_d(NAN), y); + y = vsel(vmask_isnan(x), vcast_vd_d(rtengine::RT_NAN), y); return y; } @@ -864,14 +865,14 @@ static INLINE vdouble xcbrt(vdouble d) { static INLINE vdouble xexp2(vdouble a) { vdouble u = expk(mul_ds(dd(vcast_vd_d(0.69314718055994528623), vcast_vd_d(2.3190468138462995584e-17)), a)); - u = vsel(vmask_ispinf(a), vcast_vd_d(INFINITY), u); + u = vsel(vmask_ispinf(a), vcast_vd_d(rtengine::RT_INFINITY), u); u = vsel(vmask_isminf(a), vcast_vd_d(0), u); return u; } static INLINE vdouble xexp10(vdouble a) { vdouble u = expk(mul_ds(dd(vcast_vd_d(2.3025850929940459011), vcast_vd_d(-2.1707562233822493508e-16)), a)); - u = vsel(vmask_ispinf(a), vcast_vd_d(INFINITY), u); + u = vsel(vmask_ispinf(a), vcast_vd_d(rtengine::RT_INFINITY), u); u = vsel(vmask_isminf(a), vcast_vd_d(0), u); return u; } @@ -879,7 +880,7 @@ static INLINE vdouble xexp10(vdouble a) { static INLINE vdouble xexpm1(vdouble a) { vdouble2 d = add2_ds(expk2(dd(a, vcast_vd_d(0))), vcast_vd_d(-1.0)); vdouble x = d.x + d.y; - x = vsel(vmask_ispinf(a), vcast_vd_d(INFINITY), x); + x = vsel(vmask_ispinf(a), vcast_vd_d(rtengine::RT_INFINITY), x); x = vsel(vmask_isminf(a), vcast_vd_d(-1), x); return x; } @@ -888,9 +889,9 @@ static INLINE vdouble xlog10(vdouble a) { vdouble2 d = mul_dd(logk(a), dd(vcast_vd_d(0.43429448190325176116), vcast_vd_d(6.6494347733425473126e-17))); vdouble x = d.x + d.y; - x = vsel(vmask_ispinf(a), vcast_vd_d(INFINITY), x); - x = vsel(vmask_gt(vcast_vd_d(0), a), vcast_vd_d(NAN), x); - x = vsel(vmask_eq(a, vcast_vd_d(0)), vcast_vd_d(-INFINITY), x); + x = vsel(vmask_ispinf(a), vcast_vd_d(rtengine::RT_INFINITY), x); + x = vsel(vmask_gt(vcast_vd_d(0), a), vcast_vd_d(rtengine::RT_NAN), x); + x = vsel(vmask_eq(a, vcast_vd_d(0)), vcast_vd_d(-rtengine::RT_INFINITY), x); return x; } @@ -899,9 +900,9 @@ static INLINE vdouble xlog1p(vdouble a) { vdouble2 d = logk2(add2_ss(a, vcast_vd_d(1))); vdouble x = d.x + d.y; - x = vsel(vmask_ispinf(a), vcast_vd_d(INFINITY), x); - x = vsel(vmask_gt(vcast_vd_d(-1), a), vcast_vd_d(NAN), x); - x = vsel(vmask_eq(a, vcast_vd_d(-1)), vcast_vd_d(-INFINITY), x); + x = vsel(vmask_ispinf(a), vcast_vd_d(rtengine::RT_INFINITY), x); + x = vsel(vmask_gt(vcast_vd_d(-1), a), vcast_vd_d(rtengine::RT_NAN), x); + x = vsel(vmask_eq(a, vcast_vd_d(-1)), vcast_vd_d(-rtengine::RT_INFINITY), x); return x; } @@ -1005,7 +1006,7 @@ static INLINE vfloat xsinf(vfloat d) { vint2 q; vfloat u, s; - q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)M_1_PI))); + q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)rtengine::RT_1_PI))); u = vcast_vf_vi2(q); d = vmlaf(u, vcast_vf_f(-PI4_Af*4), d); @@ -1031,7 +1032,7 @@ static INLINE vfloat xcosf(vfloat d) { vint2 q; vfloat u, s; - q = vrint_vi2_vf(vsubf(vmulf(d, vcast_vf_f((float)M_1_PI)), vcast_vf_f(0.5f))); + q = vrint_vi2_vf(vsubf(vmulf(d, vcast_vf_f((float)rtengine::RT_1_PI)), vcast_vf_f(0.5f))); q = vaddi2(vaddi2(q, q), vcast_vi2_i(1)); u = vcast_vf_vi2(q); @@ -1060,7 +1061,7 @@ static INLINE vfloat2 xsincosf(vfloat d) { vfloat u, s, t, rx, ry; vfloat2 r; - q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)M_2_PI))); + q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)rtengine::RT_2_PI))); s = d; @@ -1100,8 +1101,8 @@ static INLINE vfloat2 xsincosf(vfloat d) { r.y = vreinterpret_vf_vm(vxorm(vandm(m, vreinterpret_vm_vf(vcast_vf_f(-0.0))), vreinterpret_vm_vf(r.y))); m = vmaskf_isinf(d); - r.x = vself(m, vcast_vf_f(NAN), r.x); - r.y = vself(m, vcast_vf_f(NAN), r.y); + r.x = vself(m, vcast_vf_f(rtengine::RT_NAN), r.x); + r.y = vself(m, vcast_vf_f(rtengine::RT_NAN), r.y); return r; } @@ -1111,7 +1112,7 @@ static INLINE vfloat xtanf(vfloat d) { vmask m; vfloat u, s, x; - q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)(2 * M_1_PI)))); + q = vrint_vi2_vf(vmulf(d, vcast_vf_f((float)(2 * rtengine::RT_1_PI)))); x = d; @@ -1165,7 +1166,7 @@ static INLINE vfloat xatanf(vfloat s) { t = vaddf(s, vmulf(s, vmulf(t, u))); - t = vself(vmaski2_eq(vandi2(q, vcast_vi2_i(1)), vcast_vi2_i(1)), vsubf(vcast_vf_f((float)(M_PI/2)), t), t); + t = vself(vmaski2_eq(vandi2(q, vcast_vi2_i(1)), vcast_vi2_i(1)), vsubf(vcast_vf_f((float)(rtengine::RT_PI/2)), t), t); t = vself(vmaski2_eq(vandi2(q, vcast_vi2_i(2)), vcast_vi2_i(2)), vnegf(t), t); return t; @@ -1197,7 +1198,7 @@ static INLINE vfloat atan2kf(vfloat y, vfloat x) { u = vmlaf(u, t, vcast_vf_f(-0.333331018686294555664062f)); t = vaddf(s, vmulf(s, vmulf(t, u))); - t = vaddf(t, vmulf(vcast_vf_vi2(q), vcast_vf_f((float)(M_PI/2)))); + t = vaddf(t, vmulf(vcast_vf_vi2(q), vcast_vf_f((float)(rtengine::RT_PI/2)))); return t; } @@ -1206,9 +1207,9 @@ static INLINE vfloat xatan2f(vfloat y, vfloat x) { vfloat r = atan2kf(vabsf(y), x); r = vmulsignf(r, x); - r = vself(vorm(vmaskf_isinf(x), vmaskf_eq(x, vcast_vf_f(0.0f))), vsubf(vcast_vf_f((float)(M_PI/2)), visinf2f(x, vmulsignf(vcast_vf_f((float)(M_PI/2)), x))), r); - r = vself(vmaskf_isinf(y), vsubf(vcast_vf_f((float)(M_PI/2)), visinf2f(x, vmulsignf(vcast_vf_f((float)(M_PI/4)), x))), r); - r = vself(vmaskf_eq(y, vcast_vf_f(0.0f)), vselfzero(vmaskf_eq(vsignf(x), vcast_vf_f(-1.0f)), vcast_vf_f((float)M_PI)), r); + r = vself(vorm(vmaskf_isinf(x), vmaskf_eq(x, vcast_vf_f(0.0f))), vsubf(vcast_vf_f((float)(rtengine::RT_PI/2)), visinf2f(x, vmulsignf(vcast_vf_f((float)(rtengine::RT_PI/2)), x))), r); + r = vself(vmaskf_isinf(y), vsubf(vcast_vf_f((float)(rtengine::RT_PI/2)), visinf2f(x, vmulsignf(vcast_vf_f((float)(rtengine::RT_PI/4)), x))), r); + r = vself(vmaskf_eq(y, vcast_vf_f(0.0f)), vselfzero(vmaskf_eq(vsignf(x), vcast_vf_f(-1.0f)), vcast_vf_f((float)rtengine::RT_PI)), r); return vself(vorm(vmaskf_isnan(x), vmaskf_isnan(y)), vcast_vf_f(NANf), vmulsignf(r, y)); } @@ -1230,7 +1231,7 @@ static INLINE vfloat xacosf(vfloat d) { x = vmulf(x, y); x = vsqrtf(x); x = vmulsignf(atan2kf(x, vabsf(d)), d); - y = (vfloat)vandm(vmaskf_lt(d, vcast_vf_f(0.0f)), (vmask)vcast_vf_f((float)M_PI)); + y = (vfloat)vandm(vmaskf_lt(d, vcast_vf_f(0.0f)), (vmask)vcast_vf_f((float)rtengine::RT_PI)); x = vaddf(x, y); return x; } diff --git a/rtengine/utils.cc b/rtengine/utils.cc index f5b3e496b..c9bce803d 100644 --- a/rtengine/utils.cc +++ b/rtengine/utils.cc @@ -245,4 +245,19 @@ bool hasPngExtension(const Glib::ustring& filename) return getFileExtension(filename) == "png"; } +void swab(const void* from, void* to, ssize_t n) +{ + // Adapted from glibc + const char* char_from = static_cast(from); + char* char_to = static_cast(to); + + n &= ~static_cast(1); + + while (n > 1) { + const char b0 = char_from[--n], b1 = char_from[--n]; + char_to[n] = b0; + char_to[n + 1] = b1; + } +} + } diff --git a/rtengine/utils.h b/rtengine/utils.h index b2c1d16a8..9c56ad0a0 100644 --- a/rtengine/utils.h +++ b/rtengine/utils.h @@ -52,4 +52,6 @@ bool hasTiffExtension(const Glib::ustring& filename); // Return true if file has .png extension (ignoring case) bool hasPngExtension(const Glib::ustring& filename); +void swab(const void* from, void* to, ssize_t n); + } diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 7ff139e81..46c956436 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -2320,7 +2320,7 @@ void CropWindow::drawStraightenGuide (Cairo::RefPtr cr) if (action_x != press_x || action_y != press_y) { double arg = (press_x - action_x) / sqrt(double((press_x - action_x) * (press_x - action_x) + (press_y - action_y) * (press_y - action_y))); double sol1, sol2; - double pi = M_PI; + double pi = rtengine::RT_PI; if (press_y > action_y) { sol1 = acos(arg) * 180 / pi; diff --git a/rtgui/edit.cc b/rtgui/edit.cc index 58d856bb5..764678634 100644 --- a/rtgui/edit.cc +++ b/rtgui/edit.cc @@ -191,7 +191,7 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); cr->stroke(); } } @@ -225,7 +225,7 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer } if (filled && state != INSENSITIVE) { - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); if (innerLineWidth > 0.) { cr->fill_preserve(); @@ -234,7 +234,7 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer cr->fill(); } } else if (innerLineWidth > 0.) { - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); if (state == INSENSITIVE) { std::valarray ds(1); @@ -274,7 +274,7 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, unsigned short } else { cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); } - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0, 2.*M_PI); + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0, 2.*rtengine::RT_PI); if (filled) { if (innerLineWidth > 0.) { diff --git a/rtgui/filebrowserentry.cc b/rtgui/filebrowserentry.cc index 6e32ea742..c016717a3 100644 --- a/rtgui/filebrowserentry.cc +++ b/rtgui/filebrowserentry.cc @@ -719,7 +719,7 @@ void FileBrowserEntry::drawStraightenGuide (Cairo::RefPtr cr) if (action_x != press_x || action_y != press_y) { double arg = (press_x - action_x) / sqrt(double((press_x - action_x) * (press_x - action_x) + (press_y - action_y) * (press_y - action_y))); double sol1, sol2; - double pi = M_PI; + double pi = rtengine::RT_PI; if (press_y > action_y) { sol1 = acos(arg) * 180 / pi; diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index 50625251e..cbaa39066 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -424,7 +424,7 @@ bool Gradient::button1Pressed(const int modifierKey) draggedPoint = currPos - centerPos; // compute the projected value of the dragged point - draggedFeatherOffset = draggedPoint.radius * sin((draggedPoint.angle - degree->getValue()) / 180.*M_PI); + draggedFeatherOffset = draggedPoint.radius * sin((draggedPoint.angle - degree->getValue()) / 180.*rtengine::RT_PI); if (lastObject == 3) { draggedFeatherOffset = -draggedFeatherOffset; @@ -526,7 +526,7 @@ bool Gradient::drag1(const int modifierKey) currPos.y = p; draggedPoint = currPos - centerPos; - double currDraggedFeatherOffset = draggedPoint.radius * sin((draggedPoint.angle - degree->getValue()) / 180.*M_PI); + double currDraggedFeatherOffset = draggedPoint.radius * sin((draggedPoint.angle - degree->getValue()) / 180.*rtengine::RT_PI); if (lastObject == 2) // Dragging the upper feather bar diff --git a/rtgui/lockablecolorpicker.cc b/rtgui/lockablecolorpicker.cc index 70a06ddb2..d10dfb438 100644 --- a/rtgui/lockablecolorpicker.cc +++ b/rtgui/lockablecolorpicker.cc @@ -127,13 +127,13 @@ void LockableColorPicker::updateBackBuffer () // black background of the whole color picker bbcr->set_line_width (0.); bbcr->set_source_rgba (0., 0., 0., opacity); - bbcr->arc_negative (center, center, center, 0., (double)M_PI); + bbcr->arc_negative (center, center, center, 0., (double)rtengine::RT_PI); bbcr->line_to (0, 2. * center + textHeight); - bbcr->arc_negative (2. * textPadding, 2. * center + textHeight, 2. * textPadding, (double)M_PI, (double)M_PI / 2.); + bbcr->arc_negative (2. * textPadding, 2. * center + textHeight, 2. * textPadding, (double)rtengine::RT_PI, (double)rtengine::RT_PI / 2.); bbcr->line_to (textWidth, 2. * center + textHeight + 2. * textPadding); - bbcr->arc_negative (textWidth, 2. * center + textHeight, 2. * textPadding, (double)M_PI / 2., 0.); + bbcr->arc_negative (textWidth, 2. * center + textHeight, 2. * textPadding, (double)rtengine::RT_PI / 2., 0.); bbcr->line_to (textWidth + 2. * textPadding, 2. * center + 2. * textPadding); - bbcr->arc_negative (textWidth, 2. * center + 2. * textPadding, 2. * textPadding, 0., (double)M_PI * 1.5); + bbcr->arc_negative (textWidth, 2. * center + 2. * textPadding, 2. * textPadding, 0., (double)rtengine::RT_PI * 1.5); bbcr->line_to (2. * center, 2. * center); bbcr->close_path(); bbcr->set_line_join (Cairo::LINE_JOIN_BEVEL); @@ -141,13 +141,13 @@ void LockableColorPicker::updateBackBuffer () bbcr->fill (); // light grey circle around the color mark - bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)M_PI); + bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)rtengine::RT_PI); bbcr->set_source_rgb (0.75, 0.75, 0.75); bbcr->set_line_width (circlePadding - 2.); bbcr->stroke (); // spot disc with picked color - bbcr->arc (center, center, center - circlePadding, 0., 2. * (double)M_PI); + bbcr->arc (center, center, center - circlePadding, 0., 2. * (double)rtengine::RT_PI); bbcr->set_source_rgb (rpreview, gpreview, bpreview); // <- set the picker color here bbcr->set_line_width (0.); bbcr->fill(); @@ -226,7 +226,7 @@ void LockableColorPicker::updateBackBuffer () float center = (float)size / 2.f + circlePadding; // light grey circle around the color mark - bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)M_PI); + bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)rtengine::RT_PI); bbcr->set_source_rgba (0., 0., 0., opacity); bbcr->set_line_width(circlePadding); bbcr->stroke_preserve(); diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index aac021748..8bdb7fa0d 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -481,12 +481,12 @@ void MyDiagonalCurve::draw (int handle) double x = double(graphX + 1) + double((graphW - 2) * curve.x.at(i)); // project (curve.x.at(i), 0, 1, graphW); double y = double(graphY - 1) - double((graphH - 2) * curve.y.at(i)); // project (curve.y.at(i), 0, 1, graphH); - cr->arc (x, y, RADIUS + 0.5, 0, 2 * M_PI); + cr->arc (x, y, RADIUS + 0.5, 0, 2 * rtengine::RT_PI); cr->fill (); if (i == edited_point) { cr->set_line_width(2.); - cr->arc (x, y, RADIUS + 3.5, 0, 2 * M_PI); + cr->arc (x, y, RADIUS + 3.5, 0, 2 * rtengine::RT_PI); cr->stroke(); cr->set_line_width(1.); } diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index a5f08b8ed..532557a3b 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -456,13 +456,13 @@ void MyFlatCurve::draw () double x = double(graphX + 1) + innerW * curve.x.at(i); // project (curve.x.at(i), 0, 1, graphW); double y = double(graphY - 1) - innerH * curve.y.at(i); // project (curve.y.at(i), 0, 1, graphH); - cr->arc (x, y, (double)RADIUS, 0, 2 * M_PI); + cr->arc (x, y, (double)RADIUS, 0, 2 * rtengine::RT_PI); cr->fill (); if (i == edited_point) { cr->set_source_rgb (1.0, 0.0, 0.0); cr->set_line_width(2.); - cr->arc (x, y, RADIUS + 3.5, 0, 2 * M_PI); + cr->arc (x, y, RADIUS + 3.5, 0, 2 * rtengine::RT_PI); cr->stroke(); cr->set_line_width(1.); } diff --git a/rtgui/thumbbrowserentrybase.cc b/rtgui/thumbbrowserentrybase.cc index 8ba713c53..6fa8eac52 100644 --- a/rtgui/thumbbrowserentrybase.cc +++ b/rtgui/thumbbrowserentrybase.cc @@ -434,10 +434,10 @@ void ThumbBrowserEntryBase::drawFrame (Cairo::RefPtr cc, const G if (selected || framed) { cc->move_to (radius, 0); - cc->arc (exp_width - 1 - radius, radius, radius, -M_PI / 2, 0); - cc->arc (exp_width - 1 - radius, exp_height - 1 - radius, radius, 0, M_PI / 2); - cc->arc (radius, exp_height - 1 - radius, radius, M_PI / 2, M_PI); - cc->arc (radius, radius, radius, M_PI, -M_PI / 2); + cc->arc (exp_width - 1 - radius, radius, radius, -rtengine::RT_PI / 2, 0); + cc->arc (exp_width - 1 - radius, exp_height - 1 - radius, radius, 0, rtengine::RT_PI / 2); + cc->arc (radius, exp_height - 1 - radius, radius, rtengine::RT_PI / 2, rtengine::RT_PI); + cc->arc (radius, radius, radius, rtengine::RT_PI, -rtengine::RT_PI / 2); cc->close_path (); if (selected) { @@ -452,10 +452,10 @@ void ThumbBrowserEntryBase::drawFrame (Cairo::RefPtr cc, const G if (framed) { cc->move_to (+2 + 0.5 + radius, +2 + 0.5); - cc->arc (-2 + 0.5 + exp_width - 1 - radius, +2 + 0.5 + radius, radius, -M_PI / 2, 0); - cc->arc (-2 + 0.5 + exp_width - 1 - radius, -2 + 0.5 + exp_height - 1 - radius, radius, 0, M_PI / 2); - cc->arc (+2 + 0.5 + radius, -2 + exp_height - 1 - radius, radius, M_PI / 2, M_PI); - cc->arc (+2 + 0.5 + radius, +2 + radius, radius, M_PI, -M_PI / 2); + cc->arc (-2 + 0.5 + exp_width - 1 - radius, +2 + 0.5 + radius, radius, -rtengine::RT_PI / 2, 0); + cc->arc (-2 + 0.5 + exp_width - 1 - radius, -2 + 0.5 + exp_height - 1 - radius, radius, 0, rtengine::RT_PI / 2); + cc->arc (+2 + 0.5 + radius, -2 + exp_height - 1 - radius, radius, rtengine::RT_PI / 2, rtengine::RT_PI); + cc->arc (+2 + 0.5 + radius, +2 + radius, radius, rtengine::RT_PI, -rtengine::RT_PI / 2); cc->close_path (); cc->set_source_rgb (fg.get_red(), fg.get_green(), fg.get_blue()); cc->set_line_width (2.0); From e9b5f42a9ff9549e27c6d5d1105b5a47c7c32496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Wed, 25 Jan 2017 19:38:38 +0100 Subject: [PATCH 088/181] Sanitize `ImageDimensions` base class - Make `width` and `height` private - Drop `getW()` and `getH()` - Clean `PreviewProps` --- rtengine/FTblockDN.cc | 18 ++-- rtengine/dcp.cc | 8 +- rtengine/dcrop.cc | 2 +- rtengine/image16.cc | 6 +- rtengine/image8.cc | 6 +- rtengine/imagedimensions.cc | 79 ++++++++++++----- rtengine/imagedimensions.h | 60 ++++++------- rtengine/imagefloat.cc | 2 +- rtengine/imageio.cc | 18 ++-- rtengine/improccoordinator.cc | 4 +- rtengine/improcfun.cc | 26 +++--- rtengine/ipresize.cc | 32 +++---- rtengine/iptransform.cc | 40 ++++----- rtengine/rawimagesource.cc | 162 +++++++++++++++++----------------- rtengine/rtthumbnail.cc | 116 ++++++++++++------------ rtengine/stdimagesource.cc | 13 ++- rtgui/bqentryupdater.cc | 6 +- 17 files changed, 318 insertions(+), 280 deletions(-) diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc index 6bb0c4c3c..a62613364 100644 --- a/rtengine/FTblockDN.cc +++ b/rtengine/FTblockDN.cc @@ -495,8 +495,8 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef //printf("NL=%f \n",noisevarL); if (useNoiseLCurve || useNoiseCCurve) { - int hei = calclum->height; - int wid = calclum->width; + int hei = calclum->getHeight(); + int wid = calclum->getWidth(); TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); const float wpi[3][3] = { @@ -573,7 +573,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef calclum = nullptr; } - const short int imheight = src->height, imwidth = src->width; + const short int imheight = src->getHeight(), imwidth = src->getWidth(); if (dnparams.luma != 0 || dnparams.chroma != 0 || dnparams.methodmed == "Lab" || dnparams.methodmed == "Lonly") { // gamma transform for input data @@ -1717,8 +1717,8 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef #pragma omp parallel for #endif - for (int i = 0; i < dst->height; ++i) { - for (int j = 0; j < dst->width; ++j) { + for (int i = 0; i < dst->getHeight(); ++i) { + for (int j = 0; j < dst->getWidth(); ++j) { dst->r(i, j) = Color::gammatab_srgb[ dst->r(i, j) ]; dst->g(i, j) = Color::gammatab_srgb[ dst->g(i, j) ]; dst->b(i, j) = Color::gammatab_srgb[ dst->b(i, j) ]; @@ -1746,7 +1746,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagef //median 3x3 in complement on RGB if (dnparams.methodmed == "RGB" && dnparams.median) { //printf("RGB den\n"); - int wid = dst->width, hei = dst->height; + int wid = dst->getWidth(), hei = dst->getHeight(); float** tm; tm = new float*[hei]; @@ -3115,8 +3115,8 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat float** lumcalc; float** acalc; float** bcalc; - hei = provicalc->height; - wid = provicalc->width; + hei = provicalc->getHeight(); + wid = provicalc->getWidth(); TMatrix wprofi = iccStore->workingSpaceMatrix (params->icm.working); const float wpi[3][3] = { @@ -3165,7 +3165,7 @@ SSEFUNCTION void ImProcFunctions::RGB_denoise_info(Imagefloat * src, Imagefloat //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - const int imheight = src->height, imwidth = src->width; + const int imheight = src->getHeight(), imwidth = src->getWidth(); bool denoiseMethodRgb = (dnparams.dmethod == "RGB"); diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc index a597fa8db..58524d94c 100644 --- a/rtengine/dcp.cc +++ b/rtengine/dcp.cc @@ -1015,8 +1015,8 @@ void DCPProfile::apply( #pragma omp parallel for #endif - for (int y = 0; y < img->height; ++y) { - for (int x = 0; x < img->width; x++) { + for (int y = 0; y < img->getHeight(); ++y) { + for (int x = 0; x < img->getWidth(); x++) { const float& newr = mat[0][0] * img->r(y, x) + mat[0][1] * img->g(y, x) + mat[0][2] * img->b(y, x); const float& newg = mat[1][0] * img->r(y, x) + mat[1][1] * img->g(y, x) + mat[1][2] * img->b(y, x); const float& newb = mat[2][0] * img->r(y, x) + mat[2][1] * img->g(y, x) + mat[2][2] * img->b(y, x); @@ -1053,8 +1053,8 @@ void DCPProfile::apply( #pragma omp parallel for schedule(dynamic,16) #endif - for (int y = 0; y < img->height; ++y) { - for (int x = 0; x < img->width; x++) { + for (int y = 0; y < img->getHeight(); ++y) { + for (int x = 0; x < img->getWidth(); x++) { float newr = pro_photo[0][0] * img->r(y, x) + pro_photo[0][1] * img->g(y, x) + pro_photo[0][2] * img->b(y, x); float newg = pro_photo[1][0] * img->r(y, x) + pro_photo[1][1] * img->g(y, x) + pro_photo[1][2] * img->b(y, x); float newb = pro_photo[2][0] * img->r(y, x) + pro_photo[2][1] * img->g(y, x) + pro_photo[2][2] * img->b(y, x); diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index bf4d1b194..ffce66ed9 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -113,7 +113,7 @@ void Crop::setEditSubscriber(EditSubscriber* newSubscriber) PipetteBuffer::LabBuffer = nullptr; } - if (PipetteBuffer::singlePlaneBuffer.getW() != -1) { + if (PipetteBuffer::singlePlaneBuffer.getWidth() != -1) { PipetteBuffer::singlePlaneBuffer.flushData(); } } diff --git a/rtengine/image16.cc b/rtengine/image16.cc index df2c5a21f..e0f7470a6 100644 --- a/rtengine/image16.cc +++ b/rtengine/image16.cc @@ -153,8 +153,8 @@ void Image16::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Preview transform (pp, tran, sx1, sy1, sx2, sy2); - int imwidth = image->width; // Destination image - int imheight = image->height; // Destination image + int imwidth = image->getWidth(); // Destination image + int imheight = image->getHeight(); // Destination image if (((tran & TR_ROT) == TR_R90) || ((tran & TR_ROT) == TR_R270)) { int swap = imwidth; @@ -165,7 +165,7 @@ void Image16::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Preview int maxx = width; // Source image int maxy = height; // Source image int mtran = tran & TR_ROT; - int skip = pp.skip; + int skip = pp.getSkip(); //if ((sx1 + skip*imwidth)>maxx) imwidth -- ; // we have a boundary condition that can cause errors diff --git a/rtengine/image8.cc b/rtengine/image8.cc index 9eaf3afc5..56c2a63ee 100644 --- a/rtengine/image8.cc +++ b/rtengine/image8.cc @@ -113,8 +113,8 @@ void Image8::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewP transform (pp, tran, sx1, sy1, sx2, sy2); - int imwidth = image->width; // Destination image - int imheight = image->height; // Destination image + int imwidth = image->getWidth(); // Destination image + int imheight = image->getHeight(); // Destination image if (((tran & TR_ROT) == TR_R90) || ((tran & TR_ROT) == TR_R270)) { int swap = imwidth; @@ -125,7 +125,7 @@ void Image8::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, PreviewP int maxx = width; // Source image int maxy = height; // Source image int mtran = tran & TR_ROT; - int skip = pp.skip; + int skip = pp.getSkip(); //if ((sx1 + skip*imwidth)>maxx) imwidth -- ; // we have a boundary condition that can cause errors diff --git a/rtengine/imagedimensions.cc b/rtengine/imagedimensions.cc index f7d291483..7dec1358a 100644 --- a/rtengine/imagedimensions.cc +++ b/rtengine/imagedimensions.cc @@ -20,49 +20,88 @@ #include "imagedimensions.h" #include "rtengine.h" -void ImageDimensions::transform (PreviewProps pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2) +PreviewProps::PreviewProps(int _x, int _y, int _width, int _height, int _skip) : + x(_x), + y(_y), + width(_width), + height(_height), + skip(_skip) { +} - int sw = width, sh = height; +int PreviewProps::getX() const +{ + return x; +} + +int PreviewProps::getY() const +{ + return y; +} + +int PreviewProps::getWidth() const +{ + return width; +} + +int PreviewProps::getHeight() const +{ + return height; +} + +int PreviewProps::getSkip() const +{ + return skip; +} + +ImageDimensions::ImageDimensions() : + width(-1), + height(-1) +{ +} + +void ImageDimensions::transform(const PreviewProps& pp, int tran, int& sx1, int& sy1, int& sx2, int& sy2) const +{ + int sw = width; + int sh = height; if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { - sw = height; - sh = width; + std::swap(sw, sh); } - int ppx = pp.x, ppy = pp.y; + int ppx = pp.getX(); + int ppy = pp.getY(); if (tran & TR_HFLIP) { - ppx = sw - pp.x - pp.w; + ppx = sw - pp.getX() - pp.getWidth(); } if (tran & TR_VFLIP) { - ppy = sh - pp.y - pp.h; + ppy = sh - pp.getY() - pp.getHeight(); } sx1 = ppx; sy1 = ppy; - sx2 = ppx + pp.w; - sy2 = ppy + pp.h; + sx2 = ppx + pp.getWidth(); + sy2 = ppy + pp.getHeight(); if ((tran & TR_ROT) == TR_R180) { - sx1 = width - ppx - pp.w; - sy1 = height - ppy - pp.h; - sx2 = sx1 + pp.w; - sy2 = sy1 + pp.h; + sx1 = width - ppx - pp.getWidth(); + sy1 = height - ppy - pp.getHeight(); + sx2 = sx1 + pp.getWidth(); + sy2 = sy1 + pp.getHeight(); } else if ((tran & TR_ROT) == TR_R90) { sx1 = ppy; - sy1 = height - ppx - pp.w; - sx2 = sx1 + pp.h; - sy2 = sy1 + pp.w; + sy1 = height - ppx - pp.getWidth(); + sx2 = sx1 + pp.getHeight(); + sy2 = sy1 + pp.getWidth(); } else if ((tran & TR_ROT) == TR_R270) { - sx1 = width - ppy - pp.h; + sx1 = width - ppy - pp.getHeight(); sy1 = ppx; - sx2 = sx1 + pp.h; - sy2 = sy1 + pp.w; + sx2 = sx1 + pp.getHeight(); + sy2 = sy1 + pp.getWidth(); } - //printf ("ppx %d ppy %d ppw %d pph %d s: %d %d %d %d\n",pp.x, pp.y,pp.w,pp.h,sx1,sy1,sx2,sy2); if (sx1 < 0) { sx1 = 0; } diff --git a/rtengine/imagedimensions.h b/rtengine/imagedimensions.h index e3b98f7c5..63b1a1062 100644 --- a/rtengine/imagedimensions.h +++ b/rtengine/imagedimensions.h @@ -17,47 +17,47 @@ * along with RawTherapee. If not, see . */ -#ifndef _IMAGEDIMENSIONS_ -#define _IMAGEDIMENSIONS_ +#pragma once class PreviewProps { public: - int x, y, w, h, skip; - PreviewProps (int _x, int _y, int _w, int _h, int _skip) - : x(_x), y(_y), w(_w), h(_h), skip(_skip) {} + PreviewProps(int _x, int _y, int _width, int _height, int _skip); + + int getX() const; + int getY() const; + int getWidth() const; + int getHeight() const; + int getSkip() const; + +private: + int x; + int y; + int width; + int height; + int skip; }; /* - * Description of an image dimension, with getter and setter + * Description of an image dimension, with getter */ class ImageDimensions { - public: + ImageDimensions(); + + int getWidth() const + { + return width; + } + int getHeight() const + { + return height; + } + + void transform(const PreviewProps& pp, int tran, int& sx1, int& sy1, int& sx2, int& sy2) const; + +protected: int width; int height; - -public: - ImageDimensions() : width(-1), height(-1) {} - int getW () - { - return width; - } - int getH () - { - return height; - } - int getWidth () const - { - return width; - } - int getHeight () const - { - return height; - } - void transform (PreviewProps pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2); }; - - -#endif diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index f4629682d..aeb57c795 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -203,7 +203,7 @@ void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Prev int maxx = width; // Source image int maxy = height; // Source image int mtran = tran & TR_ROT; - int skip = pp.skip; + int skip = pp.getSkip(); // improve speed by integrating the area division into the multipliers // switched to using ints for the red/green/blue channel buffer. diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index 05684aaa7..60e037ba9 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -909,7 +909,7 @@ int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool s int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps) { - if (getW() < 1 || getH() < 1) { + if (getWidth() < 1 || getHeight() < 1) { return IMIO_HEADERERROR; } @@ -949,8 +949,8 @@ int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps) png_set_compression_level(png, compression); - int width = getW (); - int height = getH (); + int width = getWidth (); + int height = getHeight (); if (bps < 0) { bps = getBPS (); @@ -1006,7 +1006,7 @@ int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps) // Quality 0..100, subsampling: 1=low quality, 2=medium, 3=high int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) { - if (getW() < 1 || getH() < 1) { + if (getWidth() < 1 || getHeight() < 1) { return IMIO_HEADERERROR; } @@ -1054,8 +1054,8 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) jpeg_stdio_dest (&cinfo, file); - int width = getW (); - int height = getH (); + int width = getWidth (); + int height = getHeight (); cinfo.image_width = width; cinfo.image_height = height; @@ -1196,14 +1196,14 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) { - if (getW() < 1 || getH() < 1) { + if (getWidth() < 1 || getHeight() < 1) { return IMIO_HEADERERROR; } //TODO: Handling 32 bits floating point output images! bool writeOk = true; - int width = getW (); - int height = getH (); + int width = getWidth (); + int height = getHeight (); if (bps < 0) { bps = getBPS (); diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 46cf031bd..111193840 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1216,8 +1216,8 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool // image may contain out of range samples, clip them to avoid wrap-arounds #pragma omp parallel for - for(int i = 0; i < im->height; i++) { - for(int j = 0; j < im->width; j++) { + for(int i = 0; i < im->getHeight(); i++) { + for(int j = 0; j < im->getWidth(); j++) { im->r(i, j) = CLIP(im->r(i, j)); im->g(i, j) = CLIP(im->g(i, j)); im->b(i, j) = CLIP(im->b(i, j)); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 1a3a969ae..f756f347a 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -142,8 +142,8 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro lumimul[0] = wprof[1][0]; lumimul[1] = wprof[1][1]; lumimul[2] = wprof[1][2]; - int W = original->width; - int H = original->height; + int W = original->getWidth(); + int H = original->getHeight(); float lumimulf[3] = {static_cast(lumimul[0]), static_cast(lumimul[1]), static_cast(lumimul[2])}; @@ -2875,8 +2875,8 @@ void ImProcFunctions::moyeqt (Imagefloat* working, float &moyS, float &eqty) { BENCHFUN - int tHh = working->height; - int tWw = working->width; + int tHh = working->getHeight(); + int tWw = working->getWidth(); double moy = 0.0; double sqrs = 0.0; @@ -3271,11 +3271,11 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool hasgammabw = gammabwr != 1.f || gammabwg != 1.f || gammabwb != 1.f; if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled)) { - tmpImage = new Imagefloat(working->width, working->height); + tmpImage = new Imagefloat(working->getWidth(), working->getHeight()); } - int W = working->width; - int H = working->height; + int W = working->getWidth(); + int H = working->getHeight(); // For tonecurve histogram int toneCurveHistSize = histToneCurve ? histToneCurve.getSize() : 0; @@ -3340,12 +3340,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer #pragma omp for schedule(dynamic) collapse(2) #endif - for(int ii = 0; ii < working->height; ii += TS) - for(int jj = 0; jj < working->width; jj += TS) { + for(int ii = 0; ii < working->getHeight(); ii += TS) + for(int jj = 0; jj < working->getWidth(); jj += TS) { istart = ii; jstart = jj; - tH = min(ii + TS, working->height); - tW = min(jj + TS, working->width); + tH = min(ii + TS, working->getHeight()); + tW = min(jj + TS, working->getWidth()); for (int i = istart, ti = 0; i < tH; i++, ti++) { @@ -4425,8 +4425,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // starting a new tile processing with a 'reduction' clause for the auto mixer computing if (blackwhite) {//channel-mixer - int tW = working->width; - int tH = working->height; + int tW = working->getWidth(); + int tH = working->getHeight(); if (algm == 2) { //channel-mixer //end auto chmix diff --git a/rtengine/ipresize.cc b/rtengine/ipresize.cc index 8bdc0a6cb..e9c457ac8 100644 --- a/rtengine/ipresize.cc +++ b/rtengine/ipresize.cc @@ -53,18 +53,18 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) #pragma omp parallel { // storage for precomputed parameters for horisontal interpolation - float * wwh = new float[support * dst->width]; - int * jj0 = new int[dst->width]; - int * jj1 = new int[dst->width]; + float * wwh = new float[support * dst->getWidth()]; + int * jj0 = new int[dst->getWidth()]; + int * jj1 = new int[dst->getWidth()]; // temporal storage for vertically-interpolated row of pixels - float * lr = new float[src->width]; - float * lg = new float[src->width]; - float * lb = new float[src->width]; + float * lr = new float[src->getWidth()]; + float * lg = new float[src->getWidth()]; + float * lb = new float[src->getWidth()]; // Phase 1: precompute coefficients for horisontal interpolation - for (int j = 0; j < dst->width; j++) { + for (int j = 0; j < dst->getWidth(); j++) { // x coord of the center of pixel on src image float x0 = (static_cast(j) + 0.5f) * delta - 0.5f; @@ -76,7 +76,7 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) float ws = 0.0f; jj0[j] = max(0, static_cast(floorf(x0 - a / sc)) + 1); - jj1[j] = min(src->width, static_cast(floorf(x0 + a / sc)) + 1); + jj1[j] = min(src->getWidth(), static_cast(floorf(x0 + a / sc)) + 1); // calculate weights for (int jj = jj0[j]; jj < jj1[j]; jj++) { @@ -95,7 +95,7 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) // Phase 2: do actual interpolation #pragma omp for - for (int i = 0; i < dst->height; i++) { + for (int i = 0; i < dst->getHeight(); i++) { // y coord of the center of pixel on src image float y0 = (static_cast(i) + 0.5f) * delta - 0.5f; @@ -107,7 +107,7 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) float ws = 0.0f; int ii0 = max(0, static_cast(floorf(y0 - a / sc)) + 1); - int ii1 = min(src->height, static_cast(floorf(y0 + a / sc)) + 1); + int ii1 = min(src->getHeight(), static_cast(floorf(y0 + a / sc)) + 1); // calculate weights for vertical interpolation for (int ii = ii0; ii < ii1; ii++) { @@ -123,7 +123,7 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) } // Do vertical interpolation. Store results. - for (int j = 0; j < src->width; j++) { + for (int j = 0; j < src->getWidth(); j++) { float r = 0.0f, g = 0.0f, b = 0.0f; @@ -141,7 +141,7 @@ void ImProcFunctions::Lanczos(const Image16* src, Image16* dst, float scale) } // Do horizontal interpolation - for(int j = 0; j < dst->width; j++) { + for(int j = 0; j < dst->getWidth(); j++) { float * wh = wwh + support * j; @@ -407,13 +407,13 @@ void ImProcFunctions::resize (Image16* src, Image16* dst, float dScale) #pragma omp parallel for if (multiThread) #endif - for (int i = 0; i < dst->height; i++) { + for (int i = 0; i < dst->getHeight(); i++) { int sy = i / dScale; - sy = LIM(sy, 0, src->height - 1); + sy = LIM(sy, 0, src->getHeight() - 1); - for (int j = 0; j < dst->width; j++) { + for (int j = 0; j < dst->getWidth(); j++) { int sx = j / dScale; - sx = LIM(sx, 0, src->width - 1); + sx = LIM(sx, 0, src->getWidth() - 1); dst->r(i, j) = src->r(sy, sx); dst->g(i, j) = src->g(sy, sx); dst->b(i, j) = src->b(sy, sx); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index 1c0a09929..deae39108 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -299,7 +299,7 @@ void ImProcFunctions::transform (Imagefloat* original, Imagefloat* transformed, LCPProfile *pLCPProf = lcpStore->getProfile(params->lensProf.lcpFile); if (pLCPProf) pLCPMap = new LCPMapper(pLCPProf, focalLen, focalLen35mm, focusDist, 0, false, params->lensProf.useDist, - original->width, original->height, params->coarse, rawRotationDeg); + original->getWidth(), original->getHeight(), params->coarse, rawRotationDeg); } if (!(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP()) && (needsVignetting() || needsPCVignetting() || needsGradient())) { @@ -663,17 +663,17 @@ void ImProcFunctions::transformLuminanceOnly (Imagefloat* original, Imagefloat* struct pcv_params pcv; if (applyPCVignetting) { - //fprintf(stderr, "%d %d | %d %d | %d %d | %d %d [%d %d]\n", fW, fH, oW, oH, transformed->width, transformed->height, cx, cy, params->crop.w, params->crop.h); + //fprintf(stderr, "%d %d | %d %d | %d %d | %d %d [%d %d]\n", fW, fH, oW, oH, transformed->getWidth(), transformed->getHeight(), cx, cy, params->crop.w, params->crop.h); calcPCVignetteParams(fW, fH, oW, oH, params->pcvignette, params->crop, pcv); } bool darkening = (params->vignetting.amount <= 0.0); #pragma omp parallel for schedule(dynamic,16) if (multiThread) - for (int y = 0; y < transformed->height; y++) { + for (int y = 0; y < transformed->getHeight(); y++) { double vig_y_d = (double) (y + cy) - vig_h2 ; - for (int x = 0; x < transformed->width; x++) { + for (int x = 0; x < transformed->getWidth(); x++) { double factor = 1.0; if (applyVignetting) { @@ -779,8 +779,8 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr bool darkening = (params->vignetting.amount <= 0.0); #pragma omp parallel for if (multiThread) - for (int y = 0; y < transformed->height; y++) { - for (int x = 0; x < transformed->width; x++) { + for (int y = 0; y < transformed->getHeight(); y++) { + for (int x = 0; x < transformed->getWidth(); x++) { double x_d = x, y_d = y; if (enableLCPDist) { @@ -849,7 +849,7 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr yc -= sy; // Convert only valid pixels - if (yc >= 0 && yc < original->height && xc >= 0 && xc < original->width) { + if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) { // multiplier for vignetting correction double vignmul = 1.0; @@ -870,7 +870,7 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr vignmul *= calcPCVignetteFactor(pcv, cx + x, cy + y); } - if (yc > 0 && yc < original->height - 2 && xc > 0 && xc < original->width - 2) { + if (yc > 0 && yc < original->getHeight() - 2 && xc > 0 && xc < original->getWidth() - 2) { // all interpolation pixels inside image if (enableCA) { interpolateTransformChannelsCubic (chOrig[c], xc - 1, yc - 1, Dx, Dy, &(chTrans[c][y][x]), vignmul); @@ -879,10 +879,10 @@ void ImProcFunctions::transformHighQuality (Imagefloat* original, Imagefloat* tr } } else { // edge pixels - int y1 = LIM(yc, 0, original->height - 1); - int y2 = LIM(yc + 1, 0, original->height - 1); - int x1 = LIM(xc, 0, original->width - 1); - int x2 = LIM(xc + 1, 0, original->width - 1); + int y1 = LIM(yc, 0, original->getHeight() - 1); + int y2 = LIM(yc + 1, 0, original->getHeight() - 1); + int x1 = LIM(xc, 0, original->getWidth() - 1); + int x2 = LIM(xc + 1, 0, original->getWidth() - 1); if (enableCA) { chTrans[c][y][x] = vignmul * (chOrig[c][y1][x1] * (1.0 - Dx) * (1.0 - Dy) + chOrig[c][y1][x2] * Dx * (1.0 - Dy) + chOrig[c][y2][x1] * (1.0 - Dx) * Dy + chOrig[c][y2][x2] * Dx * Dy); @@ -956,8 +956,8 @@ void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transf // main cycle #pragma omp parallel for if (multiThread) - for (int y = 0; y < transformed->height; y++) { - for (int x = 0; x < transformed->width; x++) { + for (int y = 0; y < transformed->getHeight(); y++) { + for (int x = 0; x < transformed->getWidth(); x++) { double x_d = x, y_d = y; if (pLCPMap && params->lensProf.useDist) { @@ -1019,7 +1019,7 @@ void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transf yc -= sy; // Convert only valid pixels - if (yc >= 0 && yc < original->height && xc >= 0 && xc < original->width) { + if (yc >= 0 && yc < original->getHeight() && xc >= 0 && xc < original->getWidth()) { // multiplier for vignetting correction double vignmul = 1.0; @@ -1040,17 +1040,17 @@ void ImProcFunctions::transformPreview (Imagefloat* original, Imagefloat* transf vignmul *= calcPCVignetteFactor(pcv, cx + x, cy + y); } - if (yc < original->height - 1 && xc < original->width - 1) { + if (yc < original->getHeight() - 1 && xc < original->getWidth() - 1) { // all interpolation pixels inside image transformed->r(y, x) = vignmul * (original->r(yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->r(yc, xc + 1) * Dx * (1.0 - Dy) + original->r(yc + 1, xc) * (1.0 - Dx) * Dy + original->r(yc + 1, xc + 1) * Dx * Dy); transformed->g(y, x) = vignmul * (original->g(yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->g(yc, xc + 1) * Dx * (1.0 - Dy) + original->g(yc + 1, xc) * (1.0 - Dx) * Dy + original->g(yc + 1, xc + 1) * Dx * Dy); transformed->b(y, x) = vignmul * (original->b(yc, xc) * (1.0 - Dx) * (1.0 - Dy) + original->b(yc, xc + 1) * Dx * (1.0 - Dy) + original->b(yc + 1, xc) * (1.0 - Dx) * Dy + original->b(yc + 1, xc + 1) * Dx * Dy); } else { // edge pixels - int y1 = LIM(yc, 0, original->height - 1); - int y2 = LIM(yc + 1, 0, original->height - 1); - int x1 = LIM(xc, 0, original->width - 1); - int x2 = LIM(xc + 1, 0, original->width - 1); + int y1 = LIM(yc, 0, original->getHeight() - 1); + int y2 = LIM(yc + 1, 0, original->getHeight() - 1); + int x1 = LIM(xc, 0, original->getWidth() - 1); + int x2 = LIM(xc + 1, 0, original->getWidth() - 1); transformed->r(y, x) = vignmul * (original->r(y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->r(y1, x2) * Dx * (1.0 - Dy) + original->r(y2, x1) * (1.0 - Dx) * Dy + original->r(y2, x2) * Dx * Dy); transformed->g(y, x) = vignmul * (original->g(y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->g(y1, x2) * Dx * (1.0 - Dy) + original->g(y2, x1) * (1.0 - Dx) * Dy + original->g(y2, x2) * Dx * Dy); transformed->b(y, x) = vignmul * (original->b(y1, x1) * (1.0 - Dx) * (1.0 - Dy) + original->b(y1, x2) * Dx * (1.0 - Dy) + original->b(y2, x1) * (1.0 - Dx) * Dy + original->b(y2, x2) * Dx * Dy); diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 049c509f5..037c24ecf 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -101,10 +101,10 @@ void transLineFuji (const float* const red, const float* const green, const floa int y = i + j - fw; int x = fw - i + j; - if (x >= 0 && y < image->height && y >= 0 && x < image->width) { - image->r(image->height - 1 - y, image->width - 1 - x) = red[j]; - image->g(image->height - 1 - y, image->width - 1 - x) = green[j]; - image->b(image->height - 1 - y, image->width - 1 - x) = blue[j]; + if (x >= 0 && y < image->getHeight() && y >= 0 && x < image->getWidth()) { + image->r(image->getHeight() - 1 - y, image->getWidth() - 1 - x) = red[j]; + image->g(image->getHeight() - 1 - y, image->getWidth() - 1 - x) = green[j]; + image->b(image->getHeight() - 1 - y, image->getWidth() - 1 - x) = blue[j]; } } @@ -115,10 +115,10 @@ void transLineFuji (const float* const red, const float* const green, const floa int y = i + j - fw; int x = fw - i + j; - if (x >= 0 && x < image->height && y >= 0 && y < image->width) { - image->r(image->height - 1 - x, y) = red[j]; - image->g(image->height - 1 - x, y) = green[j]; - image->b(image->height - 1 - x, y) = blue[j]; + if (x >= 0 && x < image->getHeight() && y >= 0 && y < image->getWidth()) { + image->r(image->getHeight() - 1 - x, y) = red[j]; + image->g(image->getHeight() - 1 - x, y) = green[j]; + image->b(image->getHeight() - 1 - x, y) = blue[j]; } } @@ -129,10 +129,10 @@ void transLineFuji (const float* const red, const float* const green, const floa int y = i + j - fw; int x = fw - i + j; - if (x >= 0 && y < image->width && y >= 0 && x < image->height) { - image->r(x, image->width - 1 - y) = red[j]; - image->g(x, image->width - 1 - y) = green[j]; - image->b(x, image->width - 1 - y) = blue[j]; + if (x >= 0 && y < image->getWidth() && y >= 0 && x < image->getHeight()) { + image->r(x, image->getWidth() - 1 - y) = red[j]; + image->g(x, image->getWidth() - 1 - y) = green[j]; + image->b(x, image->getWidth() - 1 - y) = blue[j]; } } @@ -144,7 +144,7 @@ void transLineFuji (const float* const red, const float* const green, const floa int y = i + j - fw; int x = fw - i + j; - if (x >= 0 && y < image->height && y >= 0 && x < image->width) { + if (x >= 0 && y < image->getHeight() && y >= 0 && x < image->getWidth()) { image->r(y, x) = red[j]; image->g(y, x) = green[j]; image->b(y, x) = blue[j]; @@ -499,17 +499,18 @@ RawImageSource::~RawImageSource () void RawImageSource::transformRect (PreviewProps pp, int tran, int &ssx1, int &ssy1, int &width, int &height, int &fw) { - - pp.x += border; - pp.y += border; + int pp_x = pp.getX() + border; + int pp_y = pp.getY() + border; + int pp_width = pp.getWidth(); + int pp_height = pp.getHeight(); if (d1x) { if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { - pp.x /= 2; - pp.w = pp.w / 2 + 1; + pp_x /= 2; + pp_width = pp_width / 2 + 1; } else { - pp.y /= 2; - pp.h = pp.h / 2 + 1; + pp_y /= 2; + pp_height = pp_height / 2 + 1; } } @@ -527,44 +528,44 @@ void RawImageSource::transformRect (PreviewProps pp, int tran, int &ssx1, int &s sh = w; } - if( pp.w > sw - 2 * border) { - pp.w = sw - 2 * border; + if( pp_width > sw - 2 * border) { + pp_width = sw - 2 * border; } - if( pp.h > sh - 2 * border) { - pp.h = sh - 2 * border; + if( pp_height > sh - 2 * border) { + pp_height = sh - 2 * border; } - int ppx = pp.x, ppy = pp.y; + int ppx = pp_x, ppy = pp_y; if (tran & TR_HFLIP) { - ppx = sw - pp.x - pp.w; + ppx = sw - pp_x - pp_width; } if (tran & TR_VFLIP) { - ppy = sh - pp.y - pp.h; + ppy = sh - pp_y - pp_height; } int sx1 = ppx; // assuming it's >=0 int sy1 = ppy; // assuming it's >=0 - int sx2 = max(ppx + pp.w, w - 1); - int sy2 = max(ppy + pp.h, h - 1); + int sx2 = max(ppx + pp_width, w - 1); + int sy2 = max(ppy + pp_height, h - 1); if ((tran & TR_ROT) == TR_R180) { - sx1 = max(w - ppx - pp.w, 0); - sy1 = max(h - ppy - pp.h, 0); - sx2 = min(sx1 + pp.w, w - 1); - sy2 = min(sy1 + pp.h, h - 1); + sx1 = max(w - ppx - pp_width, 0); + sy1 = max(h - ppy - pp_height, 0); + sx2 = min(sx1 + pp_width, w - 1); + sy2 = min(sy1 + pp_height, h - 1); } else if ((tran & TR_ROT) == TR_R90) { sx1 = ppy; - sy1 = max(h - ppx - pp.w, 0); - sx2 = min(sx1 + pp.h, w - 1); - sy2 = min(sy1 + pp.w, h - 1); + sy1 = max(h - ppx - pp_width, 0); + sx2 = min(sx1 + pp_height, w - 1); + sy2 = min(sy1 + pp_width, h - 1); } else if ((tran & TR_ROT) == TR_R270) { - sx1 = max(w - ppy - pp.h, 0); + sx1 = max(w - ppy - pp_height, 0); sy1 = ppx; - sx2 = min(sx1 + pp.h, w - 1); - sy2 = min(sy1 + pp.w, h - 1); + sx2 = min(sx1 + pp_height, w - 1); + sy2 = min(sy1 + pp_width, h - 1); } if (fuji) { @@ -574,14 +575,14 @@ void RawImageSource::transformRect (PreviewProps pp, int tran, int &ssx1, int &s ssy1 = (sy1 - sx2 ) / 2 + ri->get_FujiWidth(); int ssx2 = (sx2 + sy2) / 2 + 1; int ssy2 = (sy2 - sx1) / 2 + ri->get_FujiWidth(); - fw = (sx2 - sx1) / 2 / pp.skip; - width = (ssx2 - ssx1) / pp.skip + ((ssx2 - ssx1) % pp.skip > 0); - height = (ssy2 - ssy1) / pp.skip + ((ssy2 - ssy1) % pp.skip > 0); + fw = (sx2 - sx1) / 2 / pp.getSkip(); + width = (ssx2 - ssx1) / pp.getSkip() + ((ssx2 - ssx1) % pp.getSkip() > 0); + height = (ssy2 - ssy1) / pp.getSkip() + ((ssy2 - ssy1) % pp.getSkip() > 0); } else { ssx1 = sx1; ssy1 = sy1; - width = (sx2 - sx1) / pp.skip + ((sx2 - sx1) % pp.skip > 0); - height = (sy2 - sy1) / pp.skip + ((sy2 - sy1) % pp.skip > 0); + width = (sx2 - sx1) / pp.getSkip() + ((sx2 - sx1) % pp.getSkip() > 0); + height = (sy2 - sy1) / pp.getSkip() + ((sy2 - sy1) % pp.getSkip() > 0); } } @@ -674,11 +675,11 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima int maximwidth, maximheight; if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { - maximwidth = image->height; - maximheight = image->width; + maximwidth = image->getHeight(); + maximheight = image->getWidth(); } else { - maximwidth = image->width; - maximheight = image->height; + maximwidth = image->getWidth(); + maximheight = image->getHeight(); } if (d1x) { @@ -699,7 +700,7 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima imheight = maximheight; } - int maxx = this->W, maxy = this->H, skip = pp.skip; + int maxx = this->W, maxy = this->H, skip = pp.getSkip(); // raw clip levels after white balance hlmax[0] = clmax[0] * rm; @@ -818,18 +819,18 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima #endif if (fuji) { - int a = ((tran & TR_ROT) == TR_R90 && image->width % 2 == 0) || ((tran & TR_ROT) == TR_R180 && image->height % 2 + image->width % 2 == 1) || ((tran & TR_ROT) == TR_R270 && image->height % 2 == 0); + int a = ((tran & TR_ROT) == TR_R90 && image->getWidth() % 2 == 0) || ((tran & TR_ROT) == TR_R180 && image->getHeight() % 2 + image->getWidth() % 2 == 1) || ((tran & TR_ROT) == TR_R270 && image->getHeight() % 2 == 0); // first row - for (int j = 1 + a; j < image->width - 1; j += 2) { + for (int j = 1 + a; j < image->getWidth() - 1; j += 2) { image->r(0, j) = (image->r(1, j) + image->r(0, j + 1) + image->r(0, j - 1)) / 3; image->g(0, j) = (image->g(1, j) + image->g(0, j + 1) + image->g(0, j - 1)) / 3; image->b(0, j) = (image->b(1, j) + image->b(0, j + 1) + image->b(0, j - 1)) / 3; } // other rows - for (int i = 1; i < image->height - 1; i++) { - for (int j = 2 - (a + i + 1) % 2; j < image->width - 1; j += 2) { + for (int i = 1; i < image->getHeight() - 1; i++) { + for (int j = 2 - (a + i + 1) % 2; j < image->getWidth() - 1; j += 2) { // edge-adaptive interpolation double dh = (ABS(image->r(i, j + 1) - image->r(i, j - 1)) + ABS(image->g(i, j + 1) - image->g(i, j - 1)) + ABS(image->b(i, j + 1) - image->b(i, j - 1))) / 1.0; double dv = (ABS(image->r(i + 1, j) - image->r(i - 1, j)) + ABS(image->g(i + 1, j) - image->g(i - 1, j)) + ABS(image->b(i + 1, j) - image->b(i - 1, j))) / 1.0; @@ -848,20 +849,20 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima } // last pixel - if (2 - (a + i + image->width) % 2 == 2) { - image->r(i, image->width - 1) = (image->r(i + 1, image->width - 1) + image->r(i - 1, image->width - 1) + image->r(i, image->width - 2)) / 3; - image->g(i, image->width - 1) = (image->g(i + 1, image->width - 1) + image->g(i - 1, image->width - 1) + image->g(i, image->width - 2)) / 3; - image->b(i, image->width - 1) = (image->b(i + 1, image->width - 1) + image->b(i - 1, image->width - 1) + image->b(i, image->width - 2)) / 3; + if (2 - (a + i + image->getWidth()) % 2 == 2) { + image->r(i, image->getWidth() - 1) = (image->r(i + 1, image->getWidth() - 1) + image->r(i - 1, image->getWidth() - 1) + image->r(i, image->getWidth() - 2)) / 3; + image->g(i, image->getWidth() - 1) = (image->g(i + 1, image->getWidth() - 1) + image->g(i - 1, image->getWidth() - 1) + image->g(i, image->getWidth() - 2)) / 3; + image->b(i, image->getWidth() - 1) = (image->b(i + 1, image->getWidth() - 1) + image->b(i - 1, image->getWidth() - 1) + image->b(i, image->getWidth() - 2)) / 3; } } // last row - int b = (a == 1 && image->height % 2) || (a == 0 && image->height % 2 == 0); + int b = (a == 1 && image->getHeight() % 2) || (a == 0 && image->getHeight() % 2 == 0); - for (int j = 1 + b; j < image->width - 1; j += 2) { - image->r(image->height - 1, j) = (image->r(image->height - 2, j) + image->r(image->height - 1, j + 1) + image->r(image->height - 1, j - 1)) / 3; - image->g(image->height - 1, j) = (image->g(image->height - 2, j) + image->g(image->height - 1, j + 1) + image->g(image->height - 1, j - 1)) / 3; - image->b(image->height - 1, j) = (image->b(image->height - 2, j) + image->b(image->height - 1, j + 1) + image->b(image->height - 1, j - 1)) / 3; + for (int j = 1 + b; j < image->getWidth() - 1; j += 2) { + image->r(image->getHeight() - 1, j) = (image->r(image->getHeight() - 2, j) + image->r(image->getHeight() - 1, j + 1) + image->r(image->getHeight() - 1, j - 1)) / 3; + image->g(image->getHeight() - 1, j) = (image->g(image->getHeight() - 2, j) + image->g(image->getHeight() - 1, j + 1) + image->g(image->getHeight() - 1, j - 1)) / 3; + image->b(image->getHeight() - 1, j) = (image->b(image->getHeight() - 2, j) + image->b(image->getHeight() - 1, j + 1) + image->b(image->getHeight() - 1, j - 1)) / 3; } } @@ -875,7 +876,7 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima } // Colour correction (only when running on full resolution) - if(pp.skip == 1) { + if(pp.getSkip() == 1) { switch(ri->getSensorType()) { case ST_BAYER: processFalseColorCorrection (image, raw.bayersensor.ccSteps); @@ -1472,9 +1473,8 @@ void RawImageSource::getFullSize (int& w, int& h, int tr) void RawImageSource::getSize (PreviewProps pp, int& w, int& h) { - - w = pp.w / pp.skip + (pp.w % pp.skip > 0); - h = pp.h / pp.skip + (pp.h % pp.skip > 0); + w = pp.getWidth() / pp.getSkip() + (pp.getWidth() % pp.getSkip() > 0); + h = pp.getHeight() / pp.getSkip() + (pp.getHeight() % pp.getSkip() > 0); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -3514,7 +3514,7 @@ int RawImageSource::defTransform (int tran) void RawImageSource::processFalseColorCorrectionThread (Imagefloat* im, array2D &rbconv_Y, array2D &rbconv_I, array2D &rbconv_Q, array2D &rbout_I, array2D &rbout_Q, const int row_from, const int row_to) { - const int W = im->width; + const int W = im->getWidth(); constexpr float onebynine = 1.f / 9.f; #ifdef __SSE2__ @@ -3661,7 +3661,7 @@ void RawImageSource::processFalseColorCorrectionThread (Imagefloat* im, array2D void RawImageSource::processFalseColorCorrection (Imagefloat* im, const int steps) { - if (im->height < 4 || steps < 1) { + if (im->getHeight() < 4 || steps < 1) { return; } @@ -3671,14 +3671,14 @@ void RawImageSource::processFalseColorCorrection (Imagefloat* im, const int ste multi_array2D buffer (W, 3); int tid = omp_get_thread_num(); int nthreads = omp_get_num_threads(); - int blk = (im->height - 2) / nthreads; + int blk = (im->getHeight() - 2) / nthreads; for (int t = 0; t < steps; t++) { if (tid < nthreads - 1) { processFalseColorCorrectionThread (im, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], 1 + tid * blk, 1 + (tid + 1)*blk); } else { - processFalseColorCorrectionThread (im, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], 1 + tid * blk, im->height - 1); + processFalseColorCorrectionThread (im, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], 1 + tid * blk, im->getHeight() - 1); } #pragma omp barrier @@ -3688,7 +3688,7 @@ void RawImageSource::processFalseColorCorrection (Imagefloat* im, const int ste multi_array2D buffer (W, 3); for (int t = 0; t < steps; t++) { - processFalseColorCorrectionThread (im, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], 1 , im->height - 1); + processFalseColorCorrectionThread (im, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], 1 , im->getHeight() - 1); } #endif @@ -3812,8 +3812,8 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam #pragma omp parallel for #endif - for (int i = 0; i < im->height; i++) - for (int j = 0; j < im->width; j++) { + for (int i = 0; i < im->getHeight(); i++) + for (int j = 0; j < im->getWidth(); j++) { float newr = mat[0][0] * im->r(i, j) + mat[0][1] * im->g(i, j) + mat[0][2] * im->b(i, j); float newg = mat[1][0] * im->r(i, j) + mat[1][1] * im->g(i, j) + mat[1][2] * im->b(i, j); @@ -3945,18 +3945,18 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam #pragma omp parallel #endif { - AlignedBuffer buffer(im->width * 3); - AlignedBuffer hl_buffer(im->width * 3); - AlignedBuffer hl_scale(im->width); + AlignedBuffer buffer(im->getWidth() * 3); + AlignedBuffer hl_buffer(im->getWidth() * 3); + AlignedBuffer hl_scale(im->getWidth()); #ifdef _OPENMP #pragma omp for schedule(static) #endif - for ( int h = 0; h < im->height; ++h ) { + for ( int h = 0; h < im->getHeight(); ++h ) { float *p = buffer.data, *pR = im->r(h), *pG = im->g(h), *pB = im->b(h); // Apply pre-processing - for ( int w = 0; w < im->width; ++w ) { + for ( int w = 0; w < im->getWidth(); ++w ) { float r = *(pR++); float g = *(pG++); float b = *(pB++); @@ -4027,10 +4027,10 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam } // Run icc transform - cmsDoTransform (hTransform, buffer.data, buffer.data, im->width); + cmsDoTransform (hTransform, buffer.data, buffer.data, im->getWidth()); if (separate_pcs_lab_highlights) { - cmsDoTransform (hTransform, hl_buffer.data, hl_buffer.data, im->width); + cmsDoTransform (hTransform, hl_buffer.data, hl_buffer.data, im->getWidth()); } // Apply post-processing @@ -4039,7 +4039,7 @@ void RawImageSource::colorSpaceConversion_ (Imagefloat* im, ColorManagementParam pG = im->g(h); pB = im->b(h); - for ( int w = 0; w < im->width; ++w ) { + for ( int w = 0; w < im->getWidth(); ++w ) { float r, g, b, hr, hg, hb; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 257a3eeb3..c3cfdaea4 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -98,16 +98,16 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, if (inspectorMode) { // Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature) - w = img->width; - h = img->height; + w = img->getWidth(); + h = img->getHeight(); tpp->scale = 1.; } else { if (fixwh == 1) { - w = h * img->width / img->height; - tpp->scale = (double)img->height / h; + w = h * img->getWidth() / img->getHeight(); + tpp->scale = (double)img->getHeight() / h; } else { - h = w * img->height / img->width; - tpp->scale = (double)img->width / w; + h = w * img->getHeight() / img->getWidth(); + tpp->scale = (double)img->getWidth() / w; } } @@ -223,16 +223,16 @@ Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataL if (inspectorMode) { // Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature) - w = img->width; - h = img->height; + w = img->getWidth(); + h = img->getHeight(); tpp->scale = 1.; } else { if (fixwh == 1) { - w = h * img->width / img->height; - tpp->scale = (double)img->height / h; + w = h * img->getWidth() / img->getHeight(); + tpp->scale = (double)img->getHeight() / h; } else { - h = w * img->height / img->width; - tpp->scale = (double)img->width / w; + h = w * img->getHeight() / img->getWidth(); + tpp->scale = (double)img->getWidth() / w; } } @@ -260,8 +260,8 @@ Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataL if (suffix != "mos" && suffix != "mef" && suffix != "iiq") { tpp->thumbImg->rotate(ri->get_rotateDegree()); // width/height may have changed after rotating - w = tpp->thumbImg->width; - h = tpp->thumbImg->height; + w = tpp->thumbImg->getWidth(); + h = tpp->thumbImg->getHeight(); } } @@ -810,9 +810,9 @@ IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int if (params.coarse.rotate == 90 || params.coarse.rotate == 270) { rwidth = rheight; - rheight = thumbImg->height * rwidth / thumbImg->width; + rheight = thumbImg->getHeight() * rwidth / thumbImg->getWidth(); } else { - rwidth = thumbImg->width * rheight / thumbImg->height; + rwidth = thumbImg->getWidth() * rheight / thumbImg->getHeight(); } Image8* baseImg = resizeTo(rwidth, rheight, interp, thumbImg); @@ -889,9 +889,9 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei if (params.coarse.rotate == 90 || params.coarse.rotate == 270) { rwidth = rheight; - rheight = int(size_t(thumbImg->height) * size_t(rwidth) / size_t(thumbImg->width)); + rheight = int(size_t(thumbImg->getHeight()) * size_t(rwidth) / size_t(thumbImg->getWidth())); } else { - rwidth = int(size_t(thumbImg->width) * size_t(rheight) / size_t(thumbImg->height)); + rwidth = int(size_t(thumbImg->getWidth()) * size_t(rheight) / size_t(thumbImg->getHeight())); } @@ -899,8 +899,8 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei if (params.coarse.rotate) { baseImg->rotate (params.coarse.rotate); - rwidth = baseImg->width; - rheight = baseImg->height; + rwidth = baseImg->getWidth(); + rheight = baseImg->getHeight(); } if (params.coarse.hflip) { @@ -940,12 +940,12 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei StdImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, thumbImg->getSampleFormat()); } - int fw = baseImg->width; - int fh = baseImg->height; + int fw = baseImg->getWidth(); + int fh = baseImg->getHeight(); //ColorTemp::CAT02 (baseImg, ¶ms) ;//perhaps not good! ImProcFunctions ipf (¶ms, false); - ipf.setScale (sqrt(double(fw * fw + fh * fh)) / sqrt(double(thumbImg->width * thumbImg->width + thumbImg->height * thumbImg->height))*scale); + ipf.setScale (sqrt(double(fw * fw + fh * fh)) / sqrt(double(thumbImg->getWidth() * thumbImg->getWidth() + thumbImg->getHeight() * thumbImg->getHeight()))*scale); ipf.updateColorProfiles (options.rtSettings.monitorProfile, options.rtSettings.monitorIntent, false, false); LUTu hist16 (65536); @@ -1206,9 +1206,9 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei // calculate scale if (params.coarse.rotate == 90 || params.coarse.rotate == 270) { - myscale = scale * thumbImg->width / fh; + myscale = scale * thumbImg->getWidth() / fh; } else { - myscale = scale * thumbImg->height / fh; + myscale = scale * thumbImg->getHeight() / fh; } myscale = 1.0 / myscale; @@ -1238,9 +1238,9 @@ int Thumbnail::getImageWidth (const procparams::ProcParams& params, int rheight, int rwidth; if (params.coarse.rotate == 90 || params.coarse.rotate == 270) { - ratio = (float)(thumbImg->height) / (float)(thumbImg->width); + ratio = (float)(thumbImg->getHeight()) / (float)(thumbImg->getWidth()); } else { - ratio = (float)(thumbImg->width) / (float)(thumbImg->height); + ratio = (float)(thumbImg->getWidth()) / (float)(thumbImg->getHeight()); } rwidth = (int)(ratio * (float)rheight); @@ -1251,8 +1251,8 @@ int Thumbnail::getImageWidth (const procparams::ProcParams& params, int rheight, void Thumbnail::getDimensions (int& w, int& h, double& scaleFac) { if (thumbImg) { - w = thumbImg->width; - h = thumbImg->height; + w = thumbImg->getWidth(); + h = thumbImg->getHeight(); scaleFac = scale; } else { w = 0; @@ -1314,11 +1314,11 @@ void Thumbnail::getSpotWB (const procparams::ProcParams& params, int xp, int yp, points.push_back (Coord2D (j, i)); } - int fw = thumbImg->width, fh = thumbImg->height; + int fw = thumbImg->getWidth(), fh = thumbImg->getHeight(); if (params.coarse.rotate == 90 || params.coarse.rotate == 270) { - fw = thumbImg->height; - fh = thumbImg->width; + fw = thumbImg->getHeight(); + fh = thumbImg->getWidth(); } ImProcFunctions ipf (¶ms, false); @@ -1343,8 +1343,8 @@ void Thumbnail::getSpotWB (const procparams::ProcParams& params, int xp, int yp, void Thumbnail::transformPixel (int x, int y, int tran, int& tx, int& ty) { - int W = thumbImg->width; - int H = thumbImg->height; + int W = thumbImg->getWidth(); + int H = thumbImg->getHeight(); int sw = W, sh = H; if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) { @@ -1386,12 +1386,12 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) return nullptr; } - if (thumbImg->width < trim_width) { + if (thumbImg->getWidth() < trim_width) { return nullptr; } // to utilize the 8 bit color range of the thumbnail we brighten it and apply gamma correction - unsigned char* tmpdata = new unsigned char[thumbImg->height * trim_width]; + unsigned char* tmpdata = new unsigned char[thumbImg->getHeight() * trim_width]; int ix = 0, max; if (gammaCorrected) { @@ -1417,7 +1417,7 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) } // Go down till we cut off that many pixels - unsigned long cutoff = thumbImg->height * thumbImg->height * 4 * BurnOffPct; + unsigned long cutoff = thumbImg->getHeight() * thumbImg->getHeight() * 4 * BurnOffPct; int max_; unsigned long sum = 0; @@ -1434,8 +1434,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) if (thumbImg->getType() == sImage8) { Image8 *image = static_cast(thumbImg); - for (int i = 0; i < thumbImg->height; i++) - for (int j = (thumbImg->width - trim_width) / 2; j < trim_width + (thumbImg->width - trim_width) / 2; j++) { + for (int i = 0; i < thumbImg->getHeight(); i++) + for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) { unsigned short r_, g_, b_; image->convertTo(image->r(i, j), r_); image->convertTo(image->g(i, j), g_); @@ -1448,8 +1448,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) } else if (thumbImg->getType() == sImage16) { Image16 *image = static_cast(thumbImg); - for (int i = 0; i < thumbImg->height; i++) - for (int j = (thumbImg->width - trim_width) / 2; j < trim_width + (thumbImg->width - trim_width) / 2; j++) { + for (int i = 0; i < thumbImg->getHeight(); i++) + for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) { unsigned short r_, g_, b_; image->convertTo(image->r(i, j), r_); image->convertTo(image->g(i, j), g_); @@ -1462,8 +1462,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) } else if (thumbImg->getType() == sImagefloat) { Imagefloat *image = static_cast(thumbImg); - for (int i = 0; i < thumbImg->height; i++) - for (int j = (thumbImg->width - trim_width) / 2; j < trim_width + (thumbImg->width - trim_width) / 2; j++) { + for (int i = 0; i < thumbImg->getHeight(); i++) + for (int j = (thumbImg->getWidth() - trim_width) / 2; j < trim_width + (thumbImg->getWidth() - trim_width) / 2; j++) { unsigned short r_, g_, b_; image->convertTo(image->r(i, j), r_); image->convertTo(image->g(i, j), g_); @@ -1482,8 +1482,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) Image8 *image = static_cast(thumbImg); unsigned char max_ = 0; - for (int row = 0; row < image->height; row++) - for (int col = 0; col < image->width; col++) { + for (int row = 0; row < image->getHeight(); row++) + for (int col = 0; col < image->getWidth(); col++) { if (image->r(row, col) > max_) { max_ = image->r(row, col); } @@ -1506,8 +1506,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) scaleForSave = 65535 * 8192 / max; // Correction and gamma to 8 Bit - for (int i = 0; i < image->height; i++) - for (int j = (image->width - trim_width) / 2; j < trim_width + (image->width - trim_width) / 2; j++) { + for (int i = 0; i < image->getHeight(); i++) + for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) { unsigned short rtmp, gtmp, btmp; image->convertTo(image->r(i, j), rtmp); image->convertTo(image->g(i, j), gtmp); @@ -1521,8 +1521,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) Image16 *image = static_cast(thumbImg); unsigned short max_ = 0; - for (int row = 0; row < image->height; row++) - for (int col = 0; col < image->width; col++) { + for (int row = 0; row < image->getHeight(); row++) + for (int col = 0; col < image->getWidth(); col++) { if (image->r(row, col) > max_) { max_ = image->r(row, col); } @@ -1545,8 +1545,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) scaleForSave = 65535 * 8192 / max; // Correction and gamma to 8 Bit - for (int i = 0; i < image->height; i++) - for (int j = (image->width - trim_width) / 2; j < trim_width + (image->width - trim_width) / 2; j++) { + for (int i = 0; i < image->getHeight(); i++) + for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) { unsigned short rtmp, gtmp, btmp; image->convertTo(image->r(i, j), rtmp); image->convertTo(image->g(i, j), gtmp); @@ -1560,8 +1560,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) Imagefloat *image = static_cast(thumbImg); float max_ = 0.f; - for (int row = 0; row < image->height; row++) - for (int col = 0; col < image->width; col++) { + for (int row = 0; row < image->getHeight(); row++) + for (int col = 0; col < image->getWidth(); col++) { if (image->r(row, col) > max_) { max_ = image->r(row, col); } @@ -1584,8 +1584,8 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) scaleForSave = 65535 * 8192 / max; // Correction and gamma to 8 Bit - for (int i = 0; i < image->height; i++) - for (int j = (image->width - trim_width) / 2; j < trim_width + (image->width - trim_width) / 2; j++) { + for (int i = 0; i < image->getHeight(); i++) + for (int j = (image->getWidth() - trim_width) / 2; j < trim_width + (image->getWidth() - trim_width) / 2; j++) { unsigned short rtmp, gtmp, btmp; image->convertTo(image->r(i, j), rtmp); image->convertTo(image->g(i, j), gtmp); @@ -1617,7 +1617,7 @@ unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) } if (cdf_min != -1) { - hist[i] = (cdf - cdf_min) * 255 / ((thumbImg->height * trim_width) - cdf_min); + hist[i] = (cdf - cdf_min) * 255 / ((thumbImg->getHeight() * trim_width) - cdf_min); } } @@ -1645,8 +1645,8 @@ bool Thumbnail::writeImage (const Glib::ustring& fname, int format) fwrite (thumbImg->getType(), sizeof (char), strlen(thumbImg->getType()), f); fputc ('\n', f); - guint32 w = guint32(thumbImg->width); - guint32 h = guint32(thumbImg->height); + guint32 w = guint32(thumbImg->getWidth()); + guint32 h = guint32(thumbImg->getHeight()); fwrite (&w, sizeof (guint32), 1, f); fwrite (&h, sizeof (guint32), 1, f); diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 95475e6a4..1ef4bf884 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -276,20 +276,19 @@ void StdImageSource::colorSpaceConversion (Imagefloat* im, const ColorManagement void StdImageSource::getFullSize (int& w, int& h, int tr) { - w = img->width; - h = img->height; + w = img->getWidth(); + h = img->getHeight(); if ((tr & TR_ROT) == TR_R90 || (tr & TR_ROT) == TR_R270) { - w = img->height; - h = img->width; + w = img->getHeight(); + h = img->getWidth(); } } void StdImageSource::getSize (PreviewProps pp, int& w, int& h) { - - w = pp.w / pp.skip + (pp.w % pp.skip > 0); - h = pp.h / pp.skip + (pp.h % pp.skip > 0); + w = pp.getWidth() / pp.getSkip() + (pp.getWidth() % pp.getSkip() > 0); + h = pp.getHeight() / pp.getSkip() + (pp.getHeight() % pp.getSkip() > 0); } void StdImageSource::getAutoExpHistogram (LUTu & histogram, int& histcompr) diff --git a/rtgui/bqentryupdater.cc b/rtgui/bqentryupdater.cc index 37ce4c0ef..1bb7e4257 100644 --- a/rtgui/bqentryupdater.cc +++ b/rtgui/bqentryupdater.cc @@ -120,11 +120,11 @@ void BatchQueueEntryUpdater::processThread () int prevh = img->getHeight(); #ifndef NDEBUG - if (current.ow != img->getW() || current.oh != img->getH()) { - printf("WARNING! Expected image size: %dx%d ; image size is: %dx%d\n", current.ow, current.oh, img->getW(), img->getH()); + if (current.ow != img->getWidth() || current.oh != img->getHeight()) { + printf("WARNING! Expected image size: %dx%d ; image size is: %dx%d\n", current.ow, current.oh, img->getWidth(), img->getHeight()); } - assert ((current.ow + 1)*current.oh >= img->getW()*img->getH()); + assert ((current.ow + 1)*current.oh >= img->getWidth()*img->getHeight()); #endif current.ow = prevw; current.oh = prevh; From 54d1533a7dc47143b279a602692618abf41f429d Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 15 Feb 2017 01:30:41 +0100 Subject: [PATCH 089/181] Fix autowb issues, fixes #3690 --- rtengine/improccoordinator.cc | 5 ++++- rtengine/improccoordinator.h | 6 ++++++ rtengine/rtengine.h | 9 +++++++++ rtgui/toolpanelcoord.cc | 1 + rtgui/whitebalance.cc | 8 ++++++++ rtgui/whitebalance.h | 3 ++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 46cf031bd..42e99d2a0 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -87,7 +87,7 @@ ImProcCoordinator::ImProcCoordinator () fw(0), fh(0), tr(0), fullw(1), fullh(1), pW(-1), pH(-1), - plistener(nullptr), imageListener(nullptr), aeListener(nullptr), acListener(nullptr), abwListener(nullptr), actListener(nullptr), adnListener(nullptr), awavListener(nullptr), dehaListener(nullptr), hListener(nullptr), + plistener(nullptr), imageListener(nullptr), aeListener(nullptr), acListener(nullptr), abwListener(nullptr), awbListener(nullptr), actListener(nullptr), adnListener(nullptr), awavListener(nullptr), dehaListener(nullptr), hListener(nullptr), resultValid(false), lastOutputProfile("BADFOOD"), lastOutputIntent(RI__COUNT), lastOutputBPC(false), thread(nullptr), changeSinceLast(0), updaterRunning(false), destroying(false), utili(false), autili(false), wavcontlutili(false), butili(false), ccutili(false), cclutili(false), clcutili(false), opautili(false), conversionBuffer(1, 1), colourToningSatLimit(0.f), colourToningSatLimitOpacity(0.f) {} @@ -305,6 +305,9 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) params.wb.temperature = currWB.getTemp (); params.wb.green = currWB.getGreen (); + if(params.wb.method == "Auto" && awbListener) { + awbListener->WBChanged(params.wb.temperature, params.wb.green); + } int tr = getCoarseBitMask(params.coarse); diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 2cc767b39..0e0379553 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -156,6 +156,8 @@ protected: AutoExpListener* aeListener; AutoCamListener* acListener; AutoBWListener* abwListener; + AutoWBListener* awbListener; + AutoColorTonListener* actListener; AutoChromaListener* adnListener; WaveletListener* awavListener; @@ -311,6 +313,10 @@ public: { abwListener = abw; } + void setAutoWBListener (AutoWBListener* awb) + { + awbListener = awb; + } void setAutoColorTonListener (AutoColorTonListener* bwct) { actListener = bwct; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 61c779fb7..26ffc30d1 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -310,6 +310,14 @@ public : }; +class AutoWBListener +{ +public : + virtual ~AutoWBListener() {} + virtual void WBChanged (double temp, double green) {} + +}; + class WaveletListener { public : @@ -411,6 +419,7 @@ public: virtual void setPreviewImageListener (PreviewImageListener* l) = 0; virtual void setAutoCamListener (AutoCamListener* l) = 0; virtual void setAutoBWListener (AutoBWListener* l) = 0; + virtual void setAutoWBListener (AutoWBListener* l) = 0; virtual void setAutoColorTonListener (AutoColorTonListener* l) = 0; virtual void setAutoChromaListener (AutoChromaListener* l) = 0; virtual void setRetinexListener (RetinexListener* l) = 0; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 98dc311f8..71281abd3 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -488,6 +488,7 @@ void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool ipc->setAutoExpListener (toneCurve); ipc->setAutoCamListener (colorappearance); ipc->setAutoBWListener (blackwhite); + ipc->setAutoWBListener (whitebalance); ipc->setAutoColorTonListener (colortoning); ipc->setAutoChromaListener (dirpyrdenoise); ipc->setWaveletListener (wavelet); diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index d0413412b..a15410713 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -884,3 +884,11 @@ inline Gtk::TreeRow WhiteBalance::getActiveMethod () { return *(method->get_active()); } + +void WhiteBalance::WBChanged(double temperature, double greenVal) +{ + disableListener(); + temp->setValue(temperature); + green->setValue(greenVal); + enableListener(); +} diff --git a/rtgui/whitebalance.h b/rtgui/whitebalance.h index c18b15853..0cf119a86 100644 --- a/rtgui/whitebalance.h +++ b/rtgui/whitebalance.h @@ -34,7 +34,7 @@ public: virtual void spotWBRequested (int size) {} }; -class WhiteBalance : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class WhiteBalance : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoWBListener { enum WB_LabelType { @@ -114,6 +114,7 @@ public: wblistener = l; } void setWB (int temp, double green); + void WBChanged (double temp, double green); void setAdjusterBehavior (bool tempadd, bool greenadd, bool equaladd); void trimValues (rtengine::procparams::ProcParams* pp); From 974c3ff467618eafdccfb3061c14bec2dbd5c614 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 15 Feb 2017 17:54:29 +0100 Subject: [PATCH 090/181] Cleanup for autowb fix --- rtengine/rtengine.h | 5 ++- rtgui/whitebalance.cc | 75 ++++--------------------------------------- 2 files changed, 9 insertions(+), 71 deletions(-) diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 26ffc30d1..5678d1781 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -313,9 +313,8 @@ public : class AutoWBListener { public : - virtual ~AutoWBListener() {} - virtual void WBChanged (double temp, double green) {} - + virtual ~AutoWBListener() = default; + virtual void WBChanged(double temp, double green) = 0; }; class WaveletListener diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index a15410713..25722568c 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -374,24 +374,7 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) } else if (a == equal) { cache_customEqual (eVal); - // Recomputing AutoWB if it's the current method - if (wbp && ppMethod->type == WBT_AUTO) { - double ctemp = -1.0; - double cgreen = -1.0; - wbp->getAutoWB (ctemp, cgreen, eVal); - - if (ctemp != -1.0) { - // Set the automatics temperature value only if in SET mode - if (temp->getEditedState() && !temp->getAddMode() ) { - temp->setValue (ctemp); - } - - // Set the automatics green value only if in SET mode - if (green->getEditedState() && !green->getAddMode()) { - green->setValue (cgreen); - } - } - } + // Recomputing AutoWB if it's the current method will happen in improccoordinator.cc } if (listener) { @@ -460,15 +443,7 @@ void WhiteBalance::optChanged () // equal remain as is } - if (!batchMode || equal->getEditedState()) { - double ctemp, cgreen; - wbp->getAutoWB (ctemp, cgreen, equal->getValue()); - - if (ctemp != -1.0) { - temp->setValue (temp->getAddMode() ? 0.0 : (int)ctemp); - green->setValue (green->getAddMode() ? 0.0 : cgreen); - } - } + // Recomputing AutoWB will happen in improccoordinator.cc } break; @@ -617,32 +592,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) green->setValue (0.0); } - // then check for the correct ones, if possible - if (wbp) { - double ctemp = -1.0; - double cgreen = -1.0; - wbp->getAutoWB (ctemp, cgreen, pp->wb.equal); - - if (ctemp != -1.0) { - // Set the automatics temperature if in SET mode - if (!pedited || (pedited->wb.temperature && !temp->getAddMode()) ) { - temp->setValue (ctemp); - - if (pedited) { - temp->setEditedState (Edited); - } - } - - // Set the automatics green value if in SET mode - if (!pedited || (pedited->wb.green && !green->getAddMode())) { - green->setValue (cgreen); - - if (pedited) { - green->setEditedState (Edited); - } - } - } - } + // Recomputing AutoWB will happen in improccoordinator.cc break; @@ -716,25 +666,11 @@ void WhiteBalance::setDefaults (const ProcParams* defParams, const ParamsEdited* temp->setDefault (temp->getAddMode() ? 0 : (int)ctemp); green->setDefault (green->getAddMode() ? 0 : cgreen); } - } else if (wbp && defParams->wb.method == "Auto") { - // this setDefaults method is called too early ; the wbp has been set, - // but wbp is not ready to provide! - double ctemp; - double cgreen; - wbp->getAutoWB (ctemp, cgreen, defParams->wb.equal); - - if (ctemp != -1.0) { - temp->setDefault (temp->getAddMode() ? 0 : (int)ctemp); - green->setDefault (green->getAddMode() ? 0 : cgreen); - } else { - // 6504 & 1.0 = same values as in ProcParams::setDefaults - temp->setDefault (temp->getAddMode() ? 0 : 6504); - green->setDefault (green->getAddMode() ? 0 : 1.0); - } } else { temp->setDefault (defParams->wb.temperature); green->setDefault (defParams->wb.green); } + // Recomputing AutoWB if it's the current method will happen in improccoordinator.cc if (pedited) { temp->setDefaultEditedState (pedited->wb.temperature ? Edited : UnEdited); @@ -887,8 +823,11 @@ inline Gtk::TreeRow WhiteBalance::getActiveMethod () void WhiteBalance::WBChanged(double temperature, double greenVal) { + GThreadLock lock; disableListener(); temp->setValue(temperature); green->setValue(greenVal); + temp->setDefault(temperature); + green->setDefault(greenVal); enableListener(); } From ed71a7eb4eb07c68e5bfe4a824888286f5251f6c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 16 Feb 2017 12:15:17 +0100 Subject: [PATCH 091/181] improved UI behaviour of "AWB temperature bias" --- rtgui/whitebalance.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index d4390046e..0c716647e 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -369,6 +369,8 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) if (!ppMethod || (ppMethod->ppLabel != wbCustom->ppLabel && !((a == equal || a == tempBias) && ppMethod->type == WBT_AUTO)) ) { methconn.block(true); opt = setActiveMethod(wbCustom->GUILabel); + tempBias->set_sensitive(false); + cache_customWB (tVal, gVal); if (a != equal) { cache_customEqual(eVal); @@ -430,10 +432,13 @@ void WhiteBalance::optChanged () temp->setEditedState (UnEdited); green->setEditedState (UnEdited); equal->setEditedState (UnEdited); + tempBias->setEditedState (UnEdited); } else { int methodId = findWBEntryId (row[methodColumns.colLabel], WBLT_GUI); WBEntry* currMethod = WBParams::wbEntries[methodId]; + tempBias->set_sensitive(currMethod->type == WBT_AUTO); + switch (currMethod->type) { case WBT_CAMERA: if (wbp) { @@ -442,13 +447,11 @@ void WhiteBalance::optChanged () temp->setValue (temp->getAddMode() ? 0.0 : (int)ctemp); green->setValue (green->getAddMode() ? 0.0 : cgreen); equal->setValue (equal->getAddMode() ? 0.0 : 1.0); - tempBias->setValue (tempBias->getAddMode() ? 0.0 : 0.0); if (batchMode) { temp->setEditedState (UnEdited); green->setEditedState (UnEdited); equal->setEditedState (UnEdited); - tempBias->setEditedState (UnEdited); } } @@ -472,19 +475,16 @@ void WhiteBalance::optChanged () temp->setValue (temp->getAddMode() ? 0.0 : custom_temp); green->setValue (green->getAddMode() ? 0.0 : custom_green); equal->setValue (equal->getAddMode() ? 0.0 : custom_equal); - tempBias->setValue (tempBias->getAddMode() ? 0.0 : custom_tempBias); } else { cache_customTemp (temp->getValue()); cache_customGreen (green->getValue()); cache_customEqual (equal->getValue()); - cache_customTempBias (tempBias->getValue()); } if (batchMode) { temp->setEditedState (Edited); green->setEditedState (Edited); equal->setEditedState (Edited); - tempBias->setEditedState (Edited); } break; @@ -503,13 +503,11 @@ void WhiteBalance::optChanged () temp->setValue ( temp->getAddMode() ? 0.0 : (double)(currMethod->temperature)); green->setValue (green->getAddMode() ? 0.0 : (double)(currMethod->green)); equal->setValue (equal->getAddMode() ? 0.0 : (double)(currMethod->equal)); - tempBias->setValue (tempBias->getAddMode() ? 0.0 : (double)(currMethod->tempBias)); if (batchMode) { temp->setEditedState (Edited); green->setEditedState (Edited); equal->setEditedState (Edited); - tempBias->setEditedState (Edited); } break; @@ -546,6 +544,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) methconn.block (true); equal->setValue (pp->wb.equal); tempBias->setValue (pp->wb.tempBias); + tempBias->set_sensitive(true); if (pedited) { // By default, temperature and green are said "UnEdited", but it may change later @@ -598,13 +597,12 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) // Set the camera's green value, or 0.0 if in ADD mode green->setValue (green->getAddMode() ? 0.0 : cgreen); equal->setValue (equal->getAddMode() ? 0.0 : 1.); - tempBias->setValue (tempBias->getAddMode() ? 0.0 : 0.0); } else { temp->setValue (temp->getAddMode() ? 0.0 : pp->wb.temperature); green->setValue (green->getAddMode() ? 0.0 : pp->wb.green); equal->setValue (equal->getAddMode() ? 0.0 : pp->wb.equal); - tempBias->setValue (equal->getAddMode() ? 0.0 : pp->wb.tempBias); } + tempBias->setValue (equal->getAddMode() ? 0.0 : pp->wb.tempBias); } break; @@ -656,6 +654,8 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) //cache_customGreen (pp->wb.green); break; } + + tempBias->set_sensitive(wbValues->type == WBT_AUTO); } methconn.block (false); From ea0bf123474f5cf05893b785d33ca8d7b7a1a384 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 16 Feb 2017 14:50:22 +0100 Subject: [PATCH 092/181] added blue and yellow dots to the "AWB temp bias" adjuster --- rtgui/whitebalance.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index 0c716647e..61d320833 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -310,11 +310,13 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB Gtk::Image* igreenR = Gtk::manage (new RTImage ("ajd-wb-green2.png")); Gtk::Image* iblueredL = Gtk::manage (new RTImage ("ajd-wb-bluered1.png")); Gtk::Image* iblueredR = Gtk::manage (new RTImage ("ajd-wb-bluered2.png")); + Gtk::Image* itempbiasL = Gtk::manage (new RTImage ("ajd-wb-temp1.png")); + Gtk::Image* itempbiasR = Gtk::manage (new RTImage ("ajd-wb-temp2.png")); temp = Gtk::manage (new Adjuster (M("TP_WBALANCE_TEMPERATURE"), MINTEMP, MAXTEMP, 5, CENTERTEMP, itempL, itempR, &wbSlider2Temp, &wbTemp2Slider)); green = Gtk::manage (new Adjuster (M("TP_WBALANCE_GREEN"), MINGREEN, MAXGREEN, 0.001, 1.0, igreenL, igreenR)); equal = Gtk::manage (new Adjuster (M("TP_WBALANCE_EQBLUERED"), MINEQUAL, MAXEQUAL, 0.001, 1.0, iblueredL, iblueredR)); - tempBias = Gtk::manage (new Adjuster(M("TP_WBALANCE_TEMPBIAS"), -0.5, 0.5, 0.01, 0.0)); + tempBias = Gtk::manage (new Adjuster(M("TP_WBALANCE_TEMPBIAS"), -0.5, 0.5, 0.01, 0.0, itempbiasL, itempbiasR)); cache_customTemp (0); cache_customGreen (0); cache_customEqual (0); From 1b30493bd71545e89229f734c06f460dbc55e8bc Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 16 Feb 2017 16:21:38 +0100 Subject: [PATCH 093/181] removed unused variable custom_tempBias --- rtgui/whitebalance.cc | 14 -------------- rtgui/whitebalance.h | 2 -- 2 files changed, 16 deletions(-) diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index 61d320833..72090877e 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -232,7 +232,6 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB custom_green = 1.0; custom_equal = 1.0; - custom_tempBias = 0.0; } //Add the model columns to the Combo (which is a kind of view), @@ -320,7 +319,6 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, "whitebalance", M("TP_WB cache_customTemp (0); cache_customGreen (0); cache_customEqual (0); - cache_customTempBias (0); equal->set_tooltip_markup (M("TP_WBALANCE_EQBLUERED_TOOLTIP")); tempBias->set_tooltip_markup (M("TP_WBALANCE_TEMPBIAS_TOOLTIP")); temp->show (); @@ -377,9 +375,6 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) if (a != equal) { cache_customEqual(eVal); } - if (a != tempBias) { - cache_customTempBias(tempBiasVal); - } methconn.block(false); } @@ -390,8 +385,6 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) cache_customGreen (gVal); } else if (a == equal) { cache_customEqual (eVal); - } else if (a == tempBias) { - cache_customTempBias (tempBiasVal); } // Recomputing AutoWB if it's the current method will happen in improccoordinator.cc @@ -577,7 +570,6 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) cache_customTemp (pp->wb.temperature); cache_customGreen (pp->wb.green); cache_customEqual (pp->wb.equal); - cache_customTempBias (pp->wb.tempBias); if (pedited) { // The user may have changed the temperature and green value @@ -754,7 +746,6 @@ void WhiteBalance::setWB (int vtemp, double vgreen) opt = setActiveMethod(wbValues->GUILabel); cache_customWB (vtemp, vgreen); // sequence in which this call is made is important; must be before "method->set_active (2);" cache_customEqual(equal->getValue()); - cache_customTempBias(tempBias->getValue()); temp->setEditedState (Edited); green->setEditedState (Edited); methconn.block(false); @@ -796,11 +787,6 @@ void WhiteBalance::cache_customEqual(double equal) custom_equal = equal; } -void WhiteBalance::cache_customTempBias(double tempBias) -{ - custom_tempBias = tempBias; -} - void WhiteBalance::cache_customWB(int temp, double green) { cache_customTemp (temp); diff --git a/rtgui/whitebalance.h b/rtgui/whitebalance.h index fb3a443a1..1d493c035 100644 --- a/rtgui/whitebalance.h +++ b/rtgui/whitebalance.h @@ -77,12 +77,10 @@ protected: int custom_temp; double custom_green; double custom_equal; - double custom_tempBias; void cache_customWB (int temp, double green); //cache custom WB setting to allow its recall void cache_customTemp (int temp); //cache Temperature only to allow its recall void cache_customGreen (double green); //cache Green only to allow its recall void cache_customEqual (double equal); //cache Equal only to allow its recall - void cache_customTempBias (double tempBias); //cache TempBias only to allow its recall int setActiveMethod (Glib::ustring label); int _setActiveMethod (Glib::ustring &label, Gtk::TreeModel::Children &children); From e66cc8f3fa529472ffad6d4e0b04d96645f0682e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 16 Feb 2017 18:38:57 +0100 Subject: [PATCH 094/181] use the right precision (2 digits) for the history entry of AWB temp bias --- rtgui/whitebalance.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc index 72090877e..362d03a8d 100644 --- a/rtgui/whitebalance.cc +++ b/rtgui/whitebalance.cc @@ -397,7 +397,7 @@ void WhiteBalance::adjusterChanged (Adjuster* a, double newval) } else if (a == equal) { listener->panelChanged (EvWBequal, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(3), a->getValue())); } else if (a == tempBias) { - listener->panelChanged (EvWBtempBias, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(3), a->getValue())); + listener->panelChanged (EvWBtempBias, Glib::ustring::format (std::setw(4), std::fixed, std::setprecision(2), a->getValue())); } } } From dfdcacfa52c63149811b679ed156cefdb9a3408a Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 17 Feb 2017 08:36:23 +0100 Subject: [PATCH 095/181] updated AUTHORS.txt (as suggested by heckflosse) --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 6aebfcf6e..3dc820555 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -15,6 +15,7 @@ Developement contributors, in last name alphabetical order: Flössie Jean-Christophe Frisch Ilias Giarimis + Alberto Griggio Steve Herrell Philippe Hupé Wolfgang Kuehnel From 7398a7e607be0bbb308b0f3576c313da7754932b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 17 Feb 2017 09:05:00 +0100 Subject: [PATCH 096/181] re-enabled loading of lossy DNG files --- rtengine/dcraw.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 3b6d98133..6fb2ce8c1 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -9,6 +9,7 @@ /*RT*/#define NO_JASPER /*RT*/#define LOCALTIME /*RT*/#define DJGPP +/*RT*/#include #include "opthelper.h" @@ -2604,7 +2605,7 @@ void CLASS kodak_radc_load_raw() #ifdef NO_JPEG void CLASS kodak_jpeg_load_raw() {} -void CLASS lossy_dng_load_raw() {} +// RT void CLASS lossy_dng_load_raw() {} #else METHODDEF(boolean) @@ -2661,6 +2662,7 @@ void CLASS kodak_jpeg_load_raw() } void CLASS gamma_curve (double pwr, double ts, int mode, int imax); +/*RT*/#endif void CLASS lossy_dng_load_raw() { @@ -2704,7 +2706,8 @@ void CLASS lossy_dng_load_raw() fseek (ifp, save+=4, SEEK_SET); if (tile_length < INT_MAX) fseek (ifp, get4(), SEEK_SET); - jpeg_stdio_src (&cinfo, ifp); + /*RT jpeg_stdio_src (&cinfo, ifp); */ + /*RT*/jpeg_mem_src(&cinfo, fdata(ftell(ifp), ifp), ifp->size - ftell(ifp)); jpeg_read_header (&cinfo, TRUE); jpeg_start_decompress (&cinfo); buf = (*cinfo.mem->alloc_sarray) @@ -2724,7 +2727,7 @@ void CLASS lossy_dng_load_raw() jpeg_destroy_decompress (&cinfo); maximum = 0xffff; } -#endif +// RT #endif void CLASS kodak_dc120_load_raw() { @@ -9525,8 +9528,8 @@ dng_skip: } #endif #ifdef NO_JPEG - if (load_raw == &CLASS kodak_jpeg_load_raw || - load_raw == &CLASS lossy_dng_load_raw) { + if (load_raw == &CLASS kodak_jpeg_load_raw /* RT || + load_raw == &CLASS lossy_dng_load_raw*/) { fprintf (stderr,_("%s: You must link dcraw with %s!!\n"), ifname, "libjpeg"); is_raw = 0; From 4994fb6d9d0a28a754604966bdef81241c95e21a Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Fri, 17 Feb 2017 21:09:39 +0100 Subject: [PATCH 097/181] Updated TooWaBlue theme to v2.44, #3696 --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 201 ++++++++++------------ rtdata/themes/TooWaBlue-GTK3-20_.css | 201 ++++++++++------------ 2 files changed, 190 insertions(+), 212 deletions(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index 43d9a3035..9472f4b3d 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.42 - requires RT 5.0 + Version 2.44 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,7 +105,7 @@ messagedialog { } tooltip { background-color: @bg-tooltip; - border: 1px solid @border-tooltip; + border: 0.08334em solid @border-tooltip; border-radius: 0.33334em; padding: 0; margin: 0; @@ -180,7 +180,7 @@ frame > border { dialog frame > border { padding: 0.5em; border-radius: 0; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; background-color: transparent; margin: 0 -0.5em; } @@ -241,11 +241,11 @@ textview:selected, treeview:selected { } #RightNotebook > stack > :nth-child(3) treeview { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; border-bottom: none; } -#PlacesPaned > :nth-child(1) treeview { +#PlacesPaned > box:nth-child(1) treeview { padding: 0.08334em 0 0.08334em 0.5em; -gtk-icon-style: symbolic; } @@ -275,15 +275,15 @@ textview:selected, treeview:selected { margin-bottom: 0.41667em; } -#PlacesPaned > box:nth-child(3) > :nth-child(2), +#PlacesPaned > box:nth-child(3) > box:nth-child(2), #PlacesPaned > box:nth-child(1) > :nth-child(1), #HistoryPanel > border, #Snapshots > box > :nth-child(1) { - padding: 1px; background-color: @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; } -/*Corrects the space for the snapshot view of the paned separator*/ +/*Corrects the space of the snapshot view to the paned separator*/ #Snapshots { margin-top: -0.33334em; } @@ -321,11 +321,11 @@ filechooser box { } filechooser > box > paned > box { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; background-color: @bg-dark-grey; } filechooser placessidebar { - padding: 0 1px; + padding: 0 0.08334em; background-color: @bg-dark-grey; } @@ -362,7 +362,7 @@ filechooser list row:selected { } #HistogramArea, #HistogramRGBArea { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; background-color: @bg-dark-grey; } @@ -372,7 +372,7 @@ filechooser list row:selected { margin: 0; border-color: @bg-light-grey; border-style: solid; - border-width: 0 0 0 1px; + border-width: 0 0 0 0.08334em; background-color: @bg-dark-grey; background-image: none; box-shadow: none; @@ -385,7 +385,7 @@ filechooser list row:selected { } #EditorLeftPaned #fullButton, #EditorLeftPaned #histButton { - border-width: 0 1px 0 0; + border-width: 0 0.08334em 0 0; } /*** end ***************************************************************************************/ @@ -467,8 +467,8 @@ menu separator { /*** PartialPaste ******************************************************************************/ #PartialPaste { - border-bottom: 1px solid @border-color; - border-top: 1px solid @border-color; + border-bottom: 0.08334em solid @border-color; + border-top: 0.08334em solid @border-color; padding-top: 0.5em; padding-bottom: 0.5em; } @@ -518,10 +518,10 @@ scrollbar:not(.overlay-indicator) { background-color: rgba(0,0,0,.30); } scrollbar:not(.overlay-indicator).horizontal { - border-width: 0 1px 1px 1px; + border-width: 0 0.08334em 0.08334em 0.08334em; } scrollbar:not(.overlay-indicator).vertical { - border-width: 1px 1px 1px 0; + border-width: 0.08334em 0.08334em 0.08334em 0; } scrollbar:not(.overlay-indicator) slider { background-color: shade(@text-color, .9); @@ -593,7 +593,7 @@ scale slider { margin: calc(-0.33334em - 1px); border-radius: 0.83334em; background-image: linear-gradient(to bottom, shade (@accent-color4,1.15), shade (@accent-color4,.85)); - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; box-shadow: none; } scale slider:hover { @@ -603,14 +603,14 @@ scale slider:hover { scale trough { margin: 0.5em; /* has to be half of "scale slider / min-width min-height*/ background-color: @bg-scale-entry; - border-color: @bg-dark-grey; - box-shadow: inset 0 1px rgba(255, 255, 255, 0.11), 0 1px rgba(242, 242, 242, 0.11); + border: 0.08334em solid @bg-dark-grey; + box-shadow: inset 0 0.08334em rgba(255, 255, 255, 0.11), 0 0.08334em rgba(242, 242, 242, 0.11); border-radius: 0.5em; } scale:not(:disabled) trough highlight { background-color: @accent-color2; - border-color: @bg-dark-grey; - box-shadow: inset 0 1px shade(@accent-color2, 1.3); + border: 0.08334em solid @bg-dark-grey; + box-shadow: inset 0 0.08334em shade(@accent-color2, 1.3); border-radius: 0.5em; } @@ -671,7 +671,7 @@ progressbar.horizontal trough progress { #IopsPanel progressbar.horizontal trough { min-height: 0.5em; background-color: @bg-scale-entry; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; margin-top: 0.25em; } #IopsPanel progressbar.horizontal trough progress { @@ -770,8 +770,9 @@ dialog notebook stack { #MainNotebook > stack { padding: 0.41667em; } -#MainNotebook > stack > :nth-child(2) > :nth-child(2) { - margin-bottom: 0.33334em; + +#MainNotebook > stack > :nth-child(2) > box:nth-child(3) { + margin-top: 0.41667em; } @@ -801,10 +802,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { background-color: @bg-grey; padding: 0; } -#RightNotebook > stack > :nth-child(3), -#RightNotebook > stack > :nth-child(4) { + +#RightNotebook > stack > :nth-child(3) > * > box, +#RightNotebook > stack > :nth-child(4) > * > box { padding: 0.5em; - border: 1px solid @bg-entry-border; + border: 0.08334em solid @bg-entry-border; } #PrefNotebook header { @@ -813,8 +815,9 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #AboutNotebook header { margin: -0.66667em -0.66667em 0.66667em; } -#AboutNotebook stack > * > * > * { - background-color: @dark-grey; + +#AboutNotebook stack text { + background-color: @bg-dark-grey; } /* All tool panels have a frame except for Meta which unlike the rest is a notebook itself. @@ -873,7 +876,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin: 0; } #MetaPanelNotebook .view { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; padding: 0.16667em; margin: 0; } @@ -894,8 +897,8 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 1.66667em; } #MetaPanelNotebook > stack > box > grid > button { - margin-top: 1px; - margin-bottom: 1px; + margin-top: 0.08334em; + margin-bottom: 0.08334em; min-height: 2.16667em; } @@ -908,7 +911,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /*** File Browser ******************************************************************************/ #FileCatalog { background-color: @bg-image; - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; } #FileCatalog:selected { background-color: @accent-color3; @@ -949,7 +952,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 1.66667em; margin: 0 0 0 -1.66667em; border-radius: 0 0.2em 0.2em 0; - border: 1px solid transparent; + border: 0.08334em solid transparent; padding: 0; } #ToolBarPanelFileBrowser entry, @@ -972,25 +975,17 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #BeforeAfterContainer { background-color: @bg-grey; - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; border-radius: 0; padding: 0; margin: 0.41667em 0; } -#BeforeAfterContainer > box:nth-child(1) frame { - background-color: @bg-image; - border-top: 1px solid @bg-dark-grey; - border-radius: 0; - padding: 0; - margin: -1px 0 0 0; +#BeforeAfterContainer > box:nth-child(2) > box:nth-child(2), +#BeforeAfterContainer > box:nth-child(1) > box:nth-child(2){ + border-top: 0.08334em solid @bg-dark-grey; } -#BeforeAfterContainer > box:nth-child(2) frame { - background-color: @bg-image; - border-top: 1px solid @bg-dark-grey; - border-left: 1px solid @bg-dark-grey; - border-radius: 0; - padding: 0; - margin: -1px 0 0 0; +#BeforeAfterContainer > box:nth-child(2){ + border-left: 0.08334em solid @bg-dark-grey; } #BeforeAfterContainer label { @@ -1000,8 +995,8 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /* Small Lock Button */ #BeforeAfterContainer button { min-height: 1.66667em; - min-width: 1.66667em; - margin: 0.25em 0 0.33334em; + min-width: 1.75em; + margin: 0.25em; padding: 0 0 0 0.08334em; } /**/ @@ -1034,7 +1029,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 0; margin: 0 0.16667em; padding: 0 0.16667em; - border: 1px solid transparent; + border: 0.08334em solid transparent; background-color: transparent; background-image: none; box-shadow: none; @@ -1042,13 +1037,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #EditorTopPanel > box > box > button:hover { background-color: transparent; background-image: none; - border: 1px solid transparent; + border: 0.08334em solid transparent; box-shadow: none; } #EditorTopPanel > box > box > button:checked { background-color: transparent; background-image: none; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; box-shadow: none; } @@ -1077,13 +1072,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { } /**/ #MyExpander .drawingarea:not(.slider) { - border: 1px solid @bg-light-grey; + border: 0.08334em solid @bg-light-grey; } #MyExpander .slider, #MyExpander .drawingarea:nth-child(2) { background-image: linear-gradient(to bottom, shade (@accent-color4,1.15), shade (@accent-color4,.85)); background-color: @accent-color4; - border: 1px solid rgb(15,15,15); + border: 0.08334em solid rgb(15,15,15); } #MyExpander .drawingarea:disabled { background-color: shade(@bg-grey,.85); @@ -1114,7 +1109,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /* Sub-tool (MyExpander) */ #ExpanderBox2 > box, #ExpanderBox2 > grid { background-color: transparent; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; border-radius: 0; margin: 0; padding: 0.5em; @@ -1143,13 +1138,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { border: none; padding: 0; margin: 0; - box-shadow: 0 3px 9px 1px rgba(0, 0, 0, 0.50), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.50), 0 0 0 0.08334em @bg-dark-grey; } menu { background-color: @bg-dark-grey; - border: 1px solid @accent-color; - padding: 1px; + border: 0.08334em solid @accent-color; + padding: 0.08334em; margin: 0; } menu > .top, @@ -1159,12 +1154,12 @@ menu > .bottom:hover { background-color: transparent; border: none; padding: 0.5em; - min-height: 2em; + min-height: 1.5em; } menuitem { padding: 0 0.33334em; - margin: 1px; + margin: 0.08334em; min-height: 2em; } menuitem:hover { @@ -1187,11 +1182,11 @@ entry > window > frame { } entry > window > frame > border { background-color: @bg-dark-grey; - padding: 1px; - border: 1px solid @accent-color; + padding: 0.08334em; + border: 0.08334em solid @accent-color; } .csd entry > window > frame > border { - margin: 1px; + margin: 0.08334em; } /* end */ @@ -1203,7 +1198,7 @@ entry > window > frame > border { } popover.background { background-color: @bg-dark-grey; - border-color: @accent-color; + border: 0.08334em solid @accent-color; border-radius: 0; padding: 0; margin: 0; @@ -1236,9 +1231,9 @@ button { margin: 0; padding: 0; /* x */ border-radius: 0.2em; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; background-color: transparent; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.1); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); } button.flat { @@ -1250,7 +1245,7 @@ button.text-button label { #MainNotebook > header > grid > button, button.flat { - border: 1px solid transparent; + border: 0.08334em solid transparent; box-shadow: none; background-image: none; background-color: transparent; @@ -1301,7 +1296,7 @@ scale + button.flat { button.flat:hover, button:hover { border-color: @bg-button-border; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.1); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: @bg-button-hover; } @@ -1313,7 +1308,7 @@ button.flat:checked, button:active, button:checked { border-color: @bg-button-border; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.08); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.08); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: @bg-button-active; } @@ -1322,7 +1317,7 @@ button:checked { button.Right, button.MiddleH { margin-left: 0.16667em; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; } /**/ @@ -1364,9 +1359,10 @@ dialog combobox .combo, #MyExpander button:not(.flat).Left, #MyExpander button:not(.flat) + combobox, #MyExpander combobox + button:not(.flat), -#MyExpander combobox + combobox, +#MyExpander combobox + combobox +/* Crash #MyExpander button + label, -#MyExpander combobox + label { +#MyExpander combobox + label */ { margin-left: 0.16667em; } #MyExpander label + filechooserbutton, @@ -1453,7 +1449,7 @@ window .view button { background-image: none; box-shadow: none; min-height: 2em; - min-width: 1.33334em; + min-width: 1.33334em; padding: 0 0.33334em; } dialog .view button.text-button label, @@ -1462,20 +1458,21 @@ window .view button.text-button label { } window .view button { border: none; - border-bottom: 1px solid @border-color; + border-bottom: 0.08334em solid @border-color; } dialog .view button { - border-color: @border-color; + border: 0.08334em solid @border-color; } -.view button:checked label, .view button:checked, -.view button:hover:not(:active) label, .view button:hover:not(:active) { - color: @headline-hl; background-image: none; background-color: @bg-list-hover; } +.view button:checked label, +.view button:hover:not(:active) label { + color: @headline-hl; +} dialog .view header button:not(:last-child):not(:only-child), window .view header button:not(:last-child):not(:only-child), @@ -1517,7 +1514,7 @@ window .view header button, popover button.text-button { background-color: @bg-dark-grey; background-image: none; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; box-shadow: none; background-image: none; margin: 0; @@ -1559,7 +1556,7 @@ headerbar button.titlebutton image { headerbar button.titlebutton { margin: 0 0 0 0.33334em; background-image: none; - border: 1px solid transparent; + border: 0.08334em solid transparent; background-color: transparent; box-shadow: none; min-width: 1.55em; @@ -1582,14 +1579,14 @@ messagedialog headerbar button.titlebutton { #MainNotebook tab #CloseButton:hover, headerbar button.titlebutton:hover{ border-color: rgba(0,0,0,.8); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.11); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.11); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: rgba(128, 128, 128,.20); } #MainNotebook > header > grid > button:active, headerbar button.titlebutton:active{ border-color: rgba(0,0,0,.8); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.15); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.15); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: rgba(128, 128, 128,.40); } @@ -1597,13 +1594,13 @@ headerbar button.titlebutton:active{ headerbar button.titlebutton.close:hover{ border-color: rgba(0,0,0,.8); background-image: linear-gradient(to bottom, rgb(180,0,0), rgb(160,0,0) 40%, rgb(130,0,0)); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.32); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.32); } #MainNotebook tab #CloseButton:active, headerbar button.titlebutton.close:active{ border-color: rgba(0,0,0,.8); background-image: linear-gradient(to bottom, rgb(215,0,0), rgb(185,0,0) 40%, rgb(150,0,0)); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.4); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.4); } /**/ @@ -1642,14 +1639,6 @@ checkbutton label { check { border-radius: 0.16667em; } -check:checked { - padding: 0 2px 0 0; - min-width: calc(1.16667em - 2px); -} -check:indeterminate { - min-width: 1.16667em; - padding: 0; -} radio{ border-radius: 1.16667em; @@ -1676,7 +1665,7 @@ frame > checkbutton check{ } #PrefNotebook stack > box:nth-child(3) checkbutton, #PrefNotebook stack > box:nth-child(4) checkbutton { - min-height: 2em; + min-height: 1.83334em; } #PrefNotebook radiobutton { min-height: 2em; @@ -1696,8 +1685,8 @@ entry { min-height: 1.66667em; min-width: 0; border-radius: 0.2em; - box-shadow: inset 1px 1px rgba(0, 0, 0, 0.08), 0 1px rgba(242, 242, 242, 0.1); - border: 1px solid @bg-entry-border; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; background-color: @bg-scale-entry; } @@ -1708,8 +1697,8 @@ spinbutton { min-width: 0; border-radius: 0.2em; background-color: @bg-scale-entry; - border: 1px solid @bg-entry-border; - box-shadow: inset 1px 1px rgba(0, 0, 0, 0.08), 0 1px rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); } #MyExpander spinbutton { @@ -1720,9 +1709,9 @@ spinbutton { border-top-left-radius: 1.83334em; border-bottom-left-radius: 1.83334em; background-color: shade(@bg-grey, 1.33); - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; color: @text-tbEntry; - box-shadow: inset 1px 1px rgba(0, 0, 0, .12), 0 1px rgba(255, 255, 255, 0.12); + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, .12), 0 0.08334em rgba(255, 255, 255, 0.12); } #MyExpander button + label + spinbutton { margin: 0.25em 0; /* Needed for Reset & and Auto button height*/ @@ -1808,7 +1797,7 @@ entry:focus > selection { .view entry { background-color: @bg-dark-grey; margin: 0 -2px; - border: 1px solid @accent-color; + border: 0.08334em solid @accent-color; box-shadow: none; } /* end*/ @@ -1822,14 +1811,14 @@ entry:focus > selection { border-radius: 0.41667em 0.41667em 0 0; border: none; padding: 0; - box-shadow: 0 0.25em 0.75em 1px rgba(0, 0, 0, 0.5), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.5), 0 0 0 0.08334em @bg-dark-grey; margin: 0.83334em; } headerbar { background-color: shade(@winHeaderbar,1.12); - box-shadow: inset 0 1px rgba(200,200,200,.13); + box-shadow: inset 0 0.08334em rgba(200,200,200,.13); background-image: linear-gradient(shade(@winHeaderbar,1.14), shade(@winHeaderbar,.86)); - border-bottom: 1px solid @bg-dark-grey; + border-bottom: 0.08334em solid @bg-dark-grey; border-radius: 0.41667em 0.41667em 0 0; min-height: 2.16667em; padding: 0.08334em 0.41667em 0; @@ -1851,7 +1840,7 @@ headerbar .title{ /* Window in background */ :not(.popup):not(tooltip) > decoration:backdrop { - box-shadow: 0 0.25em 0.75em 1px rgba(0, 0, 0, 0.3), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.3), 0 0 0 0.08334em @bg-dark-grey; } headerbar:backdrop { box-shadow: none; diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index e1a7e84c2..679a8ed13 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.42 - requires RT 5.0 + Version 2.44 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,7 +105,7 @@ messagedialog { } tooltip { background-color: @bg-tooltip; - border: 1px solid @border-tooltip; + border: 0.08334em solid @border-tooltip; border-radius: 0.33334em; padding: 0; margin: 0; @@ -180,7 +180,7 @@ frame > border { dialog frame > border { padding: 0.5em; border-radius: 0; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; background-color: transparent; margin: 0 -0.5em; } @@ -241,11 +241,11 @@ textview:selected, treeview:selected { } #RightNotebook > stack > :nth-child(3) treeview { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; border-bottom: none; } -#PlacesPaned > :nth-child(1) treeview { +#PlacesPaned > box:nth-child(1) treeview { padding: 0.08334em 0 0.08334em 0.5em; -gtk-icon-style: symbolic; } @@ -275,15 +275,15 @@ textview:selected, treeview:selected { margin-bottom: 0.41667em; } -#PlacesPaned > box:nth-child(3) > :nth-child(2), +#PlacesPaned > box:nth-child(3) > box:nth-child(2), #PlacesPaned > box:nth-child(1) > :nth-child(1), #HistoryPanel > border, #Snapshots > box > :nth-child(1) { - padding: 1px; background-color: @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; } -/*Corrects the space for the snapshot view of the paned separator*/ +/*Corrects the space of the snapshot view to the paned separator*/ #Snapshots { margin-top: -0.33334em; } @@ -321,11 +321,11 @@ filechooser box { } filechooser > box > paned > box { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; background-color: @bg-dark-grey; } filechooser placessidebar { - padding: 0 1px; + padding: 0 0.08334em; background-color: @bg-dark-grey; } @@ -362,7 +362,7 @@ filechooser list row:selected { } #HistogramArea, #HistogramRGBArea { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; background-color: @bg-dark-grey; } @@ -372,7 +372,7 @@ filechooser list row:selected { margin: 0; border-color: @bg-light-grey; border-style: solid; - border-width: 0 0 0 1px; + border-width: 0 0 0 0.08334em; background-color: @bg-dark-grey; background-image: none; box-shadow: none; @@ -385,7 +385,7 @@ filechooser list row:selected { } #EditorLeftPaned #fullButton, #EditorLeftPaned #histButton { - border-width: 0 1px 0 0; + border-width: 0 0.08334em 0 0; } /*** end ***************************************************************************************/ @@ -467,8 +467,8 @@ menu separator { /*** PartialPaste ******************************************************************************/ #PartialPaste { - border-bottom: 1px solid @border-color; - border-top: 1px solid @border-color; + border-bottom: 0.08334em solid @border-color; + border-top: 0.08334em solid @border-color; padding-top: 0.5em; padding-bottom: 0.5em; } @@ -518,10 +518,10 @@ scrollbar:not(.overlay-indicator) { background-color: rgba(0,0,0,.30); } scrollbar:not(.overlay-indicator).horizontal { - border-width: 0 1px 1px 1px; + border-width: 0 0.08334em 0.08334em 0.08334em; } scrollbar:not(.overlay-indicator).vertical { - border-width: 1px 1px 1px 0; + border-width: 0.08334em 0.08334em 0.08334em 0; } scrollbar:not(.overlay-indicator) slider { background-color: shade(@text-color, .9); @@ -593,7 +593,7 @@ scale slider { margin: calc(-0.33334em - 1px); border-radius: 0.83334em; background-image: linear-gradient(to bottom, shade (@accent-color4,1.15), shade (@accent-color4,.85)); - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; box-shadow: none; } scale slider:hover { @@ -603,14 +603,14 @@ scale slider:hover { scale trough { margin: 0.5em; /* has to be half of "scale slider / min-width min-height*/ background-color: @bg-scale-entry; - border-color: @bg-dark-grey; - box-shadow: inset 0 1px rgba(255, 255, 255, 0.11), 0 1px rgba(242, 242, 242, 0.11); + border: 0.08334em solid @bg-dark-grey; + box-shadow: inset 0 0.08334em rgba(255, 255, 255, 0.11), 0 0.08334em rgba(242, 242, 242, 0.11); border-radius: 0.5em; } scale:not(:disabled) trough highlight { background-color: @accent-color2; - border-color: @bg-dark-grey; - box-shadow: inset 0 1px shade(@accent-color2, 1.3); + border: 0.08334em solid @bg-dark-grey; + box-shadow: inset 0 0.08334em shade(@accent-color2, 1.3); border-radius: 0.5em; } @@ -671,7 +671,7 @@ progressbar.horizontal trough progress { #IopsPanel progressbar.horizontal trough { min-height: 0.5em; background-color: @bg-scale-entry; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; margin-top: 0.25em; } #IopsPanel progressbar.horizontal trough progress { @@ -770,8 +770,9 @@ dialog notebook stack { #MainNotebook > stack { padding: 0.41667em; } -#MainNotebook > stack > :nth-child(2) > :nth-child(2) { - margin-bottom: 0.33334em; + +#MainNotebook > stack > :nth-child(2) > box:nth-child(3) { + margin-top: 0.41667em; } @@ -801,10 +802,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { background-color: @bg-grey; padding: 0; } -#RightNotebook > stack > :nth-child(3), -#RightNotebook > stack > :nth-child(4) { + +#RightNotebook > stack > :nth-child(3) > * > box, +#RightNotebook > stack > :nth-child(4) > * > box { padding: 0.5em; - border: 1px solid @bg-entry-border; + border: 0.08334em solid @bg-entry-border; } #PrefNotebook header { @@ -813,8 +815,9 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #AboutNotebook header { margin: -0.66667em -0.66667em 0.66667em; } -#AboutNotebook stack > * > * > * { - background-color: @dark-grey; + +#AboutNotebook stack text { + background-color: @bg-dark-grey; } /* All tool panels have a frame except for Meta which unlike the rest is a notebook itself. @@ -873,7 +876,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin: 0; } #MetaPanelNotebook .view { - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; padding: 0.16667em; margin: 0; } @@ -894,8 +897,8 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 1.66667em; } #MetaPanelNotebook > stack > box > grid > button { - margin-top: 1px; - margin-bottom: 1px; + margin-top: 0.08334em; + margin-bottom: 0.08334em; min-height: 2.16667em; } @@ -908,7 +911,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /*** File Browser ******************************************************************************/ #FileCatalog { background-color: @bg-image; - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; } #FileCatalog:selected { background-color: @accent-color3; @@ -949,7 +952,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 1.66667em; margin: 0 0 0 -1.66667em; border-radius: 0 0.2em 0.2em 0; - border: 1px solid transparent; + border: 0.08334em solid transparent; padding: 0; } #ToolBarPanelFileBrowser entry, @@ -972,25 +975,17 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #BeforeAfterContainer { background-color: @bg-grey; - border: 1px solid @bg-dark-grey; + border: 0.08334em solid @bg-dark-grey; border-radius: 0; padding: 0; margin: 0.41667em 0; } -#BeforeAfterContainer > box:nth-child(1) frame { - background-color: @bg-image; - border-top: 1px solid @bg-dark-grey; - border-radius: 0; - padding: 0; - margin: -1px 0 0 0; +#BeforeAfterContainer > box:nth-child(2) > box:nth-child(2), +#BeforeAfterContainer > box:nth-child(1) > box:nth-child(2){ + border-top: 0.08334em solid @bg-dark-grey; } -#BeforeAfterContainer > box:nth-child(2) frame { - background-color: @bg-image; - border-top: 1px solid @bg-dark-grey; - border-left: 1px solid @bg-dark-grey; - border-radius: 0; - padding: 0; - margin: -1px 0 0 0; +#BeforeAfterContainer > box:nth-child(2){ + border-left: 0.08334em solid @bg-dark-grey; } #BeforeAfterContainer label { @@ -1000,8 +995,8 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /* Small Lock Button */ #BeforeAfterContainer button { min-height: 1.66667em; - min-width: 1.66667em; - margin: 0.25em 0 0.33334em; + min-width: 1.75em; + margin: 0.25em; padding: 0 0 0 0.08334em; } /**/ @@ -1034,7 +1029,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { min-width: 0; margin: 0 0.16667em; padding: 0 0.16667em; - border: 1px solid transparent; + border: 0.08334em solid transparent; background-color: transparent; background-image: none; box-shadow: none; @@ -1042,13 +1037,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { #EditorTopPanel > box > box > button:hover { background-color: transparent; background-image: none; - border: 1px solid transparent; + border: 0.08334em solid transparent; box-shadow: none; } #EditorTopPanel > box > box > button:checked { background-color: transparent; background-image: none; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; box-shadow: none; } @@ -1077,13 +1072,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { } /**/ #MyExpander .drawingarea:not(.slider) { - border: 1px solid @bg-light-grey; + border: 0.08334em solid @bg-light-grey; } #MyExpander .slider, #MyExpander .drawingarea:nth-child(2) { background-image: linear-gradient(to bottom, shade (@accent-color4,1.15), shade (@accent-color4,.85)); background-color: @accent-color4; - border: 1px solid rgb(15,15,15); + border: 0.08334em solid rgb(15,15,15); } #MyExpander .drawingarea:disabled { background-color: shade(@bg-grey,.85); @@ -1114,7 +1109,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { /* Sub-tool (MyExpander) */ #ExpanderBox2 > box, #ExpanderBox2 > grid { background-color: transparent; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; border-radius: 0; margin: 0; padding: 0.5em; @@ -1143,13 +1138,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { border: none; padding: 0; margin: 0; - box-shadow: 0 3px 9px 1px rgba(0, 0, 0, 0.50), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.50), 0 0 0 0.08334em @bg-dark-grey; } menu { background-color: @bg-dark-grey; - border: 1px solid @accent-color; - padding: 1px; + border: 0.08334em solid @accent-color; + padding: 0.08334em; margin: 0; } menu > .top, @@ -1159,12 +1154,12 @@ menu > .bottom:hover { background-color: transparent; border: none; padding: 0.5em; - min-height: 2em; + min-height: 1.5em; } menuitem { padding: 0 0.33334em; - margin: 1px; + margin: 0.08334em; min-height: 2em; } menuitem:hover { @@ -1187,11 +1182,11 @@ entry > window > frame { } entry > window > frame > border { background-color: @bg-dark-grey; - padding: 1px; - border: 1px solid @accent-color; + padding: 0.08334em; + border: 0.08334em solid @accent-color; } .csd entry > window > frame > border { - margin: 1px; + margin: 0.08334em; } /* end */ @@ -1203,7 +1198,7 @@ entry > window > frame > border { } popover.background { background-color: @bg-dark-grey; - border-color: @accent-color; + border: 0.08334em solid @accent-color; border-radius: 0; padding: 0; margin: 0; @@ -1236,9 +1231,9 @@ button { margin: 0; padding: 0; /* x */ border-radius: 0.2em; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; background-color: transparent; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.1); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); } button.flat { @@ -1250,7 +1245,7 @@ button.text-button label { #MainNotebook > header > grid > button, button.flat { - border: 1px solid transparent; + border: 0.08334em solid transparent; box-shadow: none; background-image: none; background-color: transparent; @@ -1301,7 +1296,7 @@ scale + button.flat { button.flat:hover, button:hover { border-color: @bg-button-border; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.1); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: @bg-button-hover; } @@ -1313,7 +1308,7 @@ button.flat:checked, button:active, button:checked { border-color: @bg-button-border; - box-shadow: inset 0 1px rgba(242, 242, 242, 0.08); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.08); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: @bg-button-active; } @@ -1322,7 +1317,7 @@ button:checked { button.Right, button.MiddleH { margin-left: 0.16667em; - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; } /**/ @@ -1364,9 +1359,10 @@ dialog combobox .combo, #MyExpander button:not(.flat).Left, #MyExpander button:not(.flat) + combobox, #MyExpander combobox + button:not(.flat), -#MyExpander combobox + combobox, +#MyExpander combobox + combobox +/* Crash #MyExpander button + label, -#MyExpander combobox + label { +#MyExpander combobox + label */ { margin-left: 0.16667em; } #MyExpander label + filechooserbutton, @@ -1453,7 +1449,7 @@ window .view button { background-image: none; box-shadow: none; min-height: 2em; - min-width: 1.33334em; + min-width: 1.33334em; padding: 0 0.33334em; } dialog .view button.text-button label, @@ -1462,20 +1458,21 @@ window .view button.text-button label { } window .view button { border: none; - border-bottom: 1px solid @border-color; + border-bottom: 0.08334em solid @border-color; } dialog .view button { - border-color: @border-color; + border: 0.08334em solid @border-color; } -.view button:checked label, .view button:checked, -.view button:hover:not(:active) label, .view button:hover:not(:active) { - color: @headline-hl; background-image: none; background-color: @bg-list-hover; } +.view button:checked label, +.view button:hover:not(:active) label { + color: @headline-hl; +} dialog .view header button:not(:last-child):not(:only-child), window .view header button:not(:last-child):not(:only-child), @@ -1517,7 +1514,7 @@ window .view header button, popover button.text-button { background-color: @bg-dark-grey; background-image: none; - border: 1px solid @border-color; + border: 0.08334em solid @border-color; box-shadow: none; background-image: none; margin: 0; @@ -1559,7 +1556,7 @@ headerbar button.titlebutton image { headerbar button.titlebutton { margin: 0 0 0 0.33334em; background-image: none; - border: 1px solid transparent; + border: 0.08334em solid transparent; background-color: transparent; box-shadow: none; min-width: 1.55em; @@ -1582,14 +1579,14 @@ messagedialog headerbar button.titlebutton { #MainNotebook tab #CloseButton:hover, headerbar button.titlebutton:hover{ border-color: rgba(0,0,0,.8); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.11); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.11); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: rgba(128, 128, 128,.20); } #MainNotebook > header > grid > button:active, headerbar button.titlebutton:active{ border-color: rgba(0,0,0,.8); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.15); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.15); background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3)); background-color: rgba(128, 128, 128,.40); } @@ -1597,13 +1594,13 @@ headerbar button.titlebutton:active{ headerbar button.titlebutton.close:hover{ border-color: rgba(0,0,0,.8); background-image: linear-gradient(to bottom, rgb(180,0,0), rgb(160,0,0) 40%, rgb(130,0,0)); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.32); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.32); } #MainNotebook tab #CloseButton:active, headerbar button.titlebutton.close:active{ border-color: rgba(0,0,0,.8); background-image: linear-gradient(to bottom, rgb(215,0,0), rgb(185,0,0) 40%, rgb(150,0,0)); - box-shadow: inset 0 1px rgba(242, 242, 242, 0.4); + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.4); } /**/ @@ -1642,14 +1639,6 @@ checkbutton label { check { border-radius: 0.16667em; } -check:checked { - padding: 0 2px 0 0; - min-width: calc(1.16667em - 2px); -} -check:indeterminate { - min-width: 1.16667em; - padding: 0; -} radio{ border-radius: 1.16667em; @@ -1676,7 +1665,7 @@ frame > checkbutton check{ } #PrefNotebook stack > box:nth-child(3) checkbutton, #PrefNotebook stack > box:nth-child(4) checkbutton { - min-height: 2em; + min-height: 1.83334em; } #PrefNotebook radiobutton { min-height: 2em; @@ -1696,8 +1685,8 @@ entry { min-height: 1.66667em; min-width: 0; border-radius: 0.2em; - box-shadow: inset 1px 1px rgba(0, 0, 0, 0.08), 0 1px rgba(242, 242, 242, 0.1); - border: 1px solid @bg-entry-border; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; background-color: @bg-scale-entry; } @@ -1708,8 +1697,8 @@ spinbutton { min-width: 0; border-radius: 0.2em; background-color: @bg-scale-entry; - border: 1px solid @bg-entry-border; - box-shadow: inset 1px 1px rgba(0, 0, 0, 0.08), 0 1px rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); } #MyExpander spinbutton { @@ -1720,9 +1709,9 @@ spinbutton { border-top-left-radius: 1.83334em; border-bottom-left-radius: 1.83334em; background-color: shade(@bg-grey, 1.33); - border: 1px solid @bg-button-border; + border: 0.08334em solid @bg-button-border; color: @text-tbEntry; - box-shadow: inset 1px 1px rgba(0, 0, 0, .12), 0 1px rgba(255, 255, 255, 0.12); + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, .12), 0 0.08334em rgba(255, 255, 255, 0.12); } #MyExpander button + label + spinbutton { margin: 0.25em 0; /* Needed for Reset & and Auto button height*/ @@ -1808,7 +1797,7 @@ entry:focus > selection { .view entry { background-color: @bg-dark-grey; margin: 0 -2px; - border: 1px solid @accent-color; + border: 0.08334em solid @accent-color; box-shadow: none; } /* end*/ @@ -1822,14 +1811,14 @@ entry:focus > selection { border-radius: 0.41667em 0.41667em 0 0; border: none; padding: 0; - box-shadow: 0 0.25em 0.75em 1px rgba(0, 0, 0, 0.5), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.5), 0 0 0 0.08334em @bg-dark-grey; margin: 0.83334em; } headerbar { background-color: shade(@winHeaderbar,1.12); - box-shadow: inset 0 1px rgba(200,200,200,.13); + box-shadow: inset 0 0.08334em rgba(200,200,200,.13); background-image: linear-gradient(shade(@winHeaderbar,1.14), shade(@winHeaderbar,.86)); - border-bottom: 1px solid @bg-dark-grey; + border-bottom: 0.08334em solid @bg-dark-grey; border-radius: 0.41667em 0.41667em 0 0; min-height: 2.16667em; padding: 0.08334em 0.41667em 0; @@ -1851,7 +1840,7 @@ headerbar .title{ /* Window in background */ :not(.popup):not(tooltip) > decoration:backdrop { - box-shadow: 0 0.25em 0.75em 1px rgba(0, 0, 0, 0.3), 0 0 0 1px @bg-dark-grey; + box-shadow: 0 0.25em 0.75em 0.08334em rgba(0, 0, 0, 0.3), 0 0 0 0.08334em @bg-dark-grey; } headerbar:backdrop { box-shadow: none; From 8889d626bfb1c6bed1ade47724efb4939b1c3e83 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 17 Feb 2017 21:43:33 +0100 Subject: [PATCH 098/181] use RT's jpeg_memory_src instead of jpeg_mem_src from jpegib jpeg_mem_src was introduced in libjpeg8, so it might not be available --- rtengine/dcraw.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 6fb2ce8c1..3584b7908 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -9,7 +9,7 @@ /*RT*/#define NO_JASPER /*RT*/#define LOCALTIME /*RT*/#define DJGPP -/*RT*/#include +/*RT*/#include "jpeg.h" #include "opthelper.h" @@ -2707,7 +2707,7 @@ void CLASS lossy_dng_load_raw() if (tile_length < INT_MAX) fseek (ifp, get4(), SEEK_SET); /*RT jpeg_stdio_src (&cinfo, ifp); */ - /*RT*/jpeg_mem_src(&cinfo, fdata(ftell(ifp), ifp), ifp->size - ftell(ifp)); + /*RT*/jpeg_memory_src(&cinfo, fdata(ftell(ifp), ifp), ifp->size - ftell(ifp)); jpeg_read_header (&cinfo, TRUE); jpeg_start_decompress (&cinfo); buf = (*cinfo.mem->alloc_sarray) @@ -2727,7 +2727,7 @@ void CLASS lossy_dng_load_raw() jpeg_destroy_decompress (&cinfo); maximum = 0xffff; } -// RT #endif +/*RT #endif */ void CLASS kodak_dc120_load_raw() { From ae45e3f86edff638eae3ed9da054b98e32ed60f7 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 18 Feb 2017 13:55:43 +0100 Subject: [PATCH 099/181] Update Deutsch locale AWB temp bias --- rtdata/languages/Deutsch | 243 +++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 123 deletions(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 277b6782e..78896314b 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -33,6 +33,8 @@ #32 2016-12-29 Erweiterung/Korrekturen (TooWaBoo) RT4.2.1464 #33 2017-01-04 Erweiterung/Korrekturen/Soft-Proofing (TooWaBoo) RT4.2.1477 #34 2017-01-07 IPTC (TooWaBoo) RT4.2.1492 +#35 2017-02-18 AWB bias (TooWaBoo) + ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen @@ -150,13 +152,13 @@ FILEBROWSER_OPENDEFAULTVIEWER;Windows Standard-Betracher (stapelverarbeitet) FILEBROWSER_PARTIALPASTEPROFILE;Profil selektiv einfügen FILEBROWSER_PASTEPROFILE;Profil einfügen FILEBROWSER_POPUPCANCELJOB;Job abbrechen -FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOLORLABEL0;Markierung: Ohne FILEBROWSER_POPUPCOLORLABEL1;Markierung: Rot FILEBROWSER_POPUPCOLORLABEL2;Markierung: Gelb FILEBROWSER_POPUPCOLORLABEL3;Markierung: Grün FILEBROWSER_POPUPCOLORLABEL4;Markierung: Blau FILEBROWSER_POPUPCOLORLABEL5;Markierung: Violett +FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOPYTO;Kopieren nach... FILEBROWSER_POPUPFILEOPERATIONS;Dateioperationen FILEBROWSER_POPUPMOVEEND;An das Ende der Warteschlange verschieben @@ -167,13 +169,13 @@ FILEBROWSER_POPUPOPENINEDITOR;Im Editor öffnen FILEBROWSER_POPUPPROCESS;Zur Warteschlange hinzufügen FILEBROWSER_POPUPPROCESSFAST;Zur Warteschlange hinzufügen (Schnelles Exportieren) FILEBROWSER_POPUPPROFILEOPERATIONS;Profiloperationen -FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPRANK0;Nicht bewertet FILEBROWSER_POPUPRANK1;Bewertung 1 * FILEBROWSER_POPUPRANK2;Bewertung 2 ** FILEBROWSER_POPUPRANK3;Bewertung 3 *** FILEBROWSER_POPUPRANK4;Bewertung 4 **** FILEBROWSER_POPUPRANK5;Bewertung 5 ***** +FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPREMOVE;Löschen FILEBROWSER_POPUPREMOVEINCLPROC;Löschen (auch Resultate der Stapelverarbeitung) FILEBROWSER_POPUPRENAME;Umbenennen @@ -265,105 +267,6 @@ HISTORY_CUSTOMCURVE;Benutzerdefiniert HISTORY_DELSNAPSHOT;Entfernen HISTORY_FROMCLIPBOARD;Aus der Zwischenablage HISTORY_LABEL;Historie -HISTORY_MSG_1;(Bild geladen) -HISTORY_MSG_2;(Profil geladen) -HISTORY_MSG_3;(Profil geändert) -HISTORY_MSG_4;(Historie durchsuchen) -HISTORY_MSG_5;(Belichtung) - Helligkeit -HISTORY_MSG_6;(Belichtung) - Kontrast -HISTORY_MSG_7;(Belichtung)\nSchwarzwert -HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur -HISTORY_MSG_9;(Belichtung)\nLichterkompression -HISTORY_MSG_10;(Belichtung)\nSchattenkompression -HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 -HISTORY_MSG_12;(Belichtung) - Auto -HISTORY_MSG_13;(Belichtung) - Clip-Faktor -HISTORY_MSG_14;(L*a*b*) - Helligkeit -HISTORY_MSG_15;(L*a*b*) - Kontrast -HISTORY_MSG_16;- -HISTORY_MSG_17;- -HISTORY_MSG_18;- -HISTORY_MSG_19;(L*a*b*) - L-Kurve -HISTORY_MSG_20;(Schärfung) -HISTORY_MSG_21;(Schärfung) - USM\nRadius -HISTORY_MSG_22;(Schärfung) - USM\nIntensität -HISTORY_MSG_23;(Schärfung) - USM\nSchwelle -HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen -HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius -HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz -HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle -HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität -HISTORY_MSG_29;(Schärfung) - Methode -HISTORY_MSG_30;(Schärfung) - RLD\nRadius -HISTORY_MSG_31;(Schärfung) - RLD\nIntensität -HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung -HISTORY_MSG_33;(Schärfung) - RLD\nIterationen -HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung -HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung -HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur -HISTORY_MSG_37;(Belichtung) - Auto -HISTORY_MSG_38;(Weißabgleich) - Methode -HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur -HISTORY_MSG_40;(Weißabgleich) - Tönung -HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus -HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 -HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus -HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius -HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz -HISTORY_MSG_46;(Farb-Rauschfilter) -HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) -HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve -HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination -HISTORY_MSG_50;(Schatten/Lichter) -HISTORY_MSG_51;(Schatten/Lichter)\nLichter -HISTORY_MSG_52;(Schatten/Lichter)\nSchatten -HISTORY_MSG_53;(Schatten/Lichter)\nTonwertbreite Lichter -HISTORY_MSG_54;(Schatten/Lichter)\nTonwertbreite Schatten -HISTORY_MSG_55;(Schatten/Lichter)\nLokaler Kontrast -HISTORY_MSG_56;(Schatten/Lichter)\nRadius -HISTORY_MSG_57;(Grobe Drehung) -HISTORY_MSG_58;(Horizontal spiegeln) -HISTORY_MSG_59;(Vertikal spiegeln) -HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel -HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen -HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung -HISTORY_MSG_63;(Schnappschuss\nausgewählt) -HISTORY_MSG_64;(Ausschnitt) -HISTORY_MSG_65;(Objektivkorrektur)\nFarbsaum entfernen -HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren -HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang -HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode -HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum -HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil -HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil -HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität -HISTORY_MSG_73;(RGB-Kanalmixer) -HISTORY_MSG_74;(Skalieren) - Maßstab -HISTORY_MSG_75;(Skalieren) - Methode -HISTORY_MSG_76;(Exif Metadaten) -HISTORY_MSG_77;(IPTC Metadaten) -HISTORY_MSG_78;- -HISTORY_MSG_79;(Skalieren) - Breite -HISTORY_MSG_80;(Skalieren) - Höhe -HISTORY_MSG_81;(Skalieren) -HISTORY_MSG_82;(Profil geändert) -HISTORY_MSG_83;(Schatten/Lichter)\nSchärfemaske -HISTORY_MSG_84;(Objektivkorrektur)\nPerspektive -HISTORY_MSG_85;(Objektivkorrektur)\nProfil -HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus -HISTORY_MSG_87;(Impulsrauschred.) -HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle -HISTORY_MSG_89;(Rauschreduzierung) -HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz -HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) -HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma -HISTORY_MSG_93;(Detailebenenkontrast)\nWert -HISTORY_MSG_94;(Detailebenenkontrast) -HISTORY_MSG_95;(L*a*b*) - Chromatizität -HISTORY_MSG_96;(L*a*b*) - a-Kurve -HISTORY_MSG_97;(L*a*b*) - b-Kurve -HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode -HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter HISTORY_MSG_100;(Belichtung) - Sättigung HISTORY_MSG_101;(HSV) - Farbton (H) HISTORY_MSG_102;(HSV) - Sättigung (S) @@ -374,6 +277,7 @@ HISTORY_MSG_106;(Farbsaum entfernen)\nRadius HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen +HISTORY_MSG_10;(Belichtung)\nSchattenkompression HISTORY_MSG_110;(Skalieren) - Anwenden auf: HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden HISTORY_MSG_112;--unused-- @@ -384,6 +288,7 @@ HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter +HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl @@ -394,6 +299,7 @@ HISTORY_MSG_126;(Weißbild) - Datei HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl HISTORY_MSG_128;(Weißbild)\nUnschärferadius HISTORY_MSG_129;(Weißbild) - Unschärfetyp +HISTORY_MSG_12;(Belichtung) - Auto HISTORY_MSG_130;(Autom. Verzeichnung) HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz @@ -404,6 +310,7 @@ HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_13;(Belichtung) - Clip-Faktor HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen HISTORY_MSG_142;(Kantenschärfung)\nIterationen @@ -414,6 +321,7 @@ HISTORY_MSG_146;(Kantenschärfung) HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz HISTORY_MSG_148;(Mikrokontrast) HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix +HISTORY_MSG_14;(L*a*b*) - Helligkeit HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) HISTORY_MSG_151;(Dynamik) HISTORY_MSG_152;(Dynamik) - Pastelltöne @@ -424,6 +332,7 @@ HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle HISTORY_MSG_158;(Dynamikkompression)\nIntensität HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz +HISTORY_MSG_15;(L*a*b*) - Kontrast HISTORY_MSG_160;(Dynamikkompression)\nFaktor HISTORY_MSG_161;(Dynamikkompression)\nIterationen HISTORY_MSG_162;(Dynamikkompression) @@ -434,6 +343,7 @@ HISTORY_MSG_166;(Belichtung) - Zurücksetzen HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode HISTORY_MSG_168;(L*a*b*) - CC-Kurve HISTORY_MSG_169;(L*a*b*) - CH-Kurve +HISTORY_MSG_16;- HISTORY_MSG_170;(Dynamik) - HH-Kurve HISTORY_MSG_171;(L*a*b*) - LC-Kurve HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken @@ -444,6 +354,7 @@ HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell +HISTORY_MSG_17;- HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) HISTORY_MSG_181;(CIECAM02) - Buntheit (H) HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch @@ -454,6 +365,7 @@ HISTORY_MSG_186;(CIECAM02) - Algorithmus HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) +HISTORY_MSG_18;- HISTORY_MSG_190;(CIECAM02) - Sättigung (S) HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) HISTORY_MSG_192;(CIECAM02) - Farbton (H) @@ -464,6 +376,8 @@ HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus HISTORY_MSG_197;(CIECAM02) - Farbkurve HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen +HISTORY_MSG_19;(L*a*b*) - L-Kurve +HISTORY_MSG_1;(Bild geladen) HISTORY_MSG_200;(CIECAM02)\nDynamikkompression HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb @@ -473,6 +387,7 @@ HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur +HISTORY_MSG_20;(Schärfung) HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel HISTORY_MSG_211;(Grauverlaufsfilter) HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität @@ -483,6 +398,7 @@ HISTORY_MSG_216;(Schwarz/Weiß) - Grün HISTORY_MSG_217;(Schwarz/Weiß) - Blau HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün +HISTORY_MSG_21;(Schärfung) - USM\nRadius HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben @@ -493,6 +409,7 @@ HISTORY_MSG_226;(Schwarz/Weiß) - Magenta HISTORY_MSG_227;(Schwarz/Weiß) - Violett HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_22;(Schärfung) - USM\nIntensität HISTORY_MSG_230;(Schwarz/Weiß) - Modus HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp @@ -503,6 +420,7 @@ HISTORY_MSG_236;--unused-- HISTORY_MSG_237;(Schwarz/Weiß) - Mixer HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität +HISTORY_MSG_23;(Schärfung) - USM\nSchwelle HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen HISTORY_MSG_241;(Vignettierungsfilter)\nBereich HISTORY_MSG_242;(Vignettierungsfilter)\nForm @@ -513,6 +431,7 @@ HISTORY_MSG_246;(L*a*b*) - CL-Kurve HISTORY_MSG_247;(L*a*b*) - LH-Kurve HISTORY_MSG_248;(L*a*b*) - HH-Kurve HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle +HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen @@ -523,6 +442,7 @@ HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp HISTORY_MSG_257;(Farbanpassungen) HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve +HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz HISTORY_MSG_261;(Farbanpassungen)\nMethode HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz @@ -533,6 +453,7 @@ HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot +HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich @@ -543,6 +464,7 @@ HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft HISTORY_MSG_277;--unused-- HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen HISTORY_MSG_279;(Farbanpassungen)\nSchatten +HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle HISTORY_MSG_280;(Farbanpassungen)\nLichter HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle @@ -553,6 +475,7 @@ HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche +HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau @@ -563,6 +486,8 @@ HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve HISTORY_MSG_297;(Rauschreduzierung)\nQualität HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve +HISTORY_MSG_29;(Schärfung) - Methode +HISTORY_MSG_2;(Profil geladen) HISTORY_MSG_300;- HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode @@ -573,6 +498,7 @@ HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails +HISTORY_MSG_30;(Schärfung) - RLD\nRadius HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle @@ -583,6 +509,7 @@ HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich +HISTORY_MSG_31;(Schärfung) - RLD\nIntensität HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden @@ -593,6 +520,7 @@ HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün +HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße @@ -603,6 +531,7 @@ HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität +HISTORY_MSG_33;(Schärfung) - RLD\nIterationen HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene @@ -613,6 +542,7 @@ HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 +HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund @@ -623,6 +553,7 @@ HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle +HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung HISTORY_MSG_360;(Dynamikkompression)\nGamma HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich HISTORY_MSG_362;(Wavelet) - Restbild\nKompression @@ -633,6 +564,7 @@ HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode +HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve HISTORY_MSG_371;(Skalieren) - Schärfen HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius @@ -643,6 +575,7 @@ HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität +HISTORY_MSG_37;(Belichtung) - Auto HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität @@ -653,6 +586,7 @@ HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb +HISTORY_MSG_38;(Weißabgleich) - Methode HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich @@ -663,6 +597,8 @@ HISTORY_MSG_396;(Wavelet) - Kontrast HISTORY_MSG_397;(Wavelet) - Farbe HISTORY_MSG_398;(Wavelet)\nKantenschärfung HISTORY_MSG_399;(Wavelet) - Restbild +HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur +HISTORY_MSG_3;(Profil geändert) HISTORY_MSG_400;(Wavelet) - Endretusche HISTORY_MSG_401;(Wavelet) - Tönung HISTORY_MSG_402;(Wavelet)\nRauschreduzierung @@ -673,6 +609,7 @@ HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel HISTORY_MSG_407;(Retinex) - Methode HISTORY_MSG_408;(Retinex) - Radius HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_40;(Weißabgleich) - Tönung HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich HISTORY_MSG_411;(Retinex) - Intensität HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient @@ -683,6 +620,7 @@ HISTORY_MSG_416;(Retinex) HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle HISTORY_MSG_419;(Retinex) - Farbraum +HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma @@ -693,6 +631,7 @@ HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) HISTORY_MSG_427;Ausgabe-Rendering-Intent HISTORY_MSG_428;Monitor-Rendering-Intent HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen +HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient HISTORY_MSG_432;(Retinex) - Maske\nLichter @@ -703,10 +642,74 @@ HISTORY_MSG_436;(Retinex) - Maske\nRadius HISTORY_MSG_437;(Retinex) - Maske\nMethode HISTORY_MSG_438;(Retinex) - Maske\nKurve HISTORY_MSG_439;(Retinex) - Vorschau +HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation +HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur +HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius +HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz +HISTORY_MSG_46;(Farb-Rauschfilter) +HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) +HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve +HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination +HISTORY_MSG_4;(Historie durchsuchen) +HISTORY_MSG_50;(Schatten/Lichter) +HISTORY_MSG_51;(Schatten/Lichter)\nLichter +HISTORY_MSG_52;(Schatten/Lichter)\nSchatten +HISTORY_MSG_53;(Schatten/Lichter)\nTonwertbreite Lichter +HISTORY_MSG_54;(Schatten/Lichter)\nTonwertbreite Schatten +HISTORY_MSG_55;(Schatten/Lichter)\nLokaler Kontrast +HISTORY_MSG_56;(Schatten/Lichter)\nRadius +HISTORY_MSG_57;(Grobe Drehung) +HISTORY_MSG_58;(Horizontal spiegeln) +HISTORY_MSG_59;(Vertikal spiegeln) +HISTORY_MSG_5;(Belichtung) - Helligkeit +HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel +HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen +HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung +HISTORY_MSG_63;(Schnappschuss\nausgewählt) +HISTORY_MSG_64;(Ausschnitt) +HISTORY_MSG_65;(Objektivkorrektur)\nFarbsaum entfernen +HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren +HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang +HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode +HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum +HISTORY_MSG_6;(Belichtung) - Kontrast +HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil +HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil +HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität +HISTORY_MSG_73;(RGB-Kanalmixer) +HISTORY_MSG_74;(Skalieren) - Maßstab +HISTORY_MSG_75;(Skalieren) - Methode +HISTORY_MSG_76;(Exif Metadaten) +HISTORY_MSG_77;(IPTC Metadaten) +HISTORY_MSG_78;- +HISTORY_MSG_79;(Skalieren) - Breite +HISTORY_MSG_7;(Belichtung)\nSchwarzwert +HISTORY_MSG_80;(Skalieren) - Höhe +HISTORY_MSG_81;(Skalieren) +HISTORY_MSG_82;(Profil geändert) +HISTORY_MSG_83;(Schatten/Lichter)\nSchärfemaske +HISTORY_MSG_84;(Objektivkorrektur)\nPerspektive +HISTORY_MSG_85;(Objektivkorrektur)\nProfil +HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus +HISTORY_MSG_87;(Impulsrauschred.) +HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle +HISTORY_MSG_89;(Rauschreduzierung) +HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur +HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz +HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) +HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma +HISTORY_MSG_93;(Detailebenenkontrast)\nWert +HISTORY_MSG_94;(Detailebenenkontrast) +HISTORY_MSG_95;(L*a*b*) - Chromatizität +HISTORY_MSG_96;(L*a*b*) - a-Kurve +HISTORY_MSG_97;(L*a*b*) - b-Kurve +HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode +HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter +HISTORY_MSG_9;(Belichtung)\nLichterkompression HISTORY_NEWSNAPSHOT;Hinzufügen HISTORY_NEWSNAPSHOT_TOOLTIP;Taste: Alt + s HISTORY_SNAPSHOT;Schnappschuss @@ -986,15 +989,14 @@ PREFERENCES_FLATFIELDFOUND;Gefunden PREFERENCES_FLATFIELDSDIR;Weißbild-Verzeichnis PREFERENCES_FLATFIELDSHOTS;Aufnahmen PREFERENCES_FLATFIELDTEMPLATES;Vorlagen +PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FLUOF2;Fluoreszenz F2 PREFERENCES_FLUOF7;Fluoreszenz F7 -PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FORIMAGE;Für Bilddateien PREFERENCES_FORRAW;Für RAW-Dateien PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT;Gleiche Miniaturbildgröße in der Dateiverwaltung und dem Filmstreifen verwenden PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT;Unterschiedliche Miniaturbildgrößen benötigen mehr Verarbeitungszeit beim Wechsel zwischen der Dateiverwaltung und dem Editor PREFERENCES_GIMPPATH;GIMP Installationsverzeichnis -PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREY05;Yb = 05 CIE L#30 PREFERENCES_GREY10;Yb = 10 CIE L#40 PREFERENCES_GREY15;Yb = 15 CIE L#45 @@ -1002,8 +1004,9 @@ PREFERENCES_GREY18;Yb = 18 CIE L#50 PREFERENCES_GREY23;Yb = 23 CIE L#55 PREFERENCES_GREY30;Yb = 30 CIE L#60 PREFERENCES_GREY40;Yb = 40 CIE L#70 -PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) +PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREYSC18;Yb = 18 CIE L#49 +PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) PREFERENCES_GREYSCA;Automatisch PREFERENCES_HISTOGRAMPOSITIONLEFT;Histogramm linksseitig PREFERENCES_HISTOGRAMWORKING;Das Arbeitsprofil zur Darstellung des Haupthistogramms verwenden @@ -1437,9 +1440,9 @@ TP_DIRPYRDENOISE_MANU;Benutzerdefiniert TP_DIRPYRDENOISE_MED;Medianfilter TP_DIRPYRDENOISE_MEDMETHOD;Medianmethode TP_DIRPYRDENOISE_MEDTYPE;Mediantyp -TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD11;Qualität TP_DIRPYRDENOISE_METHOD11_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. +TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD_TOOLTIP;Für RAW-Bilder kann entweder die RGB-\noder L*a*b*-Methode verwendet werden.\n\nFür andere Bilder wird unabhängig von der\nAuswahl immer die L*a*b*-Methode verwendet. TP_DIRPYRDENOISE_METM_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MET_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -1768,8 +1771,8 @@ TP_RETINEX_SLOPE;Gammasteigung TP_RETINEX_STRENGTH;Intensität TP_RETINEX_THRESHOLD;Schwelle TP_RETINEX_THRESHOLD_TOOLTIP;Limitiert den Bereich der Transmissionskurve. -TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 +TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 TP_RETINEX_TLABEL_TOOLTIP;Ergebnis der Transmissionskurve: Min, Max, Mittel und Sigma\nMin und Max hat Einfluss auf die Abweichung.\n\nTmin = Kleinster Wert der Transmissionskurve\nTmax = Größter Wert der Transmissionskurve TP_RETINEX_TRANF;Transmission TP_RETINEX_TRANSMISSION;Transmissionskurve @@ -1781,8 +1784,8 @@ TP_RETINEX_VIEW;Vorschau TP_RETINEX_VIEW_MASK;Maske TP_RETINEX_VIEW_METHOD_TOOLTIP;Standard: Normale Anzeige\n\nMaske: Zeigt die Maske an\n\nUnschärfemaske: Zeigt das Bild mit einem großen\nUnschärfemaskenradius an.\n\nTransmission-Auto / Fest: Zeigt die Transmissionskarte\nvor der Anwendung von Kontrast und Helligkeit an.\n\nACHTUNG: Die Maske zeigt nicht das Endergebnis, sondern\nverstärkt den Effekt um ihn besser beurteilen zu können. TP_RETINEX_VIEW_NONE;Standard -TP_RETINEX_VIEW_TRAN;Transmission - Auto TP_RETINEX_VIEW_TRAN2;Transmission - Fest +TP_RETINEX_VIEW_TRAN;Transmission - Auto TP_RETINEX_VIEW_UNSHARP;Unschärfemaske TP_RGBCURVES_BLUE;B TP_RGBCURVES_CHANNEL;Kanal @@ -1906,12 +1909,12 @@ TP_WAVELET_CURVEEDITOR_CL_TOOLTIP;Wendet eine Kontrasthelligkeitskurve\nam Ende TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Farbton als Funktion des Farbtons H = f(H) TP_WAVELET_DALL;Alle Richtungen -TP_WAVELET_DAUB;Kantenperformance +TP_WAVELET_DAUB10;D10 - mittel +TP_WAVELET_DAUB14;D14 - hoch TP_WAVELET_DAUB2;D2 - niedrig TP_WAVELET_DAUB4;D4 - Standard TP_WAVELET_DAUB6;D6 - Standard Plus -TP_WAVELET_DAUB10;D10 - mittel -TP_WAVELET_DAUB14;D14 - hoch +TP_WAVELET_DAUB;Kantenperformance TP_WAVELET_DAUB_TOOLTIP;Ändert den Daubechies-Koeffizienten:\nD4 = Standard\nD14 = Häufig bestes Ergebnis auf Kosten\nvon ca. 10% längerer Verarbeitungszeit.\n\nVerbessert die Kantenerkennung sowie die Qualität\nder ersten Waveletebene. Jedoch hängt die Qualität\nnicht ausschließlich mit diesem Koeffizienten zusammen\nund kann je nach Bild und Einsatz variieren. TP_WAVELET_DONE;Vertikal TP_WAVELET_DTHR;Diagonal @@ -1922,8 +1925,8 @@ TP_WAVELET_EDGCONT_TOOLTIP;Verschieben der Punkte nach links, verringert den Kon TP_WAVELET_EDGE;Kantenschärfung TP_WAVELET_EDGEAMPLI;Grundverstärkung TP_WAVELET_EDGEDETECT;Gradientenempfindlichkeit -TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) +TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;Schwelle der Kantenerkennung für feine Details.\nVerhindert die Schärfung von Rauschen. TP_WAVELET_EDGEDETECT_TOOLTIP;Verschieben des Reglers nach rechts erhöht die\nKantenempfindlichkeit. Die Einstellung wirkt sich\nauf den lokalen Kontrast, Kanteneinstellungen und\nRauschen aus. TP_WAVELET_EDGESENSI;Kantenempfindlichkeit @@ -1999,9 +2002,9 @@ TP_WAVELET_STREN;Intensität TP_WAVELET_STRENGTH;Intensität TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Schatten Schwelle -TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD2;Schattenebenen TP_WAVELET_THRESHOLD2_TOOLTIP;Legt die Ebene der Untergrenze (9 minus Wert)\nfür den Schatten-Luminanzbereich fest. Der\nmaximal mögliche Wert wird vom Wert der Lichter-\nebenen begrenzt.\n\nBeeinflussbare Ebenen: Untergrenze bis Ebene 9 +TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD_TOOLTIP;Legt die Ebene der Obergrenze für den Lichter\n-Luminanzbereich fest. Der Wert begrenzt die\nmaximal möglichen Schattenebenen.\n\nBeeinflussbare Ebenen: Ebene 1 bis Obergrenze TP_WAVELET_THRH;Lichter Schwelle TP_WAVELET_TILESBIG;Große Kacheln @@ -2024,6 +2027,9 @@ TP_WBALANCE_FLASH55;Leica TP_WBALANCE_FLASH60;Standard, Canon, Pentax, Olympus TP_WBALANCE_FLASH65;Nikon, Panasonic, Sony, Minolta TP_WBALANCE_FLASH_HEADER;Blitz +TP_WBALANCE_FLUO10;F10 - Philips TL85 +TP_WBALANCE_FLUO11;F11 - Philips TL84 +TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO1;F1 - Tageslicht TP_WBALANCE_FLUO2;F2 - Kaltweiß TP_WBALANCE_FLUO3;F3 - Weiß @@ -2033,9 +2039,6 @@ TP_WBALANCE_FLUO6;F6 - Weiß reduziert TP_WBALANCE_FLUO7;F7 - D65 Tageslichtsimulation TP_WBALANCE_FLUO8;F8 - D50 / Sylvania F40 Design TP_WBALANCE_FLUO9;F9 - Kaltweiß Deluxe -TP_WBALANCE_FLUO10;F10 - Philips TL85 -TP_WBALANCE_FLUO11;F11 - Philips TL84 -TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO_HEADER;Leuchtstofflampe TP_WBALANCE_GREEN;Tönung TP_WBALANCE_GTI;GTI @@ -2054,6 +2057,8 @@ TP_WBALANCE_SOLUX41;Solux 4100K TP_WBALANCE_SOLUX47;Solux 4700K (Vendor) TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) TP_WBALANCE_SPOTWB;Manuell setzen +TP_WBALANCE_TEMPBIAS;AWB-Temperatur-Korrektur +TP_WBALANCE_TEMPBIAS_TOOLTIP;Prozentuale Korrektur der Farbtemperatur des automatischen\nWeißabgleichs in Richtung wärmer oder kälter.\nDer Korreturwert berechnet sich aus:\nAWB-Temperatur + AWB-Temperatur * AWB-Temperatur-Korrektur TP_WBALANCE_TEMPERATURE;Farbtemperatur TP_WBALANCE_TUNGSTEN;Glühlampe TP_WBALANCE_WATER1;Unterwasser 1 @@ -2066,11 +2071,3 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Ausschnitt an Bildschirm anpassen\nTaste: Alt ZOOMPANEL_ZOOMFITSCREEN;An Bildschirm anpassen\nTaste: f ZOOMPANEL_ZOOMIN;Hineinzoomen\nTaste: + ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - - -!!!!!!!!!!!!!!!!!!!!!!!!! -! Untranslated keys follow; remove the ! prefix after an entry is translated. -!!!!!!!!!!!!!!!!!!!!!!!!! - -!HISTORY_MSG_444;WB - Temp bias -!TP_WBALANCE_TEMPBIAS;AWB temperature bias -!TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". From eb9aee64b3aa6e08eef1d7a39920097edba38531 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Sat, 18 Feb 2017 14:32:40 +0100 Subject: [PATCH 100/181] Translation files regenerated. --- rtdata/languages/Chinese (Simplified) | 4 +- rtdata/languages/Chinese (Traditional) | 4 +- rtdata/languages/Dansk | 4 +- rtdata/languages/Deutsch | 794 ++++++++++++------------- rtdata/languages/English (UK) | 4 +- rtdata/languages/English (US) | 4 +- rtdata/languages/Euskara | 4 +- rtdata/languages/Greek | 4 +- rtdata/languages/Hebrew | 4 +- rtdata/languages/Latvian | 4 +- rtdata/languages/Norsk BM | 4 +- rtdata/languages/Portugues (Brasil) | 4 +- rtdata/languages/Slovak | 2 +- rtdata/languages/Suomi | 4 +- rtdata/languages/Turkish | 4 +- 15 files changed, 424 insertions(+), 424 deletions(-) diff --git a/rtdata/languages/Chinese (Simplified) b/rtdata/languages/Chinese (Simplified) index 5900ef487..af56b7dd1 100644 --- a/rtdata/languages/Chinese (Simplified) +++ b/rtdata/languages/Chinese (Simplified) @@ -1311,7 +1311,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELDSDIR;Flat-fields directory !PREFERENCES_FLATFIELDSHOTS;shots @@ -1381,7 +1381,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Chinese (Traditional) b/rtdata/languages/Chinese (Traditional) index fdc36f91a..65abab6de 100644 --- a/rtdata/languages/Chinese (Traditional) +++ b/rtdata/languages/Chinese (Traditional) @@ -1117,7 +1117,7 @@ TP_WBALANCE_TEMPERATURE;色溫 !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1191,7 +1191,7 @@ TP_WBALANCE_TEMPERATURE;色溫 !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Dansk b/rtdata/languages/Dansk index dc28d6c59..0726bf930 100644 --- a/rtdata/languages/Dansk +++ b/rtdata/languages/Dansk @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 78896314b..0cdfd7437 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -35,7 +35,6 @@ #34 2017-01-07 IPTC (TooWaBoo) RT4.2.1492 #35 2017-02-18 AWB bias (TooWaBoo) - ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen ABOUT_TAB_LICENSE;Lizenz @@ -152,13 +151,13 @@ FILEBROWSER_OPENDEFAULTVIEWER;Windows Standard-Betracher (stapelverarbeitet) FILEBROWSER_PARTIALPASTEPROFILE;Profil selektiv einfügen FILEBROWSER_PASTEPROFILE;Profil einfügen FILEBROWSER_POPUPCANCELJOB;Job abbrechen +FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOLORLABEL0;Markierung: Ohne FILEBROWSER_POPUPCOLORLABEL1;Markierung: Rot FILEBROWSER_POPUPCOLORLABEL2;Markierung: Gelb FILEBROWSER_POPUPCOLORLABEL3;Markierung: Grün FILEBROWSER_POPUPCOLORLABEL4;Markierung: Blau FILEBROWSER_POPUPCOLORLABEL5;Markierung: Violett -FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOPYTO;Kopieren nach... FILEBROWSER_POPUPFILEOPERATIONS;Dateioperationen FILEBROWSER_POPUPMOVEEND;An das Ende der Warteschlange verschieben @@ -169,13 +168,13 @@ FILEBROWSER_POPUPOPENINEDITOR;Im Editor öffnen FILEBROWSER_POPUPPROCESS;Zur Warteschlange hinzufügen FILEBROWSER_POPUPPROCESSFAST;Zur Warteschlange hinzufügen (Schnelles Exportieren) FILEBROWSER_POPUPPROFILEOPERATIONS;Profiloperationen +FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPRANK0;Nicht bewertet FILEBROWSER_POPUPRANK1;Bewertung 1 * FILEBROWSER_POPUPRANK2;Bewertung 2 ** FILEBROWSER_POPUPRANK3;Bewertung 3 *** FILEBROWSER_POPUPRANK4;Bewertung 4 **** FILEBROWSER_POPUPRANK5;Bewertung 5 ***** -FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPREMOVE;Löschen FILEBROWSER_POPUPREMOVEINCLPROC;Löschen (auch Resultate der Stapelverarbeitung) FILEBROWSER_POPUPRENAME;Umbenennen @@ -267,394 +266,55 @@ HISTORY_CUSTOMCURVE;Benutzerdefiniert HISTORY_DELSNAPSHOT;Entfernen HISTORY_FROMCLIPBOARD;Aus der Zwischenablage HISTORY_LABEL;Historie -HISTORY_MSG_100;(Belichtung) - Sättigung -HISTORY_MSG_101;(HSV) - Farbton (H) -HISTORY_MSG_102;(HSV) - Sättigung (S) -HISTORY_MSG_103;(HSV) - Dynamik (V) -HISTORY_MSG_104;(HSV) -HISTORY_MSG_105;(Farbsaum entfernen) -HISTORY_MSG_106;(Farbsaum entfernen)\nRadius -HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle -HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle -HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen -HISTORY_MSG_10;(Belichtung)\nSchattenkompression -HISTORY_MSG_110;(Skalieren) - Anwenden auf: -HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden -HISTORY_MSG_112;--unused-- -HISTORY_MSG_113;(L*a*b*) - Hautfarbtöne\nschützen -HISTORY_MSG_114;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Iterationen -HISTORY_MSG_115;(Sensor-Matrix)\nFarbinterpolation\nFalschfarbenunterdrückung -HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung -HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot -HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau -HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter -HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 -HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich -HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur -HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl -HISTORY_MSG_123;(Dunkelbild) - Datei -HISTORY_MSG_124;(Weißpunkt)\nKorrekturfaktor -HISTORY_MSG_125;(Belichtungskorrektur)\nLichter schützen -HISTORY_MSG_126;(Weißbild) - Datei -HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl -HISTORY_MSG_128;(Weißbild)\nUnschärferadius -HISTORY_MSG_129;(Weißbild) - Unschärfetyp -HISTORY_MSG_12;(Belichtung) - Auto -HISTORY_MSG_130;(Autom. Verzeichnung) -HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz -HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz -HISTORY_MSG_133;(Farbmanagement)\nAusgabeprofil\nAusgabe-Gamma -HISTORY_MSG_134;(Farbmanagement)\nAusgabeprofil\nGamma -HISTORY_MSG_135;(Farbmanagement)\nAusgabeprofil\nFreies Gamma -HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) -HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 -HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot -HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau -HISTORY_MSG_13;(Belichtung) - Clip-Faktor -HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 -HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen -HISTORY_MSG_142;(Kantenschärfung)\nIterationen -HISTORY_MSG_143;(Kantenschärfung)\nIntensität -HISTORY_MSG_144;(Mikrokontrast)\nIntensität -HISTORY_MSG_145;(Mikrokontrast)\nGleichmäßigkeit -HISTORY_MSG_146;(Kantenschärfung) -HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz -HISTORY_MSG_148;(Mikrokontrast) -HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix -HISTORY_MSG_14;(L*a*b*) - Helligkeit -HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) -HISTORY_MSG_151;(Dynamik) -HISTORY_MSG_152;(Dynamik) - Pastelltöne -HISTORY_MSG_153;(Dynamik)\nGesättigte Töne -HISTORY_MSG_154;(Dynamik)\nHautfarbtöne schützen -HISTORY_MSG_155;(Dynamik)\nFarbverschiebungen\nvermeiden -HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln -HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle -HISTORY_MSG_158;(Dynamikkompression)\nIntensität -HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz -HISTORY_MSG_15;(L*a*b*) - Kontrast -HISTORY_MSG_160;(Dynamikkompression)\nFaktor -HISTORY_MSG_161;(Dynamikkompression)\nIterationen -HISTORY_MSG_162;(Dynamikkompression) -HISTORY_MSG_163;(RGB-Kurven) - Rot -HISTORY_MSG_164;(RGB-Kurven) - Grün -HISTORY_MSG_165;(RGB-Kurven) - Blau -HISTORY_MSG_166;(Belichtung) - Zurücksetzen -HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode -HISTORY_MSG_168;(L*a*b*) - CC-Kurve -HISTORY_MSG_169;(L*a*b*) - CH-Kurve -HISTORY_MSG_16;- -HISTORY_MSG_170;(Dynamik) - HH-Kurve -HISTORY_MSG_171;(L*a*b*) - LC-Kurve -HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken -HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails -HISTORY_MSG_174;(CIECAM02) -HISTORY_MSG_175;(CIECAM02) - Szene\nCAT02-Adaptation -HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung -HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke -HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke -HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell -HISTORY_MSG_17;- -HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) -HISTORY_MSG_181;(CIECAM02) - Buntheit (H) -HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch -HISTORY_MSG_183;(CIECAM02) - Kontrast (J) -HISTORY_MSG_184;(CIECAM02) - Szene\nDunkle Umgebung -HISTORY_MSG_185;(CIECAM02)\nBetrachtungsbed.\nGamutkontrolle -HISTORY_MSG_186;(CIECAM02) - Algorithmus -HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen -HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) -HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) -HISTORY_MSG_18;- -HISTORY_MSG_190;(CIECAM02) - Sättigung (S) -HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) -HISTORY_MSG_192;(CIECAM02) - Farbton (H) -HISTORY_MSG_193;(CIECAM02)\nTonwertkurve 1 -HISTORY_MSG_194;(CIECAM02)\nTonwertkurve 2 -HISTORY_MSG_195;(CIECAM02)\nTonwertkurve 1 - Modus -HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus -HISTORY_MSG_197;(CIECAM02) - Farbkurve -HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus -HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen -HISTORY_MSG_19;(L*a*b*) - L-Kurve HISTORY_MSG_1;(Bild geladen) -HISTORY_MSG_200;(CIECAM02)\nDynamikkompression -HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün -HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb -HISTORY_MSG_203;(Rauschreduzierung)\nMethode -HISTORY_MSG_204;(Sensor Bayer-Matrix)\nFarbinterpolation\nLMMSE-Verbesserung -HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter -HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke -HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve -HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur -HISTORY_MSG_20;(Schärfung) -HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel -HISTORY_MSG_211;(Grauverlaufsfilter) -HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität -HISTORY_MSG_213;(Vignettierungsfilter) -HISTORY_MSG_214;(Schwarz/Weiß) -HISTORY_MSG_215;(Schwarz/Weiß) - Rot -HISTORY_MSG_216;(Schwarz/Weiß) - Grün -HISTORY_MSG_217;(Schwarz/Weiß) - Blau -HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot -HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün -HISTORY_MSG_21;(Schärfung) - USM\nRadius -HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau -HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter -HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben -HISTORY_MSG_223;(Schwarz/Weiß) - Orange -HISTORY_MSG_224;(Schwarz/Weiß) - Gelb -HISTORY_MSG_225;(Schwarz/Weiß) - Cyan -HISTORY_MSG_226;(Schwarz/Weiß) - Magenta -HISTORY_MSG_227;(Schwarz/Weiß) - Violett -HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer -HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer -HISTORY_MSG_22;(Schärfung) - USM\nIntensität -HISTORY_MSG_230;(Schwarz/Weiß) - Modus -HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve -HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp -HISTORY_MSG_233;(Schwarz/Weiß)\n“Danach“-Kurve -HISTORY_MSG_234;(Schwarz/Weiß)\n“Danach“-Kurventyp -HISTORY_MSG_235;(Schwarz/Weiß)\nAuto-Kanalmixer -HISTORY_MSG_236;--unused-- -HISTORY_MSG_237;(Schwarz/Weiß) - Mixer -HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich -HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität -HISTORY_MSG_23;(Schärfung) - USM\nSchwelle -HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen -HISTORY_MSG_241;(Vignettierungsfilter)\nBereich -HISTORY_MSG_242;(Vignettierungsfilter)\nForm -HISTORY_MSG_243;(Objektivkorrektur)\nVignettierung - Radius -HISTORY_MSG_244;(Objektivkorrektur)\nVignettierung - Faktor -HISTORY_MSG_245;(Objektivkorrektur)\nVignettierung - Zentrum -HISTORY_MSG_246;(L*a*b*) - CL-Kurve -HISTORY_MSG_247;(L*a*b*) - LH-Kurve -HISTORY_MSG_248;(L*a*b*) - HH-Kurve -HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle -HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen -HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung -HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus -HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen -HISTORY_MSG_253;(Detailebenenkontrast)\nArtefakte reduzieren -HISTORY_MSG_254;(Detailebenenkontrast)\nHautfarbton -HISTORY_MSG_255;(Rauschreduzierung)\nMedianfilter -HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_257;(Farbanpassungen) -HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve -HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve -HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius -HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz -HISTORY_MSG_261;(Farbanpassungen)\nMethode -HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz -HISTORY_MSG_263;(Farbanpassungen)\nSchatten - Blau / Rot -HISTORY_MSG_264;(Farbanpassungen)\nSchatten - Cyan / Grün -HISTORY_MSG_265;(Farbanpassungen)\nSchatten - Gelb / Blau -HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot -HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün -HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau -HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot -HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz -HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün -HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau -HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich -HISTORY_MSG_273;(Farbanpassungen)\nZurücksetzen -HISTORY_MSG_274;(Farbanpassungen)\nSättigung Schatten -HISTORY_MSG_275;(Farbanpassungen)\nSättigung Lichter -HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft -HISTORY_MSG_277;--unused-- -HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen -HISTORY_MSG_279;(Farbanpassungen)\nSchatten -HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle -HISTORY_MSG_280;(Farbanpassungen)\nLichter -HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität -HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle -HISTORY_MSG_283;(Farbanpassungen)\nIntensität -HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch -HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode -HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen -HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche -HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche -HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität -HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot -HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün -HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau -HISTORY_MSG_293;(Filmsimulation) -HISTORY_MSG_294;(Filmsimulation)\nIntensität -HISTORY_MSG_295;(Filmsimulation) - Film -HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve -HISTORY_MSG_297;(Rauschreduzierung)\nQualität -HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter -HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve -HISTORY_MSG_29;(Schärfung) - Methode HISTORY_MSG_2;(Profil geladen) -HISTORY_MSG_300;- -HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle -HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode -HISTORY_MSG_303;(Rauschreduzierung)\nChrominanz - Methode -HISTORY_MSG_304;(Wavelet)\nKontrastebenen -HISTORY_MSG_305;(Wavelet) -HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene -HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung -HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung -HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails -HISTORY_MSG_30;(Schärfung) - RLD\nRadius -HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen -HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen -HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle -HISTORY_MSG_313;(Wavelet) - Farbe\nEbenengrenze -HISTORY_MSG_314;(Wavelet) - Gamut\nArtefakte reduzieren -HISTORY_MSG_315;(Wavelet) - Restbild\nKontrast -HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen -HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton -HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen -HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich -HISTORY_MSG_31;(Schärfung) - RLD\nIntensität -HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich -HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen -HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden -HISTORY_MSG_323;(Wavelet)\nKantenschärfung\nLokale Kontrastkurve -HISTORY_MSG_324;(Wavelet) - Farbe\nPastellfarben -HISTORY_MSG_325;(Wavelet) - Farbe\nGesättigte Farben -HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode -HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf -HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung -HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün -HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung -HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb -HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra -HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße -HISTORY_MSG_333;(Wavelet) - Restbild\nSchatten -HISTORY_MSG_334;(Wavelet) - Restbild\nBuntheit -HISTORY_MSG_335;(Wavelet) - Restbild\nLichter -HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle -HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton -HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius -HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität -HISTORY_MSG_33;(Schärfung) - RLD\nIterationen -HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität -HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance -HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene -HISTORY_MSG_343;(Wavelet) - Farbe\nFarbebenen -HISTORY_MSG_344;(Wavelet)\nFarbmethode\nRegler/Kurve -HISTORY_MSG_345;(Wavelet)\nKantenschärfung\nLokaler Kontrast -HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode -HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 -HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 -HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 -HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung -HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung -HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve -HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund -HISTORY_MSG_353;(Wavelet)\nKantenschärfung\nGradientenempfindlichkeit -HISTORY_MSG_354;(Wavelet)\nKantenschärfung\nErweiterter Algorithmus -HISTORY_MSG_355;(Wavelet)\nKantenschärfung\nSchwelle niedrig -HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch -HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen -HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve -HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle -HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung -HISTORY_MSG_360;(Dynamikkompression)\nGamma -HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich -HISTORY_MSG_362;(Wavelet) - Restbild\nKompression -HISTORY_MSG_363;(Wavelet) - Restbild\nKompression - Intensität -HISTORY_MSG_364;(Wavelet) - Endretusche\nKontrastausgleich -HISTORY_MSG_365;(Wavelet) - Endretusche\nDelta-Kontrastausgleich -HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression -HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve -HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve -HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode -HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur -HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve -HISTORY_MSG_371;(Skalieren) - Schärfen -HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius -HISTORY_MSG_373;(Skalieren) - Schärfen\nUSM - Intensität -HISTORY_MSG_374;(Skalieren) - Schärfen\nUSM - Schwelle -HISTORY_MSG_375;(Skalieren) - Schärfen\nUSM - Nur Kanten\nschärfen -HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius -HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz -HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle -HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität -HISTORY_MSG_37;(Belichtung) - Auto -HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode -HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius -HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität -HISTORY_MSG_383;(Skalieren) - Schärfen\nRLD - Dämpfung -HISTORY_MSG_384;(Skalieren) - Schärfen\nRLD - Iterationen -HISTORY_MSG_385;(Wavelet) - Restbild\nFarbausgleich -HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan -HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb -HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan -HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb -HISTORY_MSG_38;(Weißabgleich) - Methode -HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan -HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb -HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich -HISTORY_MSG_393;(Farbmanagement)\nEingangsfarbprofil\nDCP - Look-Tabelle -HISTORY_MSG_394;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basisbelichtung -HISTORY_MSG_395;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basistabelle -HISTORY_MSG_396;(Wavelet) - Kontrast -HISTORY_MSG_397;(Wavelet) - Farbe -HISTORY_MSG_398;(Wavelet)\nKantenschärfung -HISTORY_MSG_399;(Wavelet) - Restbild -HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur HISTORY_MSG_3;(Profil geändert) -HISTORY_MSG_400;(Wavelet) - Endretusche -HISTORY_MSG_401;(Wavelet) - Tönung -HISTORY_MSG_402;(Wavelet)\nRauschreduzierung -HISTORY_MSG_403;(Wavelet)\nKantenschärfung\nKantenempfindlichkeit -HISTORY_MSG_404;(Wavelet)\nKantenschärfung\nGrundverstärkung -HISTORY_MSG_405;(Wavelet)\nRauschreduzierung\nEbene 4 -HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel -HISTORY_MSG_407;(Retinex) - Methode -HISTORY_MSG_408;(Retinex) - Radius -HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_4;(Historie durchsuchen) +HISTORY_MSG_5;(Belichtung) - Helligkeit +HISTORY_MSG_6;(Belichtung) - Kontrast +HISTORY_MSG_7;(Belichtung)\nSchwarzwert +HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur +HISTORY_MSG_9;(Belichtung)\nLichterkompression +HISTORY_MSG_10;(Belichtung)\nSchattenkompression +HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 +HISTORY_MSG_12;(Belichtung) - Auto +HISTORY_MSG_13;(Belichtung) - Clip-Faktor +HISTORY_MSG_14;(L*a*b*) - Helligkeit +HISTORY_MSG_15;(L*a*b*) - Kontrast +HISTORY_MSG_16;- +HISTORY_MSG_17;- +HISTORY_MSG_18;- +HISTORY_MSG_19;(L*a*b*) - L-Kurve +HISTORY_MSG_20;(Schärfung) +HISTORY_MSG_21;(Schärfung) - USM\nRadius +HISTORY_MSG_22;(Schärfung) - USM\nIntensität +HISTORY_MSG_23;(Schärfung) - USM\nSchwelle +HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen +HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius +HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz +HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle +HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität +HISTORY_MSG_29;(Schärfung) - Methode +HISTORY_MSG_30;(Schärfung) - RLD\nRadius +HISTORY_MSG_31;(Schärfung) - RLD\nIntensität +HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung +HISTORY_MSG_33;(Schärfung) - RLD\nIterationen +HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung +HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung +HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur +HISTORY_MSG_37;(Belichtung) - Auto +HISTORY_MSG_38;(Weißabgleich) - Methode +HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur HISTORY_MSG_40;(Weißabgleich) - Tönung -HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich -HISTORY_MSG_411;(Retinex) - Intensität -HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient -HISTORY_MSG_413;(Retinex) - Kontrast -HISTORY_MSG_414;(Retinex) - Einstellungen\nKorrekturen\nLuminanz(L) - L*a*b* -HISTORY_MSG_415;(Retinex) - Einstellungen\nTransmission\nTransmissionskurve -HISTORY_MSG_416;(Retinex) -HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter -HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle -HISTORY_MSG_419;(Retinex) - Farbraum HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus -HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve -HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur -HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma -HISTORY_MSG_423;(Retinex) - Einstellungen\nGammasteigung -HISTORY_MSG_424;(Retinex) - Einstellungen\nHL-Schwelle -HISTORY_MSG_425;(Retinex) - Einstellungen\nBasis-Logarithmus -HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) -HISTORY_MSG_427;Ausgabe-Rendering-Intent -HISTORY_MSG_428;Monitor-Rendering-Intent -HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 -HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient -HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient -HISTORY_MSG_432;(Retinex) - Maske\nLichter -HISTORY_MSG_433;(Retinex) - Maske\nTonwertbreite Lichter -HISTORY_MSG_434;(Retinex) - Maske\nSchatten -HISTORY_MSG_435;(Retinex) - Maske\nTonwertbreite Schatten -HISTORY_MSG_436;(Retinex) - Maske\nRadius -HISTORY_MSG_437;(Retinex) - Maske\nMethode -HISTORY_MSG_438;(Retinex) - Maske\nKurve -HISTORY_MSG_439;(Retinex) - Vorschau HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus -HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge -HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung -HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung -HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation -HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz HISTORY_MSG_46;(Farb-Rauschfilter) HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination -HISTORY_MSG_4;(Historie durchsuchen) HISTORY_MSG_50;(Schatten/Lichter) HISTORY_MSG_51;(Schatten/Lichter)\nLichter HISTORY_MSG_52;(Schatten/Lichter)\nSchatten @@ -665,7 +325,6 @@ HISTORY_MSG_56;(Schatten/Lichter)\nRadius HISTORY_MSG_57;(Grobe Drehung) HISTORY_MSG_58;(Horizontal spiegeln) HISTORY_MSG_59;(Vertikal spiegeln) -HISTORY_MSG_5;(Belichtung) - Helligkeit HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung @@ -676,7 +335,6 @@ HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum -HISTORY_MSG_6;(Belichtung) - Kontrast HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität @@ -687,7 +345,6 @@ HISTORY_MSG_76;(Exif Metadaten) HISTORY_MSG_77;(IPTC Metadaten) HISTORY_MSG_78;- HISTORY_MSG_79;(Skalieren) - Breite -HISTORY_MSG_7;(Belichtung)\nSchwarzwert HISTORY_MSG_80;(Skalieren) - Höhe HISTORY_MSG_81;(Skalieren) HISTORY_MSG_82;(Profil geändert) @@ -698,7 +355,6 @@ HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus HISTORY_MSG_87;(Impulsrauschred.) HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle HISTORY_MSG_89;(Rauschreduzierung) -HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma @@ -709,7 +365,350 @@ HISTORY_MSG_96;(L*a*b*) - a-Kurve HISTORY_MSG_97;(L*a*b*) - b-Kurve HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter -HISTORY_MSG_9;(Belichtung)\nLichterkompression +HISTORY_MSG_100;(Belichtung) - Sättigung +HISTORY_MSG_101;(HSV) - Farbton (H) +HISTORY_MSG_102;(HSV) - Sättigung (S) +HISTORY_MSG_103;(HSV) - Dynamik (V) +HISTORY_MSG_104;(HSV) +HISTORY_MSG_105;(Farbsaum entfernen) +HISTORY_MSG_106;(Farbsaum entfernen)\nRadius +HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle +HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle +HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen +HISTORY_MSG_110;(Skalieren) - Anwenden auf: +HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden +HISTORY_MSG_112;--unused-- +HISTORY_MSG_113;(L*a*b*) - Hautfarbtöne\nschützen +HISTORY_MSG_114;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Iterationen +HISTORY_MSG_115;(Sensor-Matrix)\nFarbinterpolation\nFalschfarbenunterdrückung +HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung +HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot +HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau +HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter +HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich +HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur +HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl +HISTORY_MSG_123;(Dunkelbild) - Datei +HISTORY_MSG_124;(Weißpunkt)\nKorrekturfaktor +HISTORY_MSG_125;(Belichtungskorrektur)\nLichter schützen +HISTORY_MSG_126;(Weißbild) - Datei +HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl +HISTORY_MSG_128;(Weißbild)\nUnschärferadius +HISTORY_MSG_129;(Weißbild) - Unschärfetyp +HISTORY_MSG_130;(Autom. Verzeichnung) +HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz +HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz +HISTORY_MSG_133;(Farbmanagement)\nAusgabeprofil\nAusgabe-Gamma +HISTORY_MSG_134;(Farbmanagement)\nAusgabeprofil\nGamma +HISTORY_MSG_135;(Farbmanagement)\nAusgabeprofil\nFreies Gamma +HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) +HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 +HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot +HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 +HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen +HISTORY_MSG_142;(Kantenschärfung)\nIterationen +HISTORY_MSG_143;(Kantenschärfung)\nIntensität +HISTORY_MSG_144;(Mikrokontrast)\nIntensität +HISTORY_MSG_145;(Mikrokontrast)\nGleichmäßigkeit +HISTORY_MSG_146;(Kantenschärfung) +HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz +HISTORY_MSG_148;(Mikrokontrast) +HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix +HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) +HISTORY_MSG_151;(Dynamik) +HISTORY_MSG_152;(Dynamik) - Pastelltöne +HISTORY_MSG_153;(Dynamik)\nGesättigte Töne +HISTORY_MSG_154;(Dynamik)\nHautfarbtöne schützen +HISTORY_MSG_155;(Dynamik)\nFarbverschiebungen\nvermeiden +HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln +HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle +HISTORY_MSG_158;(Dynamikkompression)\nIntensität +HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz +HISTORY_MSG_160;(Dynamikkompression)\nFaktor +HISTORY_MSG_161;(Dynamikkompression)\nIterationen +HISTORY_MSG_162;(Dynamikkompression) +HISTORY_MSG_163;(RGB-Kurven) - Rot +HISTORY_MSG_164;(RGB-Kurven) - Grün +HISTORY_MSG_165;(RGB-Kurven) - Blau +HISTORY_MSG_166;(Belichtung) - Zurücksetzen +HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode +HISTORY_MSG_168;(L*a*b*) - CC-Kurve +HISTORY_MSG_169;(L*a*b*) - CH-Kurve +HISTORY_MSG_170;(Dynamik) - HH-Kurve +HISTORY_MSG_171;(L*a*b*) - LC-Kurve +HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken +HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails +HISTORY_MSG_174;(CIECAM02) +HISTORY_MSG_175;(CIECAM02) - Szene\nCAT02-Adaptation +HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung +HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke +HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke +HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell +HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) +HISTORY_MSG_181;(CIECAM02) - Buntheit (H) +HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch +HISTORY_MSG_183;(CIECAM02) - Kontrast (J) +HISTORY_MSG_184;(CIECAM02) - Szene\nDunkle Umgebung +HISTORY_MSG_185;(CIECAM02)\nBetrachtungsbed.\nGamutkontrolle +HISTORY_MSG_186;(CIECAM02) - Algorithmus +HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen +HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) +HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) +HISTORY_MSG_190;(CIECAM02) - Sättigung (S) +HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) +HISTORY_MSG_192;(CIECAM02) - Farbton (H) +HISTORY_MSG_193;(CIECAM02)\nTonwertkurve 1 +HISTORY_MSG_194;(CIECAM02)\nTonwertkurve 2 +HISTORY_MSG_195;(CIECAM02)\nTonwertkurve 1 - Modus +HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus +HISTORY_MSG_197;(CIECAM02) - Farbkurve +HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus +HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen +HISTORY_MSG_200;(CIECAM02)\nDynamikkompression +HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün +HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb +HISTORY_MSG_203;(Rauschreduzierung)\nMethode +HISTORY_MSG_204;(Sensor Bayer-Matrix)\nFarbinterpolation\nLMMSE-Verbesserung +HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter +HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke +HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve +HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur +HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel +HISTORY_MSG_211;(Grauverlaufsfilter) +HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität +HISTORY_MSG_213;(Vignettierungsfilter) +HISTORY_MSG_214;(Schwarz/Weiß) +HISTORY_MSG_215;(Schwarz/Weiß) - Rot +HISTORY_MSG_216;(Schwarz/Weiß) - Grün +HISTORY_MSG_217;(Schwarz/Weiß) - Blau +HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot +HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün +HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau +HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter +HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben +HISTORY_MSG_223;(Schwarz/Weiß) - Orange +HISTORY_MSG_224;(Schwarz/Weiß) - Gelb +HISTORY_MSG_225;(Schwarz/Weiß) - Cyan +HISTORY_MSG_226;(Schwarz/Weiß) - Magenta +HISTORY_MSG_227;(Schwarz/Weiß) - Violett +HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_230;(Schwarz/Weiß) - Modus +HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve +HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp +HISTORY_MSG_233;(Schwarz/Weiß)\n“Danach“-Kurve +HISTORY_MSG_234;(Schwarz/Weiß)\n“Danach“-Kurventyp +HISTORY_MSG_235;(Schwarz/Weiß)\nAuto-Kanalmixer +HISTORY_MSG_236;--unused-- +HISTORY_MSG_237;(Schwarz/Weiß) - Mixer +HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich +HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität +HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen +HISTORY_MSG_241;(Vignettierungsfilter)\nBereich +HISTORY_MSG_242;(Vignettierungsfilter)\nForm +HISTORY_MSG_243;(Objektivkorrektur)\nVignettierung - Radius +HISTORY_MSG_244;(Objektivkorrektur)\nVignettierung - Faktor +HISTORY_MSG_245;(Objektivkorrektur)\nVignettierung - Zentrum +HISTORY_MSG_246;(L*a*b*) - CL-Kurve +HISTORY_MSG_247;(L*a*b*) - LH-Kurve +HISTORY_MSG_248;(L*a*b*) - HH-Kurve +HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle +HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung +HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus +HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen +HISTORY_MSG_253;(Detailebenenkontrast)\nArtefakte reduzieren +HISTORY_MSG_254;(Detailebenenkontrast)\nHautfarbton +HISTORY_MSG_255;(Rauschreduzierung)\nMedianfilter +HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp +HISTORY_MSG_257;(Farbanpassungen) +HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve +HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve +HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz +HISTORY_MSG_261;(Farbanpassungen)\nMethode +HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz +HISTORY_MSG_263;(Farbanpassungen)\nSchatten - Blau / Rot +HISTORY_MSG_264;(Farbanpassungen)\nSchatten - Cyan / Grün +HISTORY_MSG_265;(Farbanpassungen)\nSchatten - Gelb / Blau +HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot +HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün +HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau +HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot +HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün +HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau +HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich +HISTORY_MSG_273;(Farbanpassungen)\nZurücksetzen +HISTORY_MSG_274;(Farbanpassungen)\nSättigung Schatten +HISTORY_MSG_275;(Farbanpassungen)\nSättigung Lichter +HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft +HISTORY_MSG_277;--unused-- +HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen +HISTORY_MSG_279;(Farbanpassungen)\nSchatten +HISTORY_MSG_280;(Farbanpassungen)\nLichter +HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität +HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle +HISTORY_MSG_283;(Farbanpassungen)\nIntensität +HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch +HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode +HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp +HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen +HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche +HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche +HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot +HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün +HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_293;(Filmsimulation) +HISTORY_MSG_294;(Filmsimulation)\nIntensität +HISTORY_MSG_295;(Filmsimulation) - Film +HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve +HISTORY_MSG_297;(Rauschreduzierung)\nQualität +HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter +HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve +HISTORY_MSG_300;- +HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle +HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode +HISTORY_MSG_303;(Rauschreduzierung)\nChrominanz - Methode +HISTORY_MSG_304;(Wavelet)\nKontrastebenen +HISTORY_MSG_305;(Wavelet) +HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene +HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung +HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung +HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails +HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen +HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen +HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle +HISTORY_MSG_313;(Wavelet) - Farbe\nEbenengrenze +HISTORY_MSG_314;(Wavelet) - Gamut\nArtefakte reduzieren +HISTORY_MSG_315;(Wavelet) - Restbild\nKontrast +HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen +HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton +HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen +HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich +HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich +HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen +HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden +HISTORY_MSG_323;(Wavelet)\nKantenschärfung\nLokale Kontrastkurve +HISTORY_MSG_324;(Wavelet) - Farbe\nPastellfarben +HISTORY_MSG_325;(Wavelet) - Farbe\nGesättigte Farben +HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode +HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf +HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung +HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün +HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb +HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra +HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße +HISTORY_MSG_333;(Wavelet) - Restbild\nSchatten +HISTORY_MSG_334;(Wavelet) - Restbild\nBuntheit +HISTORY_MSG_335;(Wavelet) - Restbild\nLichter +HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle +HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton +HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius +HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität +HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität +HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance +HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene +HISTORY_MSG_343;(Wavelet) - Farbe\nFarbebenen +HISTORY_MSG_344;(Wavelet)\nFarbmethode\nRegler/Kurve +HISTORY_MSG_345;(Wavelet)\nKantenschärfung\nLokaler Kontrast +HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode +HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 +HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 +HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 +HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung +HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve +HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund +HISTORY_MSG_353;(Wavelet)\nKantenschärfung\nGradientenempfindlichkeit +HISTORY_MSG_354;(Wavelet)\nKantenschärfung\nErweiterter Algorithmus +HISTORY_MSG_355;(Wavelet)\nKantenschärfung\nSchwelle niedrig +HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch +HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen +HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve +HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle +HISTORY_MSG_360;(Dynamikkompression)\nGamma +HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich +HISTORY_MSG_362;(Wavelet) - Restbild\nKompression +HISTORY_MSG_363;(Wavelet) - Restbild\nKompression - Intensität +HISTORY_MSG_364;(Wavelet) - Endretusche\nKontrastausgleich +HISTORY_MSG_365;(Wavelet) - Endretusche\nDelta-Kontrastausgleich +HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression +HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve +HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve +HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode +HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve +HISTORY_MSG_371;(Skalieren) - Schärfen +HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius +HISTORY_MSG_373;(Skalieren) - Schärfen\nUSM - Intensität +HISTORY_MSG_374;(Skalieren) - Schärfen\nUSM - Schwelle +HISTORY_MSG_375;(Skalieren) - Schärfen\nUSM - Nur Kanten\nschärfen +HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius +HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz +HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle +HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität +HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode +HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius +HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität +HISTORY_MSG_383;(Skalieren) - Schärfen\nRLD - Dämpfung +HISTORY_MSG_384;(Skalieren) - Schärfen\nRLD - Iterationen +HISTORY_MSG_385;(Wavelet) - Restbild\nFarbausgleich +HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan +HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb +HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan +HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb +HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan +HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb +HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich +HISTORY_MSG_393;(Farbmanagement)\nEingangsfarbprofil\nDCP - Look-Tabelle +HISTORY_MSG_394;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basisbelichtung +HISTORY_MSG_395;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basistabelle +HISTORY_MSG_396;(Wavelet) - Kontrast +HISTORY_MSG_397;(Wavelet) - Farbe +HISTORY_MSG_398;(Wavelet)\nKantenschärfung +HISTORY_MSG_399;(Wavelet) - Restbild +HISTORY_MSG_400;(Wavelet) - Endretusche +HISTORY_MSG_401;(Wavelet) - Tönung +HISTORY_MSG_402;(Wavelet)\nRauschreduzierung +HISTORY_MSG_403;(Wavelet)\nKantenschärfung\nKantenempfindlichkeit +HISTORY_MSG_404;(Wavelet)\nKantenschärfung\nGrundverstärkung +HISTORY_MSG_405;(Wavelet)\nRauschreduzierung\nEbene 4 +HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel +HISTORY_MSG_407;(Retinex) - Methode +HISTORY_MSG_408;(Retinex) - Radius +HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich +HISTORY_MSG_411;(Retinex) - Intensität +HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient +HISTORY_MSG_413;(Retinex) - Kontrast +HISTORY_MSG_414;(Retinex) - Einstellungen\nKorrekturen\nLuminanz(L) - L*a*b* +HISTORY_MSG_415;(Retinex) - Einstellungen\nTransmission\nTransmissionskurve +HISTORY_MSG_416;(Retinex) +HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter +HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle +HISTORY_MSG_419;(Retinex) - Farbraum +HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve +HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur +HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma +HISTORY_MSG_423;(Retinex) - Einstellungen\nGammasteigung +HISTORY_MSG_424;(Retinex) - Einstellungen\nHL-Schwelle +HISTORY_MSG_425;(Retinex) - Einstellungen\nBasis-Logarithmus +HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) +HISTORY_MSG_427;Ausgabe-Rendering-Intent +HISTORY_MSG_428;Monitor-Rendering-Intent +HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen +HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient +HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient +HISTORY_MSG_432;(Retinex) - Maske\nLichter +HISTORY_MSG_433;(Retinex) - Maske\nTonwertbreite Lichter +HISTORY_MSG_434;(Retinex) - Maske\nSchatten +HISTORY_MSG_435;(Retinex) - Maske\nTonwertbreite Schatten +HISTORY_MSG_436;(Retinex) - Maske\nRadius +HISTORY_MSG_437;(Retinex) - Maske\nMethode +HISTORY_MSG_438;(Retinex) - Maske\nKurve +HISTORY_MSG_439;(Retinex) - Vorschau +HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge +HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung +HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung +HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation +HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur HISTORY_NEWSNAPSHOT;Hinzufügen HISTORY_NEWSNAPSHOT_TOOLTIP;Taste: Alt + s HISTORY_SNAPSHOT;Schnappschuss @@ -989,14 +988,15 @@ PREFERENCES_FLATFIELDFOUND;Gefunden PREFERENCES_FLATFIELDSDIR;Weißbild-Verzeichnis PREFERENCES_FLATFIELDSHOTS;Aufnahmen PREFERENCES_FLATFIELDTEMPLATES;Vorlagen -PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FLUOF2;Fluoreszenz F2 PREFERENCES_FLUOF7;Fluoreszenz F7 +PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FORIMAGE;Für Bilddateien PREFERENCES_FORRAW;Für RAW-Dateien PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT;Gleiche Miniaturbildgröße in der Dateiverwaltung und dem Filmstreifen verwenden PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT;Unterschiedliche Miniaturbildgrößen benötigen mehr Verarbeitungszeit beim Wechsel zwischen der Dateiverwaltung und dem Editor PREFERENCES_GIMPPATH;GIMP Installationsverzeichnis +PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREY05;Yb = 05 CIE L#30 PREFERENCES_GREY10;Yb = 10 CIE L#40 PREFERENCES_GREY15;Yb = 15 CIE L#45 @@ -1004,9 +1004,8 @@ PREFERENCES_GREY18;Yb = 18 CIE L#50 PREFERENCES_GREY23;Yb = 23 CIE L#55 PREFERENCES_GREY30;Yb = 30 CIE L#60 PREFERENCES_GREY40;Yb = 40 CIE L#70 -PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes -PREFERENCES_GREYSC18;Yb = 18 CIE L#49 PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) +PREFERENCES_GREYSC18;Yb = 18 CIE L#49 PREFERENCES_GREYSCA;Automatisch PREFERENCES_HISTOGRAMPOSITIONLEFT;Histogramm linksseitig PREFERENCES_HISTOGRAMWORKING;Das Arbeitsprofil zur Darstellung des Haupthistogramms verwenden @@ -1440,9 +1439,9 @@ TP_DIRPYRDENOISE_MANU;Benutzerdefiniert TP_DIRPYRDENOISE_MED;Medianfilter TP_DIRPYRDENOISE_MEDMETHOD;Medianmethode TP_DIRPYRDENOISE_MEDTYPE;Mediantyp +TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD11;Qualität TP_DIRPYRDENOISE_METHOD11_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. -TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD_TOOLTIP;Für RAW-Bilder kann entweder die RGB-\noder L*a*b*-Methode verwendet werden.\n\nFür andere Bilder wird unabhängig von der\nAuswahl immer die L*a*b*-Methode verwendet. TP_DIRPYRDENOISE_METM_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MET_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -1771,8 +1770,8 @@ TP_RETINEX_SLOPE;Gammasteigung TP_RETINEX_STRENGTH;Intensität TP_RETINEX_THRESHOLD;Schwelle TP_RETINEX_THRESHOLD_TOOLTIP;Limitiert den Bereich der Transmissionskurve. -TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 +TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 TP_RETINEX_TLABEL_TOOLTIP;Ergebnis der Transmissionskurve: Min, Max, Mittel und Sigma\nMin und Max hat Einfluss auf die Abweichung.\n\nTmin = Kleinster Wert der Transmissionskurve\nTmax = Größter Wert der Transmissionskurve TP_RETINEX_TRANF;Transmission TP_RETINEX_TRANSMISSION;Transmissionskurve @@ -1784,8 +1783,8 @@ TP_RETINEX_VIEW;Vorschau TP_RETINEX_VIEW_MASK;Maske TP_RETINEX_VIEW_METHOD_TOOLTIP;Standard: Normale Anzeige\n\nMaske: Zeigt die Maske an\n\nUnschärfemaske: Zeigt das Bild mit einem großen\nUnschärfemaskenradius an.\n\nTransmission-Auto / Fest: Zeigt die Transmissionskarte\nvor der Anwendung von Kontrast und Helligkeit an.\n\nACHTUNG: Die Maske zeigt nicht das Endergebnis, sondern\nverstärkt den Effekt um ihn besser beurteilen zu können. TP_RETINEX_VIEW_NONE;Standard -TP_RETINEX_VIEW_TRAN2;Transmission - Fest TP_RETINEX_VIEW_TRAN;Transmission - Auto +TP_RETINEX_VIEW_TRAN2;Transmission - Fest TP_RETINEX_VIEW_UNSHARP;Unschärfemaske TP_RGBCURVES_BLUE;B TP_RGBCURVES_CHANNEL;Kanal @@ -1909,12 +1908,12 @@ TP_WAVELET_CURVEEDITOR_CL_TOOLTIP;Wendet eine Kontrasthelligkeitskurve\nam Ende TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Farbton als Funktion des Farbtons H = f(H) TP_WAVELET_DALL;Alle Richtungen -TP_WAVELET_DAUB10;D10 - mittel -TP_WAVELET_DAUB14;D14 - hoch +TP_WAVELET_DAUB;Kantenperformance TP_WAVELET_DAUB2;D2 - niedrig TP_WAVELET_DAUB4;D4 - Standard TP_WAVELET_DAUB6;D6 - Standard Plus -TP_WAVELET_DAUB;Kantenperformance +TP_WAVELET_DAUB10;D10 - mittel +TP_WAVELET_DAUB14;D14 - hoch TP_WAVELET_DAUB_TOOLTIP;Ändert den Daubechies-Koeffizienten:\nD4 = Standard\nD14 = Häufig bestes Ergebnis auf Kosten\nvon ca. 10% längerer Verarbeitungszeit.\n\nVerbessert die Kantenerkennung sowie die Qualität\nder ersten Waveletebene. Jedoch hängt die Qualität\nnicht ausschließlich mit diesem Koeffizienten zusammen\nund kann je nach Bild und Einsatz variieren. TP_WAVELET_DONE;Vertikal TP_WAVELET_DTHR;Diagonal @@ -1925,8 +1924,8 @@ TP_WAVELET_EDGCONT_TOOLTIP;Verschieben der Punkte nach links, verringert den Kon TP_WAVELET_EDGE;Kantenschärfung TP_WAVELET_EDGEAMPLI;Grundverstärkung TP_WAVELET_EDGEDETECT;Gradientenempfindlichkeit -TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) +TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;Schwelle der Kantenerkennung für feine Details.\nVerhindert die Schärfung von Rauschen. TP_WAVELET_EDGEDETECT_TOOLTIP;Verschieben des Reglers nach rechts erhöht die\nKantenempfindlichkeit. Die Einstellung wirkt sich\nauf den lokalen Kontrast, Kanteneinstellungen und\nRauschen aus. TP_WAVELET_EDGESENSI;Kantenempfindlichkeit @@ -2002,9 +2001,9 @@ TP_WAVELET_STREN;Intensität TP_WAVELET_STRENGTH;Intensität TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Schatten Schwelle +TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD2;Schattenebenen TP_WAVELET_THRESHOLD2_TOOLTIP;Legt die Ebene der Untergrenze (9 minus Wert)\nfür den Schatten-Luminanzbereich fest. Der\nmaximal mögliche Wert wird vom Wert der Lichter-\nebenen begrenzt.\n\nBeeinflussbare Ebenen: Untergrenze bis Ebene 9 -TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD_TOOLTIP;Legt die Ebene der Obergrenze für den Lichter\n-Luminanzbereich fest. Der Wert begrenzt die\nmaximal möglichen Schattenebenen.\n\nBeeinflussbare Ebenen: Ebene 1 bis Obergrenze TP_WAVELET_THRH;Lichter Schwelle TP_WAVELET_TILESBIG;Große Kacheln @@ -2027,9 +2026,6 @@ TP_WBALANCE_FLASH55;Leica TP_WBALANCE_FLASH60;Standard, Canon, Pentax, Olympus TP_WBALANCE_FLASH65;Nikon, Panasonic, Sony, Minolta TP_WBALANCE_FLASH_HEADER;Blitz -TP_WBALANCE_FLUO10;F10 - Philips TL85 -TP_WBALANCE_FLUO11;F11 - Philips TL84 -TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO1;F1 - Tageslicht TP_WBALANCE_FLUO2;F2 - Kaltweiß TP_WBALANCE_FLUO3;F3 - Weiß @@ -2039,6 +2035,9 @@ TP_WBALANCE_FLUO6;F6 - Weiß reduziert TP_WBALANCE_FLUO7;F7 - D65 Tageslichtsimulation TP_WBALANCE_FLUO8;F8 - D50 / Sylvania F40 Design TP_WBALANCE_FLUO9;F9 - Kaltweiß Deluxe +TP_WBALANCE_FLUO10;F10 - Philips TL85 +TP_WBALANCE_FLUO11;F11 - Philips TL84 +TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO_HEADER;Leuchtstofflampe TP_WBALANCE_GREEN;Tönung TP_WBALANCE_GTI;GTI @@ -2071,3 +2070,4 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Ausschnitt an Bildschirm anpassen\nTaste: Alt ZOOMPANEL_ZOOMFITSCREEN;An Bildschirm anpassen\nTaste: f ZOOMPANEL_ZOOMIN;Hineinzoomen\nTaste: + ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - + diff --git a/rtdata/languages/English (UK) b/rtdata/languages/English (UK) index 30e9bffab..cb0bfa7ad 100644 --- a/rtdata/languages/English (UK) +++ b/rtdata/languages/English (UK) @@ -1019,7 +1019,7 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !PREFERENCES_EXPAUT;Expert !PREFERENCES_EXTERNALEDITOR;External Editor !PREFERENCES_FBROWSEROPTS;File Browser / Thumbnail Options -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILEFORMAT;File format !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field @@ -1113,7 +1113,7 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTLANG;Select language !PREFERENCES_SELECTTHEME;Select theme !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index 63fe55143..f9aa862f4 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -948,7 +948,7 @@ !PREFERENCES_EXPAUT;Expert !PREFERENCES_EXTERNALEDITOR;External Editor !PREFERENCES_FBROWSEROPTS;File Browser / Thumbnail Options -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILEFORMAT;File format !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field @@ -1049,7 +1049,7 @@ !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SELECTLANG;Select language !PREFERENCES_SELECTTHEME;Select theme diff --git a/rtdata/languages/Euskara b/rtdata/languages/Euskara index 5bc77fdd8..7dce64cdf 100644 --- a/rtdata/languages/Euskara +++ b/rtdata/languages/Euskara @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Greek b/rtdata/languages/Greek index 6643930de..e71a2c1e0 100644 --- a/rtdata/languages/Greek +++ b/rtdata/languages/Greek @@ -1114,7 +1114,7 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1188,7 +1188,7 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Hebrew b/rtdata/languages/Hebrew index 1c3a97c16..d0a5d9023 100644 --- a/rtdata/languages/Hebrew +++ b/rtdata/languages/Hebrew @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;מידת חום !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;מידת חום !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Latvian b/rtdata/languages/Latvian index bc3546cba..4594b8250 100644 --- a/rtdata/languages/Latvian +++ b/rtdata/languages/Latvian @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Norsk BM b/rtdata/languages/Norsk BM index 79ad3ebfd..2525bb55e 100644 --- a/rtdata/languages/Norsk BM +++ b/rtdata/languages/Norsk BM @@ -1114,7 +1114,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1188,7 +1188,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Portugues (Brasil) b/rtdata/languages/Portugues (Brasil) index b7ab9b7bf..a08f2453d 100644 --- a/rtdata/languages/Portugues (Brasil) +++ b/rtdata/languages/Portugues (Brasil) @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;Temperatura !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;Temperatura !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Slovak b/rtdata/languages/Slovak index 070c2f4a1..4fbe51c42 100644 --- a/rtdata/languages/Slovak +++ b/rtdata/languages/Slovak @@ -1165,7 +1165,7 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !PREFERENCES_DAUB_TOOLTIP;The Noise Reduction and Wavelet Levels tools use a Debauchies mother wavelet. If you choose D6 instead of D4 you increase the number of orthogonal Daubechies coefficients and probably increase quality of small-scale levels. There is no memory or processing time difference between the two. !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found diff --git a/rtdata/languages/Suomi b/rtdata/languages/Suomi index d14a02808..aeed6500f 100644 --- a/rtdata/languages/Suomi +++ b/rtdata/languages/Suomi @@ -1116,7 +1116,7 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1190,7 +1190,7 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files diff --git a/rtdata/languages/Turkish b/rtdata/languages/Turkish index 03eb15ca3..e9da40531 100644 --- a/rtdata/languages/Turkish +++ b/rtdata/languages/Turkish @@ -1115,7 +1115,7 @@ TP_WBALANCE_TEMPERATURE;Isı !PREFERENCES_DIRDARKFRAMES;Dark-frames directory !PREFERENCES_EDITORLAYOUT;Editor Layout !PREFERENCES_EXPAUT;Expert -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar (de-select for low resolution display) +!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Single row file browser toolbar\n(de-select for low resolution display) !PREFERENCES_FILMSIMULATION;Film Simulation !PREFERENCES_FLATFIELD;Flat-Field !PREFERENCES_FLATFIELDFOUND;Found @@ -1189,7 +1189,7 @@ TP_WBALANCE_TEMPERATURE;Isı !PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". !PREFERENCES_RGBDTL_LABEL;Max number of threads for Noise Reduction and Wavelet Levels !PREFERENCES_RGBDTL_TOOLTIP;Leave the setting at "0" to automatically use as many threads as possible. The more threads run in parallel, the faster the computation. Refer to RawPedia for memory requirements. -!PREFERENCES_SELECTFONT;Select global font +!PREFERENCES_SELECTFONT;Select main font !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings !PREFERENCES_SERIALIZE_TIFF_READ_LABEL;Serialize read of tiff files From ef754f7593811182f0bfc640ea7a64b0130d7fd0 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 18 Feb 2017 16:01:41 +0100 Subject: [PATCH 101/181] LCP vignetting correction only works with undemosaiced raw files. fixes #3702 --- rtengine/rawimagesource.cc | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 049c509f5..5d4f51934 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1769,14 +1769,31 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); +if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for #endif - for (int y = 0; y < H; y++) { - for (int x = 0; x < W; x++) { - if (rawData[y][x] > 0) { - rawData[y][x] *= map.calcVignetteFac(x, y); + for (int y = 0; y < H; y++) { + for (int x = 0; x < W; x++) { + if (rawData[y][x] > 0) { + rawData[y][x] *= map.calcVignetteFac(x, y); + } + } + } + } else if(ri->get_colors() == 3) { +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H; y++) { + for (int x = 0; x < W; x++) { + float vignFactor = map.calcVignetteFac(x, y); + for(int c = 0;c < 3; ++c) { + if (rawData[y][3 * x + c] > 0) { + rawData[y][3 * x + c] *= vignFactor; + } + } } } } From 94129861f5366a620de1ab3d99855656793f23e5 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 19 Feb 2017 20:36:45 +0100 Subject: [PATCH 102/181] Speedup for lcp vignetting correction --- rtengine/lcp.cc | 66 ++++++++++++++++++++++++++------------ rtengine/lcp.h | 15 ++++++--- rtengine/rawimagesource.cc | 24 +++++++++++--- 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index ce1a6c67e..8579efcbd 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -19,32 +19,28 @@ #include #include "lcp.h" -#include "iccmatrices.h" -#include "iccstore.h" -#include "rawimagesource.h" -#include "improcfun.h" -#include "rt_math.h" +#include #ifdef WIN32 #include -// for GCC32 -#ifndef _WIN32_IE -#define _WIN32_IE 0x0600 -#endif #include #endif using namespace std; using namespace rtengine; -using namespace rtexif; LCPModelCommon::LCPModelCommon() { focLenX = focLenY = -1; imgXCenter = imgYCenter = 0.5; - x0 = y0 = fx = fy = meanErr = 0; + x0 = y0 = fx = fy = rfx = rfy = meanErr = 0; + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + x0v = y0v = rfxv = rfyv = ZEROV; +#endif + badErr = false; for (int i = 0; i < 5; i++) { @@ -66,7 +62,7 @@ void LCPModelCommon::print() const printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); } -// weightened merge two parameters +// weighted merge two parameters void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) { float facB = 1 - facA; @@ -81,6 +77,20 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo for (int i = 0; i < 5; i++) { param[i] = facA * a.param[i] + facB * b.param[i]; } + + double param0Sqr = param[0] * param[0]; + + vignParam[0] = - param[0]; + vignParam[1] = param0Sqr - param[1]; + vignParam[2] = param0Sqr * param[0] - 2. * param[0] * param[1] + param[2]; + vignParam[3] = param0Sqr * param0Sqr + param[1] * param[1] + 2. * param[0] * param[2] - 3. * param0Sqr * param[1]; + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + vignParamv[0] = F2V(vignParam[0]); + vignParamv[1] = F2V(vignParam[1]); + vignParamv[2] = F2V(vignParam[2]); + vignParamv[3] = F2V(vignParam[3]); +#endif // __SSE2__ } void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) @@ -113,6 +123,15 @@ void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLen fx = focLenX * Dmax; fy = focLenY * Dmax; } + rfx = 1.0 / fx; + rfy = 1.0 / fy; + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + x0v = F2V(x0); + y0v = F2V(y0); + rfxv = F2V(rfx); + rfyv = F2V(rfy); +#endif //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } @@ -264,18 +283,25 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const float LCPMapper::calcVignetteFac(int x, int y) const { // No need for swapXY, since vignette is in RAW and always before rotation - double xd = ((double)x - mc.x0) / mc.fx, yd = ((double)y - mc.y0) / mc.fy; + float xd = ((float)x - mc.x0) * mc.rfx, yd = ((float)y - mc.y0) * mc.rfy; - const float* aVig = mc.param; - double rsqr = xd * xd + yd * yd; - double param0Sqr = aVig[0] * aVig[0]; + const float* vignParam = mc.vignParam; + float rsqr = xd * xd + yd * yd; - return 1. + rsqr * (-aVig[0] + rsqr * ((param0Sqr - aVig[1]) - - (param0Sqr * aVig[0] - 2.*aVig[0] * aVig[1] + aVig[2]) * rsqr - + (param0Sqr * param0Sqr + aVig[1] * aVig[1] - + 2.*aVig[0] * aVig[2] - 3.*param0Sqr * aVig[1]) * rsqr * rsqr)); + return 1.f + rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); } +#if defined( __SSE2__ ) && defined( __x86_64__ ) +vfloat LCPMapper::calcVignetteFac(vfloat x, vfloat y) const +{ + // No need for swapXY, since vignette is in RAW and always before rotation + vfloat xd = (x - mc.x0v) * mc.rfxv, yd = (y - mc.y0v) * mc.rfyv; + vfloat rsqr = xd * xd + yd * yd; + + return F2V(1.f) + rsqr * (mc.vignParamv[0] + rsqr * (mc.vignParamv[1] - mc.vignParamv[2] * rsqr + mc.vignParamv[3] * rsqr * rsqr)); +} +#endif + LCPProfile::LCPProfile(const Glib::ustring &fname) { const int BufferSize = 8192; diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 908bacf56..74d06c958 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -21,10 +21,9 @@ #define _LCP_ #include "imagefloat.h" -#include "../rtgui/threadutils.h" +#include "opthelper.h" #include #include -#include #include #include @@ -40,8 +39,13 @@ public: double meanErr; bool badErr; - double x0, y0, fx, fy; // prepared params - + float x0, y0, fx, fy; // prepared params + float rfx, rfy; + float vignParam[4]; +#if defined( __SSE2__ ) && defined( __x86_64__ ) + vfloat vignParamv[4] ALIGNED16; + vfloat x0v, y0v, rfxv, rfyv; +#endif LCPModelCommon(); bool empty() const; // is it empty void print() const; // printf all values @@ -133,6 +137,9 @@ public: void correctDistortion(double& x, double& y) const; // MUST be the first stage void correctCA(double& x, double& y, int channel) const; float calcVignetteFac (int x, int y) const; // MUST be in RAW +#if defined( __SSE2__ ) && defined( __x86_64__ ) + vfloat calcVignetteFac(vfloat x, vfloat y) const; +#endif }; } #endif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 9c8f9788b..47613cf1a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1767,15 +1767,31 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 + StopWatch Stop1("lcp vignette correction"); LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); -if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { + if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H; y++) { - for (int x = 0; x < W; x++) { + int x = 0; + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + vfloat yv = F2V(y); + vfloat fourv = F2V(4.f); + vfloat onev = F2V(1.f); + vfloat xv = _mm_set_ps(3,2,1,0); + for (; x < W-3; x+=4) { + vfloat vignFactorv = map.calcVignetteFac(xv, yv); + vfloat rawValv = LVFU(rawData[y][x]); + rawValv *= vself(vmaskf_gt(rawValv, ZEROV), vignFactorv, onev); + STVFU(rawData[y][x], rawValv); + xv += fourv; + } +#endif + for (; x < W; x++) { if (rawData[y][x] > 0) { rawData[y][x] *= map.calcVignetteFac(x, y); } @@ -1783,7 +1799,7 @@ if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || } } else if(ri->get_colors() == 3) { #ifdef _OPENMP - #pragma omp parallel for + #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H; y++) { From bb300c53c76af9b75bc27c28192c2cea4692181e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 20 Feb 2017 09:55:57 +0100 Subject: [PATCH 103/181] improve readability of the partial profile paste dialog under the default theme --- rtdata/themes/RawTherapee-GTK3-20_.css | 12 ++++++++++++ rtdata/themes/RawTherapee-GTK3-_19.css | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index a672c66d8..d8f51d962 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -826,3 +826,15 @@ paned.vertical > separator { #RightNotebook scrolledwindow { padding: 4px; } + + +/* make the "partial profile" dialog a little bit more readable */ +#PartialPasteHeader { + margin: 1.5em 0 0 0; + padding: 0; + font-weight: bold; +} + +#PartialPasteHeaderSep { + color: #D8D8D8; +} diff --git a/rtdata/themes/RawTherapee-GTK3-_19.css b/rtdata/themes/RawTherapee-GTK3-_19.css index d9d61206b..8263dd857 100644 --- a/rtdata/themes/RawTherapee-GTK3-_19.css +++ b/rtdata/themes/RawTherapee-GTK3-_19.css @@ -487,3 +487,16 @@ GtkNotebook { .tooltip { padding: 0; } + + +/* make the "partial profile" dialog a little bit more readable */ +#PartialPasteHeader { + margin: 1.5em 0 0 0; + padding: 0; + font-weight: bold; + color: #363636; +} + +#PartialPasteHeaderSep { + color: #D8D8D8; +} From 658a826f8a590eaadc1d9b9f0e284a84b48564a2 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 21 Feb 2017 10:12:17 +0100 Subject: [PATCH 104/181] applied fix to the GTK >= 3.20 theme (as suggested by TooWaBoo) --- rtdata/themes/RawTherapee-GTK3-20_.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index d8f51d962..19bd6ac9c 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -836,5 +836,5 @@ paned.vertical > separator { } #PartialPasteHeaderSep { - color: #D8D8D8; + background-color: #D8D8D8; } From 87a280f8cafeb8aeaab81292b12a4b7fec61dcc3 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 21 Feb 2017 19:11:54 +0100 Subject: [PATCH 105/181] Additional speedup for lcp vignette correction --- rtengine/lcp.cc | 77 ++++++++++++++++++++++++++------------ rtengine/lcp.h | 9 +---- rtengine/rawimagesource.cc | 31 ++------------- 3 files changed, 58 insertions(+), 59 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 8579efcbd..5dd51b58f 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -37,10 +37,6 @@ LCPModelCommon::LCPModelCommon() imgXCenter = imgYCenter = 0.5; x0 = y0 = fx = fy = rfx = rfy = meanErr = 0; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - x0v = y0v = rfxv = rfyv = ZEROV; -#endif - badErr = false; for (int i = 0; i < 5; i++) { @@ -85,12 +81,6 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo vignParam[2] = param0Sqr * param[0] - 2. * param[0] * param[1] + param[2]; vignParam[3] = param0Sqr * param0Sqr + param[1] * param[1] + 2. * param[0] * param[2] - 3. * param0Sqr * param[1]; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - vignParamv[0] = F2V(vignParam[0]); - vignParamv[1] = F2V(vignParam[1]); - vignParamv[2] = F2V(vignParam[2]); - vignParamv[3] = F2V(vignParam[3]); -#endif // __SSE2__ } void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) @@ -126,13 +116,6 @@ void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLen rfx = 1.0 / fx; rfy = 1.0 / fy; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - x0v = F2V(x0); - y0v = F2V(y0); - rfxv = F2V(rfx); - rfyv = F2V(rfy); -#endif - //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } @@ -288,19 +271,65 @@ float LCPMapper::calcVignetteFac(int x, int y) const const float* vignParam = mc.vignParam; float rsqr = xd * xd + yd * yd; - return 1.f + rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); + return rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); } -#if defined( __SSE2__ ) && defined( __x86_64__ ) -vfloat LCPMapper::calcVignetteFac(vfloat x, vfloat y) const +SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation - vfloat xd = (x - mc.x0v) * mc.rfxv, yd = (y - mc.y0v) * mc.rfyv; - vfloat rsqr = xd * xd + yd * yd; + float yd = ((float)y - mc.y0) * mc.rfy; + yd *= yd; + int x = 0; +#ifdef __SSE2__ + const vfloat fourv = F2V(4.f); + const vfloat zerov = F2V(0.f); + const vfloat ydv = F2V(yd); + const vfloat p0 = F2V(mc.vignParam[0]); + const vfloat p1 = F2V(mc.vignParam[1]); + const vfloat p2 = F2V(mc.vignParam[2]); + const vfloat p3 = F2V(mc.vignParam[3]); + const vfloat x0v = F2V(mc.x0); + const vfloat rfxv = F2V(mc.rfx); - return F2V(1.f) + rsqr * (mc.vignParamv[0] + rsqr * (mc.vignParamv[1] - mc.vignParamv[2] * rsqr + mc.vignParamv[3] * rsqr * rsqr)); + vfloat xv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f); + for (; x < width-3; x+=4) { + vfloat xdv = (xv - x0v) * rfxv; + vfloat rsqr = xdv * xdv + ydv; + vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr)); + vfloat valv = LVFU(line[x]); + valv += valv * vselfzero(vmaskf_gt(valv, zerov), vignFactorv); + STVFU(line[x], valv); + xv += fourv; + } +#endif // __SSE2__ + for (; x < width; x++) { + if (line[x] > 0) { + float xd = ((float)x - mc.x0) * mc.rfx; + const float* vignParam = mc.vignParam; + float rsqr = xd * xd + yd; + line[x] += line[x] * rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); + } + } } -#endif + +SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float *line) const +{ + // No need for swapXY, since vignette is in RAW and always before rotation + float yd = ((float)y - mc.y0) * mc.rfy; + yd *= yd; + const float* vignParam = mc.vignParam; + for (int x = 0; x < width; x++) { + float xd = ((float)x - mc.x0) * mc.rfx; + float rsqr = xd * xd + yd; + float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); + for(int c = 0;c < 3; ++c) { + if (line[3*x+c] > 0) { + line[3*x+c] += line[3*x+c] * vignetteFactor; + } + } + } +} + LCPProfile::LCPProfile(const Glib::ustring &fname) { diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 74d06c958..449add15c 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -42,10 +42,6 @@ public: float x0, y0, fx, fy; // prepared params float rfx, rfy; float vignParam[4]; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - vfloat vignParamv[4] ALIGNED16; - vfloat x0v, y0v, rfxv, rfyv; -#endif LCPModelCommon(); bool empty() const; // is it empty void print() const; // printf all values @@ -137,9 +133,8 @@ public: void correctDistortion(double& x, double& y) const; // MUST be the first stage void correctCA(double& x, double& y, int channel) const; float calcVignetteFac (int x, int y) const; // MUST be in RAW -#if defined( __SSE2__ ) && defined( __x86_64__ ) - vfloat calcVignetteFac(vfloat x, vfloat y) const; -#endif + void processVignetteLine(int width, int y, float *line) const; + void processVignetteLine3Channels(int width, int y, float *line) const; }; } #endif diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 47613cf1a..995e3d9bd 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1771,31 +1771,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { + #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) #endif for (int y = 0; y < H; y++) { - int x = 0; - -#if defined( __SSE2__ ) && defined( __x86_64__ ) - vfloat yv = F2V(y); - vfloat fourv = F2V(4.f); - vfloat onev = F2V(1.f); - vfloat xv = _mm_set_ps(3,2,1,0); - for (; x < W-3; x+=4) { - vfloat vignFactorv = map.calcVignetteFac(xv, yv); - vfloat rawValv = LVFU(rawData[y][x]); - rawValv *= vself(vmaskf_gt(rawValv, ZEROV), vignFactorv, onev); - STVFU(rawData[y][x], rawValv); - xv += fourv; - } -#endif - for (; x < W; x++) { - if (rawData[y][x] > 0) { - rawData[y][x] *= map.calcVignetteFac(x, y); - } - } + map.processVignetteLine(W, y, rawData[y]); } } else if(ri->get_colors() == 3) { #ifdef _OPENMP @@ -1803,14 +1785,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le #endif for (int y = 0; y < H; y++) { - for (int x = 0; x < W; x++) { - float vignFactor = map.calcVignetteFac(x, y); - for(int c = 0;c < 3; ++c) { - if (rawData[y][3 * x + c] > 0) { - rawData[y][3 * x + c] *= vignFactor; - } - } - } + map.processVignetteLine3Channels(W, y, rawData[y]); } } } From f3090d146f9c13b06ed19c3fd2b1194d8a9b1d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 21 Feb 2017 20:30:03 +0100 Subject: [PATCH 106/181] Add new `win.cmake` by @TooWaBoo --- win.cmake | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/win.cmake b/win.cmake index 072061d8f..0a32ac956 100644 --- a/win.cmake +++ b/win.cmake @@ -1,48 +1,50 @@ -# Use the 'Debug' build type to have a non optimized, with debugging information, with a console executable. -# Use the 'Release' build type to have an optimized, without debugging information, console-free executable. -# Use the 'RelWithDebInfo' build type to have an optimized, without debugging information, with a console executable. -# Use the 'MinSizeRel' build type to have the smallest possible, without debugging information, console-free executable. -# +# Use the 'Debug' build type to have a non optimized, with debugging information, with a console executable +# Use the 'Release' build type to have an optimized, without debugging information, console free executable +# Use the 'RelWithDebInfo' build type to have an optimized, without debugging information, with a console executable +# Use the 'MinSizeRel' build type to have the smallest possible, without debugging information, console free executable #set(CMAKE_BUILD_TYPE Release CACHE STRING "Between: None Debug Release RelWithDebInfo MinSizeRel.") -set(CMAKE_INSTALL_PREFIX ./Builds/${CMAKE_BUILD_TYPE} CACHE PATH "Library installation path") -set(DATADIR . CACHE PATH "Data installation path") +set(CMAKE_INSTALL_PREFIX ./${CMAKE_BUILD_TYPE} CACHE PATH "Libraries installation path") +set(DATADIR . CACHE PATH "Datas installation path") set(BINDIR . CACHE PATH "Binaries installation path") -set(LIBDIR . CACHE PATH "Library installation path") +set(LIBDIR . CACHE PATH "Libraries installation path") set(DOCDIR ./doc CACHE PATH "Documentation installation path") set(CREDITSDIR . CACHE PATH "Credit file installation path") set(LICENCEDIR . CACHE PATH "Licence file installation path") -set(BUILD_SHARED OFF CACHE BOOL "Should shared libraries be generated") +set(BUILD_SHARED OFF CACHE BOOL "Should RT generate shared libraries") set(OPTION_OMP ON CACHE BOOL "Use OpenMP to speedup the preview and batch processing") -# set WITH_MYFILE_MMAP to OFF if you experience crashes caused by thumbnail creation (it will be slower but more reliable). -set(WITH_MYFILE_MMAP ON CACHE BOOL "Use the MMAP mechanism to speedup thumbnail creation") +# set WITH_MYFILE_MMAP to OFF if you experience crash with thumbnail creation (it should be slower, but more reliable) +set(WITH_MYFILE_MMAP ON CACHE BOOL "Use the MMAP mechanism to speedup thumbnail creations") -set(CACHE_NAME_SUFFIX "" CACHE STRING "RawTherapee's cache folder suffix (leave empty to use default from UpdateIndo.cmake)") +set(CACHE_NAME_SUFFIX "" CACHE STRING "RawTherapee's cache folder suffix (leave empty to use the default suffix, i.e. latesttag)") -# Choose the target processor's number from ProcessorTargets.cmake. -set(PROC_TARGET_NUMBER 0 CACHE STRING "Target processor") +# This line will let you chose the target number, and the associated processor +set(PROC_TARGET_NUMBER 0 CACHE STRING "Target Processor") # If you want to force the target processor name when PROC_TARGET_NUMBER = 0 or 2, -# uncomment the next line and replace labelWithoutQuotes with a string. -#set (PROC_LABEL labelWithoutQuotes CACHE STRING "Target processor label") +# uncomment the next line and replace labelWithoutQuotes by its value +#set (PROC_LABEL labelWithoutQuotes CACHE STRING "Target Processor label") -# Important: MinGW-w64 users may need to specify the -m32 or -m64 flag in CMAKE_CXX_FLAGS, -# CMAKE_C_FLAGS and CMAKE_EXE_LINKER_FLAGS to select between 32-/64-bit builds. -set(CMAKE_CXX_FLAGS "-mwin32 -mthreads" CACHE STRING "Compiler options for C++ source files") +# Important: MinGW-w64 user may need to specify the -m32 or -m64 flag in CMAKE_CXX_FLAGS, +# CMAKE_C_FLAGS and CMAKE_EXE_LINKER_FLAGS to select between 32/64bit build +set(CMAKE_CXX_FLAGS "-mwin32 -m64 -mthreads -msse2" CACHE STRING "Compiler options for C++ source files") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g2" CACHE STRING "Compiler options for C++ source files and Debug target") -set(CMAKE_CXX_FLAGS_RELEASE "-mwindows -DNDEBUG -O2" CACHE STRING "Compiler options for C++ source files and Release target") +set(CMAKE_CXX_FLAGS_RELEASE "-mwindows -Wno-aggressive-loop-optimizations -DNDEBUG -O3" CACHE STRING "Compiler options for C++ source files and Release target") set(CMAKE_CXX_FLAGS_MINSIZEREL "-mwindows -DNDEBUG -Os" CACHE STRING "Compiler options for C++ source files and MinSizeRel target") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g" CACHE STRING "Compiler options for C++ source files and RelWithDebInfo target") -set(CMAKE_C_FLAGS "-mwin32 -mthreads" CACHE STRING "Compiler options for C source files") +# Uncomment the next line and set the right value to override the default value (special compiling flags for RTEngine) +#set(RTENGINE_CXX_FLAGS "-funroll-loops" CACHE STRING "Special compilation flags for RTEngine") + +set(CMAKE_C_FLAGS "-mwin32 -m64 -mthreads -msse2" CACHE STRING "Compiler options for C source files") set(CMAKE_C_FLAGS_DEBUG "-O0 -g2" CACHE STRING "Compiler options for C source files and Debug target") set(CMAKE_C_FLAGS_RELEASE "-mwindows -DNDEBUG -O2" CACHE STRING "Compiler options for C source files and Release target") set(CMAKE_C_FLAGS_MINSIZEREL "-mwindows -DNDEBUG -Os" CACHE STRING "Compiler options for C source files and MinSizeRel target") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g" CACHE STRING "Compiler options for C source files and RelWithDebInfo target") -set(CMAKE_EXE_LINKER_FLAGS "-mwin32 -mthreads -static-libgcc -Wl,--large-address-aware,--verbose" CACHE STRING "Linker options") +set(CMAKE_EXE_LINKER_FLAGS "-m64 -mthreads -static-libgcc" CACHE STRING "Linker options") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-O0" CACHE STRING "Linkage options for the Debug target") -set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-mwindows -s -O2" CACHE STRING "Linkage options for the Release target") +set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-mwindows -s -O3" CACHE STRING "Linkage options for the Release target") set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-mwindows -s -Os" CACHE STRING "Linkage options for the MinSizeRel target") set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "-s -O2" CACHE STRING "Linkage options for the RelWithDebInfo target") From 99309aa4ace072af51ce74614153c805ad13db9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 21 Feb 2017 21:10:33 +0100 Subject: [PATCH 107/181] Preliminary cleanup for `LCPModelCommon` --- rtengine/lcp.cc | 166 ++++++++++++++++++++++++------------------------ rtengine/lcp.h | 50 ++++++++++----- 2 files changed, 117 insertions(+), 99 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index 5dd51b58f..ff53ad30e 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ + +#include #include #include "lcp.h" @@ -31,29 +33,33 @@ using namespace std; using namespace rtengine; -LCPModelCommon::LCPModelCommon() +LCPModelCommon::LCPModelCommon() : + foc_len_x(-1.0f), + foc_len_y(-1.0f), + img_center_x(0.5f), + img_center_y(0.5f), + param{{}}, + scale_factor(1.0f), + mean_error(0.0), + bad_error(false), + x0(0.0f), + y0(0.0f), + fx(0.0f), + fy(0.0f), + rfx(0.0f), + rfy(0.0f), + vign_param{{}} { - focLenX = focLenY = -1; - imgXCenter = imgYCenter = 0.5; - x0 = y0 = fx = fy = rfx = rfy = meanErr = 0; - - badErr = false; - - for (int i = 0; i < 5; i++) { - param[i] = 0; - } - - scaleFac = 1; } bool LCPModelCommon::empty() const { - return param[0] == 0 && param[1] == 0 && param[2] == 0; + return param[0] == 0.0f && param[1] == 0.0f && param[2] == 0.0f; } void LCPModelCommon::print() const { - printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", focLenX, focLenY, imgXCenter, imgYCenter, scaleFac, meanErr); + printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); } @@ -61,60 +67,56 @@ void LCPModelCommon::print() const // weighted merge two parameters void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) { - float facB = 1 - facA; + const float facB = 1.0f - facA; - focLenX = facA * a.focLenX + facB * b.focLenX; - focLenY = facA * a.focLenY + facB * b.focLenY; - imgXCenter = facA * a.imgXCenter + facB * b.imgXCenter; - imgYCenter = facA * a.imgYCenter + facB * b.imgYCenter; - scaleFac = facA * a.scaleFac + facB * b.scaleFac; - meanErr = facA * a.meanErr + facB * b.meanErr; + foc_len_x = facA * a.foc_len_x + facB * b.foc_len_x; + foc_len_y = facA * a.foc_len_y + facB * b.foc_len_y; + img_center_x = facA * a.img_center_x + facB * b.img_center_x; + img_center_y = facA * a.img_center_y + facB * b.img_center_y; + scale_factor = facA * a.scale_factor + facB * b.scale_factor; + mean_error = facA * a.mean_error + facB * b.mean_error; for (int i = 0; i < 5; i++) { param[i] = facA * a.param[i] + facB * b.param[i]; } - double param0Sqr = param[0] * param[0]; + const float param0Sqr = param[0] * param[0]; - vignParam[0] = - param[0]; - vignParam[1] = param0Sqr - param[1]; - vignParam[2] = param0Sqr * param[0] - 2. * param[0] * param[1] + param[2]; - vignParam[3] = param0Sqr * param0Sqr + param[1] * param[1] + 2. * param[0] * param[2] - 3. * param0Sqr * param[1]; + vign_param[0] = -param[0]; + vign_param[1] = param0Sqr - param[1]; + vign_param[2] = param0Sqr * param[0] - 2.0f * param[0] * param[1] + param[2]; + vign_param[3] = param0Sqr * param0Sqr + param[1] * param[1] + 2.0f * param[0] * param[2] - 3.0f * param0Sqr * param[1]; } void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) { // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter - int Dmax = fullWidth; - - if (fullHeight > fullWidth) { - Dmax = fullHeight; - } + const int Dmax = std::max(fullWidth, fullHeight); // correct focLens - if (focLenX < 0) { // they may not be given + if (foc_len_x < 0.0f) { // they may not be given // and 35mm may not be given either - if (focalLength35mm < 1) { + if (focalLength35mm < 1.0f) { focalLength35mm = focalLength * sensorFormatFactor; } - focLenX = focLenY = focalLength / ( 35 * focalLength / focalLength35mm); // focLen must be calculated in pixels + foc_len_x = foc_len_y = focalLength / (35.0f * focalLength / focalLength35mm); // focLen must be calculated in pixels } if (swapXY) { - x0 = (mirrorX ? 1. - imgYCenter : imgYCenter) * fullWidth; - y0 = (mirrorY ? 1. - imgXCenter : imgXCenter) * fullHeight; - fx = focLenY * Dmax; - fy = focLenX * Dmax; + x0 = (mirrorX ? 1.0f - img_center_y : img_center_y) * fullWidth; + y0 = (mirrorY ? 1.0f - img_center_x : img_center_x) * fullHeight; + fx = foc_len_y * Dmax; + fy = foc_len_x * Dmax; } else { - x0 = (mirrorX ? 1. - imgXCenter : imgXCenter) * fullWidth; - y0 = (mirrorY ? 1. - imgYCenter : imgYCenter) * fullHeight; - fx = focLenX * Dmax; - fy = focLenY * Dmax; + x0 = (mirrorX ? 1.0f - img_center_x : img_center_x) * fullWidth; + y0 = (mirrorY ? 1.0f - img_center_y : img_center_y) * fullHeight; + fx = foc_len_x * Dmax; + fy = foc_len_y * Dmax; } - rfx = 1.0 / fx; - rfy = 1.0 / fy; + rfx = 1.0f / fx; + rfy = 1.0f / fy; //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } @@ -127,9 +129,9 @@ LCPPersModel::LCPPersModel() // mode: 0=distortion, 1=vignette, 2=CA bool LCPPersModel::hasModeData(int mode) const { - return (mode == 0 && !vignette.empty() && !vignette.badErr) || (mode == 1 && !base.empty() && !base.badErr) + return (mode == 0 && !vignette.empty() && !vignette.bad_error) || (mode == 1 && !base.empty() && !base.bad_error) || (mode == 2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() && - !chromRG.badErr && !chromG.badErr && !chromBG.badErr); + !chromRG.bad_error && !chromG.bad_error && !chromBG.bad_error); } void LCPPersModel::print() const @@ -202,7 +204,7 @@ void LCPMapper::correctDistortion(double& x, double& y) const { double xd = (x - mc.x0) / mc.fx, yd = (y - mc.y0) / mc.fy; - const float* aDist = mc.param; + const LCPModelCommon::Param aDist = mc.param; double rsqr = xd * xd + yd * yd; double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3]; @@ -230,7 +232,7 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const // Green contains main distortion, just like base if (useCADist) { - const float* aDist = chrom[1].param; + const LCPModelCommon::Param aDist = chrom[1].param; double rsqr = xd * xd + yd * yd; double xfac = aDist[swapXY ? 3 : 4], yfac = aDist[swapXY ? 4 : 3]; @@ -254,12 +256,12 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const yd = ygreen; rsqr = xd * xd + yd * yd; - const float* aCA = chrom[channel].param; + const LCPModelCommon::Param aCA = chrom[channel].param; double xfac = aCA[swapXY ? 3 : 4], yfac = aCA[swapXY ? 4 : 3]; double commonSum = 1. + rsqr * (aCA[0] + rsqr * (aCA[1] + aCA[2] * rsqr)) + 2. * (yfac * yd + xfac * xd); - x = (chrom[channel].scaleFac * ( xd * commonSum + xfac * rsqr )) * chrom[channel].fx + chrom[channel].x0; - y = (chrom[channel].scaleFac * ( yd * commonSum + yfac * rsqr )) * chrom[channel].fy + chrom[channel].y0; + x = (chrom[channel].scale_factor * ( xd * commonSum + xfac * rsqr )) * chrom[channel].fx + chrom[channel].x0; + y = (chrom[channel].scale_factor * ( yd * commonSum + yfac * rsqr )) * chrom[channel].fy + chrom[channel].y0; } } @@ -268,7 +270,7 @@ float LCPMapper::calcVignetteFac(int x, int y) const // No need for swapXY, since vignette is in RAW and always before rotation float xd = ((float)x - mc.x0) * mc.rfx, yd = ((float)y - mc.y0) * mc.rfy; - const float* vignParam = mc.vignParam; + const LCPModelCommon::VignParam vignParam = mc.vign_param; float rsqr = xd * xd + yd * yd; return rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); @@ -284,10 +286,10 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c const vfloat fourv = F2V(4.f); const vfloat zerov = F2V(0.f); const vfloat ydv = F2V(yd); - const vfloat p0 = F2V(mc.vignParam[0]); - const vfloat p1 = F2V(mc.vignParam[1]); - const vfloat p2 = F2V(mc.vignParam[2]); - const vfloat p3 = F2V(mc.vignParam[3]); + const vfloat p0 = F2V(mc.vign_param[0]); + const vfloat p1 = F2V(mc.vign_param[1]); + const vfloat p2 = F2V(mc.vign_param[2]); + const vfloat p3 = F2V(mc.vign_param[3]); const vfloat x0v = F2V(mc.x0); const vfloat rfxv = F2V(mc.rfx); @@ -305,7 +307,7 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c for (; x < width; x++) { if (line[x] > 0) { float xd = ((float)x - mc.x0) * mc.rfx; - const float* vignParam = mc.vignParam; + const LCPModelCommon::VignParam vignParam = mc.vign_param; float rsqr = xd * xd + yd; line[x] += line[x] * rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); } @@ -317,7 +319,7 @@ SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; - const float* vignParam = mc.vignParam; + const LCPModelCommon::VignParam vignParam = mc.vign_param; for (int x = 0; x < width; x++) { float xd = ((float)x - mc.x0) * mc.rfx; float rsqr = xd * xd + yd; @@ -391,17 +393,17 @@ int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { if (aPersModel[pm]->hasModeData(0)) { - errVignette += aPersModel[pm]->vignette.meanErr; + errVignette += aPersModel[pm]->vignette.mean_error; vignetteCount++; } if (aPersModel[pm]->hasModeData(1)) { - errBase += aPersModel[pm]->base.meanErr; + errBase += aPersModel[pm]->base.mean_error; baseCount++; } if (aPersModel[pm]->hasModeData(2)) { - errChrom += std::max(std::max(aPersModel[pm]->chromRG.meanErr, aPersModel[pm]->chromG.meanErr), aPersModel[pm]->chromBG.meanErr); + errChrom += std::max(std::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error), aPersModel[pm]->chromBG.mean_error); chromCount++; } } @@ -424,20 +426,20 @@ int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) // Now mark all the bad ones as bad, and hasModeData will return false; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.meanErr > maxAvgDevFac * errVignette) { - aPersModel[pm]->vignette.badErr = true; + if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { + aPersModel[pm]->vignette.bad_error = true; filtered++; } - if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.meanErr > maxAvgDevFac * errBase) { - aPersModel[pm]->base.badErr = true; + if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { + aPersModel[pm]->base.bad_error = true; filtered++; } if (aPersModel[pm]->hasModeData(2) && - (aPersModel[pm]->chromRG.meanErr > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.meanErr > maxAvgDevFac * errChrom - || aPersModel[pm]->chromBG.meanErr > maxAvgDevFac * errChrom)) { - aPersModel[pm]->chromRG.badErr = aPersModel[pm]->chromG.badErr = aPersModel[pm]->chromBG.badErr = true; + (aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * errChrom + || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * errChrom)) { + aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; filtered++; } } @@ -493,48 +495,48 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float if (aPersModel[pm]->hasModeData(mode)) { if (mode == 0) { - meanErr = aPersModel[pm]->vignette.meanErr; + meanErr = aPersModel[pm]->vignette.mean_error; // by aperture (vignette), and max out focus distance // tests showed doing this by log(aperture) is not as advisable if (aPersModel[pm]->focLen == bestFocLenLow && ( - (aper == aperture && pLow->vignette.meanErr > meanErr) + (aper == aperture && pLow->vignette.mean_error > meanErr) || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture) || (aper <= aperture && (pLow->aperture > aperture || fabs(aperture - aper) < fabs(aperture - pLow->aperture))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( - (aper == aperture && pHigh->vignette.meanErr > meanErr) + (aper == aperture && pHigh->vignette.mean_error > meanErr) || (aper <= aperture && aper > pHigh->aperture && pHigh->aperture < aperture) || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) { pHigh = aPersModel[pm]; } } else { - meanErr = (mode == 1 ? aPersModel[pm]->base.meanErr : aPersModel[pm]->chromG.meanErr); + meanErr = (mode == 1 ? aPersModel[pm]->base.mean_error : aPersModel[pm]->chromG.mean_error); if (focusDist > 0) { // by focus distance if (aPersModel[pm]->focLen == bestFocLenLow && ( - (focDist == focusDist && (mode == 1 ? pLow->base.meanErr : pLow->chromG.meanErr) > meanErr) + (focDist == focusDist && (mode == 1 ? pLow->base.mean_error : pLow->chromG.mean_error) > meanErr) || (focDist >= focusDist && focDist < pLow->focDist && pLow->focDist > focusDist) || (focDist <= focusDist && (pLow->focDist > focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pLow->focDist) + euler)))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( - (focDist == focusDist && (mode == 1 ? pHigh->base.meanErr : pHigh->chromG.meanErr) > meanErr) + (focDist == focusDist && (mode == 1 ? pHigh->base.mean_error : pHigh->chromG.mean_error) > meanErr) || (focDist <= focusDist && focDist > pHigh->focDist && pHigh->focDist < focusDist) || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pHigh->focDist) + euler)))))) { pHigh = aPersModel[pm]; } } else { // no focus distance available, just error - if (aPersModel[pm]->focLen == bestFocLenLow && (mode == 1 ? pLow->base.meanErr : pLow->chromG.meanErr) > meanErr) { + if (aPersModel[pm]->focLen == bestFocLenLow && (mode == 1 ? pLow->base.mean_error : pLow->chromG.mean_error) > meanErr) { pLow = aPersModel[pm]; } - if (aPersModel[pm]->focLen == bestFocLenHigh && (mode == 1 ? pHigh->base.meanErr : pHigh->chromG.meanErr) > meanErr) { + if (aPersModel[pm]->focLen == bestFocLenHigh && (mode == 1 ? pHigh->base.mean_error : pHigh->chromG.mean_error) > meanErr) { pHigh = aPersModel[pm]; } } @@ -770,17 +772,17 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in // Section depended if (!strcmp("FocalLengthX", tag)) { - pProf->pCurCommon->focLenX = atof(raw); + pProf->pCurCommon->foc_len_x = atof(raw); } else if (!strcmp("FocalLengthY", tag)) { - pProf->pCurCommon->focLenY = atof(raw); + pProf->pCurCommon->foc_len_y = atof(raw); } else if (!strcmp("ImageXCenter", tag)) { - pProf->pCurCommon->imgXCenter = atof(raw); + pProf->pCurCommon->img_center_x = atof(raw); } else if (!strcmp("ImageYCenter", tag)) { - pProf->pCurCommon->imgYCenter = atof(raw); + pProf->pCurCommon->img_center_y = atof(raw); } else if (!strcmp("ScaleFactor", tag)) { - pProf->pCurCommon->scaleFac = atof(raw); + pProf->pCurCommon->scale_factor = atof(raw); } else if (!strcmp("ResidualMeanError", tag)) { - pProf->pCurCommon->meanErr = atof(raw); + pProf->pCurCommon->mean_error = atof(raw); } else if (!strcmp("RadialDistortParam1", tag) || !strcmp("VignetteModelParam1", tag)) { pProf->pCurCommon->param[0] = atof(raw); } else if (!strcmp("RadialDistortParam2", tag) || !strcmp("VignetteModelParam2", tag)) { diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 449add15c..92c984921 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -17,36 +17,52 @@ * along with RawTherapee. If not, see . */ -#ifndef _LCP_ -#define _LCP_ +#pragma once + +#include +#include +#include + +#include +#include #include "imagefloat.h" #include "opthelper.h" -#include -#include -#include -#include namespace rtengine { + // Perspective model common data, also used for Vignette and Fisheye -class LCPModelCommon +class LCPModelCommon final { public: - float focLenX, focLenY, imgXCenter, imgYCenter; - float param[5]; // k1..k5, resp. alpha1..5 - float scaleFac; // alpha0 - double meanErr; - bool badErr; - - float x0, y0, fx, fy; // prepared params - float rfx, rfy; - float vignParam[4]; LCPModelCommon(); bool empty() const; // is it empty void print() const; // printf all values void merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA); void prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY); + +//private: + using Param = std::array; + using VignParam = std::array; + + float foc_len_x; + float foc_len_y; + float img_center_x; + float img_center_y; + Param param; // k1..k5, resp. alpha1..5 + float scale_factor; // alpha0 + double mean_error; + bool bad_error; + + // prepared params + float x0; + float y0; + float fx; + float fy; + float rfx; + float rfy; + VignParam vign_param; }; class LCPPersModel @@ -136,5 +152,5 @@ public: void processVignetteLine(int width, int y, float *line) const; void processVignetteLine3Channels(int width, int y, float *line) const; }; + } -#endif From 1348ea06e4975d26b50f9e723ae61f87916dcf17 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 22 Feb 2017 01:53:22 +0100 Subject: [PATCH 108/181] Further cleanup and astyled lcp.* --- rtengine/lcp.cc | 401 ++++++++++++++++++++++++------------------------ rtengine/lcp.h | 31 ++-- 2 files changed, 212 insertions(+), 220 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index ff53ad30e..a870b6212 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -34,21 +34,21 @@ using namespace rtengine; LCPModelCommon::LCPModelCommon() : - foc_len_x(-1.0f), - foc_len_y(-1.0f), - img_center_x(0.5f), - img_center_y(0.5f), + foc_len_x (-1.0f), + foc_len_y (-1.0f), + img_center_x (0.5f), + img_center_y (0.5f), param{{}}, - scale_factor(1.0f), - mean_error(0.0), - bad_error(false), - x0(0.0f), - y0(0.0f), - fx(0.0f), - fy(0.0f), - rfx(0.0f), - rfy(0.0f), - vign_param{{}} +scale_factor (1.0f), + mean_error (0.0), + bad_error (false), + x0 (0.0f), + y0 (0.0f), + fx (0.0f), + fy (0.0f), + rfx (0.0f), + rfy (0.0f), +vign_param{{}} { } @@ -59,13 +59,13 @@ bool LCPModelCommon::empty() const void LCPModelCommon::print() const { - printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); - printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); - printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); + printf ("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); + printf ("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); + printf ("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); } // weighted merge two parameters -void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) +void LCPModelCommon::merge (const LCPModelCommon& a, const LCPModelCommon& b, float facA) { const float facB = 1.0f - facA; @@ -80,7 +80,7 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo param[i] = facA * a.param[i] + facB * b.param[i]; } - const float param0Sqr = param[0] * param[0]; + const double param0Sqr = param[0] * param[0]; vign_param[0] = -param[0]; vign_param[1] = param0Sqr - param[1]; @@ -89,10 +89,10 @@ void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, flo } -void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) +void LCPModelCommon::prepareParams (int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) { // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter - const int Dmax = std::max(fullWidth, fullHeight); + const int Dmax = std::max (fullWidth, fullHeight); // correct focLens if (foc_len_x < 0.0f) { // they may not be given @@ -115,8 +115,9 @@ void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLen fx = foc_len_x * Dmax; fy = foc_len_y * Dmax; } - rfx = 1.0f / fx; - rfy = 1.0f / fy; + + rfx = 1.0 / fx; // calculatiom with double precision doesn't cost anything at this step + rfy = 1.0 / fy; // " //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } @@ -127,7 +128,7 @@ LCPPersModel::LCPPersModel() } // mode: 0=distortion, 1=vignette, 2=CA -bool LCPPersModel::hasModeData(int mode) const +bool LCPPersModel::hasModeData (int mode) const { return (mode == 0 && !vignette.empty() && !vignette.bad_error) || (mode == 1 && !base.empty() && !base.bad_error) || (mode == 2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() && @@ -136,36 +137,36 @@ bool LCPPersModel::hasModeData(int mode) const void LCPPersModel::print() const { - printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); - printf("Base:\n"); + printf ("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); + printf ("Base:\n"); base.print(); if (!chromRG.empty()) { - printf("ChromRG:\n"); + printf ("ChromRG:\n"); chromRG.print(); } if (!chromG.empty()) { - printf("ChromG:\n"); + printf ("ChromG:\n"); chromG.print(); } if (!chromBG.empty()) { - printf("ChromBG:\n"); + printf ("ChromBG:\n"); chromBG.print(); } if (!vignette.empty()) { - printf("Vignette:\n"); + printf ("Vignette:\n"); vignette.print(); } - printf("\n"); + printf ("\n"); } // if !vignette then geometric and CA -LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, - int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) +LCPMapper::LCPMapper (LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, + int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) { if (pProf == nullptr) { return; @@ -186,21 +187,21 @@ LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm bool mirrorY = (rot == 180 || rot == 270); //printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); - pProf->calcParams(vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr); - mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); + pProf->calcParams (vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr); + mc.prepareParams (fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); if (!vignette) { - pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); + pProf->calcParams (2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); for (int i = 0; i < 3; i++) { - chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); + chrom[i].prepareParams (fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); } } enableCA = !vignette && focusDist > 0; } -void LCPMapper::correctDistortion(double& x, double& y) const +void LCPMapper::correctDistortion (double& x, double& y) const { double xd = (x - mc.x0) / mc.fx, yd = (y - mc.y0) / mc.fy; @@ -218,7 +219,7 @@ void LCPMapper::correctDistortion(double& x, double& y) const y = ynew * mc.fy + mc.y0; } -void LCPMapper::correctCA(double& x, double& y, int channel) const +void LCPMapper::correctCA (double& x, double& y, int channel) const { if (!enableCA) { return; @@ -265,47 +266,39 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const } } -float LCPMapper::calcVignetteFac(int x, int y) const -{ - // No need for swapXY, since vignette is in RAW and always before rotation - float xd = ((float)x - mc.x0) * mc.rfx, yd = ((float)y - mc.y0) * mc.rfy; - - const LCPModelCommon::VignParam vignParam = mc.vign_param; - float rsqr = xd * xd + yd * yd; - - return rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); -} - -SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const +SSEFUNCTION void LCPMapper::processVignetteLine (int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; int x = 0; #ifdef __SSE2__ - const vfloat fourv = F2V(4.f); - const vfloat zerov = F2V(0.f); - const vfloat ydv = F2V(yd); - const vfloat p0 = F2V(mc.vign_param[0]); - const vfloat p1 = F2V(mc.vign_param[1]); - const vfloat p2 = F2V(mc.vign_param[2]); - const vfloat p3 = F2V(mc.vign_param[3]); - const vfloat x0v = F2V(mc.x0); - const vfloat rfxv = F2V(mc.rfx); + const vfloat fourv = F2V (4.f); + const vfloat zerov = F2V (0.f); + const vfloat ydv = F2V (yd); + const vfloat p0 = F2V (mc.vign_param[0]); + const vfloat p1 = F2V (mc.vign_param[1]); + const vfloat p2 = F2V (mc.vign_param[2]); + const vfloat p3 = F2V (mc.vign_param[3]); + const vfloat x0v = F2V (mc.x0); + const vfloat rfxv = F2V (mc.rfx); - vfloat xv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f); - for (; x < width-3; x+=4) { + vfloat xv = _mm_setr_ps (0.f, 1.f, 2.f, 3.f); + + for (; x < width - 3; x += 4) { vfloat xdv = (xv - x0v) * rfxv; vfloat rsqr = xdv * xdv + ydv; vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr)); - vfloat valv = LVFU(line[x]); - valv += valv * vselfzero(vmaskf_gt(valv, zerov), vignFactorv); - STVFU(line[x], valv); + vfloat valv = LVFU (line[x]); + valv += valv * vselfzero (vmaskf_gt (valv, zerov), vignFactorv); + STVFU (line[x], valv); xv += fourv; } + #endif // __SSE2__ + for (; x < width; x++) { - if (line[x] > 0) { + if (line[x] > 0.f) { float xd = ((float)x - mc.x0) * mc.rfx; const LCPModelCommon::VignParam vignParam = mc.vign_param; float rsqr = xd * xd + yd; @@ -314,39 +307,40 @@ SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) c } } -SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float *line) const +void LCPMapper::processVignetteLine3Channels (int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; const LCPModelCommon::VignParam vignParam = mc.vign_param; + for (int x = 0; x < width; x++) { float xd = ((float)x - mc.x0) * mc.rfx; float rsqr = xd * xd + yd; float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); - for(int c = 0;c < 3; ++c) { - if (line[3*x+c] > 0) { - line[3*x+c] += line[3*x+c] * vignetteFactor; + + for (int c = 0; c < 3; ++c) { + if (line[3 * x + c] > 0) { + line[3 * x + c] += line[3 * x + c] * vignetteFactor; } } } } - -LCPProfile::LCPProfile(const Glib::ustring &fname) +LCPProfile::LCPProfile (const Glib::ustring &fname) { const int BufferSize = 8192; char buf[BufferSize]; - XML_Parser parser = XML_ParserCreate(nullptr); + XML_Parser parser = XML_ParserCreate (nullptr); if (!parser) { throw "Couldn't allocate memory for XML parser"; } - XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); - XML_SetCharacterDataHandler(parser, XmlTextHandler); - XML_SetUserData(parser, (void *)this); + XML_SetElementHandler (parser, XmlStartHandler, XmlEndHandler); + XML_SetCharacterDataHandler (parser, XmlTextHandler); + XML_SetUserData (parser, (void *)this); isFisheye = inCamProfiles = firstLIDone = inPerspect = inAlternateLensID = inAlternateLensNames = false; @@ -359,51 +353,51 @@ LCPProfile::LCPProfile(const Glib::ustring &fname) persModelCount = 0; *inInvalidTag = 0; - FILE *pFile = g_fopen(fname.c_str (), "rb"); + FILE *pFile = g_fopen (fname.c_str (), "rb"); bool done; do { - int bytesRead = (int)fread(buf, 1, BufferSize, pFile); - done = feof(pFile); + int bytesRead = (int)fread (buf, 1, BufferSize, pFile); + done = feof (pFile); - if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) { + if (XML_Parse (parser, buf, bytesRead, done) == XML_STATUS_ERROR) { throw "Invalid XML in LCP file"; } } while (!done); - fclose(pFile); + fclose (pFile); - XML_ParserFree(parser); + XML_ParserFree (parser); //printf("Parsing %s\n", fname.c_str()); // Two phase filter: first filter out the very rough ones, that distord the average a lot // force it, even if there are few frames (community profiles) - filterBadFrames(2.0, 0); + filterBadFrames (2.0, 0); // from the non-distorded, filter again on new average basis, but only if there are enough frames left - filterBadFrames(1.5, 100); + filterBadFrames (1.5, 100); } // from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values -int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) +int LCPProfile::filterBadFrames (double maxAvgDevFac, int minFramesLeft) { // take average error per type, then calculated the maximum deviation allowed double errBase = 0, errChrom = 0, errVignette = 0; int baseCount = 0, chromCount = 0, vignetteCount = 0; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(0)) { + if (aPersModel[pm]->hasModeData (0)) { errVignette += aPersModel[pm]->vignette.mean_error; vignetteCount++; } - if (aPersModel[pm]->hasModeData(1)) { + if (aPersModel[pm]->hasModeData (1)) { errBase += aPersModel[pm]->base.mean_error; baseCount++; } - if (aPersModel[pm]->hasModeData(2)) { - errChrom += std::max(std::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error), aPersModel[pm]->chromBG.mean_error); + if (aPersModel[pm]->hasModeData (2)) { + errChrom += std::max (std::max (aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error), aPersModel[pm]->chromBG.mean_error); chromCount++; } } @@ -426,17 +420,17 @@ int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) // Now mark all the bad ones as bad, and hasModeData will return false; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { + if (aPersModel[pm]->hasModeData (0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { aPersModel[pm]->vignette.bad_error = true; filtered++; } - if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { + if (aPersModel[pm]->hasModeData (1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { aPersModel[pm]->base.bad_error = true; filtered++; } - if (aPersModel[pm]->hasModeData(2) && + if (aPersModel[pm]->hasModeData (2) && (aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * errChrom)) { aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; @@ -450,23 +444,22 @@ int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) return filtered; } - // mode: 0=vignette, 1=distortion, 2=CA -void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const +void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const { - float euler = exp(1.0); + float euler = exp (1.0); // find the frames with the least distance, focal length wise LCPPersModel *pLow = nullptr, *pHigh = nullptr; - float focalLengthLog = log(focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; - float focusDistLog = focusDist > 0 ? log(focusDist) + euler : 0; + float focalLengthLog = log (focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; + float focusDistLog = focusDist > 0 ? log (focusDist) + euler : 0; // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case) for (int pm = 0; pm < persModelCount; pm++) { float f = aPersModel[pm]->focLen; - if (aPersModel[pm]->hasModeData(mode)) { + if (aPersModel[pm]->hasModeData (mode)) { if (f <= focalLength && (pLow == nullptr || f > pLow->focLen || (focusDist == 0 && f == pLow->focLen && pLow->focDist > aPersModel[pm]->focDist))) { pLow = aPersModel[pm]; } @@ -490,10 +483,10 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float for (int pm = 0; pm < persModelCount; pm++) { float aper = aPersModel[pm]->aperture; // float aperLog=log(aper); float focDist = aPersModel[pm]->focDist; - float focDistLog = log(focDist) + euler; + float focDistLog = log (focDist) + euler; double meanErr; - if (aPersModel[pm]->hasModeData(mode)) { + if (aPersModel[pm]->hasModeData (mode)) { if (mode == 0) { meanErr = aPersModel[pm]->vignette.mean_error; @@ -502,14 +495,14 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float if (aPersModel[pm]->focLen == bestFocLenLow && ( (aper == aperture && pLow->vignette.mean_error > meanErr) || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture) - || (aper <= aperture && (pLow->aperture > aperture || fabs(aperture - aper) < fabs(aperture - pLow->aperture))))) { + || (aper <= aperture && (pLow->aperture > aperture || fabs (aperture - aper) < fabs (aperture - pLow->aperture))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( (aper == aperture && pHigh->vignette.mean_error > meanErr) || (aper <= aperture && aper > pHigh->aperture && pHigh->aperture < aperture) - || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) { + || (aper >= aperture && (pHigh->aperture < aperture || fabs (aperture - aper) < fabs (aperture - pHigh->aperture))))) { pHigh = aPersModel[pm]; } } else { @@ -520,14 +513,14 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float if (aPersModel[pm]->focLen == bestFocLenLow && ( (focDist == focusDist && (mode == 1 ? pLow->base.mean_error : pLow->chromG.mean_error) > meanErr) || (focDist >= focusDist && focDist < pLow->focDist && pLow->focDist > focusDist) - || (focDist <= focusDist && (pLow->focDist > focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pLow->focDist) + euler)))))) { + || (focDist <= focusDist && (pLow->focDist > focusDist || fabs (focusDistLog - focDistLog) < fabs (focusDistLog - (log (pLow->focDist) + euler)))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( (focDist == focusDist && (mode == 1 ? pHigh->base.mean_error : pHigh->chromG.mean_error) > meanErr) || (focDist <= focusDist && focDist > pHigh->focDist && pHigh->focDist < focusDist) - || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pHigh->focDist) + euler)))))) { + || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs (focusDistLog - focDistLog) < fabs (focusDistLog - (log (pHigh->focDist) + euler)))))) { pHigh = aPersModel[pm]; } } else { @@ -552,7 +545,7 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float // There is as foclen range, take that as basis if (pLow->focLen < pHigh->focLen) { - facLow = (log(pHigh->focLen) - focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen)); + facLow = (log (pHigh->focLen) - focalLengthLog) / (log (pHigh->focLen) - log (pLow->focLen)); } else { focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength; } @@ -564,45 +557,45 @@ void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float facLow = focLenOnSpot ? facAperLow : (0.5 * facLow + 0.5 * facAperLow); } else if (mode != 0 && focusDist > 0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) { // focus distance for all else (if focus distance is given) - float facDistLow = (log(pHigh->focDist) + euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist)); + float facDistLow = (log (pHigh->focDist) + euler - focusDistLog) / (log (pHigh->focDist) - log (pLow->focDist)); facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow); } switch (mode) { - case 0: // vignette - pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); - break; + case 0: // vignette + pCorr1->merge (pLow->vignette, pHigh->vignette, facLow); + break; - case 1: // distortion - pCorr1->merge(pLow->base, pHigh->base, facLow); - break; + case 1: // distortion + pCorr1->merge (pLow->base, pHigh->base, facLow); + break; - case 2: // CA - pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); - pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); - pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); - break; + case 2: // CA + pCorr1->merge (pLow->chromRG, pHigh->chromRG, facLow); + pCorr2->merge (pLow->chromG, pHigh->chromG, facLow); + pCorr3->merge (pLow->chromBG, pHigh->chromBG, facLow); + break; } //printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow); } else { - printf("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); + printf ("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); } } void LCPProfile::print() const { - printf("=== Profile %s\n", profileName.c_str()); - printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); + printf ("=== Profile %s\n", profileName.c_str()); + printf ("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); for (int pm = 0; pm < persModelCount; pm++) { aPersModel[pm]->print(); } } -void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) +void XMLCALL LCPProfile::XmlStartHandler (void *pLCPProfile, const char *el, const char **attr) { - LCPProfile *pProf = static_cast(pLCPProfile); + LCPProfile *pProf = static_cast (pLCPProfile); bool parseAttr = false; if (*pProf->inInvalidTag) { @@ -610,29 +603,29 @@ void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, cons } // clean up tagname - const char* src = strrchr(el, ':'); + const char* src = strrchr (el, ':'); if (src == nullptr) { - src = const_cast(el); + src = const_cast (el); } else { src++; } - strcpy(pProf->lastTag, src); + strcpy (pProf->lastTag, src); - if (!strcmp("VignetteModelPiecewiseParam", src)) { - strcpy(pProf->inInvalidTag, src); + if (!strcmp ("VignetteModelPiecewiseParam", src)) { + strcpy (pProf->inInvalidTag, src); } - if (!strcmp("CameraProfiles", src)) { + if (!strcmp ("CameraProfiles", src)) { pProf->inCamProfiles = true; } - if (!strcmp("AlternateLensIDs", src)) { + if (!strcmp ("AlternateLensIDs", src)) { pProf->inAlternateLensID = true; } - if (!strcmp("AlternateLensNames", src)) { + if (!strcmp ("AlternateLensNames", src)) { pProf->inAlternateLensNames = true; } @@ -640,37 +633,37 @@ void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, cons return; } - if (!strcmp("li", src)) { + if (!strcmp ("li", src)) { pProf->pCurPersModel = new LCPPersModel(); pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel return; } - if (!strcmp("PerspectiveModel", src)) { + if (!strcmp ("PerspectiveModel", src)) { pProf->firstLIDone = true; pProf->inPerspect = true; return; - } else if (!strcmp("FisheyeModel", src)) { + } else if (!strcmp ("FisheyeModel", src)) { pProf->firstLIDone = true; pProf->inPerspect = true; pProf->isFisheye = true; // just misses third param, and different path, rest is the same return; - } else if (!strcmp("Description", src)) { + } else if (!strcmp ("Description", src)) { parseAttr = true; } // Move pointer to general section if (pProf->inPerspect) { - if (!strcmp("ChromaticRedGreenModel", src)) { + if (!strcmp ("ChromaticRedGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromRG; parseAttr = true; - } else if (!strcmp("ChromaticGreenModel", src)) { + } else if (!strcmp ("ChromaticGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromG; parseAttr = true; - } else if (!strcmp("ChromaticBlueGreenModel", src)) { + } else if (!strcmp ("ChromaticBlueGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromBG; parseAttr = true; - } else if (!strcmp("VignetteModel", src)) { + } else if (!strcmp ("VignetteModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->vignette; parseAttr = true; } @@ -680,23 +673,23 @@ void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, cons // simulate tags by feeding them in if (parseAttr && attr != nullptr) { for (int i = 0; attr[i]; i += 2) { - const char* nameStart = strrchr(attr[i], ':'); + const char* nameStart = strrchr (attr[i], ':'); if (nameStart == nullptr) { - nameStart = const_cast(attr[i]); + nameStart = const_cast (attr[i]); } else { nameStart++; } - strcpy(pProf->lastTag, nameStart); - XmlTextHandler(pLCPProfile, attr[i + 1], strlen(attr[i + 1])); + strcpy (pProf->lastTag, nameStart); + XmlTextHandler (pLCPProfile, attr[i + 1], strlen (attr[i + 1])); } } } -void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len) +void XMLCALL LCPProfile::XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len) { - LCPProfile *pProf = static_cast(pLCPProfile); + LCPProfile *pProf = static_cast (pLCPProfile); if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) { return; @@ -707,7 +700,7 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in int i = 0; while (i < len && onlyWhiteSpace) { - onlyWhiteSpace = isspace(s[i]); + onlyWhiteSpace = isspace (s[i]); i++; } @@ -717,32 +710,32 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in // convert to null terminated char raw[len + 1]; - memcpy(raw, s, len); + memcpy (raw, s, len); raw[len] = 0; char* tag = pProf->lastTag; // Common data section if (!pProf->firstLIDone) { // Generic tags are the same for all - if (!strcmp("ProfileName", tag)) { - pProf->profileName = Glib::ustring(raw); - } else if (!strcmp("Model", tag)) { - pProf->camera = Glib::ustring(raw); - } else if (!strcmp("Lens", tag)) { - pProf->lens = Glib::ustring(raw); - } else if (!strcmp("CameraPrettyName", tag)) { - pProf->cameraPrettyName = Glib::ustring(raw); - } else if (!strcmp("LensPrettyName", tag)) { - pProf->lensPrettyName = Glib::ustring(raw); - } else if (!strcmp("CameraRawProfile", tag)) { - pProf->isRaw = !strcmp("True", raw); + if (!strcmp ("ProfileName", tag)) { + pProf->profileName = Glib::ustring (raw); + } else if (!strcmp ("Model", tag)) { + pProf->camera = Glib::ustring (raw); + } else if (!strcmp ("Lens", tag)) { + pProf->lens = Glib::ustring (raw); + } else if (!strcmp ("CameraPrettyName", tag)) { + pProf->cameraPrettyName = Glib::ustring (raw); + } else if (!strcmp ("LensPrettyName", tag)) { + pProf->lensPrettyName = Glib::ustring (raw); + } else if (!strcmp ("CameraRawProfile", tag)) { + pProf->isRaw = !strcmp ("True", raw); } } // --- Now all floating points. Must replace local dot characters // WARNING: called by different threads, that may run on different local settings, // so don't use system params - if (atof("1,2345") == 1.2345) { + if (atof ("1,2345") == 1.2345) { char* p = raw; while (*p) { @@ -755,69 +748,69 @@ void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, in } if (!pProf->firstLIDone) { - if (!strcmp("SensorFormatFactor", tag)) { - pProf->sensorFormatFactor = atof(raw); + if (!strcmp ("SensorFormatFactor", tag)) { + pProf->sensorFormatFactor = atof (raw); } } // Perspective model base data - if (!strcmp("FocalLength", tag)) { - pProf->pCurPersModel->focLen = atof(raw); - } else if (!strcmp("FocusDistance", tag)) { - double focDist = atof(raw); + if (!strcmp ("FocalLength", tag)) { + pProf->pCurPersModel->focLen = atof (raw); + } else if (!strcmp ("FocusDistance", tag)) { + double focDist = atof (raw); pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000; - } else if (!strcmp("ApertureValue", tag)) { - pProf->pCurPersModel->aperture = atof(raw); + } else if (!strcmp ("ApertureValue", tag)) { + pProf->pCurPersModel->aperture = atof (raw); } // Section depended - if (!strcmp("FocalLengthX", tag)) { - pProf->pCurCommon->foc_len_x = atof(raw); - } else if (!strcmp("FocalLengthY", tag)) { - pProf->pCurCommon->foc_len_y = atof(raw); - } else if (!strcmp("ImageXCenter", tag)) { - pProf->pCurCommon->img_center_x = atof(raw); - } else if (!strcmp("ImageYCenter", tag)) { - pProf->pCurCommon->img_center_y = atof(raw); - } else if (!strcmp("ScaleFactor", tag)) { - pProf->pCurCommon->scale_factor = atof(raw); - } else if (!strcmp("ResidualMeanError", tag)) { - pProf->pCurCommon->mean_error = atof(raw); - } else if (!strcmp("RadialDistortParam1", tag) || !strcmp("VignetteModelParam1", tag)) { - pProf->pCurCommon->param[0] = atof(raw); - } else if (!strcmp("RadialDistortParam2", tag) || !strcmp("VignetteModelParam2", tag)) { - pProf->pCurCommon->param[1] = atof(raw); - } else if (!strcmp("RadialDistortParam3", tag) || !strcmp("VignetteModelParam3", tag)) { - pProf->pCurCommon->param[2] = atof(raw); - } else if (!strcmp("RadialDistortParam4", tag) || !strcmp("TangentialDistortParam1", tag)) { - pProf->pCurCommon->param[3] = atof(raw); - } else if (!strcmp("RadialDistortParam5", tag) || !strcmp("TangentialDistortParam2", tag)) { - pProf->pCurCommon->param[4] = atof(raw); + if (!strcmp ("FocalLengthX", tag)) { + pProf->pCurCommon->foc_len_x = atof (raw); + } else if (!strcmp ("FocalLengthY", tag)) { + pProf->pCurCommon->foc_len_y = atof (raw); + } else if (!strcmp ("ImageXCenter", tag)) { + pProf->pCurCommon->img_center_x = atof (raw); + } else if (!strcmp ("ImageYCenter", tag)) { + pProf->pCurCommon->img_center_y = atof (raw); + } else if (!strcmp ("ScaleFactor", tag)) { + pProf->pCurCommon->scale_factor = atof (raw); + } else if (!strcmp ("ResidualMeanError", tag)) { + pProf->pCurCommon->mean_error = atof (raw); + } else if (!strcmp ("RadialDistortParam1", tag) || !strcmp ("VignetteModelParam1", tag)) { + pProf->pCurCommon->param[0] = atof (raw); + } else if (!strcmp ("RadialDistortParam2", tag) || !strcmp ("VignetteModelParam2", tag)) { + pProf->pCurCommon->param[1] = atof (raw); + } else if (!strcmp ("RadialDistortParam3", tag) || !strcmp ("VignetteModelParam3", tag)) { + pProf->pCurCommon->param[2] = atof (raw); + } else if (!strcmp ("RadialDistortParam4", tag) || !strcmp ("TangentialDistortParam1", tag)) { + pProf->pCurCommon->param[3] = atof (raw); + } else if (!strcmp ("RadialDistortParam5", tag) || !strcmp ("TangentialDistortParam2", tag)) { + pProf->pCurCommon->param[4] = atof (raw); } } -void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) +void XMLCALL LCPProfile::XmlEndHandler (void *pLCPProfile, const char *el) { - LCPProfile *pProf = static_cast(pLCPProfile); + LCPProfile *pProf = static_cast (pLCPProfile); // We ignore everything in dirty tag till it's gone if (*pProf->inInvalidTag) { - if (strstr(el, pProf->inInvalidTag)) { + if (strstr (el, pProf->inInvalidTag)) { *pProf->inInvalidTag = 0; } return; } - if (strstr(el, ":CameraProfiles")) { + if (strstr (el, ":CameraProfiles")) { pProf->inCamProfiles = false; } - if (strstr(el, ":AlternateLensIDs")) { + if (strstr (el, ":AlternateLensIDs")) { pProf->inAlternateLensID = false; } - if (strstr(el, ":AlternateLensNames")) { + if (strstr (el, ":AlternateLensNames")) { pProf->inAlternateLensNames = false; } @@ -825,9 +818,9 @@ void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) return; } - if (strstr(el, ":PerspectiveModel") || strstr(el, ":FisheyeModel")) { + if (strstr (el, ":PerspectiveModel") || strstr (el, ":FisheyeModel")) { pProf->inPerspect = false; - } else if (strstr(el, ":li")) { + } else if (strstr (el, ":li")) { pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel; pProf->pCurPersModel = nullptr; pProf->persModelCount++; @@ -843,11 +836,11 @@ LCPStore* LCPStore::getInstance() LCPProfile* LCPStore::getProfile (Glib::ustring filename) { - if (filename.length() == 0 || !isValidLCPFileName(filename)) { + if (filename.length() == 0 || !isValidLCPFileName (filename)) { return nullptr; } - MyMutex::MyLock lock(mtx); + MyMutex::MyLock lock (mtx); std::map::iterator r = profileCache.find (filename); @@ -856,12 +849,12 @@ LCPProfile* LCPStore::getProfile (Glib::ustring filename) } // Add profile (if exists) - profileCache[filename] = new LCPProfile(filename); + profileCache[filename] = new LCPProfile (filename); //profileCache[filename]->print(); return profileCache[filename]; } -bool LCPStore::isValidLCPFileName(Glib::ustring filename) const +bool LCPStore::isValidLCPFileName (Glib::ustring filename) const { if (!Glib::file_test (filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) { return false; @@ -879,10 +872,10 @@ Glib::ustring LCPStore::getDefaultCommonDirectory() const WCHAR pathW[MAX_PATH] = {0}; char pathA[MAX_PATH]; - if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) { + if (SHGetSpecialFolderPathW (NULL, pathW, CSIDL_COMMON_APPDATA, false)) { char pathA[MAX_PATH]; - WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); - Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); + WideCharToMultiByte (CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); + Glib::ustring fullDir = Glib::ustring (pathA) + Glib::ustring ("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) { dir = fullDir; diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 92c984921..9488f74d7 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -39,8 +39,8 @@ public: LCPModelCommon(); bool empty() const; // is it empty void print() const; // printf all values - void merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA); - void prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY); + void merge (const LCPModelCommon& a, const LCPModelCommon& b, float facA); + void prepareParams (int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY); //private: using Param = std::array; @@ -75,7 +75,7 @@ public: LCPModelCommon vignette; // vignette (may be empty) LCPPersModel(); - bool hasModeData(int mode) const; + bool hasModeData (int mode) const; void print() const; }; @@ -88,11 +88,11 @@ class LCPProfile LCPPersModel* pCurPersModel; LCPModelCommon* pCurCommon; - static void XMLCALL XmlStartHandler(void *pLCPProfile, const char *el, const char **attr); + static void XMLCALL XmlStartHandler (void *pLCPProfile, const char *el, const char **attr); static void XMLCALL XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len); static void XMLCALL XmlEndHandler (void *pLCPProfile, const char *el); - int filterBadFrames(double maxAvgDevFac, int minFramesLeft); + int filterBadFrames (double maxAvgDevFac, int minFramesLeft); public: // Common data @@ -105,9 +105,9 @@ public: static const int MaxPersModelCount = 3000; LCPPersModel* aPersModel[MaxPersModelCount]; // Do NOT use std::list or something, it's buggy in GCC! - explicit LCPProfile(const Glib::ustring &fname); + explicit LCPProfile (const Glib::ustring &fname); - void calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames + void calcParams (int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames void print() const; }; @@ -121,8 +121,8 @@ class LCPStore public: Glib::ustring getDefaultCommonDirectory() const; - bool isValidLCPFileName(Glib::ustring filename) const; - LCPProfile* getProfile(Glib::ustring filename); + bool isValidLCPFileName (Glib::ustring filename) const; + LCPProfile* getProfile (Glib::ustring filename); static LCPStore* getInstance(); }; @@ -143,14 +143,13 @@ public: bool enableCA; // is the mapper capable if CA correction? // precalculates the mapper. - LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, - const CoarseTransformParams& coarse, int rawRotationDeg); + LCPMapper (LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, + const CoarseTransformParams& coarse, int rawRotationDeg); - void correctDistortion(double& x, double& y) const; // MUST be the first stage - void correctCA(double& x, double& y, int channel) const; - float calcVignetteFac (int x, int y) const; // MUST be in RAW - void processVignetteLine(int width, int y, float *line) const; - void processVignetteLine3Channels(int width, int y, float *line) const; + void correctDistortion (double& x, double& y) const; // MUST be the first stage + void correctCA (double& x, double& y, int channel) const; + void processVignetteLine (int width, int y, float *line) const; + void processVignetteLine3Channels (int width, int y, float *line) const; }; } From 3b9ca0bc87d41753dab765d5d592ad4386940cc2 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Wed, 22 Feb 2017 14:24:42 +0100 Subject: [PATCH 109/181] Update TooWaBlue to version 2.45 (#3711) Squashed and merged. --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 11 ++++++----- rtdata/themes/TooWaBlue-GTK3-20_.css | 13 +++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index 9472f4b3d..4c6bbc3ef 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.44 - requires RT 5.0 + Version 2.45 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -488,7 +488,8 @@ menu separator { } #PartialPasteHeader label { - color: @headline-frame; + color: @headline-big; + font-weight: bold; } #PartialPasteHeader { margin: 0.5em 0 0 0; @@ -1016,12 +1017,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-right: 0; } -#EditorTopPanel > box > button.image-button:not(:nth-child(6)) { +#EditorTopPanel > box:nth-child(9) > button.image-button:not(:nth-child(6)) { min-width: 0; padding-left: 0.33334em; padding-right: 0.33334em; } -#EditorTopPanel > box > button.image-button:nth-child(6) { + +#EditorTopPanel > box:nth-child(9) > button.image-button:nth-child(6) { -gtk-icon-shadow: none; } #EditorTopPanel > box > box > button { @@ -1850,5 +1852,4 @@ headerbar .title:backdrop { color: alpha(@winTitle,.60); } /**/ - /*** end ***************************************************************************************/ diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 679a8ed13..795dc0441 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.44 - requires RT 5.0 + Version 2.45 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ @define-color bg-button-border rgba(0,0,0,.60); @define-color bg-entry-border rgba(0,0,0,.40); @define-color view-grid-border rgba(255,255,255,0.15); -@define-color headline-big rgb(190,190,190); +@define-color headline-big rgb(195,195,195); @define-color headline-hl rgb(230,230,230); @define-color headline-frame rgb(215,215,215); /***********************************************************************************************/ @@ -488,7 +488,8 @@ menu separator { } #PartialPasteHeader label { - color: @headline-frame; + color: @headline-big; + font-weight: bold; } #PartialPasteHeader { margin: 0.5em 0 0 0; @@ -1016,12 +1017,13 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-right: 0; } -#EditorTopPanel > box > button.image-button:not(:nth-child(6)) { +#EditorTopPanel > box:nth-child(9) > button.image-button:not(:nth-child(6)) { min-width: 0; padding-left: 0.33334em; padding-right: 0.33334em; } -#EditorTopPanel > box > button.image-button:nth-child(6) { + +#EditorTopPanel > box:nth-child(9) > button.image-button:nth-child(6) { -gtk-icon-shadow: none; } #EditorTopPanel > box > box > button { @@ -1850,5 +1852,4 @@ headerbar .title:backdrop { color: alpha(@winTitle,.60); } /**/ - /*** end ***************************************************************************************/ From 8e205afeed8c1936bb88cac9339be21c1448ff39 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 22 Feb 2017 14:43:41 +0100 Subject: [PATCH 110/181] Revert "Further cleanup and astyled lcp.*" This reverts commit 1348ea06e4975d26b50f9e723ae61f87916dcf17. --- rtengine/lcp.cc | 401 ++++++++++++++++++++++++------------------------ rtengine/lcp.h | 31 ++-- 2 files changed, 220 insertions(+), 212 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index a870b6212..ff53ad30e 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -34,21 +34,21 @@ using namespace rtengine; LCPModelCommon::LCPModelCommon() : - foc_len_x (-1.0f), - foc_len_y (-1.0f), - img_center_x (0.5f), - img_center_y (0.5f), + foc_len_x(-1.0f), + foc_len_y(-1.0f), + img_center_x(0.5f), + img_center_y(0.5f), param{{}}, -scale_factor (1.0f), - mean_error (0.0), - bad_error (false), - x0 (0.0f), - y0 (0.0f), - fx (0.0f), - fy (0.0f), - rfx (0.0f), - rfy (0.0f), -vign_param{{}} + scale_factor(1.0f), + mean_error(0.0), + bad_error(false), + x0(0.0f), + y0(0.0f), + fx(0.0f), + fy(0.0f), + rfx(0.0f), + rfy(0.0f), + vign_param{{}} { } @@ -59,13 +59,13 @@ bool LCPModelCommon::empty() const void LCPModelCommon::print() const { - printf ("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); - printf ("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); - printf ("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); + printf("focLen %g/%g; imgCenter %g/%g; scale %g; err %g\n", foc_len_x, foc_len_y, img_center_x, img_center_y, scale_factor, mean_error); + printf("xy0 %g/%g fxy %g/%g\n", x0, y0, fx, fy); + printf("param: %g/%g/%g/%g/%g\n", param[0], param[1], param[2], param[3], param[4]); } // weighted merge two parameters -void LCPModelCommon::merge (const LCPModelCommon& a, const LCPModelCommon& b, float facA) +void LCPModelCommon::merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA) { const float facB = 1.0f - facA; @@ -80,7 +80,7 @@ void LCPModelCommon::merge (const LCPModelCommon& a, const LCPModelCommon& b, fl param[i] = facA * a.param[i] + facB * b.param[i]; } - const double param0Sqr = param[0] * param[0]; + const float param0Sqr = param[0] * param[0]; vign_param[0] = -param[0]; vign_param[1] = param0Sqr - param[1]; @@ -89,10 +89,10 @@ void LCPModelCommon::merge (const LCPModelCommon& a, const LCPModelCommon& b, fl } -void LCPModelCommon::prepareParams (int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) +void LCPModelCommon::prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY) { // Mention that the Adobe technical paper has a bug here, the DMAX is handled differently for focLen and imgCenter - const int Dmax = std::max (fullWidth, fullHeight); + const int Dmax = std::max(fullWidth, fullHeight); // correct focLens if (foc_len_x < 0.0f) { // they may not be given @@ -115,9 +115,8 @@ void LCPModelCommon::prepareParams (int fullWidth, int fullHeight, float focalLe fx = foc_len_x * Dmax; fy = foc_len_y * Dmax; } - - rfx = 1.0 / fx; // calculatiom with double precision doesn't cost anything at this step - rfy = 1.0 / fy; // " + rfx = 1.0f / fx; + rfy = 1.0f / fy; //printf("FW %i /X0 %g FH %i /Y0 %g %g\n",fullWidth,x0,fullHeight,y0, imgYCenter); } @@ -128,7 +127,7 @@ LCPPersModel::LCPPersModel() } // mode: 0=distortion, 1=vignette, 2=CA -bool LCPPersModel::hasModeData (int mode) const +bool LCPPersModel::hasModeData(int mode) const { return (mode == 0 && !vignette.empty() && !vignette.bad_error) || (mode == 1 && !base.empty() && !base.bad_error) || (mode == 2 && !chromRG.empty() && !chromG.empty() && !chromBG.empty() && @@ -137,36 +136,36 @@ bool LCPPersModel::hasModeData (int mode) const void LCPPersModel::print() const { - printf ("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); - printf ("Base:\n"); + printf("--- PersModel focLen %g; focDist %g; aperture %g\n", focLen, focDist, aperture); + printf("Base:\n"); base.print(); if (!chromRG.empty()) { - printf ("ChromRG:\n"); + printf("ChromRG:\n"); chromRG.print(); } if (!chromG.empty()) { - printf ("ChromG:\n"); + printf("ChromG:\n"); chromG.print(); } if (!chromBG.empty()) { - printf ("ChromBG:\n"); + printf("ChromBG:\n"); chromBG.print(); } if (!vignette.empty()) { - printf ("Vignette:\n"); + printf("Vignette:\n"); vignette.print(); } - printf ("\n"); + printf("\n"); } // if !vignette then geometric and CA -LCPMapper::LCPMapper (LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, - int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) +LCPMapper::LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, + int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg) { if (pProf == nullptr) { return; @@ -187,21 +186,21 @@ LCPMapper::LCPMapper (LCPProfile* pProf, float focalLength, float focalLength35m bool mirrorY = (rot == 180 || rot == 270); //printf("Vign: %i, fullWidth: %i/%i, focLen %g SwapXY: %i / MirX/Y %i / %i on rot:%i from %i\n",vignette, fullWidth, fullHeight, focalLength, swapXY, mirrorX, mirrorY, rot, rawRotationDeg); - pProf->calcParams (vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr); - mc.prepareParams (fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); + pProf->calcParams(vignette ? 0 : 1, focalLength, focusDist, aperture, &mc, nullptr, nullptr); + mc.prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); if (!vignette) { - pProf->calcParams (2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); + pProf->calcParams(2, focalLength, focusDist, aperture, &chrom[0], &chrom[1], &chrom[2]); for (int i = 0; i < 3; i++) { - chrom[i].prepareParams (fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); + chrom[i].prepareParams(fullWidth, fullHeight, focalLength, focalLength35mm, pProf->sensorFormatFactor, swapXY, mirrorX, mirrorY); } } enableCA = !vignette && focusDist > 0; } -void LCPMapper::correctDistortion (double& x, double& y) const +void LCPMapper::correctDistortion(double& x, double& y) const { double xd = (x - mc.x0) / mc.fx, yd = (y - mc.y0) / mc.fy; @@ -219,7 +218,7 @@ void LCPMapper::correctDistortion (double& x, double& y) const y = ynew * mc.fy + mc.y0; } -void LCPMapper::correctCA (double& x, double& y, int channel) const +void LCPMapper::correctCA(double& x, double& y, int channel) const { if (!enableCA) { return; @@ -266,39 +265,47 @@ void LCPMapper::correctCA (double& x, double& y, int channel) const } } -SSEFUNCTION void LCPMapper::processVignetteLine (int width, int y, float *line) const +float LCPMapper::calcVignetteFac(int x, int y) const +{ + // No need for swapXY, since vignette is in RAW and always before rotation + float xd = ((float)x - mc.x0) * mc.rfx, yd = ((float)y - mc.y0) * mc.rfy; + + const LCPModelCommon::VignParam vignParam = mc.vign_param; + float rsqr = xd * xd + yd * yd; + + return rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); +} + +SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; int x = 0; #ifdef __SSE2__ - const vfloat fourv = F2V (4.f); - const vfloat zerov = F2V (0.f); - const vfloat ydv = F2V (yd); - const vfloat p0 = F2V (mc.vign_param[0]); - const vfloat p1 = F2V (mc.vign_param[1]); - const vfloat p2 = F2V (mc.vign_param[2]); - const vfloat p3 = F2V (mc.vign_param[3]); - const vfloat x0v = F2V (mc.x0); - const vfloat rfxv = F2V (mc.rfx); + const vfloat fourv = F2V(4.f); + const vfloat zerov = F2V(0.f); + const vfloat ydv = F2V(yd); + const vfloat p0 = F2V(mc.vign_param[0]); + const vfloat p1 = F2V(mc.vign_param[1]); + const vfloat p2 = F2V(mc.vign_param[2]); + const vfloat p3 = F2V(mc.vign_param[3]); + const vfloat x0v = F2V(mc.x0); + const vfloat rfxv = F2V(mc.rfx); - vfloat xv = _mm_setr_ps (0.f, 1.f, 2.f, 3.f); - - for (; x < width - 3; x += 4) { + vfloat xv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f); + for (; x < width-3; x+=4) { vfloat xdv = (xv - x0v) * rfxv; vfloat rsqr = xdv * xdv + ydv; vfloat vignFactorv = rsqr * (p0 + rsqr * (p1 - p2 * rsqr + p3 * rsqr * rsqr)); - vfloat valv = LVFU (line[x]); - valv += valv * vselfzero (vmaskf_gt (valv, zerov), vignFactorv); - STVFU (line[x], valv); + vfloat valv = LVFU(line[x]); + valv += valv * vselfzero(vmaskf_gt(valv, zerov), vignFactorv); + STVFU(line[x], valv); xv += fourv; } - #endif // __SSE2__ - for (; x < width; x++) { - if (line[x] > 0.f) { + if (line[x] > 0) { float xd = ((float)x - mc.x0) * mc.rfx; const LCPModelCommon::VignParam vignParam = mc.vign_param; float rsqr = xd * xd + yd; @@ -307,40 +314,39 @@ SSEFUNCTION void LCPMapper::processVignetteLine (int width, int y, float *line) } } -void LCPMapper::processVignetteLine3Channels (int width, int y, float *line) const +SSEFUNCTION void LCPMapper::processVignetteLine3Channels(int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation float yd = ((float)y - mc.y0) * mc.rfy; yd *= yd; const LCPModelCommon::VignParam vignParam = mc.vign_param; - for (int x = 0; x < width; x++) { float xd = ((float)x - mc.x0) * mc.rfx; float rsqr = xd * xd + yd; float vignetteFactor = rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); - - for (int c = 0; c < 3; ++c) { - if (line[3 * x + c] > 0) { - line[3 * x + c] += line[3 * x + c] * vignetteFactor; + for(int c = 0;c < 3; ++c) { + if (line[3*x+c] > 0) { + line[3*x+c] += line[3*x+c] * vignetteFactor; } } } } -LCPProfile::LCPProfile (const Glib::ustring &fname) + +LCPProfile::LCPProfile(const Glib::ustring &fname) { const int BufferSize = 8192; char buf[BufferSize]; - XML_Parser parser = XML_ParserCreate (nullptr); + XML_Parser parser = XML_ParserCreate(nullptr); if (!parser) { throw "Couldn't allocate memory for XML parser"; } - XML_SetElementHandler (parser, XmlStartHandler, XmlEndHandler); - XML_SetCharacterDataHandler (parser, XmlTextHandler); - XML_SetUserData (parser, (void *)this); + XML_SetElementHandler(parser, XmlStartHandler, XmlEndHandler); + XML_SetCharacterDataHandler(parser, XmlTextHandler); + XML_SetUserData(parser, (void *)this); isFisheye = inCamProfiles = firstLIDone = inPerspect = inAlternateLensID = inAlternateLensNames = false; @@ -353,51 +359,51 @@ LCPProfile::LCPProfile (const Glib::ustring &fname) persModelCount = 0; *inInvalidTag = 0; - FILE *pFile = g_fopen (fname.c_str (), "rb"); + FILE *pFile = g_fopen(fname.c_str (), "rb"); bool done; do { - int bytesRead = (int)fread (buf, 1, BufferSize, pFile); - done = feof (pFile); + int bytesRead = (int)fread(buf, 1, BufferSize, pFile); + done = feof(pFile); - if (XML_Parse (parser, buf, bytesRead, done) == XML_STATUS_ERROR) { + if (XML_Parse(parser, buf, bytesRead, done) == XML_STATUS_ERROR) { throw "Invalid XML in LCP file"; } } while (!done); - fclose (pFile); + fclose(pFile); - XML_ParserFree (parser); + XML_ParserFree(parser); //printf("Parsing %s\n", fname.c_str()); // Two phase filter: first filter out the very rough ones, that distord the average a lot // force it, even if there are few frames (community profiles) - filterBadFrames (2.0, 0); + filterBadFrames(2.0, 0); // from the non-distorded, filter again on new average basis, but only if there are enough frames left - filterBadFrames (1.5, 100); + filterBadFrames(1.5, 100); } // from all frames not marked as bad already, take average and filter out frames with higher deviation than this if there are enough values -int LCPProfile::filterBadFrames (double maxAvgDevFac, int minFramesLeft) +int LCPProfile::filterBadFrames(double maxAvgDevFac, int minFramesLeft) { // take average error per type, then calculated the maximum deviation allowed double errBase = 0, errChrom = 0, errVignette = 0; int baseCount = 0, chromCount = 0, vignetteCount = 0; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData (0)) { + if (aPersModel[pm]->hasModeData(0)) { errVignette += aPersModel[pm]->vignette.mean_error; vignetteCount++; } - if (aPersModel[pm]->hasModeData (1)) { + if (aPersModel[pm]->hasModeData(1)) { errBase += aPersModel[pm]->base.mean_error; baseCount++; } - if (aPersModel[pm]->hasModeData (2)) { - errChrom += std::max (std::max (aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error), aPersModel[pm]->chromBG.mean_error); + if (aPersModel[pm]->hasModeData(2)) { + errChrom += std::max(std::max(aPersModel[pm]->chromRG.mean_error, aPersModel[pm]->chromG.mean_error), aPersModel[pm]->chromBG.mean_error); chromCount++; } } @@ -420,17 +426,17 @@ int LCPProfile::filterBadFrames (double maxAvgDevFac, int minFramesLeft) // Now mark all the bad ones as bad, and hasModeData will return false; for (int pm = 0; pm < MaxPersModelCount && aPersModel[pm]; pm++) { - if (aPersModel[pm]->hasModeData (0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { + if (aPersModel[pm]->hasModeData(0) && aPersModel[pm]->vignette.mean_error > maxAvgDevFac * errVignette) { aPersModel[pm]->vignette.bad_error = true; filtered++; } - if (aPersModel[pm]->hasModeData (1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { + if (aPersModel[pm]->hasModeData(1) && aPersModel[pm]->base.mean_error > maxAvgDevFac * errBase) { aPersModel[pm]->base.bad_error = true; filtered++; } - if (aPersModel[pm]->hasModeData (2) && + if (aPersModel[pm]->hasModeData(2) && (aPersModel[pm]->chromRG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromG.mean_error > maxAvgDevFac * errChrom || aPersModel[pm]->chromBG.mean_error > maxAvgDevFac * errChrom)) { aPersModel[pm]->chromRG.bad_error = aPersModel[pm]->chromG.bad_error = aPersModel[pm]->chromBG.bad_error = true; @@ -444,22 +450,23 @@ int LCPProfile::filterBadFrames (double maxAvgDevFac, int minFramesLeft) return filtered; } + // mode: 0=vignette, 1=distortion, 2=CA -void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const +void LCPProfile::calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const { - float euler = exp (1.0); + float euler = exp(1.0); // find the frames with the least distance, focal length wise LCPPersModel *pLow = nullptr, *pHigh = nullptr; - float focalLengthLog = log (focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; - float focusDistLog = focusDist > 0 ? log (focusDist) + euler : 0; + float focalLengthLog = log(focalLength); //, apertureLog=aperture>0 ? log(aperture) : 0; + float focusDistLog = focusDist > 0 ? log(focusDist) + euler : 0; // Pass 1: determining best focal length, if possible different focusDistances (for the focDist is not given case) for (int pm = 0; pm < persModelCount; pm++) { float f = aPersModel[pm]->focLen; - if (aPersModel[pm]->hasModeData (mode)) { + if (aPersModel[pm]->hasModeData(mode)) { if (f <= focalLength && (pLow == nullptr || f > pLow->focLen || (focusDist == 0 && f == pLow->focLen && pLow->focDist > aPersModel[pm]->focDist))) { pLow = aPersModel[pm]; } @@ -483,10 +490,10 @@ void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float for (int pm = 0; pm < persModelCount; pm++) { float aper = aPersModel[pm]->aperture; // float aperLog=log(aper); float focDist = aPersModel[pm]->focDist; - float focDistLog = log (focDist) + euler; + float focDistLog = log(focDist) + euler; double meanErr; - if (aPersModel[pm]->hasModeData (mode)) { + if (aPersModel[pm]->hasModeData(mode)) { if (mode == 0) { meanErr = aPersModel[pm]->vignette.mean_error; @@ -495,14 +502,14 @@ void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float if (aPersModel[pm]->focLen == bestFocLenLow && ( (aper == aperture && pLow->vignette.mean_error > meanErr) || (aper >= aperture && aper < pLow->aperture && pLow->aperture > aperture) - || (aper <= aperture && (pLow->aperture > aperture || fabs (aperture - aper) < fabs (aperture - pLow->aperture))))) { + || (aper <= aperture && (pLow->aperture > aperture || fabs(aperture - aper) < fabs(aperture - pLow->aperture))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( (aper == aperture && pHigh->vignette.mean_error > meanErr) || (aper <= aperture && aper > pHigh->aperture && pHigh->aperture < aperture) - || (aper >= aperture && (pHigh->aperture < aperture || fabs (aperture - aper) < fabs (aperture - pHigh->aperture))))) { + || (aper >= aperture && (pHigh->aperture < aperture || fabs(aperture - aper) < fabs(aperture - pHigh->aperture))))) { pHigh = aPersModel[pm]; } } else { @@ -513,14 +520,14 @@ void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float if (aPersModel[pm]->focLen == bestFocLenLow && ( (focDist == focusDist && (mode == 1 ? pLow->base.mean_error : pLow->chromG.mean_error) > meanErr) || (focDist >= focusDist && focDist < pLow->focDist && pLow->focDist > focusDist) - || (focDist <= focusDist && (pLow->focDist > focusDist || fabs (focusDistLog - focDistLog) < fabs (focusDistLog - (log (pLow->focDist) + euler)))))) { + || (focDist <= focusDist && (pLow->focDist > focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pLow->focDist) + euler)))))) { pLow = aPersModel[pm]; } if (aPersModel[pm]->focLen == bestFocLenHigh && ( (focDist == focusDist && (mode == 1 ? pHigh->base.mean_error : pHigh->chromG.mean_error) > meanErr) || (focDist <= focusDist && focDist > pHigh->focDist && pHigh->focDist < focusDist) - || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs (focusDistLog - focDistLog) < fabs (focusDistLog - (log (pHigh->focDist) + euler)))))) { + || (focDist >= focusDist && (pHigh->focDist < focusDist || fabs(focusDistLog - focDistLog) < fabs(focusDistLog - (log(pHigh->focDist) + euler)))))) { pHigh = aPersModel[pm]; } } else { @@ -545,7 +552,7 @@ void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float // There is as foclen range, take that as basis if (pLow->focLen < pHigh->focLen) { - facLow = (log (pHigh->focLen) - focalLengthLog) / (log (pHigh->focLen) - log (pLow->focLen)); + facLow = (log(pHigh->focLen) - focalLengthLog) / (log(pHigh->focLen) - log(pLow->focLen)); } else { focLenOnSpot = pLow->focLen == pHigh->focLen && pLow->focLen == focalLength; } @@ -557,45 +564,45 @@ void LCPProfile::calcParams (int mode, float focalLength, float focusDist, float facLow = focLenOnSpot ? facAperLow : (0.5 * facLow + 0.5 * facAperLow); } else if (mode != 0 && focusDist > 0 && pLow->focDist < focusDist && pHigh->focDist > focusDist) { // focus distance for all else (if focus distance is given) - float facDistLow = (log (pHigh->focDist) + euler - focusDistLog) / (log (pHigh->focDist) - log (pLow->focDist)); + float facDistLow = (log(pHigh->focDist) + euler - focusDistLog) / (log(pHigh->focDist) - log(pLow->focDist)); facLow = focLenOnSpot ? facDistLow : (0.8 * facLow + 0.2 * facDistLow); } switch (mode) { - case 0: // vignette - pCorr1->merge (pLow->vignette, pHigh->vignette, facLow); - break; + case 0: // vignette + pCorr1->merge(pLow->vignette, pHigh->vignette, facLow); + break; - case 1: // distortion - pCorr1->merge (pLow->base, pHigh->base, facLow); - break; + case 1: // distortion + pCorr1->merge(pLow->base, pHigh->base, facLow); + break; - case 2: // CA - pCorr1->merge (pLow->chromRG, pHigh->chromRG, facLow); - pCorr2->merge (pLow->chromG, pHigh->chromG, facLow); - pCorr3->merge (pLow->chromBG, pHigh->chromBG, facLow); - break; + case 2: // CA + pCorr1->merge(pLow->chromRG, pHigh->chromRG, facLow); + pCorr2->merge(pLow->chromG, pHigh->chromG, facLow); + pCorr3->merge(pLow->chromBG, pHigh->chromBG, facLow); + break; } //printf("LCP mode=%i, dist: %g found frames: Fno %g-%g; FocLen %g-%g; Dist %g-%g with weight %g\n", mode, focusDist, pLow->aperture, pHigh->aperture, pLow->focLen, pHigh->focLen, pLow->focDist, pHigh->focDist, facLow); } else { - printf ("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); + printf("Error: LCP file contained no %s parameters\n", mode == 0 ? "vignette" : mode == 1 ? "distortion" : "CA" ); } } void LCPProfile::print() const { - printf ("=== Profile %s\n", profileName.c_str()); - printf ("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); + printf("=== Profile %s\n", profileName.c_str()); + printf("Frames: %i, RAW: %i; Fisheye: %i; Sensorformat: %f\n", persModelCount, isRaw, isFisheye, sensorFormatFactor); for (int pm = 0; pm < persModelCount; pm++) { aPersModel[pm]->print(); } } -void XMLCALL LCPProfile::XmlStartHandler (void *pLCPProfile, const char *el, const char **attr) +void XMLCALL LCPProfile::XmlStartHandler(void *pLCPProfile, const char *el, const char **attr) { - LCPProfile *pProf = static_cast (pLCPProfile); + LCPProfile *pProf = static_cast(pLCPProfile); bool parseAttr = false; if (*pProf->inInvalidTag) { @@ -603,29 +610,29 @@ void XMLCALL LCPProfile::XmlStartHandler (void *pLCPProfile, const char *el, con } // clean up tagname - const char* src = strrchr (el, ':'); + const char* src = strrchr(el, ':'); if (src == nullptr) { - src = const_cast (el); + src = const_cast(el); } else { src++; } - strcpy (pProf->lastTag, src); + strcpy(pProf->lastTag, src); - if (!strcmp ("VignetteModelPiecewiseParam", src)) { - strcpy (pProf->inInvalidTag, src); + if (!strcmp("VignetteModelPiecewiseParam", src)) { + strcpy(pProf->inInvalidTag, src); } - if (!strcmp ("CameraProfiles", src)) { + if (!strcmp("CameraProfiles", src)) { pProf->inCamProfiles = true; } - if (!strcmp ("AlternateLensIDs", src)) { + if (!strcmp("AlternateLensIDs", src)) { pProf->inAlternateLensID = true; } - if (!strcmp ("AlternateLensNames", src)) { + if (!strcmp("AlternateLensNames", src)) { pProf->inAlternateLensNames = true; } @@ -633,37 +640,37 @@ void XMLCALL LCPProfile::XmlStartHandler (void *pLCPProfile, const char *el, con return; } - if (!strcmp ("li", src)) { + if (!strcmp("li", src)) { pProf->pCurPersModel = new LCPPersModel(); pProf->pCurCommon = &pProf->pCurPersModel->base; // iterated to next tags within persModel return; } - if (!strcmp ("PerspectiveModel", src)) { + if (!strcmp("PerspectiveModel", src)) { pProf->firstLIDone = true; pProf->inPerspect = true; return; - } else if (!strcmp ("FisheyeModel", src)) { + } else if (!strcmp("FisheyeModel", src)) { pProf->firstLIDone = true; pProf->inPerspect = true; pProf->isFisheye = true; // just misses third param, and different path, rest is the same return; - } else if (!strcmp ("Description", src)) { + } else if (!strcmp("Description", src)) { parseAttr = true; } // Move pointer to general section if (pProf->inPerspect) { - if (!strcmp ("ChromaticRedGreenModel", src)) { + if (!strcmp("ChromaticRedGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromRG; parseAttr = true; - } else if (!strcmp ("ChromaticGreenModel", src)) { + } else if (!strcmp("ChromaticGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromG; parseAttr = true; - } else if (!strcmp ("ChromaticBlueGreenModel", src)) { + } else if (!strcmp("ChromaticBlueGreenModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->chromBG; parseAttr = true; - } else if (!strcmp ("VignetteModel", src)) { + } else if (!strcmp("VignetteModel", src)) { pProf->pCurCommon = &pProf->pCurPersModel->vignette; parseAttr = true; } @@ -673,23 +680,23 @@ void XMLCALL LCPProfile::XmlStartHandler (void *pLCPProfile, const char *el, con // simulate tags by feeding them in if (parseAttr && attr != nullptr) { for (int i = 0; attr[i]; i += 2) { - const char* nameStart = strrchr (attr[i], ':'); + const char* nameStart = strrchr(attr[i], ':'); if (nameStart == nullptr) { - nameStart = const_cast (attr[i]); + nameStart = const_cast(attr[i]); } else { nameStart++; } - strcpy (pProf->lastTag, nameStart); - XmlTextHandler (pLCPProfile, attr[i + 1], strlen (attr[i + 1])); + strcpy(pProf->lastTag, nameStart); + XmlTextHandler(pLCPProfile, attr[i + 1], strlen(attr[i + 1])); } } } -void XMLCALL LCPProfile::XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len) +void XMLCALL LCPProfile::XmlTextHandler(void *pLCPProfile, const XML_Char *s, int len) { - LCPProfile *pProf = static_cast (pLCPProfile); + LCPProfile *pProf = static_cast(pLCPProfile); if (!pProf->inCamProfiles || pProf->inAlternateLensID || pProf->inAlternateLensNames || *pProf->inInvalidTag) { return; @@ -700,7 +707,7 @@ void XMLCALL LCPProfile::XmlTextHandler (void *pLCPProfile, const XML_Char *s, i int i = 0; while (i < len && onlyWhiteSpace) { - onlyWhiteSpace = isspace (s[i]); + onlyWhiteSpace = isspace(s[i]); i++; } @@ -710,32 +717,32 @@ void XMLCALL LCPProfile::XmlTextHandler (void *pLCPProfile, const XML_Char *s, i // convert to null terminated char raw[len + 1]; - memcpy (raw, s, len); + memcpy(raw, s, len); raw[len] = 0; char* tag = pProf->lastTag; // Common data section if (!pProf->firstLIDone) { // Generic tags are the same for all - if (!strcmp ("ProfileName", tag)) { - pProf->profileName = Glib::ustring (raw); - } else if (!strcmp ("Model", tag)) { - pProf->camera = Glib::ustring (raw); - } else if (!strcmp ("Lens", tag)) { - pProf->lens = Glib::ustring (raw); - } else if (!strcmp ("CameraPrettyName", tag)) { - pProf->cameraPrettyName = Glib::ustring (raw); - } else if (!strcmp ("LensPrettyName", tag)) { - pProf->lensPrettyName = Glib::ustring (raw); - } else if (!strcmp ("CameraRawProfile", tag)) { - pProf->isRaw = !strcmp ("True", raw); + if (!strcmp("ProfileName", tag)) { + pProf->profileName = Glib::ustring(raw); + } else if (!strcmp("Model", tag)) { + pProf->camera = Glib::ustring(raw); + } else if (!strcmp("Lens", tag)) { + pProf->lens = Glib::ustring(raw); + } else if (!strcmp("CameraPrettyName", tag)) { + pProf->cameraPrettyName = Glib::ustring(raw); + } else if (!strcmp("LensPrettyName", tag)) { + pProf->lensPrettyName = Glib::ustring(raw); + } else if (!strcmp("CameraRawProfile", tag)) { + pProf->isRaw = !strcmp("True", raw); } } // --- Now all floating points. Must replace local dot characters // WARNING: called by different threads, that may run on different local settings, // so don't use system params - if (atof ("1,2345") == 1.2345) { + if (atof("1,2345") == 1.2345) { char* p = raw; while (*p) { @@ -748,69 +755,69 @@ void XMLCALL LCPProfile::XmlTextHandler (void *pLCPProfile, const XML_Char *s, i } if (!pProf->firstLIDone) { - if (!strcmp ("SensorFormatFactor", tag)) { - pProf->sensorFormatFactor = atof (raw); + if (!strcmp("SensorFormatFactor", tag)) { + pProf->sensorFormatFactor = atof(raw); } } // Perspective model base data - if (!strcmp ("FocalLength", tag)) { - pProf->pCurPersModel->focLen = atof (raw); - } else if (!strcmp ("FocusDistance", tag)) { - double focDist = atof (raw); + if (!strcmp("FocalLength", tag)) { + pProf->pCurPersModel->focLen = atof(raw); + } else if (!strcmp("FocusDistance", tag)) { + double focDist = atof(raw); pProf->pCurPersModel->focDist = focDist < 10000 ? focDist : 10000; - } else if (!strcmp ("ApertureValue", tag)) { - pProf->pCurPersModel->aperture = atof (raw); + } else if (!strcmp("ApertureValue", tag)) { + pProf->pCurPersModel->aperture = atof(raw); } // Section depended - if (!strcmp ("FocalLengthX", tag)) { - pProf->pCurCommon->foc_len_x = atof (raw); - } else if (!strcmp ("FocalLengthY", tag)) { - pProf->pCurCommon->foc_len_y = atof (raw); - } else if (!strcmp ("ImageXCenter", tag)) { - pProf->pCurCommon->img_center_x = atof (raw); - } else if (!strcmp ("ImageYCenter", tag)) { - pProf->pCurCommon->img_center_y = atof (raw); - } else if (!strcmp ("ScaleFactor", tag)) { - pProf->pCurCommon->scale_factor = atof (raw); - } else if (!strcmp ("ResidualMeanError", tag)) { - pProf->pCurCommon->mean_error = atof (raw); - } else if (!strcmp ("RadialDistortParam1", tag) || !strcmp ("VignetteModelParam1", tag)) { - pProf->pCurCommon->param[0] = atof (raw); - } else if (!strcmp ("RadialDistortParam2", tag) || !strcmp ("VignetteModelParam2", tag)) { - pProf->pCurCommon->param[1] = atof (raw); - } else if (!strcmp ("RadialDistortParam3", tag) || !strcmp ("VignetteModelParam3", tag)) { - pProf->pCurCommon->param[2] = atof (raw); - } else if (!strcmp ("RadialDistortParam4", tag) || !strcmp ("TangentialDistortParam1", tag)) { - pProf->pCurCommon->param[3] = atof (raw); - } else if (!strcmp ("RadialDistortParam5", tag) || !strcmp ("TangentialDistortParam2", tag)) { - pProf->pCurCommon->param[4] = atof (raw); + if (!strcmp("FocalLengthX", tag)) { + pProf->pCurCommon->foc_len_x = atof(raw); + } else if (!strcmp("FocalLengthY", tag)) { + pProf->pCurCommon->foc_len_y = atof(raw); + } else if (!strcmp("ImageXCenter", tag)) { + pProf->pCurCommon->img_center_x = atof(raw); + } else if (!strcmp("ImageYCenter", tag)) { + pProf->pCurCommon->img_center_y = atof(raw); + } else if (!strcmp("ScaleFactor", tag)) { + pProf->pCurCommon->scale_factor = atof(raw); + } else if (!strcmp("ResidualMeanError", tag)) { + pProf->pCurCommon->mean_error = atof(raw); + } else if (!strcmp("RadialDistortParam1", tag) || !strcmp("VignetteModelParam1", tag)) { + pProf->pCurCommon->param[0] = atof(raw); + } else if (!strcmp("RadialDistortParam2", tag) || !strcmp("VignetteModelParam2", tag)) { + pProf->pCurCommon->param[1] = atof(raw); + } else if (!strcmp("RadialDistortParam3", tag) || !strcmp("VignetteModelParam3", tag)) { + pProf->pCurCommon->param[2] = atof(raw); + } else if (!strcmp("RadialDistortParam4", tag) || !strcmp("TangentialDistortParam1", tag)) { + pProf->pCurCommon->param[3] = atof(raw); + } else if (!strcmp("RadialDistortParam5", tag) || !strcmp("TangentialDistortParam2", tag)) { + pProf->pCurCommon->param[4] = atof(raw); } } -void XMLCALL LCPProfile::XmlEndHandler (void *pLCPProfile, const char *el) +void XMLCALL LCPProfile::XmlEndHandler(void *pLCPProfile, const char *el) { - LCPProfile *pProf = static_cast (pLCPProfile); + LCPProfile *pProf = static_cast(pLCPProfile); // We ignore everything in dirty tag till it's gone if (*pProf->inInvalidTag) { - if (strstr (el, pProf->inInvalidTag)) { + if (strstr(el, pProf->inInvalidTag)) { *pProf->inInvalidTag = 0; } return; } - if (strstr (el, ":CameraProfiles")) { + if (strstr(el, ":CameraProfiles")) { pProf->inCamProfiles = false; } - if (strstr (el, ":AlternateLensIDs")) { + if (strstr(el, ":AlternateLensIDs")) { pProf->inAlternateLensID = false; } - if (strstr (el, ":AlternateLensNames")) { + if (strstr(el, ":AlternateLensNames")) { pProf->inAlternateLensNames = false; } @@ -818,9 +825,9 @@ void XMLCALL LCPProfile::XmlEndHandler (void *pLCPProfile, const char *el) return; } - if (strstr (el, ":PerspectiveModel") || strstr (el, ":FisheyeModel")) { + if (strstr(el, ":PerspectiveModel") || strstr(el, ":FisheyeModel")) { pProf->inPerspect = false; - } else if (strstr (el, ":li")) { + } else if (strstr(el, ":li")) { pProf->aPersModel[pProf->persModelCount] = pProf->pCurPersModel; pProf->pCurPersModel = nullptr; pProf->persModelCount++; @@ -836,11 +843,11 @@ LCPStore* LCPStore::getInstance() LCPProfile* LCPStore::getProfile (Glib::ustring filename) { - if (filename.length() == 0 || !isValidLCPFileName (filename)) { + if (filename.length() == 0 || !isValidLCPFileName(filename)) { return nullptr; } - MyMutex::MyLock lock (mtx); + MyMutex::MyLock lock(mtx); std::map::iterator r = profileCache.find (filename); @@ -849,12 +856,12 @@ LCPProfile* LCPStore::getProfile (Glib::ustring filename) } // Add profile (if exists) - profileCache[filename] = new LCPProfile (filename); + profileCache[filename] = new LCPProfile(filename); //profileCache[filename]->print(); return profileCache[filename]; } -bool LCPStore::isValidLCPFileName (Glib::ustring filename) const +bool LCPStore::isValidLCPFileName(Glib::ustring filename) const { if (!Glib::file_test (filename, Glib::FILE_TEST_EXISTS) || Glib::file_test (filename, Glib::FILE_TEST_IS_DIR)) { return false; @@ -872,10 +879,10 @@ Glib::ustring LCPStore::getDefaultCommonDirectory() const WCHAR pathW[MAX_PATH] = {0}; char pathA[MAX_PATH]; - if (SHGetSpecialFolderPathW (NULL, pathW, CSIDL_COMMON_APPDATA, false)) { + if (SHGetSpecialFolderPathW(NULL, pathW, CSIDL_COMMON_APPDATA, false)) { char pathA[MAX_PATH]; - WideCharToMultiByte (CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); - Glib::ustring fullDir = Glib::ustring (pathA) + Glib::ustring ("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); + WideCharToMultiByte(CP_UTF8, 0, pathW, -1, pathA, MAX_PATH, 0, 0); + Glib::ustring fullDir = Glib::ustring(pathA) + Glib::ustring("\\Adobe\\CameraRaw\\LensProfiles\\1.0"); if (Glib::file_test (fullDir, Glib::FILE_TEST_IS_DIR)) { dir = fullDir; diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 9488f74d7..92c984921 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -39,8 +39,8 @@ public: LCPModelCommon(); bool empty() const; // is it empty void print() const; // printf all values - void merge (const LCPModelCommon& a, const LCPModelCommon& b, float facA); - void prepareParams (int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY); + void merge(const LCPModelCommon& a, const LCPModelCommon& b, float facA); + void prepareParams(int fullWidth, int fullHeight, float focalLength, float focalLength35mm, float sensorFormatFactor, bool swapXY, bool mirrorX, bool mirrorY); //private: using Param = std::array; @@ -75,7 +75,7 @@ public: LCPModelCommon vignette; // vignette (may be empty) LCPPersModel(); - bool hasModeData (int mode) const; + bool hasModeData(int mode) const; void print() const; }; @@ -88,11 +88,11 @@ class LCPProfile LCPPersModel* pCurPersModel; LCPModelCommon* pCurCommon; - static void XMLCALL XmlStartHandler (void *pLCPProfile, const char *el, const char **attr); + static void XMLCALL XmlStartHandler(void *pLCPProfile, const char *el, const char **attr); static void XMLCALL XmlTextHandler (void *pLCPProfile, const XML_Char *s, int len); static void XMLCALL XmlEndHandler (void *pLCPProfile, const char *el); - int filterBadFrames (double maxAvgDevFac, int minFramesLeft); + int filterBadFrames(double maxAvgDevFac, int minFramesLeft); public: // Common data @@ -105,9 +105,9 @@ public: static const int MaxPersModelCount = 3000; LCPPersModel* aPersModel[MaxPersModelCount]; // Do NOT use std::list or something, it's buggy in GCC! - explicit LCPProfile (const Glib::ustring &fname); + explicit LCPProfile(const Glib::ustring &fname); - void calcParams (int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames + void calcParams(int mode, float focalLength, float focusDist, float aperture, LCPModelCommon *pCorr1, LCPModelCommon *pCorr2, LCPModelCommon *pCorr3) const; // Interpolates between the persModels frames void print() const; }; @@ -121,8 +121,8 @@ class LCPStore public: Glib::ustring getDefaultCommonDirectory() const; - bool isValidLCPFileName (Glib::ustring filename) const; - LCPProfile* getProfile (Glib::ustring filename); + bool isValidLCPFileName(Glib::ustring filename) const; + LCPProfile* getProfile(Glib::ustring filename); static LCPStore* getInstance(); }; @@ -143,13 +143,14 @@ public: bool enableCA; // is the mapper capable if CA correction? // precalculates the mapper. - LCPMapper (LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, - const CoarseTransformParams& coarse, int rawRotationDeg); + LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, + const CoarseTransformParams& coarse, int rawRotationDeg); - void correctDistortion (double& x, double& y) const; // MUST be the first stage - void correctCA (double& x, double& y, int channel) const; - void processVignetteLine (int width, int y, float *line) const; - void processVignetteLine3Channels (int width, int y, float *line) const; + void correctDistortion(double& x, double& y) const; // MUST be the first stage + void correctCA(double& x, double& y, int channel) const; + float calcVignetteFac (int x, int y) const; // MUST be in RAW + void processVignetteLine(int width, int y, float *line) const; + void processVignetteLine3Channels(int width, int y, float *line) const; }; } From 7d9e5765ba4f181c3235205d81af1a340d1e94e9 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 22 Feb 2017 14:50:10 +0100 Subject: [PATCH 111/181] Removed unused function and stopwatch --- rtengine/lcp.cc | 11 ----------- rtengine/lcp.h | 5 ++--- rtengine/rawimagesource.cc | 1 - 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/rtengine/lcp.cc b/rtengine/lcp.cc index ff53ad30e..310361a72 100644 --- a/rtengine/lcp.cc +++ b/rtengine/lcp.cc @@ -265,17 +265,6 @@ void LCPMapper::correctCA(double& x, double& y, int channel) const } } -float LCPMapper::calcVignetteFac(int x, int y) const -{ - // No need for swapXY, since vignette is in RAW and always before rotation - float xd = ((float)x - mc.x0) * mc.rfx, yd = ((float)y - mc.y0) * mc.rfy; - - const LCPModelCommon::VignParam vignParam = mc.vign_param; - float rsqr = xd * xd + yd * yd; - - return rsqr * (vignParam[0] + rsqr * ((vignParam[1]) - (vignParam[2]) * rsqr + (vignParam[3]) * rsqr * rsqr)); -} - SSEFUNCTION void LCPMapper::processVignetteLine(int width, int y, float *line) const { // No need for swapXY, since vignette is in RAW and always before rotation diff --git a/rtengine/lcp.h b/rtengine/lcp.h index 92c984921..fa8cf226d 100644 --- a/rtengine/lcp.h +++ b/rtengine/lcp.h @@ -146,9 +146,8 @@ public: LCPMapper(LCPProfile* pProf, float focalLength, float focalLength35mm, float focusDist, float aperture, bool vignette, bool useCADistP, int fullWidth, int fullHeight, const CoarseTransformParams& coarse, int rawRotationDeg); - void correctDistortion(double& x, double& y) const; // MUST be the first stage - void correctCA(double& x, double& y, int channel) const; - float calcVignetteFac (int x, int y) const; // MUST be in RAW + void correctDistortion(double& x, double& y) const; // MUST be the first stage + void correctCA(double& x, double& y, int channel) const; void processVignetteLine(int width, int y, float *line) const; void processVignetteLine3Channels(int width, int y, float *line) const; }; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 995e3d9bd..9678fcaf5 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1767,7 +1767,6 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le LCPProfile *pLCPProf = lcpStore->getProfile(lensProf.lcpFile); if (pLCPProf) { // don't check focal length to allow distortion correction for lenses without chip, also pass dummy focal length 1 in case of 0 - StopWatch Stop1("lcp vignette correction"); LCPMapper map(pLCPProf, max(idata->getFocalLen(), 1.0), idata->getFocalLen35mm(), idata->getFocusDist(), idata->getFNumber(), true, false, W, H, coarse, -1); if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS || ri->get_colors() == 1) { From d3a5a8ee9606503927c33e4023d2065b2d65562c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 22 Feb 2017 18:47:00 +0100 Subject: [PATCH 112/181] started working on support for dynamic processing profiles --- rtengine/procparams.cc | 2 + rtgui/CMakeLists.txt | 3 +- rtgui/dynamicprofile.cc | 180 ++++++++++++++++++++++++++++++++++++++++ rtgui/dynamicprofile.h | 73 ++++++++++++++++ rtgui/main.cc | 15 +++- rtgui/options.cc | 4 + rtgui/options.h | 2 + rtgui/profilestore.cc | 21 ++++- rtgui/profilestore.h | 6 ++ rtgui/thumbnail.cc | 19 ++++- 10 files changed, 318 insertions(+), 7 deletions(-) create mode 100644 rtgui/dynamicprofile.cc create mode 100644 rtgui/dynamicprofile.h diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 7f4ae11f8..f01fa57d2 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -8079,6 +8079,8 @@ int PartialProfile::load (const Glib::ustring &fName) if (fName == DEFPROFILE_INTERNAL) { return 0; + } else if (fName == DEFPROFILE_DYNAMIC) { + return -1; // should not happen here } else { return pparams->load(fName, pedited); } diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 021f6163f..e327cb4b6 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -30,7 +30,8 @@ set (BASESOURCEFILES darkframe.cc flatfield.cc rawcacorrection.cc rawexposure.cc wavelet.cc dirpyrequalizer.cc hsvequalizer.cc defringe.cc popupcommon.cc popupbutton.cc popuptogglebutton.cc sharpenedge.cc sharpenmicro.cc colorappearance.cc - filmsimulation.cc prsharpening.cc) + filmsimulation.cc prsharpening.cc + dynamicprofile.cc) include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc new file mode 100644 index 000000000..e2cedbb0a --- /dev/null +++ b/rtgui/dynamicprofile.cc @@ -0,0 +1,180 @@ +/* -*- C++ -*- + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * 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 . + */ + +#include "dynamicprofile.h" +#include + +using namespace rtengine; +using namespace rtengine::procparams; + +DynamicProfileEntry::DynamicProfileEntry(): + serial_number(0), + has_iso(false), iso_min(0), iso_max(1000000), + has_fnumber(false), fnumber_min(0.0), fnumber_max(1000.0), + has_focallen(false), focallen_min(0.0), focallen_max(1000000.0), + has_shutterspeed(false), shutterspeed_min(1000.0), shutterspeed_max(1.0/1000000.0), + has_expcomp(false), expcomp_min(-100.0), expcomp_max(100.0), + has_make(false), make(""), + has_model(false), model(""), + has_lens(false), lens(""), + profilepath("") +{ +} + + +bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const +{ + return serial_number < other.serial_number; +} + + +bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) +{ + if (has_iso) { + int iso = im->getISOSpeed(); + if (iso < iso_min || iso > iso_max) { + return false; + } + } + if (has_fnumber) { + double fnumber = im->getFNumber(); + if (fnumber < fnumber_min || fnumber > fnumber_max) { + return false; + } + } + if (has_focallen) { + double focallen = im->getFocalLen(); + if (focallen < focallen_min || focallen > focallen_max) { + return false; + } + } + if (has_shutterspeed) { + double shutterspeed = im->getShutterSpeed(); + if (shutterspeed < shutterspeed_min || shutterspeed > shutterspeed_max){ + return false; + } + } + if (has_expcomp) { + double expcomp = im->getExpComp(); + if (expcomp < expcomp_min || expcomp > expcomp_max) { + return false; + } + } + if (has_make) { + if (im->getMake() != make) { + return false; + } + } + if (has_model) { + if (im->getModel() != model) { + return false; + } + } + if (has_lens) { + if (im->getLens() != lens) { + return false; + } + } + return true; +} + + +bool loadDynamicProfileEntries(std::vector &out) +{ + out.clear(); + Glib::KeyFile kf; + if (!kf.load_from_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + return false; + } + printf("loading dynamic profiles...\n"); + auto groups = kf.get_groups(); + for (auto group : groups) { + // groups are of the form "entry N", where N is a positive integer + if (group.find("entry ") != 0) { + return false; + } + std::istringstream buf(group.c_str() + 6); + int serial = 0; + if (!(buf >> serial) || !buf.eof()) { + return false; + } + printf(" loading entry %d\n", serial); + + out.emplace_back(DynamicProfileEntry()); + DynamicProfileEntry &entry = out.back(); + entry.serial_number = serial; + entry.has_iso = kf.get_boolean(group, "has_iso"); + entry.iso_min = kf.get_integer(group, "iso_min"); + entry.iso_max = kf.get_integer(group, "iso_max"); + + entry.has_fnumber = kf.get_boolean(group, "has_fnumber"); + entry.fnumber_min = kf.get_double(group, "fnumber_min"); + entry.fnumber_max = kf.get_double(group, "fnumber_max"); + + entry.has_focallen = kf.get_boolean(group, "has_focallen"); + entry.focallen_min = kf.get_double(group, "focallen_min"); + entry.focallen_max = kf.get_double(group, "focallen_max"); + + entry.has_shutterspeed = kf.get_boolean(group, "has_shutterspeed"); + entry.shutterspeed_min = kf.get_double(group, "shutterspeed_min"); + entry.shutterspeed_max = kf.get_double(group, "shutterspeed_max"); + + entry.has_expcomp = kf.get_boolean(group, "has_expcomp"); + entry.expcomp_min = kf.get_double(group, "expcomp_min"); + entry.expcomp_max = kf.get_double(group, "expcomp_max"); + + entry.has_make = kf.get_boolean(group, "has_make"); + entry.make = kf.get_string(group, "make"); + + entry.has_model = kf.get_boolean(group, "has_model"); + entry.model = kf.get_string(group, "model"); + + entry.has_lens = kf.get_boolean(group, "has_lens"); + entry.lens = kf.get_string(group, "lens"); + + entry.profilepath = kf.get_string(group, "profilepath"); + } + std::sort(out.begin(), out.end()); + return true; +} + + +PartialProfile *loadDynamicProfile(const ImageMetaData *im) +{ + PartialProfile *ret = new PartialProfile(true, true); + std::vector entries; + if (loadDynamicProfileEntries(entries)) { + for (auto &entry : entries) { + if (entry.matches(im)) { + printf("found matching profile %s\n", + entry.profilepath.c_str()); + PartialProfile p(true, true); + if (!p.load(options.findProfilePath(entry.profilepath))) { + p.applyTo(ret->pparams); + } else { + printf("ERROR loading matching profile\n"); + } + p.deleteInstance(); + } + } + } + return ret; +} + diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h new file mode 100644 index 000000000..5d8596c77 --- /dev/null +++ b/rtgui/dynamicprofile.h @@ -0,0 +1,73 @@ +/* -*- C++ -*- + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * 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 . + */ +#ifndef _DYNAMICPROFILE_H_ +#define _DYNAMICPROFILE_H_ + +#include + +#include "options.h" + + +class DynamicProfileEntry { +public: + DynamicProfileEntry(); + bool matches(const rtengine::ImageMetaData *im); + bool operator<(const DynamicProfileEntry &other) const; + + int serial_number; + + bool has_iso; + int iso_min; + int iso_max; + + bool has_fnumber; + double fnumber_min; + double fnumber_max; + + bool has_focallen; + double focallen_min; + double focallen_max; + + bool has_shutterspeed; + double shutterspeed_min; + double shutterspeed_max; + + bool has_expcomp; + double expcomp_min; + double expcomp_max; + + bool has_make; + std::string make; + + bool has_model; + std::string model; + + bool has_lens; + std::string lens; + + Glib::ustring profilepath; +}; + + +bool loadDynamicProfileEntries(std::vector &out); +rtengine::procparams::PartialProfile *loadDynamicProfile( + const rtengine::ImageMetaData *im); + + +#endif // _DYNAMICPROFILE_H_ diff --git a/rtgui/main.cc b/rtgui/main.cc index d6b99fdc6..ee3d3210e 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -37,6 +37,7 @@ #include "rtimage.h" #include "version.h" #include "extprog.h" +#include "dynamicprofile.h" #ifndef WIN32 #include @@ -710,7 +711,7 @@ int processLineParams( int argc, char **argv ) rawParams = new rtengine::procparams::PartialProfile(true, true); Glib::ustring profPath = options.findProfilePath(options.defProfRaw); - if (options.is_defProfRawMissing() || profPath.empty() || rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension))) { + if (options.is_defProfRawMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension)))) { std::cerr << "Error: default raw processing profile not found" << std::endl; rawParams->deleteInstance(); delete rawParams; @@ -721,7 +722,7 @@ int processLineParams( int argc, char **argv ) imgParams = new rtengine::procparams::PartialProfile(true); profPath = options.findProfilePath(options.defProfImg); - if (options.is_defProfImgMissing() || profPath.empty() || imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension))) { + if (options.is_defProfImgMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension)))) { std::cerr << "Error: default non-raw processing profile not found" << std::endl; imgParams->deleteInstance(); delete imgParams; @@ -793,9 +794,19 @@ int processLineParams( int argc, char **argv ) if (useDefault) { if (isRaw) { + if (options.defProfRaw == DEFPROFILE_DYNAMIC) { + rawParams->deleteInstance(); + delete rawParams; + rawParams = loadDynamicProfile(ii->getMetaData()); + } std::cout << " Merging default raw processing profile" << std::endl; rawParams->applyTo(¤tParams); } else { + if (options.defProfImg == DEFPROFILE_DYNAMIC) { + imgParams->deleteInstance(); + delete imgParams; + imgParams = loadDynamicProfile(ii->getMetaData()); + } std::cout << " Merging default non-raw processing profile" << std::endl; imgParams->applyTo(¤tParams); } diff --git a/rtgui/options.cc b/rtgui/options.cc index 5c0c032eb..2f71a7106 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -241,6 +241,10 @@ Glib::ustring Options::findProfilePath (Glib::ustring &profName) return profName; } + if (profName == DEFPROFILE_DYNAMIC) { + return profName; + } + Glib::ustring p = profName.substr (0, 4); if (p == "${U}") { diff --git a/rtgui/options.h b/rtgui/options.h index 43f64a4df..efc649417 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -39,6 +39,8 @@ #define DEFPROFILE_IMG "Neutral" // Profile name to use for internal values' profile #define DEFPROFILE_INTERNAL "Neutral" +// Special name for the Dynamic profile +#define DEFPROFILE_DYNAMIC "Dynamic" class SaveFormat { diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 6b1171d23..9f32b735f 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -26,7 +26,7 @@ ProfileStore profileStore; using namespace rtengine; using namespace rtengine::procparams; -ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr) +ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr), internalDynamicEntry(nullptr) { internalDefaultProfile = new AutoPartialProfile(); internalDefaultProfile->set(true); @@ -63,6 +63,8 @@ ProfileStore::~ProfileStore () partProfiles.clear (); clearFileList(); delete internalDefaultProfile; + delete internalDefaultEntry; + delete internalDynamicEntry; lock.release(); delete parseMutex; parseMutex = nullptr; @@ -140,6 +142,10 @@ void ProfileStore::_parseProfiles () entries.push_back(internalDefaultEntry); partProfiles[internalDefaultEntry] = internalDefaultProfile; + if (!internalDynamicEntry) { + internalDynamicEntry = new ProfileStoreEntry(Glib::ustring("(") + M("PROFILEPANEL_PDYNAMIC") + Glib::ustring(")"), PSET_FILE, 0, 0); + // do not add it to the entries. This is here only for the preferences dialog + } // Check if the default profiles has been found. if (findEntryFromFullPathU(options.defProfRaw) == nullptr) { @@ -273,7 +279,7 @@ const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path return nullptr; } - if (path == DEFPROFILE_INTERNAL) { + if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) { return internalDefaultEntry; } @@ -603,7 +609,6 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, */ void ProfileStoreComboBox::updateProfileList () { - // clear items clear(); refTreeModel.clear(); @@ -622,6 +627,7 @@ void ProfileStoreComboBox::updateProfileList () // special case for the Internal default entry addRow(profileStore.getInternalDefaultPSE()); } + addRow(profileStore.getInternalDynamicPSE()); // releasing the profilestore's entry list mutex profileStore.releaseFileList(); @@ -710,6 +716,11 @@ Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path) return row; } + if (path == DEFPROFILE_DYNAMIC) { + row = findRowFromEntry(profileStore.getInternalDynamicPSE()); + return row; + } + // removing the filename Glib::ustring fName = Glib::path_get_basename(path); @@ -758,6 +769,10 @@ Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow() return Glib::ustring(DEFPROFILE_INTERNAL); } + if (currEntry == profileStore.getInternalDynamicPSE()) { + return Glib::ustring(DEFPROFILE_DYNAMIC); + } + path = Glib::build_filename(profileStore.getPathFromId(currEntry->parentFolderId), currEntry->label); } diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index ab45a0867..5e5591e15 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -144,6 +144,7 @@ private: StoreState storeState; rtengine::procparams::AutoPartialProfile *internalDefaultProfile; ProfileStoreEntry *internalDefaultEntry; + ProfileStoreEntry *internalDynamicEntry; /** Alphabetically ordered list of folder and files through Gtk::Label sub-class; * ready to be used in Menu and Combobox @@ -198,6 +199,11 @@ public: return internalDefaultEntry; } + const ProfileStoreEntry* getInternalDynamicPSE() + { + return internalDynamicEntry; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 03ef49a5e..b560c5f7b 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -30,6 +30,7 @@ #include "profilestore.h" #include "batchqueue.h" #include "extprog.h" +#include "dynamicprofile.h" using namespace rtengine::procparams; @@ -217,7 +218,23 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); - if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + if (defProf == DEFPROFILE_DYNAMIC && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + rtengine::ImageMetaData* imageMetaData; + if (getType() == FT_Raw) { + rtengine::RawMetaDataLocation metaData = rtengine::Thumbnail::loadMetaDataFromRaw(fname); + imageMetaData = rtengine::ImageMetaData::fromFile (fname, &metaData); + } else { + imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); + } + PartialProfile *pp = loadDynamicProfile(imageMetaData); + if (options.paramsLoadLocation == PLL_Input) { + pp->pparams->save(fname + paramFileExtension); + } else { + pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); + } + pp->deleteInstance(); + delete pp; + } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; From b6ac12b015d14e66d3fd92b53175eb4d678763eb Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Thu, 23 Feb 2017 13:13:54 +0100 Subject: [PATCH 113/181] Update Deutsch --- rtdata/languages/Deutsch | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 0cdfd7437..deec1e3a1 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -33,7 +33,8 @@ #32 2016-12-29 Erweiterung/Korrekturen (TooWaBoo) RT4.2.1464 #33 2017-01-04 Erweiterung/Korrekturen/Soft-Proofing (TooWaBoo) RT4.2.1477 #34 2017-01-07 IPTC (TooWaBoo) RT4.2.1492 -#35 2017-02-18 AWB bias (TooWaBoo) +#35 2017-02-18 AWB bias (TooWaBoo) RT 5.0 r1 +#36 2017-02-23 Korrekturen (TooWaBoo) RT 5.0 r1 ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen @@ -1082,7 +1083,7 @@ PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Öffnen eines neuen Bildes mit den Zoom- und PREFERENCES_RGBDTL_LABEL;Maximale Anzahl Threads für Rauschreduzierung PREFERENCES_RGBDTL_TOOLTIP;Die Rauschreduzierung benötigt mindestens 128MB RAM für ein 10 Megapixel-Bild oder 512MB für ein 40 Megapixel-Bild, und zusätzlich 128MB RAM pro Thread. Je mehr Threads parallel ablaufen, desto schneller ist die Berechnung. Bei Einstellung "0" werden so viele Threads wie möglich benutzt. PREFERENCES_SELECTFONT;Schriftart -PREFERENCES_SELECTFONT_COLPICKER;Schriftart für den Farbwähler +PREFERENCES_SELECTFONT_COLPICKER;Schriftart für die Farbwähler PREFERENCES_SELECTLANG;Sprache PREFERENCES_SELECTTHEME;Oberflächendesign (erfordert Neustart) PREFERENCES_SERIALIZE_TIFF_READ;TIFF-Bilder @@ -1187,7 +1188,7 @@ THRESHOLDSELECTOR_HINT;Umschalt-Taste halten, um individuelle\nKontrollpu THRESHOLDSELECTOR_T;Oben THRESHOLDSELECTOR_TL;Oben-Links THRESHOLDSELECTOR_TR;Oben-Rechts -TOOLBAR_TOOLTIP_COLORPICKER;Farbwähler\n\nWenn eingeschaltet:\n- Mit der linken Maustaste können Sie einen Farbwähler setzen.\n- Zum Verschieben, linke Maustaste festhalten.\n- Umschalttaste + Rechts-Klick entfernt alle Farbwähler.\n- Rechts-Klick in einen freien Bereich schaltet auf das Hand-Werkzeug zurück. +TOOLBAR_TOOLTIP_COLORPICKER;Farbwähler\n\nWenn eingeschaltet:\n- Mit der linken Maustaste können Sie einen Farbwähler setzen.\n- Zum Verschieben, linke Maustaste festhalten.\n- Umschalttaste + Rechts-Klick entfernt alle Farbwähler.\n- Rechts-Klick auf den Farbwählerbutton blendet die Farbwähler ein/aus\n- Rechts-Klick in einen freien Bereich schaltet auf das Hand-Werkzeug zurück. TOOLBAR_TOOLTIP_CROP;Ausschnitt wählen\nTaste: c\n\nZum Verschieben des Ausschnitts,\nUmschalttaste festhalten. TOOLBAR_TOOLTIP_HAND;Hand-Werkzeug\nTaste: h TOOLBAR_TOOLTIP_STRAIGHTEN;Ausrichten / Drehen\nTaste: s\n\nRichtet das Bild entlang einer Leitlinie aus. @@ -1755,7 +1756,7 @@ TP_RETINEX_MAP_METHOD_TOOLTIP;Keine: Wendet die Maske, die mit der gaußs TP_RETINEX_MAP_NONE;Keine TP_RETINEX_MEDIAN;Medianfilter TP_RETINEX_METHOD;Methode -TP_RETINEX_METHOD_TOOLTIP;Schatten wirkt sich auf dunkle Bereiche aus.\n\nSchatten & Lichter wirkt sich auf dunkle und helle Bereiche aus.\n\nLichter wirkt sich auf helle Bereiche aus.\n\nSpitzlichter wirkt sich auf sehr helle Bereiche aus und reduziert\nMagenta-Falschfarben. +TP_RETINEX_METHOD_TOOLTIP;Schatten wirkt sich auf dunkle Bereiche aus.\n\nSchatten / Lichter wirkt sich auf dunkle und helle Bereiche aus.\n\nLichter wirkt sich auf helle Bereiche aus.\n\nSpitzlichter wirkt sich auf sehr helle Bereiche aus und reduziert\nMagenta-Falschfarben. TP_RETINEX_MLABEL;Schleierred: Min = %1, Max = %2 TP_RETINEX_MLABEL_TOOLTIP;Sollte nahe bei Min = 0 und Max = 32768 sein TP_RETINEX_NEIGHBOR;Radius @@ -1776,7 +1777,7 @@ TP_RETINEX_TLABEL_TOOLTIP;Ergebnis der Transmissionskurve: Min, Max, Mittel und TP_RETINEX_TRANF;Transmission TP_RETINEX_TRANSMISSION;Transmissionskurve TP_RETINEX_TRANSMISSION_TOOLTIP;Transmission in Abhängigkeit der Transmission.\n\nx-Achse: Transmission negativer Werte (Min),\nMittel und positiver Werte (Max).\n\ny-Achse: Verstärkung oder Abschwächung. -TP_RETINEX_UNIFORM;Schatten & Lichter +TP_RETINEX_UNIFORM;Schatten / Lichter TP_RETINEX_VARIANCE;Kontrast TP_RETINEX_VARIANCE_TOOLTIP;Niedrige Werte erhöhen den lokalen\nKontrast und die Sättigung, können\naber zu Artefakten führen. TP_RETINEX_VIEW;Vorschau @@ -2070,4 +2071,3 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Ausschnitt an Bildschirm anpassen\nTaste: Alt ZOOMPANEL_ZOOMFITSCREEN;An Bildschirm anpassen\nTaste: f ZOOMPANEL_ZOOMIN;Hineinzoomen\nTaste: + ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - - From e6288be74be03717fff6e880c89b1ac407397416 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 25 Feb 2017 04:47:33 +0100 Subject: [PATCH 114/181] Update TooWaBlue-GTK3-_19.css --- rtdata/themes/TooWaBlue-GTK3-_19.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-_19.css b/rtdata/themes/TooWaBlue-GTK3-_19.css index 24442aae3..530ae3d3a 100644 --- a/rtdata/themes/TooWaBlue-GTK3-_19.css +++ b/rtdata/themes/TooWaBlue-GTK3-_19.css @@ -1,7 +1,7 @@ /* This file is part of RawTherapee. - Copyright (c) 2016 TooWaBoo (v1.19.4) + Copyright (c) 2016 TooWaBoo (v1.19.5) Many thanks to the RawTherapee Developer Team for this great piece of software RawTherapee is free software: you can redistribute it and/or modify @@ -397,7 +397,7 @@ GtkDialog tab { padding: 9px; } #ToolPanelNotebook tab { - padding: 10px 4px 7px; + padding: 10px 2px 7px; } #MetaPanelNotebook tab { padding: 8px 4px; From 213d46b8b6297e94d41e6a554cb7be9dd3455f8f Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 25 Feb 2017 04:51:29 +0100 Subject: [PATCH 115/181] Update 2.46, Reserved space in toolpanelnotebook for locallab --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index 4c6bbc3ef..754aa61b7 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.45 - requires RT 5.0 + Version 2.46 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -789,6 +789,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-bottom: 0.33334em; } +#ToolPanelNotebook > header tab{ + margin-left: 0.08333em; + margin-right: 0.08333em; +} + #ToolPanelNotebook > header tab image{ min-height: 2.5em; min-width: calc(1.66667em + 6px); From 5bc94da2693be88bf2e2c503cebf28c72a67205f Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 25 Feb 2017 04:52:45 +0100 Subject: [PATCH 116/181] Update 2.46, Reserved space in toolpanelnotebook for locallab --- rtdata/themes/TooWaBlue-GTK3-20_.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 795dc0441..5c4cf2fed 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.45 - requires RT 5.0 + Version 2.46 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -789,6 +789,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-bottom: 0.33334em; } +#ToolPanelNotebook > header tab{ + margin-left: 0.08333em; + margin-right: 0.08333em; +} + #ToolPanelNotebook > header tab image{ min-height: 2.5em; min-width: calc(1.66667em + 6px); From 6162cbd77d8d4f3a4478b15cbfb700b79fe994d0 Mon Sep 17 00:00:00 2001 From: Hombre Date: Mon, 27 Feb 2017 01:12:40 +0100 Subject: [PATCH 117/181] Adding more profiles to the Output color profile combobox in ICM You can now select output profiles of the "OUTPUT" device class, but only if they have an RGB color-space. --- rtengine/iccstore.cc | 12 +++++++++--- rtengine/improccoordinator.cc | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc index 16ae455b6..52dd25573 100644 --- a/rtengine/iccstore.cc +++ b/rtengine/iccstore.cc @@ -173,9 +173,15 @@ std::vector ICCStore::getProfiles (const ProfileType type) const std::vector res; for (const auto profile : fileProfiles) { - if ( (type==ICCStore::ProfileType::MONITOR && cmsGetDeviceClass(profile.second) == cmsSigDisplayClass && cmsGetColorSpace (profile.second) == cmsSigRgbData) - || (type==ICCStore::ProfileType::PRINTER && cmsGetDeviceClass(profile.second) == cmsSigOutputClass) - || (type==ICCStore::ProfileType::OUTPUT && cmsGetDeviceClass(profile.second) == cmsSigDisplayClass && cmsGetColorSpace (profile.second) == cmsSigRgbData) ) + if ( (type==ICCStore::ProfileType::MONITOR + && cmsGetDeviceClass(profile.second) == cmsSigDisplayClass + && cmsGetColorSpace (profile.second) == cmsSigRgbData) + || (type==ICCStore::ProfileType::PRINTER + && cmsGetDeviceClass(profile.second) == cmsSigOutputClass) + || (type==ICCStore::ProfileType::OUTPUT + && (cmsGetDeviceClass(profile.second) == cmsSigDisplayClass || cmsGetDeviceClass(profile.second) == cmsSigOutputClass) + && cmsGetColorSpace (profile.second) == cmsSigRgbData) + ) { res.push_back (profile.first); } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index f02ddf14e..b36a9f9a2 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -801,7 +801,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) MyMutex::MyLock prevImgLock(previmg->getMutex()); try { - // Computing the preview image, i.e. converting from WCS->Monitor color space (soft-proofing disabled) or WCS->Output profile->Monitor color space (soft-proofing enabled) + // Computing the preview image, i.e. converting from WCS->Monitor color space (soft-proofing disabled) or WCS->Printer profile->Monitor color space (soft-proofing enabled) ipf.lab2monitorRgb (nprevl, previmg); // Computing the internal image for analysis, i.e. conversion from WCS->Output profile From 66382743cd0ab251e361b9615712aaeb9f36f19c Mon Sep 17 00:00:00 2001 From: "U-coolermaster2\\cuniek" Date: Mon, 27 Feb 2017 19:41:23 +0100 Subject: [PATCH 118/181] Improved DCB, less macroblicking on diagonals, much faster code --- rtengine/demosaic_algos.cc | 403 ++++++++++++++++++++----------------- rtengine/rawimagesource.h | 26 +-- 2 files changed, 232 insertions(+), 197 deletions(-) diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index e641ed777..0f0e4841f 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -1,3 +1,4 @@ +/* /* * This file is part of RawTherapee. * @@ -37,7 +38,7 @@ #include "sleef.c" #include "opthelper.h" #include "median.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" #ifdef _OPENMP #include @@ -3251,7 +3252,7 @@ void RawImageSource::refinement_lassus(int PassCount) * the code is open source (BSD licence) */ -#define TILESIZE 256 +#define TILESIZE 192 #define TILEBORDER 10 #define CACHESIZE (TILESIZE+2*TILEBORDER) @@ -3279,7 +3280,7 @@ inline void RawImageSource::dcb_initTileLimits(int &colMin, int &rowMin, int &co } } -void RawImageSource::fill_raw( float (*cache )[4], int x0, int y0, float** rawData) +void RawImageSource::fill_raw( float (*cache )[3], int x0, int y0, float** rawData) { int rowMin, colMin, rowMax, colMax; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 0); @@ -3290,7 +3291,7 @@ void RawImageSource::fill_raw( float (*cache )[4], int x0, int y0, float** rawDa } } -void RawImageSource::fill_border( float (*cache )[4], int border, int x0, int y0) +void RawImageSource::fill_border( float (*cache )[3], int border, int x0, int y0) { unsigned row, col, y, x, f, c; float sum[8]; @@ -3325,93 +3326,55 @@ void RawImageSource::fill_border( float (*cache )[4], int border, int x0, int y0 } } } + // saves red and blue -void RawImageSource::copy_to_buffer( float (*buffer)[3], float (*image)[4]) + +// change buffer[3] -> buffer[2], possibly to buffer[1] if split +// into two loops, one for R and another for B, could also be smaller because +// there is no need for green pixels pass +// this would decrease the amount of needed memory +// from megapixels*2 records to megapixels*0.5 +// also don't know if float is needed as data is 1-65536 integer (I believe!!) +// comment from Ingo: float is needed because rawdata in rt is float +void RawImageSource::copy_to_buffer( float (*buffer)[2], float (*image)[3]) { for (int indx = 0; indx < CACHESIZE * CACHESIZE; indx++) { buffer[indx][0] = image[indx][0]; //R - buffer[indx][2] = image[indx][2]; //B + buffer[indx][1] = image[indx][2]; //B } } // restores red and blue -void RawImageSource::restore_from_buffer(float (*image)[4], float (*buffer)[3]) + +// other comments like in copy_to_buffer +void RawImageSource::restore_from_buffer(float (*image)[3], float (*buffer)[2]) { for (int indx = 0; indx < CACHESIZE * CACHESIZE; indx++) { image[indx][0] = buffer[indx][0]; //R - image[indx][2] = buffer[indx][2]; //B + image[indx][2] = buffer[indx][1]; //B } } // First pass green interpolation -void RawImageSource::dcb_hid(float (*image)[4], float (*bufferH)[3], float (*bufferV)[3], int x0, int y0) + +// remove entirely: bufferH and bufferV +void RawImageSource::dcb_hid(float (*image)[3], int x0, int y0) { - const int u = CACHESIZE, v = 2 * CACHESIZE; - int rowMin, colMin, rowMax, colMax; + const int u = CACHESIZE; + int rowMin, colMin, rowMax, colMax, c; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2); - // green pixels - for (int row = rowMin; row < rowMax; row++) { - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col; col < colMax; col += 2, indx += 2) { - assert(indx - u >= 0 && indx + u < u * u); - bufferH[indx][1] = (image[indx - 1][1] + image[indx + 1][1]) * 0.5f; - bufferV[indx][1] = (image[indx + u][1] + image[indx - u][1]) * 0.5f; - } - } - - // red in blue pixel, blue in red pixel +// simple green bilinear in R and B pixels for (int row = rowMin; row < rowMax; row++) - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = 2 - FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { + for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx - u - 1 >= 0 && indx + u + 1 < u * u && c >= 0 && c < 3); - bufferH[indx][c] = ( 4.f * bufferH[indx][1] - - bufferH[indx + u + 1][1] - bufferH[indx + u - 1][1] - bufferH[indx - u + 1][1] - bufferH[indx - u - 1][1] - + image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] ) * 0.25f; - bufferV[indx][c] = ( 4.f * bufferV[indx][1] - - bufferV[indx + u + 1][1] - bufferV[indx + u - 1][1] - bufferV[indx - u + 1][1] - bufferV[indx - u - 1][1] - + image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] ) * 0.25f; - } - - // red or blue in green pixels - for (int row = rowMin; row < rowMax; row++) - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin + 1) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1), d = 2 - c; col < colMax; col += 2, indx += 2) { - assert(indx - u >= 0 && indx + u < u * u && c >= 0 && c < 3 && d >= 0 && d < 3); - bufferH[indx][c] = (image[indx + 1][c] + image[indx - 1][c]) * 0.5f; - bufferH[indx][d] = (2.f * bufferH[indx][1] - bufferH[indx + u][1] - bufferH[indx - u][1] + image[indx + u][d] + image[indx - u][d]) * 0.5f; - bufferV[indx][c] = (2.f * bufferV[indx][1] - bufferV[indx + 1][1] - bufferV[indx - 1][1] + image[indx + 1][c] + image[indx - 1][c]) * 0.5f; - bufferV[indx][d] = (image[indx + u][d] + image[indx - u][d]) * 0.5f; - } - - // Decide green pixels - for (int row = rowMin; row < rowMax; row++) - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col), d = 2 - c; col < colMax; col += 2, indx += 2) { - float current = max(image[indx + v][c], image[indx - v][c], image[indx - 2][c], image[indx + 2][c]) - - min(image[indx + v][c], image[indx - v][c], image[indx - 2][c], image[indx + 2][c]) + - max(image[indx + 1 + u][d], image[indx + 1 - u][d], image[indx - 1 + u][d], image[indx - 1 - u][d]) - - min(image[indx + 1 + u][d], image[indx + 1 - u][d], image[indx - 1 + u][d], image[indx - 1 - u][d]); - - float currentH = max(bufferH[indx + v][d], bufferH[indx - v][d], bufferH[indx - 2][d], bufferH[indx + 2][d]) - - min(bufferH[indx + v][d], bufferH[indx - v][d], bufferH[indx - 2][d], bufferH[indx + 2][d]) + - max(bufferH[indx + 1 + u][c], bufferH[indx + 1 - u][c], bufferH[indx - 1 + u][c], bufferH[indx - 1 - u][c]) - - min(bufferH[indx + 1 + u][c], bufferH[indx + 1 - u][c], bufferH[indx - 1 + u][c], bufferH[indx - 1 - u][c]); - - float currentV = max(bufferV[indx + v][d], bufferV[indx - v][d], bufferV[indx - 2][d], bufferV[indx + 2][d]) - - min(bufferV[indx + v][d], bufferV[indx - v][d], bufferV[indx - 2][d], bufferV[indx + 2][d]) + - max(bufferV[indx + 1 + u][c], bufferV[indx + 1 - u][c], bufferV[indx - 1 + u][c], bufferV[indx - 1 - u][c]) - - min(bufferV[indx + 1 + u][c], bufferV[indx + 1 - u][c], bufferV[indx - 1 + u][c], bufferV[indx - 1 - u][c]); - - assert(indx >= 0 && indx < u * u); - - if (ABS(current - currentH) < ABS(current - currentV)) { - image[indx][1] = bufferH[indx][1]; - } else { - image[indx][1] = bufferV[indx][1]; - } - } + image[indx][1] = 0.25*(image[indx-1][1]+image[indx+1][1]+image[indx-u][1]+image[indx+u][1]); + } } -// missing colors are interpolated -void RawImageSource::dcb_color(float (*image)[4], int x0, int y0) +// missing colours are interpolated +void RawImageSource::dcb_color(float (*image)[3], int x0, int y0) { const int u = CACHESIZE; int rowMin, colMin, rowMax, colMax; @@ -3421,22 +3384,39 @@ void RawImageSource::dcb_color(float (*image)[4], int x0, int y0) for (int row = rowMin; row < rowMax; row++) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = 2 - FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx >= 0 && indx < u * u && c >= 0 && c < 4); + + +//Jacek comment: one multiplication less + image[indx][c] = image[indx][1] + + ( image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] + - (image[indx + u + 1][1] + image[indx + u - 1][1] + image[indx - u + 1][1] + image[indx - u - 1][1]) ) * 0.25f; + +/* original image[indx][c] = ( 4.f * image[indx][1] - image[indx + u + 1][1] - image[indx + u - 1][1] - image[indx - u + 1][1] - image[indx - u - 1][1] + image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] ) * 0.25f; - } +*/ + } // red or blue in green pixels for (int row = rowMin; row < rowMax; row++) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin + 1) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1), d = 2 - c; col < colMax; col += 2, indx += 2) { assert(indx >= 0 && indx < u * u && c >= 0 && c < 4); + +//Jacek comment: two multiplications (in total) less + image[indx][c] = image[indx][1] + (image[indx + 1][c] + image[indx - 1][c] - (image[indx + 1][1] + image[indx - 1][1])) * 0.5f; + image[indx][d] = image[indx][1] + (image[indx + u][d] + image[indx - u][d] - (image[indx + u][1] + image[indx - u][1])) * 0.5f; + + +/* original image[indx][c] = (2.f * image[indx][1] - image[indx + 1][1] - image[indx - 1][1] + image[indx + 1][c] + image[indx - 1][c]) * 0.5f; image[indx][d] = (2.f * image[indx][1] - image[indx + u][1] - image[indx - u][1] + image[indx + u][d] + image[indx - u][d]) * 0.5f; - } +*/ + } } // green correction -void RawImageSource::dcb_hid2(float (*image)[4], int x0, int y0) +void RawImageSource::dcb_hid2(float (*image)[3], int x0, int y0) { const int u = CACHESIZE, v = 2 * CACHESIZE; int rowMin, colMin, rowMax, colMax; @@ -3445,8 +3425,16 @@ void RawImageSource::dcb_hid2(float (*image)[4], int x0, int y0) for (int row = rowMin; row < rowMax; row++) { for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx - v >= 0 && indx + v < u * u); - image[indx][1] = (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1]) * 0.25f + + +//Jacek comment: one multiplication less + image[indx][1] = image[indx][c] + + (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1] + - (image[indx + v][c] + image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c])) * 0.25f; + +/* original + image[indx][1] = (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1]) * 0.25f + image[indx][c] - ( image[indx + v][c] + image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c]) * 0.25f; +*/ } } } @@ -3456,9 +3444,12 @@ void RawImageSource::dcb_hid2(float (*image)[4], int x0, int y0) // 1 = vertical // 0 = horizontal // saved in image[][3] -void RawImageSource::dcb_map(float (*image)[4], int x0, int y0) + +// seems at least 2 persons implemented some code, as this one has different coding style, could be unified +// I don't know if *pix is faster than a loop working on image[] directly +void RawImageSource::dcb_map(float (*image)[3], uint8_t *map, int x0, int y0) { - const int u = 4 * CACHESIZE; + const int u = 3 * CACHESIZE; int rowMin, colMin, rowMax, colMax; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2); @@ -3468,36 +3459,41 @@ void RawImageSource::dcb_map(float (*image)[4], int x0, int y0) assert(indx >= 0 && indx < u * u); - if ( *pix > ( pix[-4] + pix[+4] + pix[-u] + pix[+u]) / 4 ) { - image[indx][3] = ((min(pix[-4], pix[+4]) + pix[-4] + pix[+4] ) < (min(pix[-u], pix[+u]) + pix[-u] + pix[+u])); + // comparing 4 * a to (b+c+d+e) instead of a to (b+c+d+e)/4 is faster because divisions are slow + if ( 4 * (*pix) > ( (pix[-3] + pix[+3]) + (pix[-u] + pix[+u])) ) { + map[indx] = ((min(pix[-3], pix[+3]) + (pix[-3] + pix[+3]) ) < (min(pix[-u], pix[+u]) + (pix[-u] + pix[+u]))); } else { - image[indx][3] = ((max(pix[-4], pix[+4]) + pix[-4] + pix[+4] ) > (max(pix[-u], pix[+u]) + pix[-u] + pix[+u])); + map[indx] = ((max(pix[-3], pix[+3]) + (pix[-3] + pix[+3]) ) > (max(pix[-u], pix[+u]) + (pix[-u] + pix[+u]))); } } } } // interpolated green pixels are corrected using the map -void RawImageSource::dcb_correction(float (*image)[4], int x0, int y0) +void RawImageSource::dcb_correction(float (*image)[3], uint8_t *map, int x0, int y0) { const int u = CACHESIZE, v = 2 * CACHESIZE; int rowMin, colMin, rowMax, colMax; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 2); for (int row = rowMin; row < rowMax; row++) { - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col; col < colMax; col += 2, indx += 2) { - float current = 4.f * image[indx][3] + - 2.f * (image[indx + u][3] + image[indx - u][3] + image[indx + 1][3] + image[indx - 1][3]) + - image[indx + v][3] + image[indx - v][3] + image[indx + 2][3] + image[indx - 2][3]; + for (int indx = row * CACHESIZE + colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1); indx < row * CACHESIZE + colMax; indx += 2) { +// for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col; col < colMax; col += 2, indx += 2) { + float current = 4 * map[indx] + + 2 * (map[indx + u] + map[indx - u] + map[indx + 1] + map[indx - 1]) + + map[indx + v] + map[indx - v] + map[indx + 2] + map[indx - 2]; assert(indx >= 0 && indx < u * u); - image[indx][1] = ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1]) * 0.5f + current * (image[indx - u][1] + image[indx + u][1]) * 0.5f ) * 0.0625f; + image[indx][1] = ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1]) + current * (image[indx - u][1] + image[indx + u][1]) ) * 0.03125f; +// image[indx][1] = ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1]) * 0.5f + current * (image[indx - u][1] + image[indx + u][1]) * 0.5f ) * 0.0625f; } } } // R and B smoothing using green contrast, all pixels except 2 pixel wide border -void RawImageSource::dcb_pp(float (*image)[4], int x0, int y0) + +// again code with *pix, is this kind of calculating faster in C, than this what was commented? +void RawImageSource::dcb_pp(float (*image)[3], int x0, int y0) { const int u = CACHESIZE; int rowMin, colMin, rowMax, colMax; @@ -3505,10 +3501,10 @@ void RawImageSource::dcb_pp(float (*image)[4], int x0, int y0) for (int row = rowMin; row < rowMax; row++) for (int col = colMin, indx = row * CACHESIZE + col; col < colMax; col++, indx++) { - //int r1 = ( image[indx-1][0] + image[indx+1][0] + image[indx-u][0] + image[indx+u][0] + image[indx-u-1][0] + image[indx+u+1][0] + image[indx-u+1][0] + image[indx+u-1][0])/8; - //int g1 = ( image[indx-1][1] + image[indx+1][1] + image[indx-u][1] + image[indx+u][1] + image[indx-u-1][1] + image[indx+u+1][1] + image[indx-u+1][1] + image[indx+u-1][1])/8; - //int b1 = ( image[indx-1][2] + image[indx+1][2] + image[indx-u][2] + image[indx+u][2] + image[indx-u-1][2] + image[indx+u+1][2] + image[indx-u+1][2] + image[indx+u-1][2])/8; - float (*pix)[4] = image + (indx - u - 1); +// float r1 = image[indx-1][0] + image[indx+1][0] + image[indx-u][0] + image[indx+u][0] + image[indx-u-1][0] + image[indx+u+1][0] + image[indx-u+1][0] + image[indx+u-1][0]; +// float g1 = image[indx-1][1] + image[indx+1][1] + image[indx-u][1] + image[indx+u][1] + image[indx-u-1][1] + image[indx+u+1][1] + image[indx-u+1][1] + image[indx+u-1][1]; +// float b1 = image[indx-1][2] + image[indx+1][2] + image[indx-u][2] + image[indx+u][2] + image[indx-u-1][2] + image[indx+u+1][2] + image[indx-u+1][2] + image[indx+u-1][2]; + float (*pix)[3] = image + (indx - u - 1); float r1 = (*pix)[0]; float g1 = (*pix)[1]; float b1 = (*pix)[2]; @@ -3543,8 +3539,8 @@ void RawImageSource::dcb_pp(float (*image)[4], int x0, int y0) r1 *= 0.125f; g1 *= 0.125f; b1 *= 0.125f; - r1 = r1 + ( image[indx][1] - g1 ); - b1 = b1 + ( image[indx][1] - g1 ); + r1 += ( image[indx][1] - g1 ); + b1 += ( image[indx][1] - g1 ); assert(indx >= 0 && indx < u * u); image[indx][0] = r1; @@ -3554,70 +3550,90 @@ void RawImageSource::dcb_pp(float (*image)[4], int x0, int y0) // interpolated green pixels are corrected using the map // with correction -void RawImageSource::dcb_correction2(float (*image)[4], int x0, int y0) +void RawImageSource::dcb_correction2(float (*image)[3], uint8_t *map, int x0, int y0) { const int u = CACHESIZE, v = 2 * CACHESIZE; int rowMin, colMin, rowMax, colMax; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 4); for (int row = rowMin; row < rowMax; row++) { - for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { - float current = 4.f * image[indx][3] + - 2.f * (image[indx + u][3] + image[indx - u][3] + image[indx + 1][3] + image[indx - 1][3]) + - image[indx + v][3] + image[indx - v][3] + image[indx + 2][3] + image[indx - 2][3]; + for (int indx = row * CACHESIZE + colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1)); indx < row * CACHESIZE + colMax; indx += 2) { + // map values are uint8_t either 0 or 1. Adding them using integer instructions is perfectly valid and fast. Final result is converted to float then + float current = 4 * map[indx] + + 2 * (map[indx + u] + map[indx - u] + map[indx + 1] + map[indx - 1]) + + map[indx + v] + map[indx - v] + map[indx + 2] + map[indx - 2]; assert(indx >= 0 && indx < u * u); + +// Jacek comment: works now, and has 3 float mults and 9 float adds + image[indx][1] = image[indx][c] + + ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1] - (image[indx + 2][c] + image[indx - 2][c])) + + current * (image[indx - u][1] + image[indx + u][1] - (image[indx + v][c] + image[indx - v][c]))) * 0.03125f; + + + // 4 float mults and 9 float adds + // Jacek comment: not mathematically identical to original +/* image[indx][1] = 16.f * image[indx][c] + + ((16.f - current) * ((image[indx - 1][1] + image[indx + 1][1]) + - (image[indx + 2][c] + image[indx - 2][c])) + + current * ((image[indx - u][1] + image[indx + u][1]) - (image[indx + v][c] + image[indx - v][c]))) * 0.03125f; +*/ + // 7 float mults and 10 float adds + // original code +/* image[indx][1] = ((16.f - current) * ((image[indx - 1][1] + image[indx + 1][1]) * 0.5f + image[indx][c] - (image[indx + 2][c] + image[indx - 2][c]) * 0.5f) + current * ((image[indx - u][1] + image[indx + u][1]) * 0.5f + image[indx][c] - (image[indx + v][c] + image[indx - v][c]) * 0.5f)) * 0.0625f; - } +*/ + } } } // image refinement -void RawImageSource::dcb_refinement(float (*image)[4], int x0, int y0) +void RawImageSource::dcb_refinement(float (*image)[3], uint8_t *map, int x0, int y0) { const int u = CACHESIZE, v = 2 * CACHESIZE, w = 3 * CACHESIZE; int rowMin, colMin, rowMax, colMax; dcb_initTileLimits(colMin, rowMin, colMax, rowMax, x0, y0, 4); - float f[5], g1, g2; + float f0, f1, f2, g1, h0, h1, h2, g2, current; for (int row = rowMin; row < rowMax; row++) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { - float current = 4.f * image[indx][3] + - 2.f * (image[indx + u][3] + image[indx - u][3] + image[indx + 1][3] + image[indx - 1][3]) - + image[indx + v][3] + image[indx - v][3] + image[indx - 2][3] + image[indx + 2][3]; - f[0] = (float)(image[indx - u][1] + image[indx + u][1]) / (2.f + 2.f * image[indx][c]); - f[1] = 2.f * image[indx - u][1] / (2 + image[indx - v][c] + image[indx][c]); - f[2] = (float)(image[indx - u][1] + image[indx - w][1]) / (2.f + 2.f * image[indx - v][c]); - f[3] = 2.f * image[indx + u][1] / (2 + image[indx + v][c] + image[indx][c]); - f[4] = (float)(image[indx + u][1] + image[indx + w][1]) / (2.f + 2.f * image[indx + v][c]); + float current = 4 * map[indx] + + 2 * (map[indx + u] + map[indx - u] + map[indx + 1] + map[indx - 1]) + + map[indx + v] + map[indx - v] + map[indx - 2] + map[indx + 2]; - g1 = (f[0] + f[1] + f[2] + f[3] + f[4] - max(f[1], f[2], f[3], f[4]) - min(f[1], f[2], f[3], f[4])) / 3.f; + float currPix = image[indx][c]; - f[0] = (float)(image[indx - 1][1] + image[indx + 1][1]) / (2.f + 2.f * image[indx][c]); - f[1] = 2.f * image[indx - 1][1] / (2 + image[indx - 2][c] + image[indx][c]); - f[2] = (float)(image[indx - 1][1] + image[indx - 3][1]) / (2.f + 2.f * image[indx - 2][c]); - f[3] = 2.f * image[indx + 1][1] / (2 + image[indx + 2][c] + image[indx][c]); - f[4] = (float)(image[indx + 1][1] + image[indx + 3][1]) / (2.f + 2.f * image[indx + 2][c]); + f0 = (float)(image[indx - u][1] + image[indx + u][1]) / (1.f + 2.f * currPix); + f1 = 2.f * image[indx - u][1] / (1.f + image[indx - v][c] + currPix); + f2 = 2.f * image[indx + u][1] / (1.f + image[indx + v][c] + currPix); - g2 = (f[0] + f[1] + f[2] + f[3] + f[4] - max(f[1], f[2], f[3], f[4]) - min(f[1], f[2], f[3], f[4])) / 3.f; + g1 = f0 + f1 + f2; + h0 = (float)(image[indx - 1][1] + image[indx + 1][1]) / (1.f + 2.f * currPix); + h1 = 2.f * image[indx - 1][1] / (1.f + image[indx - 2][c] + currPix); + h2 = 2.f * image[indx + 1][1] / (1.f + image[indx + 2][c] + currPix); + + g2 = h0 + h1 + h2; + + // new green value assert(indx >= 0 && indx < u * u); - image[indx][1] = (2.f + image[indx][c]) * (current * g1 + (16.f - current) * g2) * 0.0625f; + currPix *= (current * g1 + (16.f - current) * g2) / 48.f; - // get rid of the overshooted pixels - float min_f = min(image[indx + 1 + u][1], min(image[indx + 1 - u][1], min(image[indx - 1 + u][1], min(image[indx - 1 - u][1], min(image[indx - 1][1], min(image[indx + 1][1], min(image[indx - u][1], image[indx + u][1]))))))); - float max_f = max(image[indx + 1 + u][1], max(image[indx + 1 - u][1], max(image[indx - 1 + u][1], max(image[indx - 1 - u][1], max(image[indx - 1][1], max(image[indx + 1][1], max(image[indx - u][1], image[indx + u][1]))))))); + // get rid of the overshot pixels + float minVal = min(image[indx - 1][1], min(image[indx + 1][1], min(image[indx - u][1], image[indx + u][1]))); + float maxVal = max(image[indx - 1][1], max(image[indx + 1][1], max(image[indx - u][1], image[indx + u][1]))); + + image[indx][1] = LIM(currPix, minVal, maxVal); - image[indx][1] = LIM(image[indx][1], min_f, max_f); } } -// missing colors are interpolated using high quality algorithm by Luis Sanz Rodriguez -void RawImageSource::dcb_color_full(float (*image)[4], int x0, int y0, float (*chroma)[2]) +// missing colours are interpolated using high quality algorithm by Luis Sanz Rodriguez +void RawImageSource::dcb_color_full(float (*image)[3], int x0, int y0, float (*chroma)[2]) { const int u = CACHESIZE, w = 3 * CACHESIZE; int rowMin, colMin, rowMax, colMax; @@ -3637,10 +3653,15 @@ void RawImageSource::dcb_color_full(float (*image)[4], int x0, int y0, float (*c f[1] = 1.f / (float)(1.f + fabs(chroma[indx - u + 1][c] - chroma[indx + u - 1][c]) + fabs(chroma[indx - u + 1][c] - chroma[indx - w + 3][c]) + fabs(chroma[indx + u - 1][c] - chroma[indx - w + 3][c])); f[2] = 1.f / (float)(1.f + fabs(chroma[indx + u - 1][c] - chroma[indx - u + 1][c]) + fabs(chroma[indx + u - 1][c] - chroma[indx + w + 3][c]) + fabs(chroma[indx - u + 1][c] - chroma[indx + w - 3][c])); f[3] = 1.f / (float)(1.f + fabs(chroma[indx + u + 1][c] - chroma[indx - u - 1][c]) + fabs(chroma[indx + u + 1][c] - chroma[indx + w - 3][c]) + fabs(chroma[indx - u - 1][c] - chroma[indx + w + 3][c])); - g[0] = 1.325f * chroma[indx - u - 1][c] - 0.175f * chroma[indx - w - 3][c] - 0.075f * chroma[indx - w - 1][c] - 0.075f * chroma[indx - u - 3][c]; - g[1] = 1.325f * chroma[indx - u + 1][c] - 0.175f * chroma[indx - w + 3][c] - 0.075f * chroma[indx - w + 1][c] - 0.075f * chroma[indx - u + 3][c]; - g[2] = 1.325f * chroma[indx + u - 1][c] - 0.175f * chroma[indx + w - 3][c] - 0.075f * chroma[indx + w - 1][c] - 0.075f * chroma[indx + u - 3][c]; - g[3] = 1.325f * chroma[indx + u + 1][c] - 0.175f * chroma[indx + w + 3][c] - 0.075f * chroma[indx + w + 1][c] - 0.075f * chroma[indx + u + 3][c]; + g[0] = 1.325f * chroma[indx - u - 1][c] - 0.175f * chroma[indx - w - 3][c] - 0.075f * (chroma[indx - w - 1][c] + chroma[indx - u - 3][c]); + g[1] = 1.325f * chroma[indx - u + 1][c] - 0.175f * chroma[indx - w + 3][c] - 0.075f * (chroma[indx - w + 1][c] + chroma[indx - u + 3][c]); + g[2] = 1.325f * chroma[indx + u - 1][c] - 0.175f * chroma[indx + w - 3][c] - 0.075f * (chroma[indx + w - 1][c] + chroma[indx + u - 3][c]); + g[3] = 1.325f * chroma[indx + u + 1][c] - 0.175f * chroma[indx + w + 3][c] - 0.075f * (chroma[indx + w + 1][c] + chroma[indx + u + 3][c]); + +// g[0] = 1.325f * chroma[indx - u - 1][c] - 0.175f * chroma[indx - w - 3][c] - 0.075f * chroma[indx - w - 1][c] - 0.075f * chroma[indx - u - 3][c]; +// g[1] = 1.325f * chroma[indx - u + 1][c] - 0.175f * chroma[indx - w + 3][c] - 0.075f * chroma[indx - w + 1][c] - 0.075f * chroma[indx - u + 3][c]; +// g[2] = 1.325f * chroma[indx + u - 1][c] - 0.175f * chroma[indx + w - 3][c] - 0.075f * chroma[indx + w - 1][c] - 0.075f * chroma[indx + u - 3][c]; +// g[3] = 1.325f * chroma[indx + u + 1][c] - 0.175f * chroma[indx + w + 3][c] - 0.075f * chroma[indx + w + 1][c] - 0.075f * chroma[indx + u + 3][c]; assert(indx >= 0 && indx < u * u && c >= 0 && c < 2); chroma[indx][c] = (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) / (f[0] + f[1] + f[2] + f[3]); @@ -3649,15 +3670,20 @@ void RawImageSource::dcb_color_full(float (*image)[4], int x0, int y0, float (*c for (int row = rowMin; row < rowMax; row++) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin + 1) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1) / 2; col < colMax; col += 2, indx += 2) for(int d = 0; d <= 1; c = 1 - c, d++) { - f[0] = 1.f / (float)(1.f + fabs(chroma[indx - u][c] - chroma[indx + u][c]) + fabs(chroma[indx - u][c] - chroma[indx - w][c]) + fabs(chroma[indx + u][c] - chroma[indx - w][c])); - f[1] = 1.f / (float)(1.f + fabs(chroma[indx + 1][c] - chroma[indx - 1][c]) + fabs(chroma[indx + 1][c] - chroma[indx + 3][c]) + fabs(chroma[indx - 1][c] - chroma[indx + 3][c])); - f[2] = 1.f / (float)(1.f + fabs(chroma[indx - 1][c] - chroma[indx + 1][c]) + fabs(chroma[indx - 1][c] - chroma[indx - 3][c]) + fabs(chroma[indx + 1][c] - chroma[indx - 3][c])); - f[3] = 1.f / (float)(1.f + fabs(chroma[indx + u][c] - chroma[indx - u][c]) + fabs(chroma[indx + u][c] - chroma[indx + w][c]) + fabs(chroma[indx - u][c] - chroma[indx + w][c])); + f[0] = 1.f / (1.f + fabs(chroma[indx - u][c] - chroma[indx + u][c]) + fabs(chroma[indx - u][c] - chroma[indx - w][c]) + fabs(chroma[indx + u][c] - chroma[indx - w][c])); + f[1] = 1.f / (1.f + fabs(chroma[indx + 1][c] - chroma[indx - 1][c]) + fabs(chroma[indx + 1][c] - chroma[indx + 3][c]) + fabs(chroma[indx - 1][c] - chroma[indx + 3][c])); + f[2] = 1.f / (1.f + fabs(chroma[indx - 1][c] - chroma[indx + 1][c]) + fabs(chroma[indx - 1][c] - chroma[indx - 3][c]) + fabs(chroma[indx + 1][c] - chroma[indx - 3][c])); + f[3] = 1.f / (1.f + fabs(chroma[indx + u][c] - chroma[indx - u][c]) + fabs(chroma[indx + u][c] - chroma[indx + w][c]) + fabs(chroma[indx - u][c] - chroma[indx + w][c])); - g[0] = 0.875f * chroma[indx - u][c] + 0.125f * chroma[indx - w][c]; - g[1] = 0.875f * chroma[indx + 1][c] + 0.125f * chroma[indx + 3][c]; - g[2] = 0.875f * chroma[indx - 1][c] + 0.125f * chroma[indx - 3][c]; - g[3] = 0.875f * chroma[indx + u][c] + 0.125f * chroma[indx + w][c]; + g[0] = intp(0.875f, chroma[indx - u][c], chroma[indx - w][c]); + g[1] = intp(0.875f, chroma[indx + 1][c], chroma[indx + 3][c]); + g[2] = intp(0.875f, chroma[indx - 1][c], chroma[indx - 3][c]); + g[3] = intp(0.875f, chroma[indx + u][c], chroma[indx + w][c]); + +// g[0] = 0.875f * chroma[indx - u][c] + 0.125f * chroma[indx - w][c]; +// g[1] = 0.875f * chroma[indx + 1][c] + 0.125f * chroma[indx + 3][c]; +// g[2] = 0.875f * chroma[indx - 1][c] + 0.125f * chroma[indx - 3][c]; +// g[3] = 0.875f * chroma[indx + u][c] + 0.125f * chroma[indx + w][c]; assert(indx >= 0 && indx < u * u && c >= 0 && c < 2); chroma[indx][c] = (f[0] * g[0] + f[1] * g[1] + f[2] * g[2] + f[3] * g[3]) / (f[0] + f[1] + f[2] + f[3]); @@ -3672,9 +3698,10 @@ void RawImageSource::dcb_color_full(float (*image)[4], int x0, int y0, float (*c } } -// DCB demosaicing main routine (sharp version) +// DCB demosaicing main routine void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) { +BENCHFUN double currentProgress = 0.0; if(plistener) { @@ -3686,29 +3713,24 @@ void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) int hTiles = H / TILESIZE + (H % TILESIZE ? 1 : 0); int numTiles = wTiles * hTiles; int tilesDone = 0; + constexpr int cldf = 2; // factor to multiply cache line distance. 1 = 64 bytes, 2 = 128 bytes ... + #ifdef _OPENMP - int nthreads = omp_get_max_threads(); - float (**image)[4] = (float(**)[4]) calloc( nthreads, sizeof( void*) ); - float (**image2)[3] = (float(**)[3]) calloc( nthreads, sizeof( void*) ); - float (**image3)[3] = (float(**)[3]) calloc( nthreads, sizeof( void*) ); - float (**chroma)[2] = (float (**)[2]) calloc( nthreads, sizeof( void*) ); - - for(int i = 0; i < nthreads; i++) { - image[i] = (float(*)[4]) calloc( CACHESIZE * CACHESIZE, sizeof **image); - image2[i] = (float(*)[3]) calloc( CACHESIZE * CACHESIZE, sizeof **image2); - image3[i] = (float(*)[3]) calloc( CACHESIZE * CACHESIZE, sizeof **image3); - chroma[i] = (float (*)[2]) calloc( CACHESIZE * CACHESIZE, sizeof **chroma); - } - -#else - float (*image)[4] = (float(*)[4]) calloc( CACHESIZE * CACHESIZE, sizeof * image); - float (*image2)[3] = (float(*)[3]) calloc( CACHESIZE * CACHESIZE, sizeof * image2); - float (*image3)[3] = (float(*)[3]) calloc( CACHESIZE * CACHESIZE, sizeof * image3); - float (*chroma)[2] = (float (*)[2]) calloc( CACHESIZE * CACHESIZE, sizeof * chroma); + #pragma omp parallel #endif +{ + // assign working space + char *buffer0 = (char *) malloc(5 * sizeof(float) * CACHESIZE * CACHESIZE + sizeof(uint8_t) * CACHESIZE * CACHESIZE + 3 * cldf * 64 + 63); + // aligned to 64 byte boundary + char *data = (char*)( ( uintptr_t(buffer0) + uintptr_t(63)) / 64 * 64); + + float (*tile)[3] = (float(*)[3]) data; + float (*buffer)[2] = (float(*)[2]) ((char*)tile + sizeof(float) * CACHESIZE * CACHESIZE * 3 + cldf * 64); + float (*chrm)[2] = (float(*)[2]) (buffer); // No overlap in usage of buffer and chrm means we can reuse buffer + uint8_t *map = (uint8_t*) ((char*)buffer + sizeof(float) * CACHESIZE * CACHESIZE * 2 + cldf * 64); #ifdef _OPENMP - #pragma omp parallel for + #pragma omp for schedule(dynamic) nowait #endif for( int iTile = 0; iTile < numTiles; iTile++) { @@ -3717,19 +3739,8 @@ void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) int x0 = xTile * TILESIZE; int y0 = yTile * TILESIZE; -#ifdef _OPENMP - int tid = omp_get_thread_num(); - assert(tid < nthreads); - float (*tile)[4] = image[tid]; - float (*buffer)[3] = image2[tid]; - float (*buffer2)[3] = image3[tid]; - float (*chrm)[2] = chroma[tid]; -#else - float (*tile)[4] = image; - float (*buffer)[3] = image2; - float (*buffer2)[3] = image3; - float (*chrm)[2] = chroma; -#endif + memset(tile, 0, CACHESIZE * CACHESIZE * sizeof * tile); + memset(map, 0, CACHESIZE * CACHESIZE * sizeof * map); fill_raw( tile, x0, y0, rawData ); @@ -3737,7 +3748,44 @@ void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) fill_border(tile, 6, x0, y0); } + copy_to_buffer(buffer, tile); + dcb_hid(tile, x0, y0); + + for (int i = iterations; i > 0; i--) { + dcb_hid2(tile, x0, y0); + dcb_hid2(tile, x0, y0); + dcb_hid2(tile, x0, y0); + dcb_map(tile, map, x0, y0); + dcb_correction(tile, map, x0, y0); + } + + dcb_color(tile, x0, y0); + dcb_pp(tile, x0, y0); + dcb_map(tile, map, x0, y0); + dcb_correction2(tile, map, x0, y0); + dcb_map(tile, map, x0, y0); + dcb_correction(tile, map, x0, y0); + dcb_color(tile, x0, y0); + dcb_map(tile, map, x0, y0); + dcb_correction(tile, map, x0, y0); + dcb_map(tile, map, x0, y0); + dcb_correction(tile, map, x0, y0); + dcb_map(tile, map, x0, y0); + restore_from_buffer(tile, buffer); + + if (!dcb_enhance) + dcb_color(tile, x0, y0); + else + { + memset(chrm, 0, CACHESIZE * CACHESIZE * sizeof * chrm); + dcb_refinement(tile, map, x0, y0); + dcb_color_full(tile, x0, y0, chrm); + } + + /* dcb_hid(tile, buffer, buffer2, x0, y0); + dcb_color(tile, x0, y0); + copy_to_buffer(buffer, tile); for (int i = iterations; i > 0; i--) { @@ -3761,13 +3809,13 @@ void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) dcb_correction(tile, x0, y0); dcb_map(tile, x0, y0); restore_from_buffer(tile, buffer); - dcb_color(tile, x0, y0); + dcb_color_full(tile, x0, y0, chrm); - if (dcb_enhance) { + if (dcb_enhance) { dcb_refinement(tile, x0, y0); dcb_color_full(tile, x0, y0, chrm); - } - + } +*/ for(int y = 0; y < TILESIZE && y0 + y < H; y++) { for (int j = 0; j < TILESIZE && x0 + j < W; j++) { red[y0 + y][x0 + j] = tile[(y + TILEBORDER) * CACHESIZE + TILEBORDER + j][0]; @@ -3792,21 +3840,8 @@ void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) #endif tilesDone++; } - -#ifdef _OPENMP - - for(int i = 0; i < nthreads; i++) { - free(image[i]); - free(image2[i]); - free(image3[i]); - free(chroma[i]); - } - -#endif - free(image); - free(image2); - free(image3); - free(chroma); + free(buffer0); +} if(plistener) { plistener->setProgress (1.0); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 151edf959..9fafef8bb 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -236,19 +236,19 @@ protected: void border_interpolate(unsigned int border, float (*image)[4], unsigned int start = 0, unsigned int end = 0); void border_interpolate2(int winw, int winh, int lborders); void dcb_initTileLimits(int &colMin, int &rowMin, int &colMax, int &rowMax, int x0, int y0, int border); - void fill_raw( float (*cache )[4], int x0, int y0, float** rawData); - void fill_border( float (*cache )[4], int border, int x0, int y0); - void copy_to_buffer(float (*image2)[3], float (*image)[4]); - void dcb_hid(float (*image)[4], float (*bufferH)[3], float (*bufferV)[3], int x0, int y0); - void dcb_color(float (*image)[4], int x0, int y0); - void dcb_hid2(float (*image)[4], int x0, int y0); - void dcb_map(float (*image)[4], int x0, int y0); - void dcb_correction(float (*image)[4], int x0, int y0); - void dcb_pp(float (*image)[4], int x0, int y0); - void dcb_correction2(float (*image)[4], int x0, int y0); - void restore_from_buffer(float (*image)[4], float (*image2)[3]); - void dcb_refinement(float (*image)[4], int x0, int y0); - void dcb_color_full(float (*image)[4], int x0, int y0, float (*chroma)[2]); + void fill_raw( float (*cache )[3], int x0, int y0, float** rawData); + void fill_border( float (*cache )[3], int border, int x0, int y0); + void copy_to_buffer(float (*image2)[2], float (*image)[3]); + void dcb_hid(float (*image)[3], int x0, int y0); + void dcb_color(float (*image)[3], int x0, int y0); + void dcb_hid2(float (*image)[3], int x0, int y0); + void dcb_map(float (*image)[3], uint8_t *map, int x0, int y0); + void dcb_correction(float (*image)[3], uint8_t *map, int x0, int y0); + void dcb_pp(float (*image)[3], int x0, int y0); + void dcb_correction2(float (*image)[3], uint8_t *map, int x0, int y0); + void restore_from_buffer(float (*image)[3], float (*image2)[2]); + void dcb_refinement(float (*image)[3], uint8_t *map, int x0, int y0); + void dcb_color_full(float (*image)[3], int x0, int y0, float (*chroma)[2]); void cielab (const float (*rgb)[3], float* l, float* a, float *b, const int width, const int height, const int labWidth, const float xyz_cam[3][3]); void xtransborder_interpolate (int border); void xtrans_interpolate (const int passes, const bool useCieLab); From 5e5ca6eee4627be4fea173383c6d85c7780dd39b Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 28 Feb 2017 20:05:36 +0100 Subject: [PATCH 119/181] disabled timing code in dcb_demosaic --- rtengine/demosaic_algos.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index 0f0e4841f..1ad3a15df 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -38,7 +38,7 @@ #include "sleef.c" #include "opthelper.h" #include "median.h" -#define BENCHMARK +//#define BENCHMARK #include "StopWatch.h" #ifdef _OPENMP #include @@ -3772,7 +3772,7 @@ BENCHFUN dcb_correction(tile, map, x0, y0); dcb_map(tile, map, x0, y0); restore_from_buffer(tile, buffer); - + if (!dcb_enhance) dcb_color(tile, x0, y0); else From d72d931c9e2b090942deb48fc52c9d1021f6604d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 28 Feb 2017 20:57:19 +0100 Subject: [PATCH 120/181] Variadic template version of `rtengine::(min|max)()` This change allows for an arbitrary number of arguments to `min()` and `max()` by using recursion on variadic template functions. The disassembly of GCC 6.3 was carefully checked for regressions, but nothing was found other than the flipping of arguments (recursion is now `(((a,b),c),d)` and was `(d,(c,(a,b)))` before). I also unified the common type `_Tp` to to the even more common `T`. --- rtengine/rt_math.h | 68 ++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index a7cbbf301..32db8e92f 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -27,71 +27,57 @@ constexpr float RT_PI_F_2 = RT_PI_2; constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN(); -template -inline _Tp SQR (_Tp x) +template +inline T SQR (T x) { // return std::pow(x,2); Slower than: return x * x; } -template -inline const _Tp& min(const _Tp& a, const _Tp& b) +template +inline const T& min(const T& a, const T& b) { return std::min(a, b); } -template -inline const _Tp& max(const _Tp& a, const _Tp& b) +template +inline const T& min(const T& a, const T& b, const ARGS&... args) +{ + return min(min(a, b), args...); +} + +template +inline const T& max(const T& a, const T& b) { return std::max(a, b); } +template +inline const T& max(const T& a, const T& b, const ARGS&... args) +{ + return max(max(a, b), args...); +} -template -inline const _Tp& LIM(const _Tp& a, const _Tp& b, const _Tp& c) +template +inline const T& LIM(const T& a, const T& b, const T& c) { return std::max(b, std::min(a, c)); } -template -inline _Tp LIM01(const _Tp& a) +template +inline T LIM01(const T& a) { - return std::max(_Tp(0), std::min(a, _Tp(1))); + return std::max(T(0), std::min(a, T(1))); } -template -inline _Tp CLIP(const _Tp& a) +template +inline T CLIP(const T& a) { - return LIM(a, static_cast<_Tp>(0), static_cast<_Tp>(MAXVAL)); + return LIM(a, static_cast(0), static_cast(MAXVAL)); } - -template -inline const _Tp& min(const _Tp& a, const _Tp& b, const _Tp& c) -{ - return std::min(c, std::min(a, b)); -} - -template -inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c) -{ - return std::max(c, std::max(a, b)); -} - -template -inline const _Tp& min(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d) -{ - return std::min(d, std::min(c, std::min(a, b))); -} - -template -inline const _Tp& max(const _Tp& a, const _Tp& b, const _Tp& c, const _Tp& d) -{ - return std::max(d, std::max(c, std::max(a, b))); -} - -template -inline _Tp intp(_Tp a, _Tp b, _Tp c) +template +inline T intp(T a, T b, T c) { // calculate a * b + (1 - a) * c // following is valid: From e2b8ccd38b416736a7a2b6c46ce12543e729c8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 28 Feb 2017 21:16:10 +0100 Subject: [PATCH 121/181] Whitespace correction (#3719) --- rtengine/demosaic_algos.cc | 82 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/rtengine/demosaic_algos.cc b/rtengine/demosaic_algos.cc index 1ad3a15df..60abe8998 100644 --- a/rtengine/demosaic_algos.cc +++ b/rtengine/demosaic_algos.cc @@ -3329,8 +3329,8 @@ void RawImageSource::fill_border( float (*cache )[3], int border, int x0, int y0 // saves red and blue -// change buffer[3] -> buffer[2], possibly to buffer[1] if split -// into two loops, one for R and another for B, could also be smaller because +// change buffer[3] -> buffer[2], possibly to buffer[1] if split +// into two loops, one for R and another for B, could also be smaller because // there is no need for green pixels pass // this would decrease the amount of needed memory // from megapixels*2 records to megapixels*0.5 @@ -3369,8 +3369,8 @@ void RawImageSource::dcb_hid(float (*image)[3], int x0, int y0) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx - u - 1 >= 0 && indx + u + 1 < u * u && c >= 0 && c < 3); - image[indx][1] = 0.25*(image[indx-1][1]+image[indx+1][1]+image[indx-u][1]+image[indx+u][1]); - } + image[indx][1] = 0.25*(image[indx-1][1]+image[indx+1][1]+image[indx-u][1]+image[indx+u][1]); + } } // missing colours are interpolated @@ -3385,10 +3385,10 @@ void RawImageSource::dcb_color(float (*image)[3], int x0, int y0) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = 2 - FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx >= 0 && indx < u * u && c >= 0 && c < 4); - -//Jacek comment: one multiplication less - image[indx][c] = image[indx][1] + - ( image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] + +//Jacek comment: one multiplication less + image[indx][c] = image[indx][1] + + ( image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] - (image[indx + u + 1][1] + image[indx + u - 1][1] + image[indx - u + 1][1] + image[indx - u - 1][1]) ) * 0.25f; /* original @@ -3396,23 +3396,23 @@ void RawImageSource::dcb_color(float (*image)[3], int x0, int y0) - image[indx + u + 1][1] - image[indx + u - 1][1] - image[indx - u + 1][1] - image[indx - u - 1][1] + image[indx + u + 1][c] + image[indx + u - 1][c] + image[indx - u + 1][c] + image[indx - u - 1][c] ) * 0.25f; */ - } + } // red or blue in green pixels for (int row = rowMin; row < rowMax; row++) for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin + 1) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col + 1), d = 2 - c; col < colMax; col += 2, indx += 2) { assert(indx >= 0 && indx < u * u && c >= 0 && c < 4); -//Jacek comment: two multiplications (in total) less +//Jacek comment: two multiplications (in total) less image[indx][c] = image[indx][1] + (image[indx + 1][c] + image[indx - 1][c] - (image[indx + 1][1] + image[indx - 1][1])) * 0.5f; image[indx][d] = image[indx][1] + (image[indx + u][d] + image[indx - u][d] - (image[indx + u][1] + image[indx - u][1])) * 0.5f; - - + + /* original image[indx][c] = (2.f * image[indx][1] - image[indx + 1][1] - image[indx - 1][1] + image[indx + 1][c] + image[indx - 1][c]) * 0.5f; image[indx][d] = (2.f * image[indx][1] - image[indx + u][1] - image[indx - u][1] + image[indx + u][d] + image[indx - u][d]) * 0.5f; */ - } + } } // green correction @@ -3425,14 +3425,14 @@ void RawImageSource::dcb_hid2(float (*image)[3], int x0, int y0) for (int row = rowMin; row < rowMax; row++) { for (int col = colMin + (FC(y0 - TILEBORDER + row, x0 - TILEBORDER + colMin) & 1), indx = row * CACHESIZE + col, c = FC(y0 - TILEBORDER + row, x0 - TILEBORDER + col); col < colMax; col += 2, indx += 2) { assert(indx - v >= 0 && indx + v < u * u); - -//Jacek comment: one multiplication less + +//Jacek comment: one multiplication less image[indx][1] = image[indx][c] + - (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1] - - (image[indx + v][c] + image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c])) * 0.25f; + (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1] + - (image[indx + v][c] + image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c])) * 0.25f; /* original - image[indx][1] = (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1]) * 0.25f + + image[indx][1] = (image[indx + v][1] + image[indx - v][1] + image[indx - 2][1] + image[indx + 2][1]) * 0.25f + image[indx][c] - ( image[indx + v][c] + image[indx - v][c] + image[indx - 2][c] + image[indx + 2][c]) * 0.25f; */ } @@ -3446,7 +3446,7 @@ void RawImageSource::dcb_hid2(float (*image)[3], int x0, int y0) // saved in image[][3] // seems at least 2 persons implemented some code, as this one has different coding style, could be unified -// I don't know if *pix is faster than a loop working on image[] directly +// I don't know if *pix is faster than a loop working on image[] directly void RawImageSource::dcb_map(float (*image)[3], uint8_t *map, int x0, int y0) { const int u = 3 * CACHESIZE; @@ -3565,27 +3565,27 @@ void RawImageSource::dcb_correction2(float (*image)[3], uint8_t *map, int x0, in assert(indx >= 0 && indx < u * u); -// Jacek comment: works now, and has 3 float mults and 9 float adds - image[indx][1] = image[indx][c] + - ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1] - (image[indx + 2][c] + image[indx - 2][c])) - + current * (image[indx - u][1] + image[indx + u][1] - (image[indx + v][c] + image[indx - v][c]))) * 0.03125f; - - +// Jacek comment: works now, and has 3 float mults and 9 float adds + image[indx][1] = image[indx][c] + + ((16.f - current) * (image[indx - 1][1] + image[indx + 1][1] - (image[indx + 2][c] + image[indx - 2][c])) + + current * (image[indx - u][1] + image[indx + u][1] - (image[indx + v][c] + image[indx - v][c]))) * 0.03125f; + + // 4 float mults and 9 float adds - // Jacek comment: not mathematically identical to original -/* image[indx][1] = 16.f * image[indx][c] + + // Jacek comment: not mathematically identical to original +/* image[indx][1] = 16.f * image[indx][c] + ((16.f - current) * ((image[indx - 1][1] + image[indx + 1][1]) - (image[indx + 2][c] + image[indx - 2][c])) + current * ((image[indx - u][1] + image[indx + u][1]) - (image[indx + v][c] + image[indx - v][c]))) * 0.03125f; */ // 7 float mults and 10 float adds - // original code -/* + // original code +/* image[indx][1] = ((16.f - current) * ((image[indx - 1][1] + image[indx + 1][1]) * 0.5f + image[indx][c] - (image[indx + 2][c] + image[indx - 2][c]) * 0.5f) + current * ((image[indx - u][1] + image[indx + u][1]) * 0.5f + image[indx][c] - (image[indx + v][c] + image[indx - v][c]) * 0.5f)) * 0.0625f; */ - } + } } } @@ -3618,7 +3618,7 @@ void RawImageSource::dcb_refinement(float (*image)[3], uint8_t *map, int x0, int h2 = 2.f * image[indx + 1][1] / (1.f + image[indx + 2][c] + currPix); g2 = h0 + h1 + h2; - + // new green value assert(indx >= 0 && indx < u * u); currPix *= (current * g1 + (16.f - current) * g2) / 48.f; @@ -3698,7 +3698,7 @@ void RawImageSource::dcb_color_full(float (*image)[3], int x0, int y0, float (*c } } -// DCB demosaicing main routine +// DCB demosaicing main routine void RawImageSource::dcb_demosaic(int iterations, bool dcb_enhance) { BENCHFUN @@ -3750,7 +3750,7 @@ BENCHFUN copy_to_buffer(buffer, tile); dcb_hid(tile, x0, y0); - + for (int i = iterations; i > 0; i--) { dcb_hid2(tile, x0, y0); dcb_hid2(tile, x0, y0); @@ -3773,19 +3773,19 @@ BENCHFUN dcb_map(tile, map, x0, y0); restore_from_buffer(tile, buffer); - if (!dcb_enhance) - dcb_color(tile, x0, y0); - else - { - memset(chrm, 0, CACHESIZE * CACHESIZE * sizeof * chrm); + if (!dcb_enhance) + dcb_color(tile, x0, y0); + else + { + memset(chrm, 0, CACHESIZE * CACHESIZE * sizeof * chrm); dcb_refinement(tile, map, x0, y0); dcb_color_full(tile, x0, y0, chrm); } /* dcb_hid(tile, buffer, buffer2, x0, y0); - dcb_color(tile, x0, y0); - + dcb_color(tile, x0, y0); + copy_to_buffer(buffer, tile); for (int i = iterations; i > 0; i--) { @@ -3811,7 +3811,7 @@ BENCHFUN restore_from_buffer(tile, buffer); dcb_color_full(tile, x0, y0, chrm); - if (dcb_enhance) { + if (dcb_enhance) { dcb_refinement(tile, x0, y0); dcb_color_full(tile, x0, y0, chrm); } From 6e7712831a5e146aa694165cc488a3b7230c669f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Wed, 1 Mar 2017 18:13:16 +0100 Subject: [PATCH 122/181] Break `min()` and `max()` parameter dependencies Also convert most functions in `rt_math.h` to `constexpr` by implementing `min()` and `max()` natively. `constexpr` indeed has a positive impact on the generated assembly. --- rtengine/rt_math.h | 53 ++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 32db8e92f..e1c01a94f 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -27,57 +27,68 @@ constexpr float RT_PI_F_2 = RT_PI_2; constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN(); -template -inline T SQR (T x) +template +constexpr T SQR(T x) { -// return std::pow(x,2); Slower than: return x * x; } template -inline const T& min(const T& a, const T& b) +constexpr const T& min(const T& a) { - return std::min(a, b); + return a; +} + +template +constexpr const T& min(const T& a, const T& b) +{ + return a < b ? a : b; } template -inline const T& min(const T& a, const T& b, const ARGS&... args) +constexpr const T& min(const T& a, const T& b, const ARGS&... args) { - return min(min(a, b), args...); + return min(min(a, b), min(args...)); } template -inline const T& max(const T& a, const T& b) +constexpr const T& max(const T& a) { - return std::max(a, b); + return a; +} + +template +constexpr const T& max(const T& a, const T& b) +{ + return a < b ? b : a; } template -inline const T& max(const T& a, const T& b, const ARGS&... args) +constexpr const T& max(const T& a, const T& b, const ARGS&... args) { - return max(max(a, b), args...); + return max(max(a, b), max(args...)); } template -inline const T& LIM(const T& a, const T& b, const T& c) +constexpr const T& LIM(const T& a, const T& b, const T& c) { - return std::max(b, std::min(a, c)); + return max(b, min(a, c)); } template -inline T LIM01(const T& a) +constexpr T LIM01(const T& a) { - return std::max(T(0), std::min(a, T(1))); + return max(T(0), min(a, T(1))); } template -inline T CLIP(const T& a) +constexpr T CLIP(const T& a) { return LIM(a, static_cast(0), static_cast(MAXVAL)); } template -inline T intp(T a, T b, T c) +constexpr T intp(T a, T b, T c) { // calculate a * b + (1 - a) * c // following is valid: @@ -101,13 +112,13 @@ inline T norm2(const T& x, const T& y) template< typename T > inline T norminf(const T& x, const T& y) { - return std::max(std::abs(x), std::abs(y)); + return max(std::abs(x), std::abs(y)); } -inline int float2uint16range(float d) // clips input to [0;65535] and rounds +constexpr int float2uint16range(float d) { - d = CLIP(d); // clip to [0;65535] - return d + 0.5f; + // clips input to [0;65535] and rounds + return CLIP(d) + 0.5f; } constexpr std::uint8_t uint16ToUint8Rounded(std::uint16_t i) From afda76cf3c06069dac9244451938cc1e90b3166e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 00:41:44 +0100 Subject: [PATCH 123/181] refactoring/cleanup of class for dynamic processing profile rules --- rtgui/dynamicprofile.cc | 231 +++++++++++++++++++++++++--------------- rtgui/dynamicprofile.h | 67 ++++++------ 2 files changed, 179 insertions(+), 119 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index e2cedbb0a..aa3255467 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -23,17 +23,26 @@ using namespace rtengine; using namespace rtengine::procparams; +namespace { + +const int ISO_MAX = 512000; +const double FNUMBER_MAX = 100.0; +const double FOCALLEN_MAX = 10000.0; +const double SHUTTERSPEED_MIN = 1.0/10000.0; +const double SHUTTERSPEED_MAX = 1000.0; +const double EXPCOMP_MIN = -20.0; +const double EXPCOMP_MAX = 20.0; + +} // namespace + + DynamicProfileEntry::DynamicProfileEntry(): serial_number(0), - has_iso(false), iso_min(0), iso_max(1000000), - has_fnumber(false), fnumber_min(0.0), fnumber_max(1000.0), - has_focallen(false), focallen_min(0.0), focallen_max(1000000.0), - has_shutterspeed(false), shutterspeed_min(1000.0), shutterspeed_max(1.0/1000000.0), - has_expcomp(false), expcomp_min(-100.0), expcomp_max(100.0), - has_make(false), make(""), - has_model(false), model(""), - has_lens(false), lens(""), - profilepath("") + iso(0, ISO_MAX), + fnumber(0, FNUMBER_MAX), + focallen(0, FOCALLEN_MAX), + shutterspeed(SHUTTERSPEED_MIN, SHUTTERSPEED_MAX), + expcomp(EXPCOMP_MIN, EXPCOMP_MAX) { } @@ -46,61 +55,102 @@ bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) { - if (has_iso) { - int iso = im->getISOSpeed(); - if (iso < iso_min || iso > iso_max) { - return false; - } - } - if (has_fnumber) { - double fnumber = im->getFNumber(); - if (fnumber < fnumber_min || fnumber > fnumber_max) { - return false; - } - } - if (has_focallen) { - double focallen = im->getFocalLen(); - if (focallen < focallen_min || focallen > focallen_max) { - return false; - } - } - if (has_shutterspeed) { - double shutterspeed = im->getShutterSpeed(); - if (shutterspeed < shutterspeed_min || shutterspeed > shutterspeed_max){ - return false; - } - } - if (has_expcomp) { - double expcomp = im->getExpComp(); - if (expcomp < expcomp_min || expcomp > expcomp_max) { - return false; - } - } - if (has_make) { - if (im->getMake() != make) { - return false; - } - } - if (has_model) { - if (im->getModel() != model) { - return false; - } - } - if (has_lens) { - if (im->getLens() != lens) { - return false; - } - } - return true; + return (iso(im->getISOSpeed()) + && fnumber(im->getFNumber()) + && focallen(im->getFocalLen()) + && shutterspeed(im->getShutterSpeed()) + && expcomp(im->getExpComp()) + && make(im->getMake()) + && model(im->getModel()) + && lens(im->getLens())); } +namespace { + +void get_int_range(DynamicProfileEntry::Range &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + int min = kf.get_integer(group, key + "_min"); + int max = kf.get_integer(group, key + "_max"); + if (min <= max) { + dest.min = min; + dest.max = max; + } + } catch (Glib::KeyFileError &e) { + } +} + + +void get_double_range(DynamicProfileEntry::Range &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + int min = kf.get_double(group, key + "_min"); + int max = kf.get_double(group, key + "_max"); + if (min <= max) { + dest.min = min; + dest.max = max; + } + } catch (Glib::KeyFileError &e) { + } +} + + +void get_optional(DynamicProfileEntry::Optional &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + bool e = kf.get_boolean(group, key + "_enabled"); + if (e) { + Glib::ustring s = kf.get_string(group, key + "_value"); + dest.enabled = e; + dest.value = s; + } + } catch (Glib::KeyFileError &) { + } +} + +void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileEntry::Range &val) +{ + kf.set_integer(group, key + "_min", val.min); + kf.set_integer(group, key + "_max", val.max); +} + +void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileEntry::Range &val) +{ + kf.set_double(group, key + "_min", val.min); + kf.set_double(group, key + "_max", val.max); +} + +void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileEntry::Optional &val) +{ + kf.set_boolean(group, key + "_enabled", val.enabled); + kf.set_string(group, key + "_value", val.value); +} + +} // namespace + bool loadDynamicProfileEntries(std::vector &out) { out.clear(); Glib::KeyFile kf; - if (!kf.load_from_file( - Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + try { + if (!kf.load_from_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + return false; + } + } catch (Glib::Error &e) { return false; } printf("loading dynamic profiles...\n"); @@ -120,42 +170,48 @@ bool loadDynamicProfileEntries(std::vector &out) out.emplace_back(DynamicProfileEntry()); DynamicProfileEntry &entry = out.back(); entry.serial_number = serial; - entry.has_iso = kf.get_boolean(group, "has_iso"); - entry.iso_min = kf.get_integer(group, "iso_min"); - entry.iso_max = kf.get_integer(group, "iso_max"); - - entry.has_fnumber = kf.get_boolean(group, "has_fnumber"); - entry.fnumber_min = kf.get_double(group, "fnumber_min"); - entry.fnumber_max = kf.get_double(group, "fnumber_max"); - - entry.has_focallen = kf.get_boolean(group, "has_focallen"); - entry.focallen_min = kf.get_double(group, "focallen_min"); - entry.focallen_max = kf.get_double(group, "focallen_max"); - - entry.has_shutterspeed = kf.get_boolean(group, "has_shutterspeed"); - entry.shutterspeed_min = kf.get_double(group, "shutterspeed_min"); - entry.shutterspeed_max = kf.get_double(group, "shutterspeed_max"); - - entry.has_expcomp = kf.get_boolean(group, "has_expcomp"); - entry.expcomp_min = kf.get_double(group, "expcomp_min"); - entry.expcomp_max = kf.get_double(group, "expcomp_max"); - - entry.has_make = kf.get_boolean(group, "has_make"); - entry.make = kf.get_string(group, "make"); - - entry.has_model = kf.get_boolean(group, "has_model"); - entry.model = kf.get_string(group, "model"); - - entry.has_lens = kf.get_boolean(group, "has_lens"); - entry.lens = kf.get_string(group, "lens"); - - entry.profilepath = kf.get_string(group, "profilepath"); + get_int_range(entry.iso, kf, group, "iso"); + get_double_range(entry.fnumber, kf, group, "fnumber"); + get_double_range(entry.focallen, kf, group, "focallen"); + get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); + get_double_range(entry.expcomp, kf, group, "expcomp"); + get_optional(entry.make, kf, group, "make"); + get_optional(entry.model, kf, group, "model"); + get_optional(entry.lens, kf, group, "lens"); + try { + entry.profilepath = kf.get_string(group, "profilepath"); + } catch (Glib::KeyFileError &) { + out.pop_back(); + } } std::sort(out.begin(), out.end()); return true; } +bool storeDynamicProfileEntries(const std::vector &entries) +{ + printf("saving dynamic profiles...\n"); + Glib::KeyFile kf; + for (auto &entry : entries) { + std::ostringstream buf; + buf << "entry " << entry.serial_number; + Glib::ustring group = buf.str(); + set_int_range(kf, group, "iso", entry.iso); + set_double_range(kf, group, "fnumber", entry.fnumber); + set_double_range(kf, group, "focallen", entry.focallen); + set_double_range(kf, group, "shutterspeed", entry.shutterspeed); + set_double_range(kf, group, "expcomp", entry.expcomp); + set_optional(kf, group, "make", entry.make); + set_optional(kf, group, "model", entry.model); + set_optional(kf, group, "lens", entry.lens); + kf.set_string(group, "profilepath", entry.profilepath); + } + return kf.save_to_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg")); +} + + PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); @@ -177,4 +233,3 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) } return ret; } - diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index 5d8596c77..a70a0bff7 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -20,52 +20,57 @@ #define _DYNAMICPROFILE_H_ #include - +#include #include "options.h" class DynamicProfileEntry { public: + template + struct Range { + T min; + T max; + explicit Range(T l=T(), T u=T()): min(l), max(u) {} + + bool operator()(T val) const + { + return val >= min && val <= max; + } + }; + + template + struct Optional { + T value; + bool enabled; + explicit Optional(T v=T(), bool e=false): value(v), enabled(e) {} + + bool operator()(const T &val) const + { + return !enabled || value == val; + } + }; + DynamicProfileEntry(); bool matches(const rtengine::ImageMetaData *im); bool operator<(const DynamicProfileEntry &other) const; int serial_number; - - bool has_iso; - int iso_min; - int iso_max; - - bool has_fnumber; - double fnumber_min; - double fnumber_max; - - bool has_focallen; - double focallen_min; - double focallen_max; - - bool has_shutterspeed; - double shutterspeed_min; - double shutterspeed_max; - - bool has_expcomp; - double expcomp_min; - double expcomp_max; - - bool has_make; - std::string make; - - bool has_model; - std::string model; - - bool has_lens; - std::string lens; - + Range iso; + Range fnumber; + Range focallen; + Range shutterspeed; + Range expcomp; + Optional make; + Optional model; + Optional lens; Glib::ustring profilepath; }; bool loadDynamicProfileEntries(std::vector &out); +bool storeDynamicProfileEntries( + const std::vector &entries); + rtengine::procparams::PartialProfile *loadDynamicProfile( const rtengine::ImageMetaData *im); From 7d1bebf3419d97433bd4af5f4c0685d3367666f6 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 00:41:57 +0100 Subject: [PATCH 124/181] added GUI for editing dynamic processing profile rules --- rtgui/CMakeLists.txt | 2 +- rtgui/dynamicprofilepanel.cc | 559 +++++++++++++++++++++++++++++++++++ rtgui/dynamicprofilepanel.h | 149 ++++++++++ rtgui/preferences.cc | 12 + rtgui/preferences.h | 4 + rtgui/profilestore.cc | 1 - 6 files changed, 725 insertions(+), 2 deletions(-) create mode 100644 rtgui/dynamicprofilepanel.cc create mode 100644 rtgui/dynamicprofilepanel.h diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index e327cb4b6..153c1a0ef 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -31,7 +31,7 @@ set (BASESOURCEFILES dirpyrequalizer.cc hsvequalizer.cc defringe.cc popupcommon.cc popupbutton.cc popuptogglebutton.cc sharpenedge.cc sharpenmicro.cc colorappearance.cc filmsimulation.cc prsharpening.cc - dynamicprofile.cc) + dynamicprofile.cc dynamicprofilepanel.cc) include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc new file mode 100644 index 000000000..bfe311efb --- /dev/null +++ b/rtgui/dynamicprofilepanel.cc @@ -0,0 +1,559 @@ +/* -*- C++ -*- + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * 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 . + */ + +#include "dynamicprofilepanel.h" +#include "multilangmgr.h" +#include +#include + +namespace { + +template +Glib::ustring to_str(V n) +{ + std::ostringstream buf; + buf << std::setprecision(1) << std::fixed << n; + return buf.str(); +} + + +int to_int(const Glib::ustring &s) +{ + std::istringstream buf(s); + int r = -1; + buf >> r; + return r; +} + + +double to_double(const Glib::ustring &s) +{ + std::istringstream buf(s); + double r = 0.0; + buf >> r; + return r; +} + +} // namespace + + +//----------------------------------------------------------------------------- +// DynamicProfilePanel::EditDialog +//----------------------------------------------------------------------------- + +DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, + Gtk::Window &parent): + Gtk::Dialog(title, parent) +{ + profilepath_ = Gtk::manage(new ProfileStoreComboBox()); + Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("DYNPROFILEEDITOR_PROFILE"))), + false, false, 4); + hb->pack_start(*profilepath_, true, true, 2); + get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); + + add_optional(M("DYNPROFILEEDITOR_CAMERA_MAKE"), has_make, make); + add_optional(M("DYNPROFILEEDITOR_CAMERA_MODEL"), has_model, model); + add_optional(M("EXIFFILTER_LENS"), has_lens, lens); + + add_range(M("EXIFFILTER_ISO"), iso_min_, iso_max_); + add_range(M("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_); + add_range(M("EXIFFILTER_FOCALLEN"), focallen_min_, focallen_max_); + add_range(M("EXIFFILTER_SHUTTER"), shutterspeed_min_, shutterspeed_max_); + add_range(M("EXIFFILTER_EXPOSURECOMPENSATION"), expcomp_min_, expcomp_max_); + + add_button(M("GENERAL_OK"), 1); + add_button(M("GENERAL_CANCEL"), 2); + + set_ranges(); + + show_all_children(); +} + + +void DynamicProfilePanel::EditDialog::set_entry( + const DynamicProfileEntry &entry) +{ + iso_min_->set_value(entry.iso.min); + iso_max_->set_value(entry.iso.max); + + fnumber_min_->set_value(entry.fnumber.min); + fnumber_max_->set_value(entry.fnumber.max); + + focallen_min_->set_value(entry.focallen.min); + focallen_max_->set_value(entry.focallen.max); + + shutterspeed_min_->set_value(entry.shutterspeed.min); + shutterspeed_max_->set_value(entry.shutterspeed.max); + + expcomp_min_->set_value(entry.expcomp.min); + expcomp_max_->set_value(entry.expcomp.max); + + has_make->set_active(entry.make.enabled); + make->set_text(entry.make.value); + + has_model->set_active(entry.model.enabled); + model->set_text(entry.model.value); + + has_lens->set_active(entry.lens.enabled); + lens->set_text(entry.lens.value); + + profilepath_->updateProfileList(); + if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { + profilepath_->setInternalEntry(); + } +} + + +DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() +{ + DynamicProfileEntry ret; + ret.iso.min = iso_min_->get_value_as_int(); + ret.iso.max = iso_max_->get_value_as_int(); + + ret.fnumber.min = fnumber_min_->get_value(); + ret.fnumber.max = fnumber_max_->get_value(); + + ret.focallen.min = focallen_min_->get_value(); + ret.focallen.max = focallen_max_->get_value(); + + ret.shutterspeed.min = shutterspeed_min_->get_value(); + ret.shutterspeed.max = shutterspeed_max_->get_value(); + + ret.expcomp.min = expcomp_min_->get_value(); + ret.expcomp.max = expcomp_max_->get_value(); + + ret.make.enabled = has_make->get_active(); + ret.make.value = make->get_text(); + + ret.model.enabled = has_model->get_active(); + ret.model.value = model->get_text(); + + ret.lens.enabled = has_lens->get_active(); + ret.lens.value = lens->get_text(); + + ret.profilepath = profilepath_->getFullPathFromActiveRow(); + + return ret; +} + +void DynamicProfilePanel::EditDialog::set_ranges() +{ + DynamicProfileEntry default_entry; + iso_min_->set_digits(0); + iso_max_->set_digits(0); + iso_min_->set_increments(1, 10); + iso_max_->set_increments(1, 10); + iso_min_->set_range(default_entry.iso.min, default_entry.iso.max); + iso_max_->set_range(default_entry.iso.min, default_entry.iso.max); + iso_min_->set_value(default_entry.iso.min); + iso_max_->set_value(default_entry.iso.max); + +#define DOIT_(name) \ + name ## _min_->set_digits(1); \ + name ## _max_->set_digits(1); \ + name ## _min_->set_increments(0.1, 1); \ + name ## _max_->set_increments(0.1, 1); \ + name ## _min_->set_range(default_entry. name .min, \ + default_entry. name .max); \ + name ## _max_->set_range(default_entry. name .min, \ + default_entry. name .max); \ + name ## _min_->set_value(default_entry. name .min); \ + name ## _max_->set_value(default_entry. name .max) + + DOIT_(fnumber); + DOIT_(focallen); + DOIT_(shutterspeed); + DOIT_(expcomp); +#undef DOIT_ + + profilepath_->setInternalEntry(); +} + + +void DynamicProfilePanel::EditDialog::add_range(const Glib::ustring &name, + Gtk::SpinButton *&from, Gtk::SpinButton *&to) +{ + Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*Gtk::manage(new Gtk::Label(name)), false, false, 4); + from = Gtk::manage(new Gtk::SpinButton()); + to = Gtk::manage(new Gtk::SpinButton()); + from->set_numeric(true); + to->set_numeric(true); + hb->pack_start(*from, true, true, 2); + hb->pack_start(*Gtk::manage(new Gtk::Label(" - ")), + false, false, 4); + hb->pack_start(*to, true, true, 2); + get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); +} + + +void DynamicProfilePanel::EditDialog::add_optional(const Glib::ustring &name, + Gtk::CheckButton *&check, Gtk::Entry *&field) +{ + check = Gtk::manage (new Gtk::CheckButton(name)); + Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*check, Gtk::PACK_SHRINK, 4); + field = Gtk::manage(new Gtk::Entry()); + hb->pack_start(*field, true, true, 2); + get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); +} + + +//----------------------------------------------------------------------------- +// DynamicProfilePanel +//----------------------------------------------------------------------------- + +DynamicProfilePanel::DynamicProfilePanel(): + vbox_(Gtk::ORIENTATION_VERTICAL), + button_up_(M("DYNPROFILEEDITOR_MOVE_UP")), + button_down_(M("DYNPROFILEEDITOR_MOVE_DOWN")), + button_new_(M("DYNPROFILEEDITOR_NEW")), + button_edit_(M("DYNPROFILEEDITOR_EDIT")), + button_delete_(M("DYNPROFILEEDITOR_DELETE")) +{ + add(vbox_); + + //Add the TreeView, inside a ScrolledWindow, with the button underneath: + scrolledwindow_.add(treeview_); + + //Only show the scrollbars when they are necessary: + scrolledwindow_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + vbox_.pack_start(scrolledwindow_); + vbox_.pack_start(buttonbox_, Gtk::PACK_SHRINK); + + buttonbox_.pack_start(button_new_, Gtk::PACK_SHRINK); + buttonbox_.pack_start(button_edit_, Gtk::PACK_SHRINK); + buttonbox_.pack_start(button_delete_, Gtk::PACK_SHRINK); + buttonbox_.pack_start(button_up_, Gtk::PACK_SHRINK); + buttonbox_.pack_start(button_down_, Gtk::PACK_SHRINK); + buttonbox_.set_border_width(5); + buttonbox_.set_layout(Gtk::BUTTONBOX_END); + button_up_.signal_clicked().connect( + sigc::mem_fun(*this, &DynamicProfilePanel::on_button_up)); + button_down_.signal_clicked().connect( + sigc::mem_fun(*this, &DynamicProfilePanel::on_button_down)); + button_new_.signal_clicked().connect( + sigc::mem_fun(*this, &DynamicProfilePanel::on_button_new)); + button_edit_.signal_clicked().connect( + sigc::mem_fun(*this, &DynamicProfilePanel::on_button_edit)); + button_delete_.signal_clicked().connect( + sigc::mem_fun(*this, &DynamicProfilePanel::on_button_delete)); + + //Create the Tree model: + treemodel_ = Gtk::ListStore::create(columns_); + treeview_.set_model(treemodel_); + + //Add the TreeView's view columns: + auto cell = Gtk::manage(new Gtk::CellRendererText()); + int cols_count = treeview_.append_column( + M("DYNPROFILEEDITOR_PROFILE"), *cell); + auto col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_profilepath)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column( + M("DYNPROFILEEDITOR_CAMERA_MAKE"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_make)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column( + M("DYNPROFILEEDITOR_CAMERA_MODEL"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_model)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column(M("EXIFFILTER_LENS"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_lens)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column(M("EXIFFILTER_ISO"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_iso)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column(M("EXIFFILTER_APERTURE"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_fnumber)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column(M("EXIFFILTER_FOCALLEN"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_focallen)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column(M("EXIFFILTER_SHUTTER"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_shutterspeed)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column( + M("EXIFFILTER_EXPOSURECOMPENSATION"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_expcomp)); + } + + show_all_children(); + + std::vector entries; + if (loadDynamicProfileEntries(entries)) { + for (auto &e : entries) { + add_entry(e); + } + } +} + + +void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, + const DynamicProfileEntry &entry) +{ + row[columns_.iso] = entry.iso; + row[columns_.fnumber] = entry.fnumber; + row[columns_.focallen] = entry.focallen; + row[columns_.shutterspeed] = entry.shutterspeed; + row[columns_.expcomp] = entry.expcomp; + row[columns_.make] = entry.make; + row[columns_.model] = entry.model; + row[columns_.lens] = entry.lens; + row[columns_.profilepath] = entry.profilepath; +} + +void DynamicProfilePanel::add_entry(const DynamicProfileEntry &entry) +{ + auto row = *(treemodel_->append()); + update_entry(row, entry); +} + + +DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, + int serial) +{ + DynamicProfileEntry ret; + ret.serial_number = serial; + ret.iso = row[columns_.iso]; + ret.fnumber = row[columns_.fnumber]; + ret.focallen = row[columns_.focallen]; + ret.shutterspeed = row[columns_.shutterspeed]; + ret.expcomp = row[columns_.expcomp]; + ret.make = row[columns_.make]; + ret.model = row[columns_.model]; + ret.lens = row[columns_.lens]; + ret.profilepath = row[columns_.profilepath]; + return ret; +} + + +void DynamicProfilePanel::render_profilepath( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast(cell); + auto value = row[columns_.profilepath]; + ct->property_text() = value; +} + + +#define RENDER_RANGE_(tp, name) \ + auto row = *iter; \ + Gtk::CellRendererText *ct = static_cast(cell); \ + DynamicProfileEntry::Range r = row[columns_. name]; \ + auto value = to_str(r.min) + " - " + to_str(r.max); \ + ct->property_text() = value; + +void DynamicProfilePanel::render_iso( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(int, iso); +} + + +void DynamicProfilePanel::render_fnumber( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, fnumber); +} + + +void DynamicProfilePanel::render_focallen( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, focallen); +} + + +void DynamicProfilePanel::render_shutterspeed( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, shutterspeed); +} + + +void DynamicProfilePanel::render_expcomp( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, expcomp); +} + +#undef RENDER_RANGE_ + +#define RENDER_OPTIONAL_(name) \ + auto row = *iter; \ + Gtk::CellRendererText *ct = static_cast(cell); \ + DynamicProfileEntry::Optional o = row[columns_. name]; \ + if (o.enabled) { \ + ct->property_text() = o.value; \ + } else { \ + ct->property_text() = ""; \ + } + +void DynamicProfilePanel::render_make( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_OPTIONAL_(make); +} + + +void DynamicProfilePanel::render_model( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_OPTIONAL_(model); +} + + +void DynamicProfilePanel::render_lens( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_OPTIONAL_(lens); +} + +#undef RENDER_OPTIONAL_ + +void DynamicProfilePanel::on_button_up() +{ + auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + auto it = s->get_selected(); + if (it != treemodel_->children().begin()) { + auto it2 = it; + --it2; + treemodel_->iter_swap(it, it2); + } +} + +void DynamicProfilePanel::on_button_down() +{ + auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + auto it = s->get_selected(); + auto it2 = it; + ++it2; + if (it2 != treemodel_->children().end()) { + treemodel_->iter_swap(it, it2); + } +} + + +void DynamicProfilePanel::on_button_delete() +{ + auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + auto it = s->get_selected(); + treemodel_->erase(it); +} + + +void DynamicProfilePanel::on_button_new() +{ + EditDialog d(M("DYNPROFILEEDITOR_NEW_RULE"), + static_cast(*get_toplevel())); + int status = d.run(); + if (status == 1) { + DynamicProfileEntry entry = d.get_entry(); + add_entry(entry); + } +} + + +void DynamicProfilePanel::on_button_edit() +{ + auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + EditDialog d(M("DYNPROFILEEDITOR_EDIT_RULE"), + static_cast(*get_toplevel())); + auto it = s->get_selected(); + Gtk::TreeModel::Row row = *(s->get_selected()); + d.set_entry(to_entry(row)); + int status = d.run(); + if (status == 1) { + update_entry(row, d.get_entry()); + } +} + + +void DynamicProfilePanel::save() +{ + std::vector entries; + int serial = 1; + for (auto row : treemodel_->children()) { + entries.emplace_back(to_entry(row, serial++)); + } + if (!storeDynamicProfileEntries(entries)) { + printf("Error in saving dynamic profile rules\n"); + } else { + printf("Saved %d dynamic profile rules\n", int(entries.size())); + } +} diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h new file mode 100644 index 000000000..c24f9bfcb --- /dev/null +++ b/rtgui/dynamicprofilepanel.h @@ -0,0 +1,149 @@ +/* -*- C++ -*- + * This file is part of RawTherapee. + * + * Copyright (c) 2017 Alberto Griggio + * + * 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 . + */ +#ifndef _DYNAMICPROFILEPANEL_H_ +#define _DYNAMICPROFILEPANEL_H_ + +#include +#include "dynamicprofile.h" +#include "profilestore.h" + +class DynamicProfilePanel: public Gtk::VBox { +public: + DynamicProfilePanel(); + void save(); + +private: + void update_entry(Gtk::TreeModel::Row row, + const DynamicProfileEntry &entry); + void add_entry(const DynamicProfileEntry &entry); + DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); + + //Signal handlers: + void on_button_quit(); + void on_button_up(); + void on_button_down(); + void on_button_new(); + void on_button_edit(); + void on_button_delete(); + + //Tree model columns: + class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord { + public: + DynamicProfileColumns() + { + add(iso); + add(fnumber); + add(focallen); + add(shutterspeed); + add(expcomp); + add(make); + add(model); + add(lens); + add(profilepath); + } + + Gtk::TreeModelColumn> iso; + Gtk::TreeModelColumn> fnumber; + Gtk::TreeModelColumn> focallen; + Gtk::TreeModelColumn> shutterspeed; + Gtk::TreeModelColumn> expcomp; + Gtk::TreeModelColumn> make; + Gtk::TreeModelColumn> model; + Gtk::TreeModelColumn> lens; + Gtk::TreeModelColumn profilepath; + }; + + // cell renderers + void render_iso(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_fnumber(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_focallen(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_shutterspeed(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_expcomp(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_make(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_model(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_lens(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_profilepath(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + + class EditDialog: public Gtk::Dialog { + public: + EditDialog(const Glib::ustring &title, Gtk::Window &parent); + void set_entry(const DynamicProfileEntry &entry); + DynamicProfileEntry get_entry(); + + private: + void set_ranges(); + void add_range(const Glib::ustring &name, + Gtk::SpinButton *&from, Gtk::SpinButton *&to); + void add_optional(const Glib::ustring &name, + Gtk::CheckButton *&check, Gtk::Entry *&field); + + Gtk::SpinButton *iso_min_; + Gtk::SpinButton *iso_max_; + + Gtk::SpinButton *fnumber_min_; + Gtk::SpinButton *fnumber_max_; + + Gtk::SpinButton *focallen_min_; + Gtk::SpinButton *focallen_max_; + + Gtk::SpinButton *shutterspeed_min_; + Gtk::SpinButton *shutterspeed_max_; + + Gtk::SpinButton *expcomp_min_; + Gtk::SpinButton *expcomp_max_; + + Gtk::CheckButton *has_make; + Gtk::Entry *make; + + Gtk::CheckButton *has_model; + Gtk::Entry *model; + + Gtk::CheckButton *has_lens; + Gtk::Entry *lens; + + ProfileStoreComboBox *profilepath_; + }; + + DynamicProfileColumns columns_; + + //Child widgets: + Gtk::Box vbox_; + + Gtk::ScrolledWindow scrolledwindow_; + Gtk::TreeView treeview_; + Glib::RefPtr treemodel_; + + Gtk::ButtonBox buttonbox_; + Gtk::Button button_up_; + Gtk::Button button_down_; + Gtk::Button button_new_; + Gtk::Button button_edit_; + Gtk::Button button_delete_; +}; + +#endif // _DYNAMICPROFILEPANEL_H_ diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 0de355bd2..e43138473 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -82,6 +82,7 @@ Preferences::Preferences (RTWindow *rtwindow) nb->append_page (*getGeneralPanel(), M("PREFERENCES_TAB_GENERAL")); nb->append_page (*getProcParamsPanel(), M("PREFERENCES_TAB_IMPROC")); + nb->append_page (*getDynProfilePanel(), M("PREFERENCES_TAB_DYNAMICPROFILE")); nb->append_page (*getFileBrowserPanel(), M("PREFERENCES_TAB_BROWSER")); nb->append_page (*getColorManagementPanel(), M("PREFERENCES_TAB_COLORMGR")); nb->append_page (*getBatchProcPanel(), M("PREFERENCES_BATCH_PROCESSING")); @@ -420,6 +421,14 @@ void Preferences::behSetRadioToggled (const Glib::ustring& path) iter->set_value (behavColumns.badd, false); } + +Gtk::Widget *Preferences::getDynProfilePanel() +{ + dynProfilePanel = Gtk::manage(new DynamicProfilePanel()); + return dynProfilePanel; +} + + Gtk::Widget* Preferences::getProcParamsPanel () { @@ -2020,6 +2029,7 @@ void Preferences::okPressed () options.copyFrom (&moptions); options.filterOutParsedExtensions(); Options::save (); + dynProfilePanel->save(); hide (); } @@ -2181,6 +2191,8 @@ void Preferences::updateProfileList() { rprofiles->updateProfileList(); iprofiles->updateProfileList(); + rprofiles->addRow(profileStore.getInternalDynamicPSE()); + iprofiles->addRow(profileStore.getInternalDynamicPSE()); } void Preferences::restoreValue() diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 91951a2df..50f72a957 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -24,6 +24,7 @@ #include "options.h" #include #include "rtwindow.h" +#include "dynamicprofilepanel.h" class Preferences : public Gtk::Dialog, public ProfileStoreListener { @@ -201,6 +202,8 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Gtk::CheckButton* ckbHideTPVScrollbar; Gtk::CheckButton* ckbUseIconNoText; + DynamicProfilePanel *dynProfilePanel; + Glib::ustring storedValueRaw; Glib::ustring storedValueImg; @@ -239,6 +242,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Gtk::Widget* getBatchProcPanel (); Gtk::Widget* getPerformancePanel (); Gtk::Widget* getSoundPanel (); + Gtk::Widget* getDynProfilePanel (); public: explicit Preferences (RTWindow *rtwindow); diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 9f32b735f..31b490ce1 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -627,7 +627,6 @@ void ProfileStoreComboBox::updateProfileList () // special case for the Internal default entry addRow(profileStore.getInternalDefaultPSE()); } - addRow(profileStore.getInternalDynamicPSE()); // releasing the profilestore's entry list mutex profileStore.releaseFileList(); From 6ae57cd556accd50c65ba3037eedbf769dcc208e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 09:11:37 +0100 Subject: [PATCH 125/181] dynamic profile: merge "make" and "model" into "camera", and allow to use regexps --- rtdata/languages/default | 10 ++++++ rtgui/dynamicprofile.cc | 30 +++++++++++++----- rtgui/dynamicprofile.h | 16 ++++------ rtgui/dynamicprofilepanel.cc | 59 +++++++++++------------------------- rtgui/dynamicprofilepanel.h | 27 ++++++----------- 5 files changed, 64 insertions(+), 78 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 5bcfc2892..dbfe533bd 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1078,6 +1078,7 @@ PREFERENCES_TAB_GENERAL;General PREFERENCES_TAB_IMPROC;Image Processing PREFERENCES_TAB_PERFORMANCE;Performance & Quality PREFERENCES_TAB_SOUND;Sounds +PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules PREFERENCES_TIMAX;High PREFERENCES_TINB;Number of tiles PREFERENCES_TISTD;Standard @@ -1105,6 +1106,7 @@ PROFILEPANEL_PCUSTOM;Custom PROFILEPANEL_PFILE;From file PROFILEPANEL_PINTERNAL;Neutral PROFILEPANEL_PLASTSAVED;Last Saved +PROFILEPANEL_PDYNAMIC;Dynamic PROFILEPANEL_SAVEDLGLABEL;Save Processing Parameters... PROFILEPANEL_SAVEPPASTE;Parameters to save PROFILEPANEL_TOOLTIPCOPY;Copy current processing profile to clipboard.\nCtrl-click to select the parameters to copy. @@ -2037,3 +2039,11 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - +DYNPROFILEEDITOR_PROFILE;Processing profile +DYNPROFILEEDITOR_MOVE_UP;Move up +DYNPROFILEEDITOR_MOVE_DOWN;Move down +DYNPROFILEEDITOR_NEW;New +DYNPROFILEEDITOR_EDIT;Edit +DYNPROFILEEDITOR_DELETE;Delete +DYNPROFILEEDITOR_NEW_RULE;New dynamic profile rule +DYNPROFILEEDITOR_EDIT_RULE;Edit dynamic profile rule diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index aa3255467..c15799fc1 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -19,6 +19,7 @@ #include "dynamicprofile.h" #include +#include using namespace rtengine; using namespace rtengine::procparams; @@ -36,6 +37,22 @@ const double EXPCOMP_MAX = 20.0; } // namespace +bool DynamicProfileEntry::Optional::operator()(const Glib::ustring &val) const +{ + if (!enabled) { + return true; + } + if (value.find("re:") == 0) { + // this is a regexp + return Glib::Regex::match_simple(value.substr(3), val, + Glib::REGEX_CASELESS); + } else { + // normal string comparison + return value.casefold() == val.casefold(); + } +} + + DynamicProfileEntry::DynamicProfileEntry(): serial_number(0), iso(0, ISO_MAX), @@ -60,8 +77,7 @@ bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) && focallen(im->getFocalLen()) && shutterspeed(im->getShutterSpeed()) && expcomp(im->getExpComp()) - && make(im->getMake()) - && model(im->getModel()) + && camera(im->getCamera()) && lens(im->getLens())); } @@ -99,7 +115,7 @@ void get_double_range(DynamicProfileEntry::Range &dest, } -void get_optional(DynamicProfileEntry::Optional &dest, +void get_optional(DynamicProfileEntry::Optional &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -132,7 +148,7 @@ void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Optional &val) + const DynamicProfileEntry::Optional &val) { kf.set_boolean(group, key + "_enabled", val.enabled); kf.set_string(group, key + "_value", val.value); @@ -175,8 +191,7 @@ bool loadDynamicProfileEntries(std::vector &out) get_double_range(entry.focallen, kf, group, "focallen"); get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); get_double_range(entry.expcomp, kf, group, "expcomp"); - get_optional(entry.make, kf, group, "make"); - get_optional(entry.model, kf, group, "model"); + get_optional(entry.camera, kf, group, "camera"); get_optional(entry.lens, kf, group, "lens"); try { entry.profilepath = kf.get_string(group, "profilepath"); @@ -202,8 +217,7 @@ bool storeDynamicProfileEntries(const std::vector &entries) set_double_range(kf, group, "focallen", entry.focallen); set_double_range(kf, group, "shutterspeed", entry.shutterspeed); set_double_range(kf, group, "expcomp", entry.expcomp); - set_optional(kf, group, "make", entry.make); - set_optional(kf, group, "model", entry.model); + set_optional(kf, group, "camera", entry.camera); set_optional(kf, group, "lens", entry.lens); kf.set_string(group, "profilepath", entry.profilepath); } diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index a70a0bff7..0847cd8ef 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -38,16 +38,13 @@ public: } }; - template struct Optional { - T value; + Glib::ustring value; bool enabled; - explicit Optional(T v=T(), bool e=false): value(v), enabled(e) {} + explicit Optional(const Glib::ustring v="", bool e=false): + value(v), enabled(e) {} - bool operator()(const T &val) const - { - return !enabled || value == val; - } + bool operator()(const Glib::ustring &val) const; }; DynamicProfileEntry(); @@ -60,9 +57,8 @@ public: Range focallen; Range shutterspeed; Range expcomp; - Optional make; - Optional model; - Optional lens; + Optional camera; + Optional lens; Glib::ustring profilepath; }; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index bfe311efb..3307cbcf3 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -68,9 +68,8 @@ DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, hb->pack_start(*profilepath_, true, true, 2); get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); - add_optional(M("DYNPROFILEEDITOR_CAMERA_MAKE"), has_make, make); - add_optional(M("DYNPROFILEEDITOR_CAMERA_MODEL"), has_model, model); - add_optional(M("EXIFFILTER_LENS"), has_lens, lens); + add_optional(M("EXIFFILTER_CAMERA"), has_camera_, camera_); + add_optional(M("EXIFFILTER_LENS"), has_lens_, lens_); add_range(M("EXIFFILTER_ISO"), iso_min_, iso_max_); add_range(M("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_); @@ -105,14 +104,11 @@ void DynamicProfilePanel::EditDialog::set_entry( expcomp_min_->set_value(entry.expcomp.min); expcomp_max_->set_value(entry.expcomp.max); - has_make->set_active(entry.make.enabled); - make->set_text(entry.make.value); + has_camera_->set_active(entry.camera.enabled); + camera_->set_text(entry.camera.value); - has_model->set_active(entry.model.enabled); - model->set_text(entry.model.value); - - has_lens->set_active(entry.lens.enabled); - lens->set_text(entry.lens.value); + has_lens_->set_active(entry.lens.enabled); + lens_->set_text(entry.lens.value); profilepath_->updateProfileList(); if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { @@ -139,14 +135,11 @@ DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() ret.expcomp.min = expcomp_min_->get_value(); ret.expcomp.max = expcomp_max_->get_value(); - ret.make.enabled = has_make->get_active(); - ret.make.value = make->get_text(); + ret.camera.enabled = has_camera_->get_active(); + ret.camera.value = camera_->get_text(); - ret.model.enabled = has_model->get_active(); - ret.model.value = model->get_text(); - - ret.lens.enabled = has_lens->get_active(); - ret.lens.value = lens->get_text(); + ret.lens.enabled = has_lens_->get_active(); + ret.lens.value = lens_->get_text(); ret.profilepath = profilepath_->getFullPathFromActiveRow(); @@ -273,21 +266,12 @@ DynamicProfilePanel::DynamicProfilePanel(): } cell = Gtk::manage(new Gtk::CellRendererText()); cols_count = treeview_.append_column( - M("DYNPROFILEEDITOR_CAMERA_MAKE"), *cell); + M("EXIFFILTER_CAMERA"), *cell); col = treeview_.get_column(cols_count - 1); if (col) { col->set_cell_data_func( *cell, sigc::mem_fun( - *this, &DynamicProfilePanel::render_make)); - } - cell = Gtk::manage(new Gtk::CellRendererText()); - cols_count = treeview_.append_column( - M("DYNPROFILEEDITOR_CAMERA_MODEL"), *cell); - col = treeview_.get_column(cols_count - 1); - if (col) { - col->set_cell_data_func( - *cell, sigc::mem_fun( - *this, &DynamicProfilePanel::render_model)); + *this, &DynamicProfilePanel::render_camera)); } cell = Gtk::manage(new Gtk::CellRendererText()); cols_count = treeview_.append_column(M("EXIFFILTER_LENS"), *cell); @@ -358,8 +342,7 @@ void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, row[columns_.focallen] = entry.focallen; row[columns_.shutterspeed] = entry.shutterspeed; row[columns_.expcomp] = entry.expcomp; - row[columns_.make] = entry.make; - row[columns_.model] = entry.model; + row[columns_.camera] = entry.camera; row[columns_.lens] = entry.lens; row[columns_.profilepath] = entry.profilepath; } @@ -381,8 +364,7 @@ DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, ret.focallen = row[columns_.focallen]; ret.shutterspeed = row[columns_.shutterspeed]; ret.expcomp = row[columns_.expcomp]; - ret.make = row[columns_.make]; - ret.model = row[columns_.model]; + ret.camera = row[columns_.camera]; ret.lens = row[columns_.lens]; ret.profilepath = row[columns_.profilepath]; return ret; @@ -445,24 +427,17 @@ void DynamicProfilePanel::render_expcomp( #define RENDER_OPTIONAL_(name) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Optional o = row[columns_. name]; \ + DynamicProfileEntry::Optional o = row[columns_. name]; \ if (o.enabled) { \ ct->property_text() = o.value; \ } else { \ ct->property_text() = ""; \ } -void DynamicProfilePanel::render_make( +void DynamicProfilePanel::render_camera( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_OPTIONAL_(make); -} - - -void DynamicProfilePanel::render_model( - Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) -{ - RENDER_OPTIONAL_(model); + RENDER_OPTIONAL_(camera); } diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h index c24f9bfcb..12b68fadc 100644 --- a/rtgui/dynamicprofilepanel.h +++ b/rtgui/dynamicprofilepanel.h @@ -34,7 +34,6 @@ private: void add_entry(const DynamicProfileEntry &entry); DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); - //Signal handlers: void on_button_quit(); void on_button_up(); void on_button_down(); @@ -42,7 +41,6 @@ private: void on_button_edit(); void on_button_delete(); - //Tree model columns: class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord { public: DynamicProfileColumns() @@ -52,8 +50,7 @@ private: add(focallen); add(shutterspeed); add(expcomp); - add(make); - add(model); + add(camera); add(lens); add(profilepath); } @@ -63,9 +60,8 @@ private: Gtk::TreeModelColumn> focallen; Gtk::TreeModelColumn> shutterspeed; Gtk::TreeModelColumn> expcomp; - Gtk::TreeModelColumn> make; - Gtk::TreeModelColumn> model; - Gtk::TreeModelColumn> lens; + Gtk::TreeModelColumn camera; + Gtk::TreeModelColumn lens; Gtk::TreeModelColumn profilepath; }; @@ -80,10 +76,8 @@ private: const Gtk::TreeModel::iterator& iter); void render_expcomp(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); - void render_make(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_model(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); + void render_camera(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); void render_lens(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); void render_profilepath(Gtk::CellRenderer* cell, @@ -117,14 +111,11 @@ private: Gtk::SpinButton *expcomp_min_; Gtk::SpinButton *expcomp_max_; - Gtk::CheckButton *has_make; - Gtk::Entry *make; - - Gtk::CheckButton *has_model; - Gtk::Entry *model; + Gtk::CheckButton *has_camera_; + Gtk::Entry *camera_; - Gtk::CheckButton *has_lens; - Gtk::Entry *lens; + Gtk::CheckButton *has_lens_; + Gtk::Entry *lens_; ProfileStoreComboBox *profilepath_; }; From eeb2da2613fab4798ac8df7762c32726d4545fed Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 10:00:19 +0100 Subject: [PATCH 126/181] fixed bugs in loading dynamic profiles --- rtgui/dynamicprofile.cc | 9 +++++---- rtgui/dynamicprofilepanel.cc | 8 +++++++- rtgui/preferences.cc | 2 ++ rtgui/thumbnail.cc | 8 ++++++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index c15799fc1..0f09e8a5f 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -18,6 +18,7 @@ */ #include "dynamicprofile.h" +#include "profilestore.h" #include #include @@ -235,13 +236,13 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) if (entry.matches(im)) { printf("found matching profile %s\n", entry.profilepath.c_str()); - PartialProfile p(true, true); - if (!p.load(options.findProfilePath(entry.profilepath))) { - p.applyTo(ret->pparams); + const PartialProfile *p = + profileStore.getProfile(entry.profilepath); + if (p != nullptr) { + p->applyTo(ret->pparams); } else { printf("ERROR loading matching profile\n"); } - p.deleteInstance(); } } } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 3307cbcf3..61614b90c 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -19,6 +19,7 @@ #include "dynamicprofilepanel.h" #include "multilangmgr.h" +#include "profilestore.h" #include #include @@ -377,7 +378,12 @@ void DynamicProfilePanel::render_profilepath( auto row = *iter; Gtk::CellRendererText *ct = static_cast(cell); auto value = row[columns_.profilepath]; - ct->property_text() = value; + auto pse = profileStore.findEntryFromFullPath(value); + if (pse != nullptr) { + ct->property_text() = pse->label; + } else { + ct->property_text() = value; + } } diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index e43138473..ed613c61f 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -438,11 +438,13 @@ Gtk::Widget* Preferences::getProcParamsPanel () Gtk::VBox* vbpp = Gtk::manage (new Gtk::VBox ()); Gtk::Label* drlab = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORRAW") + ":", Gtk::ALIGN_START)); rprofiles = Gtk::manage (new ProfileStoreComboBox ()); + rprofiles->addRow(profileStore.getInternalDynamicPSE()); setExpandAlignProperties(rprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); rprofiles->set_size_request(50, -1); rpconn = rprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forRAWComboChanged) ); Gtk::Label* drimg = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORIMAGE") + ":", Gtk::ALIGN_START)); iprofiles = Gtk::manage (new ProfileStoreComboBox ()); + iprofiles->addRow(profileStore.getInternalDynamicPSE()); iprofiles->set_size_request(50, -1); setExpandAlignProperties(iprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); ipconn = iprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forImageComboChanged) ); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index b560c5f7b..60da23d01 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -227,13 +227,17 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); } PartialProfile *pp = loadDynamicProfile(imageMetaData); + int err = 0; if (options.paramsLoadLocation == PLL_Input) { - pp->pparams->save(fname + paramFileExtension); + err = pp->pparams->save(fname + paramFileExtension); } else { - pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); + err = pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); } pp->deleteInstance(); delete pp; + if (!err) { + loadProcParams(); + } } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; From 01eb437b9002ea0831eaacd4ae39239ce887fd8f Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 11:00:32 +0100 Subject: [PATCH 127/181] some tweaks to the GUI of the dynamic profile editor --- rtdata/languages/default | 11 ++++++----- rtgui/dynamicprofilepanel.cc | 19 +------------------ 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index dbfe533bd..31b14aa2f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2039,11 +2039,12 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - -DYNPROFILEEDITOR_PROFILE;Processing profile -DYNPROFILEEDITOR_MOVE_UP;Move up -DYNPROFILEEDITOR_MOVE_DOWN;Move down +DYNPROFILEEDITOR_PROFILE;Processing Profile +DYNPROFILEEDITOR_MOVE_UP;Move Up +DYNPROFILEEDITOR_MOVE_DOWN;Move Down DYNPROFILEEDITOR_NEW;New DYNPROFILEEDITOR_EDIT;Edit DYNPROFILEEDITOR_DELETE;Delete -DYNPROFILEEDITOR_NEW_RULE;New dynamic profile rule -DYNPROFILEEDITOR_EDIT_RULE;Edit dynamic profile rule +DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 61614b90c..1340e0b31 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -33,24 +33,6 @@ Glib::ustring to_str(V n) return buf.str(); } - -int to_int(const Glib::ustring &s) -{ - std::istringstream buf(s); - int r = -1; - buf >> r; - return r; -} - - -double to_double(const Glib::ustring &s) -{ - std::istringstream buf(s); - double r = 0.0; - buf >> r; - return r; -} - } // namespace @@ -207,6 +189,7 @@ void DynamicProfilePanel::EditDialog::add_optional(const Glib::ustring &name, field = Gtk::manage(new Gtk::Entry()); hb->pack_start(*field, true, true, 2); get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); + field->set_tooltip_text(M("DYNPROFILEEDITOR_ENTRY_TOOLTIP")); } From f67d903550d4493972a4581bcb40380816821261 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 12:38:34 +0100 Subject: [PATCH 128/181] some tweaks to the UI of the dynamic profile editor --- rtgui/dynamicprofilepanel.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 1340e0b31..843b8fd72 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -207,10 +207,9 @@ DynamicProfilePanel::DynamicProfilePanel(): { add(vbox_); - //Add the TreeView, inside a ScrolledWindow, with the button underneath: + treeview_.set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL); scrolledwindow_.add(treeview_); - //Only show the scrollbars when they are necessary: scrolledwindow_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); vbox_.pack_start(scrolledwindow_); @@ -234,11 +233,9 @@ DynamicProfilePanel::DynamicProfilePanel(): button_delete_.signal_clicked().connect( sigc::mem_fun(*this, &DynamicProfilePanel::on_button_delete)); - //Create the Tree model: treemodel_ = Gtk::ListStore::create(columns_); treeview_.set_model(treemodel_); - //Add the TreeView's view columns: auto cell = Gtk::manage(new Gtk::CellRendererText()); int cols_count = treeview_.append_column( M("DYNPROFILEEDITOR_PROFILE"), *cell); @@ -374,8 +371,13 @@ void DynamicProfilePanel::render_profilepath( auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ DynamicProfileEntry::Range r = row[columns_. name]; \ - auto value = to_str(r.min) + " - " + to_str(r.max); \ - ct->property_text() = value; + DynamicProfileEntry dflt; \ + if (r.min > dflt.name.min || r.max < dflt.name.max) { \ + auto value = to_str(r.min) + " - " + to_str(r.max); \ + ct->property_text() = value; \ + } else { \ + ct->property_text() = ""; \ + } void DynamicProfilePanel::render_iso( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) From b3f75398c5723a86343a9fc26074b3ca50a2c6cf Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 16:27:13 +0100 Subject: [PATCH 129/181] increased the number of decimals for shutterspeed in the dynamic profile editor --- rtgui/dynamicprofile.cc | 3 +-- rtgui/dynamicprofilepanel.cc | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 0f09e8a5f..af829b024 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -30,7 +30,6 @@ namespace { const int ISO_MAX = 512000; const double FNUMBER_MAX = 100.0; const double FOCALLEN_MAX = 10000.0; -const double SHUTTERSPEED_MIN = 1.0/10000.0; const double SHUTTERSPEED_MAX = 1000.0; const double EXPCOMP_MIN = -20.0; const double EXPCOMP_MAX = 20.0; @@ -59,7 +58,7 @@ DynamicProfileEntry::DynamicProfileEntry(): iso(0, ISO_MAX), fnumber(0, FNUMBER_MAX), focallen(0, FOCALLEN_MAX), - shutterspeed(SHUTTERSPEED_MIN, SHUTTERSPEED_MAX), + shutterspeed(0, SHUTTERSPEED_MAX), expcomp(EXPCOMP_MIN, EXPCOMP_MAX) { } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 843b8fd72..1f3059a2c 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -158,6 +158,8 @@ void DynamicProfilePanel::EditDialog::set_ranges() DOIT_(shutterspeed); DOIT_(expcomp); #undef DOIT_ + shutterspeed_min_->set_digits(4); + shutterspeed_max_->set_digits(4); profilepath_->setInternalEntry(); } From 0720659627bc453a0fb9ad9adf59e872321a490b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 13:48:19 +0100 Subject: [PATCH 130/181] fixed reading and displaying of floating-point values in dynamic profile rules --- rtgui/dynamicprofile.cc | 4 ++-- rtgui/dynamicprofilepanel.cc | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index af829b024..749c9b02e 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -104,8 +104,8 @@ void get_double_range(DynamicProfileEntry::Range &dest, const Glib::ustring &key) { try { - int min = kf.get_double(group, key + "_min"); - int max = kf.get_double(group, key + "_max"); + double min = kf.get_double(group, key + "_min"); + double max = kf.get_double(group, key + "_max"); if (min <= max) { dest.min = min; dest.max = max; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 1f3059a2c..0ac82f7ae 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -26,10 +26,10 @@ namespace { template -Glib::ustring to_str(V n) +Glib::ustring to_str(V n, int precision=1) { std::ostringstream buf; - buf << std::setprecision(1) << std::fixed << n; + buf << std::setprecision(precision) << std::fixed << n; return buf.str(); } @@ -369,13 +369,13 @@ void DynamicProfilePanel::render_profilepath( } -#define RENDER_RANGE_(tp, name) \ +#define RENDER_RANGE_(tp, name, prec) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ DynamicProfileEntry::Range r = row[columns_. name]; \ DynamicProfileEntry dflt; \ if (r.min > dflt.name.min || r.max < dflt.name.max) { \ - auto value = to_str(r.min) + " - " + to_str(r.max); \ + auto value = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ ct->property_text() = value; \ } else { \ ct->property_text() = ""; \ @@ -384,35 +384,35 @@ void DynamicProfilePanel::render_profilepath( void DynamicProfilePanel::render_iso( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(int, iso); + RENDER_RANGE_(int, iso, 0); } void DynamicProfilePanel::render_fnumber( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, fnumber); + RENDER_RANGE_(double, fnumber, 1); } void DynamicProfilePanel::render_focallen( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, focallen); + RENDER_RANGE_(double, focallen, 1); } void DynamicProfilePanel::render_shutterspeed( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, shutterspeed); + RENDER_RANGE_(double, shutterspeed, 4); } void DynamicProfilePanel::render_expcomp( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, expcomp); + RENDER_RANGE_(double, expcomp, 1); } #undef RENDER_RANGE_ From bc5a6fc7c350c9b0a7c5e4b4b3baf3a8d4c7519d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 14:22:52 +0100 Subject: [PATCH 131/181] renamed DynamicProfileEntry to DynamicProfileRule --- rtgui/dynamicprofile.cc | 86 ++++++++++++------------- rtgui/dynamicprofile.h | 12 ++-- rtgui/dynamicprofilepanel.cc | 118 +++++++++++++++++------------------ rtgui/dynamicprofilepanel.h | 26 ++++---- 4 files changed, 121 insertions(+), 121 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 749c9b02e..d1a5a0177 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -37,7 +37,7 @@ const double EXPCOMP_MAX = 20.0; } // namespace -bool DynamicProfileEntry::Optional::operator()(const Glib::ustring &val) const +bool DynamicProfileRule::Optional::operator()(const Glib::ustring &val) const { if (!enabled) { return true; @@ -53,7 +53,7 @@ bool DynamicProfileEntry::Optional::operator()(const Glib::ustring &val) const } -DynamicProfileEntry::DynamicProfileEntry(): +DynamicProfileRule::DynamicProfileRule(): serial_number(0), iso(0, ISO_MAX), fnumber(0, FNUMBER_MAX), @@ -64,13 +64,13 @@ DynamicProfileEntry::DynamicProfileEntry(): } -bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const +bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const { return serial_number < other.serial_number; } -bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) +bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) { return (iso(im->getISOSpeed()) && fnumber(im->getFNumber()) @@ -83,7 +83,7 @@ bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) namespace { -void get_int_range(DynamicProfileEntry::Range &dest, +void get_int_range(DynamicProfileRule::Range &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -99,7 +99,7 @@ void get_int_range(DynamicProfileEntry::Range &dest, } -void get_double_range(DynamicProfileEntry::Range &dest, +void get_double_range(DynamicProfileRule::Range &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -115,7 +115,7 @@ void get_double_range(DynamicProfileEntry::Range &dest, } -void get_optional(DynamicProfileEntry::Optional &dest, +void get_optional(DynamicProfileRule::Optional &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -132,7 +132,7 @@ void get_optional(DynamicProfileEntry::Optional &dest, void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Range &val) + const DynamicProfileRule::Range &val) { kf.set_integer(group, key + "_min", val.min); kf.set_integer(group, key + "_max", val.max); @@ -140,7 +140,7 @@ void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Range &val) + const DynamicProfileRule::Range &val) { kf.set_double(group, key + "_min", val.min); kf.set_double(group, key + "_max", val.max); @@ -148,7 +148,7 @@ void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Optional &val) + const DynamicProfileRule::Optional &val) { kf.set_boolean(group, key + "_enabled", val.enabled); kf.set_string(group, key + "_value", val.value); @@ -157,7 +157,7 @@ void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, } // namespace -bool loadDynamicProfileEntries(std::vector &out) +bool loadDynamicProfileRules(std::vector &out) { out.clear(); Glib::KeyFile kf; @@ -172,29 +172,29 @@ bool loadDynamicProfileEntries(std::vector &out) printf("loading dynamic profiles...\n"); auto groups = kf.get_groups(); for (auto group : groups) { - // groups are of the form "entry N", where N is a positive integer - if (group.find("entry ") != 0) { + // groups are of the form "rule N", where N is a positive integer + if (group.find("rule ") != 0) { return false; } - std::istringstream buf(group.c_str() + 6); + std::istringstream buf(group.c_str() + 5); int serial = 0; if (!(buf >> serial) || !buf.eof()) { return false; } - printf(" loading entry %d\n", serial); + printf(" loading rule %d\n", serial); - out.emplace_back(DynamicProfileEntry()); - DynamicProfileEntry &entry = out.back(); - entry.serial_number = serial; - get_int_range(entry.iso, kf, group, "iso"); - get_double_range(entry.fnumber, kf, group, "fnumber"); - get_double_range(entry.focallen, kf, group, "focallen"); - get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); - get_double_range(entry.expcomp, kf, group, "expcomp"); - get_optional(entry.camera, kf, group, "camera"); - get_optional(entry.lens, kf, group, "lens"); + out.emplace_back(DynamicProfileRule()); + DynamicProfileRule &rule = out.back(); + rule.serial_number = serial; + get_int_range(rule.iso, kf, group, "iso"); + get_double_range(rule.fnumber, kf, group, "fnumber"); + get_double_range(rule.focallen, kf, group, "focallen"); + get_double_range(rule.shutterspeed, kf, group, "shutterspeed"); + get_double_range(rule.expcomp, kf, group, "expcomp"); + get_optional(rule.camera, kf, group, "camera"); + get_optional(rule.lens, kf, group, "lens"); try { - entry.profilepath = kf.get_string(group, "profilepath"); + rule.profilepath = kf.get_string(group, "profilepath"); } catch (Glib::KeyFileError &) { out.pop_back(); } @@ -204,22 +204,22 @@ bool loadDynamicProfileEntries(std::vector &out) } -bool storeDynamicProfileEntries(const std::vector &entries) +bool storeDynamicProfileRules(const std::vector &rules) { printf("saving dynamic profiles...\n"); Glib::KeyFile kf; - for (auto &entry : entries) { + for (auto &rule : rules) { std::ostringstream buf; - buf << "entry " << entry.serial_number; + buf << "rule " << rule.serial_number; Glib::ustring group = buf.str(); - set_int_range(kf, group, "iso", entry.iso); - set_double_range(kf, group, "fnumber", entry.fnumber); - set_double_range(kf, group, "focallen", entry.focallen); - set_double_range(kf, group, "shutterspeed", entry.shutterspeed); - set_double_range(kf, group, "expcomp", entry.expcomp); - set_optional(kf, group, "camera", entry.camera); - set_optional(kf, group, "lens", entry.lens); - kf.set_string(group, "profilepath", entry.profilepath); + set_int_range(kf, group, "iso", rule.iso); + set_double_range(kf, group, "fnumber", rule.fnumber); + set_double_range(kf, group, "focallen", rule.focallen); + set_double_range(kf, group, "shutterspeed", rule.shutterspeed); + set_double_range(kf, group, "expcomp", rule.expcomp); + set_optional(kf, group, "camera", rule.camera); + set_optional(kf, group, "lens", rule.lens); + kf.set_string(group, "profilepath", rule.profilepath); } return kf.save_to_file( Glib::build_filename(Options::rtdir, "dynamicprofile.cfg")); @@ -229,14 +229,14 @@ bool storeDynamicProfileEntries(const std::vector &entries) PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); - std::vector entries; - if (loadDynamicProfileEntries(entries)) { - for (auto &entry : entries) { - if (entry.matches(im)) { + std::vector rules; + if (loadDynamicProfileRules(rules)) { + for (auto &rule : rules) { + if (rule.matches(im)) { printf("found matching profile %s\n", - entry.profilepath.c_str()); + rule.profilepath.c_str()); const PartialProfile *p = - profileStore.getProfile(entry.profilepath); + profileStore.getProfile(rule.profilepath); if (p != nullptr) { p->applyTo(ret->pparams); } else { diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index 0847cd8ef..c662a51f7 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -24,7 +24,7 @@ #include "options.h" -class DynamicProfileEntry { +class DynamicProfileRule { public: template struct Range { @@ -47,9 +47,9 @@ public: bool operator()(const Glib::ustring &val) const; }; - DynamicProfileEntry(); + DynamicProfileRule(); bool matches(const rtengine::ImageMetaData *im); - bool operator<(const DynamicProfileEntry &other) const; + bool operator<(const DynamicProfileRule &other) const; int serial_number; Range iso; @@ -63,9 +63,9 @@ public: }; -bool loadDynamicProfileEntries(std::vector &out); -bool storeDynamicProfileEntries( - const std::vector &entries); +bool loadDynamicProfileRules(std::vector &out); +bool storeDynamicProfileRules( + const std::vector &rules); rtengine::procparams::PartialProfile *loadDynamicProfile( const rtengine::ImageMetaData *im); diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 0ac82f7ae..2d8ba638a 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -69,40 +69,40 @@ DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, } -void DynamicProfilePanel::EditDialog::set_entry( - const DynamicProfileEntry &entry) +void DynamicProfilePanel::EditDialog::set_rule( + const DynamicProfileRule &rule) { - iso_min_->set_value(entry.iso.min); - iso_max_->set_value(entry.iso.max); + iso_min_->set_value(rule.iso.min); + iso_max_->set_value(rule.iso.max); - fnumber_min_->set_value(entry.fnumber.min); - fnumber_max_->set_value(entry.fnumber.max); + fnumber_min_->set_value(rule.fnumber.min); + fnumber_max_->set_value(rule.fnumber.max); - focallen_min_->set_value(entry.focallen.min); - focallen_max_->set_value(entry.focallen.max); + focallen_min_->set_value(rule.focallen.min); + focallen_max_->set_value(rule.focallen.max); - shutterspeed_min_->set_value(entry.shutterspeed.min); - shutterspeed_max_->set_value(entry.shutterspeed.max); + shutterspeed_min_->set_value(rule.shutterspeed.min); + shutterspeed_max_->set_value(rule.shutterspeed.max); - expcomp_min_->set_value(entry.expcomp.min); - expcomp_max_->set_value(entry.expcomp.max); + expcomp_min_->set_value(rule.expcomp.min); + expcomp_max_->set_value(rule.expcomp.max); - has_camera_->set_active(entry.camera.enabled); - camera_->set_text(entry.camera.value); + has_camera_->set_active(rule.camera.enabled); + camera_->set_text(rule.camera.value); - has_lens_->set_active(entry.lens.enabled); - lens_->set_text(entry.lens.value); + has_lens_->set_active(rule.lens.enabled); + lens_->set_text(rule.lens.value); profilepath_->updateProfileList(); - if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { + if (!profilepath_->setActiveRowFromFullPath(rule.profilepath)) { profilepath_->setInternalEntry(); } } -DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() +DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() { - DynamicProfileEntry ret; + DynamicProfileRule ret; ret.iso.min = iso_min_->get_value_as_int(); ret.iso.max = iso_max_->get_value_as_int(); @@ -131,27 +131,27 @@ DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() void DynamicProfilePanel::EditDialog::set_ranges() { - DynamicProfileEntry default_entry; + DynamicProfileRule default_rule; iso_min_->set_digits(0); iso_max_->set_digits(0); iso_min_->set_increments(1, 10); iso_max_->set_increments(1, 10); - iso_min_->set_range(default_entry.iso.min, default_entry.iso.max); - iso_max_->set_range(default_entry.iso.min, default_entry.iso.max); - iso_min_->set_value(default_entry.iso.min); - iso_max_->set_value(default_entry.iso.max); + iso_min_->set_range(default_rule.iso.min, default_rule.iso.max); + iso_max_->set_range(default_rule.iso.min, default_rule.iso.max); + iso_min_->set_value(default_rule.iso.min); + iso_max_->set_value(default_rule.iso.max); #define DOIT_(name) \ name ## _min_->set_digits(1); \ name ## _max_->set_digits(1); \ name ## _min_->set_increments(0.1, 1); \ name ## _max_->set_increments(0.1, 1); \ - name ## _min_->set_range(default_entry. name .min, \ - default_entry. name .max); \ - name ## _max_->set_range(default_entry. name .min, \ - default_entry. name .max); \ - name ## _min_->set_value(default_entry. name .min); \ - name ## _max_->set_value(default_entry. name .max) + name ## _min_->set_range(default_rule. name .min, \ + default_rule. name .max); \ + name ## _max_->set_range(default_rule. name .min, \ + default_rule. name .max); \ + name ## _min_->set_value(default_rule. name .min); \ + name ## _max_->set_value(default_rule. name .max) DOIT_(fnumber); DOIT_(focallen); @@ -308,39 +308,39 @@ DynamicProfilePanel::DynamicProfilePanel(): show_all_children(); - std::vector entries; - if (loadDynamicProfileEntries(entries)) { - for (auto &e : entries) { - add_entry(e); + std::vector rules; + if (loadDynamicProfileRules(rules)) { + for (auto &r : rules) { + add_rule(r); } } } -void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, - const DynamicProfileEntry &entry) +void DynamicProfilePanel::update_rule(Gtk::TreeModel::Row row, + const DynamicProfileRule &rule) { - row[columns_.iso] = entry.iso; - row[columns_.fnumber] = entry.fnumber; - row[columns_.focallen] = entry.focallen; - row[columns_.shutterspeed] = entry.shutterspeed; - row[columns_.expcomp] = entry.expcomp; - row[columns_.camera] = entry.camera; - row[columns_.lens] = entry.lens; - row[columns_.profilepath] = entry.profilepath; + row[columns_.iso] = rule.iso; + row[columns_.fnumber] = rule.fnumber; + row[columns_.focallen] = rule.focallen; + row[columns_.shutterspeed] = rule.shutterspeed; + row[columns_.expcomp] = rule.expcomp; + row[columns_.camera] = rule.camera; + row[columns_.lens] = rule.lens; + row[columns_.profilepath] = rule.profilepath; } -void DynamicProfilePanel::add_entry(const DynamicProfileEntry &entry) +void DynamicProfilePanel::add_rule(const DynamicProfileRule &rule) { auto row = *(treemodel_->append()); - update_entry(row, entry); + update_rule(row, rule); } -DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, +DynamicProfileRule DynamicProfilePanel::to_rule(Gtk::TreeModel::Row row, int serial) { - DynamicProfileEntry ret; + DynamicProfileRule ret; ret.serial_number = serial; ret.iso = row[columns_.iso]; ret.fnumber = row[columns_.fnumber]; @@ -372,8 +372,8 @@ void DynamicProfilePanel::render_profilepath( #define RENDER_RANGE_(tp, name, prec) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Range r = row[columns_. name]; \ - DynamicProfileEntry dflt; \ + DynamicProfileRule::Range r = row[columns_. name]; \ + DynamicProfileRule dflt; \ if (r.min > dflt.name.min || r.max < dflt.name.max) { \ auto value = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ ct->property_text() = value; \ @@ -420,7 +420,7 @@ void DynamicProfilePanel::render_expcomp( #define RENDER_OPTIONAL_(name) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Optional o = row[columns_. name]; \ + DynamicProfileRule::Optional o = row[columns_. name]; \ if (o.enabled) { \ ct->property_text() = o.value; \ } else { \ @@ -488,8 +488,8 @@ void DynamicProfilePanel::on_button_new() static_cast(*get_toplevel())); int status = d.run(); if (status == 1) { - DynamicProfileEntry entry = d.get_entry(); - add_entry(entry); + DynamicProfileRule rule = d.get_rule(); + add_rule(rule); } } @@ -504,24 +504,24 @@ void DynamicProfilePanel::on_button_edit() static_cast(*get_toplevel())); auto it = s->get_selected(); Gtk::TreeModel::Row row = *(s->get_selected()); - d.set_entry(to_entry(row)); + d.set_rule(to_rule(row)); int status = d.run(); if (status == 1) { - update_entry(row, d.get_entry()); + update_rule(row, d.get_rule()); } } void DynamicProfilePanel::save() { - std::vector entries; + std::vector rules; int serial = 1; for (auto row : treemodel_->children()) { - entries.emplace_back(to_entry(row, serial++)); + rules.emplace_back(to_rule(row, serial++)); } - if (!storeDynamicProfileEntries(entries)) { + if (!storeDynamicProfileRules(rules)) { printf("Error in saving dynamic profile rules\n"); } else { - printf("Saved %d dynamic profile rules\n", int(entries.size())); + printf("Saved %d dynamic profile rules\n", int(rules.size())); } } diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h index 12b68fadc..72ff95b9a 100644 --- a/rtgui/dynamicprofilepanel.h +++ b/rtgui/dynamicprofilepanel.h @@ -29,10 +29,10 @@ public: void save(); private: - void update_entry(Gtk::TreeModel::Row row, - const DynamicProfileEntry &entry); - void add_entry(const DynamicProfileEntry &entry); - DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); + void update_rule(Gtk::TreeModel::Row row, + const DynamicProfileRule &rule); + void add_rule(const DynamicProfileRule &rule); + DynamicProfileRule to_rule(Gtk::TreeModel::Row row, int serial=0); void on_button_quit(); void on_button_up(); @@ -55,13 +55,13 @@ private: add(profilepath); } - Gtk::TreeModelColumn> iso; - Gtk::TreeModelColumn> fnumber; - Gtk::TreeModelColumn> focallen; - Gtk::TreeModelColumn> shutterspeed; - Gtk::TreeModelColumn> expcomp; - Gtk::TreeModelColumn camera; - Gtk::TreeModelColumn lens; + Gtk::TreeModelColumn> iso; + Gtk::TreeModelColumn> fnumber; + Gtk::TreeModelColumn> focallen; + Gtk::TreeModelColumn> shutterspeed; + Gtk::TreeModelColumn> expcomp; + Gtk::TreeModelColumn camera; + Gtk::TreeModelColumn lens; Gtk::TreeModelColumn profilepath; }; @@ -86,8 +86,8 @@ private: class EditDialog: public Gtk::Dialog { public: EditDialog(const Glib::ustring &title, Gtk::Window &parent); - void set_entry(const DynamicProfileEntry &entry); - DynamicProfileEntry get_entry(); + void set_rule(const DynamicProfileRule &rule); + DynamicProfileRule get_rule(); private: void set_ranges(); From dd4fd82582d9688488d4038f7bcce16b53b1fa52 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 14:16:26 +0100 Subject: [PATCH 132/181] Cache DynamicProfileRuleS in the profileStore for reduced I/O --- rtgui/dynamicprofile.cc | 23 ++++++++++------------- rtgui/dynamicprofilepanel.cc | 11 +++++------ rtgui/profilestore.cc | 1 + rtgui/profilestore.h | 11 ++++++++++- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index d1a5a0177..27d4f0178 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -229,19 +229,16 @@ bool storeDynamicProfileRules(const std::vector &rules) PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); - std::vector rules; - if (loadDynamicProfileRules(rules)) { - for (auto &rule : rules) { - if (rule.matches(im)) { - printf("found matching profile %s\n", - rule.profilepath.c_str()); - const PartialProfile *p = - profileStore.getProfile(rule.profilepath); - if (p != nullptr) { - p->applyTo(ret->pparams); - } else { - printf("ERROR loading matching profile\n"); - } + for (auto &rule : profileStore.getDynamicProfileRules()) { + if (rule.matches(im)) { + printf("found matching profile %s\n", + rule.profilepath.c_str()); + const PartialProfile *p = + profileStore.getProfile(rule.profilepath); + if (p != nullptr) { + p->applyTo(ret->pparams); + } else { + printf("ERROR loading matching profile\n"); } } } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 2d8ba638a..bd7665c36 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -308,11 +308,8 @@ DynamicProfilePanel::DynamicProfilePanel(): show_all_children(); - std::vector rules; - if (loadDynamicProfileRules(rules)) { - for (auto &r : rules) { - add_rule(r); - } + for (auto &r : profileStore.getDynamicProfileRules()) { + add_rule(r); } } @@ -514,13 +511,15 @@ void DynamicProfilePanel::on_button_edit() void DynamicProfilePanel::save() { - std::vector rules; + auto &rules = profileStore.getDynamicProfileRules(); + rules.clear(); int serial = 1; for (auto row : treemodel_->children()) { rules.emplace_back(to_rule(row, serial++)); } if (!storeDynamicProfileRules(rules)) { printf("Error in saving dynamic profile rules\n"); + rules.clear(); } else { printf("Saved %d dynamic profile rules\n", int(rules.size())); } diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 31b490ce1..7eaf50b77 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -42,6 +42,7 @@ bool ProfileStore::init () storeState = STORESTATE_BEINGINITIALIZED; parseMutex = new MyMutex(); _parseProfiles (); + loadDynamicProfileRules(dynamicRules); storeState = STORESTATE_INITIALIZED; } diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index 5e5591e15..022652287 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -29,6 +29,7 @@ #include "threadutils.h" #include "paramsedited.h" #include "guiutils.h" +#include "dynamicprofile.h" /** @brief This will implement callback functions for the ProfileStore @@ -161,6 +162,9 @@ private: /** List of the client of this store */ std::list listeners; + /** cache for dynamic profile rules */ + std::vector dynamicRules; + /** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3 * * @param realPath current full path of the scanned directory ; e.g.: ~/MyProfiles/ @@ -203,7 +207,12 @@ public: { return internalDynamicEntry; } - + + std::vector &getDynamicProfileRules() + { + return dynamicRules; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); From 6125b47c2667028b186bcaa6faff62b3851d1c89 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 4 Mar 2017 15:10:50 +0100 Subject: [PATCH 133/181] Update v2.47, dynamic profile --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 39 +++++++++++++++-------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index 754aa61b7..842dc0698 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.46 - requires RT 5.0 + Version 2.47 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,10 +76,10 @@ } #ToolPanelNotebook { - min-width: 23em; + min-width: 24em; } #HistoryPanel { - min-width: 17.83334em; + min-width: 18em; } window.background { @@ -789,14 +789,18 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-bottom: 0.33334em; } -#ToolPanelNotebook > header tab{ - margin-left: 0.08333em; - margin-right: 0.08333em; +#ToolPanelNotebook > header tab { + margin-left: 0; + margin-right: 0; + padding: 0 0.33334em; +} +#ToolPanelNotebook > header tab + tab { + margin-left: 0.25em; } #ToolPanelNotebook > header tab image{ min-height: 2.5em; - min-width: calc(1.66667em + 6px); + min-width: 2em; padding: 0; margin: 0; } @@ -1379,6 +1383,13 @@ dialog combobox .combo, margin-left: 0.33334em; } +buttonbox:not(.dialog-action-area) button{ + margin: 0.16667em 0 0.33334em 0.16667em; +} +#PrefNotebook buttonbox:not(.dialog-action-area) { + margin-right: -5px; +} + /* Arrow toggle combo button */ #IopsPanel button:not(.flat).Left + button:not(.flat).Right, #MyExpander button:not(.flat).Left + button:not(.flat).Right { @@ -1398,7 +1409,7 @@ dialog combobox .combo, /**/ /* Add extra top and bottom space to buttons in toolbox & Preferences*/ -#PrefNotebook stack > box:nth-child(4) combobox, +#PrefNotebook stack > box:nth-child(5) combobox, #MyExpander button.flat, #MyExpander button { margin-top: 0.08334em; @@ -1481,10 +1492,10 @@ dialog .view button { color: @headline-hl; } -dialog .view header button:not(:last-child):not(:only-child), -window .view header button:not(:last-child):not(:only-child), -.path-bar button:not(:last-child):not(:only-child) { - border-right: none; +dialog .view header button:not(:first-child):not(:only-child), +window .view header button:not(:first-child):not(:only-child), +.path-bar button:not(:first-child):not(:only-child) { + border-left: none; } dialog .view header button, window .view header button, @@ -1670,8 +1681,8 @@ frame > checkbutton check{ #PrefNotebook checkbutton { min-height: 1.16667em; } -#PrefNotebook stack > box:nth-child(3) checkbutton, -#PrefNotebook stack > box:nth-child(4) checkbutton { +#PrefNotebook stack > box:nth-child(4) checkbutton, +#PrefNotebook stack > box:nth-child(5) checkbutton { min-height: 1.83334em; } #PrefNotebook radiobutton { From d86a26ccf14b09e19b0d20247c8460f903e818e9 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sat, 4 Mar 2017 15:12:17 +0100 Subject: [PATCH 134/181] Update v2.47, dynamic profile --- rtdata/themes/TooWaBlue-GTK3-20_.css | 39 ++++++++++++++++++---------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 5c4cf2fed..4cdb4a308 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.46 - requires RT 5.0 + Version 2.47 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,10 +76,10 @@ } #ToolPanelNotebook { - min-width: 23em; + min-width: 24em; } #HistoryPanel { - min-width: 17.83334em; + min-width: 18em; } window.background { @@ -789,14 +789,18 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin-bottom: 0.33334em; } -#ToolPanelNotebook > header tab{ - margin-left: 0.08333em; - margin-right: 0.08333em; +#ToolPanelNotebook > header tab { + margin-left: 0; + margin-right: 0; + padding: 0 0.33334em; +} +#ToolPanelNotebook > header tab + tab { + margin-left: 0.25em; } #ToolPanelNotebook > header tab image{ min-height: 2.5em; - min-width: calc(1.66667em + 6px); + min-width: 2em; padding: 0; margin: 0; } @@ -1379,6 +1383,13 @@ dialog combobox .combo, margin-left: 0.33334em; } +buttonbox:not(.dialog-action-area) button{ + margin: 0.16667em 0 0.33334em 0.16667em; +} +#PrefNotebook buttonbox:not(.dialog-action-area) { + margin-right: -5px; +} + /* Arrow toggle combo button */ #IopsPanel button:not(.flat).Left + button:not(.flat).Right, #MyExpander button:not(.flat).Left + button:not(.flat).Right { @@ -1398,7 +1409,7 @@ dialog combobox .combo, /**/ /* Add extra top and bottom space to buttons in toolbox & Preferences*/ -#PrefNotebook stack > box:nth-child(4) combobox, +#PrefNotebook stack > box:nth-child(5) combobox, #MyExpander button.flat, #MyExpander button { margin-top: 0.08334em; @@ -1481,10 +1492,10 @@ dialog .view button { color: @headline-hl; } -dialog .view header button:not(:last-child):not(:only-child), -window .view header button:not(:last-child):not(:only-child), -.path-bar button:not(:last-child):not(:only-child) { - border-right: none; +dialog .view header button:not(:first-child):not(:only-child), +window .view header button:not(:first-child):not(:only-child), +.path-bar button:not(:first-child):not(:only-child) { + border-left: none; } dialog .view header button, window .view header button, @@ -1670,8 +1681,8 @@ frame > checkbutton check{ #PrefNotebook checkbutton { min-height: 1.16667em; } -#PrefNotebook stack > box:nth-child(3) checkbutton, -#PrefNotebook stack > box:nth-child(4) checkbutton { +#PrefNotebook stack > box:nth-child(4) checkbutton, +#PrefNotebook stack > box:nth-child(5) checkbutton { min-height: 1.83334em; } #PrefNotebook radiobutton { From d40d806224e33e206dc9b97e4e8c3e1dcd593119 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 09:52:32 +0100 Subject: [PATCH 135/181] slightly more informative error log message --- rtgui/dynamicprofile.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 27d4f0178..86b8126e8 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -238,7 +238,8 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) if (p != nullptr) { p->applyTo(ret->pparams); } else { - printf("ERROR loading matching profile\n"); + printf("ERROR loading matching profile from: %s\n", + rule.profilepath.c_str()); } } } From f8bf8d18dc128c06449cabd1d3f60a880a11f985 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 11:07:07 +0100 Subject: [PATCH 136/181] minor cosmetic tweak to the way dynamic profile rules are shown --- rtgui/dynamicprofilepanel.cc | 44 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index bd7665c36..f148dab2e 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -20,21 +20,10 @@ #include "dynamicprofilepanel.h" #include "multilangmgr.h" #include "profilestore.h" +#include "../rtengine/rtengine.h" #include #include -namespace { - -template -Glib::ustring to_str(V n, int precision=1) -{ - std::ostringstream buf; - buf << std::setprecision(precision) << std::fixed << n; - return buf.str(); -} - -} // namespace - //----------------------------------------------------------------------------- // DynamicProfilePanel::EditDialog @@ -366,50 +355,67 @@ void DynamicProfilePanel::render_profilepath( } -#define RENDER_RANGE_(tp, name, prec) \ +#define RENDER_RANGE_(tp, name, tostr) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ DynamicProfileRule::Range r = row[columns_. name]; \ DynamicProfileRule dflt; \ if (r.min > dflt.name.min || r.max < dflt.name.max) { \ - auto value = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ + auto value = tostr(r.min) + " - " + tostr(r.max); \ ct->property_text() = value; \ } else { \ ct->property_text() = ""; \ } + +namespace { + +template +Glib::ustring to_str(V n, int precision=1) +{ + std::ostringstream buf; + buf << std::setprecision(precision) << std::fixed << n; + return buf.str(); +} + +} // namespace + void DynamicProfilePanel::render_iso( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(int, iso, 0); + RENDER_RANGE_(int, iso, to_str); } void DynamicProfilePanel::render_fnumber( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, fnumber, 1); + RENDER_RANGE_(double, fnumber, + [](double f) + { return std::string("f/") + + rtengine::ImageMetaData::apertureToString(f); }); } void DynamicProfilePanel::render_focallen( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, focallen, 1); + RENDER_RANGE_(double, focallen, to_str); } void DynamicProfilePanel::render_shutterspeed( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, shutterspeed, 4); + RENDER_RANGE_(double, shutterspeed, + rtengine::ImageMetaData::shutterToString); } void DynamicProfilePanel::render_expcomp( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, expcomp, 1); + RENDER_RANGE_(double, expcomp, to_str); } #undef RENDER_RANGE_ From 43e32e81ef596560842d673a84cbcd0538ae190b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 18:10:42 +0100 Subject: [PATCH 137/181] some code style improvements --- rtgui/dynamicprofile.cc | 2 +- rtgui/dynamicprofile.h | 2 +- rtgui/dynamicprofilepanel.cc | 5 ++--- rtgui/profilestore.h | 7 ++++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 86b8126e8..69bda1d89 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -70,7 +70,7 @@ bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const } -bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) +bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) const { return (iso(im->getISOSpeed()) && fnumber(im->getFNumber()) diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index c662a51f7..4c5e552e4 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -48,7 +48,7 @@ public: }; DynamicProfileRule(); - bool matches(const rtengine::ImageMetaData *im); + bool matches(const rtengine::ImageMetaData *im) const; bool operator<(const DynamicProfileRule &other) const; int serial_number; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index f148dab2e..d06f9be4f 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -517,16 +517,15 @@ void DynamicProfilePanel::on_button_edit() void DynamicProfilePanel::save() { - auto &rules = profileStore.getDynamicProfileRules(); - rules.clear(); + std::vector rules; int serial = 1; for (auto row : treemodel_->children()) { rules.emplace_back(to_rule(row, serial++)); } if (!storeDynamicProfileRules(rules)) { printf("Error in saving dynamic profile rules\n"); - rules.clear(); } else { + profileStore.setDynamicProfileRules(rules); printf("Saved %d dynamic profile rules\n", int(rules.size())); } } diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index 022652287..f97cd02e1 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -208,11 +208,16 @@ public: return internalDynamicEntry; } - std::vector &getDynamicProfileRules() + const std::vector &getDynamicProfileRules() const { return dynamicRules; } + void setDynamicProfileRules(const std::vector &r) + { + dynamicRules = r; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); From 48ac4e3112c40e00ee09be4f117f564b7b2df51e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 22:45:13 +0100 Subject: [PATCH 138/181] added workaround for #3727 (by heckflosse) --- rtgui/profilestore.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 7eaf50b77..364ae5c6c 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -577,12 +577,12 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, // creating and assigning the custom Label object newSubMenu[methodColumns.label] = entry->label; newSubMenu[methodColumns.profileStoreEntry] = entry; - +#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION == 18 // HACK: Workaround for bug in Gtk+ 3.18... Gtk::TreeModel::Row menuHeader = *(refTreeModel->append(newSubMenu->children())); - menuHeader[methodColumns.label] = entry->label; + menuHeader[methodColumns.label] = "-"; menuHeader[methodColumns.profileStoreEntry] = entry; - +#endif refreshProfileList_ (&newSubMenu, entry->folderId, false, entryList); } else { refreshProfileList_ (parentRow, entry->folderId, true, entryList); From f7f4575c54a97034186cc7380d6e7dbd5284972e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 22:59:44 +0100 Subject: [PATCH 139/181] allow to run the custom profile builder (if set) after the creation of the dynamic profile --- rtgui/thumbnail.cc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 60da23d01..b9fb5ed2e 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -217,8 +217,16 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); + bool create = (!hasProcParams() || forceCPB); - if (defProf == DEFPROFILE_DYNAMIC && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + Glib::ustring outFName; + if (options.paramsLoadLocation == PLL_Input) { + outFName = fname + paramFileExtension; + } else { + outFName = getCacheFileName("profiles", paramFileExtension); + } + + if (defProf == DEFPROFILE_DYNAMIC && create && cfs && cfs->exifValid) { rtengine::ImageMetaData* imageMetaData; if (getType() == FT_Raw) { rtengine::RawMetaDataLocation metaData = rtengine::Thumbnail::loadMetaDataFromRaw(fname); @@ -227,18 +235,15 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); } PartialProfile *pp = loadDynamicProfile(imageMetaData); - int err = 0; - if (options.paramsLoadLocation == PLL_Input) { - err = pp->pparams->save(fname + paramFileExtension); - } else { - err = pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); - } + int err = pp->pparams->save(outFName); pp->deleteInstance(); delete pp; if (!err) { loadProcParams(); } - } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + } + + if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && create && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; @@ -254,14 +259,6 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const rtexif::TagDirectory* exifDir = nullptr; if (imageMetaData && (exifDir = imageMetaData->getExifData())) { - Glib::ustring outFName; - - if (options.paramsLoadLocation == PLL_Input) { - outFName = fname + paramFileExtension; - } else { - outFName = getCacheFileName("profiles", paramFileExtension); - } - exifDir->CPBDump(tmpFileName, fname, outFName, defaultPparamsPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(defaultPparamsPath, Glib::path_get_basename(defProf) + paramFileExtension), cfs, From a1a042e9eaae14f5450beea4b079e2c340831996 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 09:00:13 +0100 Subject: [PATCH 140/181] code style cleanups (after floessie's feedback) --- rtgui/profilestore.cc | 19 +++++++++++++++++-- rtgui/profilestore.h | 17 ++++++----------- rtgui/thumbnail.cc | 12 +++++------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 364ae5c6c..d7bf18ab5 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -20,13 +20,14 @@ #include "options.h" #include "toolpanel.h" #include "guiutils.h" +#include "dynamicprofile.h" ProfileStore profileStore; using namespace rtengine; using namespace rtengine::procparams; -ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr), internalDynamicEntry(nullptr) +ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr), internalDynamicEntry(nullptr), dynamicRules(new std::vector()) { internalDefaultProfile = new AutoPartialProfile(); internalDefaultProfile->set(true); @@ -42,7 +43,7 @@ bool ProfileStore::init () storeState = STORESTATE_BEINGINITIALIZED; parseMutex = new MyMutex(); _parseProfiles (); - loadDynamicProfileRules(dynamicRules); + loadDynamicProfileRules(*dynamicRules); storeState = STORESTATE_INITIALIZED; } @@ -506,6 +507,20 @@ void ProfileStore::dumpFolderList() printf("\n"); } + +const std::vector &ProfileStore::getDynamicProfileRules() const +{ + return *dynamicRules; +} + + +void ProfileStore::setDynamicProfileRules(const std::vector &r) +{ + *dynamicRules = r; +} + + + ProfileStoreEntry::ProfileStoreEntry() : label(""), type(PSET_FOLDER), parentFolderId(0), folderId(0) {} ProfileStoreEntry::ProfileStoreEntry(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) : label(label), type(type), parentFolderId(parentFolder), folderId(folder) {} diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index f97cd02e1..ad569c180 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -29,9 +29,11 @@ #include "threadutils.h" #include "paramsedited.h" #include "guiutils.h" -#include "dynamicprofile.h" +// forward decl +class DynamicProfileRule; + /** @brief This will implement callback functions for the ProfileStore * */ @@ -163,7 +165,7 @@ private: std::list listeners; /** cache for dynamic profile rules */ - std::vector dynamicRules; + std::unique_ptr> dynamicRules; /** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3 * @@ -208,15 +210,8 @@ public: return internalDynamicEntry; } - const std::vector &getDynamicProfileRules() const - { - return dynamicRules; - } - - void setDynamicProfileRules(const std::vector &r) - { - dynamicRules = r; - } + const std::vector &getDynamicProfileRules() const; + void setDynamicProfileRules(const std::vector &r); void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index b9fb5ed2e..62f543d4a 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -217,14 +217,12 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); - bool create = (!hasProcParams() || forceCPB); + const bool create = (!hasProcParams() || forceCPB); - Glib::ustring outFName; - if (options.paramsLoadLocation == PLL_Input) { - outFName = fname + paramFileExtension; - } else { - outFName = getCacheFileName("profiles", paramFileExtension); - } + const Glib::ustring outFName = + (options.paramsLoadLocation == PLL_Input) ? + fname + paramFileExtension : + getCacheFileName("profiles", paramFileExtension); if (defProf == DEFPROFILE_DYNAMIC && create && cfs && cfs->exifValid) { rtengine::ImageMetaData* imageMetaData; From e8d4378659b21e4165b255e403b9bca1a65701bb Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 15:02:39 +0100 Subject: [PATCH 141/181] print loading/saving dynamic profile messages only in verbose mode --- rtgui/dynamicprofile.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 69bda1d89..4439a1562 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -169,7 +169,9 @@ bool loadDynamicProfileRules(std::vector &out) } catch (Glib::Error &e) { return false; } - printf("loading dynamic profiles...\n"); + if (options.rtSettings.verbose) { + printf("loading dynamic profiles...\n"); + } auto groups = kf.get_groups(); for (auto group : groups) { // groups are of the form "rule N", where N is a positive integer @@ -181,7 +183,9 @@ bool loadDynamicProfileRules(std::vector &out) if (!(buf >> serial) || !buf.eof()) { return false; } - printf(" loading rule %d\n", serial); + if (options.rtSettings.verbose) { + printf(" loading rule %d\n", serial); + } out.emplace_back(DynamicProfileRule()); DynamicProfileRule &rule = out.back(); @@ -206,7 +210,9 @@ bool loadDynamicProfileRules(std::vector &out) bool storeDynamicProfileRules(const std::vector &rules) { - printf("saving dynamic profiles...\n"); + if (options.rtSettings.verbose) { + printf("saving dynamic profiles...\n"); + } Glib::KeyFile kf; for (auto &rule : rules) { std::ostringstream buf; @@ -231,8 +237,10 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) PartialProfile *ret = new PartialProfile(true, true); for (auto &rule : profileStore.getDynamicProfileRules()) { if (rule.matches(im)) { - printf("found matching profile %s\n", - rule.profilepath.c_str()); + if (options.rtSettings.verbose) { + printf("found matching profile %s\n", + rule.profilepath.c_str()); + } const PartialProfile *p = profileStore.getProfile(rule.profilepath); if (p != nullptr) { From 6ace581fcb8e4b9be555330082f97ae9821b39b5 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Mon, 6 Mar 2017 16:07:46 +0100 Subject: [PATCH 142/181] Updated RawTherapee GTK3.20 theme by TooWaBoo, closes #3733 --- rtdata/themes/RawTherapee-GTK3-20_.css | 48 ++++++++++++++++---------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index 19bd6ac9c..a343dd9fb 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -50,30 +50,36 @@ window.background { background-color: #484848; } -headerbar { - box-shadow: none; - background-color: #262626; - padding: 0; - margin: 0; - border-radius: 0; - border-style: none none solid none; +/*** Window decoration *********************************************************/ +@define-color winHeaderbar rgb(50,50,50); + +:not(.popup):not(tooltip) > decoration { + background-color: #484848; background-image: none; -} - -/* Warning: maximized and fullscreen can be set at the same time ! */ -window.maximized > decoration, window.fullscreen > decoration { + border-radius: 5px 5px 0 0; + border: none; padding: 0; - margin: 0; + box-shadow: 0 3px 9px 1px rgba(0, 0, 0, 0.5), 0 0 0 1px #242424; + margin: 10px; +} +headerbar { + background-color: shade(@winHeaderbar,1.12); + box-shadow: inset 0 1px rgba(200,200,200,.13); + background-image: linear-gradient(shade(@winHeaderbar,1.14), shade(@winHeaderbar,.86)); + border-bottom: 1px solid #242424; + border-radius: 5px 5px 0 0; + min-height: 26px; + padding: 1px 5px 0; + margin: 0 0 1px 0; } -window > decoration, dialog > decoration { - box-shadow: none; - margin: 0px; - border-style: none; +/* Window state */ +.maximized > headerbar { border-radius: 0; - padding: 3px; - background-color: #262626; } +/**/ + +/*** End Window decoration *****************************************************/ dialog.background { background-color: #484848; @@ -196,6 +202,12 @@ button:hover , button.flat:hover, checkbutton:hover > check, radiobutton:hover > button.popupbutton-arrow { min-width: 18px; } +/* Save, Cancel, OK ... buttons */ +.dialog-action-area button { + min-height: 24px; + margin-top: 6px; +} +/**/ combobox { min-height: 5px; From 48946d9d09d257d3ce88714013b4eb7adc233785 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Mon, 6 Mar 2017 16:08:20 +0100 Subject: [PATCH 143/181] Renamed BW themes to Black-and-White --- .../{BW/BW 1.pp3 => Black-and-White/Black-and-White 1.pp3} | 0 .../{BW/BW 2.pp3 => Black-and-White/Black-and-White 2.pp3} | 0 .../{BW/BW 3.pp3 => Black-and-White/Black-and-White 3.pp3} | 0 .../{BW/BW 4.pp3 => Black-and-White/Black-and-White 4.pp3} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename rtdata/profiles/{BW/BW 1.pp3 => Black-and-White/Black-and-White 1.pp3} (100%) rename rtdata/profiles/{BW/BW 2.pp3 => Black-and-White/Black-and-White 2.pp3} (100%) rename rtdata/profiles/{BW/BW 3.pp3 => Black-and-White/Black-and-White 3.pp3} (100%) rename rtdata/profiles/{BW/BW 4.pp3 => Black-and-White/Black-and-White 4.pp3} (100%) diff --git a/rtdata/profiles/BW/BW 1.pp3 b/rtdata/profiles/Black-and-White/Black-and-White 1.pp3 similarity index 100% rename from rtdata/profiles/BW/BW 1.pp3 rename to rtdata/profiles/Black-and-White/Black-and-White 1.pp3 diff --git a/rtdata/profiles/BW/BW 2.pp3 b/rtdata/profiles/Black-and-White/Black-and-White 2.pp3 similarity index 100% rename from rtdata/profiles/BW/BW 2.pp3 rename to rtdata/profiles/Black-and-White/Black-and-White 2.pp3 diff --git a/rtdata/profiles/BW/BW 3.pp3 b/rtdata/profiles/Black-and-White/Black-and-White 3.pp3 similarity index 100% rename from rtdata/profiles/BW/BW 3.pp3 rename to rtdata/profiles/Black-and-White/Black-and-White 3.pp3 diff --git a/rtdata/profiles/BW/BW 4.pp3 b/rtdata/profiles/Black-and-White/Black-and-White 4.pp3 similarity index 100% rename from rtdata/profiles/BW/BW 4.pp3 rename to rtdata/profiles/Black-and-White/Black-and-White 4.pp3 From ff5919be4abbcdd4d1d29125bb2b8f424c880af1 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 6 Mar 2017 16:52:31 +0100 Subject: [PATCH 144/181] Shadows/Highlights: Use iterated boxblur for radius > 40 when Sharp mask is disabled, fixes #3730 --- rtengine/shmap.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rtengine/shmap.cc b/rtengine/shmap.cc index 65c1070a2..a92264117 100644 --- a/rtengine/shmap.cc +++ b/rtengine/shmap.cc @@ -85,12 +85,21 @@ void SHMap::update (Imagefloat* img, double radius, double lumi[3], bool hq, int if (!hq) { fillLuminance( img, map, lumi); + float *buffer = nullptr; + + if(radius > 40.) { + // When we pass another buffer to gaussianBlur, it will use iterated boxblur which is less prone to artifacts + buffer = new float[W * H]; + } + #ifdef _OPENMP #pragma omp parallel #endif { - gaussianBlur (map, map, W, H, radius); + gaussianBlur (map, map, W, H, radius, buffer); } + + delete [] buffer; } else { From d40a0c750d184a71f269dc18a4c354e654de5693 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 17:56:10 +0100 Subject: [PATCH 145/181] print loading/saving dynamic profile messages only in verbose mode (forgot one from previous commit) --- rtgui/dynamicprofilepanel.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index d06f9be4f..e5f9ae1cc 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -526,6 +526,8 @@ void DynamicProfilePanel::save() printf("Error in saving dynamic profile rules\n"); } else { profileStore.setDynamicProfileRules(rules); - printf("Saved %d dynamic profile rules\n", int(rules.size())); + if (options.rtSettings.verbose) { + printf("Saved %d dynamic profile rules\n", int(rules.size())); + } } } From 34350c65bfe7833b4eb6deb34a7ca52df9289fd3 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Mon, 6 Mar 2017 21:41:05 +0100 Subject: [PATCH 146/181] Deutsch locale update "Dynamic Profiles" --- rtdata/languages/Deutsch | 242 ++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 115 deletions(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index deec1e3a1..8d91c9d38 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -35,6 +35,7 @@ #34 2017-01-07 IPTC (TooWaBoo) RT4.2.1492 #35 2017-02-18 AWB bias (TooWaBoo) RT 5.0 r1 #36 2017-02-23 Korrekturen (TooWaBoo) RT 5.0 r1 +#37 2017-03-06 Dynamisches Profil (TooWaBoo) ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen @@ -70,6 +71,15 @@ CURVEEDITOR_TOOLTIPPASTE;Kurve aus Zwischenablage einfügen CURVEEDITOR_TOOLTIPSAVE;Kurve speichern CURVEEDITOR_TYPE;Typ: DIRBROWSER_FOLDERS;Ordner +DYNPROFILEEDITOR_DELETE;Löschen +DYNPROFILEEDITOR_EDIT;Ändern +DYNPROFILEEDITOR_EDIT_RULE;Profilregel ändern +DYNPROFILEEDITOR_ENTRY_TOOLTIP;Groß-/Kleinschreibung wird NICHT berücksichtigt.\nFür einen regulären Ausdruck benutzen Sie bitte\n"re:" als Prefix. +DYNPROFILEEDITOR_MOVE_DOWN;Runter +DYNPROFILEEDITOR_MOVE_UP;Hoch +DYNPROFILEEDITOR_NEW;Neu +DYNPROFILEEDITOR_NEW_RULE;Profilregel erstellen +DYNPROFILEEDITOR_PROFILE;Profil EDITWINDOW_TITLE;Bildbearbeitung EDIT_OBJECT_TOOLTIP;Schaltet das Einstellungswerkzeug\nim Vorschaubild ein/aus EDIT_PIPETTE_TOOLTIP;Um einen Punkt der Kurve hinzuzufügen, halten Sie die Strg-Taste gedrückt und klicken mit der linke Maustaste auf die gewünschte Stelle in der Vorschau.\nUm den Punkt anzupassen, halten Sie die Strg-Taste gedrückt und klicken Sie mit der linken Maustaste auf den entsprechenden Bereich in der Vorschau. Dann lassen Sie die Strg-Taste los (es sei denn, Sie möchten eine Feineinstellung vornehmen) und bewegen die Maus bei gedrückter linker Maustaste nach oben oder unten, um den Punkt auf der Kurve zu bewegen. @@ -152,13 +162,13 @@ FILEBROWSER_OPENDEFAULTVIEWER;Windows Standard-Betracher (stapelverarbeitet) FILEBROWSER_PARTIALPASTEPROFILE;Profil selektiv einfügen FILEBROWSER_PASTEPROFILE;Profil einfügen FILEBROWSER_POPUPCANCELJOB;Job abbrechen -FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOLORLABEL0;Markierung: Ohne FILEBROWSER_POPUPCOLORLABEL1;Markierung: Rot FILEBROWSER_POPUPCOLORLABEL2;Markierung: Gelb FILEBROWSER_POPUPCOLORLABEL3;Markierung: Grün FILEBROWSER_POPUPCOLORLABEL4;Markierung: Blau FILEBROWSER_POPUPCOLORLABEL5;Markierung: Violett +FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOPYTO;Kopieren nach... FILEBROWSER_POPUPFILEOPERATIONS;Dateioperationen FILEBROWSER_POPUPMOVEEND;An das Ende der Warteschlange verschieben @@ -169,13 +179,13 @@ FILEBROWSER_POPUPOPENINEDITOR;Im Editor öffnen FILEBROWSER_POPUPPROCESS;Zur Warteschlange hinzufügen FILEBROWSER_POPUPPROCESSFAST;Zur Warteschlange hinzufügen (Schnelles Exportieren) FILEBROWSER_POPUPPROFILEOPERATIONS;Profiloperationen -FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPRANK0;Nicht bewertet FILEBROWSER_POPUPRANK1;Bewertung 1 * FILEBROWSER_POPUPRANK2;Bewertung 2 ** FILEBROWSER_POPUPRANK3;Bewertung 3 *** FILEBROWSER_POPUPRANK4;Bewertung 4 **** FILEBROWSER_POPUPRANK5;Bewertung 5 ***** +FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPREMOVE;Löschen FILEBROWSER_POPUPREMOVEINCLPROC;Löschen (auch Resultate der Stapelverarbeitung) FILEBROWSER_POPUPRENAME;Umbenennen @@ -267,105 +277,6 @@ HISTORY_CUSTOMCURVE;Benutzerdefiniert HISTORY_DELSNAPSHOT;Entfernen HISTORY_FROMCLIPBOARD;Aus der Zwischenablage HISTORY_LABEL;Historie -HISTORY_MSG_1;(Bild geladen) -HISTORY_MSG_2;(Profil geladen) -HISTORY_MSG_3;(Profil geändert) -HISTORY_MSG_4;(Historie durchsuchen) -HISTORY_MSG_5;(Belichtung) - Helligkeit -HISTORY_MSG_6;(Belichtung) - Kontrast -HISTORY_MSG_7;(Belichtung)\nSchwarzwert -HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur -HISTORY_MSG_9;(Belichtung)\nLichterkompression -HISTORY_MSG_10;(Belichtung)\nSchattenkompression -HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 -HISTORY_MSG_12;(Belichtung) - Auto -HISTORY_MSG_13;(Belichtung) - Clip-Faktor -HISTORY_MSG_14;(L*a*b*) - Helligkeit -HISTORY_MSG_15;(L*a*b*) - Kontrast -HISTORY_MSG_16;- -HISTORY_MSG_17;- -HISTORY_MSG_18;- -HISTORY_MSG_19;(L*a*b*) - L-Kurve -HISTORY_MSG_20;(Schärfung) -HISTORY_MSG_21;(Schärfung) - USM\nRadius -HISTORY_MSG_22;(Schärfung) - USM\nIntensität -HISTORY_MSG_23;(Schärfung) - USM\nSchwelle -HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen -HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius -HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz -HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle -HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität -HISTORY_MSG_29;(Schärfung) - Methode -HISTORY_MSG_30;(Schärfung) - RLD\nRadius -HISTORY_MSG_31;(Schärfung) - RLD\nIntensität -HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung -HISTORY_MSG_33;(Schärfung) - RLD\nIterationen -HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung -HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung -HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur -HISTORY_MSG_37;(Belichtung) - Auto -HISTORY_MSG_38;(Weißabgleich) - Methode -HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur -HISTORY_MSG_40;(Weißabgleich) - Tönung -HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus -HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 -HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus -HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius -HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz -HISTORY_MSG_46;(Farb-Rauschfilter) -HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) -HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve -HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination -HISTORY_MSG_50;(Schatten/Lichter) -HISTORY_MSG_51;(Schatten/Lichter)\nLichter -HISTORY_MSG_52;(Schatten/Lichter)\nSchatten -HISTORY_MSG_53;(Schatten/Lichter)\nTonwertbreite Lichter -HISTORY_MSG_54;(Schatten/Lichter)\nTonwertbreite Schatten -HISTORY_MSG_55;(Schatten/Lichter)\nLokaler Kontrast -HISTORY_MSG_56;(Schatten/Lichter)\nRadius -HISTORY_MSG_57;(Grobe Drehung) -HISTORY_MSG_58;(Horizontal spiegeln) -HISTORY_MSG_59;(Vertikal spiegeln) -HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel -HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen -HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung -HISTORY_MSG_63;(Schnappschuss\nausgewählt) -HISTORY_MSG_64;(Ausschnitt) -HISTORY_MSG_65;(Objektivkorrektur)\nFarbsaum entfernen -HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren -HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang -HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode -HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum -HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil -HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil -HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität -HISTORY_MSG_73;(RGB-Kanalmixer) -HISTORY_MSG_74;(Skalieren) - Maßstab -HISTORY_MSG_75;(Skalieren) - Methode -HISTORY_MSG_76;(Exif Metadaten) -HISTORY_MSG_77;(IPTC Metadaten) -HISTORY_MSG_78;- -HISTORY_MSG_79;(Skalieren) - Breite -HISTORY_MSG_80;(Skalieren) - Höhe -HISTORY_MSG_81;(Skalieren) -HISTORY_MSG_82;(Profil geändert) -HISTORY_MSG_83;(Schatten/Lichter)\nSchärfemaske -HISTORY_MSG_84;(Objektivkorrektur)\nPerspektive -HISTORY_MSG_85;(Objektivkorrektur)\nProfil -HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus -HISTORY_MSG_87;(Impulsrauschred.) -HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle -HISTORY_MSG_89;(Rauschreduzierung) -HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz -HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) -HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma -HISTORY_MSG_93;(Detailebenenkontrast)\nWert -HISTORY_MSG_94;(Detailebenenkontrast) -HISTORY_MSG_95;(L*a*b*) - Chromatizität -HISTORY_MSG_96;(L*a*b*) - a-Kurve -HISTORY_MSG_97;(L*a*b*) - b-Kurve -HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode -HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter HISTORY_MSG_100;(Belichtung) - Sättigung HISTORY_MSG_101;(HSV) - Farbton (H) HISTORY_MSG_102;(HSV) - Sättigung (S) @@ -376,6 +287,7 @@ HISTORY_MSG_106;(Farbsaum entfernen)\nRadius HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen +HISTORY_MSG_10;(Belichtung)\nSchattenkompression HISTORY_MSG_110;(Skalieren) - Anwenden auf: HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden HISTORY_MSG_112;--unused-- @@ -386,6 +298,7 @@ HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter +HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl @@ -396,6 +309,7 @@ HISTORY_MSG_126;(Weißbild) - Datei HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl HISTORY_MSG_128;(Weißbild)\nUnschärferadius HISTORY_MSG_129;(Weißbild) - Unschärfetyp +HISTORY_MSG_12;(Belichtung) - Auto HISTORY_MSG_130;(Autom. Verzeichnung) HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz @@ -406,6 +320,7 @@ HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_13;(Belichtung) - Clip-Faktor HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen HISTORY_MSG_142;(Kantenschärfung)\nIterationen @@ -416,6 +331,7 @@ HISTORY_MSG_146;(Kantenschärfung) HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz HISTORY_MSG_148;(Mikrokontrast) HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix +HISTORY_MSG_14;(L*a*b*) - Helligkeit HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) HISTORY_MSG_151;(Dynamik) HISTORY_MSG_152;(Dynamik) - Pastelltöne @@ -426,6 +342,7 @@ HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle HISTORY_MSG_158;(Dynamikkompression)\nIntensität HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz +HISTORY_MSG_15;(L*a*b*) - Kontrast HISTORY_MSG_160;(Dynamikkompression)\nFaktor HISTORY_MSG_161;(Dynamikkompression)\nIterationen HISTORY_MSG_162;(Dynamikkompression) @@ -436,6 +353,7 @@ HISTORY_MSG_166;(Belichtung) - Zurücksetzen HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode HISTORY_MSG_168;(L*a*b*) - CC-Kurve HISTORY_MSG_169;(L*a*b*) - CH-Kurve +HISTORY_MSG_16;- HISTORY_MSG_170;(Dynamik) - HH-Kurve HISTORY_MSG_171;(L*a*b*) - LC-Kurve HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken @@ -446,6 +364,7 @@ HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell +HISTORY_MSG_17;- HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) HISTORY_MSG_181;(CIECAM02) - Buntheit (H) HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch @@ -456,6 +375,7 @@ HISTORY_MSG_186;(CIECAM02) - Algorithmus HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) +HISTORY_MSG_18;- HISTORY_MSG_190;(CIECAM02) - Sättigung (S) HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) HISTORY_MSG_192;(CIECAM02) - Farbton (H) @@ -466,6 +386,8 @@ HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus HISTORY_MSG_197;(CIECAM02) - Farbkurve HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen +HISTORY_MSG_19;(L*a*b*) - L-Kurve +HISTORY_MSG_1;(Bild geladen) HISTORY_MSG_200;(CIECAM02)\nDynamikkompression HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb @@ -475,6 +397,7 @@ HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur +HISTORY_MSG_20;(Schärfung) HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel HISTORY_MSG_211;(Grauverlaufsfilter) HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität @@ -485,6 +408,7 @@ HISTORY_MSG_216;(Schwarz/Weiß) - Grün HISTORY_MSG_217;(Schwarz/Weiß) - Blau HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün +HISTORY_MSG_21;(Schärfung) - USM\nRadius HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben @@ -495,6 +419,7 @@ HISTORY_MSG_226;(Schwarz/Weiß) - Magenta HISTORY_MSG_227;(Schwarz/Weiß) - Violett HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_22;(Schärfung) - USM\nIntensität HISTORY_MSG_230;(Schwarz/Weiß) - Modus HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp @@ -505,6 +430,7 @@ HISTORY_MSG_236;--unused-- HISTORY_MSG_237;(Schwarz/Weiß) - Mixer HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität +HISTORY_MSG_23;(Schärfung) - USM\nSchwelle HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen HISTORY_MSG_241;(Vignettierungsfilter)\nBereich HISTORY_MSG_242;(Vignettierungsfilter)\nForm @@ -515,6 +441,7 @@ HISTORY_MSG_246;(L*a*b*) - CL-Kurve HISTORY_MSG_247;(L*a*b*) - LH-Kurve HISTORY_MSG_248;(L*a*b*) - HH-Kurve HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle +HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen @@ -525,6 +452,7 @@ HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp HISTORY_MSG_257;(Farbanpassungen) HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve +HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz HISTORY_MSG_261;(Farbanpassungen)\nMethode HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz @@ -535,6 +463,7 @@ HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot +HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich @@ -545,6 +474,7 @@ HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft HISTORY_MSG_277;--unused-- HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen HISTORY_MSG_279;(Farbanpassungen)\nSchatten +HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle HISTORY_MSG_280;(Farbanpassungen)\nLichter HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle @@ -555,6 +485,7 @@ HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche +HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau @@ -565,6 +496,8 @@ HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve HISTORY_MSG_297;(Rauschreduzierung)\nQualität HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve +HISTORY_MSG_29;(Schärfung) - Methode +HISTORY_MSG_2;(Profil geladen) HISTORY_MSG_300;- HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode @@ -575,6 +508,7 @@ HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails +HISTORY_MSG_30;(Schärfung) - RLD\nRadius HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle @@ -585,6 +519,7 @@ HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich +HISTORY_MSG_31;(Schärfung) - RLD\nIntensität HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden @@ -595,6 +530,7 @@ HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün +HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße @@ -605,6 +541,7 @@ HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität +HISTORY_MSG_33;(Schärfung) - RLD\nIterationen HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene @@ -615,6 +552,7 @@ HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 +HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund @@ -625,6 +563,7 @@ HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle +HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung HISTORY_MSG_360;(Dynamikkompression)\nGamma HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich HISTORY_MSG_362;(Wavelet) - Restbild\nKompression @@ -635,6 +574,7 @@ HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode +HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve HISTORY_MSG_371;(Skalieren) - Schärfen HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius @@ -645,6 +585,7 @@ HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität +HISTORY_MSG_37;(Belichtung) - Auto HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität @@ -655,6 +596,7 @@ HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb +HISTORY_MSG_38;(Weißabgleich) - Methode HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich @@ -665,6 +607,8 @@ HISTORY_MSG_396;(Wavelet) - Kontrast HISTORY_MSG_397;(Wavelet) - Farbe HISTORY_MSG_398;(Wavelet)\nKantenschärfung HISTORY_MSG_399;(Wavelet) - Restbild +HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur +HISTORY_MSG_3;(Profil geändert) HISTORY_MSG_400;(Wavelet) - Endretusche HISTORY_MSG_401;(Wavelet) - Tönung HISTORY_MSG_402;(Wavelet)\nRauschreduzierung @@ -675,6 +619,7 @@ HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel HISTORY_MSG_407;(Retinex) - Methode HISTORY_MSG_408;(Retinex) - Radius HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_40;(Weißabgleich) - Tönung HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich HISTORY_MSG_411;(Retinex) - Intensität HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient @@ -685,6 +630,7 @@ HISTORY_MSG_416;(Retinex) HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle HISTORY_MSG_419;(Retinex) - Farbraum +HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma @@ -695,6 +641,7 @@ HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) HISTORY_MSG_427;Ausgabe-Rendering-Intent HISTORY_MSG_428;Monitor-Rendering-Intent HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen +HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient HISTORY_MSG_432;(Retinex) - Maske\nLichter @@ -705,11 +652,74 @@ HISTORY_MSG_436;(Retinex) - Maske\nRadius HISTORY_MSG_437;(Retinex) - Maske\nMethode HISTORY_MSG_438;(Retinex) - Maske\nKurve HISTORY_MSG_439;(Retinex) - Vorschau +HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur +HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius +HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz +HISTORY_MSG_46;(Farb-Rauschfilter) +HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) +HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve +HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination +HISTORY_MSG_4;(Historie durchsuchen) +HISTORY_MSG_50;(Schatten/Lichter) +HISTORY_MSG_51;(Schatten/Lichter)\nLichter +HISTORY_MSG_52;(Schatten/Lichter)\nSchatten +HISTORY_MSG_53;(Schatten/Lichter)\nTonwertbreite Lichter +HISTORY_MSG_54;(Schatten/Lichter)\nTonwertbreite Schatten +HISTORY_MSG_55;(Schatten/Lichter)\nLokaler Kontrast +HISTORY_MSG_56;(Schatten/Lichter)\nRadius +HISTORY_MSG_57;(Grobe Drehung) +HISTORY_MSG_58;(Horizontal spiegeln) +HISTORY_MSG_59;(Vertikal spiegeln) +HISTORY_MSG_5;(Belichtung) - Helligkeit +HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel +HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen +HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung +HISTORY_MSG_63;(Schnappschuss\nausgewählt) +HISTORY_MSG_64;(Ausschnitt) +HISTORY_MSG_65;(Objektivkorrektur)\nFarbsaum entfernen +HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren +HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang +HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode +HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum +HISTORY_MSG_6;(Belichtung) - Kontrast +HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil +HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil +HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität +HISTORY_MSG_73;(RGB-Kanalmixer) +HISTORY_MSG_74;(Skalieren) - Maßstab +HISTORY_MSG_75;(Skalieren) - Methode +HISTORY_MSG_76;(Exif Metadaten) +HISTORY_MSG_77;(IPTC Metadaten) +HISTORY_MSG_78;- +HISTORY_MSG_79;(Skalieren) - Breite +HISTORY_MSG_7;(Belichtung)\nSchwarzwert +HISTORY_MSG_80;(Skalieren) - Höhe +HISTORY_MSG_81;(Skalieren) +HISTORY_MSG_82;(Profil geändert) +HISTORY_MSG_83;(Schatten/Lichter)\nSchärfemaske +HISTORY_MSG_84;(Objektivkorrektur)\nPerspektive +HISTORY_MSG_85;(Objektivkorrektur)\nProfil +HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus +HISTORY_MSG_87;(Impulsrauschred.) +HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle +HISTORY_MSG_89;(Rauschreduzierung) +HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur +HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz +HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) +HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma +HISTORY_MSG_93;(Detailebenenkontrast)\nWert +HISTORY_MSG_94;(Detailebenenkontrast) +HISTORY_MSG_95;(L*a*b*) - Chromatizität +HISTORY_MSG_96;(L*a*b*) - a-Kurve +HISTORY_MSG_97;(L*a*b*) - b-Kurve +HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode +HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter +HISTORY_MSG_9;(Belichtung)\nLichterkompression HISTORY_NEWSNAPSHOT;Hinzufügen HISTORY_NEWSNAPSHOT_TOOLTIP;Taste: Alt + s HISTORY_SNAPSHOT;Schnappschuss @@ -989,15 +999,14 @@ PREFERENCES_FLATFIELDFOUND;Gefunden PREFERENCES_FLATFIELDSDIR;Weißbild-Verzeichnis PREFERENCES_FLATFIELDSHOTS;Aufnahmen PREFERENCES_FLATFIELDTEMPLATES;Vorlagen +PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FLUOF2;Fluoreszenz F2 PREFERENCES_FLUOF7;Fluoreszenz F7 -PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FORIMAGE;Für Bilddateien PREFERENCES_FORRAW;Für RAW-Dateien PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT;Gleiche Miniaturbildgröße in der Dateiverwaltung und dem Filmstreifen verwenden PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT;Unterschiedliche Miniaturbildgrößen benötigen mehr Verarbeitungszeit beim Wechsel zwischen der Dateiverwaltung und dem Editor PREFERENCES_GIMPPATH;GIMP Installationsverzeichnis -PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREY05;Yb = 05 CIE L#30 PREFERENCES_GREY10;Yb = 10 CIE L#40 PREFERENCES_GREY15;Yb = 15 CIE L#45 @@ -1005,8 +1014,9 @@ PREFERENCES_GREY18;Yb = 18 CIE L#50 PREFERENCES_GREY23;Yb = 23 CIE L#55 PREFERENCES_GREY30;Yb = 30 CIE L#60 PREFERENCES_GREY40;Yb = 40 CIE L#70 -PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) +PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREYSC18;Yb = 18 CIE L#49 +PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) PREFERENCES_GREYSCA;Automatisch PREFERENCES_HISTOGRAMPOSITIONLEFT;Histogramm linksseitig PREFERENCES_HISTOGRAMWORKING;Das Arbeitsprofil zur Darstellung des Haupthistogramms verwenden @@ -1108,6 +1118,7 @@ PREFERENCES_STARTUPIMDIR;Bildverzeichnis beim Programmstart PREFERENCES_STDAUT;Standard PREFERENCES_TAB_BROWSER;Dateiverwaltung PREFERENCES_TAB_COLORMGR;Farbmanagement +PREFERENCES_TAB_DYNAMICPROFILE;Dynamisches Profil PREFERENCES_TAB_GENERAL;Allgemein PREFERENCES_TAB_IMPROC;Bildbearbeitung PREFERENCES_TAB_PERFORMANCE;Performance & Qualität @@ -1136,6 +1147,7 @@ PROFILEPANEL_MODE_TIP;Ist der Button aktiviert, werden Teilprofile\nals vollstä PROFILEPANEL_MYPROFILES;Meine Profile PROFILEPANEL_PASTEPPASTE;Einzufügende Parameter PROFILEPANEL_PCUSTOM;Benutzerdefiniert +PROFILEPANEL_PDYNAMIC;Dynamisches Profil PROFILEPANEL_PFILE;Aus Datei PROFILEPANEL_PINTERNAL;Neutral PROFILEPANEL_PLASTSAVED;Zuletzt gespeichert @@ -1440,9 +1452,9 @@ TP_DIRPYRDENOISE_MANU;Benutzerdefiniert TP_DIRPYRDENOISE_MED;Medianfilter TP_DIRPYRDENOISE_MEDMETHOD;Medianmethode TP_DIRPYRDENOISE_MEDTYPE;Mediantyp -TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD11;Qualität TP_DIRPYRDENOISE_METHOD11_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. +TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD_TOOLTIP;Für RAW-Bilder kann entweder die RGB-\noder L*a*b*-Methode verwendet werden.\n\nFür andere Bilder wird unabhängig von der\nAuswahl immer die L*a*b*-Methode verwendet. TP_DIRPYRDENOISE_METM_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MET_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -1771,8 +1783,8 @@ TP_RETINEX_SLOPE;Gammasteigung TP_RETINEX_STRENGTH;Intensität TP_RETINEX_THRESHOLD;Schwelle TP_RETINEX_THRESHOLD_TOOLTIP;Limitiert den Bereich der Transmissionskurve. -TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 +TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 TP_RETINEX_TLABEL_TOOLTIP;Ergebnis der Transmissionskurve: Min, Max, Mittel und Sigma\nMin und Max hat Einfluss auf die Abweichung.\n\nTmin = Kleinster Wert der Transmissionskurve\nTmax = Größter Wert der Transmissionskurve TP_RETINEX_TRANF;Transmission TP_RETINEX_TRANSMISSION;Transmissionskurve @@ -1784,8 +1796,8 @@ TP_RETINEX_VIEW;Vorschau TP_RETINEX_VIEW_MASK;Maske TP_RETINEX_VIEW_METHOD_TOOLTIP;Standard: Normale Anzeige\n\nMaske: Zeigt die Maske an\n\nUnschärfemaske: Zeigt das Bild mit einem großen\nUnschärfemaskenradius an.\n\nTransmission-Auto / Fest: Zeigt die Transmissionskarte\nvor der Anwendung von Kontrast und Helligkeit an.\n\nACHTUNG: Die Maske zeigt nicht das Endergebnis, sondern\nverstärkt den Effekt um ihn besser beurteilen zu können. TP_RETINEX_VIEW_NONE;Standard -TP_RETINEX_VIEW_TRAN;Transmission - Auto TP_RETINEX_VIEW_TRAN2;Transmission - Fest +TP_RETINEX_VIEW_TRAN;Transmission - Auto TP_RETINEX_VIEW_UNSHARP;Unschärfemaske TP_RGBCURVES_BLUE;B TP_RGBCURVES_CHANNEL;Kanal @@ -1909,12 +1921,12 @@ TP_WAVELET_CURVEEDITOR_CL_TOOLTIP;Wendet eine Kontrasthelligkeitskurve\nam Ende TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Farbton als Funktion des Farbtons H = f(H) TP_WAVELET_DALL;Alle Richtungen -TP_WAVELET_DAUB;Kantenperformance +TP_WAVELET_DAUB10;D10 - mittel +TP_WAVELET_DAUB14;D14 - hoch TP_WAVELET_DAUB2;D2 - niedrig TP_WAVELET_DAUB4;D4 - Standard TP_WAVELET_DAUB6;D6 - Standard Plus -TP_WAVELET_DAUB10;D10 - mittel -TP_WAVELET_DAUB14;D14 - hoch +TP_WAVELET_DAUB;Kantenperformance TP_WAVELET_DAUB_TOOLTIP;Ändert den Daubechies-Koeffizienten:\nD4 = Standard\nD14 = Häufig bestes Ergebnis auf Kosten\nvon ca. 10% längerer Verarbeitungszeit.\n\nVerbessert die Kantenerkennung sowie die Qualität\nder ersten Waveletebene. Jedoch hängt die Qualität\nnicht ausschließlich mit diesem Koeffizienten zusammen\nund kann je nach Bild und Einsatz variieren. TP_WAVELET_DONE;Vertikal TP_WAVELET_DTHR;Diagonal @@ -1925,8 +1937,8 @@ TP_WAVELET_EDGCONT_TOOLTIP;Verschieben der Punkte nach links, verringert den Kon TP_WAVELET_EDGE;Kantenschärfung TP_WAVELET_EDGEAMPLI;Grundverstärkung TP_WAVELET_EDGEDETECT;Gradientenempfindlichkeit -TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) +TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;Schwelle der Kantenerkennung für feine Details.\nVerhindert die Schärfung von Rauschen. TP_WAVELET_EDGEDETECT_TOOLTIP;Verschieben des Reglers nach rechts erhöht die\nKantenempfindlichkeit. Die Einstellung wirkt sich\nauf den lokalen Kontrast, Kanteneinstellungen und\nRauschen aus. TP_WAVELET_EDGESENSI;Kantenempfindlichkeit @@ -2002,9 +2014,9 @@ TP_WAVELET_STREN;Intensität TP_WAVELET_STRENGTH;Intensität TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Schatten Schwelle -TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD2;Schattenebenen TP_WAVELET_THRESHOLD2_TOOLTIP;Legt die Ebene der Untergrenze (9 minus Wert)\nfür den Schatten-Luminanzbereich fest. Der\nmaximal mögliche Wert wird vom Wert der Lichter-\nebenen begrenzt.\n\nBeeinflussbare Ebenen: Untergrenze bis Ebene 9 +TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD_TOOLTIP;Legt die Ebene der Obergrenze für den Lichter\n-Luminanzbereich fest. Der Wert begrenzt die\nmaximal möglichen Schattenebenen.\n\nBeeinflussbare Ebenen: Ebene 1 bis Obergrenze TP_WAVELET_THRH;Lichter Schwelle TP_WAVELET_TILESBIG;Große Kacheln @@ -2027,6 +2039,9 @@ TP_WBALANCE_FLASH55;Leica TP_WBALANCE_FLASH60;Standard, Canon, Pentax, Olympus TP_WBALANCE_FLASH65;Nikon, Panasonic, Sony, Minolta TP_WBALANCE_FLASH_HEADER;Blitz +TP_WBALANCE_FLUO10;F10 - Philips TL85 +TP_WBALANCE_FLUO11;F11 - Philips TL84 +TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO1;F1 - Tageslicht TP_WBALANCE_FLUO2;F2 - Kaltweiß TP_WBALANCE_FLUO3;F3 - Weiß @@ -2036,9 +2051,6 @@ TP_WBALANCE_FLUO6;F6 - Weiß reduziert TP_WBALANCE_FLUO7;F7 - D65 Tageslichtsimulation TP_WBALANCE_FLUO8;F8 - D50 / Sylvania F40 Design TP_WBALANCE_FLUO9;F9 - Kaltweiß Deluxe -TP_WBALANCE_FLUO10;F10 - Philips TL85 -TP_WBALANCE_FLUO11;F11 - Philips TL84 -TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO_HEADER;Leuchtstofflampe TP_WBALANCE_GREEN;Tönung TP_WBALANCE_GTI;GTI From 9a742508f27b71cd2796ded6157efb775282e9bb Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Tue, 7 Mar 2017 01:20:43 +0100 Subject: [PATCH 147/181] Update RawTherapee-GTK3-20_.css --- rtdata/themes/RawTherapee-GTK3-20_.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index a343dd9fb..27d2b5f13 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -70,7 +70,10 @@ headerbar { border-radius: 5px 5px 0 0; min-height: 26px; padding: 1px 5px 0; - margin: 0 0 1px 0; + margin: 0; +} +.csd #MainNotebook > header.top { + border-top: 1px solid #484848; } /* Window state */ From 61b913f7f9ea6cadf4db63ba4d3b748080e0cdb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Wed, 8 Mar 2017 20:23:57 +0100 Subject: [PATCH 148/181] Fix `rtengine::min()` for NaNs (#3742) Also fix `LuminanceToneCurve::Apply()`. Kudos to @heckflosse! --- rtengine/curves.h | 5 +++-- rtengine/rt_math.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rtengine/curves.h b/rtengine/curves.h index 348772075..cd4384bb4 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -994,8 +994,9 @@ inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const assert (lutToneCurve); float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; - float newLuminance = lutToneCurve[currLuminance]; - float coef = newLuminance / currLuminance; + const float newLuminance = lutToneCurve[currLuminance]; + currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; + const float coef = newLuminance / currLuminance; r = LIM(r * coef, 0.f, 65535.f); g = LIM(g * coef, 0.f, 65535.f); b = LIM(b * coef, 0.f, 65535.f); diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index e1c01a94f..6e1138476 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -42,7 +42,7 @@ constexpr const T& min(const T& a) template constexpr const T& min(const T& a, const T& b) { - return a < b ? a : b; + return b < a ? b : a; } template From dfcab20b35714e9a9180535e9d7d91a3a6618945 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Wed, 8 Mar 2017 20:55:20 +0100 Subject: [PATCH 149/181] Language files updated. --- rtdata/languages/Catala | 11 + rtdata/languages/Chinese (Simplified) | 11 + rtdata/languages/Chinese (Traditional) | 11 + rtdata/languages/Czech | 11 + rtdata/languages/Dansk | 11 + rtdata/languages/Deutsch | 793 +++++++++--------- rtdata/languages/English (UK) | 11 + rtdata/languages/English (US) | 11 + rtdata/languages/Espanol | 11 + rtdata/languages/Euskara | 11 + rtdata/languages/Francais | 11 + rtdata/languages/Greek | 11 + rtdata/languages/Hebrew | 11 + rtdata/languages/Italiano | 11 + rtdata/languages/Japanese | 11 + rtdata/languages/Latvian | 11 + rtdata/languages/Magyar | 11 + rtdata/languages/Nederlands | 11 + rtdata/languages/Norsk BM | 11 + rtdata/languages/Polish | 11 + rtdata/languages/Polish (Latin Characters) | 11 + rtdata/languages/Portugues (Brasil) | 11 + rtdata/languages/Russian | 11 + rtdata/languages/Serbian (Cyrilic Characters) | 11 + rtdata/languages/Serbian (Latin Characters) | 11 + rtdata/languages/Slovak | 11 + rtdata/languages/Suomi | 11 + rtdata/languages/Swedish | 11 + rtdata/languages/Turkish | 11 + rtdata/languages/default | 22 +- 30 files changed, 716 insertions(+), 407 deletions(-) diff --git a/rtdata/languages/Catala b/rtdata/languages/Catala index 991d99479..df7cc515d 100644 --- a/rtdata/languages/Catala +++ b/rtdata/languages/Catala @@ -972,6 +972,15 @@ ZOOMPANEL_ZOOMOUT;Allunya\nDrecera: - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels @@ -1437,6 +1446,7 @@ ZOOMPANEL_ZOOMOUT;Allunya\nDrecera: - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles @@ -1451,6 +1461,7 @@ ZOOMPANEL_ZOOMOUT;Allunya\nDrecera: - !PROFILEPANEL_GLOBALPROFILES;Bundled profiles !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROGRESSBAR_NOIMAGES;No images found !PROGRESSBAR_SNAPSHOT_ADDED;Snapshot added diff --git a/rtdata/languages/Chinese (Simplified) b/rtdata/languages/Chinese (Simplified) index af56b7dd1..01dd62cdf 100644 --- a/rtdata/languages/Chinese (Simplified) +++ b/rtdata/languages/Chinese (Simplified) @@ -802,6 +802,15 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !CURVEEDITOR_PARAMETRIC;Parametric !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXIFFILTER_EXPOSURECOMPENSATION;Exposure compensation (EV) @@ -1399,6 +1408,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles @@ -1417,6 +1427,7 @@ ZOOMPANEL_ZOOMOUT;缩放拉远\n快捷键: - !PROFILEPANEL_LOADPPASTE;Parameters to load !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_PROCESSING_PROFILESAVED;Processing profile saved diff --git a/rtdata/languages/Chinese (Traditional) b/rtdata/languages/Chinese (Traditional) index 65abab6de..5d188ba67 100644 --- a/rtdata/languages/Chinese (Traditional) +++ b/rtdata/languages/Chinese (Traditional) @@ -458,6 +458,15 @@ TP_WBALANCE_TEMPERATURE;色溫 !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1209,6 +1218,7 @@ TP_WBALANCE_TEMPERATURE;色溫 !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1232,6 +1242,7 @@ TP_WBALANCE_TEMPERATURE;色溫 !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Czech b/rtdata/languages/Czech index 08efca6cb..4a6aa685f 100644 --- a/rtdata/languages/Czech +++ b/rtdata/languages/Czech @@ -2074,6 +2074,17 @@ ZOOMPANEL_ZOOMOUT;Oddálit\nZkratka: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !HISTORY_MSG_444;WB - Temp bias +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules +!PROFILEPANEL_PDYNAMIC;Dynamic !TP_WBALANCE_TEMPBIAS;AWB temperature bias !TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Dansk b/rtdata/languages/Dansk index 0726bf930..7ace0541a 100644 --- a/rtdata/languages/Dansk +++ b/rtdata/languages/Dansk @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;Temperatur !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 8d91c9d38..1d1c8275e 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -162,13 +162,13 @@ FILEBROWSER_OPENDEFAULTVIEWER;Windows Standard-Betracher (stapelverarbeitet) FILEBROWSER_PARTIALPASTEPROFILE;Profil selektiv einfügen FILEBROWSER_PASTEPROFILE;Profil einfügen FILEBROWSER_POPUPCANCELJOB;Job abbrechen +FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOLORLABEL0;Markierung: Ohne FILEBROWSER_POPUPCOLORLABEL1;Markierung: Rot FILEBROWSER_POPUPCOLORLABEL2;Markierung: Gelb FILEBROWSER_POPUPCOLORLABEL3;Markierung: Grün FILEBROWSER_POPUPCOLORLABEL4;Markierung: Blau FILEBROWSER_POPUPCOLORLABEL5;Markierung: Violett -FILEBROWSER_POPUPCOLORLABEL;Farbmarkierung FILEBROWSER_POPUPCOPYTO;Kopieren nach... FILEBROWSER_POPUPFILEOPERATIONS;Dateioperationen FILEBROWSER_POPUPMOVEEND;An das Ende der Warteschlange verschieben @@ -179,13 +179,13 @@ FILEBROWSER_POPUPOPENINEDITOR;Im Editor öffnen FILEBROWSER_POPUPPROCESS;Zur Warteschlange hinzufügen FILEBROWSER_POPUPPROCESSFAST;Zur Warteschlange hinzufügen (Schnelles Exportieren) FILEBROWSER_POPUPPROFILEOPERATIONS;Profiloperationen +FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPRANK0;Nicht bewertet FILEBROWSER_POPUPRANK1;Bewertung 1 * FILEBROWSER_POPUPRANK2;Bewertung 2 ** FILEBROWSER_POPUPRANK3;Bewertung 3 *** FILEBROWSER_POPUPRANK4;Bewertung 4 **** FILEBROWSER_POPUPRANK5;Bewertung 5 ***** -FILEBROWSER_POPUPRANK;Bewertung FILEBROWSER_POPUPREMOVE;Löschen FILEBROWSER_POPUPREMOVEINCLPROC;Löschen (auch Resultate der Stapelverarbeitung) FILEBROWSER_POPUPRENAME;Umbenennen @@ -277,394 +277,55 @@ HISTORY_CUSTOMCURVE;Benutzerdefiniert HISTORY_DELSNAPSHOT;Entfernen HISTORY_FROMCLIPBOARD;Aus der Zwischenablage HISTORY_LABEL;Historie -HISTORY_MSG_100;(Belichtung) - Sättigung -HISTORY_MSG_101;(HSV) - Farbton (H) -HISTORY_MSG_102;(HSV) - Sättigung (S) -HISTORY_MSG_103;(HSV) - Dynamik (V) -HISTORY_MSG_104;(HSV) -HISTORY_MSG_105;(Farbsaum entfernen) -HISTORY_MSG_106;(Farbsaum entfernen)\nRadius -HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle -HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle -HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen -HISTORY_MSG_10;(Belichtung)\nSchattenkompression -HISTORY_MSG_110;(Skalieren) - Anwenden auf: -HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden -HISTORY_MSG_112;--unused-- -HISTORY_MSG_113;(L*a*b*) - Hautfarbtöne\nschützen -HISTORY_MSG_114;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Iterationen -HISTORY_MSG_115;(Sensor-Matrix)\nFarbinterpolation\nFalschfarbenunterdrückung -HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung -HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot -HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau -HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter -HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 -HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich -HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur -HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl -HISTORY_MSG_123;(Dunkelbild) - Datei -HISTORY_MSG_124;(Weißpunkt)\nKorrekturfaktor -HISTORY_MSG_125;(Belichtungskorrektur)\nLichter schützen -HISTORY_MSG_126;(Weißbild) - Datei -HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl -HISTORY_MSG_128;(Weißbild)\nUnschärferadius -HISTORY_MSG_129;(Weißbild) - Unschärfetyp -HISTORY_MSG_12;(Belichtung) - Auto -HISTORY_MSG_130;(Autom. Verzeichnung) -HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz -HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz -HISTORY_MSG_133;(Farbmanagement)\nAusgabeprofil\nAusgabe-Gamma -HISTORY_MSG_134;(Farbmanagement)\nAusgabeprofil\nGamma -HISTORY_MSG_135;(Farbmanagement)\nAusgabeprofil\nFreies Gamma -HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) -HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 -HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot -HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau -HISTORY_MSG_13;(Belichtung) - Clip-Faktor -HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 -HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen -HISTORY_MSG_142;(Kantenschärfung)\nIterationen -HISTORY_MSG_143;(Kantenschärfung)\nIntensität -HISTORY_MSG_144;(Mikrokontrast)\nIntensität -HISTORY_MSG_145;(Mikrokontrast)\nGleichmäßigkeit -HISTORY_MSG_146;(Kantenschärfung) -HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz -HISTORY_MSG_148;(Mikrokontrast) -HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix -HISTORY_MSG_14;(L*a*b*) - Helligkeit -HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) -HISTORY_MSG_151;(Dynamik) -HISTORY_MSG_152;(Dynamik) - Pastelltöne -HISTORY_MSG_153;(Dynamik)\nGesättigte Töne -HISTORY_MSG_154;(Dynamik)\nHautfarbtöne schützen -HISTORY_MSG_155;(Dynamik)\nFarbverschiebungen\nvermeiden -HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln -HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle -HISTORY_MSG_158;(Dynamikkompression)\nIntensität -HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz -HISTORY_MSG_15;(L*a*b*) - Kontrast -HISTORY_MSG_160;(Dynamikkompression)\nFaktor -HISTORY_MSG_161;(Dynamikkompression)\nIterationen -HISTORY_MSG_162;(Dynamikkompression) -HISTORY_MSG_163;(RGB-Kurven) - Rot -HISTORY_MSG_164;(RGB-Kurven) - Grün -HISTORY_MSG_165;(RGB-Kurven) - Blau -HISTORY_MSG_166;(Belichtung) - Zurücksetzen -HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode -HISTORY_MSG_168;(L*a*b*) - CC-Kurve -HISTORY_MSG_169;(L*a*b*) - CH-Kurve -HISTORY_MSG_16;- -HISTORY_MSG_170;(Dynamik) - HH-Kurve -HISTORY_MSG_171;(L*a*b*) - LC-Kurve -HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken -HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails -HISTORY_MSG_174;(CIECAM02) -HISTORY_MSG_175;(CIECAM02) - Szene\nCAT02-Adaptation -HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung -HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke -HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke -HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell -HISTORY_MSG_17;- -HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) -HISTORY_MSG_181;(CIECAM02) - Buntheit (H) -HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch -HISTORY_MSG_183;(CIECAM02) - Kontrast (J) -HISTORY_MSG_184;(CIECAM02) - Szene\nDunkle Umgebung -HISTORY_MSG_185;(CIECAM02)\nBetrachtungsbed.\nGamutkontrolle -HISTORY_MSG_186;(CIECAM02) - Algorithmus -HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen -HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) -HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) -HISTORY_MSG_18;- -HISTORY_MSG_190;(CIECAM02) - Sättigung (S) -HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) -HISTORY_MSG_192;(CIECAM02) - Farbton (H) -HISTORY_MSG_193;(CIECAM02)\nTonwertkurve 1 -HISTORY_MSG_194;(CIECAM02)\nTonwertkurve 2 -HISTORY_MSG_195;(CIECAM02)\nTonwertkurve 1 - Modus -HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus -HISTORY_MSG_197;(CIECAM02) - Farbkurve -HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus -HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen -HISTORY_MSG_19;(L*a*b*) - L-Kurve HISTORY_MSG_1;(Bild geladen) -HISTORY_MSG_200;(CIECAM02)\nDynamikkompression -HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün -HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb -HISTORY_MSG_203;(Rauschreduzierung)\nMethode -HISTORY_MSG_204;(Sensor Bayer-Matrix)\nFarbinterpolation\nLMMSE-Verbesserung -HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter -HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke -HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve -HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur -HISTORY_MSG_20;(Schärfung) -HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel -HISTORY_MSG_211;(Grauverlaufsfilter) -HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität -HISTORY_MSG_213;(Vignettierungsfilter) -HISTORY_MSG_214;(Schwarz/Weiß) -HISTORY_MSG_215;(Schwarz/Weiß) - Rot -HISTORY_MSG_216;(Schwarz/Weiß) - Grün -HISTORY_MSG_217;(Schwarz/Weiß) - Blau -HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot -HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün -HISTORY_MSG_21;(Schärfung) - USM\nRadius -HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau -HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter -HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben -HISTORY_MSG_223;(Schwarz/Weiß) - Orange -HISTORY_MSG_224;(Schwarz/Weiß) - Gelb -HISTORY_MSG_225;(Schwarz/Weiß) - Cyan -HISTORY_MSG_226;(Schwarz/Weiß) - Magenta -HISTORY_MSG_227;(Schwarz/Weiß) - Violett -HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer -HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer -HISTORY_MSG_22;(Schärfung) - USM\nIntensität -HISTORY_MSG_230;(Schwarz/Weiß) - Modus -HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve -HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp -HISTORY_MSG_233;(Schwarz/Weiß)\n“Danach“-Kurve -HISTORY_MSG_234;(Schwarz/Weiß)\n“Danach“-Kurventyp -HISTORY_MSG_235;(Schwarz/Weiß)\nAuto-Kanalmixer -HISTORY_MSG_236;--unused-- -HISTORY_MSG_237;(Schwarz/Weiß) - Mixer -HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich -HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität -HISTORY_MSG_23;(Schärfung) - USM\nSchwelle -HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen -HISTORY_MSG_241;(Vignettierungsfilter)\nBereich -HISTORY_MSG_242;(Vignettierungsfilter)\nForm -HISTORY_MSG_243;(Objektivkorrektur)\nVignettierung - Radius -HISTORY_MSG_244;(Objektivkorrektur)\nVignettierung - Faktor -HISTORY_MSG_245;(Objektivkorrektur)\nVignettierung - Zentrum -HISTORY_MSG_246;(L*a*b*) - CL-Kurve -HISTORY_MSG_247;(L*a*b*) - LH-Kurve -HISTORY_MSG_248;(L*a*b*) - HH-Kurve -HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle -HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen -HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung -HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus -HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen -HISTORY_MSG_253;(Detailebenenkontrast)\nArtefakte reduzieren -HISTORY_MSG_254;(Detailebenenkontrast)\nHautfarbton -HISTORY_MSG_255;(Rauschreduzierung)\nMedianfilter -HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_257;(Farbanpassungen) -HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve -HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve -HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius -HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz -HISTORY_MSG_261;(Farbanpassungen)\nMethode -HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz -HISTORY_MSG_263;(Farbanpassungen)\nSchatten - Blau / Rot -HISTORY_MSG_264;(Farbanpassungen)\nSchatten - Cyan / Grün -HISTORY_MSG_265;(Farbanpassungen)\nSchatten - Gelb / Blau -HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot -HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün -HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau -HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot -HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz -HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün -HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau -HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich -HISTORY_MSG_273;(Farbanpassungen)\nZurücksetzen -HISTORY_MSG_274;(Farbanpassungen)\nSättigung Schatten -HISTORY_MSG_275;(Farbanpassungen)\nSättigung Lichter -HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft -HISTORY_MSG_277;--unused-- -HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen -HISTORY_MSG_279;(Farbanpassungen)\nSchatten -HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle -HISTORY_MSG_280;(Farbanpassungen)\nLichter -HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität -HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle -HISTORY_MSG_283;(Farbanpassungen)\nIntensität -HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch -HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode -HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen -HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche -HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche -HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität -HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot -HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün -HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau -HISTORY_MSG_293;(Filmsimulation) -HISTORY_MSG_294;(Filmsimulation)\nIntensität -HISTORY_MSG_295;(Filmsimulation) - Film -HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve -HISTORY_MSG_297;(Rauschreduzierung)\nQualität -HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter -HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve -HISTORY_MSG_29;(Schärfung) - Methode HISTORY_MSG_2;(Profil geladen) -HISTORY_MSG_300;- -HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle -HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode -HISTORY_MSG_303;(Rauschreduzierung)\nChrominanz - Methode -HISTORY_MSG_304;(Wavelet)\nKontrastebenen -HISTORY_MSG_305;(Wavelet) -HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene -HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung -HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung -HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails -HISTORY_MSG_30;(Schärfung) - RLD\nRadius -HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen -HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen -HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle -HISTORY_MSG_313;(Wavelet) - Farbe\nEbenengrenze -HISTORY_MSG_314;(Wavelet) - Gamut\nArtefakte reduzieren -HISTORY_MSG_315;(Wavelet) - Restbild\nKontrast -HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen -HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton -HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen -HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich -HISTORY_MSG_31;(Schärfung) - RLD\nIntensität -HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich -HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen -HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden -HISTORY_MSG_323;(Wavelet)\nKantenschärfung\nLokale Kontrastkurve -HISTORY_MSG_324;(Wavelet) - Farbe\nPastellfarben -HISTORY_MSG_325;(Wavelet) - Farbe\nGesättigte Farben -HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode -HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf -HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung -HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün -HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung -HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb -HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra -HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße -HISTORY_MSG_333;(Wavelet) - Restbild\nSchatten -HISTORY_MSG_334;(Wavelet) - Restbild\nBuntheit -HISTORY_MSG_335;(Wavelet) - Restbild\nLichter -HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle -HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton -HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius -HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität -HISTORY_MSG_33;(Schärfung) - RLD\nIterationen -HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität -HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance -HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene -HISTORY_MSG_343;(Wavelet) - Farbe\nFarbebenen -HISTORY_MSG_344;(Wavelet)\nFarbmethode\nRegler/Kurve -HISTORY_MSG_345;(Wavelet)\nKantenschärfung\nLokaler Kontrast -HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode -HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 -HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 -HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 -HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung -HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung -HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve -HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund -HISTORY_MSG_353;(Wavelet)\nKantenschärfung\nGradientenempfindlichkeit -HISTORY_MSG_354;(Wavelet)\nKantenschärfung\nErweiterter Algorithmus -HISTORY_MSG_355;(Wavelet)\nKantenschärfung\nSchwelle niedrig -HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch -HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen -HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve -HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle -HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung -HISTORY_MSG_360;(Dynamikkompression)\nGamma -HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich -HISTORY_MSG_362;(Wavelet) - Restbild\nKompression -HISTORY_MSG_363;(Wavelet) - Restbild\nKompression - Intensität -HISTORY_MSG_364;(Wavelet) - Endretusche\nKontrastausgleich -HISTORY_MSG_365;(Wavelet) - Endretusche\nDelta-Kontrastausgleich -HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression -HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve -HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve -HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode -HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur -HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve -HISTORY_MSG_371;(Skalieren) - Schärfen -HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius -HISTORY_MSG_373;(Skalieren) - Schärfen\nUSM - Intensität -HISTORY_MSG_374;(Skalieren) - Schärfen\nUSM - Schwelle -HISTORY_MSG_375;(Skalieren) - Schärfen\nUSM - Nur Kanten\nschärfen -HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius -HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz -HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle -HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität -HISTORY_MSG_37;(Belichtung) - Auto -HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode -HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius -HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität -HISTORY_MSG_383;(Skalieren) - Schärfen\nRLD - Dämpfung -HISTORY_MSG_384;(Skalieren) - Schärfen\nRLD - Iterationen -HISTORY_MSG_385;(Wavelet) - Restbild\nFarbausgleich -HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan -HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb -HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan -HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb -HISTORY_MSG_38;(Weißabgleich) - Methode -HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan -HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb -HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich -HISTORY_MSG_393;(Farbmanagement)\nEingangsfarbprofil\nDCP - Look-Tabelle -HISTORY_MSG_394;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basisbelichtung -HISTORY_MSG_395;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basistabelle -HISTORY_MSG_396;(Wavelet) - Kontrast -HISTORY_MSG_397;(Wavelet) - Farbe -HISTORY_MSG_398;(Wavelet)\nKantenschärfung -HISTORY_MSG_399;(Wavelet) - Restbild -HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur HISTORY_MSG_3;(Profil geändert) -HISTORY_MSG_400;(Wavelet) - Endretusche -HISTORY_MSG_401;(Wavelet) - Tönung -HISTORY_MSG_402;(Wavelet)\nRauschreduzierung -HISTORY_MSG_403;(Wavelet)\nKantenschärfung\nKantenempfindlichkeit -HISTORY_MSG_404;(Wavelet)\nKantenschärfung\nGrundverstärkung -HISTORY_MSG_405;(Wavelet)\nRauschreduzierung\nEbene 4 -HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel -HISTORY_MSG_407;(Retinex) - Methode -HISTORY_MSG_408;(Retinex) - Radius -HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_4;(Historie durchsuchen) +HISTORY_MSG_5;(Belichtung) - Helligkeit +HISTORY_MSG_6;(Belichtung) - Kontrast +HISTORY_MSG_7;(Belichtung)\nSchwarzwert +HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur +HISTORY_MSG_9;(Belichtung)\nLichterkompression +HISTORY_MSG_10;(Belichtung)\nSchattenkompression +HISTORY_MSG_11;(Belichtung)\nTonwertkurve 1 +HISTORY_MSG_12;(Belichtung) - Auto +HISTORY_MSG_13;(Belichtung) - Clip-Faktor +HISTORY_MSG_14;(L*a*b*) - Helligkeit +HISTORY_MSG_15;(L*a*b*) - Kontrast +HISTORY_MSG_16;- +HISTORY_MSG_17;- +HISTORY_MSG_18;- +HISTORY_MSG_19;(L*a*b*) - L-Kurve +HISTORY_MSG_20;(Schärfung) +HISTORY_MSG_21;(Schärfung) - USM\nRadius +HISTORY_MSG_22;(Schärfung) - USM\nIntensität +HISTORY_MSG_23;(Schärfung) - USM\nSchwelle +HISTORY_MSG_24;(Schärfung) - USM\nNur Kanten schärfen +HISTORY_MSG_25;(Schärfung) - USM\nKantenschärfung\nRadius +HISTORY_MSG_26;(Schärfung) - USM\nKantenschärfung\nKantentoleranz +HISTORY_MSG_27;(Schärfung) - USM\nHalokontrolle +HISTORY_MSG_28;(Schärfung) - USM\nHalokontrolle - Intensität +HISTORY_MSG_29;(Schärfung) - Methode +HISTORY_MSG_30;(Schärfung) - RLD\nRadius +HISTORY_MSG_31;(Schärfung) - RLD\nIntensität +HISTORY_MSG_32;(Schärfung) - RLD\nDämpfung +HISTORY_MSG_33;(Schärfung) - RLD\nIterationen +HISTORY_MSG_34;(Objektivkorrektur)\nProfil - Verzeichnung +HISTORY_MSG_35;(Objektivkorrektur)\nProfil - Vignettierung +HISTORY_MSG_36;(Objektivkorrektur)\nProfil - CA-Korrektur +HISTORY_MSG_37;(Belichtung) - Auto +HISTORY_MSG_38;(Weißabgleich) - Methode +HISTORY_MSG_39;(Weißabgleich)\nFarbtemperatur HISTORY_MSG_40;(Weißabgleich) - Tönung -HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich -HISTORY_MSG_411;(Retinex) - Intensität -HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient -HISTORY_MSG_413;(Retinex) - Kontrast -HISTORY_MSG_414;(Retinex) - Einstellungen\nKorrekturen\nLuminanz(L) - L*a*b* -HISTORY_MSG_415;(Retinex) - Einstellungen\nTransmission\nTransmissionskurve -HISTORY_MSG_416;(Retinex) -HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter -HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle -HISTORY_MSG_419;(Retinex) - Farbraum HISTORY_MSG_41;(Belichtung)\nTonwertkurve 1 - Modus -HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve -HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur -HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma -HISTORY_MSG_423;(Retinex) - Einstellungen\nGammasteigung -HISTORY_MSG_424;(Retinex) - Einstellungen\nHL-Schwelle -HISTORY_MSG_425;(Retinex) - Einstellungen\nBasis-Logarithmus -HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) -HISTORY_MSG_427;Ausgabe-Rendering-Intent -HISTORY_MSG_428;Monitor-Rendering-Intent -HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen HISTORY_MSG_42;(Belichtung)\nTonwertkurve 2 -HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient -HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient -HISTORY_MSG_432;(Retinex) - Maske\nLichter -HISTORY_MSG_433;(Retinex) - Maske\nTonwertbreite Lichter -HISTORY_MSG_434;(Retinex) - Maske\nSchatten -HISTORY_MSG_435;(Retinex) - Maske\nTonwertbreite Schatten -HISTORY_MSG_436;(Retinex) - Maske\nRadius -HISTORY_MSG_437;(Retinex) - Maske\nMethode -HISTORY_MSG_438;(Retinex) - Maske\nKurve -HISTORY_MSG_439;(Retinex) - Vorschau HISTORY_MSG_43;(Belichtung)\nTonwertkurve 2 - Modus -HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge -HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung -HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung -HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation -HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur HISTORY_MSG_44;(Luminanz-Rauschfilter)\nRadius HISTORY_MSG_45;(Luminanz-Rauschfilter)\nKantentoleranz HISTORY_MSG_46;(Farb-Rauschfilter) HISTORY_MSG_47;(ICC Lichter aus Matrix\nüberlagern) HISTORY_MSG_48;(Farbmanagement)\nEingangsfarbprofil\nDCP - Tonwertkurve HISTORY_MSG_49;(Farbmanagement)\nEingangsfarbprofil\nDCP - Illumination -HISTORY_MSG_4;(Historie durchsuchen) HISTORY_MSG_50;(Schatten/Lichter) HISTORY_MSG_51;(Schatten/Lichter)\nLichter HISTORY_MSG_52;(Schatten/Lichter)\nSchatten @@ -675,7 +336,6 @@ HISTORY_MSG_56;(Schatten/Lichter)\nRadius HISTORY_MSG_57;(Grobe Drehung) HISTORY_MSG_58;(Horizontal spiegeln) HISTORY_MSG_59;(Vertikal spiegeln) -HISTORY_MSG_5;(Belichtung) - Helligkeit HISTORY_MSG_60;(Objektivkorrektur)\nDrehen - Winkel HISTORY_MSG_61;(Objektivkorrektur)\nAuto-Füllen HISTORY_MSG_62;(Objektivkorrektur)\nVerzeichnung @@ -686,7 +346,6 @@ HISTORY_MSG_66;(Belichtung)\nLichter rekonstruieren HISTORY_MSG_67;(Belichtung)\nLichterkompression\nUmfang HISTORY_MSG_68;(Belichtung)\nLichterkompression\nMethode HISTORY_MSG_69;(Farbmanagement)\nArbeitsfarbraum -HISTORY_MSG_6;(Belichtung) - Kontrast HISTORY_MSG_70;(Farbmanagement)\nAusgabeprofil HISTORY_MSG_71;(Farbmanagement)\nEingangsfarbprofil HISTORY_MSG_72;(Objektivkorrektur)\nVignettierung - Intensität @@ -697,7 +356,6 @@ HISTORY_MSG_76;(Exif Metadaten) HISTORY_MSG_77;(IPTC Metadaten) HISTORY_MSG_78;- HISTORY_MSG_79;(Skalieren) - Breite -HISTORY_MSG_7;(Belichtung)\nSchwarzwert HISTORY_MSG_80;(Skalieren) - Höhe HISTORY_MSG_81;(Skalieren) HISTORY_MSG_82;(Profil geändert) @@ -708,7 +366,6 @@ HISTORY_MSG_86;(RGB-Kurven)\nHelligkeitsmodus HISTORY_MSG_87;(Impulsrauschred.) HISTORY_MSG_88;(Impulsrauschred.)\nSchwelle HISTORY_MSG_89;(Rauschreduzierung) -HISTORY_MSG_8;(Belichtung)\nBelichtungskorrektur HISTORY_MSG_90;(Rauschreduzierung)\nLuminanz HISTORY_MSG_91;(Rauschreduzierung)\nChrominanz (Master) HISTORY_MSG_92;(Rauschreduzierung)\nChrominanz - Gamma @@ -719,7 +376,350 @@ HISTORY_MSG_96;(L*a*b*) - a-Kurve HISTORY_MSG_97;(L*a*b*) - b-Kurve HISTORY_MSG_98;(Sensor-Matrix)\nFarbinterpolation\nMethode HISTORY_MSG_99;(Vorverarbeitung)\nHot-Pixel-Filter -HISTORY_MSG_9;(Belichtung)\nLichterkompression +HISTORY_MSG_100;(Belichtung) - Sättigung +HISTORY_MSG_101;(HSV) - Farbton (H) +HISTORY_MSG_102;(HSV) - Sättigung (S) +HISTORY_MSG_103;(HSV) - Dynamik (V) +HISTORY_MSG_104;(HSV) +HISTORY_MSG_105;(Farbsaum entfernen) +HISTORY_MSG_106;(Farbsaum entfernen)\nRadius +HISTORY_MSG_107;(Farbsaum entfernen)\nSchwelle +HISTORY_MSG_108;(Belichtung)\nLichterkompression\nSchwelle +HISTORY_MSG_109;(Skalieren) - Begrenzungsrahmen +HISTORY_MSG_110;(Skalieren) - Anwenden auf: +HISTORY_MSG_111;(L*a*b*) - Farbverschiebung\nvermeiden +HISTORY_MSG_112;--unused-- +HISTORY_MSG_113;(L*a*b*) - Hautfarbtöne\nschützen +HISTORY_MSG_114;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Iterationen +HISTORY_MSG_115;(Sensor-Matrix)\nFarbinterpolation\nFalschfarbenunterdrückung +HISTORY_MSG_116;(Sensor Bayer-Matrix)\nFarbinterpolation\nDCB-Verbesserung +HISTORY_MSG_117;(Sensor Bayer-Matrix)\nChromatische Aberration\nRot +HISTORY_MSG_118;(Sensor Bayer-Matrix)\nChromatische Aberration\nBlau +HISTORY_MSG_119;(Sensor Bayer-Matrix)\nVorverarbeitung\nZeilenrauschfilter +HISTORY_MSG_120;(Sensor Bayer-Matrix)\nVorverarbeitung\nGrün-Ausgleich +HISTORY_MSG_121;(Sensor Bayer-Matrix)\nChromatische Aberration\nAutomatische Korrektur +HISTORY_MSG_122;(Dunkelbild)\nAutomatische Auswahl +HISTORY_MSG_123;(Dunkelbild) - Datei +HISTORY_MSG_124;(Weißpunkt)\nKorrekturfaktor +HISTORY_MSG_125;(Belichtungskorrektur)\nLichter schützen +HISTORY_MSG_126;(Weißbild) - Datei +HISTORY_MSG_127;(Weißbild)\nAutomatische Auswahl +HISTORY_MSG_128;(Weißbild)\nUnschärferadius +HISTORY_MSG_129;(Weißbild) - Unschärfetyp +HISTORY_MSG_130;(Autom. Verzeichnung) +HISTORY_MSG_131;(Rauschreduzierung)\nLuminanz +HISTORY_MSG_132;(Rauschreduzierung)\nChrominanz +HISTORY_MSG_133;(Farbmanagement)\nAusgabeprofil\nAusgabe-Gamma +HISTORY_MSG_134;(Farbmanagement)\nAusgabeprofil\nGamma +HISTORY_MSG_135;(Farbmanagement)\nAusgabeprofil\nFreies Gamma +HISTORY_MSG_136;(Farbmanagement)\nAusgabeprofil\nGradient (linear) +HISTORY_MSG_137;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 1 +HISTORY_MSG_138;(Sensor Bayer-Matrix)\nSchwarzpunkt - Rot +HISTORY_MSG_139;(Sensor Bayer-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_140;(Sensor Bayer-Matrix)\nSchwarzpunkt - Grün 2 +HISTORY_MSG_141;(Sensor Bayer-Matrix)\nSchwarzpunkt\nGrün-Werte angleichen +HISTORY_MSG_142;(Kantenschärfung)\nIterationen +HISTORY_MSG_143;(Kantenschärfung)\nIntensität +HISTORY_MSG_144;(Mikrokontrast)\nIntensität +HISTORY_MSG_145;(Mikrokontrast)\nGleichmäßigkeit +HISTORY_MSG_146;(Kantenschärfung) +HISTORY_MSG_147;(Kantenschärfung)\nNur Luminanz +HISTORY_MSG_148;(Mikrokontrast) +HISTORY_MSG_149;(Mikrokontrast)\n3×3-Matrix +HISTORY_MSG_150;(Artefakt-/Rauschred.\nnach Farbinterpolation) +HISTORY_MSG_151;(Dynamik) +HISTORY_MSG_152;(Dynamik) - Pastelltöne +HISTORY_MSG_153;(Dynamik)\nGesättigte Töne +HISTORY_MSG_154;(Dynamik)\nHautfarbtöne schützen +HISTORY_MSG_155;(Dynamik)\nFarbverschiebungen\nvermeiden +HISTORY_MSG_156;(Dynamik)\nPastell und gesättigte\nTöne koppeln +HISTORY_MSG_157;(Dynamik)\nPastell/gesättigte Töne\nSchwelle +HISTORY_MSG_158;(Dynamikkompression)\nIntensität +HISTORY_MSG_159;(Dynamikkompression)\nKantenschutz +HISTORY_MSG_160;(Dynamikkompression)\nFaktor +HISTORY_MSG_161;(Dynamikkompression)\nIterationen +HISTORY_MSG_162;(Dynamikkompression) +HISTORY_MSG_163;(RGB-Kurven) - Rot +HISTORY_MSG_164;(RGB-Kurven) - Grün +HISTORY_MSG_165;(RGB-Kurven) - Blau +HISTORY_MSG_166;(Belichtung) - Zurücksetzen +HISTORY_MSG_167;(Sensor-Matrix)\nFarbinterpolation\nMethode +HISTORY_MSG_168;(L*a*b*) - CC-Kurve +HISTORY_MSG_169;(L*a*b*) - CH-Kurve +HISTORY_MSG_170;(Dynamik) - HH-Kurve +HISTORY_MSG_171;(L*a*b*) - LC-Kurve +HISTORY_MSG_172;(L*a*b*) - LC-Kurve\nbeschränken +HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails +HISTORY_MSG_174;(CIECAM02) +HISTORY_MSG_175;(CIECAM02) - Szene\nCAT02-Adaptation +HISTORY_MSG_176;(CIECAM02)\nBetrachtungsbed.\nUmgebung +HISTORY_MSG_177;(CIECAM02) - Szene\nLeuchtstärke +HISTORY_MSG_178;(CIECAM02)\nBetrachtungsbed.\nLeuchtstärke +HISTORY_MSG_179;(CIECAM02) - Szene\nWeißpunktmodell +HISTORY_MSG_180;(CIECAM02) - Helligkeit (J) +HISTORY_MSG_181;(CIECAM02) - Buntheit (H) +HISTORY_MSG_182;(CIECAM02) - Szene\nCAT02-Automatisch +HISTORY_MSG_183;(CIECAM02) - Kontrast (J) +HISTORY_MSG_184;(CIECAM02) - Szene\nDunkle Umgebung +HISTORY_MSG_185;(CIECAM02)\nBetrachtungsbed.\nGamutkontrolle +HISTORY_MSG_186;(CIECAM02) - Algorithmus +HISTORY_MSG_187;(CIECAM02) - Hautfarbtöne\nschützen +HISTORY_MSG_188;(CIECAM02) - Helligkeit (Q) +HISTORY_MSG_189;(CIECAM02) - Kontrast (Q) +HISTORY_MSG_190;(CIECAM02) - Sättigung (S) +HISTORY_MSG_191;(CIECAM02) - Farbigkeit (M) +HISTORY_MSG_192;(CIECAM02) - Farbton (H) +HISTORY_MSG_193;(CIECAM02)\nTonwertkurve 1 +HISTORY_MSG_194;(CIECAM02)\nTonwertkurve 2 +HISTORY_MSG_195;(CIECAM02)\nTonwertkurve 1 - Modus +HISTORY_MSG_196;(CIECAM02)\nTonwertkurve 2 - Modus +HISTORY_MSG_197;(CIECAM02) - Farbkurve +HISTORY_MSG_198;(CIECAM02) - Farbkurve\nModus +HISTORY_MSG_199;(CIECAM02) - Ausgabe-\nHistogramm anzeigen +HISTORY_MSG_200;(CIECAM02)\nDynamikkompression +HISTORY_MSG_201;(Rauschreduzierung)\nDelta-Chrominanz\nRot / Grün +HISTORY_MSG_202;(Rauschreduzierung)\nDelta-Chrominanz\nBlau / Gelb +HISTORY_MSG_203;(Rauschreduzierung)\nMethode +HISTORY_MSG_204;(Sensor Bayer-Matrix)\nFarbinterpolation\nLMMSE-Verbesserung +HISTORY_MSG_205;(CIECAM02)\nBetrachtungsbed.\nHot / Bad-Pixelfilter +HISTORY_MSG_206;(CIECAM02) - Szene\nAuto-Leuchtstärke +HISTORY_MSG_207;(Farbsaum entfernen)\nFarbtonkurve +HISTORY_MSG_208;(Weißabgleich)\nBlau / Rot-Korrektur +HISTORY_MSG_210;(Grauverlaufsfilter)\nRotationswinkel +HISTORY_MSG_211;(Grauverlaufsfilter) +HISTORY_MSG_212;(Vignettierungsfilter)\nIntensität +HISTORY_MSG_213;(Vignettierungsfilter) +HISTORY_MSG_214;(Schwarz/Weiß) +HISTORY_MSG_215;(Schwarz/Weiß) - Rot +HISTORY_MSG_216;(Schwarz/Weiß) - Grün +HISTORY_MSG_217;(Schwarz/Weiß) - Blau +HISTORY_MSG_218;(Schwarz/Weiß)\nGamma - Rot +HISTORY_MSG_219;(Schwarz/Weiß)\nGamma - Grün +HISTORY_MSG_220;(Schwarz/Weiß)\nGamma - Blau +HISTORY_MSG_221;(Schwarz/Weiß)\nFarbfilter +HISTORY_MSG_222;(Schwarz/Weiß)\nVorgaben +HISTORY_MSG_223;(Schwarz/Weiß) - Orange +HISTORY_MSG_224;(Schwarz/Weiß) - Gelb +HISTORY_MSG_225;(Schwarz/Weiß) - Cyan +HISTORY_MSG_226;(Schwarz/Weiß) - Magenta +HISTORY_MSG_227;(Schwarz/Weiß) - Violett +HISTORY_MSG_228;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_229;(Schwarz/Weiß)\nLuminanzequalizer +HISTORY_MSG_230;(Schwarz/Weiß) - Modus +HISTORY_MSG_231;(Schwarz/Weiß)\n“Bevor“-Kurve +HISTORY_MSG_232;(Schwarz/Weiß)\n“Bevor“-Kurventyp +HISTORY_MSG_233;(Schwarz/Weiß)\n“Danach“-Kurve +HISTORY_MSG_234;(Schwarz/Weiß)\n“Danach“-Kurventyp +HISTORY_MSG_235;(Schwarz/Weiß)\nAuto-Kanalmixer +HISTORY_MSG_236;--unused-- +HISTORY_MSG_237;(Schwarz/Weiß) - Mixer +HISTORY_MSG_238;(Grauverlaufsfilter)\nBereich +HISTORY_MSG_239;(Grauverlaufsfilter)\nIntensität +HISTORY_MSG_240;(Grauverlaufsfilter)\nRotationsachsen +HISTORY_MSG_241;(Vignettierungsfilter)\nBereich +HISTORY_MSG_242;(Vignettierungsfilter)\nForm +HISTORY_MSG_243;(Objektivkorrektur)\nVignettierung - Radius +HISTORY_MSG_244;(Objektivkorrektur)\nVignettierung - Faktor +HISTORY_MSG_245;(Objektivkorrektur)\nVignettierung - Zentrum +HISTORY_MSG_246;(L*a*b*) - CL-Kurve +HISTORY_MSG_247;(L*a*b*) - LH-Kurve +HISTORY_MSG_248;(L*a*b*) - HH-Kurve +HISTORY_MSG_249;(Detailebenenkontrast)\nSchwelle +HISTORY_MSG_250;(Rauschreduzierung)\nVerbesserung +HISTORY_MSG_251;(Schwarz/Weiß)\nAlgorithmus +HISTORY_MSG_252;(Detailebenenkontrast)\nHautfarbtöne schützen +HISTORY_MSG_253;(Detailebenenkontrast)\nArtefakte reduzieren +HISTORY_MSG_254;(Detailebenenkontrast)\nHautfarbton +HISTORY_MSG_255;(Rauschreduzierung)\nMedianfilter +HISTORY_MSG_256;(Rauschreduzierung)\nMediantyp +HISTORY_MSG_257;(Farbanpassungen) +HISTORY_MSG_258;(Farbanpassungen)\nFarbkurve +HISTORY_MSG_259;(Farbanpassungen)\nDeckkraftkurve +HISTORY_MSG_260;(Farbanpassungen)\na*[b*]-Transparenz +HISTORY_MSG_261;(Farbanpassungen)\nMethode +HISTORY_MSG_262;(Farbanpassungen)\nb*-Transparenz +HISTORY_MSG_263;(Farbanpassungen)\nSchatten - Blau / Rot +HISTORY_MSG_264;(Farbanpassungen)\nSchatten - Cyan / Grün +HISTORY_MSG_265;(Farbanpassungen)\nSchatten - Gelb / Blau +HISTORY_MSG_266;(Farbanpassungen)\nMitten - Blau / Rot +HISTORY_MSG_267;(Farbanpassungen)\nMitten - Cyan / Grün +HISTORY_MSG_268;(Farbanpassungen)\nMitten - Gelb / Blau +HISTORY_MSG_269;(Farbanpassungen)\nLichter - Blau / Rot +HISTORY_MSG_270;(Farbanpassungen)\nLichter - Cyan / Grün +HISTORY_MSG_271;(Farbanpassungen)\nLichter - Gelb / Blau +HISTORY_MSG_272;(Farbanpassungen)\nFarbausgleich +HISTORY_MSG_273;(Farbanpassungen)\nZurücksetzen +HISTORY_MSG_274;(Farbanpassungen)\nSättigung Schatten +HISTORY_MSG_275;(Farbanpassungen)\nSättigung Lichter +HISTORY_MSG_276;(Farbanpassungen)\nDeckkraft +HISTORY_MSG_277;--unused-- +HISTORY_MSG_278;(Farbanpassungen)\nLuminanz schützen +HISTORY_MSG_279;(Farbanpassungen)\nSchatten +HISTORY_MSG_280;(Farbanpassungen)\nLichter +HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität +HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle +HISTORY_MSG_283;(Farbanpassungen)\nIntensität +HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch +HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode +HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp +HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen +HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche +HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche +HISTORY_MSG_290;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Rot +HISTORY_MSG_291;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Grün +HISTORY_MSG_292;(Sensor X-Trans-Matrix)\nSchwarzpunkt - Blau +HISTORY_MSG_293;(Filmsimulation) +HISTORY_MSG_294;(Filmsimulation)\nIntensität +HISTORY_MSG_295;(Filmsimulation) - Film +HISTORY_MSG_296;(Rauschreduzierung)\nLuminanzkurve +HISTORY_MSG_297;(Rauschreduzierung)\nQualität +HISTORY_MSG_298;(Vorverarbeitung)\nDead-Pixel-Filter +HISTORY_MSG_299;(Rauschreduzierung)\nChrominanzkurve +HISTORY_MSG_300;- +HISTORY_MSG_301;(Rauschreduzierung)\nLuminanzkontrolle +HISTORY_MSG_302;(Rauschreduzierung)\nChrominanz - Methode +HISTORY_MSG_303;(Rauschreduzierung)\nChrominanz - Methode +HISTORY_MSG_304;(Wavelet)\nKontrastebenen +HISTORY_MSG_305;(Wavelet) +HISTORY_MSG_306;(Wavelet) - Einstellungen\nVerarbeitungsebene +HISTORY_MSG_307;(Wavelet) - Einstellungen\nVerarbeitung +HISTORY_MSG_308;(Wavelet) - Einstellungen\nVerarbeitungsrichtung +HISTORY_MSG_309;(Wavelet)\nKantenschärfung\nDetails +HISTORY_MSG_310;(Wavelet) - Restbild\nHimmelsfarbtöne\nschützen +HISTORY_MSG_311;(Wavelet) - Einstellungen\nAnzahl der Ebenen +HISTORY_MSG_312;(Wavelet) - Restbild\nSchatten Schwelle +HISTORY_MSG_313;(Wavelet) - Farbe\nEbenengrenze +HISTORY_MSG_314;(Wavelet) - Gamut\nArtefakte reduzieren +HISTORY_MSG_315;(Wavelet) - Restbild\nKontrast +HISTORY_MSG_316;(Wavelet) - Gamut\nHautfarbtöne schützen +HISTORY_MSG_317;(Wavelet) - Gamut\nHautfarbton +HISTORY_MSG_318;(Wavelet) - Kontrast\nLichterebenen +HISTORY_MSG_319;(Wavelet) - Kontrast\nLichter-Luminanzbereich +HISTORY_MSG_320;(Wavelet) - Kontrast\nSchatten-Luminanzbereich +HISTORY_MSG_321;(Wavelet) - Kontrast\nSchattenebenen +HISTORY_MSG_322;(Wavelet) - Gamut\nFarbverschiebungen\nvermeiden +HISTORY_MSG_323;(Wavelet)\nKantenschärfung\nLokale Kontrastkurve +HISTORY_MSG_324;(Wavelet) - Farbe\nPastellfarben +HISTORY_MSG_325;(Wavelet) - Farbe\nGesättigte Farben +HISTORY_MSG_326;(Wavelet) - Farbe\nChrominanzethode +HISTORY_MSG_327;(Wavelet) - Kontrast\nAnwenden auf +HISTORY_MSG_328;(Wavelet) - Farbe\nFarb-Kontrast-\nVerknüpfung +HISTORY_MSG_329;(Wavelet) - Tönung\nDeckkraft Rot / Grün +HISTORY_MSG_330;(Wavelet) - Tönung\nDeckkraft Blau / Gelb +HISTORY_MSG_331;(Wavelet)\nKontrastebenen\nExtra +HISTORY_MSG_332;(Wavelet)- -Einstellungen\nKachelgröße +HISTORY_MSG_333;(Wavelet) - Restbild\nSchatten +HISTORY_MSG_334;(Wavelet) - Restbild\nBuntheit +HISTORY_MSG_335;(Wavelet) - Restbild\nLichter +HISTORY_MSG_336;(Wavelet) - Restbild\nLichter Schwelle +HISTORY_MSG_337;(Wavelet) - Restbild\nHimmelsfarbton +HISTORY_MSG_338;(Wavelet)\nKantenschärfung\nRadius +HISTORY_MSG_339;(Wavelet)\nKantenschärfung\nIntensität +HISTORY_MSG_340;(Wavelet) - Einstellungen\nIntensität +HISTORY_MSG_341;(Wavelet) - Einstellungen\nKantenperformance +HISTORY_MSG_342;(Wavelet)\nKantenschärfung\nErste Ebene +HISTORY_MSG_343;(Wavelet) - Farbe\nFarbebenen +HISTORY_MSG_344;(Wavelet)\nFarbmethode\nRegler/Kurve +HISTORY_MSG_345;(Wavelet)\nKantenschärfung\nLokaler Kontrast +HISTORY_MSG_346;(Wavelet)\nKantenschärfung\nLokale Kontrastmethode +HISTORY_MSG_347;(Wavelet)\nRauschreduzierung\nEbene 1 +HISTORY_MSG_348;(Wavelet)\nRauschreduzierung\nEbene 2 +HISTORY_MSG_349;(Wavelet)\nRauschreduzierung\nEbene 3 +HISTORY_MSG_350;(Wavelet)\nKantenschärfung\nKantenerkennung +HISTORY_MSG_351;(Wavelet) - Restbild\nHH-Kurve +HISTORY_MSG_352;(Wavelet) - Einstellungen\nHintergrund +HISTORY_MSG_353;(Wavelet)\nKantenschärfung\nGradientenempfindlichkeit +HISTORY_MSG_354;(Wavelet)\nKantenschärfung\nErweiterter Algorithmus +HISTORY_MSG_355;(Wavelet)\nKantenschärfung\nSchwelle niedrig +HISTORY_MSG_356;(Wavelet)\nKantenschärfung\nSchwelle hoch +HISTORY_MSG_357;(Wavelet)\nRauschreduzierung\nSchärfung verknüpfen +HISTORY_MSG_358;(Wavelet) - Gamut\nKontrastkurve +HISTORY_MSG_359;(Vorverarbeitung)\nHot / Dead-Pixel-Filter\nSchwelle +HISTORY_MSG_360;(Dynamikkompression)\nGamma +HISTORY_MSG_361;(Wavelet) - Endretusche\nFarbausgleich +HISTORY_MSG_362;(Wavelet) - Restbild\nKompression +HISTORY_MSG_363;(Wavelet) - Restbild\nKompression - Intensität +HISTORY_MSG_364;(Wavelet) - Endretusche\nKontrastausgleich +HISTORY_MSG_365;(Wavelet) - Endretusche\nDelta-Kontrastausgleich +HISTORY_MSG_366;(Wavelet) - Restbild\nGammakompression +HISTORY_MSG_367;(Wavelet) - Endretusche\n"Danach"-Kontrastkurve +HISTORY_MSG_368;(Wavelet) - Endretusche\nKontrastausgleichskurve +HISTORY_MSG_369;(Wavelet) - Endretusche\nKontrastmethode +HISTORY_MSG_370;(Wavelet) - Endretusche\nLokale Kontrastkurve +HISTORY_MSG_371;(Skalieren) - Schärfen +HISTORY_MSG_372;(Skalieren) - Schärfen\nUSM - Radius +HISTORY_MSG_373;(Skalieren) - Schärfen\nUSM - Intensität +HISTORY_MSG_374;(Skalieren) - Schärfen\nUSM - Schwelle +HISTORY_MSG_375;(Skalieren) - Schärfen\nUSM - Nur Kanten\nschärfen +HISTORY_MSG_376;(Skalieren) - Schärfen\nUSM - Kantenschärfung\nRadius +HISTORY_MSG_377;(Skalieren) - Schärfen\nUSM - Kantentoleranz +HISTORY_MSG_378;(Skalieren) - Schärfen\nUSM - Halokontrolle +HISTORY_MSG_379;(Skalieren) - Schärfen\nUSM - Halokontrolle\nIntensität +HISTORY_MSG_380;(Skalieren) - Schärfen\nMethode +HISTORY_MSG_381;(Skalieren) - Schärfen\nRLD - Radius +HISTORY_MSG_382;(Skalieren) - Schärfen\nRLD - Intensität +HISTORY_MSG_383;(Skalieren) - Schärfen\nRLD - Dämpfung +HISTORY_MSG_384;(Skalieren) - Schärfen\nRLD - Iterationen +HISTORY_MSG_385;(Wavelet) - Restbild\nFarbausgleich +HISTORY_MSG_386;(Wavelet) - Restbild\nFarbausgleich\nLichter Grün / Cyan +HISTORY_MSG_387;(Wavelet) - Restbild\nFarbausgleich\nLichter Blau / Gelb +HISTORY_MSG_388;(Wavelet) - Restbild\nFarbausgleich\nMitten Grün / Cyan +HISTORY_MSG_389;(Wavelet) - Restbild\nFarbausgleich\nMitten Blau / Gelb +HISTORY_MSG_390;(Wavelet) - Restbild\nFarbausgleich\nSchatten Grün / Cyan +HISTORY_MSG_391;(Wavelet) - Restbild\nFarbausgleich\nSchatten Blau / Gelb +HISTORY_MSG_392;(Wavelet) - Restbild\nFarbausgleich +HISTORY_MSG_393;(Farbmanagement)\nEingangsfarbprofil\nDCP - Look-Tabelle +HISTORY_MSG_394;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basisbelichtung +HISTORY_MSG_395;(Farbmanagement)\nEingangsfarbprofil\nDCP - Basistabelle +HISTORY_MSG_396;(Wavelet) - Kontrast +HISTORY_MSG_397;(Wavelet) - Farbe +HISTORY_MSG_398;(Wavelet)\nKantenschärfung +HISTORY_MSG_399;(Wavelet) - Restbild +HISTORY_MSG_400;(Wavelet) - Endretusche +HISTORY_MSG_401;(Wavelet) - Tönung +HISTORY_MSG_402;(Wavelet)\nRauschreduzierung +HISTORY_MSG_403;(Wavelet)\nKantenschärfung\nKantenempfindlichkeit +HISTORY_MSG_404;(Wavelet)\nKantenschärfung\nGrundverstärkung +HISTORY_MSG_405;(Wavelet)\nRauschreduzierung\nEbene 4 +HISTORY_MSG_406;(Wavelet)\nKantenschärfung\nBenachbarte Pixel +HISTORY_MSG_407;(Retinex) - Methode +HISTORY_MSG_408;(Retinex) - Radius +HISTORY_MSG_409;(Retinex) - Einstellungen\nKontrast +HISTORY_MSG_410;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nAusgleich +HISTORY_MSG_411;(Retinex) - Intensität +HISTORY_MSG_412;(Retinex) - Einstellungen\nDynamikkompression\nGaußscher Gradient +HISTORY_MSG_413;(Retinex) - Kontrast +HISTORY_MSG_414;(Retinex) - Einstellungen\nKorrekturen\nLuminanz(L) - L*a*b* +HISTORY_MSG_415;(Retinex) - Einstellungen\nTransmission\nTransmissionskurve +HISTORY_MSG_416;(Retinex) +HISTORY_MSG_417;(Retinex) - Einstellungen\nTransmission\nMedianfilter +HISTORY_MSG_418;(Retinex) - Einstellungen\nTransmission\nSchwelle +HISTORY_MSG_419;(Retinex) - Farbraum +HISTORY_MSG_420;(Retinex) - Einstellungen\nHSL-Kurve +HISTORY_MSG_421;(Retinex) - Einstellungen\nKorrekturen\nGammakorrektur +HISTORY_MSG_422;(Retinex) - Einstellungen\nGamma +HISTORY_MSG_423;(Retinex) - Einstellungen\nGammasteigung +HISTORY_MSG_424;(Retinex) - Einstellungen\nHL-Schwelle +HISTORY_MSG_425;(Retinex) - Einstellungen\nBasis-Logarithmus +HISTORY_MSG_426;(Retinex) - Einstellungen\nKorrekturen - Farbton (H) +HISTORY_MSG_427;Ausgabe-Rendering-Intent +HISTORY_MSG_428;Monitor-Rendering-Intent +HISTORY_MSG_429;(Retinex) - Einstellungen\nDynamikkompression\nIterationen +HISTORY_MSG_430;(Retinex) - Einstellungen\nDynamikkompression\nTransmission Gradient +HISTORY_MSG_431;(Retinex) - Einstellungen\nDynamikkompression\nIntensität Gradient +HISTORY_MSG_432;(Retinex) - Maske\nLichter +HISTORY_MSG_433;(Retinex) - Maske\nTonwertbreite Lichter +HISTORY_MSG_434;(Retinex) - Maske\nSchatten +HISTORY_MSG_435;(Retinex) - Maske\nTonwertbreite Schatten +HISTORY_MSG_436;(Retinex) - Maske\nRadius +HISTORY_MSG_437;(Retinex) - Maske\nMethode +HISTORY_MSG_438;(Retinex) - Maske\nKurve +HISTORY_MSG_439;(Retinex) - Vorschau +HISTORY_MSG_440;(Detailebenenkontrast)\nProzessreihenfolge +HISTORY_MSG_441;(Retinex) - Einstellungen\nVerstärkung und Ausgleich\nTransmissionsverstärkung +HISTORY_MSG_442;(Retinex) - Einstellungen\nTransmission - Skalierung +HISTORY_MSG_443;(Farbmanagement)\nAusgabeprofil\nSchwarzpunkt-Kompensation +HISTORY_MSG_444;(Weißabgleich)\nAWB-Temperatur-Korrektur HISTORY_NEWSNAPSHOT;Hinzufügen HISTORY_NEWSNAPSHOT_TOOLTIP;Taste: Alt + s HISTORY_SNAPSHOT;Schnappschuss @@ -999,14 +999,15 @@ PREFERENCES_FLATFIELDFOUND;Gefunden PREFERENCES_FLATFIELDSDIR;Weißbild-Verzeichnis PREFERENCES_FLATFIELDSHOTS;Aufnahmen PREFERENCES_FLATFIELDTEMPLATES;Vorlagen -PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FLUOF2;Fluoreszenz F2 PREFERENCES_FLUOF7;Fluoreszenz F7 +PREFERENCES_FLUOF11;Fluoreszenz F11 PREFERENCES_FORIMAGE;Für Bilddateien PREFERENCES_FORRAW;Für RAW-Dateien PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT;Gleiche Miniaturbildgröße in der Dateiverwaltung und dem Filmstreifen verwenden PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT;Unterschiedliche Miniaturbildgrößen benötigen mehr Verarbeitungszeit beim Wechsel zwischen der Dateiverwaltung und dem Editor PREFERENCES_GIMPPATH;GIMP Installationsverzeichnis +PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes PREFERENCES_GREY05;Yb = 05 CIE L#30 PREFERENCES_GREY10;Yb = 10 CIE L#40 PREFERENCES_GREY15;Yb = 15 CIE L#45 @@ -1014,9 +1015,8 @@ PREFERENCES_GREY18;Yb = 18 CIE L#50 PREFERENCES_GREY23;Yb = 23 CIE L#55 PREFERENCES_GREY30;Yb = 30 CIE L#60 PREFERENCES_GREY40;Yb = 40 CIE L#70 -PREFERENCES_GREY;Yb-Luminanz (%) des Ausgabegerätes -PREFERENCES_GREYSC18;Yb = 18 CIE L#49 PREFERENCES_GREYSC;Szenen-Yb-Luminanz (%) +PREFERENCES_GREYSC18;Yb = 18 CIE L#49 PREFERENCES_GREYSCA;Automatisch PREFERENCES_HISTOGRAMPOSITIONLEFT;Histogramm linksseitig PREFERENCES_HISTOGRAMWORKING;Das Arbeitsprofil zur Darstellung des Haupthistogramms verwenden @@ -1452,9 +1452,9 @@ TP_DIRPYRDENOISE_MANU;Benutzerdefiniert TP_DIRPYRDENOISE_MED;Medianfilter TP_DIRPYRDENOISE_MEDMETHOD;Medianmethode TP_DIRPYRDENOISE_MEDTYPE;Mediantyp +TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD11;Qualität TP_DIRPYRDENOISE_METHOD11_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. -TP_DIRPYRDENOISE_METHOD;Methode TP_DIRPYRDENOISE_METHOD_TOOLTIP;Für RAW-Bilder kann entweder die RGB-\noder L*a*b*-Methode verwendet werden.\n\nFür andere Bilder wird unabhängig von der\nAuswahl immer die L*a*b*-Methode verwendet. TP_DIRPYRDENOISE_METM_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MET_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -1783,8 +1783,8 @@ TP_RETINEX_SLOPE;Gammasteigung TP_RETINEX_STRENGTH;Intensität TP_RETINEX_THRESHOLD;Schwelle TP_RETINEX_THRESHOLD_TOOLTIP;Limitiert den Bereich der Transmissionskurve. -TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 TP_RETINEX_TLABEL;T: Min = %1, Max = %2\nT: Mittel = %3, Sigma = %4 +TP_RETINEX_TLABEL2;T: Tmin = %1, Tmax = %2 TP_RETINEX_TLABEL_TOOLTIP;Ergebnis der Transmissionskurve: Min, Max, Mittel und Sigma\nMin und Max hat Einfluss auf die Abweichung.\n\nTmin = Kleinster Wert der Transmissionskurve\nTmax = Größter Wert der Transmissionskurve TP_RETINEX_TRANF;Transmission TP_RETINEX_TRANSMISSION;Transmissionskurve @@ -1796,8 +1796,8 @@ TP_RETINEX_VIEW;Vorschau TP_RETINEX_VIEW_MASK;Maske TP_RETINEX_VIEW_METHOD_TOOLTIP;Standard: Normale Anzeige\n\nMaske: Zeigt die Maske an\n\nUnschärfemaske: Zeigt das Bild mit einem großen\nUnschärfemaskenradius an.\n\nTransmission-Auto / Fest: Zeigt die Transmissionskarte\nvor der Anwendung von Kontrast und Helligkeit an.\n\nACHTUNG: Die Maske zeigt nicht das Endergebnis, sondern\nverstärkt den Effekt um ihn besser beurteilen zu können. TP_RETINEX_VIEW_NONE;Standard -TP_RETINEX_VIEW_TRAN2;Transmission - Fest TP_RETINEX_VIEW_TRAN;Transmission - Auto +TP_RETINEX_VIEW_TRAN2;Transmission - Fest TP_RETINEX_VIEW_UNSHARP;Unschärfemaske TP_RGBCURVES_BLUE;B TP_RGBCURVES_CHANNEL;Kanal @@ -1921,12 +1921,12 @@ TP_WAVELET_CURVEEDITOR_CL_TOOLTIP;Wendet eine Kontrasthelligkeitskurve\nam Ende TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Farbton als Funktion des Farbtons H = f(H) TP_WAVELET_DALL;Alle Richtungen -TP_WAVELET_DAUB10;D10 - mittel -TP_WAVELET_DAUB14;D14 - hoch +TP_WAVELET_DAUB;Kantenperformance TP_WAVELET_DAUB2;D2 - niedrig TP_WAVELET_DAUB4;D4 - Standard TP_WAVELET_DAUB6;D6 - Standard Plus -TP_WAVELET_DAUB;Kantenperformance +TP_WAVELET_DAUB10;D10 - mittel +TP_WAVELET_DAUB14;D14 - hoch TP_WAVELET_DAUB_TOOLTIP;Ändert den Daubechies-Koeffizienten:\nD4 = Standard\nD14 = Häufig bestes Ergebnis auf Kosten\nvon ca. 10% längerer Verarbeitungszeit.\n\nVerbessert die Kantenerkennung sowie die Qualität\nder ersten Waveletebene. Jedoch hängt die Qualität\nnicht ausschließlich mit diesem Koeffizienten zusammen\nund kann je nach Bild und Einsatz variieren. TP_WAVELET_DONE;Vertikal TP_WAVELET_DTHR;Diagonal @@ -1937,8 +1937,8 @@ TP_WAVELET_EDGCONT_TOOLTIP;Verschieben der Punkte nach links, verringert den Kon TP_WAVELET_EDGE;Kantenschärfung TP_WAVELET_EDGEAMPLI;Grundverstärkung TP_WAVELET_EDGEDETECT;Gradientenempfindlichkeit -TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) TP_WAVELET_EDGEDETECTTHR;Schwelle niedrig (Rauschen) +TP_WAVELET_EDGEDETECTTHR2;Schwelle hoch (Erkennung) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;Schwelle der Kantenerkennung für feine Details.\nVerhindert die Schärfung von Rauschen. TP_WAVELET_EDGEDETECT_TOOLTIP;Verschieben des Reglers nach rechts erhöht die\nKantenempfindlichkeit. Die Einstellung wirkt sich\nauf den lokalen Kontrast, Kanteneinstellungen und\nRauschen aus. TP_WAVELET_EDGESENSI;Kantenempfindlichkeit @@ -2014,9 +2014,9 @@ TP_WAVELET_STREN;Intensität TP_WAVELET_STRENGTH;Intensität TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Schatten Schwelle +TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD2;Schattenebenen TP_WAVELET_THRESHOLD2_TOOLTIP;Legt die Ebene der Untergrenze (9 minus Wert)\nfür den Schatten-Luminanzbereich fest. Der\nmaximal mögliche Wert wird vom Wert der Lichter-\nebenen begrenzt.\n\nBeeinflussbare Ebenen: Untergrenze bis Ebene 9 -TP_WAVELET_THRESHOLD;Lichterebenen TP_WAVELET_THRESHOLD_TOOLTIP;Legt die Ebene der Obergrenze für den Lichter\n-Luminanzbereich fest. Der Wert begrenzt die\nmaximal möglichen Schattenebenen.\n\nBeeinflussbare Ebenen: Ebene 1 bis Obergrenze TP_WAVELET_THRH;Lichter Schwelle TP_WAVELET_TILESBIG;Große Kacheln @@ -2039,9 +2039,6 @@ TP_WBALANCE_FLASH55;Leica TP_WBALANCE_FLASH60;Standard, Canon, Pentax, Olympus TP_WBALANCE_FLASH65;Nikon, Panasonic, Sony, Minolta TP_WBALANCE_FLASH_HEADER;Blitz -TP_WBALANCE_FLUO10;F10 - Philips TL85 -TP_WBALANCE_FLUO11;F11 - Philips TL84 -TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO1;F1 - Tageslicht TP_WBALANCE_FLUO2;F2 - Kaltweiß TP_WBALANCE_FLUO3;F3 - Weiß @@ -2051,6 +2048,9 @@ TP_WBALANCE_FLUO6;F6 - Weiß reduziert TP_WBALANCE_FLUO7;F7 - D65 Tageslichtsimulation TP_WBALANCE_FLUO8;F8 - D50 / Sylvania F40 Design TP_WBALANCE_FLUO9;F9 - Kaltweiß Deluxe +TP_WBALANCE_FLUO10;F10 - Philips TL85 +TP_WBALANCE_FLUO11;F11 - Philips TL84 +TP_WBALANCE_FLUO12;F12 - Philips TL83 TP_WBALANCE_FLUO_HEADER;Leuchtstofflampe TP_WBALANCE_GREEN;Tönung TP_WBALANCE_GTI;GTI @@ -2083,3 +2083,4 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Ausschnitt an Bildschirm anpassen\nTaste: Alt ZOOMPANEL_ZOOMFITSCREEN;An Bildschirm anpassen\nTaste: f ZOOMPANEL_ZOOMIN;Hineinzoomen\nTaste: + ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - + diff --git a/rtdata/languages/English (UK) b/rtdata/languages/English (UK) index cb0bfa7ad..9a4910be1 100644 --- a/rtdata/languages/English (UK) +++ b/rtdata/languages/English (UK) @@ -142,6 +142,15 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !CURVEEDITOR_TOOLTIPSAVE;Save current curve. !CURVEEDITOR_TYPE;Type: !DIRBROWSER_FOLDERS;Folders +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1137,6 +1146,7 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !PREFERENCES_STARTUPIMDIR;Image Directory at Startup !PREFERENCES_STDAUT;Standard !PREFERENCES_TAB_BROWSER;File Browser +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_GENERAL;General !PREFERENCES_TAB_IMPROC;Image Processing !PREFERENCES_TAB_PERFORMANCE;Performance & Quality @@ -1165,6 +1175,7 @@ TP_WBALANCE_EQBLUERED_TOOLTIP;Allows to deviate from the normal behaviour of "wh !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste !PROFILEPANEL_PCUSTOM;Custom +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PFILE;From file !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_PLASTSAVED;Last Saved diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index f9aa862f4..6a9ab689b 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -37,6 +37,15 @@ !CURVEEDITOR_TOOLTIPSAVE;Save current curve. !CURVEEDITOR_TYPE;Type: !DIRBROWSER_FOLDERS;Folders +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1075,6 +1084,7 @@ !PREFERENCES_STDAUT;Standard !PREFERENCES_TAB_BROWSER;File Browser !PREFERENCES_TAB_COLORMGR;Color Management +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_GENERAL;General !PREFERENCES_TAB_IMPROC;Image Processing !PREFERENCES_TAB_PERFORMANCE;Performance & Quality @@ -1103,6 +1113,7 @@ !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste !PROFILEPANEL_PCUSTOM;Custom +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PFILE;From file !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_PLASTSAVED;Last Saved diff --git a/rtdata/languages/Espanol b/rtdata/languages/Espanol index 3cd36a01f..170213157 100644 --- a/rtdata/languages/Espanol +++ b/rtdata/languages/Espanol @@ -1514,6 +1514,15 @@ ZOOMPANEL_ZOOMOUT;Reducir Zoom\nAtajo: - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels !FILEBROWSER_POPUPCOLORLABEL0;Label: None !FILEBROWSER_SHOWNOTTRASHHINT;Show only non-deleted images. @@ -1779,6 +1788,7 @@ ZOOMPANEL_ZOOMOUT;Reducir Zoom\nAtajo: - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1787,6 +1797,7 @@ ZOOMPANEL_ZOOMOUT;Reducir Zoom\nAtajo: - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Euskara b/rtdata/languages/Euskara index 7dce64cdf..404505e11 100644 --- a/rtdata/languages/Euskara +++ b/rtdata/languages/Euskara @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;Tenperatura !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index d1031f359..0a7ea0edd 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -2039,6 +2039,17 @@ ZOOMPANEL_ZOOMOUT;Zoom Arrière\nRaccourci: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !HISTORY_MSG_444;WB - Temp bias +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules +!PROFILEPANEL_PDYNAMIC;Dynamic !TP_WBALANCE_TEMPBIAS;AWB temperature bias !TP_WBALANCE_TEMPBIAS_TOOLTIP;Allows to alter the computation of the "auto white balance"\nby biasing it towards warmer or cooler temperatures. The bias\nis expressed as a percentage of the computed temperature,\nso that the result is given by "computedTemp + computedTemp * bias". diff --git a/rtdata/languages/Greek b/rtdata/languages/Greek index e71a2c1e0..57ec0b394 100644 --- a/rtdata/languages/Greek +++ b/rtdata/languages/Greek @@ -449,6 +449,15 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1206,6 +1215,7 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1229,6 +1239,7 @@ TP_WBALANCE_TEMPERATURE;Θερμοκρασία !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Hebrew b/rtdata/languages/Hebrew index d0a5d9023..54d165be1 100644 --- a/rtdata/languages/Hebrew +++ b/rtdata/languages/Hebrew @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;מידת חום !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;מידת חום !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;מידת חום !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Italiano b/rtdata/languages/Italiano index ae60b9303..a79d46555 100644 --- a/rtdata/languages/Italiano +++ b/rtdata/languages/Italiano @@ -1345,6 +1345,15 @@ ZOOMPANEL_ZOOMOUT;Rimpicciolisci.\nScorciatoia: - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels !FILEBROWSER_SHOWNOTTRASHHINT;Show only non-deleted images. !FILEBROWSER_SHOWORIGINALHINT;Show only original images.\n\nWhen several images exist with the same filename but different extensions, the one considered original is the one whose extension is nearest the top of the parsed extensions list in Preferences > File Browser > Parsed Extensions. @@ -1650,6 +1659,7 @@ ZOOMPANEL_ZOOMOUT;Rimpicciolisci.\nScorciatoia: - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1658,6 +1668,7 @@ ZOOMPANEL_ZOOMOUT;Rimpicciolisci.\nScorciatoia: - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Japanese b/rtdata/languages/Japanese index c879c649c..38050e140 100644 --- a/rtdata/languages/Japanese +++ b/rtdata/languages/Japanese @@ -1884,6 +1884,15 @@ ZOOMPANEL_ZOOMOUT;ズームアウト\nショートカット: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !FILEBROWSER_SHOWORIGINALHINT;Show only original images.\n\nWhen several images exist with the same filename but different extensions, the one considered original is the one whose extension is nearest the top of the parsed extensions list in Preferences > File Browser > Parsed Extensions. !GENERAL_APPLY;Apply !GENERAL_OPEN;Open @@ -1967,7 +1976,9 @@ ZOOMPANEL_ZOOMOUT;ズームアウト\nショートカット: - !PREFERENCES_PRTINTENT;Rendering intent !PREFERENCES_PRTPROFILE;Color profile !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TUNNELMETADATA;Copy Exif/IPTC/XMP unchanged to output file +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Latvian b/rtdata/languages/Latvian index 4594b8250..72d49e307 100644 --- a/rtdata/languages/Latvian +++ b/rtdata/languages/Latvian @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;Temperatūra !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Magyar b/rtdata/languages/Magyar index 1188f5344..2e3ce1f40 100644 --- a/rtdata/languages/Magyar +++ b/rtdata/languages/Magyar @@ -887,6 +887,15 @@ ZOOMPANEL_ZOOMOUT;Kicsinyítés - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels @@ -1374,6 +1383,7 @@ ZOOMPANEL_ZOOMOUT;Kicsinyítés - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles @@ -1388,6 +1398,7 @@ ZOOMPANEL_ZOOMOUT;Kicsinyítés - !PROFILEPANEL_GLOBALPROFILES;Bundled profiles !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROGRESSBAR_NOIMAGES;No images found !PROGRESSBAR_PROCESSING_PROFILESAVED;Processing profile saved diff --git a/rtdata/languages/Nederlands b/rtdata/languages/Nederlands index 636efe1ad..04d5bd4d5 100644 --- a/rtdata/languages/Nederlands +++ b/rtdata/languages/Nederlands @@ -1998,6 +1998,15 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !GENERAL_APPLY;Apply !GENERAL_OPEN;Open !HISTORY_MSG_439;Retinex - Process @@ -2038,6 +2047,8 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: - !PREFERENCES_PRTINTENT;Rendering intent !PREFERENCES_PRTPROFILE;Color profile !PREFERENCES_SELECTFONT_COLPICKER;Select Color Picker's font +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules +!PROFILEPANEL_PDYNAMIC;Dynamic !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. !TOOLBAR_TOOLTIP_COLORPICKER;Lockable Color Picker\n\nWhen enabled:\nClick in the preview with left mouse button to add a color picker\nDrag it around while pressing the left mouse button\nDelete the color picker with a right mouse button click\nDelete all color pickers with Shift + Right mouse button click\nRight click away from any color picker to go back to the Hand tool diff --git a/rtdata/languages/Norsk BM b/rtdata/languages/Norsk BM index 2525bb55e..3d93ba84d 100644 --- a/rtdata/languages/Norsk BM +++ b/rtdata/languages/Norsk BM @@ -449,6 +449,15 @@ TP_WBALANCE_TEMPERATURE;Temperatur !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1206,6 +1215,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1229,6 +1239,7 @@ TP_WBALANCE_TEMPERATURE;Temperatur !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Polish b/rtdata/languages/Polish index 6493ba694..8aedbc0cb 100644 --- a/rtdata/languages/Polish +++ b/rtdata/languages/Polish @@ -1472,6 +1472,15 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrót: - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels !FILEBROWSER_SHOWNOTTRASHHINT;Show only non-deleted images. !FILEBROWSER_SHOWORIGINALHINT;Show only original images.\n\nWhen several images exist with the same filename but different extensions, the one considered original is the one whose extension is nearest the top of the parsed extensions list in Preferences > File Browser > Parsed Extensions. @@ -1727,6 +1736,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrót: - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1735,6 +1745,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrót: - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Polish (Latin Characters) b/rtdata/languages/Polish (Latin Characters) index ce490653c..207fcd12b 100644 --- a/rtdata/languages/Polish (Latin Characters) +++ b/rtdata/languages/Polish (Latin Characters) @@ -1472,6 +1472,15 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrot: - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels !FILEBROWSER_SHOWNOTTRASHHINT;Show only non-deleted images. !FILEBROWSER_SHOWORIGINALHINT;Show only original images.\n\nWhen several images exist with the same filename but different extensions, the one considered original is the one whose extension is nearest the top of the parsed extensions list in Preferences > File Browser > Parsed Extensions. @@ -1727,6 +1736,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrot: - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1735,6 +1745,7 @@ ZOOMPANEL_ZOOMOUT;Oddal\nSkrot: - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Portugues (Brasil) b/rtdata/languages/Portugues (Brasil) index a08f2453d..dee162661 100644 --- a/rtdata/languages/Portugues (Brasil) +++ b/rtdata/languages/Portugues (Brasil) @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;Temperatura !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;Temperatura !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;Temperatura !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Russian b/rtdata/languages/Russian index 9ed2b940b..7f7953a35 100644 --- a/rtdata/languages/Russian +++ b/rtdata/languages/Russian @@ -1281,6 +1281,15 @@ ZOOMPANEL_ZOOMOUT;Удалить - !CURVEEDITOR_AXIS_OUT;O: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels @@ -1607,6 +1616,7 @@ ZOOMPANEL_ZOOMOUT;Удалить - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1615,6 +1625,7 @@ ZOOMPANEL_ZOOMOUT;Удалить - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SAVEDLG_SUBSAMP_TOOLTIP;Best compression:\nJ:a:b 4:2:0\nh/v 2/2\nChroma halved horizontally and vertically.\n\nBalanced:\nJ:a:b 4:2:2\nh/v 2/1\nChroma halved horizontally.\n\nBest quality:\nJ:a:b 4:4:4\nh/v 1/1\nNo chroma subsampling. !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. diff --git a/rtdata/languages/Serbian (Cyrilic Characters) b/rtdata/languages/Serbian (Cyrilic Characters) index 648a123e2..a6237816d 100644 --- a/rtdata/languages/Serbian (Cyrilic Characters) +++ b/rtdata/languages/Serbian (Cyrilic Characters) @@ -1437,6 +1437,15 @@ ZOOMPANEL_ZOOMOUT;Умањује приказ слике - !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. !DIRBROWSER_FOLDERS;Folders +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels @@ -1773,6 +1782,7 @@ ZOOMPANEL_ZOOMOUT;Умањује приказ слике - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1780,6 +1790,7 @@ ZOOMPANEL_ZOOMOUT;Умањује приказ слике - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. !THRESHOLDSELECTOR_BL;Bottom-left diff --git a/rtdata/languages/Serbian (Latin Characters) b/rtdata/languages/Serbian (Latin Characters) index 49f5eee27..227267045 100644 --- a/rtdata/languages/Serbian (Latin Characters) +++ b/rtdata/languages/Serbian (Latin Characters) @@ -1437,6 +1437,15 @@ ZOOMPANEL_ZOOMOUT;Umanjuje prikaz slike - !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. !DIRBROWSER_FOLDERS;Folders +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. !EXPORT_BYPASS_EQUALIZER;Bypass Wavelet Levels @@ -1773,6 +1782,7 @@ ZOOMPANEL_ZOOMOUT;Umanjuje prikaz slike - !PREFERENCES_SIMPLAUT;Tool mode !PREFERENCES_SMA;Small (250x287) !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TIMAX;High !PREFERENCES_TINB;Number of tiles !PREFERENCES_TISTD;Standard @@ -1780,6 +1790,7 @@ ZOOMPANEL_ZOOMOUT;Umanjuje prikaz slike - !PREFERENCES_WLONE;One level !PREFERENCES_WLTWO;Two levels !PREFERENCES_WLZER;No +!PROFILEPANEL_PDYNAMIC;Dynamic !SOFTPROOF_GAMUTCHECK_TOOLTIP;If active, indicates in grey the pixels which have out of gamut colors from the output profile. !SOFTPROOF_TOOLTIP;Soft-proofing\nIf active, let you simulate de rendering generated by the output profile of the ICM tool. Most useful for simulating printing outputs. !THRESHOLDSELECTOR_BL;Bottom-left diff --git a/rtdata/languages/Slovak b/rtdata/languages/Slovak index 4fbe51c42..ca1ad5bf7 100644 --- a/rtdata/languages/Slovak +++ b/rtdata/languages/Slovak @@ -532,6 +532,15 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !CURVEEDITOR_MINMAXCPOINTS;Equalizer !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1251,6 +1260,7 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1272,6 +1282,7 @@ ZOOMPANEL_ZOOMOUT;Oddialiť - !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Suomi b/rtdata/languages/Suomi index aeed6500f..0994e6d44 100644 --- a/rtdata/languages/Suomi +++ b/rtdata/languages/Suomi @@ -451,6 +451,15 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1208,6 +1217,7 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1231,6 +1241,7 @@ TP_WBALANCE_TEMPERATURE;Lämpötila [K] !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/Swedish b/rtdata/languages/Swedish index b25ebf2a5..ceeec013e 100644 --- a/rtdata/languages/Swedish +++ b/rtdata/languages/Swedish @@ -1895,6 +1895,15 @@ ZOOMPANEL_ZOOMOUT;Förminska.\nKortkommando: - !CURVEEDITOR_AXIS_LEFT_TAN;LT: !CURVEEDITOR_AXIS_RIGHT_TAN;RT: !CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click on a node to select it.\nRight-click on empty space to de-select the node. +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !HISTORY_MSG_257;Color Toning !HISTORY_MSG_288;Flat Field - Clip control !HISTORY_MSG_289;Flat Field - Clip control - Auto @@ -1957,6 +1966,8 @@ ZOOMPANEL_ZOOMOUT;Förminska.\nKortkommando: - !PREFERENCES_PRTINTENT;Rendering intent !PREFERENCES_PRTPROFILE;Color profile !PREFERENCES_SERIALIZE_TIFF_READ;Tiff Read Settings +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules +!PROFILEPANEL_PDYNAMIC;Dynamic !TP_CBDL_METHOD;Process located !TP_COLORTONING_CURVEEDITOR_CL_TOOLTIP;Chroma opacity as a function of luminance oC=f(L) !TP_COLORTONING_LABEL;Color Toning diff --git a/rtdata/languages/Turkish b/rtdata/languages/Turkish index e9da40531..342363700 100644 --- a/rtdata/languages/Turkish +++ b/rtdata/languages/Turkish @@ -450,6 +450,15 @@ TP_WBALANCE_TEMPERATURE;Isı !CURVEEDITOR_TOOLTIPCOPY;Copy current curve to clipboard. !CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. !CURVEEDITOR_TYPE;Type: +!DYNPROFILEEDITOR_DELETE;Delete +!DYNPROFILEEDITOR_EDIT;Edit +!DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +!DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +!DYNPROFILEEDITOR_MOVE_DOWN;Move Down +!DYNPROFILEEDITOR_MOVE_UP;Move Up +!DYNPROFILEEDITOR_NEW;New +!DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +!DYNPROFILEEDITOR_PROFILE;Processing Profile !EDITWINDOW_TITLE;Image Edit !EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. !EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1207,6 +1216,7 @@ TP_WBALANCE_TEMPERATURE;Isı !PREFERENCES_SND_LNGEDITPROCDONE;Editor processing done !PREFERENCES_SND_TRESHOLDSECS;After seconds !PREFERENCES_STDAUT;Standard +!PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules !PREFERENCES_TAB_PERFORMANCE;Performance & Quality !PREFERENCES_TAB_SOUND;Sounds !PREFERENCES_TIMAX;High @@ -1230,6 +1240,7 @@ TP_WBALANCE_TEMPERATURE;Isı !PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial profiles will be converted to full profiles; the missing values will be replaced with hard-coded defaults.\n\nButton released: profiles will be applied as they are, altering only those values which they contain. !PROFILEPANEL_MYPROFILES;My profiles !PROFILEPANEL_PASTEPPASTE;Parameters to paste +!PROFILEPANEL_PDYNAMIC;Dynamic !PROFILEPANEL_PINTERNAL;Neutral !PROFILEPANEL_SAVEPPASTE;Parameters to save !PROGRESSBAR_LOADINGTHUMBS;Loading thumbnails... diff --git a/rtdata/languages/default b/rtdata/languages/default index 31b14aa2f..ec61230ff 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -36,6 +36,15 @@ CURVEEDITOR_TOOLTIPPASTE;Paste curve from clipboard. CURVEEDITOR_TOOLTIPSAVE;Save current curve. CURVEEDITOR_TYPE;Type: DIRBROWSER_FOLDERS;Folders +DYNPROFILEEDITOR_DELETE;Delete +DYNPROFILEEDITOR_EDIT;Edit +DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule +DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. +DYNPROFILEEDITOR_MOVE_DOWN;Move Down +DYNPROFILEEDITOR_MOVE_UP;Move Up +DYNPROFILEEDITOR_NEW;New +DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule +DYNPROFILEEDITOR_PROFILE;Processing Profile EDITWINDOW_TITLE;Image Edit EDIT_OBJECT_TOOLTIP;Displays a widget on the preview window which lets you adjust this tool. EDIT_PIPETTE_TOOLTIP;To add an adjustment point to the curve, hold the Ctrl key while left-clicking the desired spot in the image preview.\nTo adjust the point, hold the Ctrl key while left-clicking the corresponding area in the preview, then let go of Ctrl (unless you desire fine control) and while still holding the left mouse button move the mouse up or down to move that point up or down in the curve. @@ -1074,11 +1083,11 @@ PREFERENCES_STARTUPIMDIR;Image Directory at Startup PREFERENCES_STDAUT;Standard PREFERENCES_TAB_BROWSER;File Browser PREFERENCES_TAB_COLORMGR;Color Management +PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules PREFERENCES_TAB_GENERAL;General PREFERENCES_TAB_IMPROC;Image Processing PREFERENCES_TAB_PERFORMANCE;Performance & Quality PREFERENCES_TAB_SOUND;Sounds -PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules PREFERENCES_TIMAX;High PREFERENCES_TINB;Number of tiles PREFERENCES_TISTD;Standard @@ -1103,10 +1112,10 @@ PROFILEPANEL_MODE_TIP;Processing profile fill mode.\n\nButton pressed: partial p PROFILEPANEL_MYPROFILES;My profiles PROFILEPANEL_PASTEPPASTE;Parameters to paste PROFILEPANEL_PCUSTOM;Custom +PROFILEPANEL_PDYNAMIC;Dynamic PROFILEPANEL_PFILE;From file PROFILEPANEL_PINTERNAL;Neutral PROFILEPANEL_PLASTSAVED;Last Saved -PROFILEPANEL_PDYNAMIC;Dynamic PROFILEPANEL_SAVEDLGLABEL;Save Processing Parameters... PROFILEPANEL_SAVEPPASTE;Parameters to save PROFILEPANEL_TOOLTIPCOPY;Copy current processing profile to clipboard.\nCtrl-click to select the parameters to copy. @@ -2039,12 +2048,3 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: + ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: - -DYNPROFILEEDITOR_PROFILE;Processing Profile -DYNPROFILEEDITOR_MOVE_UP;Move Up -DYNPROFILEEDITOR_MOVE_DOWN;Move Down -DYNPROFILEEDITOR_NEW;New -DYNPROFILEEDITOR_EDIT;Edit -DYNPROFILEEDITOR_DELETE;Delete -DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule -DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule -DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression. From 652b9090912d2ae77979d18c7847870cf280b856 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Wed, 8 Mar 2017 21:51:13 +0100 Subject: [PATCH 150/181] Update v2.48, fine tuning --- rtdata/themes/TooWaBlue-GTK3-20_.css | 53 ++++++++++++---------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 4cdb4a308..94fdea332 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.47 - requires RT 5.0 + Version 2.48 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,7 +76,7 @@ } #ToolPanelNotebook { - min-width: 24em; + min-width: 24.08334em; } #HistoryPanel { min-width: 18em; @@ -795,7 +795,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { padding: 0 0.33334em; } #ToolPanelNotebook > header tab + tab { - margin-left: 0.25em; + margin-left: 0.33333em; } #ToolPanelNotebook > header tab image{ @@ -901,6 +901,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin: 0; } +#MetaPanelNotebook combobox button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: none; +} #MetaPanelNotebook combobox + button, #MetaPanelNotebook combobox + button + button { margin-left: 0.16667em; @@ -1096,6 +1101,9 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { border-color: @bg-dark-grey; background-image: none; } +#ThresholdAdjuster { + margin: 0.08334em 0 0.16667em 0; +} #ToolPanelNotebook scrolledwindow viewport.frame { padding: 0 0.56em; @@ -1349,13 +1357,14 @@ button.MiddleH { } /**/ -/* Base format for Toolbox and dialogs */ +/* Button base format for Toolbox and dialogs */ dialog button, #MyExpander button, #BatchQueueButtonsMainContainer button { min-height: 1.66667em; min-width: 0; padding: 0 0.375em; + margin: 0.08334em 0; } combobox .combo, dialog combobox .combo, @@ -1384,7 +1393,7 @@ dialog combobox .combo, } buttonbox:not(.dialog-action-area) button{ - margin: 0.16667em 0 0.33334em 0.16667em; + margin: 0.08334em 0 0.33334em 0.16667em; } #PrefNotebook buttonbox:not(.dialog-action-area) { margin-right: -5px; @@ -1408,13 +1417,6 @@ buttonbox:not(.dialog-action-area) button{ } /**/ -/* Add extra top and bottom space to buttons in toolbox & Preferences*/ -#PrefNotebook stack > box:nth-child(5) combobox, -#MyExpander button.flat, -#MyExpander button { - margin-top: 0.08334em; - margin-bottom: 0.08334em; -} /**/ #MyExpander button.text-button label { margin: 0;/* x */ @@ -1469,6 +1471,7 @@ window .view button { min-height: 2em; min-width: 1.33334em; padding: 0 0.33334em; + margin: 0; } dialog .view button.text-button label, window .view button.text-button label { @@ -1504,6 +1507,7 @@ window .view header button, } #pathbarbox button:last-child { + min-height: 2em; min-width: 2em; margin: 0; padding: 0; @@ -1535,7 +1539,7 @@ popover button.text-button { border: 0.08334em solid @border-color; box-shadow: none; background-image: none; - margin: 0; + margin: 0.083334em 0; min-height: 1.66667em; padding: 0 0.66667em; } @@ -1625,17 +1629,14 @@ headerbar button.titlebutton.close:active{ /*** end ***************************************************************************************/ /*** Ckeckbox & Radio **************************************************************************/ -checkbox { - padding:0; - margin:0; - min-height: 2em; -} +checkbox, checkbutton, radiobutton { padding: 0; margin: 0; - min-height: 2em; + min-height: 2em;; } + check, radio { border: calc(0.083334em + 0.18px) solid shade(@text-color, .9); @@ -1678,16 +1679,6 @@ frame > checkbutton check{ min-height: 1.4em; margin-left: 0.5em; } -#PrefNotebook checkbutton { - min-height: 1.16667em; -} -#PrefNotebook stack > box:nth-child(4) checkbutton, -#PrefNotebook stack > box:nth-child(5) checkbutton { - min-height: 1.83334em; -} -#PrefNotebook radiobutton { - min-height: 2em; -} #MyExpander button + checkbutton:last-child { margin-left: 0.33334em; @@ -1698,7 +1689,7 @@ frame > checkbutton check{ /*** Entry & Spinbutton ************************************************************************/ #MyExpander entry, entry { - margin: 0; + margin: 0.08334em 0; padding: 0 0.33334em; min-height: 1.66667em; min-width: 0; @@ -1709,7 +1700,7 @@ entry { } spinbutton { - margin: 0; + margin: 0.08334em 0; padding: 0; min-height: 1.66667em; min-width: 0; From 85d9cc5b090cd83f76ffcad1abdce1358160cb50 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Wed, 8 Mar 2017 21:52:48 +0100 Subject: [PATCH 151/181] Update v2.48, fine tuning --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 53 ++++++++++------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index 842dc0698..ce4bc93e0 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.47 - requires RT 5.0 + Version 2.48 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -76,7 +76,7 @@ } #ToolPanelNotebook { - min-width: 24em; + min-width: 24.08334em; } #HistoryPanel { min-width: 18em; @@ -795,7 +795,7 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { padding: 0 0.33334em; } #ToolPanelNotebook > header tab + tab { - margin-left: 0.25em; + margin-left: 0.33333em; } #ToolPanelNotebook > header tab image{ @@ -901,6 +901,11 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { margin: 0; } +#MetaPanelNotebook combobox button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: none; +} #MetaPanelNotebook combobox + button, #MetaPanelNotebook combobox + button + button { margin-left: 0.16667em; @@ -1096,6 +1101,9 @@ window.csd:not(.fullscreen) #MainNotebook > header.top { border-color: @bg-dark-grey; background-image: none; } +#ThresholdAdjuster { + margin: 0.08334em 0 0.16667em 0; +} #ToolPanelNotebook scrolledwindow viewport.frame { padding: 0 0.56em; @@ -1349,13 +1357,14 @@ button.MiddleH { } /**/ -/* Base format for Toolbox and dialogs */ +/* Button base format for Toolbox and dialogs */ dialog button, #MyExpander button, #BatchQueueButtonsMainContainer button { min-height: 1.66667em; min-width: 0; padding: 0 0.375em; + margin: 0.08334em 0; } combobox .combo, dialog combobox .combo, @@ -1384,7 +1393,7 @@ dialog combobox .combo, } buttonbox:not(.dialog-action-area) button{ - margin: 0.16667em 0 0.33334em 0.16667em; + margin: 0.08334em 0 0.33334em 0.16667em; } #PrefNotebook buttonbox:not(.dialog-action-area) { margin-right: -5px; @@ -1408,13 +1417,6 @@ buttonbox:not(.dialog-action-area) button{ } /**/ -/* Add extra top and bottom space to buttons in toolbox & Preferences*/ -#PrefNotebook stack > box:nth-child(5) combobox, -#MyExpander button.flat, -#MyExpander button { - margin-top: 0.08334em; - margin-bottom: 0.08334em; -} /**/ #MyExpander button.text-button label { margin: 0;/* x */ @@ -1469,6 +1471,7 @@ window .view button { min-height: 2em; min-width: 1.33334em; padding: 0 0.33334em; + margin: 0; } dialog .view button.text-button label, window .view button.text-button label { @@ -1504,6 +1507,7 @@ window .view header button, } #pathbarbox button:last-child { + min-height: 2em; min-width: 2em; margin: 0; padding: 0; @@ -1535,7 +1539,7 @@ popover button.text-button { border: 0.08334em solid @border-color; box-shadow: none; background-image: none; - margin: 0; + margin: 0.083334em 0; min-height: 1.66667em; padding: 0 0.66667em; } @@ -1625,17 +1629,14 @@ headerbar button.titlebutton.close:active{ /*** end ***************************************************************************************/ /*** Ckeckbox & Radio **************************************************************************/ -checkbox { - padding:0; - margin:0; - min-height: 2em; -} +checkbox, checkbutton, radiobutton { padding: 0; margin: 0; - min-height: 2em; + min-height: 2em;; } + check, radio { border: calc(0.083334em + 0.18px) solid shade(@text-color, .9); @@ -1678,16 +1679,6 @@ frame > checkbutton check{ min-height: 1.4em; margin-left: 0.5em; } -#PrefNotebook checkbutton { - min-height: 1.16667em; -} -#PrefNotebook stack > box:nth-child(4) checkbutton, -#PrefNotebook stack > box:nth-child(5) checkbutton { - min-height: 1.83334em; -} -#PrefNotebook radiobutton { - min-height: 2em; -} #MyExpander button + checkbutton:last-child { margin-left: 0.33334em; @@ -1698,7 +1689,7 @@ frame > checkbutton check{ /*** Entry & Spinbutton ************************************************************************/ #MyExpander entry, entry { - margin: 0; + margin: 0.08334em 0; padding: 0 0.33334em; min-height: 1.66667em; min-width: 0; @@ -1709,7 +1700,7 @@ entry { } spinbutton { - margin: 0; + margin: 0.08334em 0; padding: 0; min-height: 1.66667em; min-width: 0; From ac3354233d03df061b4c27ea8367ceac8d7a8700 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Thu, 9 Mar 2017 00:25:20 +0100 Subject: [PATCH 152/181] Typo corrected Thanks --- rtdata/themes/TooWaBlue-GTK3-20_.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 94fdea332..1741dc352 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.48 - requires RT 5.0 + Version 2.49 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1634,7 +1634,7 @@ checkbutton, radiobutton { padding: 0; margin: 0; - min-height: 2em;; + min-height: 2em; } check, From 812d16815edca887a4dc3bd5c45ca1a992d07809 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Thu, 9 Mar 2017 00:26:21 +0100 Subject: [PATCH 153/181] Typo corrected --- rtdata/themes/TooWaBlue-Dark-GTK3-20_.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css index ce4bc93e0..3832f5bcd 100644 --- a/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-Dark-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.48 - requires RT 5.0 + Version 2.49 - requires RT 5.0 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1634,7 +1634,7 @@ checkbutton, radiobutton { padding: 0; margin: 0; - min-height: 2em;; + min-height: 2em; } check, From 245796e452a87d2afe46feda9f16bf00e4369659 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Thu, 9 Mar 2017 14:19:18 +0100 Subject: [PATCH 154/181] Added dual-illuminant NIKON D300 DCP, closes #3661 --- rtdata/dcpprofiles/NIKON D300.dcp | Bin 1012990 -> 1045582 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/rtdata/dcpprofiles/NIKON D300.dcp b/rtdata/dcpprofiles/NIKON D300.dcp index 6aafdc61653c6b4fd64c552a2ac2a889aea79634..008383c59d0689291e60f99c0f9587ed0b7904d4 100644 GIT binary patch delta 65857 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 QYQR^+!!tWJ`gryK0gPF Date: Thu, 9 Mar 2017 14:24:44 +0100 Subject: [PATCH 155/181] Added dual-illuminant NIKON D810 DCP, closes #3662 --- rtdata/dcpprofiles/NIKON D810.dcp | Bin 1012990 -> 1045582 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/rtdata/dcpprofiles/NIKON D810.dcp b/rtdata/dcpprofiles/NIKON D810.dcp index a946c1be216692a0ed52806719d6894470abad82..2a32a1376d88013f9feb274c7320709a8036d8c8 100644 GIT binary patch delta 33068 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 W%mu{UK+FTgyg Date: Thu, 9 Mar 2017 14:30:48 +0100 Subject: [PATCH 156/181] Added dual-illuminant FUJIFILM X100S DCP, closes #3723 --- rtdata/dcpprofiles/FUJIFILM X100S.dcp | Bin 0 -> 1045590 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 rtdata/dcpprofiles/FUJIFILM X100S.dcp diff --git a/rtdata/dcpprofiles/FUJIFILM X100S.dcp b/rtdata/dcpprofiles/FUJIFILM X100S.dcp new file mode 100644 index 0000000000000000000000000000000000000000..962d58469a2729044b6233da3516330762423fd5 GIT binary patch literal 1045590 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�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

mhzei&{!Y_5^dg2N;9jz>(TC4&ghW`H?{# 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%0Aw%?B7K?^F$obJ;<4E*paF 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%0AUtZl$R;qK*D&K~9h|9bxYubulN#>7O{y#pG% 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

~ptbrj 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

NW6aoKs(16!Qk9x{a=!y0FqIq%(Gld#( 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 z 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

(b$D8x$p4nh&Y9_opwpq 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 +d;>qna0l6wB5+<) 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

*KWZpSy2fTo^kLnM`t=;o{@RS!-|wrXseH zYf9LN5%3yA+-A>cBITvTQFaOb-39s*uNzBK?|akf9s?x1#Ax6=y~)^xliH1lqT0Lg z=sY=FiiVHeJ3PDZZ&pfMhlGsp^J z2PZy3x-CNgGsTy_+}$aan+4J61V7r`f0q;m9OyMK?3;S;mVT8$Zw+(m!kOukT_5O? zLa)Q{dV(}I7G4Rc|1$K}NV6!klvW&KHoMPcBVEvq?V{R@{TF!S$k)x z#Ukvk+pEZI;z;S8FM2in9W(XzvtEep+pfsNZ@Lu73Hme3BI%|44fjZ?q>z!weKnO_ zDKvy$PQ$KRD}zg_#oTCgEM@5o=91b0*I*F`KRaug|9E%WTZVccW~! zFgcc5A@|be3+&@h)XY!4X?xsLHq{9po``K;qpxf_v`r#$=AGX)m#(^pQ#a&)jm-Aa z2kaJGV*WO$ysH!;3n81in9&6ekiH!WB9{yBf7~=mI&cX3FyO)%26K{r>%f0%V~thg zB?EXObd2+(E#4EQyMy7Ya@?PG51t@Bi-Be!cF>lsc;qhdSf44-^O#7-f)v21p$9qI zT$+vkabr;sIa+OHR*3CBVAl?J+LzyPULdsiBFSw|2G=BBNfi;%l(KaWikiMQ@u3eX~K?XGP@->JMgLb|8c(6sG)w{ql{_nbF(zEMVwm< zF=C-_Q)oKk98^D;l~yOwaMW_`JuFxU!$cZ_8l$ka6T563Pm6H(Et;cX$J1k}3^}9- za|vDexP8Fco@I4_1#gD731ZvPB%h_!!gmWluL`)zf>hyj0&%uBe!>PoyZtkEBGXpA zXD8-|&@t>R%Es2S*bB%BCx8vU+d_JS`)&mAkT%=3rO}v?HeQWgUPN1I#UT~6dXP7E zw3A}d6K}&F%k*Iz>Bb2q{Q?h5Hd|9Vh&`hfdc2GG8%eDW;a&sw*LZdzYj#;l#dSe+ zE_W!~X%k4_qp=g35S+hbWgs0akEF}z?KsCYCGuW0rG8z^J(wOq!!%;act;2BZ*87D z0C&H2jO>B2oZ`IW>F1mS<}2617aID)|3A)@*f7oqH(Rs!Ez`&wabEbYJ$v*ng#r=h z6YJWs_t2;MJI~ax?#7mNPNYkSv!rFh4%tA%2mQz(VJh>=h$U0x5L#)+TDOd$V)P(S z}VGBrhtJvv+W9NX$f8%&hu&e zi)<@$@S1YyxSB|;wTY5e=_qN%#2ogu5FWCADzZ4}$F!gwI!6zErtG7b0PnUc=rdlu z7?i(x9r(7bq9}Ak2ktX?G0VUwu_|xF-NKC8)HIe(3u@UTJwH0O3_ey><}%d~Io*-s zsXofe+aH)dxM=O>7Bzlp6`J>uL3^~U|~hKy^4`3BDR@vZ&X$n8<|1NFxK{Uezt zyd`%aw>WJZ$LwOm=?wDo#ju&|sa6;{VivPV*Ov9%6hh_bO?visV%Cq~br}!t^SrIB zEA*z$3+PSWd$G370)cCV@6Td6i-fMsFgrgw+)d8(r@^anA2f}vJX!jEB}HMb;njIF z+WXUsKSx2;7Su`mz}>l$6jb7}(i3@igl2eP_dH`bochZbKk2X{akY z9hL1uoNKVddlYq0b^~#qG&PpKKeUrY>if}M_&^LCQ)0e$D0b%eaOP~@n}u%irU^;^ zi*wtYhH;LGUd_(5Nu%?K^Nc^gM7#GX^cit39(z#?$9XnFZE$Ge32_|q#ty`}d`_;o z8oHN5QF{!qIW6wl9ZOpf=VeueVqctRCE{GU@EY2*DB6cJJZa+tQ5SJmBF;wpUWj^# z^CZ;d9TPu@(}2sa!G3De)F0we#CgwHoZ+&jtR5IVmmt)8*&3`ME09{@{ZvJ@W{uDv zAM6az!Ll~&Xf*1I$-tHuw`R+bH$Gz*lIO0;`eVlCUJ*c=Q=75-<1xqUsic#mYs6+n z;I_non=vU@%q)X1P~TuG{nbOXb_^ur3t{xrcxis>`atqPoL_z$AuCP=rnEkqmb4ox zbHMyxF(H;Z#=bDWrQ=6B&T*7CZ-M#P!I-IBiYHsW*=83v!bjKhe{uf0v|*fmj5f2A z9nz=*ab^iM;tRz2?>+Ui@qRG@`FRH7+~Y%lxVB>=?LwS2vK__dR`E0$ao)0bu2`D} z{v6_58e}QjHH)Fmi1UM-$znb9hdmJIl1~%G<;WXH@!7BMBvFOD@dCA?zrz&qMoZ{k zYhiyp%~BNChEN|9;GiOBi&t<*E%gNU>7$J}J{hyI4svQzJx?514$b@~z<kkn-T!)_+>gMj<|ELc< z1CF|TFwNQMpk9xhtOwt|3d7#{JYrs`8AY0|%gx{59_#`SL8GOM&A)*^cVGatX!2W{ z>$ZX3B)ssX;SOd~`U4}M{GS@5&JjMYvP6peuOI!$=gI$3T_qo8Xplx)zrAUN zTeax=A%&*=jq{NhamOcUCm_x}n@@$>Pj#ZuQY)HyJHU0%p94zlxfy)R1}L%&%w{ zOvP{4nm@Moq(cf=0&;HOB^F@BIg8+^n+m!rx3=gs^h*sDbRiJ?>}E&*sLHaYj{MHtLN=CD7LAvh)7NHOstVoGo9g*zeA1Gz@W$Z2MiD zQI$d)5a%6bAz}#XItAj~DZj61toxtyY}D$cdZ1-Iy+wY0F|M_`HX)Yca1ZtiYGoew zE1Jr2Zuc6P$o8XuGQd3;5EUzP{~AHkQ*Y84enqwrnBKp0Y1zhavh`o!<<$gUd%Gx>N?Yvz?F3|;SO&Jq`8>kpB%_@p$y(Yz@_+ao5TeyQIY*G@Wcm9 z<-URwk%7IGtM6p4$0^)>rvm8Qb>dzQQ&K0)mbbjnEB%Qpx4?EA6Za?ng5>iF?2ySx!?n^H~rxm zWB2ZP{$A`JyVSwwQLxT;nuXlCJmLTR=sn#V*3Z%HquAOWX*3UUCWj_0?PChXBF-yJ zBgL+Wb0Xq=+M}14f<7e(=Q%5Kr}`55lwibJHe`E2Wo#_n!@S6(LPPcgy>=ST?b{#m zvW&I3PY~OV1E0yXt0U;uW9(u3w&r&12q$Zt`Lj9wIK`V#I)PdDvK->RK(FgCYUyY9 z=5jiTK{OQDfY!E-Twk0q?lJB}y{(*t8pD?XPhafGy+n;6e~sP$Pj7ByGZkid@K!j# zgPXb^+^h5eGMu}aYuZOi^xu`v?dk^KTffL&` z-G}bC5xB0?PxbcaY ziz=~yjhW6>8z|}KQ_MG>$~dhw@FsZh+)ik6!!Tz)l?Se@^(I*e=Bst)VRX5+O@0AB zzqE%I!`M9q z%O5%rI5p@V82#6e{@S7c^(=qm{PpBM)?-K-?LnL?_^vGUOA3`E&S$a;#m=|~CB!+{ zS1y(!&NC3_cU2?A%ZT#<^eJiM>(p=1cQ;0y-_6>q?t?fhafUtTO;dkzz--_le8hv# z6r{e7ppN%24_w(b{~PAAh7a)H6tUf0buAQlM9iNz|1dwNi#|mYc+s2@vTkdF;57i; z>5JvEZTRjMBKB5m{bcJ^D#`+G?fmWtStRt;*U z^xbQ2c~jeBPTb}gPGLpfw6k=ym<#QQSp2?@)kv``cHBA0)jP+Sh<`a@cB6qovKT5h zg_ptb9AM;r3=r846|DsZ!|7^o@d-HKe!#%l*>n{*mMCfaH$SR#*A>mZF~f(BfoGnE z`1LpVJ$n>1QT(RV&FVkE`(oaI{EPL}=#x6YFYVf|ywNXwNV7PGuFrAK-(HX1Yc_Ne z|LaHpDzV`lNxRos7Q>~{55&3P>ooT8cgjClf~@B{tWjMO{Y0D%wSS1qP(KeuoTc>F z;>cO>jm2H2G4ZK58}p6fxcdyCy0__fG)+eyKCr(;JnI}qws+Apilt)BXVi7qy~)-1 zm6(A!(w)oR(4cuIh8zv0V#NPa#V4^TG>z7M#h&HSchN=`M9+ZHtbMwRnik+l3Zs!5x-Z1vj#58 z@C_HmE?X4j0SuYL&ImEp3+Eho_b{ub;_pV-r9_5OV$042c|WmR35=ivsn^Uc`=Xc8 zh@u&@&Y1^do}CLmN$J-G<|Fp|QmlV8O^b9f)41hBQwKsvV#&~a-`Bv`J&E~0KYD#& z7XN<_{`WlZcDc8_YC8Ncx2Mhk^4z=*LXL9J=PV5DYT!ka+h8}Qk3OtT4JF(~M zROI6djO;~6_M{W|#hBgQ*>BGtVqVeVh9AA`xR}W>uiMZK7)OVhtS>MFTfpUUZfDK{ z?XVBrtE7jETd`dHj{GO;j8IpxX$9s{mZ3DJY*B&JOJEg-M^KHzM)nvS!RGs*nHg^> zOFRb*Fz%b&ZlBCQM*Gs}&xrr^5#}ARtGozI^TG+)d6mE}I^djj{I4Iqd|t!(bB*0y zHqJbih6CG{b$Tmn#l_Q^Yv*_(>_N&q z&S#cRvD6%&n@?NGl-JNdp}*s;T-Y#K6wO2rGOM)@>!OD4BI@L%dXOb3u%9w`=tu2`Br~g($PH_OD_RrH(py7wMhkchreNNfBl&@sIBw=- z_VsU{0*-F0wkO1+n7c+`w)J*wM8OrzQgW=LNzlxaIX_mCI6Rj4q&+e(d(yn@xcp|AY&R}Z>Y zG+c@rvMQ2(cRF2AlmPrBbZNc5{FKc&tfJO!5@|wEtxN?BmrI`{nj@vi0`~e*-lZhE zyh2-M=m(vQxykf)-X62Fx!953f&TM<{phud8ulpx{5@8-Je5?~HQ!2AvN;!0NQJ&k z=ZG6y-WdNLuoK2lec14ci6qPh{$^S*I}{#I3+%A_Fil{l<+1;0IlSq(m)T<$-ozF2 z`7Xy^bqIMbM<3(b0FqOTY*zGD`%R}oE*Ku zhpv`ZGhOt9HZIu7M*m{V`UlebILw#o8cVa=fWN5;u5U(DX)5l!qu_vBv}-EWIpfa4 zEV_AMBWdAQ)V-0w&z=6xE^dOB6wc+M$Z~cW^{^axjM`P%EDm$OzVa~I`?V*l>k$k+ z1n5JZEG;nefi4#OWBzE2;9i6WlJnf1bZdYq$O?-+4nO4eyNXu$2*fE&8HwF%P{Hq~P23MK%cT3%Q-5_;UD0t20)n z`epDxE0f^!34W~WUVbNXJ*rZ{zxpWfT2b-TKOc9Bf@)s(7cw%?L%v)6kl%xhiTx^Q zd7ggY7ivXA#~lc+o|do!nq|`xXf7O@3Qezt!}kw0^JqiiK__rFas#QvxSh}gp70~V zTOKgVRH*3$U$xJH)XmyVaGQ!5)Lv-q$C(LdJ0p{^wTwFHcM<|{*AD9zMAp<^XgUK~ z_Ta(PR2T@gxJPdQN2p})b>0;|3HNibe{o}ZgC}8R1RwkdCw`Usp_etbOD45(-yq%$ zFU?=Qwo}UO&EkW5gDLuS4rTZ0CN{#Es68W>RCh8&cJ5NjD#(T3M1+$IW}BNY=ThIl zdGtE3bN+P>W-PhN`$l4JWsSY`YAhe$CW8XULPua3&Ci&h22V%q1AEeWts^P4Iu76W zXM6dVhRGyHtraXk$$PCzq~(>^ZyRp#i8r9fzYm@G#y7kzyn-&h52V?7+CpV{3@!W# zZH|9)p=d-j&C~_AFs-eiJReE6I>_K2(^=TjCxYI805^EAl~A)ejE>bp$9ufD;2?ye z_69GvaDdS_hibZ#_U(l~_#r*jm6ga}n75t61Du#1 zI+4`kdB>Ao0@-v#ukeYUFmoHSO8dc2qem+t)ijnWosn;T z9hr}5m|=~9htD1xp$a-glX37_8sAUQf-lX-k?`n{Ita@#TY2h=Ow#8h$V|iN{8Vt} zorej>a6TGFNT{FdaG_^^^h=wmJ;YpvcyI}Bqwg>!dZ^HIA+qChA0NJle|M;&$*WE|{>JY>QlY*+=Ne zfb*gSx%_>(^3StL(MrBQcueZo`w92Bl!LNA&vx$;OWWy5{yf+G&7*I1x8PqmC)!=% zUp>ww(GaY?eQA96wHY)T`-6Q>Hvi&g8cog%Boogfp2NO%;wCgNN2>T$xKpy=R2<#- zoS)MM#xa=jv8MZKHVlv=$m|xw+sYiKTF*1hc~4fRrN+?cZ2@nB8MykH@hs-GJ85uU zIU<06x?Y2`Y13YOhe;ZoRVROO7Fc)c_y62^TczHQ)zAN|pBt`K^WD*}^FuvQa4C~- zQIJ6yGUPTj%HzGdrc?b1Xp5>izEKjm{pe3lv%kgLp!Qe;ZEs7%Z~XhJ)h+UoT! zg!a0L6rTV;8DFeh|9Dz~3Gz-+FQMuOe6XMeTe;Rr_^<^YX0Ih=b7Pcnsv(Lld_iV# zlj*`u=vm4?tNoPb3x&{a9eFMxHG^+ zH!ojdI>B?*3qC*hd<8R4%$uyB@2lM`=oBj`H41aXDVqfG6!7c7arELDK@UA=ORGo< zj@`}MUxA+?2mXZFbyIE{+DzFsa`VOhgU$Dd0Qbp7`-caAL%4m3Ei9H z-VZOYvo{6_Z^F?Vu)}wJ+Xx}iFqWjFrL;0{x{w+bO&!2jJ0n{p)U}GDm7Y>MKX;ul z#1H*a)NDUr1qz{u!stv}WLty<3zp}RSzM0UXXi+vJ9@|s3!v$n87D;bQP6A5kSgmE zgx~FhXej2ktXYEa3mIiE*GXyY7r7vw3eIh3_$y)QRwJ{+4_pGzA*KB5tO#VUr;>Ka z4=yz^l5z&`re62D@(o@ZoZa-7^C#|TaF+C1!(W8XLXB;^9!vRV`Wl>j`HbVQp3&gE z@%YRC7w66HRs5XjELt)JI2UZ=oqA`|hTY&np54oLI-O3F8*sm9SHaI(m_{qzB;=?6 zlHbsfLRXW)Asp94$VNVb!+m%&9_b(ic1fa8OZ3oiPJP>#KwE)f-Cag_F*qJs%INw0 zoFM$LY1U3NpTnf`ly+e3!vH8a^~y`1u{Ytesfr z4ave$>;rW+;BvId66#{%je&mY&Hmek_-#S75NBnrS&mTtLQXE7QP)Oq6Qcd(WC)FA z@~mjV9$fO4;9(?1O%V2|La9(Ug3jlh=N*h~PGJcOSK9)$Y&ZCUmIi}P3G3f|*P78!U3 zQl05G{wr!cEe@FP-N#p2Wza;NYm#B-`P^H`$ikhsO7@0N3QDEzRT6UM^#!9|Dbxzs zPI=Ku7>53<;YumRE$Ay4ElH%ghoyA=hKq3Vdp!02fc}ftbiph%j%vChS9ju4Vflj? zx&`i*)ir;i;$jqSwm>$vOesXqh@gWf;DOmXQLsCzq;z-;7JtqX^3fNakA11TI8V5U zUYrir@VHrfgo{6*Yio}E+_q5YU>!swdq6Kgy+D{9C#Ops&gH^XVO|RbRaVOB#>BZo zbph^xf#I~=>Mk!?5<#8vQpmPdJARrHp0DS3(X?+(c!xO}oQ)oh=MPk9aGvWpj^A)V zgLD7-LA)VoluXyl zkij&&pHSZ?iI!squQktA(63IQM{{K~cEAi_Hgt%pY-CJNUM_q)9Sd&|89A?%2t(iv z>x%Pxe%}aT5q+MJ)|ZoBoPiVb?J5>)T1G^iYA&kd4_N_A84^2Lzw3 z3R<#4LOwY~f?XtX>sDiLxm6@ggAcv;J>1jh?cj|#o9 z2RGoH^_*8Ch@syM##zv#a<>q4N+qhl@m}23 zQ5*h)^O~cj{1UHha>5zC~t50*`*DHMCUwZH@`yClnN;#Gc<;CDejfp7jix%IH19 zusHPb{1w#Nd#%8r2m5VtH06A}%kOZHrpmzWu3yhHpg@S=mv!QtC+nXlfQPV4ib8?E}pyDUkg zGxo@_@@pZOBP-dW1bILs&4s&NQmE7k9{Umfg(Y=KR0eF%FvEp_s6=XJjyyqs58;I> zp4LqdqJed5gw>e8q=Uy+u~Z?<%8jD(YnVf6#R<21MNr~cIW08F6n;B`+g}Zxym6io zS00KSXBm}+>=hpPLZ=L$K-0h?A!{Zy5txJ9vPXn(MVMJZHFvX^IW%-9IIJLQxE6H^P}%% zQXS@td-7NCu8%X|O@e#DH3eUOE1iD8ukX4~8h^4PjoR%;AGY`){{y{KnMg)qyDRw( zfhpAg4EEcjPxy6i$7w+bP}!_#?!Duhm+ySi1dfCl_FeC9nKc?jQ8oAp?#M21-_!5A8d$HNlI z?$su~$;4Pvjmf7&gWB_FK1BV&YTOv)m|JOZ_K&&9kw zOj?Wj?Mt`=?}|B#l@(@VtH$%|Kc!LlAt`;i<;|zwNu@i^@Nn56?2Ldd&NA#dOr){UA9EI23%vf@ zL^u!=OlQ!8=zq0?u-7D*0!kIgN6{1JoIzG>`*2#ep^#q+KHY@D$uuIf4KIEkOAliU zNFH;HJN;IJ^O(3$&carM^NzJkxGAL?oWHn^Bo28l3MW{#KbE)0~4< z?l(&x-_`8Z|HS#lV=sQw>nzeysC_W>_~Ck4WZY6h8*1-yTZ}TPIqt|NjyJjXh8f8C zgZD@AL(Wh?9o%y%xz~K;(lJB4?II)J-Fp0^>nXJ9GCZ*bBi{P}GFj%pPb8}|ztSIE z68LZ(dEA3*q(OPr{HE@`c`tC+njiV`Ffmv`L>r|TAQ_iXPkHgLP(LKS8V_#`-9_f8u75f)w zk1=~y&ftxxu^nh1s@k$wgY!Pf#0*J zCvXmUw4WQ(EDN46$WaKH&DFL+ehb!fyK^152BQo*g!LSGsaEw6I6DI8yhl4!yMgn| zq2LqxtylQ~XEFBAJ7xV;xA!C8%~MXN9(^hm0q6Jd*-5BRDm?_8ZGiLHb4I1Z?x0_N zPveVq1o=THyD4b7$y-O?yU{cW`o4p9%baRwAty*%LFw6hoZ4d+@D%r6zlFD)^w%k= z+jZpEh_yuDW`t5JHwAg$Z7DjA?|9lA%>Rl^MAM%vY5nhqi~GRSpo~xtm=uXC1mu06m(e}@ zo8o53VK76@ws2d6*lKPPZG}F4y|W=xXr4%&=D`c*usQSLP&|#e97HpfeHlN@Ip4O2 zx8Z=H%;G)Bu;>n-)<;vA!~IZqHG-F)$s#7AHEIuNhP}+zF&%@Hl!Q4)dCgYFD;_)| z^g{!6rOau}tXmF3&!|euOv63SW3ZAAfA(Z5;l0^&OEd)?Xu(X%fi7xS2G#m{i<{ok z;OxY`5emP$Qwr~5Q&2vd}tLIY0AXzu*!OtyD2Eguihk+AiQC$e!| zugYoEj8Nvzvjl1fz2xS}NemAj#D;V5?F!6g%o%7d9MGq@yq}qSCx%Y%RM3UnDrWzf zXga8ebMEO`W;wJ_J<+?LI=F^;Qi=1iIG7fcJYdW_htXU&WH6mXPC7jQqx?f@&5CzS ztC68}cozKn51wb%*dcG?bPT;n4rC4+!`Hff7QJkLTRaY2ay34;(+7x8zynJCT-1;& zn$bnGo(&dE5XScQ2L|*SR$q6{8SLBP=J&F;l$Pp2~NEKSs#s-qVrd%fqPN8=45dCB2MAHah(C zTNf5Gui*pS|9Unx_}vh%f26^A_JA@`_}~3`Hq%iw^DoY!zFnNA|HV0c=4Hq6e{tRu zH`lR+o@PC(_lLuO`x{ByCzteE@L!xKj@ITw0H?5x2XN zK|@eKx9&ZVi9ec73!&|mKJsC9qyKLV9>cYSXlCH@R4VK%gHLZh6F3BYydyI5o>R^o zMqXh7>gT!Z?lQ{`C(`K>=tnB+8RLb}WZ5HMC#f-V+v3S@2ej_nT2XXSEGbMBG~tdZ zU057LPZAZ>u9+33^o=GN_=wB3dxI~4988@MYMg3M*{;aU90D)A+#xjJUN-}5e?aG`Uc;@p zZG2scrhb0jww{0PqUk?#@_W1ixbEur6sJn~9Zs75b3uABcMkUk_3KXDOx2t!&A#z{ zNl;0Z&3|!z5Yf}A=uQ@80Oz=!hr~0G^EMDThYm1fGF2I55-%aCt|#*;EuE%z0=G;r zl9{<7jf!(|7xOP*`V9j&3|{J)!4*s(a@AAz19OvSjNREJitR7Qd_;$ewj`4EJ~;&| zjYwvbKxS4!)S;0X{X%B_-XyGNRS)_EUiw)>1$~kaBBvoSRG9+5Y*S}sP)CE81m3`m z(R8$96itNQ^?S!j_@%*l z?zP+eYV--!*uMDT$$wp^!P$1gD9+}A2Ip+#(YJl0xf}lT9DH5VN*r62^~aNc-`f}B zLqaAU!+o&BVG-k#hyF$!?mU%&j6{-7n>$J={P=dpY+4!}+=gtFS>;S8^HiE+1KoPu z6J|~=GIQX|`QAnsbF3uF>LI7<9&KnSv|s&pz{7BV7xeAoX*YaDb|&_tLaZ+ieV?Bm zEPBeZv`!0}>Ph3N<5uMJfri@y zUO@R}WHvoV*3u??e-vSKabN^J&<>(4J&_A`LG3-^4ezROitdv@PmJ3`6A(jsY1z~( zx-V03S+kzsugK$biZtu_bYg#ge1K+u&YswtKLFoIHMZsD&$!};n!0XO+9IxZCk@Vx z4v$uS!HiV>9(Ux?_bmP|&L>Qj;=w%X4Xo#}IV~BN{h2fdIB!`w9p_F4^@x>FM3ZP{ z(9(3eZ6c)~!-^OKN8AUq;GaMK2Giwt3T1Vb(KMX~W_D>Z`Rss~NU9;7Tb)F&y2_#Z zwID6j7_ZTDU%Rm%>BFNVq*V}INOLBG#mLmdoucg4M4E`RFY0Fyy$YI77_MIz86hny|bzB?=ei@a~hn7^LgCfo*JABKM&+3 z=z*(o%arxz=fTrn{aiia5!b&~gLCi}5AMB*2Isf(0jdoZ8l2U6^rHg*i?hG>IkDTm zEYbtcgS@SnncFkzB{&kL(iKdDEQ1!rAV=wZIukN2o&4HMsW$%vliVeZ9;HiZzTY!u zH*(c|Es!1lyD^>3NG2gV!E+2p1$Djwq@Wr z@^py9eg#k3vN`0O8AHvno-6BDQwP+sTqyK@-I4J&K9V*K2L}uHlAOsAWCzaZY2?C1 zAv5dCFytr6!M%tGCrjuq)?Q1bSKv|qs);05J`w)^(BRLGp<}Ir$@x||#hk`^K4wq1 zw?(7&%O>-$3z&9gf7kQAbS}BK2IsUp1Nds02Is7C-TAjoH8__{zR3x=GpRAZn&-wP zqVKDI{w(gSDmtsdS%f_L5pMs*nY*ULjM%xq!S9MRxBHavoR+DC))5#zr6 z5`_lmRL+v$)I@{xo@wW~82E{)v2A(EiDUoH2U{#`th#twgL546=$CZ(FU~_vEEtdY zEGh!dnQh$AGR&lDz}dWGC}ZlAK?8wv(b#=VmP0ztHI`CR;x(oYS?&tVn6`KP#W>^N zdDv7&LHkF5PU3L;V~y-GiB7ltr&_A5_*xGB)ipX*v2E zb8L&4Y=aD%gnRd$bGMj*ztdCVe}Ju%SRs|gL{1hbd2zl1P@X2CW;pKPD6e${3SMk z3w17qLcl9q{TV)zmoe8J6-nv$lWFt=S9p2GP|%ZYq#qi|xaMhau3S2f%NeM_`H7Di zKQ&x~^P6P`d_yA*&Q^tm-25vVoKs9pIENM*oU#A6~vJU*e5s@^$aRQYVETjI*kshxLZl8e$ zXQRbAr5{de_6>C{XHoiJoMS!?Wt!~IqPy5Pv~v0~QJ4?9quv-DIgZ(SI)n64Kie+Z z#4H8B(Gm9)ucdL!At8<0pwHhWZVxkmPbxVMLiS~oa%NX#3NluZTet8IlQS=mg{OG!H`00zaCQT&MtMIe3 zi6x!x6G#snjd9iSfHwOshwWTIr}t{;v78i zMaoR=no3K$$%s1?&s@hn#kf#L;+&n#%I8V6Y#@A5Hy&k{l_ion@*OrBR4{JPvmAjg zUEy(u5y8K44C>L#Pu?=!qhiS}2tE3>ICsrZj(8LV9z({auBk9l@3u=%23;7}N6)Y{N7y9`LI1k)sv?1w`NE+^+ zOaE~`OTQ%)fwdSJCK|l znlRbE>7*k;F3g+O%+1YdbW9hSfY-V)#cNZ^Jy=QxXB?PCi&IF^Tt*ibO<*=nN+#zh zVE$$?qZph-;jQJ=@pb@X*DjIX#LMa0!!V{3yw}#41^dPnu0PBI`6JmB_^8EfbT=xH4Mf~%SN+L1KzXA))GL^G`pMAP!(Y!XRM zi0ex=IGe`5QU!@LIERNsbGGn1Rb$xtQ4m+&UW0S1_0}BsRD*Nc#6*=A>RI)B<{Xlg zcB#?q8|{!sUwi()I9I+K&s;$LR_m+g(AJA{G5fOz_qEaEOX9LX)JbbFx5}szTS`*t zBseE#4cEoh0jQNrvGWB^WrKb0OiC-;BqLWLI8Leo}WWYag0^?^Y47bW@D3S0X%zG z-JCC8i=TbPNqAEK^b(In55I5-=FvBO#AFX&jsh7Cs@o*CfcEYwJmBh@2Z`f5#8G0B zoU(JH#CeV3)npn({X8?o^G-!mIc7r7qV|fzJdpc0UO}pxC&Zf@fKN~8gdf$2`}RlP zcUTDh9P(6L3$Lo~-byMP`$@d|ayZ!@fZuMh7PDk;IJF!ZMWJsRF_}xl$sfA-_*0+7 zdh^k*Sd&P-=jDof-HoEEh2S}7ZWH;s$I{G{Jrp%NMU}v6a6TE?pmNF5;N0-*p{ls2 z2It+aqEs6&b5P?Pl+s!CkkR1mns&9c=_k#4zKJ~g=Xd{$bCa_(nc0t1$p>8eK95d| zOK!nu<1TV%`YjN@fnK?>n}kjpHWfdqg%|NL_@lN<64}Ay>lS8Mz1IG8I&wRKSZHIC z238%r2fb0%FXSCxyIFeb2C`6*>uUDGK~;P;7JNO_Bu1-Lifg!!h~<>|AVl@>4zx|h zsP~E^RQ=1N=#(gkdYLO!;mC5jh4W_PCNI?$U~7}5pz*uyRH0+TsRr7rF5VAI7d!)x zG$@p6>bsVzYQm`B&M;cFca+m4WIQ#4$Mmo-EkvuIVxHI%nV{~LqSx~x$nPvPc)Zdn z7I*Uf(EYB9*HuMNjiHxacTw|qi&dA0YH(iM+Do+^oK-cR=la^InmTH5zBKtk=|f`; z&JVbYr9Gx=aIPQUQS}tPH}(75B9H#f%`N1(d*8q2DQ%9rp{Yv($#~{5v4c}G zm0SoUO12UE4osqV_7bw*?jQ=YON90a_wlY)s`0&%Q~wQTy3N@Ra-L!6iG!(ka~kf2@>oH(X+<{d#a^qY}6YlcMR^6=E&RkhuTd!+8(Kltn{)Zl#3QRd|0rolO~xv^7zUk%PO+u+iajT)Sj zyLzas?KC*6^XTo9w@{T?{=aaZzIh3=A6^~z=fM-T_hGU3(L`v?;Dd3isd#J#^tGLk z8`aIK#8HXa^acrSOsi2H^@}6BDrgpFv)okVr#pgQSNT!S<*bb%v$gR49kPQf+8B*| zc`4~6ALnXiQ8W#Ar*GqKaXR3>X3fTIYs*`14Ro3-ijmX3R*M&X3a8%aJN4Vxgm2j< zoSN>G)8y(FyahBAVW>|+wi)rdU6dq%8|w3`6>lvIrH{}Gc6)Eghm8pRRG3K^s`x% zY6-Mg8~parE#%RU8^49jC;gkhF?0PIMn5r;ykuoem!})0C63tb@_b+sSlm@gt$Td90 z{R^C>spS{BlJ^lb9Gu38?C0FACJ~hP5q!MkzqxUkB|UJ2XV=@N{DI|R^dnA2wfaW< zxV^}eZ6v2B_Qrhj-B7xcEGLU)Cj1;|D%W@-d)KxD@6N&-4W6M|ey#Z&=>G60lD?A; z=Ilyp4X>EsL)W--bHd4SecilGt@m8%#6bJ?3fhMrvLh;|Gtp$HkkF*4e%v739S)vCF8ig;TzYaOjnV?= z>PH%98yx|^b}5;kDCQE9!>N9Wl)k4`aVlg_)S)->eAj(0>#CCUE=y^3{#(upS%kG6 zWHiS6I~TGPoUSc0+Uc&vD>1(w*dQZbPn$RU0$tS}IrU!Jzy)s#riwh|AG$r@UV1@u z`8t?>-aW?EObVkr6-sh=6UiBm38zZr=q&9%gZqV^p23x9a-3nvc}_vbS^Ice{e8Vk zro!HG5;bUExKnxw_MFq;4pu2e=X-?G@ex_{`9_{dJTRE1ly9RGS`$PE!Pz-i01eAm z-O`kYQaU+nJ6%jws@#Ed^{DJWdGzI{ed#iI3;)E~^NfO-&cjc53bLr8YQ^=N;^@J4 z_>J_MB+eKJZ#2{%13JWrj{d^W2p+7w>B-VhxZ^69fp5S0jH-G&W}~^le3B*SrHml= zYv3oJn!-KXg3KD!+p{VIxL@c4+w?@n?v5DlH}vbC6TyGJoW=1+;EA|ZO6^DFbJ5@6 z3wlIKN?|`&IR*V?aJXOOALNP-!Q%*+C-vFKb;RCm-(F73pKRwE!(T3EY7o_|RdR_h z)t)fO0@>)nd6}T5Zl$E2>$-8#HA-5BvoOj-i%UMFgkM1m7>5w%&Ppl3E2lp1l#=_B}1lg4J! zg)GC;mPg^m;emBqK0sw*g5Fzc`ky@d)@i=9>hr(#-9_Bk<{!L_d zWIzW*5#lzHF|_z0^nLfu#C2}bG}jW@qi0h@)4;tyian&oT&Yvlg9zFe25nB{x6(By z!fC@1aKf91sjgs;Y;spZWPeW8kgufqKcPeU{as}Zty3pcDf!kIao_HR5HAMTG~bNV zw+NvbsL4A%w&n`A1XJE2DTTV*a=}j(^d0Lw`idpjaT$0V;O;iDwBT-I{cd4{=;$5` zuBDxlR3YHRJZs9i*M*YgZ3sQwQ>7YpC4_W8Krdu+Ocjgoh4cL|;y&$BZA}ZIEL-%L z+NY_UmV}TVd@FWsSE`!n!mq{w=elyUs?-{_B=``mhfY;(dJqH;pA>3vZ>f6gET@NV zY1EwEUivr_SvCjL=+oTv(h_*ty^yE=$)k^wBi{@2xqq(bo8MC4DIG_hput@4)`mH^ zDV7eTz~|GH6A!mRkL(VzX7obE!;p<~8K2qOg2`g*%t&f74jjCg?&95;qpk4+@9}6e z@fnwJ`j8``1L;phmoPJ$g8k&RgCIKG4L-Nv{5uz9iF}}w`uY{0%XPVEEY|H0^jbS* z%@lo$4W<^@WrtVUiFUk3R;Is{PM0+jou93MCIJ2ZyZxQGl?s}gCZm%9YgJD0EO|Q$ zS%J@6aYp(|ioh)3#?O|Ve^CgX1n!$ z3a-S_6l#<)Pc=4P3O~72>f(`E^3X~`ySJqL$)i8*?@I^f{afF?MZTX&6XK|s8}jUi z^ka6PjG=x}$O=rX7Z(~uQ#R%V7phN+e_?)f3VXHv{XFrw@0cB%fmd&rAht$^<0>cc zbr&kcTLvkK2madoSBt-Y3ZVg5^C$aE6Q4g9OoE4m;+qW-+o%+@8PEMY8jA;?$C>6S zp?v{oL}G9oFKh*;eZHaNO7LHt8>5$!V!$OO!%r1G>D#@eT&sc*y5AY=$uXUqy#!fr z(eNL6x|f@IAe5Xt1=EbS@tl?|iP79`MXW=)oN6y^> z@9`35GZou2xv*E*m$paKVJhR=nPWX0C6HBa9oOzV{3Wb)K7q ze#|rbJi43Si|YxFyj{sIIyc*>)BtNr{XEYy(_zQOE%dnI-#X3H6=h7+%{XMEA=^tk zn7Q~Q26}z$$s3hSE$Vh#9(q;exQ#yvKk3)VnvLR^aT6lwOIyr8Cp}?)ehnk@0hrqw z>eJf8;A4)F(9Q8(=u$8|Y-dTRIGuqmCxk{XLiVwlC)uwFCMzEabwz!14zsEC*yqPV zYv=?1w(C;lV0PF|aj2bjeBtr5U!Ypl&J60Ael<7YsTxcJYmjSv=rc8fPVIfY3^~Yp ztj_rmieC^!a#>4u<_q|(Jw!I~xQ^`nD)`3K1XHe-4Lf`VdMCm7?DPAwPT((0ejQ5s z%E9bC@SfTjhSTEjPHcN?tbwvf>ftEo=+=d7w^TV@#Ca33ZyQ@T0GS}(Q8e8voSmMF+0nUJT6%mI zn^YwwSDbguq|U6%MtHg|N+pxoQnG_D*W&f+j3xCiOY=BI}fZwkGYLo!X> z6h=1&3#ID$$V@l?N=GqAJp!EDZ*R$dKZ@*Y_?CRx)rH;K zfIQ*|3FW#EVQ-?h5tt7A8&6=}!Oz@OAff&4OWDp|;6Gl1*XS-8`=KI;j`e`HYjZri z5^GhAK5c&7F4oL8m|WmH+EH4(5c(i?6t)TdiM$XD@iSD z{RAG`_JSmb>CLdugAH?sgBQXD^dMGtK z+aLF{rf(u?V^uag9X)#nygh%`gH0-dM;Cfz|HOHuT^-|57e}tp!#&8rLN21 z2PL#74t&Dzsqn!l7LgyaA)1^DBtP#J)DGI!^DhG7K^jL}9)W*?yZs{BL5i&mqYt*~ z{OGGR`ZRJahJu^t^OgEu3#CufC6tzL$o4?}riXpwrcXDvyk7`%b|kczabyqp2Gg2w zWJ%{wWUtpK$OM`r>yoAHFGu8=A4Z?ByNn%{kGi=3a=u?`I~S~k zmlC6(UPttu)1QII4V_{C9xa{4==Jv9r=YOYEu4F*q%^q%-w_f`@+0{8UArmSK*Ji~DgvS0eL{o!4?e+ACR4I@}- zZ7_|)+J0p_hkfRVx(@stvf0FD0H5di;70|8vRyEzZFv?tJJ)1(RycCHUm>U7cNe=9 z>%3!s%zAc|ur4@TZ-ZmGZTKa&+Y|-$cETDpdCn$>p$6VAqbhw}XL~|kF?v}WZ48{p zV&4Aa4YKj(89TSJlGDziK{VN}le2F%u(b%HdDfksVE`k=m( zV{Ln{JHmR8gNN$^%pm4guvt|>WDJh7PtJX|`$*)UC!-$``;B#WN8R{MMgzAucb*NN zt;!498OM&!O&#Ur0j}DT(OsN(3DA`yCog8am2)R(_`ZQ7asPQ&=PL*BeZpRJe6NM` z;vT4#pk2}n?c}V4UM(Kk7HOM|oQ)R-P|s%n&O1Jx^PTyGnL~vwG*(fKXu;4}nl>J@ zmF0G1`4XDGwb0kQ&nK6CQM4f*vw#e>jv=@Cz4@t&$sAu%hE8YsJ@Yh=!;Nx(Uw1Nc2=W z+gEfrC5$p2p>{jlgbh7{z4{C4nZ6y_7LP;!)GA+w_Gay&fl}XB7nQMW$Q<}Kpw?ee zFog{+gs)M5)Y8qo*?i2eW)bwErv7ZoLS(B=L_gL{$&S1fM5}=Rtoj62iSK%3yoB_v zbJzlvoL*gk?mplEn{))(bVI>wU3#1i!wLrjx}13UKKJY3d^!a6+k6=r?d#;syoE2z zPyCsUES#T=gjRdFl)C42bY}Y@zpm@Q^9L{c)H5e=Pw8U@FSZOFYS|~2%;6{VGqpRN z!g>y#241w8JN-BsMGY(ApMx`GC-mki!SJDq*-n9B5i}i`|Lj{v%fWeI4=C9(0h9%MCwvNog9EHNN_#G)q(RX;ydQOJtF?jN-%wO#H(Nfyj7FyaNy3Ri6Q(9xsxvXmJyyATzT|`|n zUe?UnVKDMS(I5CYQ{TC!YXJ3wuH>KVdDx2Aj5G~7=_bgajQ+-a?u6c9U-)iCo6~0S z{jzX>sqi073ks3lgS&yPv1Q*bDPG5F8 zJdVad``qyhvB{{Dn_R

}s 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 T9dqQEneHe{b{@TXGxGco_7<^* literal 0 HcmV?d00001 From c7c0d6675e6d1548b2e2be0bcf6c93ce96536db7 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Thu, 9 Mar 2017 14:50:11 +0100 Subject: [PATCH 157/181] Added Travis CI file. --- .travis.yml | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..b04234357 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +sudo: required +dist: trusty + +language: cpp + +compiler: + - gcc + +os: + - linux + +#branches: +# only: +# - master + +notifications: + irc: + channels: + - "chat.freenode.net#rawtherapee" + skip_join: true + template: + - "%{repository}/%{branch} (%{commit} - %{author}): %{build_url}: %{message}" + email: + on_success: change + on_failure: always + +env: + global: + - OMP_NUM_THREADS=4 + +before_install: + - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + - sudo apt-get -qq update + - sudo apt-get install build-essential cmake curl git libbz2-dev libcanberra-gtk3-dev libexiv2-dev libexpat-dev libfftw3-dev libglibmm-2.4-dev libgtk-3-dev libgtkmm-3.0-dev libiptcdata0-dev libjpeg8-dev liblcms2-dev libpng12-dev libsigc++-2.0-dev libtiff5-dev zlib1g-dev + - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 + +before_script: + - mkdir build + - cd build + - cmake -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations" -DWITH_LTO="OFF" -DPROC_TARGET_NUMBER="2" .. + +script: make From 15933354856b1ef1b7af69a982153c4a35268905 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Thu, 9 Mar 2017 15:03:16 +0100 Subject: [PATCH 158/181] Updated .travis.yml. Possibly more commit-spam to follow. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b04234357..9bf1f651f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,9 @@ env: before_install: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get -qq update + - sudo apt-get install gcc-6.2 g++-6.2 + - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6.2 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6.2 - sudo apt-get install build-essential cmake curl git libbz2-dev libcanberra-gtk3-dev libexiv2-dev libexpat-dev libfftw3-dev libglibmm-2.4-dev libgtk-3-dev libgtkmm-3.0-dev libiptcdata0-dev libjpeg8-dev liblcms2-dev libpng12-dev libsigc++-2.0-dev libtiff5-dev zlib1g-dev - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 before_script: - mkdir build From b84e570f500a19fec0f6a35fe775061116be283b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Thu, 9 Mar 2017 17:10:33 +0100 Subject: [PATCH 159/181] Revise `SaveAsDialog::okPressed()` (fixes #3737) --- rtgui/options.h | 15 ++++++++-- rtgui/saveasdlg.cc | 72 +++++++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/rtgui/options.h b/rtgui/options.h index efc649417..522fe2bf5 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -42,10 +42,20 @@ // Special name for the Dynamic profile #define DEFPROFILE_DYNAMIC "Dynamic" -class SaveFormat +struct SaveFormat { + SaveFormat() : + format("jpg"), + pngBits(8), + pngCompression(6), + jpegQuality(90), + jpegSubSamp(2), + tiffBits(8), + tiffUncompressed(true), + saveParams(true) + { + } -public: Glib::ustring format; int pngBits; int pngCompression; @@ -54,7 +64,6 @@ public: int tiffBits; bool tiffUncompressed; bool saveParams; - SaveFormat () : format("jpg"), pngBits(8), pngCompression(6), jpegQuality(90), jpegSubSamp(2), tiffBits(8), tiffUncompressed(true), saveParams(true) {}; }; enum ThFileType {FT_Invalid = -1, FT_None = 0, FT_Raw = 1, FT_Jpeg = 2, FT_Tiff = 3, FT_Png = 4, FT_Custom = 5, FT_Tiff16 = 6, FT_Png16 = 7, FT_Custom16 = 8}; diff --git a/rtgui/saveasdlg.cc b/rtgui/saveasdlg.cc index 9ca26d217..865373b60 100644 --- a/rtgui/saveasdlg.cc +++ b/rtgui/saveasdlg.cc @@ -21,6 +21,8 @@ #include "guiutils.h" #include "rtimage.h" +#include "../rtengine/utils.h" + extern Options options; SaveAsDialog::SaveAsDialog (const Glib::ustring &initialDir, Gtk::Window* parent) @@ -217,41 +219,57 @@ SaveFormat SaveAsDialog::getFormat () void SaveAsDialog::okPressed () { - fname = fchooser->get_filename(); - // checking if the filename field is empty. The user have to click Cancel if he don't want to specify a filename // NB: There seem to be a bug in Gtkmm2.22 / FileChooserWidget : if you suppress the filename entry and // click on a folder in the list, the filename field is empty but get_filename will return the folder's path :/ - if (!fname.length() || Glib::file_test (fname, Glib::FILE_TEST_IS_DIR)) { - Glib::ustring msg_ = Glib::ustring("") + M("MAIN_MSG_EMPTYFILENAME") + ""; - Gtk::MessageDialog msgd (*this, msg_, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true); - msgd.run (); + if (Glib::file_test(fname, Glib::FILE_TEST_IS_DIR)) { + fname = fchooser->get_current_name(); + } + + // Checking if the filename field is empty. The user have to click Cancel if he don't want to specify a filename + if (fname.empty()) { + Gtk::MessageDialog( + *this, + Glib::ustring("") + + M("MAIN_MSG_EMPTYFILENAME") + + "", + true, + Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_OK, + true + ).run(); return; } - // resolve extension ambiguities - SaveFormat sf = formatOpts->getFormat (); - Glib::ustring extLower = getExtension (fname).lowercase (); - bool extIsEmpty = (extLower == ""); - bool extIsJpeg = (extLower == "jpg" || extLower == "jpeg" || extLower == "jpe"); - bool extIsTiff = (extLower == "tif" || extLower == "tiff"); - bool extIsPng = (extLower == "png"); + if (getExtension(fname).empty()) { + // Extension is either empty or unfamiliar + fname += '.' + formatOpts->getFormat().format; + } else if ( + !rtengine::hasJpegExtension(fname) + && !rtengine::hasTiffExtension(fname) + && !rtengine::hasPngExtension(fname) + ) { + // Create dialog to warn user that the filename may have two extensions on the end + Gtk::MessageDialog msgd( + *this, + Glib::ustring("") + + M("GENERAL_WARNING") + + ": " + + M("SAVEDLG_WARNFILENAME") + + " \"" + + Glib::path_get_basename (fname) + + '.' + + formatOpts->getFormat().format + + "\"", + true, + Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_OK_CANCEL, + true + ); - if (extIsEmpty || !(extIsJpeg || extIsTiff || extIsPng)) { - // extension is either empty or unfamiliar. - fname += Glib::ustring (".") + sf.format; - } else if ( !(sf.format == "jpg" && extIsJpeg) - && !(sf.format == "tif" && extIsTiff) - && !(sf.format == "png" && extIsPng ) ) { - // create dialog to warn user that the filename may have two extensions on the end. - Glib::ustring msg_ = Glib::ustring ("") + M("GENERAL_WARNING") + ": " - + M("SAVEDLG_WARNFILENAME") + " \"" + Glib::path_get_basename (fname) - + "." + sf.format + "\""; - Gtk::MessageDialog msgd (*this, msg_, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK_CANCEL, true); - - if (msgd.run () == Gtk::RESPONSE_OK) { - fname += Glib::ustring (".") + sf.format; + if (msgd.run() == Gtk::RESPONSE_OK) { + fname += "." + formatOpts->getFormat().format; } else { return; } From 510174bfa3683be17921a0ac2084eab1212fbe18 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Fri, 10 Mar 2017 13:12:00 +0100 Subject: [PATCH 160/181] Updated .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9bf1f651f..5fd28cfdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,8 +31,8 @@ env: before_install: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get -qq update - - sudo apt-get install gcc-6.2 g++-6.2 - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6.2 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6.2 + - sudo apt-get install gcc-6 g++-6 + - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 - sudo apt-get install build-essential cmake curl git libbz2-dev libcanberra-gtk3-dev libexiv2-dev libexpat-dev libfftw3-dev libglibmm-2.4-dev libgtk-3-dev libgtkmm-3.0-dev libiptcdata0-dev libjpeg8-dev liblcms2-dev libpng12-dev libsigc++-2.0-dev libtiff5-dev zlib1g-dev before_script: From 181365dd791a10eebc1cb2e8515bf75352312ebb Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Fri, 10 Mar 2017 13:35:23 +0100 Subject: [PATCH 161/181] Updated .travis.yml, last attempt. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5fd28cfdf..0aa85f3b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ env: before_install: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + - sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ xenial main" - sudo apt-get -qq update - sudo apt-get install gcc-6 g++-6 - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6 From 2b3279c3d7a5f6ae6aa0a5a0498bcc919d0e9253 Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Fri, 10 Mar 2017 13:46:24 +0100 Subject: [PATCH 162/181] Turned off Travis until it supports GTK+ >=3.16. --- .travis.yml => .travis.yml.fixme | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .travis.yml => .travis.yml.fixme (100%) diff --git a/.travis.yml b/.travis.yml.fixme similarity index 100% rename from .travis.yml rename to .travis.yml.fixme From 825002378b061385dab0e982a674355bc34418a8 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 10 Mar 2017 23:05:19 +0100 Subject: [PATCH 163/181] =?UTF-8?q?Added=20JK=20Han=20and=20Kalle=20S?= =?UTF-8?q?=C3=B6derman=20for=20contributing=20to=20Pentax=20pixelshift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AUTHORS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 3aa555bc0..ff0f89361 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -45,6 +45,7 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati André Gauthier Sébastien Guyader M. Dávid Gyurkó + JK Han (pinholecam) Arturs Jekabsons Marián Kyral Oscar de Lama @@ -55,6 +56,7 @@ Other contributors (profiles, ideas, mockups, testing, forum activity, translati Wim ter Meer Alberto Righetto Kostia (Kildor) Romanov + Kalle Söderman Johan Thor Vitalis Tiknius TooWaBoo From f226934b5736d8ca2d3f3314bb2ce5777e8ae0be Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 11 Mar 2017 00:25:49 +0100 Subject: [PATCH 164/181] Pixelshift: Added option to equalize brightness of frames and cleaned gui. --- rtdata/languages/default | 22 +- rtengine/pixelshift.cc | 197 ++++++++++++-- rtengine/procevents.h | 1 + rtengine/procparams.cc | 14 + rtengine/procparams.h | 1 + rtengine/rawimagesource.cc | 6 + rtengine/refreshmap.cc | 3 +- rtgui/bayerprocess.cc | 531 +++++++++++++++++++++---------------- rtgui/bayerprocess.h | 68 +++-- rtgui/guiutils.h | 22 ++ rtgui/paramsedited.cc | 8 +- rtgui/paramsedited.h | 1 + 12 files changed, 577 insertions(+), 297 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 575cc5136..118e0ed8b 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -704,6 +704,7 @@ HISTORY_MSG_469;EvPixelShiftMedian3 HISTORY_MSG_470;EvPixelShiftMotionMethod HISTORY_MSG_471;EvPixelShiftSmooth HISTORY_MSG_472;EvPixelShiftLmmse +HISTORY_MSG_473;EvPixelShiftEqualBright HISTORY_NEWSNAPSHOT;Add HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s HISTORY_SNAPSHOT;Snapshot @@ -1692,20 +1693,24 @@ TP_RAW_PIXELSHIFTNONGREENHORIZONTAL;Check red/blue horizontal TP_RAW_PIXELSHIFTNONGREENVERTICAL;Check red/blue vertical TP_RAW_PIXELSHIFTMEDIAN;Median TP_RAW_PIXELSHIFTMEDIAN3;Exclude selected frame from median -TP_RAW_PIXELSHIFTHOLEFILL;3x3 new: Fill holes -TP_RAW_PIXELSHIFTBLUR;3x3 new: Blur -TP_RAW_PIXELSHIFTSMOOTH;3x3 new: Smooth transitions +TP_RAW_PIXELSHIFTHOLEFILL;Fill holes in motion mask +TP_RAW_PIXELSHIFTBLUR;Blur motion mask +TP_RAW_PIXELSHIFTSIGMA_TOOLTIP;Default radius of 1.0 usually fits good for base ISO. Increase value for high ISO shots,\n5.0 is a good starting point for high ISO shots.\nWatch motion mask while changing the value. +TP_RAW_PIXELSHIFTSMOOTH;Smooth transitions +TP_RAW_PIXELSHIFTLMMSE_TOOLTIP;Use lmmse instead of amaze for motion areas.\nUseful for High ISO images. TP_RAW_PIXELSHIFTLMMSE;Use lmmse for motion parts +TP_RAW_PIXELSHIFTEQUALBRIGHT;Equalize brightness of frames +TP_RAW_PIXELSHIFTEQUALBRIGHT_TOOLTIP;Equalize the brightness of the frames to the brightness of the selected frame.\nIf there are overexposed areas in the frames select the brightest frame to avoid magenta colour cast in overexposed areas or enable motion correction. TP_RAW_PIXELSHIFTEXP0;Experimental -TP_RAW_PIXELSHIFTGREEN;Check dual green -TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue cross +TP_RAW_PIXELSHIFTGREEN;Check green channel for motion +TP_RAW_PIXELSHIFTNONGREENCROSS;Check red/blue channels for motion TP_RAW_PIXELSHIFTNONGREENCROSS2;Check green amaze TP_RAW_PIXELSHIFTNONGREENAMAZE;Check red/blue amaze TP_RAW_PIXELSHIFTMOTION;Motion detection level (deprecated) TP_RAW_PIXELSHIFTMOTION_TOOLTIP;0 means no motion detection\n1 - 99 means motion will be detected according to this value. Increase value to increase detection rate\n100 means the Amaze demosaiced frame will be used TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP;Fill holes in motion mask TP_RAW_PIXELSHIFTBLUR_TOOLTIP;Blur motion mask -TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions (requires 3x3 new Blur)\nSet to 0 to disable smooth transitions\nSet to 1 to get Amaze or Median +TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP;Smooth transitions between areas with and without motion.\nSet to 0 to disable smooth transitions\nSet to 1 to get Amaze/lmmse or Median TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP;Use median of all frames instead of selected frame for regions with motion.\nRemoves objects which are at different places in all frames.\nGives motion effect on slow moving (overlapping) objects. TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP;Excludes selected frame from median.\nUseful if moving objects overlap in frame 2 and 3 TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a mask showing the regions with motion @@ -1718,10 +1723,11 @@ TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN;StdDev factor Green TP_RAW_PIXELSHIFTSTDDEVFACTORRED;StdDev factor Red TP_RAW_PIXELSHIFTSTDDEVFACTORBLUE;StdDev factor Blue -TP_RAW_PIXELSHIFTEPERISO;e per ISO +TP_RAW_PIXELSHIFTEPERISO;ISO adaption +TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;The default value (0.0) should work fine for base ISO.\nIncrease the value to improve motion detection for higher ISO.\nIncrease in small steps and watch the motion mask while increasing. TP_RAW_PIXELSHIFTNREADISO;nRead TP_RAW_PIXELSHIFTPRNU;PRNU (%) -TP_RAW_PIXELSHIFTSIGMA;Blur sigma +TP_RAW_PIXELSHIFTSIGMA;Blur radius TP_RAW_PIXELSHIFTMASKTHRESHOLD;3x3 new threshold TP_RAW_PIXELSHIFTREDBLUEWEIGHT;Red&Blue weight TP_RAW_SENSOR_BAYER_LABEL;Sensor with Bayer Matrix diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 7696c1874..8ba9c1cfd 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -45,7 +45,7 @@ float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperI float gDiff = a - b; gDiff *= eperIso; gDiff *= gDiff; - float avg = (a + b) / 2.f; + float avg = (a + b) * 0.5f; avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); @@ -96,17 +96,17 @@ float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nr float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // check non green cross - float hDiff = (right + left) / 2.f - centre; + float hDiff = (right + left) * 0.5f - centre; hDiff *= eperIso; hDiff *= hDiff; - float vDiff = (top + bottom) / 2.f - centre; + float vDiff = (top + bottom) * 0.5f - centre; vDiff *= eperIso; vDiff *= vDiff; - float avg = (right + left + top + bottom) / 4.f; + float avg = ((right + left) + (top + bottom)) * 0.25f; avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); - float result = std::min(hDiff - stddev, vDiff - stddev); + float result = std::min(hDiff, vDiff) - stddev; if(!showMotion) { return result; @@ -896,21 +896,31 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool det #else void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection) { - +#ifdef PIXELSHIFTDEV BENCHFUN - +#endif const bool detectMotion = bayerParams.pixelShiftMotion > 0; const int motion = bayerParams.pixelShiftMotion; const bool showMotion = bayerParams.pixelshiftShowMotion; - const bool showOnlyMask = bayerParams.pixelshiftShowMotionMaskOnly; + const bool showOnlyMask = bayerParams.pixelshiftShowMotionMaskOnly && showMotion; const RAWParams::BayerSensor::ePSMotionCorrection gridSize_ = bayerParams.pixelShiftMotionCorrection; const bool adaptive = bayerParams.pixelShiftAutomatic; +#ifdef PIXELSHIFTDEV float stddevFactorGreen = bayerParams.pixelShiftStddevFactorGreen; float stddevFactorRed = bayerParams.pixelShiftStddevFactorRed; float stddevFactorBlue = bayerParams.pixelShiftStddevFactorBlue; - float eperIso = bayerParams.pixelShiftEperIso; float nreadIso = bayerParams.pixelShiftNreadIso; float prnu = bayerParams.pixelShiftPrnu; + const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight + 1.f; +#else + float stddevFactorGreen = 5.f; + float stddevFactorRed = 5.f; + float stddevFactorBlue = 5.f; + float nreadIso = 0.f; + float prnu = 1.f; + const float redBlueWeight = 0.7f + 1.f; +#endif + float eperIso = bayerParams.pixelShiftEperIso; const bool checkNonGreenHorizontal = bayerParams.pixelShiftNonGreenHorizontal; const bool checkNonGreenVertical = bayerParams.pixelShiftNonGreenVertical; const bool checkNonGreenCross = bayerParams.pixelShiftNonGreenCross; @@ -918,13 +928,18 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const bool checkNonGreenCross2 = bayerParams.pixelShiftNonGreenCross2; const bool checkGreen = bayerParams.pixelShiftGreen; const float greenWeight = 2.f; - const float redBlueWeight = bayerParams.pixelShiftRedBlueWeight + 1.f; const bool blurMap = bayerParams.pixelShiftBlur; const float sigma = bayerParams.pixelShiftSigma; +#ifdef PIXELSHIFTDEV const float threshold = bayerParams.pixelShiftSum + 9.f; +#else + const float threshold = 3.f + 9.f; +#endif const bool experimental0 = bayerParams.pixelShiftExp0; const bool holeFill = bayerParams.pixelShiftHoleFill; - const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmoothFactor > 0.; + const bool equalBrightness = bayerParams.pixelShiftEqualBright; + const bool smoothTransitions = blurMap && bayerParams.pixelShiftSmoothFactor > 0. && !showOnlyMask; + const bool automatic = bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic; const float smoothFactor = 1.0 - bayerParams.pixelShiftSmoothFactor; static const float nReadK3II[] = { 3.4f, // ISO 100 @@ -1040,6 +1055,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); int gridSize = 1; bool nOf3x3 = false; + switch (gridSize_) { case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: @@ -1067,6 +1083,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(plistener) { plistener->setProgress(1.0); } + return; } @@ -1110,11 +1127,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); } +#ifdef PIXELSHIFTDEV std::cout << "WL: " << c_white[0] << " BL: " << c_black[0] << " ePerIso multiplicator: " << (65535.f / (c_white[0] - c_black[0])) << std::endl; - - float eperIsoRed = (eperIso / scale_mul[0]) * (65535.f / (c_white[0]- c_black[0])); - float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1]- c_black[1])); - float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2]- c_black[2])); +#endif + float eperIsoRed = (eperIso / scale_mul[0]) * (65535.f / (c_white[0] - c_black[0])); + float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1] - c_black[1])); + float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2] - c_black[2])); // printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); @@ -1161,6 +1179,90 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA array2D psG2(winw + 32, winh); array2D psBlue(winw + 32, winh); +// calculate average green brightness for each frame + double greenBrightness[4] = {}; + + if(equalBrightness) { + LUT *histo[4]; + + for(int i = 0; i < 4; ++i) { + histo[i] = new LUT(65536); + histo[i]->clear(); + } + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + LUT *histoThr[4]; + + for(int i = 0; i < 4; ++i) { + histoThr[i] = new LUT(65536); + histoThr[i]->clear(); + } + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + int j = winx + 1; + int c = FC(i, j); + + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = (c & 1); + offset ^= 1; // 0 => 1 or 1 => 0 + + for(; j < winw - 1; ++j) { + offset ^= 1; // 0 => 1 or 1 => 0 + + // store the values from the 4 frames into 4 different temporary planes + float green1 = (*rawDataFrames[1 - offset])[i - offset + 1][j]; + float green2 = (*rawDataFrames[3 - offset])[i + offset][j + 1]; + (*histoThr[1 - offset])[green1]++; + (*histoThr[3 - offset])[green2]++; + } + } + + #pragma omp critical + { + for(int i = 0; i < 4; ++i) { + (*histo[i]) += (*histoThr[i]); + delete histoThr[i]; + } + } + } + + float medians[4]; + + for(int i = 0; i < 4; ++i) { + //find median of histogram + uint32_t median = 0, count = 0; + uint32_t datalen = (winh - 2) * (winw - 2) / 2; + + while (count < datalen / 2) { + count += (*histo[i])[median]; + ++median; + } + + medians[i] = (median + median - 1) / 2.f; + delete histo[i]; + } + + const float medianMaster = medians[frame]; + + for(int i = 0; i < 4; ++i) { + greenBrightness[i] = medianMaster / medians[i]; + } + +#ifdef PIXELSHIFTDEV + std::cout << "brightness factors by median : " << greenBrightness[0] << " " << greenBrightness[1] << " " << greenBrightness[2] << " " << greenBrightness[3] << std::endl; +#endif + + } else { + greenBrightness[0] = greenBrightness[1] = greenBrightness[2] = greenBrightness[3] = 1.f; + } + // fill channels psRed, psG1, psG2 and psBlue #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) @@ -1188,16 +1290,17 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA offset ^= 1; // 0 => 1 or 1 => 0 // store the values from the 4 frames into 4 different temporary planes - greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j]; - greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1]; - nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; + greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset]; + greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]; + nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset] * greenBrightness[(offset << 1) + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * greenBrightness[2 - offset]; } } // now that the temporary planes are filled for easy access we do the motion detection +#ifdef PIXELSHIFTDEV int sum[2] = {0}; - +#endif float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; array2D psMask(winw, winh); @@ -1207,7 +1310,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #pragma omp parallel #endif { +#ifdef PIXELSHIFTDEV int sumThr[2] = {0}; +#endif #ifdef _OPENMP #pragma omp for schedule(dynamic,16) nowait #endif @@ -1220,7 +1325,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float greenDifMax[gridSize]; // Here we store the maximum differences per Column + // green channel motion detection checks the grid around the pixel for differences in green channels +#ifdef PIXELSHIFTDEV + if(detectMotion || (adaptive && checkGreen)) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid @@ -1312,6 +1420,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } +#endif // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 int lastIndex = gridSize - 1; @@ -1336,9 +1445,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA bool skipNext = false; float gridMax; +#ifdef PIXELSHIFTDEV + if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps +#endif gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); +#ifdef PIXELSHIFTDEV + skipNext = skip; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex @@ -1386,12 +1500,19 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; } +#endif + if (gridMax > thresh - korr) { +#ifdef PIXELSHIFTDEV sumThr[offset] ++; if(nOf3x3) { +#endif psMask[i][j] = greenWeight; - } else if((offset == (frame & 1)) && checkNonGreenVertical) { +#ifdef PIXELSHIFTDEV + } + + else if((offset == (frame & 1)) && checkNonGreenVertical) { if(frame > 1) { green[i + offsY][j + offsX] = blueRow ? psG1[i][j] : psG2[i][j]; } else { @@ -1408,6 +1529,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, redDest, blueDest); } } + +#endif // do not set the motion pixel values. They have already been set by demosaicer or showMotion continue; } @@ -1424,12 +1547,17 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); if(redDiff > 0.f) { +#ifdef PIXELSHIFTDEV + if(nOf3x3) { +#endif psMask[i][j] = redBlueWeight; +#ifdef PIXELSHIFTDEV } else { paintMotionMask(j + offsX, showMotion, redDiff, showOnlyMask, redDest, blueDest, greenDest); } +#endif continue; } @@ -1442,17 +1570,24 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); if(blueDiff > 0.f) { +#ifdef PIXELSHIFTDEV + if(nOf3x3) { +#endif psMask[i][j] = redBlueWeight; +#ifdef PIXELSHIFTDEV } else { paintMotionMask(j + offsX, showMotion, blueDiff, showOnlyMask, blueDest, redDest, greenDest); } +#endif continue; } } +#ifdef PIXELSHIFTDEV + if(checkNonGreenHorizontal) { float redLeft = psRed[ i ][j - 1]; float redCentre = psRed[ i ][ j ]; @@ -1599,9 +1734,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(experimental0) { // for experiments } + +#endif } - if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else if(!(adaptive && nOf3x3)) { // no motion detected, replace the a priori demosaiced values by the pixelshift combined values @@ -1612,6 +1749,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } +#ifdef PIXELSHIFTDEV + #ifdef _OPENMP #pragma omp critical #endif @@ -1619,13 +1758,16 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA sum[0] += sumThr[0]; sum[1] += sumThr[0]; } +#endif } +#ifdef PIXELSHIFTDEV float percent0 = 100.f * sum[0] / pixelcount; float percent1 = 100.f * sum[1] / pixelcount; std::cout << fileName << " : Green detections at stddev " << std::setprecision( 2 ) << bayerParams.pixelShiftStddevFactorGreen << " : Frame 1/3 : " << std::setprecision( 6 ) << sum[0] << " (" << percent0 << "%)" << " Frame 2/4 : " << sum[1] << " (" << percent1 << "%)" << std::endl; +#endif if(adaptive && nOf3x3) { if(blurMap) { @@ -1641,9 +1783,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #pragma omp parallel for schedule(dynamic,16) for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { - float *greenDest = green[i + offsY]; - float *redDest = red[i + offsY]; - float *blueDest = blue[i + offsY]; int j = winx + border - offsX; float v3sum[3] = {0.f}; @@ -1680,7 +1819,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { #ifdef __SSE2__ - if(!(showMotion && showOnlyMask) && smoothTransitions) { + if(smoothTransitions) { vfloat onev = F2V(1.f); vfloat smoothv = F2V(smoothFactor); int j = winx + border - offsX; @@ -1704,7 +1843,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { if(mask[i][j] == 255) { paintMotionMask(j + offsX, showMotion, 0.5f, showOnlyMask, greenDest, redDest, blueDest); - } else if(showMotion && showOnlyMask) { // we want only motion mask => paint areas without motion in pure black + } else if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black red[i + offsY][j + offsX] = green[i + offsY][j + offsX] = blue[i + offsY][j + offsX] = 0.f; } else { if(smoothTransitions) { @@ -1714,11 +1853,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float blend = pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); #endif red[i + offsY][j + offsX] = intp(blend, red[i + offsY][j + offsX], psRed[i][j] ); - green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX], (psG1[i][j] + psG2[i][j]) / 2.f); + green[i + offsY][j + offsX] = intp(blend, green[i + offsY][j + offsX], (psG1[i][j] + psG2[i][j]) * 0.5f); blue[i + offsY][j + offsX] = intp(blend, blue[i + offsY][j + offsX], psBlue[i][j]); } else { red[i + offsY][j + offsX] = psRed[i][j]; - green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) / 2.f; + green[i + offsY][j + offsX] = (psG1[i][j] + psG2[i][j]) * 0.5f; blue[i + offsY][j + offsX] = psBlue[i][j]; } } diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 13294c73a..3252bcc86 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -499,6 +499,7 @@ enum ProcEvent { EvPixelShiftMotionMethod = 469, EvPixelShiftSmooth = 470, EvPixelShiftLmmse = 471, + EvPixelShiftEqualBright = 472, NUMOFEVENTS }; diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 50b77b8d1..ea18492e2 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -900,6 +900,7 @@ void RAWParams::BayerSensor::setPixelShiftDefaults() pixelShiftSmoothFactor = 0.7; pixelShiftExp0 = false; pixelShiftLmmse = false; + pixelShiftEqualBright = false; pixelShiftNonGreenCross = true; pixelShiftNonGreenCross2 = false; pixelShiftNonGreenAmaze = false; @@ -3497,6 +3498,10 @@ int ProcParams::save (const Glib::ustring &fname, const Glib::ustring &fname2, b keyFile.set_boolean ("RAW Bayer", "pixelShiftLmmse", raw.bayersensor.pixelShiftLmmse ); } + if (!pedited || pedited->raw.bayersensor.pixelShiftEqualBright) { + keyFile.set_boolean ("RAW Bayer", "pixelShiftEqualBright", raw.bayersensor.pixelShiftEqualBright ); + } + if (!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross) { keyFile.set_boolean ("RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross ); } @@ -7765,6 +7770,14 @@ int ProcParams::load (const Glib::ustring &fname, ParamsEdited* pedited) } } + if (keyFile.has_key ("RAW Bayer", "pixelShiftEqualBright")) { + raw.bayersensor.pixelShiftEqualBright = keyFile.get_boolean("RAW Bayer", "pixelShiftEqualBright"); + + if (pedited) { + pedited->raw.bayersensor.pixelShiftEqualBright = true; + } + } + if (keyFile.has_key ("RAW Bayer", "pixelShiftNonGreenCross")) { raw.bayersensor.pixelShiftNonGreenCross = keyFile.get_boolean("RAW Bayer", "pixelShiftNonGreenCross"); @@ -8251,6 +8264,7 @@ bool ProcParams::operator== (const ProcParams& other) && raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor && raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0 && raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse + && raw.bayersensor.pixelShiftEqualBright == other.raw.bayersensor.pixelShiftEqualBright && raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross && raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2 && raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 096decd90..77531a072 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1232,6 +1232,7 @@ public: double pixelShiftSmoothFactor; bool pixelShiftExp0; bool pixelShiftLmmse; + bool pixelShiftEqualBright; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; bool pixelShiftNonGreenAmaze; diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 2cb642285..1c4b771ea 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2029,7 +2029,9 @@ void RawImageSource::demosaic(const RAWParams &raw) } else { RAWParams::BayerSensor bayerParams = raw.bayersensor; if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic) { + bool pixelShiftEqualBright = bayerParams.pixelShiftEqualBright; bayerParams.setPixelShiftDefaults(); + bayerParams.pixelShiftEqualBright = pixelShiftEqualBright; } else if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Off) { bayerParams.pixelShiftMotion = 0; bayerParams.pixelShiftAutomatic = false; @@ -2038,7 +2040,9 @@ void RawImageSource::demosaic(const RAWParams &raw) if(!bayerParams.pixelshiftShowMotion || bayerParams.pixelShiftNonGreenAmaze || bayerParams.pixelShiftNonGreenCross2 || (bayerParams.pixelShiftBlur && bayerParams.pixelShiftSmoothFactor > 0.0)) { if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction +#ifdef PIXELSHIFTDEV if(!bayerParams.pixelShiftMedian3) { +#endif if(bayerParams.pixelShiftLmmse) { lmmse_interpolate_omp(W, H, *(rawDataFrames[0]), red, green, blue, raw.bayersensor.lmmse_iterations); } else { @@ -2066,6 +2070,7 @@ void RawImageSource::demosaic(const RAWParams &raw) blue[i][j] = median(blue[i][j],blueTmp[0][i+1][j],blueTmp[1][i+1][j+1],blueTmp[2][i][j+1]); } } +#ifdef PIXELSHIFTDEV } else { multi_array2D redTmp(W,H); multi_array2D greenTmp(W,H); @@ -2135,6 +2140,7 @@ void RawImageSource::demosaic(const RAWParams &raw) } } } +#endif } else { if(bayerParams.pixelShiftLmmse) { lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index d759f6866..fa673b0eb 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -498,7 +498,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DEMOSAIC, // EvPixelShiftMedian3 DEMOSAIC, // EvPixelShiftMotionMethod DEMOSAIC, // EvPixelShiftSmooth - DEMOSAIC // EvPixelShiftLmmse + DEMOSAIC, // EvPixelShiftLmmse + DEMOSAIC // EvPixelShiftEqualBright }; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index dbf41781a..af079e5fa 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -73,7 +73,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA } dcbIterations->show(); - dcbEnhance = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_DCBENHANCE"))); + dcbEnhance = Gtk::manage (new MyCheckButton(M("TP_RAW_DCBENHANCE"))); dcbOptions->pack_start(*dcbIterations); dcbOptions->pack_start(*dcbEnhance); pack_start( *dcbOptions, Gtk::PACK_SHRINK, 4); @@ -96,6 +96,10 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftFrame = Gtk::manage (new Gtk::VBox ()); pixelShiftFrame->set_border_width(0); + pixelShiftEqualBright = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTEQUALBRIGHT"))); + pixelShiftEqualBright->set_tooltip_text (M("TP_RAW_PIXELSHIFTEQUALBRIGHT_TOOLTIP")); + pixelShiftFrame->pack_start(*pixelShiftEqualBright); + Gtk::HBox* hb3 = Gtk::manage (new Gtk::HBox ()); hb3->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONMETHOD") + ": ")), Gtk::PACK_SHRINK, 4); pixelShiftMotionMethod = Gtk::manage (new MyComboBoxText ()); @@ -110,24 +114,44 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); pixelShiftOptions->set_border_width(0); - pixelShiftShowMotion = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); + pixelShiftShowMotion = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); pixelShiftShowMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP")); pixelShiftFrame->pack_start(*pixelShiftShowMotion); - pixelShiftShowMotionMaskOnly = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); + pixelShiftShowMotionMaskOnly = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); pixelShiftShowMotionMaskOnly->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP")); pixelShiftFrame->pack_start(*pixelShiftShowMotionMaskOnly); - pixelShiftAutomatic = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); +#ifdef PIXELSHIFTDEV + pixelShiftAutomatic = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); pixelShiftOptions->pack_start(*pixelShiftAutomatic); - - pixelShiftGreen = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTGREEN"))); +#endif + pixelShiftGreen = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTGREEN"))); pixelShiftOptions->pack_start(*pixelShiftGreen); - pixelShiftBlur = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTBLUR"))); - pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTBLUR_TOOLTIP")); + pixelShiftNonGreenCross = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); + pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); + + pixelShiftHoleFill = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); + pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); + pixelShiftOptions->pack_start(*pixelShiftHoleFill); + + pixelShiftBlur = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTBLUR"))); + pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTSIGMA_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftBlur); + pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 25, 0.1, 1.0)); + pixelShiftSigma->set_tooltip_text (M("TP_RAW_PIXELSHIFTSIGMA_TOOLTIP")); + pixelShiftSigma->setAdjusterListener (this); + + if (pixelShiftSigma->delay < options.adjusterMaxDelay) { + pixelShiftSigma->delay = options.adjusterMaxDelay; + } + + pixelShiftSigma->show(); + pixelShiftOptions->pack_start(*pixelShiftSigma); + + pixelShiftSmooth = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSMOOTH"), 0, 1, 0.05, 0.7)); pixelShiftSmooth->set_tooltip_text (M("TP_RAW_PIXELSHIFTSMOOTH_TOOLTIP")); pixelShiftSmooth->setAdjusterListener (this); @@ -139,39 +163,48 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftSmooth->show(); pixelShiftOptions->pack_start(*pixelShiftSmooth); - pixelShiftHoleFill = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); - pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); - pixelShiftOptions->pack_start(*pixelShiftHoleFill); + pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -5.0, 5.0, 0.05, 0.0)); + pixelShiftEperIso->set_tooltip_text(M("TP_RAW_PIXELSHIFTEPERISO_TOOLTIP")); + pixelShiftEperIso->setAdjusterListener (this); - pixelShiftMedian = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTMEDIAN"))); + if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { + pixelShiftEperIso->delay = options.adjusterMaxDelay; + } + + pixelShiftEperIso->show(); + pixelShiftOptions->pack_start(*pixelShiftEperIso); + + + pixelShiftMedian = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTMEDIAN"))); pixelShiftMedian->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftMedian); - pixelShiftMedian3 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTMEDIAN3"))); + +#ifdef PIXELSHIFTDEV + pixelShiftMedian3 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTMEDIAN3"))); pixelShiftMedian3->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftMedian3); - pixelShiftNonGreenCross = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); - pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); - - pixelShiftNonGreenCross2 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); + pixelShiftNonGreenCross2 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); + pixelShiftNonGreenAmaze = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); - pixelShiftNonGreenHorizontal = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); + pixelShiftNonGreenHorizontal = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"))); + pixelShiftNonGreenVertical = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"))); pixelShiftOptions->pack_start(*pixelShiftNonGreenVertical); - pixelShiftExp0 = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTEXP0"))); + pixelShiftExp0 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTEXP0"))); pixelShiftOptions->pack_start(*pixelShiftExp0); - - pixelShiftLmmse = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_PIXELSHIFTLMMSE"))); +#endif + pixelShiftLmmse = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTLMMSE"))); + pixelShiftLmmse->set_tooltip_text (M("TP_RAW_PIXELSHIFTLMMSE_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftLmmse); +#ifdef PIXELSHIFTDEV pixelShiftMotion = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMOTION"), 0, 100, 1, 70)); pixelShiftMotion->setAdjusterListener (this); pixelShiftMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTMOTION_TOOLTIP")); @@ -182,7 +215,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotion->show(); pixelShiftOptions->pack_start(*pixelShiftMotion); - Gtk::HBox* hb2 = Gtk::manage (new Gtk::HBox ()); hb2->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONCORRECTION") + ": ")), Gtk::PACK_SHRINK, 0); pixelShiftMotionCorrection = Gtk::manage (new MyComboBoxText ()); @@ -196,7 +228,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftMotionCorrection->show(); hb2->pack_start(*pixelShiftMotionCorrection); pixelShiftOptions->pack_start(*hb2); - pixelShiftStddevFactorGreen = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN"), 2, 8, 0.1, 5)); pixelShiftStddevFactorGreen->setAdjusterListener (this); @@ -226,17 +257,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftStddevFactorBlue->show(); pixelShiftOptions->pack_start(*pixelShiftStddevFactorBlue); +#endif - pixelShiftEperIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTEPERISO"), -5.0, 5.0, 0.05, 0.0)); - pixelShiftEperIso->setAdjusterListener (this); - - if (pixelShiftEperIso->delay < options.adjusterMaxDelay) { - pixelShiftEperIso->delay = options.adjusterMaxDelay; - } - - pixelShiftEperIso->show(); - pixelShiftOptions->pack_start(*pixelShiftEperIso); - +#ifdef PIXELSHIFTDEV pixelShiftNreadIso = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTNREADISO"), -2.0, 2.0, 0.05, 0.0)); pixelShiftNreadIso->setAdjusterListener (this); @@ -258,16 +281,6 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftPrnu->show(); pixelShiftOptions->pack_start(*pixelShiftPrnu); - pixelShiftSigma = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTSIGMA"), 0.5, 25, 0.1, 1.0)); - pixelShiftSigma->setAdjusterListener (this); - - if (pixelShiftSigma->delay < options.adjusterMaxDelay) { - pixelShiftSigma->delay = options.adjusterMaxDelay; - } - - pixelShiftSigma->show(); - pixelShiftOptions->pack_start(*pixelShiftSigma); - pixelShiftSum = Gtk::manage (new Adjuster (M("TP_RAW_PIXELSHIFTMASKTHRESHOLD"), 1.0, 8.0, 0.1, 3.0)); pixelShiftSum->setAdjusterListener (this); @@ -287,6 +300,7 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftRedBlueWeight->show(); pixelShiftOptions->pack_start(*pixelShiftRedBlueWeight); +#endif pixelShiftFrame->pack_start(*pixelShiftOptions); pixelShiftOptions->hide(); @@ -296,40 +310,42 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA //pack_start( *Gtk::manage( new Gtk::HSeparator()), Gtk::PACK_SHRINK, 0 ); //allOptions = Gtk::manage (new Gtk::VBox ()); //allOptions->set_border_width(2); - //allEnhance = Gtk::manage (new Gtk::CheckButton(M("TP_RAW_ALLENHANCE"))); + //allEnhance = Gtk::manage (new MyCheckButton(M("TP_RAW_ALLENHANCE"))); //allOptions->pack_start(*allEnhance); //pack_start( *allOptions, Gtk::PACK_SHRINK, 4); - methodconn = method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) ); - psmcconn = pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) ); - imagenumberconn = imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) ); - dcbEnhconn = dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true); - pixelShiftMotionMethodConn = pixelShiftMotionMethod->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::pixelShiftMotionMethodChanged) ); - pixelShiftShowMotionconn = pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true); - pixelShiftShowMotionMaskOnlyconn = pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true); - pixelShiftAutomaticconn = pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true); - pixelShiftNonGreenHorizontalconn = pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true); - pixelShiftNonGreenVerticalconn = pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true); - pixelShiftHoleFillconn = pixelShiftHoleFill->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftHoleFillChanged), true); - pixelShiftMedianconn = pixelShiftMedian->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedianChanged), true); - pixelShiftMedian3conn = pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true); - pixelShiftGreenconn = pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true); - pixelShiftBlurconn = pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true); - pixelShiftExp0conn = pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true); - pixelShiftLmmseconn = pixelShiftLmmse->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftLmmseChanged), true); - pixelShiftNonGreenCrossconn = pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true); - pixelShiftNonGreenCross2conn = pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true); - pixelShiftNonGreenAmazeconn = pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true); + method->connect(method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) )); + imageNumber->connect(imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) )); + dcbEnhance->connect ( dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true)); + pixelShiftMotionMethod->connect(pixelShiftMotionMethod->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::pixelShiftMotionMethodChanged) )); + pixelShiftShowMotion->connect(pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true)); + pixelShiftShowMotionMaskOnly->connect(pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true)); + pixelShiftHoleFill->connect(pixelShiftHoleFill->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftHoleFillChanged), true)); + pixelShiftMedian->connect(pixelShiftMedian->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedianChanged), true)); + pixelShiftGreen->connect(pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true)); + pixelShiftBlur->connect(pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true)); + pixelShiftLmmse->connect(pixelShiftLmmse->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftLmmseChanged), true)); + pixelShiftEqualBright->connect(pixelShiftEqualBright->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftEqualBrightChanged), true)); + pixelShiftNonGreenCross->connect(pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true)); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->connect(pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) )); + pixelShiftAutomatic->connect(pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true)); + pixelShiftNonGreenHorizontal->connect(pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true)); + pixelShiftNonGreenVertical->connect(pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true)); + pixelShiftMedian3->connect(pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true)); + pixelShiftExp0->connect(pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true)); + pixelShiftNonGreenCross2->connect(pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true)); + pixelShiftNonGreenAmaze->connect(pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true)); +#endif } void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { disableListener (); - methodconn.block (true); - dcbEnhconn.block (true); - imagenumberconn.block (true); - psmcconn.block (true); + method->block (true); + dcbEnhance->block (true); + imageNumber->block (true); //allEnhconn.block (true); method->set_active(procparams::RAWParams::BayerSensor::numMethods); @@ -349,31 +365,34 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); pixelShiftShowMotionMaskOnly->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); - pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); - pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftHoleFill->set_inconsistent(!pedited->raw.bayersensor.pixelShiftHoleFill); pixelShiftMedian->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian); - pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_inconsistent(!pedited->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); pixelShiftSmooth->setEditedState ( pedited->raw.bayersensor.pixelShiftSmooth ? Edited : UnEdited); - pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); pixelShiftLmmse->set_inconsistent(!pedited->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->set_inconsistent(!pedited->raw.bayersensor.pixelShiftEqualBright); pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); - pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); - pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); + pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); +#ifdef PIXELSHIFTDEV + pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); + pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); pixelShiftStddevFactorGreen->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); pixelShiftStddevFactorRed->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); pixelShiftStddevFactorBlue->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); - pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); - pixelShiftNreadIso->setEditedState ( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); - pixelShiftPrnu->setEditedState ( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); - pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); pixelShiftSum->setEditedState ( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); + pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); + pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftRedBlueWeight->setEditedState ( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); +#endif if(!pedited->raw.bayersensor.method) { method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name @@ -381,9 +400,11 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params if(!pedited->raw.bayersensor.imageNum) { imageNumber->set_active_text(M("GENERAL_UNCHANGED")); } +#ifdef PIXELSHIFTDEV if(!pedited->raw.bayersensor.pixelShiftMotionCorrection) { pixelShiftMotionCorrection->set_active_text(M("GENERAL_UNCHANGED")); } +#endif if(!pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod) { pixelShiftMotionMethod->set_active_text(M("GENERAL_UNCHANGED")); } @@ -395,39 +416,41 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); pixelShiftShowMotionMaskOnly->set_active(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); - pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); - pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); pixelShiftHoleFill->set_active(pp->raw.bayersensor.pixelShiftHoleFill); pixelShiftMedian->set_active(pp->raw.bayersensor.pixelShiftMedian); - pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); pixelShiftGreen->set_active(pp->raw.bayersensor.pixelShiftGreen); pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); - pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); pixelShiftLmmse->set_active(pp->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->set_active(pp->raw.bayersensor.pixelShiftEqualBright); pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); - pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); ccSteps->setValue (pp->raw.bayersensor.ccSteps); lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); - pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); - pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); + pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); + pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); +#ifdef PIXELSHIFTDEV pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); - pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); - pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); - pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); - pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); - pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); - + pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); + pixelShiftMedian3->set_sensitive(pixelShiftMedian->get_active()); + pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); + pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->get_active()); - + pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); + pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); +#endif if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { @@ -462,13 +485,34 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params ccSteps->hide();*/ } - lastDCBen = pp->raw.bayersensor.dcb_enhance; + dcbEnhance->setLastActive(pp->raw.bayersensor.dcb_enhance); + pixelShiftShowMotion->setLastActive(pp->raw.bayersensor.pixelshiftShowMotion); + pixelShiftShowMotionMaskOnly->setLastActive(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); + pixelShiftNonGreenCross->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenCross); + pixelShiftGreen->setLastActive(pp->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->setLastActive(pp->raw.bayersensor.pixelShiftBlur); + pixelShiftHoleFill->setLastActive(pp->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->setLastActive(pp->raw.bayersensor.pixelShiftMedian); + pixelShiftLmmse->setLastActive(pp->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->setLastActive(pp->raw.bayersensor.pixelShiftEqualBright); +#ifdef PIXELSHIFTDEV + pixelShiftMedian3->setLastActive(pp->raw.bayersensor.pixelShiftMedian3); + pixelShiftAutomatic->setLastActive(pp->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftNonGreenCross2->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftExp0->setLastActive(pp->raw.bayersensor.pixelShiftExp0); +#endif + //lastALLen = pp->raw.bayersensor.all_enhance; - methodconn.block (false); - psmcconn.block (false); - imagenumberconn.block (false); - dcbEnhconn.block (false); + method->block (false); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->block (false); +#endif + imageNumber->block (false); + dcbEnhance->block (false); //allEnhconn.block (false); enableListener (); @@ -481,34 +525,37 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pp->raw.bayersensor.dcb_enhance = dcbEnhance->get_active(); //pp->raw.bayersensor.all_enhance = allEnhance->get_active(); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); - pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); - pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); pp->raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)pixelShiftMotionMethod->get_active_row_number(); - pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); - pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); - pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); - pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); - pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); pp->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getValue(); - pp->raw.bayersensor.pixelShiftSum = pixelShiftSum->getValue(); - pp->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getValue(); pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); - pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); pp->raw.bayersensor.pixelShiftHoleFill = pixelShiftHoleFill->get_active(); pp->raw.bayersensor.pixelShiftMedian = pixelShiftMedian->get_active(); - pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->get_active(); pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); pp->raw.bayersensor.pixelShiftSmoothFactor = pixelShiftSmooth->getValue(); - pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); pp->raw.bayersensor.pixelShiftLmmse = pixelShiftLmmse->get_active(); + pp->raw.bayersensor.pixelShiftEqualBright = pixelShiftEqualBright->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); +#ifdef PIXELSHIFTDEV + pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); + pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); + pp->raw.bayersensor.pixelShiftSum = pixelShiftSum->getValue(); + pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); + pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); + pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); + pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); + pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); + pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); + pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); + pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); + pp->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getValue(); +#endif int currentRow = method->get_active_row_number(); if( currentRow >= 0 && currentRow < procparams::RAWParams::BayerSensor::numMethods) { @@ -529,34 +576,37 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe pedited->raw.bayersensor.dcbEnhance = !dcbEnhance->get_inconsistent(); //pedited->raw.bayersensor.allEnhance = !allEnhance->get_inconsistent(); pedited->raw.bayersensor.lmmseIterations = lmmseIterations->getEditedState (); - pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); - pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); pedited->raw.bayersensor.pixelShiftMotionCorrectionMethod = pixelShiftMotionMethod->get_active_text() != M("GENERAL_UNCHANGED"); - pedited->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getEditedState (); - pedited->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getEditedState (); - pedited->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getEditedState (); pedited->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getEditedState (); - pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); - pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); pedited->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getEditedState (); - pedited->raw.bayersensor.pixelShiftSum = pixelShiftSum->getEditedState (); - pedited->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getEditedState (); pedited->raw.bayersensor.pixelshiftShowMotion = !pixelShiftShowMotion->get_inconsistent(); pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly = !pixelShiftShowMotionMaskOnly->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); pedited->raw.bayersensor.pixelShiftHoleFill = !pixelShiftHoleFill->get_inconsistent(); pedited->raw.bayersensor.pixelShiftMedian = !pixelShiftMedian->get_inconsistent(); - pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); pedited->raw.bayersensor.pixelShiftGreen = !pixelShiftGreen->get_inconsistent(); pedited->raw.bayersensor.pixelShiftBlur = !pixelShiftBlur->get_inconsistent(); pedited->raw.bayersensor.pixelShiftSmooth = pixelShiftSmooth->getEditedState(); - pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); pedited->raw.bayersensor.pixelShiftLmmse = !pixelShiftLmmse->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftEqualBright = !pixelShiftEqualBright->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross = !pixelShiftNonGreenCross->get_inconsistent(); +#ifdef PIXELSHIFTDEV + pedited->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getEditedState (); + pedited->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getEditedState (); + pedited->raw.bayersensor.pixelShiftSum = pixelShiftSum->getEditedState (); + pedited->raw.bayersensor.pixelShiftMedian3 = !pixelShiftMedian3->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getEditedState (); + pedited->raw.bayersensor.pixelShiftMotionCorrection = pixelShiftMotionCorrection->get_active_text() != M("GENERAL_UNCHANGED"); + pedited->raw.bayersensor.pixelShiftAutomatic = !pixelShiftAutomatic->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenHorizontal = !pixelShiftNonGreenHorizontal->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNonGreenVertical = !pixelShiftNonGreenVertical->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftExp0 = !pixelShiftExp0->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenCross2 = !pixelShiftNonGreenCross2->get_inconsistent(); pedited->raw.bayersensor.pixelShiftNonGreenAmaze = !pixelShiftNonGreenAmaze->get_inconsistent(); + pedited->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getEditedState (); + pedited->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getEditedState (); + pedited->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getEditedState (); +#endif } } @@ -564,8 +614,10 @@ void BayerProcess::setBatchMode(bool batchMode) { method->append_text (M("GENERAL_UNCHANGED")); method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name +#ifdef PIXELSHIFTDEV pixelShiftMotionCorrection->append_text (M("GENERAL_UNCHANGED")); pixelShiftMotionCorrection->set_active (4); +#endif pixelShiftMotionMethod->append_text (M("GENERAL_UNCHANGED")); pixelShiftMotionMethod->set_active (4); imageNumber->append_text (M("GENERAL_UNCHANGED")); @@ -577,61 +629,69 @@ void BayerProcess::setBatchMode(bool batchMode) ccSteps->showEditedCB (); dcbIterations->showEditedCB (); lmmseIterations->showEditedCB (); +#ifdef PIXELSHIFTDEV pixelShiftMotion->showEditedCB (); + pixelShiftSum->showEditedCB (); pixelShiftStddevFactorGreen->showEditedCB (); pixelShiftStddevFactorRed->showEditedCB (); pixelShiftStddevFactorBlue->showEditedCB (); - pixelShiftEperIso->showEditedCB (); pixelShiftNreadIso->showEditedCB (); pixelShiftPrnu->showEditedCB (); - pixelShiftSigma->showEditedCB (); - pixelShiftSum->showEditedCB (); pixelShiftRedBlueWeight->showEditedCB (); +#endif + pixelShiftEperIso->showEditedCB (); + pixelShiftSigma->showEditedCB (); } void BayerProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) { dcbIterations->setDefault( defParams->raw.bayersensor.dcb_iterations); lmmseIterations->setDefault( defParams->raw.bayersensor.lmmse_iterations); +#ifdef PIXELSHIFTDEV pixelShiftMotion->setDefault( defParams->raw.bayersensor.pixelShiftMotion); + pixelShiftSum->setDefault( defParams->raw.bayersensor.pixelShiftSum); pixelShiftStddevFactorGreen->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorGreen); pixelShiftStddevFactorRed->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorRed); pixelShiftStddevFactorBlue->setDefault( defParams->raw.bayersensor.pixelShiftStddevFactorBlue); - pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); pixelShiftNreadIso->setDefault( defParams->raw.bayersensor.pixelShiftNreadIso); pixelShiftPrnu->setDefault( defParams->raw.bayersensor.pixelShiftPrnu); - pixelShiftSigma->setDefault( defParams->raw.bayersensor.pixelShiftSigma); - pixelShiftSum->setDefault( defParams->raw.bayersensor.pixelShiftSum); pixelShiftRedBlueWeight->setDefault( defParams->raw.bayersensor.pixelShiftRedBlueWeight); +#endif + pixelShiftEperIso->setDefault( defParams->raw.bayersensor.pixelShiftEperIso); + pixelShiftSigma->setDefault( defParams->raw.bayersensor.pixelShiftSigma); ccSteps->setDefault (defParams->raw.bayersensor.ccSteps); if (pedited) { dcbIterations->setDefaultEditedState( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); lmmseIterations->setDefaultEditedState( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); +#ifdef PIXELSHIFTDEV pixelShiftMotion->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); + pixelShiftSum->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); pixelShiftStddevFactorGreen->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorGreen ? Edited : UnEdited); pixelShiftStddevFactorRed->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); pixelShiftStddevFactorBlue->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); - pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftNreadIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftNreadIso ? Edited : UnEdited); pixelShiftPrnu->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftPrnu ? Edited : UnEdited); - pixelShiftSigma->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); - pixelShiftSum->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); pixelShiftRedBlueWeight->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); +#endif + pixelShiftEperIso->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); + pixelShiftSigma->setDefaultEditedState( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); ccSteps->setDefaultEditedState(pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); } else { dcbIterations->setDefaultEditedState( Irrelevant ); lmmseIterations->setDefaultEditedState( Irrelevant ); +#ifdef PIXELSHIFTDEV pixelShiftMotion->setDefaultEditedState( Irrelevant ); + pixelShiftSum->setDefaultEditedState( Irrelevant ); pixelShiftStddevFactorGreen->setDefaultEditedState( Irrelevant ); pixelShiftStddevFactorRed->setDefaultEditedState( Irrelevant ); pixelShiftStddevFactorBlue->setDefaultEditedState( Irrelevant ); - pixelShiftEperIso->setDefaultEditedState( Irrelevant ); pixelShiftNreadIso->setDefaultEditedState( Irrelevant ); pixelShiftPrnu->setDefaultEditedState( Irrelevant ); - pixelShiftSigma->setDefaultEditedState( Irrelevant ); - pixelShiftSum->setDefaultEditedState( Irrelevant ); pixelShiftRedBlueWeight->setDefaultEditedState( Irrelevant ); +#endif + pixelShiftEperIso->setDefaultEditedState( Irrelevant ); + pixelShiftSigma->setDefaultEditedState( Irrelevant ); ccSteps->setDefaultEditedState(Irrelevant ); } } @@ -645,32 +705,35 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) listener->panelChanged (EvDemosaicFalseColorIter, a->getTextValue() ); } else if (a == lmmseIterations) { listener->panelChanged (EvDemosaicLMMSEIter, a->getTextValue() ); +#ifdef PIXELSHIFTDEV } else if (a == pixelShiftMotion) { listener->panelChanged (EvPixelShiftMotion, a->getTextValue() ); + } else if (a == pixelShiftSum) { + listener->panelChanged (EvPixelShiftSum, a->getTextValue() ); } else if (a == pixelShiftStddevFactorGreen) { listener->panelChanged (EvPixelShiftStddevFactorGreen, a->getTextValue() ); } else if (a == pixelShiftStddevFactorRed) { listener->panelChanged (EvPixelShiftStddevFactorRed, a->getTextValue() ); } else if (a == pixelShiftStddevFactorBlue) { listener->panelChanged (EvPixelShiftStddevFactorBlue, a->getTextValue() ); - } else if (a == pixelShiftEperIso) { - listener->panelChanged (EvPixelShiftEperIso, a->getTextValue() ); } else if (a == pixelShiftNreadIso) { listener->panelChanged (EvPixelShiftNreadIso, a->getTextValue() ); } else if (a == pixelShiftPrnu) { listener->panelChanged (EvPixelShiftPrnu, a->getTextValue() ); - } else if (a == pixelShiftSigma) { - listener->panelChanged (EvPixelShiftSigma, a->getTextValue() ); - } else if (a == pixelShiftSum) { - listener->panelChanged (EvPixelShiftSum, a->getTextValue() ); } else if (a == pixelShiftRedBlueWeight) { listener->panelChanged (EvPixelShiftRedBlueWeight, a->getTextValue() ); +#endif + } else if (a == pixelShiftEperIso) { + listener->panelChanged (EvPixelShiftEperIso, a->getTextValue() ); + } else if (a == pixelShiftSigma) { + listener->panelChanged (EvPixelShiftSigma, a->getTextValue() ); } else if (a == pixelShiftSmooth) { listener->panelChanged (EvPixelShiftSmooth, a->getTextValue() ); } } } +#ifdef PIXELSHIFTDEV void BayerProcess::psMotionCorrectionChanged () { if(pixelShiftMotionCorrection->get_active_row_number() == 5) { @@ -687,7 +750,7 @@ void BayerProcess::psMotionCorrectionChanged () listener->panelChanged (EvPixelShiftMotionCorrection, pixelShiftMotionCorrection->get_active_text()); } } - +#endif void BayerProcess::methodChanged () { int curSelection = method->get_active_row_number(); @@ -745,14 +808,14 @@ void BayerProcess::dcbEnhanceChanged () if (batchMode) { if (dcbEnhance->get_inconsistent()) { dcbEnhance->set_inconsistent (false); - dcbEnhconn.block (true); + dcbEnhance->block (true); dcbEnhance->set_active (false); - dcbEnhconn.block (false); - } else if (lastDCBen) { + dcbEnhance->block (false); + } else if (dcbEnhance->getLastActive()) { dcbEnhance->set_inconsistent (true); } - lastDCBen = dcbEnhance->get_active (); + dcbEnhance->setLastActive(); } if (listener) { @@ -785,14 +848,14 @@ void BayerProcess::pixelShiftShowMotionChanged () if (batchMode) { if (pixelShiftShowMotion->get_inconsistent()) { pixelShiftShowMotion->set_inconsistent (false); - pixelShiftShowMotionconn.block (true); + pixelShiftShowMotion->block (true); pixelShiftShowMotion->set_active (false); - pixelShiftShowMotionconn.block (false); - } else if (lastDCBen) { + pixelShiftShowMotion->block (false); + } else if (pixelShiftShowMotion->getLastActive()) { pixelShiftShowMotion->set_inconsistent (true); } - lastDCBen = pixelShiftShowMotion->get_active (); + pixelShiftShowMotion->setLastActive(); } pixelShiftShowMotionMaskOnly->set_sensitive(pixelShiftShowMotion->get_active ()); if (listener) { @@ -805,14 +868,14 @@ void BayerProcess::pixelShiftShowMotionMaskOnlyChanged () if (batchMode) { if (pixelShiftShowMotionMaskOnly->get_inconsistent()) { pixelShiftShowMotionMaskOnly->set_inconsistent (false); - pixelShiftShowMotionMaskOnlyconn.block (true); + pixelShiftShowMotionMaskOnly->block (true); pixelShiftShowMotionMaskOnly->set_active (false); - pixelShiftShowMotionMaskOnlyconn.block (false); - } else if (lastDCBen) { + pixelShiftShowMotionMaskOnly->block (false); + } else if (pixelShiftShowMotionMaskOnly->getLastActive()) { pixelShiftShowMotionMaskOnly->set_inconsistent (true); } - lastDCBen = pixelShiftShowMotionMaskOnly->get_active (); + pixelShiftShowMotionMaskOnly->setLastActive(); } if (listener) { @@ -820,19 +883,20 @@ void BayerProcess::pixelShiftShowMotionMaskOnlyChanged () } } +#ifdef PIXELSHIFTDEV void BayerProcess::pixelShiftAutomaticChanged () { if (batchMode) { if (pixelShiftAutomatic->get_inconsistent()) { pixelShiftAutomatic->set_inconsistent (false); - pixelShiftAutomaticconn.block (true); + pixelShiftAutomatic->block (true); pixelShiftAutomatic->set_active (false); - pixelShiftAutomaticconn.block (false); - } else if (lastDCBen) { + pixelShiftAutomatic->block (false); + } else if (pixelShiftAutomatic->getLastActive()) { pixelShiftAutomatic->set_inconsistent (true); } - lastDCBen = pixelShiftAutomatic->get_active (); + pixelShiftAutomatic->setLastActive(); } pixelShiftMotion->set_sensitive(!pixelShiftAutomatic->get_active ()); pixelShiftEperIso->set_sensitive(pixelShiftAutomatic->get_active ()); @@ -861,20 +925,19 @@ void BayerProcess::pixelShiftAutomaticChanged () } } - void BayerProcess::pixelShiftNonGreenHorizontalChanged () { if (batchMode) { if (pixelShiftNonGreenHorizontal->get_inconsistent()) { pixelShiftNonGreenHorizontal->set_inconsistent (false); - pixelShiftNonGreenHorizontalconn.block (true); + pixelShiftNonGreenHorizontal->block (true); pixelShiftNonGreenHorizontal->set_active (false); - pixelShiftNonGreenHorizontalconn.block (false); - } else if (lastDCBen) { + pixelShiftNonGreenHorizontal->block (false); + } else if (pixelShiftNonGreenHorizontal->getLastActive()) { pixelShiftNonGreenHorizontal->set_inconsistent (true); } - lastDCBen = pixelShiftNonGreenHorizontal->get_active (); + pixelShiftNonGreenHorizontal->setLastActive(); } if (listener) { @@ -887,34 +950,35 @@ void BayerProcess::pixelShiftNonGreenVerticalChanged () if (batchMode) { if (pixelShiftNonGreenVertical->get_inconsistent()) { pixelShiftNonGreenVertical->set_inconsistent (false); - pixelShiftNonGreenVerticalconn.block (true); + pixelShiftNonGreenVertical->block (true); pixelShiftNonGreenVertical->set_active (false); - pixelShiftNonGreenVerticalconn.block (false); - } else if (lastDCBen) { + pixelShiftNonGreenVertical->block (false); + } else if (pixelShiftNonGreenVertical->getLastActive()) { pixelShiftNonGreenVertical->set_inconsistent (true); } - lastDCBen = pixelShiftNonGreenVertical->get_active (); + pixelShiftNonGreenVertical->setLastActive(); } if (listener) { listener->panelChanged (EvPixelShiftNonGreenVertical, pixelShiftNonGreenVertical->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } +#endif void BayerProcess::pixelShiftHoleFillChanged () { if (batchMode) { if (pixelShiftHoleFill->get_inconsistent()) { pixelShiftHoleFill->set_inconsistent (false); - pixelShiftHoleFillconn.block (true); + pixelShiftHoleFill->block (true); pixelShiftHoleFill->set_active (false); - pixelShiftHoleFillconn.block (false); - } else if (lastDCBen) { + pixelShiftHoleFill->block (false); + } else if (pixelShiftHoleFill->getLastActive()) { pixelShiftHoleFill->set_inconsistent (true); } - lastDCBen = pixelShiftHoleFill->get_active (); + pixelShiftHoleFill->setLastActive(); } if (listener) { @@ -927,56 +991,56 @@ void BayerProcess::pixelShiftMedianChanged () if (batchMode) { if (pixelShiftMedian->get_inconsistent()) { pixelShiftMedian->set_inconsistent (false); - pixelShiftMedianconn.block (true); + pixelShiftMedian->block (true); pixelShiftMedian->set_active (false); - pixelShiftMedianconn.block (false); - } else if (lastDCBen) { + pixelShiftMedian->block (false); + } else if (pixelShiftMedian->getLastActive()) { pixelShiftMedian->set_inconsistent (true); } - lastDCBen = pixelShiftMedian->get_active (); + pixelShiftMedian->setLastActive(); } - +#ifdef PIXELSHIFTDEV pixelShiftMedian3->set_sensitive(pixelShiftMedian->get_active ()); - +#endif if (listener) { listener->panelChanged (EvPixelShiftMedian, pixelShiftMedian->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } - +#ifdef PIXELSHIFTDEV void BayerProcess::pixelShiftMedian3Changed () { if (batchMode) { if (pixelShiftMedian3->get_inconsistent()) { pixelShiftMedian3->set_inconsistent (false); - pixelShiftMedian3conn.block (true); + pixelShiftMedian3->block (true); pixelShiftMedian3->set_active (false); - pixelShiftMedian3conn.block (false); - } else if (lastDCBen) { + pixelShiftMedian3->block (false); + } else if (pixelShiftMedian3->getLastActive()) { pixelShiftMedian3->set_inconsistent (true); } - lastDCBen = pixelShiftMedian3->get_active (); + pixelShiftMedian3->setLastActive(); } if (listener) { listener->panelChanged (EvPixelShiftMedian3, pixelShiftMedian3->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } - +#endif void BayerProcess::pixelShiftGreenChanged () { if (batchMode) { if (pixelShiftGreen->get_inconsistent()) { pixelShiftGreen->set_inconsistent (false); - pixelShiftGreenconn.block (true); + pixelShiftGreen->block (true); pixelShiftGreen->set_active (false); - pixelShiftGreenconn.block (false); - } else if (lastDCBen) { + pixelShiftGreen->block (false); + } else if (pixelShiftGreen->getLastActive()) { pixelShiftGreen->set_inconsistent (true); } - lastDCBen = pixelShiftGreen->get_active (); + pixelShiftGreen->setLastActive(); } if (listener) { @@ -989,56 +1053,56 @@ void BayerProcess::pixelShiftBlurChanged () if (batchMode) { if (pixelShiftBlur->get_inconsistent()) { pixelShiftBlur->set_inconsistent (false); - pixelShiftBlurconn.block (true); + pixelShiftBlur->block (true); pixelShiftBlur->set_active (false); - pixelShiftBlurconn.block (false); - } else if (lastDCBen) { + pixelShiftBlur->block (false); + } else if (pixelShiftBlur->getLastActive()) { pixelShiftBlur->set_inconsistent (true); } - lastDCBen = pixelShiftBlur->get_active (); + pixelShiftBlur->setLastActive(); } pixelShiftSmooth->set_sensitive(pixelShiftBlur->get_active ()); - + pixelShiftSigma->set_sensitive(pixelShiftBlur->get_active ()); if (listener) { listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } - +#ifdef PIXELSHIFTDEV void BayerProcess::pixelShiftExp0Changed () { if (batchMode) { if (pixelShiftExp0->get_inconsistent()) { pixelShiftExp0->set_inconsistent (false); - pixelShiftExp0conn.block (true); + pixelShiftExp0->block (true); pixelShiftExp0->set_active (false); - pixelShiftExp0conn.block (false); - } else if (lastDCBen) { + pixelShiftExp0->block (false); + } else if (pixelShiftExp0->getLastActive()) { pixelShiftExp0->set_inconsistent (true); } - lastDCBen = pixelShiftExp0->get_active (); + pixelShiftExp0->setLastActive(); } if (listener) { listener->panelChanged (EvPixelShiftExp0, pixelShiftExp0->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } - +#endif void BayerProcess::pixelShiftLmmseChanged () { if (batchMode) { if (pixelShiftLmmse->get_inconsistent()) { pixelShiftLmmse->set_inconsistent (false); - pixelShiftLmmseconn.block (true); + pixelShiftLmmse->block (true); pixelShiftLmmse->set_active (false); - pixelShiftLmmseconn.block (false); - } else if (lastDCBen) { + pixelShiftLmmse->block (false); + } else if (pixelShiftLmmse->getLastActive()) { pixelShiftLmmse->set_inconsistent (true); } - lastDCBen = pixelShiftLmmse->get_active (); + pixelShiftLmmse->setLastActive(); } if (listener) { @@ -1046,39 +1110,59 @@ void BayerProcess::pixelShiftLmmseChanged () } } +void BayerProcess::pixelShiftEqualBrightChanged () +{ + if (batchMode) { + if (pixelShiftEqualBright->get_inconsistent()) { + pixelShiftEqualBright->set_inconsistent (false); + pixelShiftEqualBright->block (true); + pixelShiftEqualBright->set_active (false); + pixelShiftEqualBright->block (false); + } else if (pixelShiftEqualBright->getLastActive()) { + pixelShiftEqualBright->set_inconsistent (true); + } + + pixelShiftEqualBright->setLastActive(); + } + + if (listener) { + listener->panelChanged (EvPixelShiftEqualBright, pixelShiftEqualBright->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } +} + void BayerProcess::pixelShiftNonGreenCrossChanged () { if (batchMode) { if (pixelShiftNonGreenCross->get_inconsistent()) { pixelShiftNonGreenCross->set_inconsistent (false); - pixelShiftNonGreenCrossconn.block (true); + pixelShiftNonGreenCross->block (true); pixelShiftNonGreenCross->set_active (false); - pixelShiftNonGreenCrossconn.block (false); - } else if (lastDCBen) { + pixelShiftNonGreenCross->block (false); + } else if (pixelShiftNonGreenCross->getLastActive()) { pixelShiftNonGreenCross->set_inconsistent (true); } - lastDCBen = pixelShiftNonGreenCross->get_active (); + pixelShiftNonGreenCross->setLastActive(); } if (listener) { listener->panelChanged (EvPixelShiftNonGreenCross, pixelShiftNonGreenCross->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } - +#ifdef PIXELSHIFTDEV void BayerProcess::pixelShiftNonGreenCross2Changed () { if (batchMode) { if (pixelShiftNonGreenCross2->get_inconsistent()) { pixelShiftNonGreenCross2->set_inconsistent (false); - pixelShiftNonGreenCross2conn.block (true); + pixelShiftNonGreenCross2->block (true); pixelShiftNonGreenCross2->set_active (false); - pixelShiftNonGreenCross2conn.block (false); - } else if (lastDCBen) { + pixelShiftNonGreenCross2->block (false); + } else if (pixelShiftNonGreenCross2->getLastActive()) { pixelShiftNonGreenCross2->set_inconsistent (true); } - lastDCBen = pixelShiftNonGreenCross2->get_active (); + pixelShiftNonGreenCross2->setLastActive(); } if (listener) { @@ -1091,17 +1175,18 @@ void BayerProcess::pixelShiftNonGreenAmazeChanged () if (batchMode) { if (pixelShiftNonGreenAmaze->get_inconsistent()) { pixelShiftNonGreenAmaze->set_inconsistent (false); - pixelShiftNonGreenAmazeconn.block (true); + pixelShiftNonGreenAmaze->block (true); pixelShiftNonGreenAmaze->set_active (false); - pixelShiftNonGreenAmazeconn.block (false); - } else if (lastDCBen) { + pixelShiftNonGreenAmaze->block (false); + } else if (pixelShiftNonGreenAmaze->getLastActive()) { pixelShiftNonGreenAmaze->set_inconsistent (true); } - lastDCBen = pixelShiftNonGreenAmaze->get_active (); + pixelShiftNonGreenAmaze->setLastActive(); } if (listener) { listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } +#endif \ No newline at end of file diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index c8658f5bf..463e8fb58 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -36,48 +36,43 @@ protected: Adjuster* ccSteps; Gtk::VBox *dcbOptions; Adjuster* dcbIterations; - Gtk::CheckButton* dcbEnhance; - //Gtk::VBox *allOptions; - //Gtk::CheckButton* allEnhance; + MyCheckButton* dcbEnhance; Gtk::VBox *lmmseOptions; Adjuster* lmmseIterations; Gtk::VBox *pixelShiftFrame; Gtk::VBox *pixelShiftOptions; + MyComboBoxText* pixelShiftMotionMethod; + MyCheckButton* pixelShiftShowMotion; + MyCheckButton* pixelShiftShowMotionMaskOnly; + MyCheckButton* pixelShiftNonGreenCross; + MyCheckButton* pixelShiftGreen; + MyCheckButton* pixelShiftBlur; + MyCheckButton* pixelShiftHoleFill; + MyCheckButton* pixelShiftMedian; + MyCheckButton* pixelShiftLmmse; + MyCheckButton* pixelShiftEqualBright; + Adjuster* pixelShiftSmooth; + Adjuster* pixelShiftEperIso; + Adjuster* pixelShiftSigma; +#ifdef PIXELSHIFTDEV + Adjuster* pixelShiftSum; Adjuster* pixelShiftMotion; MyComboBoxText* pixelShiftMotionCorrection; - MyComboBoxText* pixelShiftMotionMethod; - Gtk::CheckButton* pixelShiftShowMotion; - Gtk::CheckButton* pixelShiftShowMotionMaskOnly; - Gtk::CheckButton* pixelShiftAutomatic; - Gtk::CheckButton* pixelShiftNonGreenHorizontal; - Gtk::CheckButton* pixelShiftNonGreenVertical; - Gtk::CheckButton* pixelShiftNonGreenCross; - Gtk::CheckButton* pixelShiftNonGreenCross2; - Gtk::CheckButton* pixelShiftNonGreenAmaze; - Gtk::CheckButton* pixelShiftGreen; - Gtk::CheckButton* pixelShiftBlur; - Gtk::CheckButton* pixelShiftExp0; - Gtk::CheckButton* pixelShiftHoleFill; - Gtk::CheckButton* pixelShiftMedian; - Gtk::CheckButton* pixelShiftMedian3; - Gtk::CheckButton* pixelShiftLmmse; - Adjuster* pixelShiftSmooth; + MyCheckButton* pixelShiftAutomatic; + MyCheckButton* pixelShiftNonGreenHorizontal; + MyCheckButton* pixelShiftNonGreenVertical; + MyCheckButton* pixelShiftNonGreenCross2; + MyCheckButton* pixelShiftNonGreenAmaze; + MyCheckButton* pixelShiftExp0; + MyCheckButton* pixelShiftMedian3; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; - Adjuster* pixelShiftEperIso; Adjuster* pixelShiftNreadIso; Adjuster* pixelShiftPrnu; - Adjuster* pixelShiftSigma; - Adjuster* pixelShiftSum; Adjuster* pixelShiftRedBlueWeight; - bool lastDCBen; +#endif int oldMethod; - //bool lastALLen; - sigc::connection methodconn, imagenumberconn, psmcconn, dcbEnhconn, - pixelShiftShowMotionconn, pixelShiftShowMotionMaskOnlyconn, pixelShiftAutomaticconn, - pixelShiftNonGreenHorizontalconn, pixelShiftNonGreenVerticalconn, pixelShiftHoleFillconn, pixelShiftMedianconn, pixelShiftMedian3conn, pixelShiftNonGreenCrossconn, - pixelShiftNonGreenCross2conn, pixelShiftNonGreenAmazeconn, pixelShiftGreenconn, pixelShiftBlurconn, pixelShiftSmoothconn, pixelShiftExp0conn, pixelShiftLmmseconn, pixelShiftMotionMethodConn; public: BayerProcess (); @@ -88,26 +83,29 @@ public: void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); void methodChanged (); - void psMotionCorrectionChanged (); void imageNumberChanged (); void adjusterChanged (Adjuster* a, double newval); void dcbEnhanceChanged(); void pixelShiftShowMotionChanged(); void pixelShiftShowMotionMaskOnlyChanged(); - void pixelShiftAutomaticChanged(); - void pixelShiftNonGreenHorizontalChanged(); - void pixelShiftNonGreenVerticalChanged(); void pixelShiftHoleFillChanged(); void pixelShiftMedianChanged(); void pixelShiftMedian3Changed(); void pixelShiftGreenChanged(); void pixelShiftBlurChanged(); - void pixelShiftExp0Changed(); void pixelShiftLmmseChanged(); + void pixelShiftEqualBrightChanged(); void pixelShiftNonGreenCrossChanged(); + void pixelShiftMotionMethodChanged(); +#ifdef PIXELSHIFTDEV + void psMotionCorrectionChanged (); + void pixelShiftAutomaticChanged(); + void pixelShiftNonGreenHorizontalChanged(); + void pixelShiftNonGreenVerticalChanged(); + void pixelShiftExp0Changed(); void pixelShiftNonGreenCross2Changed(); void pixelShiftNonGreenAmazeChanged(); - void pixelShiftMotionMethodChanged(); +#endif }; #endif diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index 597173086..0803a2573 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -24,6 +24,7 @@ #include "../rtengine/coord.h" #include #include +#include Glib::ustring escapeHtmlChars(const Glib::ustring &src); bool removeIfThere (Gtk::Container* cont, Gtk::Widget* w, bool increference = true); @@ -259,6 +260,24 @@ public: MyScrolledWindow(); }; +/** + * @brief subclass of Gtk::CheckButton in order to handle the last active state + */ +class MyCheckButton : public Gtk::CheckButton +{ + + bool lastActive = false; + sigc::connection myConnection; +public: + using CheckButton::CheckButton; + void setLastActive() {lastActive = get_active();}; + void setLastActive(bool active) {lastActive = active;}; + bool getLastActive() {return lastActive;}; + void connect(const sigc::connection &connection) {myConnection = connection;}; + void block(bool blocked) {myConnection.block(blocked);}; +}; + + /** * @brief subclass of Gtk::ComboBox in order to handle the scrollwheel */ @@ -278,9 +297,12 @@ class MyComboBoxText : public Gtk::ComboBoxText { bool on_scroll_event (GdkEventScroll* event); + sigc::connection myConnection; public: MyComboBoxText (); + void connect(const sigc::connection &connection) {myConnection = connection;}; + void block(bool blocked) {myConnection.block(blocked);}; }; /** diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 939413f18..a84d1e8be 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -395,6 +395,7 @@ void ParamsEdited::set (bool v) raw.bayersensor.pixelShiftSmooth = v; raw.bayersensor.pixelShiftExp0 = v; raw.bayersensor.pixelShiftLmmse = v; + raw.bayersensor.pixelShiftEqualBright = v; raw.bayersensor.pixelShiftNonGreenCross = v; raw.bayersensor.pixelShiftNonGreenCross2 = v; raw.bayersensor.pixelShiftNonGreenAmaze = v; @@ -919,6 +920,7 @@ void ParamsEdited::initFrom (const std::vector raw.bayersensor.pixelShiftSmooth = raw.bayersensor.pixelShiftSmooth && p.raw.bayersensor.pixelShiftSmoothFactor == other.raw.bayersensor.pixelShiftSmoothFactor; raw.bayersensor.pixelShiftExp0 = raw.bayersensor.pixelShiftExp0 && p.raw.bayersensor.pixelShiftExp0 == other.raw.bayersensor.pixelShiftExp0; raw.bayersensor.pixelShiftLmmse = raw.bayersensor.pixelShiftLmmse && p.raw.bayersensor.pixelShiftLmmse == other.raw.bayersensor.pixelShiftLmmse; + raw.bayersensor.pixelShiftEqualBright = raw.bayersensor.pixelShiftEqualBright && p.raw.bayersensor.pixelShiftEqualBright == other.raw.bayersensor.pixelShiftEqualBright; raw.bayersensor.pixelShiftNonGreenCross = raw.bayersensor.pixelShiftNonGreenCross && p.raw.bayersensor.pixelShiftNonGreenCross == other.raw.bayersensor.pixelShiftNonGreenCross; raw.bayersensor.pixelShiftNonGreenCross2 = raw.bayersensor.pixelShiftNonGreenCross2 && p.raw.bayersensor.pixelShiftNonGreenCross2 == other.raw.bayersensor.pixelShiftNonGreenCross2; raw.bayersensor.pixelShiftNonGreenAmaze = raw.bayersensor.pixelShiftNonGreenAmaze && p.raw.bayersensor.pixelShiftNonGreenAmaze == other.raw.bayersensor.pixelShiftNonGreenAmaze; @@ -2434,6 +2436,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten toEdit.raw.bayersensor.pixelShiftLmmse = mods.raw.bayersensor.pixelShiftLmmse; } + if (raw.bayersensor.pixelShiftEqualBright) { + toEdit.raw.bayersensor.pixelShiftEqualBright = mods.raw.bayersensor.pixelShiftEqualBright; + } + if (raw.bayersensor.pixelShiftNonGreenCross) { toEdit.raw.bayersensor.pixelShiftNonGreenCross = mods.raw.bayersensor.pixelShiftNonGreenCross; } @@ -2958,7 +2964,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const return method && imageNum && dcbIterations && dcbEnhance && lmmseIterations/*&& allEnhance*/ && greenEq && pixelShiftMotion && pixelShiftMotionCorrection && pixelShiftMotionCorrectionMethod && pixelShiftStddevFactorGreen && pixelShiftStddevFactorRed && pixelShiftStddevFactorBlue && pixelShiftEperIso && pixelShiftNreadIso && pixelShiftPrnu && pixelShiftSigma && pixelShiftSum && pixelShiftRedBlueWeight && pixelshiftShowMotion && pixelshiftShowMotionMaskOnly - && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 && pixelShiftLmmse + && pixelShiftAutomatic && pixelShiftNonGreenHorizontal && pixelShiftNonGreenVertical && pixelShiftHoleFill && pixelShiftMedian && pixelShiftMedian3 && pixelShiftNonGreenCross && pixelShiftNonGreenCross2 && pixelShiftNonGreenAmaze && pixelShiftGreen && pixelShiftBlur && pixelShiftSmooth && pixelShiftExp0 && pixelShiftLmmse && pixelShiftEqualBright && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 5f66c3d18..89a490b7f 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -717,6 +717,7 @@ public: bool pixelShiftSmooth; bool pixelShiftExp0; bool pixelShiftLmmse; + bool pixelShiftEqualBright; bool pixelShiftNonGreenCross; bool pixelShiftNonGreenCross2; bool pixelShiftNonGreenAmaze; From ebd9e5e13203f3d813d17efaaefbfe7aec527f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sat, 11 Mar 2017 17:14:11 +0100 Subject: [PATCH 165/181] Make `rtengine::Cache` build again with hashable keys While STL containers are not guaranteed to build with incomplete types, `rtengine::Cache` used to build with `std::map` as well as `std::unordered_map` when I first implemented it. With GCC 6.3 I recently realized, that this was no longer the case for hashable keys (i.e. `std::unordered_map` as `Store`). The recommended workaround is to use a `std::unique_ptr`. --- rtengine/cache.h | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/rtengine/cache.h b/rtengine/cache.h index f4c232673..ec284b2ae 100644 --- a/rtengine/cache.h +++ b/rtengine/cache.h @@ -19,11 +19,12 @@ #pragma once -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include "../rtgui/threadutils.h" @@ -87,9 +88,9 @@ public: lru_list.splice( lru_list.begin(), lru_list, - store_it->second.lru_list_it + store_it->second->lru_list_it ); - value = store_it->second.value; + value = store_it->second->value; } mutex.unlock(); @@ -139,7 +140,7 @@ public: mutex.lock(); if (hook) { for (const auto& entry : store) { - hook->onRemove(entry.first, entry.second.value); + hook->onRemove(entry.first, entry.second->value); } } lru_list.clear(); @@ -152,13 +153,13 @@ private: using Store = typename std::conditional< cache_helper::has_hash::value, - std::unordered_map, - std::map + std::unordered_map>, + std::map> >::type; using StoreIterator = typename Store::iterator; using StoreConstIterator = typename Store::const_iterator; - typedef std::list LruList; + using LruList = std::list; using LruListIterator = typename LruList::iterator; struct Value { @@ -176,7 +177,7 @@ private: { const StoreIterator store_it = lru_list.back(); if (hook) { - hook->onDiscard(store_it->first, store_it->second.value); + hook->onDiscard(store_it->first, store_it->second->value); } store.erase(store_it); lru_list.pop_back(); @@ -193,23 +194,25 @@ private: discard(); } lru_list.push_front(store.end()); - const Value v = { - value, - lru_list.begin() - }; - lru_list.front() = store.emplace(key, v).first; + std::unique_ptr v( + new Value{ + value, + lru_list.begin() + } + ); + lru_list.front() = store.emplace(key, std::move(v)).first; } } else { if (mode == Mode::UNCOND || mode == Mode::KNOWN) { if (hook) { - hook->onDisplace(key, store_it->second.value); + hook->onDisplace(key, store_it->second->value); } lru_list.splice( lru_list.begin(), lru_list, - store_it->second.lru_list_it + store_it->second->lru_list_it ); - store_it->second.value = value; + store_it->second->value = value; } } mutex.unlock(); @@ -220,9 +223,9 @@ private: void remove(const StoreIterator& store_it) { if (hook) { - hook->onRemove(store_it->first, store_it->second.value); + hook->onRemove(store_it->first, store_it->second->value); } - lru_list.erase(store_it->second.lru_list_it); + lru_list.erase(store_it->second->lru_list_it); store.erase(store_it); } From 174949e0f0a55cb88875a08828d37334aa0eb3da Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 12 Mar 2017 20:39:03 +0100 Subject: [PATCH 166/181] Fix slowdown for decode of non pixelshift files --- rtengine/rawimagesource.cc | 57 +++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 28d3c1e89..a25e63e56 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1518,32 +1518,39 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) numFrames = ri->getFrameCount(); errCode = 0; -#ifdef _OPENMP -#pragma omp parallel if(numFrames > 1) -#endif -{ - int errCodeThr = 0; -#ifdef _OPENMP -#pragma omp for nowait -#endif - for(unsigned int i = 0; i < numFrames; ++i) { - if(i == 0) { - riFrames[i] = ri; - errCodeThr = riFrames[i]->loadRaw (true, i, true, plistener, 0.8); - } else { - riFrames[i] = new RawImage(fname); - errCodeThr = riFrames[i]->loadRaw (true, i); - } - riFrames[i]->compress_image(i); - } -#ifdef _OPENMP -#pragma omp critical -#endif -{ - errCode = errCodeThr ? errCodeThr : errCode; -} -} + if(numFrames > 1) { +#ifdef _OPENMP + #pragma omp parallel +#endif + { + int errCodeThr = 0; +#ifdef _OPENMP + #pragma omp for nowait +#endif + for(unsigned int i = 0; i < numFrames; ++i) { + if(i == 0) { + riFrames[i] = ri; + errCodeThr = riFrames[i]->loadRaw (true, i, true, plistener, 0.8); + } else { + riFrames[i] = new RawImage(fname); + errCodeThr = riFrames[i]->loadRaw (true, i); + } + riFrames[i]->compress_image(i); + } +#ifdef _OPENMP + #pragma omp critical +#endif + { + errCode = errCodeThr ? errCodeThr : errCode; + } + } + } else { + riFrames[0] = ri; + errCode = riFrames[0]->loadRaw (true, 0, true, plistener, 0.8); + riFrames[0]->compress_image(0); + } + if(errCode) { return errCode; } From 0578010fce1928fe50d999ef328b3cba4fe7d1fa Mon Sep 17 00:00:00 2001 From: Hombre Date: Sun, 12 Mar 2017 23:18:04 +0100 Subject: [PATCH 167/181] Fix unexpected font change on first Preference change (issue #3637) --- rtgui/preferences.cc | 66 ++++++++++++++++++++++++++++++++++++++------ rtgui/preferences.h | 8 ++++-- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index ed613c61f..5b28d94c1 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -41,6 +41,8 @@ Preferences::Preferences (RTWindow *rtwindow) , rprofiles (nullptr) , iprofiles (nullptr) , parent (rtwindow) + , newFont (false) + , newCPFont (false) { regex = Glib::Regex::create(THEMEREGEXSTR, Glib::RegexCompileFlags::REGEX_CASELESS); @@ -56,6 +58,10 @@ Preferences::Preferences (RTWindow *rtwindow) set_size_request (650, -1); set_default_size (options.preferencesWidth, options.preferencesHeight); + Pango::FontDescription defaultFont = get_style_context ()->get_font(); + initialFontFamily = defaultFont.get_family (); + initialFontSize = defaultFont.get_size () / Pango::SCALE; + Gtk::Box* mainBox = get_content_area (); //GTK318 #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 20 @@ -1032,7 +1038,11 @@ Gtk::Widget* Preferences::getGeneralPanel () fontButton = Gtk::manage( new Gtk::FontButton ()); setExpandAlignProperties(fontButton, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); fontButton->set_use_size(true); - fontButton->set_font_name(Glib::ustring::compose("%1 %2", options.fontFamily == "default" ? "sans" : options.fontFamily, options.fontSize)); + if (options.fontFamily == "default") { + fontButton->set_font_name (Glib::ustring::compose("%1 %2", initialFontFamily, initialFontSize)); + } else { + fontButton->set_font_name (Glib::ustring::compose("%1 %2", options.fontFamily, options.fontSize)); + } themeGrid->attach_next_to(*fontlab, *theme, Gtk::POS_RIGHT, 1, 1); themeGrid->attach_next_to(*fontButton, *fontlab, Gtk::POS_RIGHT, 1, 1); @@ -1042,7 +1052,11 @@ Gtk::Widget* Preferences::getGeneralPanel () colorPickerFontButton = Gtk::manage( new Gtk::FontButton ()); setExpandAlignProperties(fontButton, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); colorPickerFontButton->set_use_size(true); - colorPickerFontButton->set_font_name(Glib::ustring::compose("%1 %2", options.CPFontFamily == "default" ? "sans" : options.CPFontFamily, options.CPFontSize)); + if (options.fontFamily == "default") { + colorPickerFontButton->set_font_name (Glib::ustring::compose("%1 %2", initialFontFamily, initialFontSize)); + } else { + colorPickerFontButton->set_font_name (Glib::ustring::compose("%1 %2", options.CPFontFamily, options.CPFontSize)); + } themeGrid->attach_next_to(*cpfontlab, *fontButton, Gtk::POS_RIGHT, 1, 1); themeGrid->attach_next_to(*colorPickerFontButton, *cpfontlab, Gtk::POS_RIGHT, 1, 1); @@ -1193,6 +1207,7 @@ Gtk::Widget* Preferences::getGeneralPanel () langAutoDetectConn = ckbLangAutoDetect->signal_toggled().connect (sigc::mem_fun(*this, &Preferences::langAutoDetectToggled)); tconn = theme->signal_changed().connect( sigc::mem_fun(*this, &Preferences::themeChanged) ); fconn = fontButton->signal_font_set().connect( sigc::mem_fun(*this, &Preferences::fontChanged) ); + cpfconn = colorPickerFontButton->signal_font_set().connect( sigc::mem_fun(*this, &Preferences::cpFontChanged) ); return mvbsd; } @@ -1582,12 +1597,16 @@ void Preferences::storePreferences () moptions.navGuideBrush[3] = butNavGuideCol->get_alpha() / 65535.0; Pango::FontDescription fd(fontButton->get_font_name()); - moptions.fontFamily = fd.get_family(); - moptions.fontSize = fd.get_size() / Pango::SCALE; + if (newFont) { + moptions.fontFamily = fd.get_family(); + moptions.fontSize = fd.get_size() / Pango::SCALE; + } Pango::FontDescription cpfd(colorPickerFontButton->get_font_name()); - moptions.CPFontFamily = cpfd.get_family(); - moptions.CPFontSize = cpfd.get_size() / Pango::SCALE; + if (newCPFont) { + moptions.CPFontFamily = cpfd.get_family(); + moptions.CPFontSize = cpfd.get_size() / Pango::SCALE; + } #ifdef WIN32 moptions.gimpDir = gimpDir->get_filename (); @@ -1758,6 +1777,7 @@ void Preferences::fillPreferences () tconn.block (true); fconn.block (true); + cpfconn.block (true); sconn.block (true); dfconn.block (true); ffconn.block (true); @@ -1843,8 +1863,17 @@ void Preferences::fillPreferences () butNavGuideCol->set_rgba(NavGuideCol); butNavGuideCol->set_alpha ( (unsigned short)(moptions.navGuideBrush[3] * 65535.0)); - fontButton->set_font_name(Glib::ustring::compose("%1 %2", options.fontFamily == "default" ? "sans" : options.fontFamily, options.fontSize)); - colorPickerFontButton->set_font_name(Glib::ustring::compose("%1 %2", options.CPFontFamily == "default" ? "sans" : options.CPFontFamily, options.CPFontSize)); + if (options.fontFamily == "default") { + fontButton->set_font_name (Glib::ustring::compose("%1 %2", initialFontFamily, initialFontSize)); + } else { + fontButton->set_font_name (Glib::ustring::compose("%1 %2", options.fontFamily, options.fontSize)); + } + + if (options.CPFontFamily == "default") { + colorPickerFontButton->set_font_name (Glib::ustring::compose("%1 %2", initialFontFamily, initialFontSize)); + } else { + colorPickerFontButton->set_font_name (Glib::ustring::compose("%1 %2", options.CPFontFamily, options.CPFontSize)); + } showDateTime->set_active (moptions.fbShowDateTime); showBasicExif->set_active (moptions.fbShowBasicExif); @@ -1965,6 +1994,7 @@ void Preferences::fillPreferences () addc.block (false); setc.block (false); + cpfconn.block (false); fconn.block (false); tconn.block (false); sconn.block (false); @@ -2047,7 +2077,11 @@ void Preferences::cancelPressed () // set the initial font back Pango::FontDescription fd(fontButton->get_font_name()); if (fd.get_family() != options.fontFamily && (fd.get_size() / Pango::SCALE) != options.fontSize) { - switchFontTo(options.fontFamily == "default" ? "sans" : options.fontFamily, options.fontSize); + if (options.fontFamily == "default") { + switchFontTo(initialFontFamily, initialFontSize); + } else { + switchFontTo(options.fontFamily, options.fontSize); + } } // update the profileStore @@ -2244,10 +2278,17 @@ void Preferences::switchThemeTo(Glib::ustring newTheme) void Preferences::fontChanged () { + newFont = true; Pango::FontDescription fd(fontButton->get_font_name()); switchFontTo(fd.get_family(), fd.get_size() / Pango::SCALE); } +void Preferences::cpFontChanged () +{ + + newCPFont = true; +} + void Preferences::switchFontTo(const Glib::ustring &newFontFamily, const int newFontSize) { @@ -2272,6 +2313,13 @@ void Preferences::switchFontTo(const Glib::ustring &newFontFamily, const int new printf("Error: Can't find the font named \"%s\"\n", newFontFamily.c_str()); } } + else { + if (fontcss) { + fontcss = Gtk::CssProvider::create(); + Glib::RefPtr screen = Gdk::Screen::get_default(); + Gtk::StyleContext::remove_provider_for_screen(screen, fontcss); + } + } } void Preferences::workflowUpdate () diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 50f72a957..18c8a466b 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -208,10 +208,13 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Glib::ustring storedValueImg; Options moptions; - sigc::connection tconn, sconn, fconn, addc, setc, dfconn, ffconn, bpconn, rpconn, ipconn; + sigc::connection tconn, sconn, fconn, cpfconn, addc, setc, dfconn, ffconn, bpconn, rpconn, ipconn; sigc::connection autoMonProfileConn, sndEnableConn, langAutoDetectConn, autocielabConn; Glib::ustring initialTheme; - Glib::ustring initialFont; + Glib::ustring initialFontFamily; + int initialFontSize; + bool newFont; + bool newCPFont; void fillPreferences (); void storePreferences (); @@ -222,6 +225,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener void workflowUpdate(); void themeChanged (); void fontChanged (); + void cpFontChanged (); void forRAWComboChanged (); void forImageComboChanged (); void layoutComboChanged (); From 845ebe6906ddb4d5ed4e3630030e00f26ee82fc2 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 13 Mar 2017 15:44:36 +0100 Subject: [PATCH 168/181] Faster loading of pixelshift files --- rtengine/rawimagesource.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index a25e63e56..71d1e10a1 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1536,7 +1536,6 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) riFrames[i] = new RawImage(fname); errCodeThr = riFrames[i]->loadRaw (true, i); } - riFrames[i]->compress_image(i); } #ifdef _OPENMP #pragma omp critical @@ -1548,10 +1547,13 @@ int RawImageSource::load (const Glib::ustring &fname, int imageNum, bool batch) } else { riFrames[0] = ri; errCode = riFrames[0]->loadRaw (true, 0, true, plistener, 0.8); - riFrames[0]->compress_image(0); } - if(errCode) { + if(!errCode) { + for(unsigned int i = 0; i < numFrames; ++i) { + riFrames[i]->compress_image(i); + } + } else { return errCode; } From c9d9715e6dcbac93b7d0a09b8378c5d0311ab127 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 13 Mar 2017 21:38:52 +0100 Subject: [PATCH 169/181] pixelshift: cleaned code --- rtengine/pixelshift.cc | 826 ++++++----------------------------------- 1 file changed, 122 insertions(+), 704 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 2e001f686..692e3d584 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -2,7 +2,6 @@ // // pentax pixelshift algorithm with motion detection // -// non adaptive mode is derived from dcrawps (https://github.com/tomtor/dcrawps), but with additional motion correction methods and adapted for RawTherapee data structures // // If motion correction is enabled only the pixels which are not detected as motion are set // That means for a complete image you have to demosaic one of the frames with a bayer demosaicer to fill red, green and blue @@ -37,10 +36,12 @@ namespace { -float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion, int x, int y) +float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between two green samples +#ifdef PIXELSHIFTDEV if(adaptive) { +#endif float gDiff = a - b; gDiff *= eperIso; gDiff *= gDiff; @@ -48,12 +49,6 @@ float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperI avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); - -// if(x >= 4294 && x <= 4303 && y >= 3056 && y <= 3058) { -// #pragma omp critical -// std::cout << "x : " << x << " y : " << y << " stddev : " << stddev << " avg : " << avg << " gDiff : " << gDiff << std::endl; -// } - float result = gDiff - stddev; if(!showMotion) { @@ -63,14 +58,18 @@ float greenDiff(float a, float b, bool adaptive, float stddevFactor, float eperI } else { return 0.f; } +#ifdef PIXELSHIFTDEV + } else { float gDiff = std::fabs(a - b); // add a small epsilon to avoid division by zero float maxVal = std::max(a, b) + 0.01f; return gDiff / maxVal; } +#endif } +#ifdef PIXELSHIFTDEV float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { // calculate the difference between two nongreen samples @@ -91,6 +90,7 @@ float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nr return 0.f; } } +#endif float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { @@ -300,611 +300,22 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma } } - } using namespace std; using namespace rtengine; -#ifdef __OLDPS__ -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, bool detectMotion, int motion, bool showMotion, bool showOnlyMask, unsigned int frame, RAWParams::BayerSensor::ePSMotionCorrection gridSize_, bool adaptive, float stddevFactorGreen, float stddevFactorRed, float stddevFactorBlue, float eperIso, float nreadIso, float prnu, const std::string &model, float rawWpCorrection, bool checkNonGreenHorizontal, bool checkNonGreenVertical, bool checkNonGreenCross) -{ - - BENCHFUN - - static const float nReadK3II[] = { 3.4f, // ISO 100 - 3.1f, // ISO 125 - 2.5f, // ISO 160 - 2.5f, // ISO 200 - 2.5f, // ISO 250 - 2.5f, // ISO 320 - 2.3f, // ISO 400 - 2.5f, // ISO 500 - 2.3f, // ISO 640 - 2.3f, // ISO 800 - 2.4f, // ISO 1000 - 2.3f, // ISO 1250 - 1.75f, // ISO 1600 - 1.75f, // ISO 2000 - 1.75f, // ISO 2500 - 1.75f, // ISO 3200 - 1.75f, // ISO 4000 - 1.75f, // ISO 5000 - 1.75f, // ISO 6400 - 1.75f, // ISO 8000 - 1.75f, // ISO 10000 - 1.5f, // ISO 12800 - 1.5f, // ISO 16000 - 1.5f, // ISO 20000 - 1.5f, // ISO 25600 - 1.5f, // ISO 32000 - 1.5f, // ISO 40000 - 1.5f, // ISO 51200 - 1.5f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK3II = 0.35f; - - static const float nReadK1[] = { 3.45f, // ISO 100 - 3.15f, // ISO 125 - 3.45f, // ISO 160 - 3.0f, // ISO 200 - 3.0f, // ISO 250 - 3.0f, // ISO 320 - 2.7f, // ISO 400 - 2.7f, // ISO 500 - 2.7f, // ISO 640 - 2.5f, // ISO 800 - 2.5f, // ISO 1000 - 2.5f, // ISO 1250 - 2.4f, // ISO 1600 - 2.4f, // ISO 2000 - 2.4f, // ISO 2500 - 2.4f, // ISO 3200 - 2.4f, // ISO 4000 - 2.4f, // ISO 5000 - 2.4f, // ISO 6400 - 2.4f, // ISO 8000 - 2.4f, // ISO 10000 - 2.4f, // ISO 12800 - 2.4f, // ISO 16000 - 2.4f, // ISO 20000 - 2.4f, // ISO 25600 - 2.4f, // ISO 32000 - 2.4f, // ISO 40000 - 2.4f, // ISO 51200 - 2.4f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK1 = 0.75f; - - static const float nReadK70[] = { 3.0f, // ISO 100 - 3.0f, // ISO 125 - 3.0f, // ISO 160 - 3.0f, // ISO 200 - 3.0f, // ISO 250 - 3.0f, // ISO 320 - 3.0f, // ISO 400 - 3.0f, // ISO 500 - 3.0f, // ISO 640 - 3.0f, // ISO 800 - 3.0f, // ISO 1000 - 3.0f, // ISO 1250 - 3.0f, // ISO 1600 - 3.0f, // ISO 2000 - 3.0f, // ISO 2500 - 3.0f, // ISO 3200 - 3.0f, // ISO 4000 - 3.0f, // ISO 5000 - 3.0f, // ISO 6400 - 3.0f, // ISO 8000 - 3.0f, // ISO 10000 - 3.0f, // ISO 12800 - 3.0f, // ISO 16000 - 3.0f, // ISO 20000 - 3.0f, // ISO 25600 - 3.0f, // ISO 32000 - 3.0f, // ISO 40000 - 3.0f, // ISO 51200 - 3.0f // ISO > 51200 (we get a max ISO value of 65535 from dcraw) - }; - - static const float ePerIsoK70 = 0.5f; - - if (plistener) { - plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); - plistener->setProgress(0.0); - } - - - const bool skip = (gridSize_ == RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2); - int gridSize = 1; - - switch (gridSize_) { - case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x1: - case RAWParams::BayerSensor::ePSMotionCorrection::Grid1x2: - gridSize = 1; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid3x3: - gridSize = 3; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid5x5: - gridSize = 5; - break; - - case RAWParams::BayerSensor::ePSMotionCorrection::Grid7x7: - gridSize = 7; - } - - // Lookup table for non adaptive (slider) mode - LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); - - if(detectMotion && !adaptive) { - const float lutStrength = 2.f; - log2Lut[0] = 0; - - for(int i = 2; i < 65536; i += 2) { - log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; - } - } - - const float scaleGreen = 1.f / scale_mul[1]; - - float nRead; - float eperIsoModel; - - int nReadIndex = static_cast(round(log2(idata->getISOSpeed() / 100.f) * 3.f)); - - if(model.find("K-3") != string::npos) { - nRead = nReadK3II[nReadIndex]; - eperIsoModel = ePerIsoK3II; - } else if(model.find("K-1") != string::npos) { - nRead = nReadK1[nReadIndex]; - eperIsoModel = ePerIsoK1; - } else { - nRead = nReadK70[nReadIndex]; - eperIsoModel = ePerIsoK70; - } - - nRead *= pow(2.f, nreadIso); - eperIsoModel *= pow(2.f, eperIso); - eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); - float eperIsoGreen = eperIso * scaleGreen; - -// printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); - - prnu /= 100.f; - stddevFactorGreen *= stddevFactorGreen; - stddevFactorRed *= stddevFactorRed; - stddevFactorBlue *= stddevFactorBlue; - - - nRead *= nRead; - - // If the values of two corresponding green pixels differ my more then motionThreshold %, the pixel will be treated as a badGreen pixel - float motionThreshold = 1.f - (motion / 100.f); - // For shades of green motion indicators - const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); - - unsigned int offsX = 0, offsY = 0; - - // We have to adjust the offsets for the selected subframe we use for areas with motion - switch (frame) { - case 0: - offsX = offsY = 0; - break; - - case 1: - offsX = 0; - offsY = 1; - break; - - case 2: - offsX = offsY = 1; - break; - - case 3: - offsX = 1; - offsY = 0; - } - - const float thresh = adaptive ? 0.f : motionThreshold; - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - - for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { - float *greenDest = green[i + offsY]; - float *nonGreenDest0 = red[i + offsY]; - float *nonGreenDest1 = blue[i + offsY]; - int j = winx + border - offsX; - int c = FC(i, j); - float scaleNonGreen0 = 1.f / scale_mul[0]; - float scaleNonGreen2 = 1.f / scale_mul[2]; - float eperIsoNonGreen0 = eperIso / scale_mul[0]; - float eperIsoNonGreen2 = eperIso / scale_mul[2]; - float stddevFactorNonGreen0 = stddevFactorRed; - float stddevFactorNonGreen2 = stddevFactorBlue; - bool blueRow = false; - - if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { - // row with blue pixels => swap destination pointers for non green pixels - blueRow = true; - std::swap(nonGreenDest0, nonGreenDest1); - std::swap(scaleNonGreen0, scaleNonGreen2); - std::swap(eperIsoNonGreen0, eperIsoNonGreen2); - std::swap(stddevFactorNonGreen0, stddevFactorNonGreen2); - } - - // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); - - float greenDifMax[gridSize]; - - // green channel motion detection checks the grid around the pixel for differences in green channels - if(detectMotion || adaptive) { - if(gridSize == 3) { - // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - } else if(gridSize == 5) { - // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - } else if(gridSize == 7) { - // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 3], (*rawDataFrames[3 - offset])[i + offset - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 3], (*rawDataFrames[2 + offset])[i - offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 3], (*rawDataFrames[3 - offset])[i + offset - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 3], (*rawDataFrames[2 + offset])[i - offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 3], (*rawDataFrames[3 - offset])[i + offset + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 3], (*rawDataFrames[2 + offset])[i - offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 3], (*rawDataFrames[3 - offset])[i + offset + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[1] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j - 2], (*rawDataFrames[2 + offset])[i - offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j - 2], (*rawDataFrames[3 - offset])[i + offset - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j - 2], (*rawDataFrames[2 + offset])[i - offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j - 2], (*rawDataFrames[3 - offset])[i + offset][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j - 2], (*rawDataFrames[2 + offset])[i - offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j - 2], (*rawDataFrames[3 - offset])[i + offset + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j - 2], (*rawDataFrames[2 + offset])[i - offset + 4][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[2] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j - 1], (*rawDataFrames[3 - offset])[i + offset - 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j - 1], (*rawDataFrames[2 + offset])[i - offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j - 1], (*rawDataFrames[3 - offset])[i + offset - 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j - 1], (*rawDataFrames[2 + offset])[i - offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j - 1], (*rawDataFrames[3 - offset])[i + offset + 1][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j - 1], (*rawDataFrames[2 + offset])[i - offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j - 1], (*rawDataFrames[3 - offset])[i + offset + 3][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[3] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j], (*rawDataFrames[2 + offset])[i - offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j], (*rawDataFrames[3 - offset])[i + offset - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j], (*rawDataFrames[2 + offset])[i - offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j], (*rawDataFrames[2 + offset])[i - offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j], (*rawDataFrames[3 - offset])[i + offset + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j], (*rawDataFrames[2 + offset])[i - offset + 4][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[4] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 1], (*rawDataFrames[3 - offset])[i + offset - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 1], (*rawDataFrames[2 + offset])[i - offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 1], (*rawDataFrames[2 + offset])[i - offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 1], (*rawDataFrames[3 - offset])[i + offset + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - greenDifMax[5] = std::max({greenDiff((*rawDataFrames[0 + offset])[i + offset - 3][j + 2], (*rawDataFrames[2 + offset])[i - offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 3][j + 2], (*rawDataFrames[2 + offset])[i - offset + 4][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - } - - } - - offset ^= 1; // 0 => 1 or 1 => 0 - - // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 - int lastIndex = gridSize - 1; - float korr = 0.f; - - for(; j < winw - (border + offsX); ++j) { - offset ^= 1; // 0 => 1 or 1 => 0 - - if(detectMotion || adaptive) { - bool skipNext = false; - float gridMax; - - if(gridSize < 2) { - // compute difference for current pixel and skip next pixel, that's the method from dcrawps - gridMax = greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j], (*rawDataFrames[3 - offset])[i + offset][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); - skipNext = skip; - } else if(gridSize == 3) { - // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 1], (*rawDataFrames[3 - offset])[i + offset - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 1], (*rawDataFrames[2 + offset])[i - offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 1], (*rawDataFrames[3 - offset])[i + offset + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); - } else if(gridSize == 5) { - // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 1][j + 2], (*rawDataFrames[3 - offset])[i + offset - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 1][j + 2], (*rawDataFrames[2 + offset])[i - offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j + 2], (*rawDataFrames[3 - offset])[i + offset][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 1][j + 2], (*rawDataFrames[2 + offset])[i - offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 3][j + 2], (*rawDataFrames[3 - offset])[i + offset + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); - } else if(gridSize == 7) { - // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff((*rawDataFrames[1 - offset])[i - offset - 2][j + 3], (*rawDataFrames[3 - offset])[i + offset - 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset - 2][j + 3], (*rawDataFrames[2 + offset])[i - offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset][j + 3], (*rawDataFrames[3 - offset])[i + offset - 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset][j + 3], (*rawDataFrames[2 + offset])[i - offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 2][j + 3], (*rawDataFrames[3 - offset])[i + offset + 1][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[0 + offset])[i + offset + 2][j + 3], (*rawDataFrames[2 + offset])[i - offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff((*rawDataFrames[1 - offset])[i - offset + 4][j + 3], (*rawDataFrames[3 - offset])[i + offset + 3][j + 4], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) - }); - gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); - } - - - // adjust index for next column - lastIndex ++; - lastIndex = lastIndex == gridSize ? 0 : lastIndex; - - // increase motion detection dependent on brightness - if(!adaptive) { - korr = log2Lut[((int)((*rawDataFrames[1 - offset])[i - offset + 1][j] * scaleGreen)) >> 1]; - } - - if (gridMax > thresh - korr) { - // at least one of the tested pixels of the grid is detected as motion - paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, nonGreenDest0, nonGreenDest1); - - if(skipNext) { - // treat the horizontally next pixel also as motion - j++; - paintMotionMask(j + offsX, showMotion, (gridMax - thresh + korr) * blendFactor, showOnlyMask, greenDest, nonGreenDest0, nonGreenDest1); - offset ^= 1; - } - - // do not set the motion pixel values. They have already been set by demosaicer or showMotion - continue; - } - - if(adaptive && checkNonGreenCross) { - float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; - float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float diffRight = ngRight - ngCentre; - float diffLeft = ngLeft - ngCentre; - float diffHorNg0 = -1.f; - - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - diffHorNg0 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - -// if(diff > 0.f) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } - } - - float diffHorNg1 = -1.f; - ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; - ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - diffRight = ngRight - ngCentre; - diffLeft = ngLeft - ngCentre; - - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - float diffHorNg1 = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - -// if(diff > 0.f) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); -// continue; -// } - } - - if( diffHorNg0 * diffHorNg1 < 0.f) { - paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); - continue; - - } - -// bool motion = false; -// float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; -// float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; -// float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; -// float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; -// float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; -// float diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); -// -// if(diff > 0.f) { -// motion = true; -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } -// -// ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; -// ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; -// ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; -// ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; -// ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; -// diff = nonGreenDiffCross(ngRight, ngLeft, ngTop, ngBottom, ngCentre, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); -// -//// if(diff > 0.f) { -// if((diff > 0.f && !motion) || (diff <= 0.f && motion) ) { -// paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); -// continue; -// } - } - - if(adaptive && checkNonGreenHorizontal) { -// float lg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j - 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j]) / 2.f; -// float cg = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; -// float rg = ((*rawDataFrames[1 - (offset^1)])[i - (offset^1) + 1][j + 1] + (*rawDataFrames[3 - (offset^1)])[i + (offset^1)][j + 2]) / 2.f; -// -// float lr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) - 1]; -// float cr = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; -// float rr = (*rawDataFrames[((offset^1) << 1) + (offset^1)])[i][j + (offset^1) + 1]; -// -// float lb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1)]; -// float cb = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; -// float rb = (*rawDataFrames[2 - (offset^1)])[i + 1][j - (offset^1) + 2]; -// -// if(blueRow) { -// std::swap(lr, lb); -// std::swap(cr, cb); -// std::swap(rr, rb); -// } -// -// float lh = Color::rgb2h(lr, lg, lb); -// float ch = Color::rgb2h(cr, cg, cb); -// float rh = Color::rgb2h(rr, rg, rb); -// -// float lHueDiff = lh - ch; -// float rHueDiff = rh - ch; -// if(lHueDiff * rHueDiff > 0.f) { -// if(std::fabs(lHueDiff) > 0.5f && std::fabs(rHueDiff) > 0.5f/* && std::fabs(lHueDiff) < 3.f && std::fabs(rHueDiff) < 3.f*/) { -// paintMotionMask(j + offsX, showMotion, 1.f, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); -// continue; -// } -// } - float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ngRight = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) + 1]; - float ngLeft = (*rawDataFrames[((offset ^ 1) << 1) + (offset ^ 1)])[i][j + (offset ^ 1) - 1]; - float diffRight = ngRight - ngCentre; - float diffLeft = ngLeft - ngCentre; - - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); - continue; - } - } - - ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ngRight = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1) + 2]; - ngLeft = (*rawDataFrames[2 - (offset ^ 1)])[i + 1][j - (offset ^ 1)]; - diffRight = ngRight - ngCentre; - diffLeft = ngLeft - ngCentre; - - if(diffRight * diffLeft >= 0.f) { - float avg = (ngRight + ngLeft) / 2.f; - float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); - continue; - } - } - } - - if(adaptive && checkNonGreenVertical) { - float ngCentre = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - float ngTop = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i][j + offset]; - float ngBottom = (*rawDataFrames[((offset << 1) + offset) ^ 1])[i + 2][j + offset]; - - float diffTop = ngTop - ngCentre; - float diffBottom = ngBottom - ngCentre; - - if(diffTop * diffBottom >= 0.f) { - float avg = (ngTop + ngBottom) / 2.f; - float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen0, eperIsoNonGreen0, nRead, prnu, showMotion); - - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest0, nonGreenDest1, greenDest); - continue; - } - } - - ngCentre = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - ngTop = (*rawDataFrames[3 - ((offset << 1) + offset)])[i - 1][j - offset + 1]; - ngBottom = (*rawDataFrames[3 - ((offset << 1) + offset)])[i + 1][j - offset + 1]; - - diffTop = ngTop - ngCentre; - diffBottom = ngBottom - ngCentre; - - if(diffTop * diffBottom >= 0.f) { - float avg = (ngTop + ngBottom) / 2.f; - float diff = nonGreenDiff(ngCentre, avg, stddevFactorNonGreen2, eperIsoNonGreen2, nRead, prnu, showMotion); - - if(diff > 0.f) { - paintMotionMask(j + offsX, showMotion, diff, showOnlyMask, nonGreenDest1, nonGreenDest0, greenDest); - continue; - } - } - } - } - - if(showMotion && showOnlyMask) { - greenDest[j + offsX] = nonGreenDest0[j + offsX] = nonGreenDest1[j + offsX] = 0.f; - continue; - } - - // motion correction disabled or no motion detected => combine the values from the four pixelshift frames - greenDest[j + offsX] = ((*rawDataFrames[1 - offset])[i - offset + 1][j] + (*rawDataFrames[3 - offset])[i + offset][j + 1]) / 2.f; - nonGreenDest0[j + offsX] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset]; - nonGreenDest1[j + offsX] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1]; - } - } - - if(plistener) { - plistener->setProgress(1.0); - } -} -#else void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection) { #ifdef PIXELSHIFTDEV BENCHFUN #endif - const bool detectMotion = bayerParams.pixelShiftMotion > 0; const int motion = bayerParams.pixelShiftMotion; const bool showMotion = bayerParams.pixelshiftShowMotion; const bool showOnlyMask = bayerParams.pixelshiftShowMotionMaskOnly && showMotion; const RAWParams::BayerSensor::ePSMotionCorrection gridSize_ = bayerParams.pixelShiftMotionCorrection; const bool adaptive = bayerParams.pixelShiftAutomatic; #ifdef PIXELSHIFTDEV + const bool detectMotion = bayerParams.pixelShiftMotion > 0; float stddevFactorGreen = bayerParams.pixelShiftStddevFactorGreen; float stddevFactorRed = bayerParams.pixelShiftStddevFactorRed; float stddevFactorBlue = bayerParams.pixelShiftStddevFactorBlue; @@ -932,7 +343,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #ifdef PIXELSHIFTDEV const float threshold = bayerParams.pixelShiftSum + 9.f; #else - const float threshold = 3.f + 9.f; + constexpr float threshold = 3.f + 9.f; #endif const bool experimental0 = bayerParams.pixelShiftExp0; const bool holeFill = bayerParams.pixelShiftHoleFill; @@ -1086,7 +497,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA return; } - +#ifdef PIXELSHIFTDEV // Lookup table for non adaptive (slider) mode LUTf log2Lut(32768, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); @@ -1098,6 +509,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA log2Lut[i >> 1] = lutStrength * log2(i) / 100.f; } } +#endif const float scaleGreen = 1.f / scale_mul[1]; @@ -1120,11 +532,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA nRead *= pow(2.f, nreadIso); eperIsoModel *= pow(2.f, eperIso); +#ifdef PIXELSHIFTDEV if(adaptive && experimental0) { eperIso = eperIsoModel * sqrtf(100.f / (rawWpCorrection * idata->getISOSpeed())); } else { eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); } +#else + eperIso = eperIsoModel * (100.f / (rawWpCorrection * idata->getISOSpeed())); +#endif #ifdef PIXELSHIFTDEV std::cout << "WL: " << c_white[0] << " BL: " << c_black[0] << " ePerIso multiplicator: " << (65535.f / (c_white[0] - c_black[0])) << std::endl; @@ -1133,8 +549,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1] - c_black[1])); float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2] - c_black[2])); -// printf("Pixelshift parameters : gridSize %d\tadaptive %d\tstdDevFactorGreen %f\telectrons %1.8f\tnread %f\tprnu %1.1f%%\n", gridSize, adaptive, stddevFactorGreen, eperIso, nRead, prnu); - prnu /= 100.f; stddevFactorGreen *= stddevFactorGreen; stddevFactorRed *= stddevFactorRed; @@ -1178,8 +592,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA array2D psG2(winw + 32, winh); array2D psBlue(winw + 32, winh); -// calculate average green brightness for each frame - double greenBrightness[4] = {}; + // calculate average green brightness for each frame + float greenBrightness[4] = {1.f, 1.f, 1.f, 1.f}; if(equalBrightness) { LUT *histo[4]; @@ -1209,17 +623,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA int c = FC(i, j); // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); - offset ^= 1; // 0 => 1 or 1 => 0 + unsigned int offset = c & 1; for(; j < winw - 1; ++j) { - offset ^= 1; // 0 => 1 or 1 => 0 - - // store the values from the 4 frames into 4 different temporary planes float green1 = (*rawDataFrames[1 - offset])[i - offset + 1][j]; float green2 = (*rawDataFrames[3 - offset])[i + offset][j + 1]; (*histoThr[1 - offset])[green1]++; (*histoThr[3 - offset])[green2]++; + offset ^= 1; // 0 => 1 or 1 => 0 } } @@ -1244,22 +655,19 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA ++median; } - medians[i] = (median + median - 1) / 2.f; + const float weight = (count - datalen / 2.f) / (*histo[i])[median - 1]; + medians[i] = intp(weight, (float)(median - 2), (float)(median - 1)); delete histo[i]; } - const float medianMaster = medians[frame]; - for(int i = 0; i < 4; ++i) { - greenBrightness[i] = medianMaster / medians[i]; + greenBrightness[i] = medians[frame] / medians[i]; } #ifdef PIXELSHIFTDEV std::cout << "brightness factors by median : " << greenBrightness[0] << " " << greenBrightness[1] << " " << greenBrightness[2] << " " << greenBrightness[3] << std::endl; #endif - } else { - greenBrightness[0] = greenBrightness[1] = greenBrightness[2] = greenBrightness[3] = 1.f; } // fill channels psRed, psG1, psG2 and psBlue @@ -1322,8 +730,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float *blueDest = blue[i + offsY]; int j = winx + border - offsX; +#ifdef PIXELSHIFTDEV float greenDifMax[gridSize]; // Here we store the maximum differences per Column - +#endif // green channel motion detection checks the grid around the pixel for differences in green channels #ifdef PIXELSHIFTDEV @@ -1331,89 +740,89 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(detectMotion || (adaptive && checkGreen)) { if(gridSize == 3) { // compute maximum of differences for first two columns of 3x3 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[0] = std::max({greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[1] = std::max({greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); } else if(gridSize == 5) { // compute maximum of differences for first four columns of 5x5 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[0] = std::max({greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[1] = std::max({greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[2] = std::max({greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[3] = std::max({greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); } else if(gridSize == 7) { // compute maximum of differences for first six columns of 7x7 grid - greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[0] = std::max({greenDiff(psG1[i - 3][j - 3], psG2[i - 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 3], psG2[i - 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 3], psG2[i - 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 3], psG2[ i ][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 3], psG2[i + 1][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 3], psG2[i + 2][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 3], psG2[i + 3][j - 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[1] = std::max({greenDiff(psG1[i - 3][j - 2], psG2[i - 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 2], psG2[i - 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 2], psG2[i - 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 2], psG2[ i ][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 2], psG2[i + 1][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 2], psG2[i + 2][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 2], psG2[i + 3][j - 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[2] = std::max({greenDiff(psG1[i - 3][j - 1], psG2[i - 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j - 1], psG2[i - 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j - 1], psG2[i - 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j - 1], psG2[ i ][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j - 1], psG2[i + 1][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j - 1], psG2[i + 2][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j - 1], psG2[i + 3][j - 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[3] = std::max({greenDiff(psG1[i - 3][ j ], psG2[i - 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][ j ], psG2[i - 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][ j ], psG2[i - 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][ j ], psG2[ i ][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][ j ], psG2[i + 1][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][ j ], psG2[i + 2][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][ j ], psG2[i + 3][ j ], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[4] = std::max({greenDiff(psG1[i - 3][j + 1], psG2[i - 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 1], psG2[i - 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 1], psG2[i + 2][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 1], psG2[i + 3][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); - greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[5] = std::max({greenDiff(psG1[i - 3][j + 2], psG2[i - 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 2], psG2[i + 3][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); } @@ -1422,7 +831,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #endif // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 +#ifdef PIXELSHIFTDEV int lastIndex = gridSize - 1; +#endif + float korr = 0.f; int c = FC(i, j); bool blueRow = false; @@ -1440,8 +852,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA offset ^= 1; // 0 => 1 or 1 => 0 +#ifdef PIXELSHIFTDEV if(detectMotion || (adaptive && checkGreen)) { bool skipNext = false; +#else + if(adaptive && checkGreen) { +#endif float gridMax; #ifdef PIXELSHIFTDEV @@ -1449,15 +865,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(gridSize < 2) { // compute difference for current pixel and skip next pixel, that's roughly the method from dcrawps #endif - gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i); + gridMax = greenDiff(psG1[i][j], psG2[i][j], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion); #ifdef PIXELSHIFTDEV skipNext = skip; } else if(gridSize == 3) { // compute maximum of differences for third column of 3x3 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 1][j + 1], psG2[i - 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 1], psG2[ i ][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 1], psG2[i + 1][j + 1], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2]}); @@ -1466,11 +882,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA lastIndex = lastIndex == gridSize ? 0 : lastIndex; } else if(gridSize == 5) { // compute maximum of differences for fifth column of 5x5 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i) + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 2][j + 2], psG2[i - 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 2], psG2[i - 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 2], psG2[ i ][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 2], psG2[i + 1][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 2], psG2[i + 2][j + 2], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion) }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4]}); @@ -1479,13 +895,13 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA lastIndex = lastIndex == gridSize ? 0 : lastIndex; } else if(gridSize == 7) { // compute maximum of differences for 7th column of 7x7 grid and save at position lastIndex - greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), - greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion, j, i), + greenDifMax[lastIndex] = std::max({greenDiff(psG1[i - 3][j + 3], psG2[i - 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 2][j + 3], psG2[i - 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i - 1][j + 3], psG2[i - 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[ i ][j + 3], psG2[ i ][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 1][j + 3], psG2[i + 1][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 2][j + 3], psG2[i + 2][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), + greenDiff(psG1[i + 3][j + 3], psG2[i + 3][j + 3], adaptive, stddevFactorGreen, eperIsoGreen, nRead, prnu, showMotion), }); // calculate maximum of whole grid by calculating maximum of grid column max values gridMax = std::max({greenDifMax[0], greenDifMax[1], greenDifMax[2], greenDifMax[3], greenDifMax[4], greenDifMax[5], greenDifMax[6]}); @@ -1499,9 +915,12 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; } + if (gridMax > thresh - korr) { +#else + if (gridMax > thresh) { + #endif - if (gridMax > thresh - korr) { #ifdef PIXELSHIFTDEV sumThr[offset] ++; @@ -1755,7 +1174,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #endif { sum[0] += sumThr[0]; - sum[1] += sumThr[0]; + sum[1] += sumThr[1]; } #endif } @@ -1818,7 +1237,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { #ifdef __SSE2__ - if(smoothTransitions) { + if(smoothTransitions) { // vfloat onev = F2V(1.f); vfloat smoothv = F2V(smoothFactor); int j = winx + border - offsX; @@ -1868,4 +1287,3 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA plistener->setProgress(1.0); } } -#endif \ No newline at end of file From 3660ed5f95065d40f924467d6acfd1caf69dd5a6 Mon Sep 17 00:00:00 2001 From: Hombre Date: Mon, 13 Mar 2017 21:41:01 +0100 Subject: [PATCH 170/181] (attempt to ) solving issue #3759 --- rtgui/wavelet.cc | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/rtgui/wavelet.cc b/rtgui/wavelet.cc index 71f3235c6..e1d0fc425 100644 --- a/rtgui/wavelet.cc +++ b/rtgui/wavelet.cc @@ -172,8 +172,7 @@ Wavelet::Wavelet() : enableFinalConn = expfinal->signal_enabled_toggled().connect ( sigc::bind( sigc::mem_fun(this, &Wavelet::enableToggled), expfinal) ); // Wavelet Settings - Gtk::VBox* const settingsVBox = Gtk::manage(new Gtk::VBox()); - settingsVBox->set_spacing(2); + ToolParamBlock* const settingsBox = Gtk::manage (new ToolParamBlock()); strength->setAdjusterListener (this); @@ -247,16 +246,16 @@ Wavelet::Wavelet() : levdirSubHBox->pack_start(*Lmethod); levdirSubHBox->pack_start(*Dirmethod, Gtk::PACK_EXPAND_WIDGET, 2); // same, but 2 not 4? - settingsVBox->pack_start(*strength); - settingsVBox->pack_start(*thres); - settingsVBox->pack_start(*tilesizeHBox); - settingsVBox->pack_start(*daubcoeffHBox); - settingsVBox->pack_start(*backgroundHBox); - settingsVBox->pack_start(*levdirMainHBox); - settingsVBox->pack_start(*levdirSubHBox); + settingsBox->pack_start(*strength); + settingsBox->pack_start(*thres); + settingsBox->pack_start(*tilesizeHBox); + settingsBox->pack_start(*daubcoeffHBox); + settingsBox->pack_start(*backgroundHBox); + settingsBox->pack_start(*levdirMainHBox); + settingsBox->pack_start(*levdirSubHBox); // Contrast - Gtk::VBox* const levBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const levBox = Gtk::manage (new ToolParamBlock()); Gtk::HBox* const buttonBox = Gtk::manage (new Gtk::HBox(true, 10)); levBox->pack_start(*buttonBox, Gtk::PACK_SHRINK, 2); @@ -335,7 +334,7 @@ Wavelet::Wavelet() : levBox->pack_start(*contrastSHFrame); // Chromaticity - Gtk::VBox* const chBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const chBox = Gtk::manage (new ToolParamBlock()); Gtk::Label* const labmch = Gtk::manage(new Gtk::Label(M("TP_WAVELET_CHTYPE") + ":")); Gtk::HBox* const ctboxch = Gtk::manage(new Gtk::HBox()); @@ -405,7 +404,7 @@ Wavelet::Wavelet() : } // Toning - Gtk::VBox* const tonBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const tonBox = Gtk::manage (new ToolParamBlock()); opaCurveEditorG->setCurveListener (this); @@ -434,7 +433,7 @@ Wavelet::Wavelet() : tonBox->pack_start( *opacityCurveEditorG, Gtk::PACK_SHRINK, 2); // Denoise and Refine - Gtk::VBox* const noiseBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const noiseBox = Gtk::manage (new ToolParamBlock()); linkedg->set_active (true); linkedgConn = linkedg->signal_toggled().connect( sigc::mem_fun(*this, &Wavelet::linkedgToggled) ); @@ -458,7 +457,7 @@ Wavelet::Wavelet() : noiseBox->pack_start( *level3noise, Gtk::PACK_SHRINK, 0); // Edge Sharpness - Gtk::VBox* const edgBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const edgBox = Gtk::manage (new ToolParamBlock()); edgval->setAdjusterListener(this); edgBox->pack_start(*edgval); @@ -571,7 +570,7 @@ Wavelet::Wavelet() : edgBox->pack_start(*ctboxES); // Gamut - Gtk::VBox* const conBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const conBox = Gtk::manage (new ToolParamBlock()); median->set_active (true); medianConn = median->signal_toggled().connect( sigc::mem_fun(*this, &Wavelet::medianToggled) ); @@ -621,7 +620,7 @@ Wavelet::Wavelet() : conBox->pack_start(*avoid); // Residual Image - Gtk::VBox* const resBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const resBox = Gtk::manage (new ToolParamBlock()); rescon->setAdjusterListener (this); resBox->pack_start(*rescon, Gtk::PACK_SHRINK); @@ -822,7 +821,7 @@ Wavelet::Wavelet() : tmr->set_tooltip_text (M("TP_WAVELET_BALCHRO_TOOLTIP")); tmrConn = tmr->signal_toggled().connect( sigc::mem_fun(*this, &Wavelet::tmrToggled) ); - Gtk::VBox* const finalBox = Gtk::manage (new ToolParamBlock()); + ToolParamBlock* const finalBox = Gtk::manage (new ToolParamBlock()); finalBox->pack_start (*ctboxBA); finalBox->pack_start(*balance); @@ -839,7 +838,7 @@ Wavelet::Wavelet() : //----------------------------- - expsettings->add(*settingsVBox); + expsettings->add(*settingsBox); expsettings->setLevel(2); pack_start (*expsettings); From 499ca64a2c614827988f700b7f24402002e99f7c Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 15 Mar 2017 01:17:17 +0100 Subject: [PATCH 171/181] pixelshift: Allow translation of previously hardcoded motion correction methods Off/Automatic/Custom --- rtdata/languages/default | 3 +++ rtgui/bayerprocess.cc | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 02260f2e5..4d3bdab5c 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1730,6 +1730,9 @@ TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP;Shows the motion mask without the im TP_RAW_PIXELSHIFTMOTIONCORRECTION;Green motion correction size TP_RAW_PIXELSHIFTMOTIONCORRECTION_TOOLTIP;1 = 2 pixels\n3 = 3x3 grid\n5 = 5x5 grid TP_RAW_PIXELSHIFTMOTIONMETHOD;Motion Correction +TP_RAW_PIXELSHIFTMM_OFF;Off +TP_RAW_PIXELSHIFTMM_AUTO;Automatic +TP_RAW_PIXELSHIFTMM_CUSTOM;Custom TP_RAW_PIXELSHIFTSHOWMOTION;Show motion TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY;Show mask only TP_RAW_PIXELSHIFTSTDDEVFACTORGREEN;StdDev factor Green diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 5c559fc1f..f8ac4b5a3 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -101,10 +101,10 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA Gtk::HBox* hb3 = Gtk::manage (new Gtk::HBox ()); hb3->pack_start (*Gtk::manage (new Gtk::Label ( M("TP_RAW_PIXELSHIFTMOTIONMETHOD") + ": ")), Gtk::PACK_SHRINK, 4); pixelShiftMotionMethod = Gtk::manage (new MyComboBoxText ()); - pixelShiftMotionMethod->append("Off"); - pixelShiftMotionMethod->append("Automatic"); - pixelShiftMotionMethod->append("Custom"); - pixelShiftMotionMethod->set_active(1); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_OFF")); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_AUTO")); + pixelShiftMotionMethod->append(M("TP_RAW_PIXELSHIFTMM_CUSTOM")); + pixelShiftMotionMethod->set_active(RAWParams::BayerSensor::ePSMotionCorrectionMethod::Automatic); pixelShiftMotionMethod->show(); hb3->pack_start(*pixelShiftMotionMethod); pixelShiftFrame->pack_start(*hb3); @@ -762,7 +762,7 @@ void BayerProcess::methodChanged () } if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { - if(pixelShiftMotionMethod->get_active_row_number() == 2) { + if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Custom) { pixelShiftOptions->show(); } else { pixelShiftOptions->hide(); @@ -819,11 +819,11 @@ void BayerProcess::dcbEnhanceChanged () void BayerProcess::pixelShiftMotionMethodChanged () { - if(pixelShiftMotionMethod->get_active_row_number() == 0) { + if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Off) { pixelShiftOptions->hide(); pixelShiftShowMotion->hide(); pixelShiftShowMotionMaskOnly->hide(); - } else if(pixelShiftMotionMethod->get_active_row_number() == 2) { + } else if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Custom) { pixelShiftOptions->show(); pixelShiftShowMotion->show(); pixelShiftShowMotionMaskOnly->show(); From 28015d8c26b906d854dbb3edb867eedffd1f730d Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 15 Mar 2017 23:24:18 +0100 Subject: [PATCH 172/181] =?UTF-8?q?Pixelshift:=20reduce=20number=20of=20fa?= =?UTF-8?q?lse=20positive=20motion=20detections=20for=20red/blue=20check?= =?UTF-8?q?=20on=20transitions=20between=20overexposed=20a=C5=84d=20correc?= =?UTF-8?q?tly=20exposed=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rtengine/pixelshift.cc | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 692e3d584..41e1c9af2 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -92,8 +92,11 @@ float nonGreenDiff(float a, float b, float stddevFactor, float eperIso, float nr } #endif -float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) +float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float clippedVal, float stddevFactor, float eperIso, float nreadIso, float prnu, bool showMotion) { + if(rtengine::max(right, left, top, bottom, centre) > clippedVal) { + return 0.f; + } // check non green cross float hDiff = (right + left) * 0.5f - centre; hDiff *= eperIso; @@ -549,6 +552,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1] - c_black[1])); float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2] - c_black[2])); + const float clippedRed = 65535.f / scale_mul[0]; + const float clippedBlue = 65535.f / scale_mul[2]; + prnu /= 100.f; stddevFactorGreen *= stddevFactorGreen; stddevFactorRed *= stddevFactorRed; @@ -615,7 +621,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } #ifdef _OPENMP - #pragma omp for schedule(dynamic,16) + #pragma omp for schedule(dynamic,16) nowait #endif for(int i = winy + 1; i < winh - 1; ++i) { @@ -683,36 +689,33 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA int j = winx + 1; int c = FC(i, j); - if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { + if ((c + FC(i, j + 1)) == 3) { // row with blue pixels => swap destination pointers for non green pixels std::swap(nonGreenDest0, nonGreenDest1); std::swap(greenDest1, greenDest2); } // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); - offset ^= 1; // 0 => 1 or 1 => 0 + unsigned int offset = c & 1; for(; j < winw - 1; ++j) { - offset ^= 1; // 0 => 1 or 1 => 0 - // store the values from the 4 frames into 4 different temporary planes greenDest1[j] = (*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset]; greenDest2[j] = (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]; nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset] * greenBrightness[(offset << 1) + offset]; nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * greenBrightness[2 - offset]; + offset ^= 1; // 0 => 1 or 1 => 0 } } // now that the temporary planes are filled for easy access we do the motion detection #ifdef PIXELSHIFTDEV int sum[2] = {0}; -#endif float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; +#endif array2D psMask(winw, winh); - #ifdef _OPENMP #pragma omp parallel #endif @@ -732,10 +735,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #ifdef PIXELSHIFTDEV float greenDifMax[gridSize]; // Here we store the maximum differences per Column -#endif // green channel motion detection checks the grid around the pixel for differences in green channels -#ifdef PIXELSHIFTDEV if(detectMotion || (adaptive && checkGreen)) { if(gridSize == 3) { @@ -828,24 +829,22 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } -#endif - // this is the index for the last column of the grid. Obviously we have to start with gridSize - 1 -#ifdef PIXELSHIFTDEV int lastIndex = gridSize - 1; + float korr = 0.f; + bool blueRow = false; #endif - float korr = 0.f; int c = FC(i, j); - bool blueRow = false; +#ifdef PIXELSHIFTDEV if (c == 2 || ((c & 1) && FC(i, j + 1) == 2)) { // row with blue pixels => swap destination pointers for non green pixels blueRow = true; } - +#endif // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop - unsigned int offset = (c & 1); + unsigned int offset = c & 1; for(; j < winw - (border + offsX); ++j) { psMask[i][j] = 1.f; @@ -962,7 +961,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float redCentre = psRed[ i ][ j ]; float redRight = psRed[ i ][j + 1]; float redBottom = psRed[i + 1][ j ]; - float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + float redDiff = nonGreenDiffCross(redRight, redLeft, redTop, redBottom, redCentre, clippedRed, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); if(redDiff > 0.f) { #ifdef PIXELSHIFTDEV @@ -985,7 +984,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float blueCentre = psBlue[ i ][ j ]; float blueRight = psBlue[ i ][j + 1]; float blueBottom = psBlue[i + 1][ j ]; - float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); + float blueDiff = nonGreenDiffCross(blueRight, blueLeft, blueTop, blueBottom, blueCentre, clippedBlue, stddevFactorBlue, eperIsoBlue, nRead, prnu, showMotion); if(blueDiff > 0.f) { #ifdef PIXELSHIFTDEV From a25a5856732b417b7170abf023d2674bbe7f9d51 Mon Sep 17 00:00:00 2001 From: Hombre Date: Thu, 16 Mar 2017 01:16:37 +0100 Subject: [PATCH 173/181] Checkbox class added to simplify event handling. (issue #3739) --- rtgui/CMakeLists.txt | 2 +- rtgui/bayerprocess.cc | 825 +++++++++++++++--------------------------- rtgui/bayerprocess.h | 57 ++- rtgui/checkbox.cc | 162 +++++++++ rtgui/checkbox.h | 78 ++++ rtgui/guiutils.h | 22 +- 6 files changed, 547 insertions(+), 599 deletions(-) create mode 100644 rtgui/checkbox.cc create mode 100644 rtgui/checkbox.h diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 153c1a0ef..795f6f28d 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -1,7 +1,7 @@ set (BASESOURCEFILES editwindow.cc batchtoolpanelcoord.cc paramsedited.cc cropwindow.cc previewhandler.cc previewwindow.cc navigator.cc indclippedpanel.cc previewmodepanel.cc filterpanel.cc - exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc + exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc checkbox.cc ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coordinateadjuster.cc diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index f8ac4b5a3..75b27b360 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -22,6 +22,7 @@ using namespace rtengine; using namespace rtengine::procparams; + BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RAW_LABEL"), true) { Gtk::HBox* hb1 = Gtk::manage (new Gtk::HBox ()); @@ -72,7 +73,8 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA } dcbIterations->show(); - dcbEnhance = Gtk::manage (new MyCheckButton(M("TP_RAW_DCBENHANCE"))); + dcbEnhance = Gtk::manage (new CheckBox(M("TP_RAW_DCBENHANCE"), multiImage)); + dcbEnhance->setCheckBoxListener (this); dcbOptions->pack_start(*dcbIterations); dcbOptions->pack_start(*dcbEnhance); pack_start( *dcbOptions, Gtk::PACK_SHRINK, 4); @@ -94,7 +96,8 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftFrame = Gtk::manage (new Gtk::VBox ()); pixelShiftFrame->set_border_width(0); - pixelShiftEqualBright = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTEQUALBRIGHT"))); + pixelShiftEqualBright = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTEQUALBRIGHT"), multiImage)); + pixelShiftEqualBright->setCheckBoxListener (this); pixelShiftEqualBright->set_tooltip_text (M("TP_RAW_PIXELSHIFTEQUALBRIGHT_TOOLTIP")); pixelShiftFrame->pack_start(*pixelShiftEqualBright); @@ -112,29 +115,36 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftOptions = Gtk::manage (new Gtk::VBox ()); pixelShiftOptions->set_border_width(0); - pixelShiftShowMotion = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTION"))); + pixelShiftShowMotion = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTSHOWMOTION"), multiImage)); + pixelShiftShowMotion->setCheckBoxListener (this); pixelShiftShowMotion->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP")); pixelShiftFrame->pack_start(*pixelShiftShowMotion); - pixelShiftShowMotionMaskOnly = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"))); + pixelShiftShowMotionMaskOnly = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY"), multiImage)); + pixelShiftShowMotionMaskOnly->setCheckBoxListener (this); pixelShiftShowMotionMaskOnly->set_tooltip_text (M("TP_RAW_PIXELSHIFTSHOWMOTIONMASKONLY_TOOLTIP")); pixelShiftFrame->pack_start(*pixelShiftShowMotionMaskOnly); #ifdef PIXELSHIFTDEV - pixelShiftAutomatic = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTADAPTIVE"))); + pixelShiftAutomatic = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTADAPTIVE"), multiImage)); + pixelShiftAutomatic->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftAutomatic); #endif - pixelShiftGreen = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTGREEN"))); + pixelShiftGreen = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTGREEN"), multiImage)); + pixelShiftGreen->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftGreen); - pixelShiftNonGreenCross = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS"))); + pixelShiftNonGreenCross = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENCROSS"), multiImage)); + pixelShiftNonGreenCross->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross); - pixelShiftHoleFill = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTHOLEFILL"))); + pixelShiftHoleFill = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTHOLEFILL"), multiImage)); + pixelShiftHoleFill->setCheckBoxListener (this); pixelShiftHoleFill->set_tooltip_text (M("TP_RAW_PIXELSHIFTHOLEFILL_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftHoleFill); - pixelShiftBlur = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTBLUR"))); + pixelShiftBlur = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTBLUR"), multiImage)); + pixelShiftBlur->setCheckBoxListener (this); pixelShiftBlur->set_tooltip_text (M("TP_RAW_PIXELSHIFTSIGMA_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftBlur); @@ -173,32 +183,40 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA pixelShiftOptions->pack_start(*pixelShiftEperIso); - pixelShiftMedian = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTMEDIAN"))); + pixelShiftMedian = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTMEDIAN"), multiImage)); + pixelShiftMedian->setCheckBoxListener (this); pixelShiftMedian->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftMedian); #ifdef PIXELSHIFTDEV - pixelShiftMedian3 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTMEDIAN3"))); + pixelShiftMedian3 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTMEDIAN3"), multiImage)); + pixelShiftMedian3->setCheckBoxListener (this); pixelShiftMedian3->set_tooltip_text (M("TP_RAW_PIXELSHIFTMEDIAN3_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftMedian3); - pixelShiftNonGreenCross2 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"))); + pixelShiftNonGreenCross2 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENCROSS2"), multiImage)); + pixelShiftNonGreenCross2->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"))); + pixelShiftNonGreenAmaze = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENAMAZE"), multiImage)); + pixelShiftNonGreenAmaze->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftNonGreenAmaze); - pixelShiftNonGreenHorizontal = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"))); + pixelShiftNonGreenHorizontal = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENHORIZONTAL"), multiImage)); + pixelShiftNonGreenHorizontal->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"))); + pixelShiftNonGreenVertical = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTNONGREENVERTICAL"), multiImage)); + pixelShiftNonGreenVertical->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftNonGreenVertical); - pixelShiftExp0 = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTEXP0"))); + pixelShiftExp0 = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTEXP0"), multiImage)); + pixelShiftExp0->setCheckBoxListener (this); pixelShiftOptions->pack_start(*pixelShiftExp0); #endif - pixelShiftLmmse = Gtk::manage (new MyCheckButton(M("TP_RAW_PIXELSHIFTLMMSE"))); + pixelShiftLmmse = Gtk::manage (new CheckBox(M("TP_RAW_PIXELSHIFTLMMSE"), multiImage)); + pixelShiftLmmse->setCheckBoxListener (this); pixelShiftLmmse->set_tooltip_text (M("TP_RAW_PIXELSHIFTLMMSE_TOOLTIP")); pixelShiftOptions->pack_start(*pixelShiftLmmse); @@ -307,26 +325,9 @@ BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RA method->connect(method->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::methodChanged) )); imageNumber->connect(imageNumber->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::imageNumberChanged) )); - dcbEnhance->connect ( dcbEnhance->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::dcbEnhanceChanged), true)); pixelShiftMotionMethod->connect(pixelShiftMotionMethod->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::pixelShiftMotionMethodChanged) )); - pixelShiftShowMotion->connect(pixelShiftShowMotion->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionChanged), true)); - pixelShiftShowMotionMaskOnly->connect(pixelShiftShowMotionMaskOnly->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftShowMotionMaskOnlyChanged), true)); - pixelShiftHoleFill->connect(pixelShiftHoleFill->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftHoleFillChanged), true)); - pixelShiftMedian->connect(pixelShiftMedian->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedianChanged), true)); - pixelShiftGreen->connect(pixelShiftGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftGreenChanged), true)); - pixelShiftBlur->connect(pixelShiftBlur->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftBlurChanged), true)); - pixelShiftLmmse->connect(pixelShiftLmmse->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftLmmseChanged), true)); - pixelShiftEqualBright->connect(pixelShiftEqualBright->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftEqualBrightChanged), true)); - pixelShiftNonGreenCross->connect(pixelShiftNonGreenCross->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCrossChanged), true)); #ifdef PIXELSHIFTDEV pixelShiftMotionCorrection->connect(pixelShiftMotionCorrection->signal_changed().connect( sigc::mem_fun(*this, &BayerProcess::psMotionCorrectionChanged) )); - pixelShiftAutomatic->connect(pixelShiftAutomatic->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftAutomaticChanged), true)); - pixelShiftNonGreenHorizontal->connect(pixelShiftNonGreenHorizontal->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenHorizontalChanged), true)); - pixelShiftNonGreenVertical->connect(pixelShiftNonGreenVertical->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenVerticalChanged), true)); - pixelShiftMedian3->connect(pixelShiftMedian3->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftMedian3Changed), true)); - pixelShiftExp0->connect(pixelShiftExp0->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftExp0Changed), true)); - pixelShiftNonGreenCross2->connect(pixelShiftNonGreenCross2->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenCross2Changed), true)); - pixelShiftNonGreenAmaze->connect(pixelShiftNonGreenAmaze->signal_toggled().connect ( sigc::mem_fun(*this, &BayerProcess::pixelShiftNonGreenAmazeChanged), true)); #endif } @@ -335,9 +336,11 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params { disableListener (); method->block (true); - dcbEnhance->block (true); imageNumber->block (true); //allEnhconn.block (true); +#ifdef PIXELSHIFTDEV + pixelShiftMotionCorrection->block (true); +#endif method->set_active(procparams::RAWParams::BayerSensor::numMethods); imageNumber->set_active(pp->raw.bayersensor.imageNum); @@ -350,20 +353,75 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } } + //allEnhance->set_active(pp->raw.bayersensor.all_enhance); + + dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); + dcbEnhance->setValue (pp->raw.bayersensor.dcb_enhance); + pixelShiftShowMotion->setValue (pp->raw.bayersensor.pixelshiftShowMotion); + if (!batchMode) { + pixelShiftShowMotionMaskOnly->set_sensitive (pp->raw.bayersensor.pixelshiftShowMotion); + } + pixelShiftShowMotionMaskOnly->setValue (pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); + pixelShiftHoleFill->setValue (pp->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->setValue (pp->raw.bayersensor.pixelShiftMedian); + pixelShiftGreen->setValue (pp->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->setValue (pp->raw.bayersensor.pixelShiftBlur); + if (!batchMode) { + pixelShiftSmooth->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); + } + pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); + pixelShiftLmmse->setValue (pp->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->setValue (pp->raw.bayersensor.pixelShiftEqualBright); + pixelShiftNonGreenCross->setValue (pp->raw.bayersensor.pixelShiftNonGreenCross); + ccSteps->setValue (pp->raw.bayersensor.ccSteps); + lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); + pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); + pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); + pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); + if (!batchMode) { + pixelShiftSigma->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); + } +#ifdef PIXELSHIFTDEV + pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); + pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); + pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); + pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); + pixelShiftMedian3->setValue (pp->raw.bayersensor.pixelShiftMedian3); + if (!batchMode) { + pixelShiftMedian3->set_sensitive (pixelShiftMedian->getValue() != CheckValue::off); + } + pixelShiftAutomatic->setValue (pp->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->setValue (pp->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->setValue (pp->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftExp0->setValue (pp->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->setValue (pp->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->setValue (pp->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); + pixelShiftMotionCorrection->setValue ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); + if (!batchMode) { + pixelShiftHoleFill->set_sensitive (pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftBlur->set_sensitive(pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->getValue () != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->getValue() != CheckValue::off); + } + pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); + pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); + pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); +#endif + if(pedited) { ccSteps->setEditedState (pedited->raw.bayersensor.ccSteps ? Edited : UnEdited); dcbIterations->setEditedState ( pedited->raw.bayersensor.dcbIterations ? Edited : UnEdited); - dcbEnhance->set_inconsistent(!pedited->raw.bayersensor.dcbEnhance); - pixelShiftShowMotion->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotion); - pixelShiftShowMotionMaskOnly->set_inconsistent(!pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); - pixelShiftHoleFill->set_inconsistent(!pedited->raw.bayersensor.pixelShiftHoleFill); - pixelShiftMedian->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian); - pixelShiftGreen->set_inconsistent(!pedited->raw.bayersensor.pixelShiftGreen); - pixelShiftBlur->set_inconsistent(!pedited->raw.bayersensor.pixelShiftBlur); + dcbEnhance->setEdited (pedited->raw.bayersensor.dcbEnhance); + pixelShiftShowMotion->setEdited (pedited->raw.bayersensor.pixelshiftShowMotion); + pixelShiftShowMotionMaskOnly->setEdited (pedited->raw.bayersensor.pixelshiftShowMotionMaskOnly); + pixelShiftHoleFill->setEdited (pedited->raw.bayersensor.pixelShiftHoleFill); + pixelShiftMedian->setEdited(pedited->raw.bayersensor.pixelShiftMedian); + pixelShiftGreen->setEdited (pedited->raw.bayersensor.pixelShiftGreen); + pixelShiftBlur->setEdited (pedited->raw.bayersensor.pixelShiftBlur); pixelShiftSmooth->setEditedState ( pedited->raw.bayersensor.pixelShiftSmooth ? Edited : UnEdited); - pixelShiftLmmse->set_inconsistent(!pedited->raw.bayersensor.pixelShiftLmmse); - pixelShiftEqualBright->set_inconsistent(!pedited->raw.bayersensor.pixelShiftEqualBright); - pixelShiftNonGreenCross->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross); + pixelShiftLmmse->setEdited (pedited->raw.bayersensor.pixelShiftLmmse); + pixelShiftEqualBright->setEdited (pedited->raw.bayersensor.pixelShiftEqualBright); + pixelShiftNonGreenCross->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenCross); lmmseIterations->setEditedState ( pedited->raw.bayersensor.lmmseIterations ? Edited : UnEdited); pixelShiftEperIso->setEditedState ( pedited->raw.bayersensor.pixelShiftEperIso ? Edited : UnEdited); pixelShiftSigma->setEditedState ( pedited->raw.bayersensor.pixelShiftSigma ? Edited : UnEdited); @@ -374,13 +432,13 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftStddevFactorRed->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorRed ? Edited : UnEdited); pixelShiftStddevFactorBlue->setEditedState ( pedited->raw.bayersensor.pixelShiftStddevFactorBlue ? Edited : UnEdited); pixelShiftSum->setEditedState ( pedited->raw.bayersensor.pixelShiftSum ? Edited : UnEdited); - pixelShiftAutomatic->set_inconsistent(!pedited->raw.bayersensor.pixelShiftAutomatic); - pixelShiftNonGreenHorizontal->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenVertical); - pixelShiftMedian3->set_inconsistent(!pedited->raw.bayersensor.pixelShiftMedian3); - pixelShiftExp0->set_inconsistent(!pedited->raw.bayersensor.pixelShiftExp0); - pixelShiftNonGreenCross2->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_inconsistent(!pedited->raw.bayersensor.pixelShiftNonGreenAmaze); + pixelShiftAutomatic->setEdited (pedited->raw.bayersensor.pixelShiftAutomatic); + pixelShiftNonGreenHorizontal->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenHorizontal); + pixelShiftNonGreenVertical->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenVertical); + pixelShiftMedian3->setEdited (pedited->raw.bayersensor.pixelShiftMedian3); + pixelShiftExp0->setEdited (pedited->raw.bayersensor.pixelShiftExp0); + pixelShiftNonGreenCross2->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenCross2); + pixelShiftNonGreenAmaze->setEdited (pedited->raw.bayersensor.pixelShiftNonGreenAmaze); pixelShiftMotion->setEditedState ( pedited->raw.bayersensor.pixelShiftMotion ? Edited : UnEdited); pixelShiftRedBlueWeight->setEditedState ( pedited->raw.bayersensor.pixelShiftRedBlueWeight ? Edited : UnEdited); #endif @@ -401,50 +459,6 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params } } - //allEnhance->set_active(pp->raw.bayersensor.all_enhance); - - dcbIterations->setValue (pp->raw.bayersensor.dcb_iterations); - dcbEnhance->set_active(pp->raw.bayersensor.dcb_enhance); - pixelShiftShowMotion->set_active(pp->raw.bayersensor.pixelshiftShowMotion); - pixelShiftShowMotionMaskOnly->set_sensitive(pp->raw.bayersensor.pixelshiftShowMotion); - pixelShiftShowMotionMaskOnly->set_active(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); - pixelShiftHoleFill->set_active(pp->raw.bayersensor.pixelShiftHoleFill); - pixelShiftMedian->set_active(pp->raw.bayersensor.pixelShiftMedian); - pixelShiftGreen->set_active(pp->raw.bayersensor.pixelShiftGreen); - pixelShiftBlur->set_active(pp->raw.bayersensor.pixelShiftBlur); - pixelShiftSmooth->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); - pixelShiftSmooth->setValue (pp->raw.bayersensor.pixelShiftSmoothFactor); - pixelShiftLmmse->set_active(pp->raw.bayersensor.pixelShiftLmmse); - pixelShiftEqualBright->set_active(pp->raw.bayersensor.pixelShiftEqualBright); - pixelShiftNonGreenCross->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross); - ccSteps->setValue (pp->raw.bayersensor.ccSteps); - lmmseIterations->setValue (pp->raw.bayersensor.lmmse_iterations); - pixelShiftMotionMethod->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrectionMethod); - pixelShiftEperIso->setValue (pp->raw.bayersensor.pixelShiftEperIso); - pixelShiftSigma->setValue (pp->raw.bayersensor.pixelShiftSigma); - pixelShiftSigma->set_sensitive (pp->raw.bayersensor.pixelShiftBlur); -#ifdef PIXELSHIFTDEV - pixelShiftStddevFactorGreen->setValue (pp->raw.bayersensor.pixelShiftStddevFactorGreen); - pixelShiftStddevFactorRed->setValue (pp->raw.bayersensor.pixelShiftStddevFactorRed); - pixelShiftStddevFactorBlue->setValue (pp->raw.bayersensor.pixelShiftStddevFactorBlue); - pixelShiftSum->setValue (pp->raw.bayersensor.pixelShiftSum); - pixelShiftMedian3->set_active(pp->raw.bayersensor.pixelShiftMedian3); - pixelShiftMedian3->set_sensitive(pixelShiftMedian->get_active()); - pixelShiftAutomatic->set_active(pp->raw.bayersensor.pixelShiftAutomatic); - pixelShiftNonGreenHorizontal->set_active(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical->set_active(pp->raw.bayersensor.pixelShiftNonGreenVertical); - pixelShiftExp0->set_active(pp->raw.bayersensor.pixelShiftExp0); - pixelShiftNonGreenCross2->set_active(pp->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->set_active(pp->raw.bayersensor.pixelShiftNonGreenAmaze); - pixelShiftMotion->setValue (pp->raw.bayersensor.pixelShiftMotion); - pixelShiftMotionCorrection->set_active ((int)pp->raw.bayersensor.pixelShiftMotionCorrection); - pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); - pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); - pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->get_active()); - pixelShiftNreadIso->setValue (pp->raw.bayersensor.pixelShiftNreadIso); - pixelShiftPrnu->setValue (pp->raw.bayersensor.pixelShiftPrnu); - pixelShiftRedBlueWeight->setValue (pp->raw.bayersensor.pixelShiftRedBlueWeight); -#endif if (!batchMode) { if (pp->raw.bayersensor.method == procparams::RAWParams::BayerSensor::methodstring[procparams::RAWParams::BayerSensor::dcb] || method->get_active_row_number() == procparams::RAWParams::BayerSensor::numMethods) { @@ -479,26 +493,6 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params ccSteps->hide();*/ } - dcbEnhance->setLastActive(pp->raw.bayersensor.dcb_enhance); - pixelShiftShowMotion->setLastActive(pp->raw.bayersensor.pixelshiftShowMotion); - pixelShiftShowMotionMaskOnly->setLastActive(pp->raw.bayersensor.pixelshiftShowMotionMaskOnly); - pixelShiftNonGreenCross->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenCross); - pixelShiftGreen->setLastActive(pp->raw.bayersensor.pixelShiftGreen); - pixelShiftBlur->setLastActive(pp->raw.bayersensor.pixelShiftBlur); - pixelShiftHoleFill->setLastActive(pp->raw.bayersensor.pixelShiftHoleFill); - pixelShiftMedian->setLastActive(pp->raw.bayersensor.pixelShiftMedian); - pixelShiftLmmse->setLastActive(pp->raw.bayersensor.pixelShiftLmmse); - pixelShiftEqualBright->setLastActive(pp->raw.bayersensor.pixelShiftEqualBright); -#ifdef PIXELSHIFTDEV - pixelShiftMedian3->setLastActive(pp->raw.bayersensor.pixelShiftMedian3); - pixelShiftAutomatic->setLastActive(pp->raw.bayersensor.pixelShiftAutomatic); - pixelShiftNonGreenHorizontal->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenHorizontal); - pixelShiftNonGreenVertical->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenVertical); - pixelShiftNonGreenCross2->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenCross2); - pixelShiftNonGreenAmaze->setLastActive(pp->raw.bayersensor.pixelShiftNonGreenAmaze); - pixelShiftExp0->setLastActive(pp->raw.bayersensor.pixelShiftExp0); -#endif - //lastALLen = pp->raw.bayersensor.all_enhance; method->block (false); @@ -506,7 +500,6 @@ void BayerProcess::read(const rtengine::procparams::ProcParams* pp, const Params pixelShiftMotionCorrection->block (false); #endif imageNumber->block (false); - dcbEnhance->block (false); //allEnhconn.block (false); enableListener (); @@ -516,36 +509,36 @@ void BayerProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pe { pp->raw.bayersensor.ccSteps = ccSteps->getIntValue(); pp->raw.bayersensor.dcb_iterations = dcbIterations->getIntValue(); - pp->raw.bayersensor.dcb_enhance = dcbEnhance->get_active(); - //pp->raw.bayersensor.all_enhance = allEnhance->get_active(); + pp->raw.bayersensor.dcb_enhance = dcbEnhance->getLastActive (); + //pp->raw.bayersensor.all_enhance = allEnhance->getLastActive (); pp->raw.bayersensor.lmmse_iterations = lmmseIterations->getIntValue(); pp->raw.bayersensor.pixelShiftMotionCorrectionMethod = (RAWParams::BayerSensor::ePSMotionCorrectionMethod)pixelShiftMotionMethod->get_active_row_number(); pp->raw.bayersensor.pixelShiftEperIso = pixelShiftEperIso->getValue(); pp->raw.bayersensor.pixelShiftSigma = pixelShiftSigma->getValue(); - pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->get_active(); - pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->get_active(); - pp->raw.bayersensor.pixelShiftHoleFill = pixelShiftHoleFill->get_active(); - pp->raw.bayersensor.pixelShiftMedian = pixelShiftMedian->get_active(); - pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->get_active(); - pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->get_active(); + pp->raw.bayersensor.pixelshiftShowMotion = pixelShiftShowMotion->getLastActive (); + pp->raw.bayersensor.pixelshiftShowMotionMaskOnly = pixelShiftShowMotionMaskOnly->getLastActive (); + pp->raw.bayersensor.pixelShiftHoleFill = pixelShiftHoleFill->getLastActive (); + pp->raw.bayersensor.pixelShiftMedian = pixelShiftMedian->getLastActive (); + pp->raw.bayersensor.pixelShiftGreen = pixelShiftGreen->getLastActive (); + pp->raw.bayersensor.pixelShiftBlur = pixelShiftBlur->getLastActive (); pp->raw.bayersensor.pixelShiftSmoothFactor = pixelShiftSmooth->getValue(); - pp->raw.bayersensor.pixelShiftLmmse = pixelShiftLmmse->get_active(); - pp->raw.bayersensor.pixelShiftEqualBright = pixelShiftEqualBright->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->get_active(); + pp->raw.bayersensor.pixelShiftLmmse = pixelShiftLmmse->getLastActive (); + pp->raw.bayersensor.pixelShiftEqualBright = pixelShiftEqualBright->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenCross = pixelShiftNonGreenCross->getLastActive (); #ifdef PIXELSHIFTDEV pp->raw.bayersensor.pixelShiftStddevFactorGreen = pixelShiftStddevFactorGreen->getValue(); pp->raw.bayersensor.pixelShiftStddevFactorRed = pixelShiftStddevFactorRed->getValue(); pp->raw.bayersensor.pixelShiftStddevFactorBlue = pixelShiftStddevFactorBlue->getValue(); pp->raw.bayersensor.pixelShiftSum = pixelShiftSum->getValue(); - pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->get_active(); + pp->raw.bayersensor.pixelShiftMedian3 = pixelShiftMedian3->getLastActive (); pp->raw.bayersensor.pixelShiftMotion = pixelShiftMotion->getIntValue(); pp->raw.bayersensor.pixelShiftMotionCorrection = (RAWParams::BayerSensor::ePSMotionCorrection)pixelShiftMotionCorrection->get_active_row_number(); - pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->get_active(); - pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->get_active(); - pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->get_active(); + pp->raw.bayersensor.pixelShiftAutomatic = pixelShiftAutomatic->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenHorizontal = pixelShiftNonGreenHorizontal->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenVertical = pixelShiftNonGreenVertical->getLastActive (); + pp->raw.bayersensor.pixelShiftExp0 = pixelShiftExp0->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenCross2 = pixelShiftNonGreenCross2->getLastActive (); + pp->raw.bayersensor.pixelShiftNonGreenAmaze = pixelShiftNonGreenAmaze->getLastActive (); pp->raw.bayersensor.pixelShiftNreadIso = pixelShiftNreadIso->getValue(); pp->raw.bayersensor.pixelShiftPrnu = pixelShiftPrnu->getValue(); pp->raw.bayersensor.pixelShiftRedBlueWeight = pixelShiftRedBlueWeight->getValue(); @@ -610,15 +603,12 @@ void BayerProcess::setBatchMode(bool batchMode) method->set_active(procparams::RAWParams::BayerSensor::numMethods); // No name #ifdef PIXELSHIFTDEV pixelShiftMotionCorrection->append (M("GENERAL_UNCHANGED")); - pixelShiftMotionCorrection->set_active (4); + pixelShiftMotionCorrection->set_active_text (M("GENERAL_UNCHANGED")); #endif pixelShiftMotionMethod->append (M("GENERAL_UNCHANGED")); - pixelShiftMotionMethod->set_active (4); + pixelShiftMotionMethod->set_active_text (M("GENERAL_UNCHANGED")); imageNumber->append (M("GENERAL_UNCHANGED")); - imageNumber->set_active(4); - dcbOptions->hide(); - lmmseOptions->hide(); - pixelShiftOptions->hide(); + imageNumber->set_active_text (M("GENERAL_UNCHANGED")); ToolPanel::setBatchMode (batchMode); ccSteps->showEditedCB (); dcbIterations->showEditedCB (); @@ -730,14 +720,16 @@ void BayerProcess::adjusterChanged (Adjuster* a, double newval) #ifdef PIXELSHIFTDEV void BayerProcess::psMotionCorrectionChanged () { - if(pixelShiftMotionCorrection->get_active_row_number() == 5) { - pixelShiftBlur->set_sensitive(true); - pixelShiftHoleFill->set_sensitive(true); - pixelShiftSmooth->set_sensitive(pixelShiftBlur->get_active()); - } else { - pixelShiftBlur->set_sensitive(false); - pixelShiftHoleFill->set_sensitive(false); - pixelShiftSmooth->set_sensitive(false); + if (!batchMode) { + if(pixelShiftMotionCorrection->get_active_row_number() == 5) { + pixelShiftBlur->set_sensitive(true); + pixelShiftHoleFill->set_sensitive(true); + pixelShiftSmooth->set_sensitive(pixelShiftBlur->getValue() != CheckValue::off); + } else { + pixelShiftBlur->set_sensitive(false); + pixelShiftHoleFill->set_sensitive(false); + pixelShiftSmooth->set_sensitive(false); + } } if (listener) { @@ -745,31 +737,34 @@ void BayerProcess::psMotionCorrectionChanged () } } #endif + void BayerProcess::methodChanged () { int curSelection = method->get_active_row_number(); - if ( curSelection == procparams::RAWParams::BayerSensor::dcb) { - dcbOptions->show(); - } else { - dcbOptions->hide(); - } - - if ( curSelection == procparams::RAWParams::BayerSensor::lmmse) { - lmmseOptions->show(); - } else { - lmmseOptions->hide(); - } - - if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { - if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Custom) { - pixelShiftOptions->show(); + if (!batchMode) { + if ( curSelection == procparams::RAWParams::BayerSensor::dcb) { + dcbOptions->show(); } else { - pixelShiftOptions->hide(); + dcbOptions->hide(); + } + + if ( curSelection == procparams::RAWParams::BayerSensor::lmmse) { + lmmseOptions->show(); + } else { + lmmseOptions->hide(); + } + + if ( curSelection == procparams::RAWParams::BayerSensor::pixelshift) { + if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + } else { + pixelShiftOptions->hide(); + } + pixelShiftFrame->show(); + } else { + pixelShiftFrame->hide(); } - pixelShiftFrame->show(); - } else { - pixelShiftFrame->hide(); } Glib::ustring methodName = ""; @@ -797,390 +792,136 @@ void BayerProcess::imageNumberChanged () } } -void BayerProcess::dcbEnhanceChanged () +void BayerProcess::checkBoxToggled (CheckBox* c, CheckValue newval) { - if (batchMode) { - if (dcbEnhance->get_inconsistent()) { - dcbEnhance->set_inconsistent (false); - dcbEnhance->block (true); - dcbEnhance->set_active (false); - dcbEnhance->block (false); - } else if (dcbEnhance->getLastActive()) { - dcbEnhance->set_inconsistent (true); + if (c == dcbEnhance) { + if (listener) { + listener->panelChanged (EvDemosaicDCBEnhanced, dcbEnhance->getValueAsStr ()); + } + } else if (c == pixelShiftShowMotion) { + if (!batchMode) { + pixelShiftShowMotionMaskOnly->set_sensitive(newval != CheckValue::off); + } + if (listener) { + listener->panelChanged (EvPixelshiftShowMotion, pixelShiftShowMotion->getValueAsStr ()); + } + } else if (c == pixelShiftShowMotionMaskOnly) { + if (listener) { + listener->panelChanged (EvPixelshiftShowMotionMaskOnly, pixelShiftShowMotionMaskOnly->getValueAsStr ()); + } + } else if (c == pixelShiftHoleFill) { + if (listener) { + listener->panelChanged (EvPixelShiftHoleFill, pixelShiftHoleFill->getValueAsStr ()); + } + } else if (c == pixelShiftMedian) { +#ifdef PIXELSHIFTDEV + if (!batchMode) { + pixelShiftMedian3->set_sensitive(newval != CheckValue::off); + } +#endif + if (listener) { + listener->panelChanged (EvPixelShiftMedian, pixelShiftMedian->getValueAsStr ()); + } + } else if (c == pixelShiftGreen) { + if (listener) { + listener->panelChanged (EvPixelShiftGreen, pixelShiftGreen->getValueAsStr ()); + } + } else if (c == pixelShiftBlur) { + if (!batchMode) { + pixelShiftSmooth->set_sensitive(newval != CheckValue::off); + pixelShiftSigma->set_sensitive(newval != CheckValue::off); + } + if (listener) { + listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->getValueAsStr ()); + } + } else if (c == pixelShiftLmmse) { + if (listener) { + listener->panelChanged (EvPixelShiftLmmse, pixelShiftLmmse->getValueAsStr ()); + } + } else if (c == pixelShiftEqualBright) { + if (listener) { + listener->panelChanged (EvPixelShiftEqualBright, pixelShiftEqualBright->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenCross) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenCross, pixelShiftNonGreenCross->getValueAsStr ()); + } + } +#ifdef PIXELSHIFTDEV + else if (c == pixelShiftAutomatic) { + if (!batchMode) { + pixelShiftMotion->set_sensitive(!newval != CheckValue::off); + pixelShiftEperIso->set_sensitive(newval != CheckValue::off); + pixelShiftNreadIso->set_sensitive(newval != CheckValue::off); + pixelShiftPrnu->set_sensitive(newval != CheckValue::off); + pixelShiftSigma->set_sensitive(newval != CheckValue::off); + pixelShiftSum->set_sensitive(newval != CheckValue::off); + pixelShiftRedBlueWeight->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorGreen->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorRed->set_sensitive(newval != CheckValue::off); + pixelShiftStddevFactorBlue->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenHorizontal->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenVertical->set_sensitive(newval != CheckValue::off); + pixelShiftHoleFill->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftMedian3->set_sensitive(newval != CheckValue::off && pixelShiftMedian->getValue () != CheckValue::off); + pixelShiftGreen->set_sensitive(newval != CheckValue::off); + pixelShiftBlur->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5); + pixelShiftSmooth->set_sensitive(newval != CheckValue::off && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->getValue () != CheckValue::off); + pixelShiftExp0->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenCross->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenCross2->set_sensitive(newval != CheckValue::off); + pixelShiftNonGreenAmaze->set_sensitive(newval != CheckValue::off); } - dcbEnhance->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvDemosaicDCBEnhanced, dcbEnhance->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + if (listener) { + listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenHorizontal) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenHorizontal, pixelShiftNonGreenHorizontal->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenVertical) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenVertical, pixelShiftNonGreenVertical->getValueAsStr ()); + } + } else if (c == pixelShiftMedian3) { + if (listener) { + listener->panelChanged (EvPixelShiftMedian3, pixelShiftMedian3->getValueAsStr ()); + } + } else if (c == pixelShiftExp0) { + if (listener) { + listener->panelChanged (EvPixelShiftExp0, pixelShiftExp0->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenCross2) { + if (listener) { + listener->panelChanged (EvPixelShiftGreenAmaze, pixelShiftNonGreenCross2->getValueAsStr ()); + } + } else if (c == pixelShiftNonGreenAmaze) { + if (listener) { + listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->getValueAsStr ()); + } } +#endif } void BayerProcess::pixelShiftMotionMethodChanged () { - if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Off) { - pixelShiftOptions->hide(); - pixelShiftShowMotion->hide(); - pixelShiftShowMotionMaskOnly->hide(); - } else if(pixelShiftMotionMethod->get_active_row_number() == RAWParams::BayerSensor::ePSMotionCorrectionMethod::Custom) { - pixelShiftOptions->show(); - pixelShiftShowMotion->show(); - pixelShiftShowMotionMaskOnly->show(); - } else { - pixelShiftOptions->hide(); - pixelShiftShowMotion->show(); - pixelShiftShowMotionMaskOnly->show(); + if (!batchMode) { + if(pixelShiftMotionMethod->get_active_row_number() == 0) { + pixelShiftOptions->hide(); + pixelShiftShowMotion->hide(); + pixelShiftShowMotionMaskOnly->hide(); + } else if(pixelShiftMotionMethod->get_active_row_number() == 2) { + pixelShiftOptions->show(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } else { + pixelShiftOptions->hide(); + pixelShiftShowMotion->show(); + pixelShiftShowMotionMaskOnly->show(); + } } if (listener) { listener->panelChanged (EvPixelShiftMotionMethod, pixelShiftMotionMethod->get_active_text()); } } - -void BayerProcess::pixelShiftShowMotionChanged () -{ - if (batchMode) { - if (pixelShiftShowMotion->get_inconsistent()) { - pixelShiftShowMotion->set_inconsistent (false); - pixelShiftShowMotion->block (true); - pixelShiftShowMotion->set_active (false); - pixelShiftShowMotion->block (false); - } else if (pixelShiftShowMotion->getLastActive()) { - pixelShiftShowMotion->set_inconsistent (true); - } - - pixelShiftShowMotion->setLastActive(); - } - pixelShiftShowMotionMaskOnly->set_sensitive(pixelShiftShowMotion->get_active ()); - if (listener) { - listener->panelChanged (EvPixelshiftShowMotion, pixelShiftShowMotion->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftShowMotionMaskOnlyChanged () -{ - if (batchMode) { - if (pixelShiftShowMotionMaskOnly->get_inconsistent()) { - pixelShiftShowMotionMaskOnly->set_inconsistent (false); - pixelShiftShowMotionMaskOnly->block (true); - pixelShiftShowMotionMaskOnly->set_active (false); - pixelShiftShowMotionMaskOnly->block (false); - } else if (pixelShiftShowMotionMaskOnly->getLastActive()) { - pixelShiftShowMotionMaskOnly->set_inconsistent (true); - } - - pixelShiftShowMotionMaskOnly->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelshiftShowMotionMaskOnly, pixelShiftShowMotionMaskOnly->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -#ifdef PIXELSHIFTDEV -void BayerProcess::pixelShiftAutomaticChanged () -{ - if (batchMode) { - if (pixelShiftAutomatic->get_inconsistent()) { - pixelShiftAutomatic->set_inconsistent (false); - pixelShiftAutomatic->block (true); - pixelShiftAutomatic->set_active (false); - pixelShiftAutomatic->block (false); - } else if (pixelShiftAutomatic->getLastActive()) { - pixelShiftAutomatic->set_inconsistent (true); - } - - pixelShiftAutomatic->setLastActive(); - } - pixelShiftMotion->set_sensitive(!pixelShiftAutomatic->get_active ()); - pixelShiftEperIso->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNreadIso->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftPrnu->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftSigma->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftSum->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftRedBlueWeight->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftStddevFactorGreen->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftStddevFactorRed->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftStddevFactorBlue->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenHorizontal->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenVertical->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftHoleFill->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); - pixelShiftMedian3->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMedian->get_active()); - pixelShiftGreen->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftBlur->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5); - pixelShiftSmooth->set_sensitive(pixelShiftAutomatic->get_active () && pixelShiftMotionCorrection->get_active_row_number() == 5 && pixelShiftBlur->get_active()); - pixelShiftExp0->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenCross->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenCross2->set_sensitive(pixelShiftAutomatic->get_active ()); - pixelShiftNonGreenAmaze->set_sensitive(pixelShiftAutomatic->get_active ()); - - if (listener) { - listener->panelChanged (EvPixelShiftAutomatic, pixelShiftAutomatic->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftNonGreenHorizontalChanged () -{ - if (batchMode) { - if (pixelShiftNonGreenHorizontal->get_inconsistent()) { - pixelShiftNonGreenHorizontal->set_inconsistent (false); - pixelShiftNonGreenHorizontal->block (true); - pixelShiftNonGreenHorizontal->set_active (false); - pixelShiftNonGreenHorizontal->block (false); - } else if (pixelShiftNonGreenHorizontal->getLastActive()) { - pixelShiftNonGreenHorizontal->set_inconsistent (true); - } - - pixelShiftNonGreenHorizontal->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenHorizontal, pixelShiftNonGreenHorizontal->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftNonGreenVerticalChanged () -{ - if (batchMode) { - if (pixelShiftNonGreenVertical->get_inconsistent()) { - pixelShiftNonGreenVertical->set_inconsistent (false); - pixelShiftNonGreenVertical->block (true); - pixelShiftNonGreenVertical->set_active (false); - pixelShiftNonGreenVertical->block (false); - } else if (pixelShiftNonGreenVertical->getLastActive()) { - pixelShiftNonGreenVertical->set_inconsistent (true); - } - - pixelShiftNonGreenVertical->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenVertical, pixelShiftNonGreenVertical->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#endif - -void BayerProcess::pixelShiftHoleFillChanged () -{ - if (batchMode) { - if (pixelShiftHoleFill->get_inconsistent()) { - pixelShiftHoleFill->set_inconsistent (false); - pixelShiftHoleFill->block (true); - pixelShiftHoleFill->set_active (false); - pixelShiftHoleFill->block (false); - } else if (pixelShiftHoleFill->getLastActive()) { - pixelShiftHoleFill->set_inconsistent (true); - } - - pixelShiftHoleFill->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftHoleFill, pixelShiftHoleFill->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftMedianChanged () -{ - if (batchMode) { - if (pixelShiftMedian->get_inconsistent()) { - pixelShiftMedian->set_inconsistent (false); - pixelShiftMedian->block (true); - pixelShiftMedian->set_active (false); - pixelShiftMedian->block (false); - } else if (pixelShiftMedian->getLastActive()) { - pixelShiftMedian->set_inconsistent (true); - } - - pixelShiftMedian->setLastActive(); - } -#ifdef PIXELSHIFTDEV - pixelShiftMedian3->set_sensitive(pixelShiftMedian->get_active ()); -#endif - if (listener) { - listener->panelChanged (EvPixelShiftMedian, pixelShiftMedian->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#ifdef PIXELSHIFTDEV -void BayerProcess::pixelShiftMedian3Changed () -{ - if (batchMode) { - if (pixelShiftMedian3->get_inconsistent()) { - pixelShiftMedian3->set_inconsistent (false); - pixelShiftMedian3->block (true); - pixelShiftMedian3->set_active (false); - pixelShiftMedian3->block (false); - } else if (pixelShiftMedian3->getLastActive()) { - pixelShiftMedian3->set_inconsistent (true); - } - - pixelShiftMedian3->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftMedian3, pixelShiftMedian3->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#endif -void BayerProcess::pixelShiftGreenChanged () -{ - if (batchMode) { - if (pixelShiftGreen->get_inconsistent()) { - pixelShiftGreen->set_inconsistent (false); - pixelShiftGreen->block (true); - pixelShiftGreen->set_active (false); - pixelShiftGreen->block (false); - } else if (pixelShiftGreen->getLastActive()) { - pixelShiftGreen->set_inconsistent (true); - } - - pixelShiftGreen->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftGreen, pixelShiftGreen->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftBlurChanged () -{ - if (batchMode) { - if (pixelShiftBlur->get_inconsistent()) { - pixelShiftBlur->set_inconsistent (false); - pixelShiftBlur->block (true); - pixelShiftBlur->set_active (false); - pixelShiftBlur->block (false); - } else if (pixelShiftBlur->getLastActive()) { - pixelShiftBlur->set_inconsistent (true); - } - - pixelShiftBlur->setLastActive(); - } - - pixelShiftSmooth->set_sensitive(pixelShiftBlur->get_active ()); - pixelShiftSigma->set_sensitive(pixelShiftBlur->get_active ()); - if (listener) { - listener->panelChanged (EvPixelShiftBlur, pixelShiftBlur->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#ifdef PIXELSHIFTDEV -void BayerProcess::pixelShiftExp0Changed () -{ - if (batchMode) { - if (pixelShiftExp0->get_inconsistent()) { - pixelShiftExp0->set_inconsistent (false); - pixelShiftExp0->block (true); - pixelShiftExp0->set_active (false); - pixelShiftExp0->block (false); - } else if (pixelShiftExp0->getLastActive()) { - pixelShiftExp0->set_inconsistent (true); - } - - pixelShiftExp0->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftExp0, pixelShiftExp0->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#endif -void BayerProcess::pixelShiftLmmseChanged () -{ - if (batchMode) { - if (pixelShiftLmmse->get_inconsistent()) { - pixelShiftLmmse->set_inconsistent (false); - pixelShiftLmmse->block (true); - pixelShiftLmmse->set_active (false); - pixelShiftLmmse->block (false); - } else if (pixelShiftLmmse->getLastActive()) { - pixelShiftLmmse->set_inconsistent (true); - } - - pixelShiftLmmse->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftLmmse, pixelShiftLmmse->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftEqualBrightChanged () -{ - if (batchMode) { - if (pixelShiftEqualBright->get_inconsistent()) { - pixelShiftEqualBright->set_inconsistent (false); - pixelShiftEqualBright->block (true); - pixelShiftEqualBright->set_active (false); - pixelShiftEqualBright->block (false); - } else if (pixelShiftEqualBright->getLastActive()) { - pixelShiftEqualBright->set_inconsistent (true); - } - - pixelShiftEqualBright->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftEqualBright, pixelShiftEqualBright->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftNonGreenCrossChanged () -{ - if (batchMode) { - if (pixelShiftNonGreenCross->get_inconsistent()) { - pixelShiftNonGreenCross->set_inconsistent (false); - pixelShiftNonGreenCross->block (true); - pixelShiftNonGreenCross->set_active (false); - pixelShiftNonGreenCross->block (false); - } else if (pixelShiftNonGreenCross->getLastActive()) { - pixelShiftNonGreenCross->set_inconsistent (true); - } - - pixelShiftNonGreenCross->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenCross, pixelShiftNonGreenCross->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#ifdef PIXELSHIFTDEV -void BayerProcess::pixelShiftNonGreenCross2Changed () -{ - if (batchMode) { - if (pixelShiftNonGreenCross2->get_inconsistent()) { - pixelShiftNonGreenCross2->set_inconsistent (false); - pixelShiftNonGreenCross2->block (true); - pixelShiftNonGreenCross2->set_active (false); - pixelShiftNonGreenCross2->block (false); - } else if (pixelShiftNonGreenCross2->getLastActive()) { - pixelShiftNonGreenCross2->set_inconsistent (true); - } - - pixelShiftNonGreenCross2->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftGreenAmaze, pixelShiftNonGreenCross2->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - -void BayerProcess::pixelShiftNonGreenAmazeChanged () -{ - if (batchMode) { - if (pixelShiftNonGreenAmaze->get_inconsistent()) { - pixelShiftNonGreenAmaze->set_inconsistent (false); - pixelShiftNonGreenAmaze->block (true); - pixelShiftNonGreenAmaze->set_active (false); - pixelShiftNonGreenAmaze->block (false); - } else if (pixelShiftNonGreenAmaze->getLastActive()) { - pixelShiftNonGreenAmaze->set_inconsistent (true); - } - - pixelShiftNonGreenAmaze->setLastActive(); - } - - if (listener) { - listener->panelChanged (EvPixelShiftNonGreenAmaze, pixelShiftNonGreenAmaze->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} -#endif \ No newline at end of file diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 463e8fb58..9c2285951 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -21,11 +21,12 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "guiutils.h" #include "toolpanel.h" -class BayerProcess : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class BayerProcess : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: @@ -36,21 +37,21 @@ protected: Adjuster* ccSteps; Gtk::VBox *dcbOptions; Adjuster* dcbIterations; - MyCheckButton* dcbEnhance; + CheckBox* dcbEnhance; Gtk::VBox *lmmseOptions; Adjuster* lmmseIterations; Gtk::VBox *pixelShiftFrame; Gtk::VBox *pixelShiftOptions; MyComboBoxText* pixelShiftMotionMethod; - MyCheckButton* pixelShiftShowMotion; - MyCheckButton* pixelShiftShowMotionMaskOnly; - MyCheckButton* pixelShiftNonGreenCross; - MyCheckButton* pixelShiftGreen; - MyCheckButton* pixelShiftBlur; - MyCheckButton* pixelShiftHoleFill; - MyCheckButton* pixelShiftMedian; - MyCheckButton* pixelShiftLmmse; - MyCheckButton* pixelShiftEqualBright; + CheckBox* pixelShiftShowMotion; + CheckBox* pixelShiftShowMotionMaskOnly; + CheckBox* pixelShiftNonGreenCross; + CheckBox* pixelShiftGreen; + CheckBox* pixelShiftBlur; + CheckBox* pixelShiftHoleFill; + CheckBox* pixelShiftMedian; + CheckBox* pixelShiftLmmse; + CheckBox* pixelShiftEqualBright; Adjuster* pixelShiftSmooth; Adjuster* pixelShiftEperIso; Adjuster* pixelShiftSigma; @@ -58,13 +59,13 @@ protected: Adjuster* pixelShiftSum; Adjuster* pixelShiftMotion; MyComboBoxText* pixelShiftMotionCorrection; - MyCheckButton* pixelShiftAutomatic; - MyCheckButton* pixelShiftNonGreenHorizontal; - MyCheckButton* pixelShiftNonGreenVertical; - MyCheckButton* pixelShiftNonGreenCross2; - MyCheckButton* pixelShiftNonGreenAmaze; - MyCheckButton* pixelShiftExp0; - MyCheckButton* pixelShiftMedian3; + CheckBox* pixelShiftAutomatic; + CheckBox* pixelShiftNonGreenHorizontal; + CheckBox* pixelShiftNonGreenVertical; + CheckBox* pixelShiftNonGreenCross2; + CheckBox* pixelShiftNonGreenAmaze; + CheckBox* pixelShiftExp0; + CheckBox* pixelShiftMedian3; Adjuster* pixelShiftStddevFactorGreen; Adjuster* pixelShiftStddevFactorRed; Adjuster* pixelShiftStddevFactorBlue; @@ -84,27 +85,11 @@ public: void methodChanged (); void imageNumberChanged (); - void adjusterChanged (Adjuster* a, double newval); - void dcbEnhanceChanged(); - void pixelShiftShowMotionChanged(); - void pixelShiftShowMotionMaskOnlyChanged(); - void pixelShiftHoleFillChanged(); - void pixelShiftMedianChanged(); - void pixelShiftMedian3Changed(); - void pixelShiftGreenChanged(); - void pixelShiftBlurChanged(); - void pixelShiftLmmseChanged(); - void pixelShiftEqualBrightChanged(); - void pixelShiftNonGreenCrossChanged(); + void adjusterChanged (Adjuster* a, double newval); + void checkBoxToggled (CheckBox* c, CheckValue newval); void pixelShiftMotionMethodChanged(); #ifdef PIXELSHIFTDEV void psMotionCorrectionChanged (); - void pixelShiftAutomaticChanged(); - void pixelShiftNonGreenHorizontalChanged(); - void pixelShiftNonGreenVerticalChanged(); - void pixelShiftExp0Changed(); - void pixelShiftNonGreenCross2Changed(); - void pixelShiftNonGreenAmazeChanged(); #endif }; diff --git a/rtgui/checkbox.cc b/rtgui/checkbox.cc new file mode 100644 index 000000000..3c8d0d3e0 --- /dev/null +++ b/rtgui/checkbox.cc @@ -0,0 +1,162 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 . + */ + +#include + +#include "multilangmgr.h" +#include "checkbox.h" +#include "guiutils.h" + +CheckBox::CheckBox (Glib::ustring label, bool const& multiImageVal) + : Gtk::CheckButton (label) + , listener (nullptr) + , lastActive (false) + , inBatchMode (false) + , multiImage (multiImageVal) +{ + conn = signal_toggled().connect( sigc::mem_fun(*this, &CheckBox::buttonToggled) ); +} + +void CheckBox::buttonToggled () +{ + + CheckValue newValue; + + if (multiImage) { + if (get_inconsistent()) { + set_inconsistent (false); + ConnectionBlocker bloker (conn); + set_active (false); + newValue = CheckValue::off; + } else if (getLastActive()) { + set_inconsistent (true); + newValue = CheckValue::unchanged; + } + } else { + newValue = get_active () ? CheckValue::on : CheckValue::off; + } + setLastActive(); + + if (listener) { + listener->checkBoxToggled(this, newValue); + } +} + +void CheckBox::setLastActive() +{ + lastActive = get_active(); +} + +// return the actual bool value, ignoring the inconsistent state +bool CheckBox::getLastActive () +{ + return lastActive; +} + +void CheckBox::setValue (CheckValue newValue) +{ + + ConnectionBlocker blocker (conn); + switch (newValue) { + case CheckValue::on: + set_inconsistent (false); + set_active(true); + lastActive = true; + break; + case CheckValue::off: + set_inconsistent (false); + set_active(true); + lastActive = false; + break; + case CheckValue::unchanged: + set_inconsistent (true); + break; + default: + break; + } +} + +void CheckBox::setValue (bool active) +{ + + ConnectionBlocker blocker (conn); + if (active) { + set_inconsistent (false); + set_active(true); + lastActive = true; + } else { + set_inconsistent (false); + set_active(true); + lastActive = false; + } +} + +CheckValue CheckBox::getValue () +{ + return (get_inconsistent() ? CheckValue::unchanged : get_active() ? CheckValue::on : CheckValue::off); +} + +Glib::ustring CheckBox::getValueAsStr () +{ + if (get_inconsistent()) { + return M("GENERAL_UNCHANGED"); + } else if (get_active ()) { + return M("GENERAL_ENABLED"); + } else { + return M("GENERAL_DISABLED"); + } +} + +/* +void CheckBox::set_sensitive (bool isSensitive) +{ + Gtk::CheckButton::set_sensitive(isSensitive); +} + +void CheckBox::set_tooltip_text (const Glib::ustring& tooltip) +{ + Gtk::CheckButton::set_tooltip_text (tooltip); +} + +void CheckBox::set_tooltip_markup (const Glib::ustring& tooltip) +{ + Gtk::CheckButton::set_tooltip_markup (tooltip); +} +*/ + +void CheckBox::setEdited (bool edited) +{ + + ConnectionBlocker blocker (conn); + set_inconsistent (!edited); + if (edited) { + set_active (lastActive); + } +} + +bool CheckBox::getEdited () +{ + + return !get_inconsistent (); +} + +void CheckBox::setCheckBoxListener (CheckBoxListener* cblistener) +{ + listener = cblistener; +} diff --git a/rtgui/checkbox.h b/rtgui/checkbox.h new file mode 100644 index 000000000..86d8e3622 --- /dev/null +++ b/rtgui/checkbox.h @@ -0,0 +1,78 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 . + */ +#ifndef _CHECKBOX_H_ +#define _CHECKBOX_H_ + +#include +#include "editedstate.h" +#include "guiutils.h" + +class CheckBox; + +enum class CheckValue { + on, + off, + unchanged +}; + +class CheckBoxListener +{ + +public: + virtual ~CheckBoxListener() {}; + virtual void checkBoxToggled (CheckBox* c, CheckValue newval) {} +}; + + +/** + * @brief subclass of Gtk::CheckButton for convenience + */ +class CheckBox : public Gtk::CheckButton // Should ideally be private, but in this case build fail on the instantiation +{ + + CheckBoxListener *listener; + bool lastActive; + bool inBatchMode; + bool const& multiImage; + sigc::connection conn; + void buttonToggled (); + void setLastActive(); + +public: + //using CheckButton::CheckButton; + explicit CheckBox (Glib::ustring label, bool const& multiImageVal); + bool getLastActive(); + void setValue (CheckValue newValue); + void setValue (bool active); + CheckValue getValue (); + void setEdited (bool edited); + bool getEdited (); + Glib::ustring getValueAsStr (); + + void setCheckBoxListener (CheckBoxListener* cblistener); + + /* Used if the Gtk::CheckButton parent class can be private + * + void set_sensitive (bool isSensitive = true); + void set_tooltip_text (const Glib::ustring& tooltip); + void set_tooltip_markup (const Glib::ustring& tooltip); + */ +}; + +#endif diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index 5601d28f3..f41e21b93 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -290,24 +290,6 @@ public: MyScrolledWindow(); }; -/** - * @brief subclass of Gtk::CheckButton in order to handle the last active state - */ -class MyCheckButton : public Gtk::CheckButton -{ - - bool lastActive = false; - sigc::connection myConnection; -public: - using CheckButton::CheckButton; - void setLastActive() {lastActive = get_active();}; - void setLastActive(bool active) {lastActive = active;}; - bool getLastActive() {return lastActive;}; - void connect(const sigc::connection &connection) {myConnection = connection;}; - void block(bool blocked) {myConnection.block(blocked);}; -}; - - /** * @brief subclass of Gtk::ComboBox in order to handle the scrollwheel */ @@ -341,8 +323,8 @@ public: MyComboBoxText (bool has_entry = false); void setPreferredWidth (int minimum_width, int natural_width); - void connect(const sigc::connection &connection) {myConnection = connection;}; - void block(bool blocked) {myConnection.block(blocked);}; + void connect(const sigc::connection &connection) { myConnection = connection; } + void block(bool blocked) { myConnection.block(blocked); } }; /** From bb518eeef826f8fbc2eacb48bf39c5bf696a8bdc Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 16 Mar 2017 13:34:11 +0100 Subject: [PATCH 174/181] Used new CheckBox in rawcacorrection; don't zero red and blue when autoca is enabled --- rtengine/CA_correct_RT.cc | 5 +-- rtengine/rawimagesource.cc | 4 +- rtengine/rawimagesource.h | 2 +- rtgui/rawcacorrection.cc | 76 ++++++++++---------------------------- rtgui/rawcacorrection.h | 10 ++--- 5 files changed, 29 insertions(+), 68 deletions(-) diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc index 97450a61a..fc9967619 100644 --- a/rtengine/CA_correct_RT.cc +++ b/rtengine/CA_correct_RT.cc @@ -112,7 +112,7 @@ bool LinEqSolve(int nDim, double* pfMatr, double* pfVect, double* pfSolution) using namespace std; using namespace rtengine; -void RawImageSource::CA_correct_RT(const double cared, const double cablue, const double caautostrength, array2D &rawData) +void RawImageSource::CA_correct_RT(const bool autoCA, const double cared, const double cablue, const double caautostrength, array2D &rawData) { // multithreaded and partly vectorized by Ingo Weyrich constexpr int ts = 128; @@ -134,7 +134,6 @@ void RawImageSource::CA_correct_RT(const double cared, const double cablue, cons plistener->setProgress (progress); } - const bool autoCA = (cared == 0 && cablue == 0); // local variables const int width = W, height = H; //temporary array to store simple interpolation of G @@ -695,7 +694,7 @@ void RawImageSource::CA_correct_RT(const double cared, const double cablue, cons //fitparams[polyord*i+j] gives the coefficients of (vblock^i hblock^j) in a polynomial fit for i,j<=4 } //end of initialization for CA correction pass - //only executed if cared and cablue are zero + //only executed if autoCA is true } // Main algorithm: Tile loop diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 71d1e10a1..38f0a260e 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1980,10 +1980,10 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift]) { for(int i=0; i<4; ++i) { - CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); + CA_correct_RT(raw.ca_autocorrect, raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); } } else { - CA_correct_RT(raw.cared, raw.cablue, 10.0 - raw.caautostrength, rawData); + CA_correct_RT(raw.ca_autocorrect, raw.cared, raw.cablue, 10.0 - raw.caautostrength, rawData); } } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index cac29c138..0efa2b828 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -216,7 +216,7 @@ protected: inline void interpolate_row_rb (float* ar, float* ab, float* pg, float* cg, float* ng, int i); inline void interpolate_row_rb_mul_pp (float* ar, float* ab, float* pg, float* cg, float* ng, int i, float r_mul, float g_mul, float b_mul, int x1, int width, int skip); - void CA_correct_RT (const double cared, const double cablue, const double caautostrength, array2D &rawData); + void CA_correct_RT (const bool autoCA, const double cared, const double cablue, const double caautostrength, array2D &rawData); void ddct8x8s(int isgn, float a[8][8]); void processRawWhitepoint (float expos, float preser, array2D &rawData); // exposure before interpolation diff --git a/rtgui/rawcacorrection.cc b/rtgui/rawcacorrection.cc index d619ab112..01dccb39c 100644 --- a/rtgui/rawcacorrection.cc +++ b/rtgui/rawcacorrection.cc @@ -18,7 +18,6 @@ */ #include "rawcacorrection.h" #include "guiutils.h" -#include #include "rtimage.h" using namespace rtengine; @@ -31,7 +30,8 @@ RAWCACorr::RAWCACorr () : FoldableToolPanel(this, "rawcacorrection", M("TP_CHROM Gtk::Image* icablueL = Gtk::manage (new RTImage ("ajd-ca-blue1.png")); Gtk::Image* icablueR = Gtk::manage (new RTImage ("ajd-ca-blue2.png")); - caAutocorrect = Gtk::manage(new Gtk::CheckButton((M("TP_RAWCACORR_AUTO")))); + caAutocorrect = Gtk::manage (new CheckBox(M("TP_RAWCACORR_AUTO"), multiImage)); + caAutocorrect->setCheckBoxListener (this); caStrength = Gtk::manage(new Adjuster (M("TP_RAWCACORR_CASTR"), 2.0, 8.0, 0.5, 6.0)); caStrength->setAdjusterListener (this); @@ -62,40 +62,35 @@ RAWCACorr::RAWCACorr () : FoldableToolPanel(this, "rawcacorrection", M("TP_CHROM pack_start( *caRed, Gtk::PACK_SHRINK, 4); pack_start( *caBlue, Gtk::PACK_SHRINK, 4); - caacsconn = caAutocorrect->signal_toggled().connect ( sigc::mem_fun(*this, &RAWCACorr::caCorrectionChanged), true); } void RAWCACorr::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { disableListener (); - caacsconn.block (true); if(pedited ) { - caAutocorrect->set_inconsistent(!pedited->raw.caCorrection); + caAutocorrect->setEdited(pedited->raw.caCorrection); caStrength->setEditedState( pedited->raw.caAutoStrength ? Edited : UnEdited ); caRed->setEditedState( pedited->raw.caRed ? Edited : UnEdited ); caBlue->setEditedState( pedited->raw.caBlue ? Edited : UnEdited ); } - lastCA = pp->raw.ca_autocorrect; - caStrength->set_sensitive(pp->raw.ca_autocorrect); // disable Red and Blue sliders when caAutocorrect is enabled caRed->set_sensitive(!pp->raw.ca_autocorrect); caBlue->set_sensitive(!pp->raw.ca_autocorrect); - caAutocorrect->set_active(pp->raw.ca_autocorrect); + caAutocorrect->setValue(pp->raw.ca_autocorrect); caStrength->setValue (pp->raw.caautostrength); caRed->setValue (pp->raw.cared); caBlue->setValue (pp->raw.cablue); - caacsconn.block (false); enableListener (); } void RAWCACorr::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { - pp->raw.ca_autocorrect = caAutocorrect->get_active(); + pp->raw.ca_autocorrect = caAutocorrect->getLastActive(); pp->raw.caautostrength = caStrength->getValue(); pp->raw.cared = caRed->getValue(); pp->raw.cablue = caBlue->getValue(); @@ -125,6 +120,21 @@ void RAWCACorr::adjusterChanged (Adjuster* a, double newval) } } +void RAWCACorr::checkBoxToggled (CheckBox* c, CheckValue newval) +{ + if (c == caAutocorrect) { + if (!batchMode) { + caStrength->set_sensitive(caAutocorrect->get_active ()); + // disable Red and Blue sliders when caAutocorrect is enabled + caRed->set_sensitive(!caAutocorrect->get_active ()); + caBlue->set_sensitive(!caAutocorrect->get_active ()); + } + if (listener) { + listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + } + } +} + void RAWCACorr::setBatchMode(bool batchMode) { ToolPanel::setBatchMode (batchMode); @@ -150,52 +160,6 @@ void RAWCACorr::setDefaults(const rtengine::procparams::ProcParams* defParams, c } } -void RAWCACorr::caCorrectionChanged() -{ - if (batchMode) { - if (caAutocorrect->get_inconsistent()) { - caAutocorrect->set_inconsistent (false); - caacsconn.block (true); - caAutocorrect->set_active (false); - caacsconn.block (false); - } else if (lastCA) { - caAutocorrect->set_inconsistent (true); - } - - lastCA = caAutocorrect->get_active (); - - } - - /*else { - // For non batch mode, we disable the red and blue slider if caAutocorrect is true - if (caAutocorrect->get_active ()) { - caRed->set_sensitive(false); - caBlue->set_sensitive(false); - } - else { - caRed->set_sensitive(true); - caBlue->set_sensitive(true); - } - }*/ - - caStrength->set_sensitive(caAutocorrect->get_active ()); - // disable Red and Blue sliders when caAutocorrect is enabled - caRed->set_sensitive(!caAutocorrect->get_active ()); - caBlue->set_sensitive(!caAutocorrect->get_active ()); - - if (caAutocorrect->get_active ()) { - // set caRed and caBlue to 0 as RawImageSource::CA_correct_RT uses this as - // a condition for auto-CA correction. Alternative would be to change - // RawImageSource::CA_correct_RT and pass it ca_autocorrect value - caRed->setValue(0); - caBlue->setValue(0); - } - - if (listener) { - listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); - } -} - void RAWCACorr::setAdjusterBehavior (bool caadd) { diff --git a/rtgui/rawcacorrection.h b/rtgui/rawcacorrection.h index 4edf9f9f2..a1873b2ad 100644 --- a/rtgui/rawcacorrection.h +++ b/rtgui/rawcacorrection.h @@ -21,19 +21,17 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "toolpanel.h" -#include "../rtengine/rawimage.h" -class RAWCACorr : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class RAWCACorr : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: - Gtk::CheckButton* caAutocorrect; + CheckBox* caAutocorrect; Adjuster* caStrength; Adjuster* caRed; Adjuster* caBlue; - bool lastCA; - sigc::connection caacsconn; public: @@ -47,7 +45,7 @@ public: void trimValues (rtengine::procparams::ProcParams* pp); void adjusterChanged (Adjuster* a, double newval); - void caCorrectionChanged (); + void checkBoxToggled (CheckBox* c, CheckValue newval); }; #endif From df37ab6b4c04c5e3c638d5cd7a21383a5585d2c3 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 16 Mar 2017 15:09:56 +0100 Subject: [PATCH 175/181] used new CheckBox class in bayerrawexposure.* --- rtgui/bayerrawexposure.cc | 52 ++++++++++++--------------------------- rtgui/bayerrawexposure.h | 12 +++------ rtgui/rawcacorrection.cc | 8 +++--- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/rtgui/bayerrawexposure.cc b/rtgui/bayerrawexposure.cc index ee9b4fc4d..2194d1639 100644 --- a/rtgui/bayerrawexposure.cc +++ b/rtgui/bayerrawexposure.cc @@ -18,7 +18,6 @@ */ #include "bayerrawexposure.h" #include "guiutils.h" -#include using namespace rtengine; using namespace rtengine::procparams; @@ -57,9 +56,9 @@ BayerRAWExposure::BayerRAWExposure () : FoldableToolPanel(this, "bayerrawexposur } PexBlack0->show(); - PextwoGreen = Gtk::manage(new Gtk::CheckButton((M("TP_RAWEXPOS_TWOGREEN"))));// two green + PextwoGreen = Gtk::manage(new CheckBox(M("TP_RAWEXPOS_TWOGREEN"), multiImage));// two green PextwoGreen->set_active (true); - greenconn = PextwoGreen->signal_toggled().connect ( sigc::mem_fun(*this, &BayerRAWExposure::GreenChanged)); + PextwoGreen->setCheckBoxListener (this); pack_start( *PexBlack1, Gtk::PACK_SHRINK, 0);//black R pack_start( *PexBlack0, Gtk::PACK_SHRINK, 0);//black G1 @@ -79,16 +78,13 @@ void BayerRAWExposure::read(const rtengine::procparams::ProcParams* pp, const Pa PexBlack3->setEditedState( pedited->raw.bayersensor.exBlack3 ? Edited : UnEdited ); } - greenconn.block (true); - PextwoGreen->set_active (pp->raw.bayersensor.twogreen); - greenconn.block (false); - lastPextwoGreen = pp->raw.bayersensor.twogreen; + PextwoGreen->setValue (pp->raw.bayersensor.twogreen); PexBlack0->setValue (pp->raw.bayersensor.black0);//black PexBlack1->setValue (pp->raw.bayersensor.black1);//black PexBlack2->setValue (pp->raw.bayersensor.black2);//black - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { PexBlack3->setValue (pp->raw.bayersensor.black3); } else { PexBlack3->setValue (PexBlack0->getValue()); @@ -102,9 +98,9 @@ void BayerRAWExposure::write( rtengine::procparams::ProcParams* pp, ParamsEdited pp->raw.bayersensor.black0 = PexBlack0->getValue();// black pp->raw.bayersensor.black1 = PexBlack1->getValue();// black pp->raw.bayersensor.black2 = PexBlack2->getValue();// black - pp->raw.bayersensor.twogreen = PextwoGreen->get_active(); + pp->raw.bayersensor.twogreen = PextwoGreen->getLastActive(); - if(PextwoGreen->get_active()) { + if(PextwoGreen->getLastActive()) { pp->raw.bayersensor.black3 = pp->raw.bayersensor.black0; // active or desactive 2 green together } else { pp->raw.bayersensor.black3 = PexBlack3->getValue(); @@ -126,7 +122,7 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) Glib::ustring value = a->getTextValue(); if (a == PexBlack0) { - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { listener->panelChanged (EvPreProcessExpBlackzero, value ); } else { listener->panelChanged (EvPreProcessExpBlackzero, value ); @@ -137,7 +133,7 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) } else if (a == PexBlack2) { listener->panelChanged (EvPreProcessExpBlacktwo, value ); } else if (a == PexBlack3) { - if(!PextwoGreen->get_active()) { + if(!PextwoGreen->getLastActive()) { listener->panelChanged (EvPreProcessExpBlackthree, value ); } else { listener->panelChanged (EvPreProcessExpBlackthree, value ); @@ -146,33 +142,17 @@ void BayerRAWExposure::adjusterChanged (Adjuster* a, double newval) } } } -void BayerRAWExposure::GreenChanged() + +void BayerRAWExposure::checkBoxToggled (CheckBox* c, CheckValue newval) { - if (batchMode) { - if (PextwoGreen->get_inconsistent()) { - PextwoGreen->set_inconsistent (false); - greenconn.block (true); - PextwoGreen->set_active (false); - greenconn.block (false); - } else if (lastPextwoGreen) { - PextwoGreen->set_inconsistent (true); + if (c == PextwoGreen) { + if (listener) { + listener->panelChanged (EvPreProcessExptwoGreen, PextwoGreen->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + if (PextwoGreen->getLastActive()) { + PexBlack3->setValue (PexBlack0->getValue());//two green together + } } - - lastPextwoGreen = PextwoGreen->get_active (); } - - if (listener) { - if (PextwoGreen->get_active()) { - listener->panelChanged (EvPreProcessExptwoGreen, M("GENERAL_ENABLED")); - PexBlack3->setValue (PexBlack0->getValue());//two green together - } - - else { - listener->panelChanged (EvPreProcessExptwoGreen, M("GENERAL_DISABLED")); - } - - } - } void BayerRAWExposure::setBatchMode(bool batchMode) diff --git a/rtgui/bayerrawexposure.h b/rtgui/bayerrawexposure.h index 733aafc25..5d51babbb 100644 --- a/rtgui/bayerrawexposure.h +++ b/rtgui/bayerrawexposure.h @@ -21,10 +21,10 @@ #include #include "adjuster.h" +#include "checkbox.h" #include "toolpanel.h" -#include "../rtengine/rawimage.h" -class BayerRAWExposure : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel +class BayerRAWExposure : public ToolParamBlock, public AdjusterListener, public CheckBoxListener, public FoldableToolPanel { protected: @@ -32,12 +32,8 @@ protected: Adjuster* PexBlack1; Adjuster* PexBlack2; Adjuster* PexBlack3; - bool lastPextwoGreen; - sigc::connection greenconn; - Gtk::CheckButton* PextwoGreen; + CheckBox* PextwoGreen; -private: -// Gtk::CheckButton* PextwoGreen; public: BayerRAWExposure (); @@ -46,8 +42,8 @@ public: void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr); void setBatchMode (bool batchMode); void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr); - void GreenChanged() ; void adjusterChanged (Adjuster* a, double newval); + void checkBoxToggled (CheckBox* c, CheckValue newval); void setAdjusterBehavior (bool pexblackadd); void trimValues (rtengine::procparams::ProcParams* pp); }; diff --git a/rtgui/rawcacorrection.cc b/rtgui/rawcacorrection.cc index 01dccb39c..683dcc97f 100644 --- a/rtgui/rawcacorrection.cc +++ b/rtgui/rawcacorrection.cc @@ -124,13 +124,13 @@ void RAWCACorr::checkBoxToggled (CheckBox* c, CheckValue newval) { if (c == caAutocorrect) { if (!batchMode) { - caStrength->set_sensitive(caAutocorrect->get_active ()); + caStrength->set_sensitive(caAutocorrect->getLastActive ()); // disable Red and Blue sliders when caAutocorrect is enabled - caRed->set_sensitive(!caAutocorrect->get_active ()); - caBlue->set_sensitive(!caAutocorrect->get_active ()); + caRed->set_sensitive(!caAutocorrect->getLastActive ()); + caBlue->set_sensitive(!caAutocorrect->getLastActive ()); } if (listener) { - listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); + listener->panelChanged (EvPreProcessAutoCA, caAutocorrect->getLastActive() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED")); } } } From fe026d2b0383173d75907cd59637b7c4a06d828a Mon Sep 17 00:00:00 2001 From: Hombre Date: Thu, 16 Mar 2017 21:25:10 +0100 Subject: [PATCH 176/181] Updated copyright notes, no issue --- rtgui/checkbox.cc | 2 +- rtgui/checkbox.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtgui/checkbox.cc b/rtgui/checkbox.cc index 3c8d0d3e0..22a03d816 100644 --- a/rtgui/checkbox.cc +++ b/rtgui/checkbox.cc @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2017 Jean-Christophe FRISCH * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/rtgui/checkbox.h b/rtgui/checkbox.h index 86d8e3622..8745de6d3 100644 --- a/rtgui/checkbox.h +++ b/rtgui/checkbox.h @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2017 Jean-Christophe FRISCH * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 3db29b206006cbf7ea974c616fc9b8f5de7f159d Mon Sep 17 00:00:00 2001 From: heckflosse Date: Thu, 16 Mar 2017 23:54:59 +0100 Subject: [PATCH 177/181] Fixed bug with raw ca-correction when pixelshift image was opened with amaze demosaic and then method was switched to pixelshift --- rtengine/rawimagesource.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 38f0a260e..9e63ce529 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -1978,7 +1978,7 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le plistener->setProgressStr ("CA Auto Correction..."); plistener->setProgress (0.0); } - if(numFrames == 4 && raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift]) { + if(numFrames == 4) { for(int i=0; i<4; ++i) { CA_correct_RT(raw.ca_autocorrect, raw.cared, raw.cablue, 10.0 - raw.caautostrength, *rawDataFrames[i]); } From 6a3baa3335710102dc417ccc9aa57d4951359018 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 17 Mar 2017 01:03:16 +0100 Subject: [PATCH 178/181] Set homedir as default path for queue_save/gimp/ps path if path from options not exists --- rtgui/batchqueuepanel.cc | 2 ++ rtgui/preferences.cc | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 66f5341fa..1810501b5 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -115,6 +115,8 @@ BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) if (Glib::file_test (options.savePathFolder, Glib::FILE_TEST_IS_DIR)) { outdirFolder->set_current_folder (options.savePathFolder); + } else { + outdirFolder->set_current_folder (Glib::get_home_dir()); } outdirFolderButton = 0; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 5b28d94c1..5a142ff29 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -1895,13 +1895,13 @@ void Preferences::fillPreferences () if (Glib::file_test (moptions.gimpDir, Glib::FILE_TEST_IS_DIR)) { gimpDir->set_current_folder (moptions.gimpDir); } else { - gimpDir->set_current_folder (""); + gimpDir->set_current_folder (Glib::get_home_dir()); } if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { psDir->set_current_folder (moptions.psDir); } else { - psDir->set_current_folder (""); + psDir->set_current_folder (Glib::get_home_dir()); } #elif defined __APPLE__ @@ -1909,6 +1909,8 @@ void Preferences::fillPreferences () if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { psDir->set_current_folder (moptions.psDir); + } else { + psDir->set_current_folder (Glib::get_home_dir()); } #endif From 6f1438e09d604f1a668077f5a9f6ada25e53cbd6 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 17 Mar 2017 16:05:54 +0100 Subject: [PATCH 179/181] fixed bug in CheckBox::setValue (bool active) --- rtgui/checkbox.cc | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/rtgui/checkbox.cc b/rtgui/checkbox.cc index 22a03d816..716802f0f 100644 --- a/rtgui/checkbox.cc +++ b/rtgui/checkbox.cc @@ -96,15 +96,9 @@ void CheckBox::setValue (bool active) { ConnectionBlocker blocker (conn); - if (active) { - set_inconsistent (false); - set_active(true); - lastActive = true; - } else { - set_inconsistent (false); - set_active(true); - lastActive = false; - } + set_inconsistent (false); + set_active(active); + lastActive = active; } CheckValue CheckBox::getValue () From 75895515f31298cbda95587b7d48226dc332dcde Mon Sep 17 00:00:00 2001 From: heckflosse Date: Fri, 17 Mar 2017 23:09:55 +0100 Subject: [PATCH 180/181] pixelshift: don't generate demosaiced motion replacement when motion correction is disabled --- rtengine/rawimagesource.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 9e63ce529..ca74921ca 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2159,7 +2159,7 @@ void RawImageSource::demosaic(const RAWParams &raw) amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); // for non pixelshift files use amaze if pixelshift is selected. We need it also for motion correction } } - } else { + } else if(bayerParams.pixelShiftMotionCorrectionMethod != RAWParams::BayerSensor::Off) { if(bayerParams.pixelShiftLmmse) { lmmse_interpolate_omp(W, H, rawData, red, green, blue, raw.bayersensor.lmmse_iterations); } else { From 5349fea8c136a452845fd9b40c0d691bdfbf6c98 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 18 Mar 2017 15:15:20 +0100 Subject: [PATCH 181/181] pixelshift: moved code from rawimagesource.cc to pixelshift.cc and fixed a bug --- rtengine/pixelshift.cc | 148 ++++++++++++++++++++++++++++++++++++- rtengine/rawimagesource.cc | 135 +-------------------------------- 2 files changed, 146 insertions(+), 137 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 41e1c9af2..0ef4f6b6b 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -30,6 +30,7 @@ #include "../rtgui/multilangmgr.h" #include "procparams.h" #include "gauss.h" +#include "median.h" #define BENCHMARK #include "StopWatch.h" @@ -307,11 +308,151 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma using namespace std; using namespace rtengine; -void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParams, unsigned int frame, const std::string &model, float rawWpCorrection) +void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RAWParams::BayerSensor &bayerParamsIn, unsigned int frame, const std::string &model, float rawWpCorrection) { #ifdef PIXELSHIFTDEV BENCHFUN #endif + + if(numFrames != 4) { // fallback for non pixelshift files + amaze_demosaic_RT (0, 0, winw, winh, rawData, red, green, blue); + return; + } + + RAWParams::BayerSensor bayerParams = bayerParamsIn; + +#ifndef PIXELSHIFTDEV + bayerParams.pixelShiftAutomatic = true; +#endif + + if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Automatic) { + bool pixelShiftEqualBright = bayerParams.pixelShiftEqualBright; + bayerParams.setPixelShiftDefaults(); + bayerParams.pixelShiftEqualBright = pixelShiftEqualBright; + } else if(bayerParams.pixelShiftMotionCorrectionMethod == RAWParams::BayerSensor::Off) { + bayerParams.pixelShiftMotion = 0; + bayerParams.pixelShiftAutomatic = false; + bayerParams.pixelshiftShowMotion = false; + } + + if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic)) { + if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction +#ifdef PIXELSHIFTDEV + if(!bayerParams.pixelShiftMedian3) { +#endif + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[0]), red, green, blue); + } + multi_array2D redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0;i<3;i++) { + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); + } + } + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;i redTmp(W,H); + multi_array2D greenTmp(W,H); + multi_array2D blueTmp(W,H); + for(int i=0, frameIndex = 0;i<4;++i) { + if(i != currFrame) { + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex], raw.bayersensor.lmmse_iterations); + } else { + amaze_demosaic_RT (0, 0, winw, winh, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); + } + ++frameIndex; + } + } + unsigned int offsX0 = 0, offsY0 = 0; + unsigned int offsX1 = 0, offsY1 = 0; + unsigned int offsX2 = 0, offsY2 = 0; + + // We have to adjust the offsets for the selected subframe we exclude from median + switch (currFrame) { + case 0: + offsY0 = 1; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 1: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 1; + offsY2 = 0; + offsX2 = 1; + break; + + case 2: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 0; + offsX2 = 1; + break; + + case 3: + offsY0 = 0; + offsX0 = 0; + offsY1 = 1; + offsX1 = 0; + offsY2 = 1; + offsX2 = 1; + } + + #pragma omp parallel for schedule(dynamic,16) + for(int i=border;i 0.0)) { - if((bayerParams.pixelShiftMotion > 0 || bayerParams.pixelShiftAutomatic) && numFrames == 4) { - if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction -#ifdef PIXELSHIFTDEV - if(!bayerParams.pixelShiftMedian3) { -#endif - if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(W, H, *(rawDataFrames[0]), red, green, blue, raw.bayersensor.lmmse_iterations); - } else { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[0]), red, green, blue); - } - multi_array2D redTmp(W,H); - multi_array2D greenTmp(W,H); - multi_array2D blueTmp(W,H); - for(int i=0;i<3;i++) { - if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i], raw.bayersensor.lmmse_iterations); - } else { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i+1]), redTmp[i], greenTmp[i], blueTmp[i]); - } - } - #pragma omp parallel for schedule(dynamic,16) - for(int i=border;i redTmp(W,H); - multi_array2D greenTmp(W,H); - multi_array2D blueTmp(W,H); - for(int i=0, frameIndex = 0;i<4;++i) { - if(i != currFrame) { - if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex], raw.bayersensor.lmmse_iterations); - } else { - amaze_demosaic_RT (0, 0, W, H, *(rawDataFrames[i]), redTmp[frameIndex], greenTmp[frameIndex], blueTmp[frameIndex]); - } - ++frameIndex; - } - } - unsigned int offsX0 = 0, offsY0 = 0; - unsigned int offsX1 = 0, offsY1 = 0; - unsigned int offsX2 = 0, offsY2 = 0; - - // We have to adjust the offsets for the selected subframe we exclude from median - switch (currFrame) { - case 0: - offsY0 = 1; - offsX0 = 0; - offsY1 = 1; - offsX1 = 1; - offsY2 = 0; - offsX2 = 1; - break; - - case 1: - offsY0 = 0; - offsX0 = 0; - offsY1 = 1; - offsX1 = 1; - offsY2 = 0; - offsX2 = 1; - break; - - case 2: - offsY0 = 0; - offsX0 = 0; - offsY1 = 1; - offsX1 = 0; - offsY2 = 0; - offsX2 = 1; - break; - - case 3: - offsY0 = 0; - offsX0 = 0; - offsY1 = 1; - offsX1 = 0; - offsY2 = 1; - offsX2 = 1; - } - - #pragma omp parallel for schedule(dynamic,16) - for(int i=border;iget_model(), raw.expos); - } + pixelshift(0, 0, W, H, raw.bayersensor, currFrame, ri->get_model(), raw.expos); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::dcb] ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::eahd]) {

Fsz{+ZdlQ{1Xn$p35e#3nD%IJNIR;W}TtQkQl<7!dA+j zo(4^uGrs5P;cRuCj7G)6!{}EcOAd1M!K5^_Q#Sj_Ac!K+r#kyMkJZN>KI$v7GrJsS zXBk1`_YT!L__8DanfCO*ehUzS@Ytm z$Lw6}2jeoqPoD9ZU2X!8;VJ*#H$Fap%s4EMqj_!6=Qnu9R5XdDd~4w8(25MsMpFRl zJ>9v3$R{<5oJL_!-a3bNZNl8y9s9|wAS!^~KX?(m!oWLthyHB~?B}JihbVka7`4EC zXY;m7$_-W0R;>Sx9Us%d{7@Pm1%D6opVaCaa;XxKkL=o<^=$@^z7(7Vtxed{5y5mi z4SUqLZtSH91tn$T@9Nl}^}?QPgR}TxJh7HexU(EWE_2jawlEiY$KZLtm_Lhs-bqgV z8v;qMVj0^d2i{aTb8Qm+*wB+QT2LXO8eYau&y-Vn4DvzFD%cX#E!I-3?Sql5fW3XJ zzMK}+r?UJe=<|%B;a$Fs9UTVUWH;!em+WM(9+Oa)zR>4a?qM(8#b=K93^{O+?bICq zR$fL^rXONkv_x)C*uQZ$czlJir#Nabz#4YG#4LIrLvuR8SL*&xrsz;Kt?mw+3%*_rsg zItuIq!Hd`)5y(v0Eu$}Um$N?$B=nAhw)*LM*6I|plnu{K<3xw=m5T@0r}5 z4!A+HV1xO1WIwtBPO?kyK-yjCMgarDDWo5=S;Lo*yF(c59{|s#ASv}9siggbfLmEK z^;i%}E3uc&g$~CcG=v`D^}(R2O*rVlC>;P}8;7{br!tVi}^;Z{Zjfpwk3OD z3pj2!q_lNhYj!{kctdZb4T5ZP}1^u}8uP$s-QUHxW|NQb+YxeaQf0~`~@BO*Z z{~)uqK9<~dQ71HwVqTt&q2*2BpXQpzOp3*1a|*oY>MA>WYpRbjDrU2tJDN-Yn{l?8vnFgY^Rp-2q)XhRpCcf{j@mVD0(y$gXYTpOmg9W>&4)xu*DabzV zlR*;}LMM&=r_>N$HkgTZvO$m6dlmJ^eX-Aa%=AC6!agXY`$-aNAuf*py=VL5mb2N-{^iacG&SJ)(H_)Lm{6c3GFfCr7*8yyAj6TZfLHAIpk3GTr zBr_iKzQ!$(pR%-yIfHZ1zhxk~G`hxoKZH9mzF!4ZcbKi;arU*vIWp}5qrRJbYKQFI z&X1UD*>cn&czxXi<`}Z(w_)EqU37$m)A9`N~Rz2ToNXUwY-{@|Qm|3SPIoR;=@ z&-T_a;?;*^X(*n14D2RW%!r{PJnz4sDH_)T+(KY0bz0(JelwCh@N-`A3sX758!Zml zcGzvqDZn#n4Qx$Z7IHgKOJ4-Go|iMZ^I1x@*D^d4tGH2zLa7{|!xL6tK+> zH0OJMMZXu=4m5Y>d!awy40t|1wwRxT{?8p#^}RKe_eRf6)iscYHqYT5^nxhPE|AVn zEaSi9ZWn=`*uCTT`DMpZBTvA4Y;}e9-DPyh3w7f@JHhuDG+`UznbU8DuqOcZwhXoC zOQrA${Hcl%T~BnicNYP?;zg<5#MGLS<3cM3g#GlR2WXk@N%(*DhtyPrJ?T$a{>wn|yp^`9%R;ANL^zY)AKNB}IO{JxHzR-H5xUwF+WjwFC zxl{DuO9Ca}SvqW{YGCJh>I%$TZW_kL!Ox))Ft_QrmpgL-Gelkd%&)b0Lf`2)Fn_wM z4?pEa1f>G=2d49RG5UL@I13COhV%Em!l)bekQ|Ra{7JEr-e7MjZ&byf!3@+K-~INZ zKJcGyk!x&%pFh|@a75oDs0-@tCf$T;^o#6p7L3#r3%ZFxbZ0m+H>=!*=6B`v33scW zUpELdEP-J-`i-e!f*tOHi*P>1-pdv2F3RY^bNqYV&IsGF|8%uPt)B8wNWeb7MkJwm zreB0+UNX`fiDyoyY>lUsmQRq-rglxrvL_=03cZ5Vj!ny)u@AMv*}7|$UfCz$Y_k%x zxwQ>K8P@Z8^lJXOo|pCB!U!|A<2BR(`r1s{8SvcU1s!zsfLMPz`1}pH zujpUzuXYrBfT!ewXYI&qBIoKvvc&WI^^s2L58^2v&wVx*sdhGxqde@fb9#Gn^6@dW z2!H0AwLF)$N0UvT(xWB+*({QL@p6SLFLz!4_SoGEHi8sKak$I|iH+r>)yY)xhW7(=B4IM;38kkvBTQPF(p79tH5hPRytm zIo?TypC~fupJa>f-%N%-12Ti&n23U2z-#tR0Exv0r5_CwC?5G8iwDS5JCJ>>g{(Jw zWfLwwJ_h-!c%9~O&)|2_63@2lc5-#|B5Cvsc(0Fs!Fhq#+!%%a2_*KZKpe9sGu&2px^Mg;o=ioN7=|94=6m{TSH}oT( z*$ST$;IUSYz7vfTlA2=;PeM(6(pN~qouwamQ!X+S! zD-*us-0V(r5-q3_-rq+*4E%YAOIL*WP3Y5ah8|>HwV=Nn8OP`^z5U4xlWzD^&r`Vb z|M{ifKTva;*)Topa%jRYFpRv(6@zb6@i+i8svq2fV)R`fAU7f0 zfG=qs`^UrIWqc?8RA4k+z8OGfZ@csDen*l6o~?%u;tgXXh{f-9vv%fpI6#kvHDC39 z48Qg}eCI3zY52pLJO_SmF21*uKCR?a!T)NNhkYhf!GD^K+{|vMA4aC~0rmT25%mFo4&UJj{|x%F1vo>`&Jy^G zF33zrEj|C(X?|WKC5^z{Giy>6pNjclW8BSh$6w*6qc@`3sP30u$p^rY{ z;tmqrH1AWH#MCO6-eDhBnO;$a6mKIN>=h5k9O4#BvuG98@W8JI{KthEbOL+AqESD|9A=qwkvf`^G)N? z4+OUF{)esi0E#N>x`tK6gkp{u5HO-*Mx^_0L=;RIKv5J0F<=%GAR^G@Op|GHRLm$S zq3=0n#hewvoU@oP`>(crpMU0arfRBgb@ezii^IPA?7hN;>uhKPWGce9|HY!G%q|pO z0hdv`d}>)G^bVT%clB6XkI%XuPRrV&9}aKCA7W;*4)>81Q*G`H?cniD+&8zi=Cv9j z^aed}th@s^xPbibThM)+>%?`@hj#XsIK=3?Z|?8l5^-F5Z_ zx(&Y`n77*XVfHTr>Hbsf7)}^T1HG{em>fbKZ`2dkpZBL-Z7|!_=^)I(9=KjqC}o;x z3XY#p`yarcVw1o0J8H!!>^3Y9-ZL?C+f3c<|GiJy^s$4~qhBt~0M3?OJVpD9S!7U) zY=Yey>_JurHNjnA`6x4%8-~5&D{l-IJlLJE6#9xBgp|vPEDRodACNb(q~Bhqyq-u8 zZh6y_{L@U<33u-s-gIQpLzcKI4mm!^C@lEKzJjCM^c?oVZyNCi6TrI%&hFz|^Y|;! z6ywZqEbGiiyG7Eddg#>`_uxzPBPiDpp1*th^RP3}%DbY6JUxtmb`K@zySM|smU3Hg z&&Oh38Kz;uo!SIb@htFgdW_|{L*V~B1zfT;bAH_i`=|x}1flqNXoVM#`OMhu2FZXBo?>zeZ=9}pR_8BKCi6vKaL2?wZhMVAZ>m5J?=ak*^%wH5 zj?hwk!@MqU84p_q&&KC6ntFRV_r)ALyWs~2-4jUd|8BY@sCnaw|8&-=m*e}B{C z?Xa&2!w&w=GH=%9a1gY-p(O1VByA_fJ{JVaC65LL^ILbMfEP?Y$ouA_A zI@x57zT%6;1Xgw}gI3^u_iA`3i>XK>hTdSo7rfu$L*F0oske16vM#mBBqDD@KJ`6& zYLrCIc$XP0Y{EZ!Bv40W)7(z&#J^reW)aSCjS=!;&7ore&f~t0;N6eOsSUn=_S#t9 z7CsxgIKvx8+wvu!B56%O+*yt~@m0PNl)nyl^TJhp2e`7Mp5uO6=Ea|ZFIO+!mvm}Tm}vU-dQ($(UHd@2iYHIxcQa7eBn3fs!$)gd^P10F@seA^T>%4`O(jE3df)Mt==4793D-< zn9ZJiu#C^p23G=SxPkK~UI4A}iUQol?ga9ujl*dkcIe|hB6-mr)aN!bvZ#sW1sa&U z;`jN;ssvsK`Is}&Kb*Bq;PdtdQ=Vf0wbzT~zmPfjYAtek+lTR=xM4v^CM|RiB&QpaE znVA25j-dd{hdeO`06I~o9bMf|2xk+rrZ-}#pIGTaP~?Y%I4~4lQZ6Z z55j!etuGn02Yp4Cj|Hq-P1^5_q{jJ|*w3HP(W940>G_Ra>#d**IFmZ5E&0&JN%RSI z_}BOzeE9wZnhrb{x{u(M?c?c3g*VZ+@%#|7ZJ*+tk2pGqPtc8_QuLD*`&RPOlxRBN z8(f7QK3u68Mf*{Qv$luwB>1eVIIG7@CPN%BhRKmoO1Mog_0Bo>4)vzTr94n^y&625XKi z5ARUE_j)8*p_f>|;(1f>;3}{m?B$ce?@>7Ny`hV_m(S~kfa}}EkB%OJU+%k5>MFsW zdfhJWY!XV7!5gz&wu^f%2_a*9_;_e;=gr`;e>)NU=|FB_z?GXu2Gg^j zVPsOJWQ%?9eRz!K*559@dM=o{+>WJJmpz1C(2vf$98WK{y9*)s_w;@OztmlAg#nL! zDJU!veNjv4IxXmOrAfc@=*Pd@NH3iJo!4epcPMLeAeZccv-bc;Hr*tfoNDl1Yn8xK znr2cYYO?=V&TQ(Z(_P#(PT9U-wyjgi9=*ZqNG;x&6x0G|*lI<0Ug!O1kQQux%N7Ii4zz%R6oLCy?jr?&(7edtz_9kH)5kL8}= zs~^LiF03M*Kb{Fs9tC#U)3oAh| z+|-soMh-f*Gm3t0!1HGX?>RD(rgs3B zW_&)MHYtK8zXoS1U^gFK98S`gn8O<$;5E>&xV^&uqOQp6Pk?vvHE=EV9ON_Mg}V%W z+l!(@{1ozicEKYh(t-0P;1D^1OZr5kgf}+CS;e{6n;Xep?|`d=UZPdAA8hZ>5Q=*j zL)TmMicyJRQ`XGT^4kFikL1%sg^Npnkyy@Po0et!F zSZdF`Y3On@o|`YHTfjNU!G<5W8bx(+o&(Ys^Wn7-l(-zTyOx`HqvhcgsEPehn;`yk zA@U}w!4sGq$)|uzHtvCpHoc7HZGlraa4;r5NaWrjA>{K5{vJ1yx&I|_Q(Ivlvoe{# z$N6RGiQBYJ;3GZYEpY{$-JnQ54ZX2_7P#*;lj~sy8_^m4O`S4U0e!)#h*+xkR8x7* zBZM~0Orpizilim8gQ$KTXjTtrNC%_#3zy-i-=n`&=>dM({&bqQbDE@O5I9^@v;K#z zg$MLR|LE)59d~3?4RYxYaIWoR$s)tD$Phhq{QAXg#6V=-;64)8Jd8E^m`W?qzfI_y z$3m_uX!BL%;;lN!Y_2BJF4T%U`qk|8OL!3+^`X0`z zYTceYmC4BmILi!s@Z&X6v<5TJ%63D!n+`a8^RXA0Hkx0CXU@yF*gYMY!tbpGzxpKf zM-H?3y|ba@dKo_3b)C8Ulu)Yp0UhX<#XJyxRFz#Zk4RX;M_|5`I5Lp*S1sn#!9_0s z&;Me9Bkv9U#W(B~wglVodmX?Dc^yidtGjZOe#rRnz}&DPkhQu4KY3)YeyqM+ZV2ze zHOTZjsoWr);TlBQ`S2WRZzt7z;ZG~~rICKpeaTwva_^ug+Z0w`@)9$`TCE)Vmb_1* z>M@MKh5jebsXJU)uU@&-5cRpk-J$G#cow+>XV)lm7BeV=ZUE53k=n&q0!D;bq4d!*R=qsj%6tG12)LlG|d%B;C?cwc_v5_U930c5Ld$eV-XS#1|cTQYicg%5H{cR;he zSC5ab3ZmJB45xk_c)d~JmZM+Ubn8Al46mgorIGYvjljBo384k!;I}@buzYzga)^c} z(}ov4qR!TNqgPWwrmbHh;93HpqAhw)CFnaGrD!4JVXe>yjp>7SERJ-kl` zo-1Tl&7)}*_KAC{IeTgj9{XhMZD*fj1LMP~+X>8~dsMM`;QDS(h8E`gLzaxXu^)cs zZw9?!-DZW-QtY;0IJ{=v?}pG4c;c1cc*TZbmSedLb2^J>tZOnjdq;!t`+0|5`+z*A zIUzLA?gHy%5C3z_xUH5cSgCU`t+^LTdKGop$3~dDdcYGzH>&*8ju0BW3^~!iu1Z`3 zgQ$2WeC0-zNM!I46H?M?Z)1t%ApTndI%JVu;Z)Op;HPv>$)&{)O8yh)>vg=?i~hM} zr@}e3BXf()qHn-?#^VOe0XQcB=VqfCu|YV`6LG)xTGWz#y{^E%8M%->yRy!}x%7fJ z6&Chmk3S^PE%a|M>Py%*;QR(>xU2gF)?r;N-2%>Q?%K1W^K!a@Ixl;?l=-wmJ}>&A zPi3A=HhZ+P^D_3|OVD)uu&tkmbEQ zonCs*H0^-%{Ky~+{Ou_ty?20bD9fb}<4^qOdH&KhoOuk-#mokCq`^&Dw*L%{DH z}B+>cB8l&oN5j5nWs3oi$BkXP>pruY za`vKF`hs_&S8#CoM^R2gP|sUzd{~tv{9K`+zQB3s{t?Q%6-o30xZPOqGjbgIXG6RP?GuMd!rLPI7rk81=b4g% zdAMt%$J@Q)mgFcrbza~*^oDjXle_5b9bxSvp}#5dDI_@oq^6>D=k4E#Ra0{n#LR`w4vu zbeemv1)3CuhmhXqB(m|CTkhkGJhFW$^yZ#yQH)9>sC2+$2o7LA(2k4j-syUJ^UNblHRHV&kXQ1X4PFMon#7~(?TD* z8x|mK1b?p-Xl%xXMoJ5<;mOz%I{lS#(yOoGc?tiE?_r73n8Xk=KkiRc4kb#XdZ0(9 zKzfrAC-n#nCV5s6nSF|o=0cY-3;TfHE&QY&W4lVk!t8X%xD=Y_zPj8CyA^+MnwAdHDo?~cWm~5VYJJJC zY*Zln4fiY(Y}=HnaCU2-_b;5Ao!rOvTjzp%jJt`wK6?7CZn1JH?U;JzcdI6xW*&)am&lxln|rH;DTchAJx zmd%llds5-ywjgrz1}RXhZ$Wu zbby;%Y?SJ|1=Go(AhKz^N?LOcJw|i*o)^!NHZuyMchN}|kkxAGm*AnJ8aK|`YK%fFU`D=J5>$-DaHcNfdZ7h3ObEhEb& zWB2{QG4p?U^d~3e{0rx{TMn{_8Mzb=oOiz*%Cr-+khkMaD~xW7Yx-qS3~+XN5idpp z=b6CSX4zD60&rH{Q$`xL5+5H*qVKp5wq9{eIj=H-2BMd9c3H1{+c=&SIKzu~)|Trb zLwHT0H*LOMT2=sFf%iWA9G7e`tpa!bAZo~oh7Bc$&qPpb4RCVKSxU+^!zp6|c5kP> zC8sZjQZP6zH+u69Ne;BL2lt24laNi4chJCo(F31*plJGwhfvna zSUPcTNLc~;mQ&S%JEUyn z66|D$<9+rgkAC##|NjoII^QW*&anaWa)|-wk2@wX*OV*@0nX2MHDta<8B_?Ids?0o ze;_y33^?a6DG(>4Zdd~6!|P+j$RkNqgZ)UUy`Q)b=h*=@`Mt&(@phAVI*5clmVbsSAc>!({ z#Fq?y5x5&RU1%weLf&GtDqngNK1vKZjNftaE&^jmim#zTuJQ7R7t&y{cWp2&d>cRo zvAx78Gf{it9o_d#C-E8Xh3^js(}>nuVpU`K!ouI*VM&E@gBE@k4X}N}<5rgS#$EOa zX2<`RN55$AzjJuUIo@VzF1b_#ob#_PW+qu#bQCx*mG)*Ehi1T660@W7X6y?5goXj< z31{oFmZueT8^7;%C11qJCzGfaz1)IXwW8Aw+B6pRulFnK7yvAR=Wpkv%qWc&=>~ZZ*EJLy9aZK z3&>4=--=ym8%p};pmCbeiY1k!cL3MWtZfT6eja?y7lY$fzbQM0`DY-!Dcy@Tnf`dp z;&2w*e1=9lHHfkjgK4zlzG#G<)mPN|RO4LH<`cMlnAa7DHWFjyA@rm)hC0hbvbQQ34WaLr0n|CdKU z_4&W`uqJ_T*r5fv^aVH%d+5)4q-9YFaNag#Ds#kn4g=0!01wAh^6(Jm`B}mM&6&C z<|pD^P_>fnbB#j%1Ljd1nFVwK2l4ZAjF7QLrI;sUp8I-EAPWHR@#Zmb)F%hCCEB6z z+Qh75VK6I(c0S;fj7shYvXxUYpNE!Yinky0s|+Tyec+Xfp3DaO!B#)92i&)eB?Jah z%EDm!_;@-Sc?Y=RPIAAl4r_$D$4l!7TI}F0hOGk^V+wLFQn#1q)d?ZltORm8+f)*D z1K*#UM0yXOnOe_=RwyW$Dq0RTJ&1R*)IR-y6|ZJ@ zGn42lzSDWPH}izIQZ{&TJ3a=n4G-a;ih8?FH=6b936D7Z9A8gMVonZn+Ws6kpU-4b z;I12ErqnmCfbD@N>%&jD6RZ)(BB#q+S2xgSpkr=FJoY=yl{7WVohT_k5N2hodQ_%*j3 zE4kGtfOhL=l55g-)0W$1)Xq7JZpQ61X&MhLNLKd$^5{4GKkq@kM>XuDeGZ+)yT0rG z7-l^qi*D&azwp+Zz17d4L-_w~%)HsRU#aAS`p2&(iyM(a11?;@XPgb>a+*fa=!TwVX7IeVGyzxE z?mDY{6+uO&m~WUoV9wYhg-*x6cj7CiA%m{H(1)ZhA6QG=(ROZzAH|1SW{g@f;x0IZ zeLk~%OZdE|`qPhgAK9aRA=Ec5kW6~LW>1jo`rrt>9vtqoICy6+yB}%%dms8BO=h(|S--he z8&-y~kMlDq7g*UoiegR1rO^(19}2ZhVQtM)==3t2?VSZ|KRnsE2RLtwO4%)!M4IFe zZm@R+Q^duSGz=VFyGmw%IhN)`!rw#olwIf=Lro$ub8)L>1dke70JMju8hj}gzzRX*6Gk zT0d?#s~4X_qf>D&u6vj*%|o8%Ug*nyU1jOVkVkVAKNsug?8f_eI(ZS^-IhOD8gd?N zZsO-s)QB5yilKOL;dZ@g$y<~}Q@`ub>~z!NuP#Dkdm7xBu?AfG4!jmI7cCstpL^n- z@&w=SGIuCHihf{>CGrsanD7jo)o@efjF_A93hbU5gAddA)kvNRPwZaUy|#a4%zNQy zVFIkbZyCgIV&AuBG4dzg81UOe&|||}6S{Bq6h1Ba$Px(d)QdU3gQs`HE!0&yk)Lo< zUS>!ki{P6FY%=B>vW@A3|*?t$h@Hh>cG@RR&hne(E3O&WoFJf6Gdy73})l2-0 zw%4#<{gW_D@ukn5HTe~X1RAb`Ioq(7{995S$@}=yqpHsQI6Tbf41#ai$6ovae1X!8 zpu2yHj6!%-PS!4eITweAY?iO9=4&4xPX=+Tz{} zudp4R=WuadFzHV7qYnG$@b@{$jxT_=dX7E!M6G_47fd6a%(-P_sKyK!9-Z(^awdA-pd{^Wh|@6YXz6!9jf)HqLi zzK=J7c3k!S!%4e&`~fx2bIsEJ6X!Yi-?HbmIpjAS+;rz{%t0@kmaX)mf#KVj`=Cs! zhj-BER!ZhAq*M1Rs4aAbwL~Vu@Vdwsvwy>!)+=Z@Fl={Mlj~%F3k{8SYih%7FDFn_ z7hhVwR*&~Xk2k;zx{hH3c{kh*?Lwg)Hka}aIdVE5=}QZ3$MK)Y@?H>zcU(7HZtNOK zZKL2XF~@z&DkYjn;n7A7&2_O7TEDV&sE-yG4Ssn!haf}R)8CEaJD7i z^CJ{hb)#tf>!~48=qq;tO$j|U2bN61zKP#bTIJ;@65)9Lh2;5_Of8+{~| zzJuH5rl`x8zf;f{;Hfy$lGk*He>{Y326}u4AKtP- zIOQWNXHl^qKUEV7Z+aOm*cHGp4+y1r%#>pL2J*^_A(V=I7SkR9yzg7w>AnY1Og$O2 zB=Bkc1+NNgcP>i}MTViAjE{`sO-4u1C6iPNSQo~t+pEuW{-g7pL*Jpoty92N-VM8N z)pM5YC|`kHy6X9GVll7t9CIW*>%|xFV4O$QbA(OIf8zY;!7Da@SuPn*MDO&qkWJl% z^PCJH*XyP1#GOpK_yGF8_)9Ffbp~bh1?TER4LdX@jif7p$pE~wJX2`mHeY(7+nM_p zCsXWQU;2g}_s4gMlndRMqn{aPLlS8HFd4bqOyhGm#Lfno^P#o; zZeuw$oG&9kJ74~BP87|xgVwh;h!-D^ppN+dBHsvp8FReoxiUKUR?Z!?!pJodelAh* z{22CD-P zXVaYvU!%!+RxWzhQ2r7;C>2&#SvmYjK-ll+jj!_gAQN@nSb8~^F9k1M^&OccgOA>& z#yO=&JYN^5#<}ZO&;P{vo!bl6drvM+wgYdlO%b#Bl1=W|!K~BdOxG%lg0SB|nsSp( zj>w=M(7&|1_MNr9lt#y(C%5g`lJD(^9Je>Vj6FE5wi5mh9vHj_?9<3Z#}c$^7T35NeLS!kKYK+z|eU7tZI9HF@yne{ddO z7|V5+sd1L{kK;`+15shycSsc1AE3s0*0La;o}uUMH*JMY7TQL7z) ze>#;WnII?VNMBylHHE$e<9GQrasRc+bmxMMOh!!S)d!J3sO?8C^Oo}CS_xDRovK+E zA70!bj-0Li=+LDwo)ICZH~R3O^-to1cEDSE56P zF)!gp6il@a@Iq|9Oa)Ib&9L#g}r#BfJkhu>+pUXlbV-+#Nm}KlQa~i7%CPs?Q(lWYF>M&_tJZ;HoTjc_8ke)&uyJk12Ev-b6Xe zE%?is3YsEC?)-6kzM%*nRiW_3?cmCrG)yFqQ_%gk_UEiwJlQ?=8<3`p~9sKg9Qg594#nm+tL3$=$Mp$gkL! z@=u)QOQCzX^8)^Cxfi&$HhcvOWu#$ofv*AP=9mG^`ni)2xCLH1yu4=K8VzngJSV^A zkms~fd|fLw&Z9CWadUe$&Vyqn^8p%aoM$vPhK~oo(RQOhX?Mxu3H#ye4oy^pPrG@- z-UvE5&yTtc7I_!zFzRt!Mz3C;;1SUw^lKh8IR+Q_In?2$#xn8(cQtWLTWxzAU-jxp?l}zRh zz}x=i&+Cj!r28u0C$AVD2rt&kCK+oi&A2ZdgACyn&DL&MvUIu&g0bJ;j$flXGoprvIL!r0e z1N;0qdvYk7DrGpsL!U8aW)=qqw44SD&hOftOaN2fpN@OgtW=sxCV@xma!4gCKl z9Rg^`I7?pWkxDhd)^6Zj-tCEkDo3Dhnz->cWl1#EH;`PyLb%J@c)A0>zpr0 z_n*NNIJ$**XcA3D>jUWHq+-4tvnI=inBn(1#4|0!X+()XUG9E{Z#x)@9>t{5n*hOU7yQT35BF=y=^-QUOXqg^v(LkFaL@B4&FFa6O*@+mg5WDXa|ioX zokGgLyl4UPnH9U_A%_ul z!(lv|x&(JasV~|0G-Cs1(_p;U?wreE8qzfCC=DQai~a0vRSMat@?FlKWcQaTXh8_PcoM5w$NVI! zXc|PN7Vp@0n*`FchW}?*O&3x!QbGWD-WW%_l3<2XWlSBlD_1|(2yqA z*o4MvoKJ=9U>@z&I4{hOWW@{BI9p_FWD4wQRrq`NT*!_Ws&S6aux7F-4f{g)uxtN*c1QFEXg#Bly@~6-!q$!Xg4`0iiDeTU>B&xz*>E_+}Y~kSq(%%$Bae8iS zlnyc%u%~v0j`(sv@axB6Ui>baH7Jjy;ho^&ema@$eiTl(`UTP0tW0LSH;f8O!5?Uq z&ko-Wr9SW?;=biM)b#=GDZl!QOD2Hl51a!$e<)Au zXVEy|+`7F|sq-=ezFFYMI)*7%0q1AXk{kCOuN(rLtI$id_s~+BG)$!^;Oy0VXF0su1t>8sX3XJ1R8LhuC}H_$enIx3F(=HiaGH_UY10y(*QfxEosw5e9T zC~{j71izhnlF#PYH9;R}ChH_A9~e$^y@RRy;{g)u;4m_)8%n>dB$9nmVKg@@oc4A# zki17mm=|(tECT9C3iH%B4|b209D(Lbh0oK+MH1^pYMh(O4@l;}RO8&M=YGj?r5fjR zvNXvU*%f}ll~zKV z+ytt@c~*GO6$WU>(Sr0Kx?{gysADcC3WG1z$N(V@I>`2!L9|+k6wa@RpxMa1`Y=9T zumWGYwG{l$Ukc%Qqi|~2I*gXpOB0w*IQ{w*K|b;TArRSQEruk~J|h#M0zNw`ds7nopFlkp#5IIpZ{4m{LJ5qX$g6B5jek09ip7)mPbH~CF(&5u{s4Z}Ix=<{Y z_0FcZxKn2zttTA(m`PcK{pie?-olW588rVE{PR603Ljk3F;9SJS>-Arr*#?)L@&2x zbcmq8JB6qkzN@4VET9{Dg*(HXv)hEb2a~9K=O7wUrGr z7-{|s^?5ES+AMupu0GHG-p-Ov{Bxd7UiXsD`h&B-;Hwk4+9m;H_NgPtj3 zaAY1G0?w@$J&-)dJ>@Fu^VI`oQWwf0eY^+FZQBSAT6hoQe!VxsQs{m$lgy0#DEX_q z&?yN0^9?_`QyeEm3`>XZ*`L0~?+^?sQ>hv|OKaVu!p9XUbl@DkFwCojGuXeKK>t=+ zStFFLPog7@g5WivL3?TvXd`OH^GVut%q^ZePlShH=MHqXWgLBP5kiu--O1o;4Bf$g zRN1Q!4Y(?&T7TpNogYY3>&VG40{gx%#-!CPnsmNIQCWaMFFHq&{(j_i-WWu7*P>}` zX);OPJQJ>Kt8p%O8ZY?XRpZ?DMY>e;Pu(b(*+?4mN{w@1ESJptbDoR*b0tmw;C$R~ zv!vS}oQLjjCCUDS^UIX$Bf|~;#<};Wmf|722Xlb)^FA`^I=ft22b|r%G!*Lf${}6! zb-|IAg7O7;cmBA~N@T)&%sNhh3zXa}Q}COKoeTKhb^S!)z^^p=0B?)IyKV{-Qd7wj zchu*;wZitkDKy|v0LflACjVW@RN?@?hLi0{1Ny9SwSly1a1W~5mOy==@B2_^Fpa|w zcRIKi6 zZ@SQN&8@$Pw4kxQ)+nC33|c@AX2|hKM6PPwFdDQz?)SMp@hVG5k*jf*ZztBjx%%Gi z>~@pcqW-APc8=F6W?rQ3pT}&O#iqjRK=t`H+i3CTpZSJPZcnBAM)i4qh&=kP4>tY& z&Ohr$V){hWwrldBg~cwr@R;Br0EBL-iN8e zS-(sQ=#9I{cP2!DgLmnyAC0|sS1>-4Mz`1aleckQ3Ywov&)Wvj;Hxdk?1h3(q1SER zUyqJ0MYgFmb`tu7DeGw>4S60&uj5Bi%qnE5p%JB6HZ-iFzNYcX^#b-D`97-pb_ zKbBHaHM~!ELH`rHfl?%R-WN`$*tZRaPTa~8x^?tDKkenz8oFRl%|N0j$TikYprS1J zyiSRxn5hb~*yljEZpKo1%@&$E!VAYt%I@JuZCopbSM@SzO@$w3Uq6L6 zX=$`|xj)@*(2D9CrBV_+aom>~kl{WBb%KY^Y472rJt&zXF=q%cwj!4ui8SIKYREu) z+W9q}`fUbxh%F~igE(5P2kr&5SZ45}?s7kvKF7m11imPj8;4TkM{=5qjHjbj@Z(qn zz1PMlx(Gdhq+J?~9~ng(CCKr8ofoxTQ5M#;!qyq1W3S zp7Oeocptpofb;gT@H4uCcb^fm6TXJg>AJ`+ z#=iOU!+6p}jV%ZarAy<}$q|{k*W}3RF3zQEAK)=-6h(0x3&?n16d6(saz}Phm_-zw zjE$#mb22Cs{l@JMsWhhdLfUPi#`%-0D745`pFnN zbW!8{Zp#DlLU;9fetU7Ycn4Y;)pu5&8mhd8d8g{x0(tZ^jQ+-X{Hcf1fkGa&0M2PU zjfM8Da>)=l*FWtijJ<#!qZ)JF;X8$CK3SB%Sw^*^uM01GXHuW;e&l$#4oy9hPA3oh zp|@*G@zc{t5B_m6_xsZ7J1JCK2Y2BtqUm!L@LNQE{$@jK?mJdx3mKW&MMad75=D!g6G(Gq0i}x3wA}$&?=Rh;kyYc& z?p_hxqSQEBPwZfNZkHP8t9^R049uxixHUZ0hmH8NPkFtyQe360#@X-dL@_N~jdS>> zE=o7>a#i0~<w3Lr%Hr% zE3;^imyD9$J`hH<$)vz;el)MVA$7yuWX3^1@MF4Av}qb`n}>eGa0rSgI~G>bEKCY*HT6EMEJ>p|Av`?#=v;ejlsPS`1PF-i+LWrGajap z1m{`nNC@4@&m-AvIef~)Xu0c7n$uNIXIe*6PT4*xd>l8_YnFRzF5HI+^c7T(?&JU`x`||`Xbv+^?8V|E*rN* zjq}3RdaO=&^?Ba5<&3!T&-~e@MIy1;p6)gfV&V-~&HgngguE8&wyCK-42qu`3B)I*U@ zbD1CgdZb73z0&B;9OOse9ziE}rqGGc$hPyhrm+JRWE&qqX3Z8*1I#q~K(FdLcO!*? z|L^`NkY-`tGxjZXdzjZGHH;_!fw9yKyo)nUGimuC^ccgy*P2j3osd;zF(#b)UD`vQ zv2xNpfxM(!`>BDAoPrc`YIm=U-k?4tLo@Ycf|3Rdj;8eeiPYSph!$kPhu}vl-owF^ zTBgSN(Xj7A5$>BRtnRorlBO4`aZYd2o=KsnQ+>YCz5|=7ug2Nqj40~;!8ygeulN=B z3DtMbbh%%Cb-fzrD&*0dh5wCn$;0VF+w45*lL4Mv&N^XJ47>|QA-}*ZU1+f>hx$B$ z24~m_AcF`+us(nuE` zWeuKNlljjSir2vVuHOP;-U?cN5?)$%8z}&1J0T5Pk*Dx9dWQR8LNK}6!4oAko|ZwI z`hG(?WrGhn{(Tr-=K0uv#1XBEr1g7t((K4M8f^!Ej4u0Vv1J@(#X=|Lxu0C%;db_I zBKn>Kln0L>p)!RW4GSq`WGtO~4^JX+sJSbl|?o-+JLrURrGB zdNs~TOPjH!J=Hj$eYaCw{0C>#nH|K#m|v=J9*|X5Ui_zjR`qfdXa0@z(>eCSlbAdz zNJn1S?Ky%ZAeZKv!85jHu&{1J4n2M3OI63V2<>NQ(_z&8&lMHII^!&468TZtftSMb zdZ;15x!_YnI(RG{oHy*DHg}{}UTL&%VgUU*+?SdTN~ORC@OJAeA%0jvUT5G1Khv6K zHc6(s892jFoXAl(ky^kDb5GB~5j zY;7Dh)`cI9E3!y2581XRj&67+(fBja!hB7n_pZs*#W0pG!TV-Zk2pHx97`7rvdQMs z0t!fnPd@NDVOK3&pCSMKoV>lGbmV^Zd9HZ$UL5JB#yNHMXR)-m8t3Vwl10-$??H?3 z24V}$FI6}{kI5;I{BxdFINLbvE<|!p|VLX-9X*&c)dW#2gjfU zeVs**5Fzn)7M;XB<#lehFja}Y)c`-bxJ(pYhkzqp37^?BH-rO|(`kH=KQfX&2w_dq z$Z~i9J+5v@3z1*Xo(GUid|T=V?t%Rd%tbW}$X%I47vUq2>oI;<47E;9p>+dRQ}Jl<^si>ot}Z6jyFiWe^qz%65_rNYtVTY#E4gw& zjdP;cIWfRJ>^J_AC6~o;Lp9Dm4gADz2I}`djiA%QyJjh5#RF*P!#l!( zBgv%g3Eu{XkHXmzNn~b@%%d?4$ksi9zUZoa6}Xo@~qEcURi zbOd+(Y5jtJkW~6$>Q4!t8-=enDWnHY-v&jfP|#jMJ^TWYW11xNxC5`MVc0|O&KH(%NTj;h zp?nS4F9ckRrx~Y%XwR1u!pQD%;3I=a`|^hH<5vus;;#Md*i*p`+yjDEcBs`SAuj{@ zzhk4Q!?!vVo*zRG!(-@OxF$IU$I$Fi2{dhdBihyl8B#-`aq!k4w>{X=tj?q+qfZL{ zm`$m0Ycykokh)%7H(o_nm=4E$TZQw@Pv@)W}!Sqo+#;waT2h}wO0 z7Cyrl!!%;7OrdS|AKvCa1CRKTUa@BgBn}Asy=siXOE=Sp89r^ZYs2q{D;MIG3&n zDlbr~ah_^tAw~md73T7r=He)1@Ts1=oqMg!L@iW3d(N7!)W9BAh3(E&PUS{__#4-e zN8jAzZ=472StB&7lSvuid$*YNPHOQioeH#|i`2=J=2WFow^-nO&R1G}9J%5;(Ak#E zllCY~p(nXA+^?;rUJ3ZX882ob5JeUMt@| ziKI#Q!pLDmmhw@LsNdRQ?I!J%7iUD#J#C!bn#QIFry^VDcsw;N`Di-oZZy@lP9-hh zoRMBL)c3)Irn8l8p_x!&b*S}DWev`=3ahiNGL`*MpH-h9shy*oGe(W`m5A!{5mVGS zPZ_wjy#7)3dA=ksDZgK&##xm|uNnO}&LxodO`Kwidz>SD zDTC`ni-9ZvUHGp0G-gRwkueg-=m*<_)|f7bb}*==0o@w$Oa9iDF)b zx^5g=d{g=M&wJ3XNqgm|4QiaXXv$1l!E;81ziGDAbcVAUXI1~^cw2p*EeF(7vP){5 zReAKu8Gqv(eB4)<3~%8}i;%Ap1dZO4Xz8DXV9A;Sc%x;R8N5jwmA)OT8Nw;jW`WP;5s#2flVD?s-wO3uA{r5C2H(aZJ z4?fz{S8@z`PZiF&MTQc)O@DB1(APA$uHWy^yB8TLlfJ5PR^`#Z-}`rc)@&arG@qA9 z0rTLieC@cjWt{|?T?#++E?*@L%HoJ?V{bKnm~zE>_=3*$rCY}R#ZA%}nzs$PQ9nY( znJwh>@U1VhJP(MP@1khk5E)6Y-WE%5!`IMNM&wh6=~YG0`yDch`v2Ja3aBW%^?M5u z5XA<>L<|&LQDEkI$Hp!URP0vl4j5ntn4!C*1wj-M6L`-V5m5{j6~#ovMzOp88^`}z z-}k%UUF)tpmxT)ZJ@0d#*w5aMjoGyqKBRi#MosFQu?Nsb6Z^rZdv#}a4}6RnLEtJ( z=*dLvz;bb)cBkSQJgiKDPk}9x`Kusk}xH9jn@tLr01sv)?*F!D^0Ky zrEAAB(y$j;4?gtrUaWMK4~?w$C!Z-MtX_RTS`OairG_uXah(Hb*gNO~Oz{)9S7>qW zX!8Y zVdz6IL*I32eLXe}yMWWuJm^kJBUbOOCyn~9phXiKv9dAVEvD ze{p^z36eGrLT_LS`206tNbWTYrYzLp`2)sE`X>ZZ*h5$NzmKpxG!T85wsNXJZCPH! zGCwMvEC*M>KvenpQkDyR8%H75P4=O>d%;7lsS?S^(@Z?_`VW;)?0nulxCwMi`@>;TAyomkN7;wxQK8+VY&Bk2h4S3^E zLq$c1l5(!Wt7~zT_~^1Hech&@pARC$K6qYLzfte>3KD;osHs6CH8sid5?Ad2r&rgH zyymVKKcDlVGtiq3OBo@4x#UA0&=p!Yu8|lY>qFN1*iW$v&sz=u@r;ysa?BZ@TaFsE z2J;WAN^JA62UmmhIg@X;;~r>nwwqpPvt9jnKP9L^UUh)>JfCo=XJh#3f8$1$Z_>*besqK%ryZN}U-|5o0M7d5`k*saiOAl60Ak%v9j!aF8nXVfBt5}?C# zw4>PkiIS!rg7@pRj^f&O(6zn*-^S8b;#nWeVQOPAdZJ!FxG)|Hif-Oiw4Dck!_(06o84TjkGa-n>}5IcXefHfRHR8%lV_=}IBpL1 zJv;ePeeqY`Cw#vy_%|un9_3vd3+=&%q0}_(be?Nl6&)EE1zoE6yu0B2IuyoJyH7jv zlGb|>x7bIU`?Sj||Ek4#MI+zbbs1WmTV(IdZGd;W=DBoYuLc`c@1VpE|JDh#C`*=F zcL*k(ajv8rV=PS#!u|qgUB7DONRHPHpiJC}KUZv*9C!C4`UyXRuRSFl4SlJ#1^g{; zey~%dL;HFt{9C7_*sY(XBIB8IV)2vhW_IucM-2+xBeV7$XYlK5j$r<({(8{%ZAMr0~aC=S%V`u zW|3G&4o>y1L~_1hB~H~-(&t0_sEm8%)kVLd2HP1IkL7&OXwUQeLFWe#xw3<1t%gR= zpZ{pX~@PP8jii!$4&c5XLSvrajx*7$$u`%Yzv;%0avOTmLVy&_Mx(i&>=aj zkbFWu6<&`0po)o-g&OE8Ja#4P(QPF9Ryb4lvEvzDYUh6(^&g(m=h?mNo;F3_?hNkM zaZB?2)+lM85&TKTTY0bRLyKzxb{stFiSgLEjw+JVNd5Mr-9b;vT;N7?rwd};QC{%l z1JD1=66Ar<5CDHKpj0JxfyT~NE9BTqlf|uh&>%N~R(4^sIAEEIs*Yhl*?z6q@;r1z z74Up=8Yfy{uKO6AyrQTL;s@|SZf?MQ>vf?C7+ z{wL1Mx80C74-KLwL!fEq5Gj2(6W)%Pb#1$`M`{c{!-u`#`F9tk-y?j;6g#T-Q=Ukp zuz$Ds9=wku8`7ES&>MM%eeF@*sR&xuzR)BY_GKK!dMRlo@^C3_qE`JD z>IsgXJ0-NMFF2uBePxXYyj_|Ib*FlhG2V?tg_%%w4|nTh%o(qA6|#4E)7dT1SzBu@ z9KxU7j=0eB^)O*BW(yq!HC3dH7b5FHuK<4JAKp6%7u!H@YH}d$=rTj_G6Aoo2ej`e z3>ElGB{fGMXJmB;p>nkr=S>TKQRl7he=vU=yqA_^52@z49PUyk83k>vn$Q2ld8+OU zsYOx{1xesr1|6T^seDyclsN3G*!@O{MqC! zO@zne;YIKcn$I6P3;Kw|OM78=`=*`XavXKxDmf*_j2GaPMFX+hmDpsyaD{`9@C-h% z6E+H)30%A@Zq%-uhv0zc+s)L223q?G+i_=&JfI-nKSJp92yq5lT?Sc+!W}O!>i^w~ z>YP0&Jo};~LoaVywlYU(3to1e?$E3`lP|Pgi=JXrc>A3>E0jwWWQqFF^YSU-<2?^* zfd0sCO{Va9Easne!l|1_kf65#xqo}i?K~CucVh#ncm@30f=np{`;y5a$iLr?qTYj`lYb0($RigDI0~=K5?9((9!KNQ zOPl=&em&8r=~g3eQon)5e#0tqdEkjV1e$Kc41`tia=G>qo>u2N3M;_>3c&a5mDvfO z8^dcD=fB!)y0D|Wf@al04)bY)@br>`-Vc(~#42yW7T#eFxLe1)3K!OSDk&a)t<TD0qHS;4I>NysIs{i#?$M*c;z9l=g+gR6osDQONce_OXg2=9S?tNzf_dH-72;RBw?WcYz8e+e$H;kRCl{mo_d zWvrGb6=T=FvdU0)cP5@AW(}hYTgs}k;VHezlWwN9mDO3|4h?w3!O87qpFUvUDAJ3j zRhr4VV^*cpP(__%TF6FXb~+B+vy8_EvL(2ehOhRaIR{<~p;0bmcJ<%B9zD4p-7N_M z{{i~6!&{NOEPzf!|G~Z2aMB;-Pu{o_O}4pFYmF~;N`O!3j1=Mvec;!DbK9htf-KbJ zehr?vt-jG!9q5ESfqy|%bK&GS_&$7arN9n7g(D5TXc+L!Z(}cv?g@{Q`p|^gzgQTz zN=Zwwb7XN%E*K#HPsjaOkr5~;uxpdu9CxBqqHq9uah*G32fW1*!5{m?x>Jy=>^dt{ zDUp)|$?5ckdx8sQHTSO6?BsqH7QBaV5@Jj3bzouK#MisKL9fa{cCD6@Y8%1>Xirla zHU??acH}Mkrm~&-O0vJ8piABzWm{f?>pDS6i`(^(9VK@P#4c_7ZM|h<*1OTtE=uft z^^zqvcOw<{i-x?klsWH~lgAb4wKcGiEl)=8&B%*x>$H?HC3M91d;RM?_lRmp9yf!i zd3VIjH)imCK}|XtpYKn^9=kv7#N2Bo?vL5f!B!!L^c_hcazTThCeCx zI;%|U2s$fN#K7hUCjnHt9g7V*?Kj2(n*6t*DIy!E2`J|C70e!)4ZQ(uI*;sbR z4?9U;(ED1{PS(#?fxfy2bV9nw23_``Bl-#&JFAb(3Vf@$WXvplt!1rE-RLcz%e}|e zvO8zMxzJHi@#_JyUs5>@Sf!wa4f@FncHxe4R8T>euCk1!@FfWOw;%ofD~99*j~?m_ z55b%cR0}*nPkUFoYCfGT!Kb>o9A2|qeCQQ&{9{U2(%Fzjx1Yl|Gaf$Fw{KC!8SL~y z6JhnfI)V@KD_!JrzYm!Rs|s=N-^D)uk3K@_V=uZ4%!fFR5pIC9T>C3x`@O}&YA5Wt z1M^Wqu0ro)@QrDNdoa;Ys4&A^%LHdwwntcn9J#9ndWLBkLXd%ij`Wig^%aH55vadM z!YAf&vG5e1$8JLoANo)j^}z%4bZAgC_$E}Nw`Pu>xu&au%tMan)eGm6wUQN@q1I>& z?YWS)vLxKgF$V7RM5nWChZkZpdbwFoddcJ;!BvTLr^m$uWmizk_8Q|(Z~6_Ajr5b# z#dvpG2h2YI)9gIv!6vkvH<%|ZSK{D#>3Q9igJONJdJ2NPfN@rQTyh3lmFsiwDhXX+RKq$%H_ zgM9*C>KmF1Eew!D6(OF#wG>=>LOT&TMpKU=LZd}q^cJ~CFXkxdr=xa$i@3IbgHQ^+ zsO%rE3O0jv%Qy02k**g^xH=*>?6}#f<1EVb#Jif zEAzw8ej4Am;BjA>$wYV-Epwv^ZYAqJ2>v|N|Ls30*VUtEm{;~SgHPzX)|3w2fJxTK z6Kuy)F1!g`@aH$R#4`ui{oGRE8M&8UTO-EfJ-N1~h~hi>z#|xW@`Y;3?5d_R#PD0M z^aW>I6$NKOH`}_M@Elr3NyrJAU4P+buos!5-idl>FI1O7EAb}uhu_Z?64CE8t8fLE zYqKy6dBsKKA((Z zc~NK=*)`N3YmxIWx9=pY-`Rz-c4Ei>AN8Q)$-3mS9hz;e-~*f2jLdYPH{KI>Um{Vx zB!4m)3SV8D&GdVfA5~0)x7W@HI&6;}F~pOLmyXjKXn`I;T{E!$Ey~7j<7F>b@)W+) z#c3*XMxW&M+{S_q>V?Qe==`{J5ISdi(HPX2Va@vsYu>=05xHXF=MlmZJLF&tvxe4= zf)n~hHwwUe&si?coubX!-W3GX^ZMY^V}*) z_y&&fcS~qvMja9^<++o=COQ4cWWo*!;!KsCcH~|Z@(_njfn&1jw%~{OA9Br&;;vT- z7Kp)@o8jF*QcJcq7dcy)8>Q53AZvCWngOWr{O$8rlV!Xt=@a%XuVK3^AVR$a4YX%D+k^60k zf*;UodqIZtTp5RWa$%nkM3GuTK#gf@?V$XSiQ1v84@!`-95ghT&Vp;NHTmORcn7bKU|(Ol+ee4TWSgxh-9DW8dZxwDC|4n5&HTaerPcN8|8AYZ^8 zeY8t=!MEIvo}iX!yR@gUe7!qmfZL}3#8Q}nS=oRRmxUE+%iq*TNm2o zINSd`&s&G*OTDTBsZm|j+9RW+#n%EzM;B)=WuNrO9)DV{k9)Kyc6oOCQEx-!3=2l;1_?})L(&@lk^mFjD-F42Uz0fE4-3C9y>Rej+*^Lei#7sFkpA6J) zG!3y%F`tt$>bstP;0S%)PmP;+P|7#trAqWE+IUcw1N5Cnf+t%uliDChY)Yh7oACF9 zqYrGDLb`}Or*Wn)x6ULx-0kC1gH*ddEs(gcaC@|Sz=PIqx!#uPe>i1@9X}T4BDS^qCE1^cjoERMyRV4c&1M#WJ@PvzI_V#cW}#TO^fFb*C6$e!i?gs-tnEK6oFuU5u4( zz`H&IUw6nakUT%(PPzu@kG0<`b~^=MV|@LcQAgGY_u(nLYtIMHX1iy)(NzcZ=g+QY zcTfYan2+8U^i<12fdRhmFnN!7UjctbbM4*dRkfSEfX_d1?)pJ34b2OssrY(y#T`j{ zL-1Yk*}~pJ(s)AJMGkB#W*nn|=dn3ES?^h>Wq{}Q3xVtc`fP)N zXRn>9Z1rLEpK&iwJH^=fYV?bM;rGIuEDC(FWx(N=`+Ml_D(I92VxqAgAN0_Js(a(# zT4>4-+C%@6@I7%>ybu_UoCMF)N*U*E;2XFA|CR+ZbVMCE0A|bpa;-M| z`N>D_G)nGDS7zt(KCRv97QTL`#W_A^D*TmPTxnhB%iI9x%3YZ_9vy{z(mQ*0l z@X4tm?BEP|&>$8ax_*M4sK)sR&iCTW*;LGwn&Ew4tEa;gSE(orvBlq|HTRw1O$9A* z=127BaU-BJ3~ZNQAJ1P-_N4n=v4`Gj6`!>ZTCIrTIrWwN!d?ZXOmwArVmSAB-AfH!mt(sl59SZf@g2CB~TDA8xE!g73fB z2fX?LZ8WFQTTTIQ_Mp6rrX&LOicC(^y7$!lNWys@0iCFmy){h_xKi3Ucq{L>()h-^ z(7jo5>J!yPWB+nDRj&Sb-1zm*N7_EVW|oDR{L)a`W=S%||8SwgCOML~jtOJ~o!hR; z9g;FtEGf{xe$&-RlKnUuS}*XqTUN`i?WRbYgwOt`FXpwg1n(H11qU~AalKGl1rG~@ zHs#{gy3ld`?n1*oyD;Ytfz6!6AI% z)LRqK74OJGrPymazK6~??!LDs6Ewva5f@d^7H}G`@d<;LbEF$J z9WX-E9XK~V@$Y+Z{oJ+EekEzt_7^nFZ7U`7ucS~Q@bR_wlNf(WqG2DQCx3OQzLu~RUkXAf!A!mngqAxfVUn=3dx6O);+3H6Rp+UW< zY#Dn1U4jR=A0H(}u#9zx|2WU9sted#Q)q4@$M{|NjWxRg562|L_SS$W%IWa;Vs|Zo!*z+(Fxl1nB85!3ojuX zM>gyOI3b8dr^YxleIFm9?s)G$d9V?d&<$RV-11jAD}s;M%2J%IqAd1I=|x7%<(Ri# zW2O?+n?`PQuIeY-03O-23#cjljkz`Q!=HZe>TNxMcZv3(Oa~8o_F)V!M(?f3M(mcx zFXTz+qb^ESQtUW6-~JdL6ZW38ZJR&OMw}lQ?Fk>=2<|o>zKA98CQOLpSKM9j&f(v4 z_U0$}y`4xPX<7wTfUmR*dDpdnY`Z93YEeclV5 zKwy}EIh?gQ2S2nk(CUv$VUD$esf7kU2%$$<=%heW6u8igxKpf9^ry$b`J(3~)_;T_ zT}3>vy{wGQsPLgn$e)|+eaLL&@R>oMcKGy{tTA*iMHA#;;UAgzeJ||f$?0LIA8ah{ zQ=2I8K-T|cy17cKt8}MtcAuFU`qkf%7alWx!JJTcw_C5EM=KApmsb?@4)geF+dH#i z2}-g-pK;{Q1aUlit%WUBWSSc#>ZZ9v&jxy?jf%wIF}M?(s;R5I4zoN2?RIz!4P9R* zMxWaazU#kz(!0GrB3Y-KPP)K3e&&rl^OmX9_XFbRQH?l!d@}9884f5hVY#^pbpHu_ zO9zizs%yzVu7wW;a^0_befUQUc#0v$ww*A5w`&Q`V{dl~ z@*KdQS$h(fVt>xJH}`Tzjy?^w?A=cMND^xKxk?K5c+C3RqyKN`Mfo>Ai+Zg*;YqC` z%jYo?Kg8=m0@d}%|Zhom7eJM|6vP4J`FjowYG zXZ!stZrq;POVX@!I+^3X``uqDUhSJoNr)8{p^eyYXZU(o;oN?l$b`xSYKSwu_Jk)} zyDN?=aNd3u?`7e3dnmUQzGL-Hvr(JfjNc4(`h{JKKrOE;`wo@WRCl8qht_E_erKMI9I(4A8P#me8xu8I>g&b+n=AnesOpnJR*IEa98ZbSKuAFJ9r|`$^kzdIY#)^IedSAfAVs|tRj9T z&piaqz$?K2`!?QX5b|?5v~FD7`TlBfkmlij@9o8l6<Ew=_Eo!Ged$l-~G7yuWZJxvGq$HCe@`u#JVJzD`k@g`Dudf!_?lW<; z>IQm$VUL)H-yWKRGyH6l4!7SFP16d|d#!8CCvJ|U#fT5hMqBbu{^7Lw7;+DNJAU9) zDAfbb#hu3SRn0$aO2@8zQ6#)55qs0E?fBTE;dD961s<6bxZmqg=-nds zNMFFi$Aa^5+l6Kg-Nbhv38I#$O-w93xEZ*enlh9Y$46WCuX?%sKrab}fv|!}{zO8|pHeimDGbfWb8iZN=Z*UhD9pr|oMFUHHq4Vj?GzRc!`V~a;H#C->;GQ)3 zP#6{5-YM0f2R;^hspFeXl3u><1~1V_aG-VR66m}g=7)FZ0-4EOFqfIjY*`ZWiHf0@`QDN7e|iB&8#-* z@za_;)BtDr(Sdf{AtRdJAr7=YFo4&|j-9=R0YYQNq7)PS|0= z24tnf(+t1wm$|G*WGeN+-M9E#Bzsw!Oj8guLyXU|tJATwiTiGq^GDYFcN|T;fS6#~ zlrJgXLl!v0U3YZjvvZ?q@NpLkJSy;m*c0?Q;6g*=rt^^J;gp2W1 zksCki5<&sU*@SU^JTyB9elhT7ZWGNF*d<|SP`|g?$Hzf4KNNBJOv=$Q<2(L`kCgaxG_yF9 z4&O6)xwhKEe3DXW5AMG7<;g7g9`fgVs0Y86u&YkcJi&bze!LbJ>c@jK=t3iI8*`V^ zJrsvKuD)p>-U9DR0`kZy_V&EZrAYdnf}GN3J`ckl@Wo6#m*(5J4tD#x-co@IA z!gr^D^Hobtos|XL6dEecoqcIsffrwN8UFR)W)0{P%EIBba#0mclRn&+oB#*D219l0 z=F-L{%0Hh6P8%ty18!2y*BiE3CPpB~todw`d_;V(Z2OEi@aif;ceETXClqO8MblJ;a)}XuR(n1DYWGlzQ7r0_4-(A z-fs!axC|b(IDk@7YlVv^xd`1$+riL>|5d=x6~f1LggZ?`uk6xKU$R2a;$GjI z+}IR)n9%*a?Rt;Tm-`Y$kR8KNcSBvw}DR(6A>uGTg8M;#9 z(OHXg4W3i-wK!{P4Y3)Jz4d>1jO~YQVaH9=DIPf6w%y3;JV+rGaGv=flwEk1L{_+` z)|#GV!}}&s4Bqzz3(HwzDl}veV*`Tf@>|1V=rqpof(xzqbDby(NQ75x#~$4A3GUnQ z8jn1In|%zW`bqGwH+A40>td%EwamshE4b;8Kr(NK89`q;--msu;|h4MRQd3G3!ral zi@n8N;XG@qFS%oucY0PlUz`SCm7%EV%2N5>Z)(cN{G)Ec0iK3A`!R43_g%{3CE$?9 zrYR{%yr(tXRs2S>^TScJjB~<9UlsgPvqVAgZB=;VvoFee9JiG zffd+sDp9a_a5WD!jR(&nOH%dQ>(BXAZ0|1Z{SCYtoZFnyU8KJ|YH?0JZ7zLPsKxnc zv(eI;ys74U{&}7gHn=cZn{>JaoL`@r!yM4xcnX|LCb%%C7fEylINv%O!Myt?;P*g( zNjS`Mz>* zdVsvSC|SYPy}hYlcNJw`cjfaGnCUF=p#kmI@X645HAjxVDWD@i6audbXi_}X*s<^s z_^9J|-fv+c{qj|d^Mp91^yWJ)&i#gmOY5N5QG+Ls+%Fw{T8nMhUibeK=iVw0CTX8e zKY;VSC8ODZhbd%-9+COh>8u<$J0h1mL0ee&0nlJ9h8N*Le|8EQGRJZEU6&`bl(8{n zc@+6@Xby|Qom@K&z0=oMn90X*svirFu%1uZ%DQ2c37iiH{A8AGp+SQ8;>ZSlE}93? z74-A_UTn#Wy9H47a_~A9ne&A0e$*H}x6dg(`7$SHKL3D+#Z7De^Z>LLJGoQxbt&Ir zfm+QOz2?(H`5^36w$4Dl)n^2+2To5RdKA9maQ+HB|M7A!(iF(xVc|_PoK*B(IhePG zPTI*tc!XcHvt%1`}I`R#H#;b=-FhKbnHhh&b93|QvOPd z^MT=&Qgh57YjAdKRG+$^!ps?;Q+HVXsRRBm|Jlz%#g3Y%!=DG|Kk@2Bh_ z`l7KPQC}s0V!x;PQol;*aIUD$TOns|4=!SdYdSm#I6EP3JQW-8(Z1NP%EA6XsUH7% z4LfoA@XmbDfdA;IqQlWRyBnZGzSWzOT~su%%P;0!g1B+fhrW0|W0Ij>^dr}we%6X+ z5%33TJ}L||r*WduU!0GO$&ePk)#B`A`%^jwJ*XONBTgDqZc8oB6*~q}ZH*S^S|%(0 z;QW8%Mz`1icC1S}IRfV+nO5vMaJ~YZA6m3xEuJS)7xXb!1hrzuh|eLw`MOaDc04VX z2AoEGDDTOB+sBX{ayioqDO+L`MbGxbC;I0EwjSqs4eq|k9rM|T+F@ja`22L#2DZ3; z2tC3xT3pASSpny^m~E|i?ZX~JW2Y@i>9t~_d?a`j+w8}E+ z_}5yTZ)(h_GjvjGFtjU@l5-0!&L-Cvkq_6#jb5W_`WpWmpUvt=uyH-oNd=tKD?738 zz}X0S-I0gC#O_a%$Qd{*9G-~JdLm~8&gTj%#QS?=X$Nr5ef~t;H#~;qz?YgebC2*wY^G@lokp(GcuFg_0&$}rW-EcW+A~*6 z^yttd$a-MO3Z4A^a8NB+KX#+PFFig0Evy(D=Csp?a?8L!{wZan(MOt(Ia7wMj5)#! zECL+UFVzAwc?3@-Xcr$oD`9v0t0)^B@{9J?%nTfoM~L0EAN62Y!SjFd44P~yW~?*% z#OLq%Q_P`a@$eh))7FL2^zdd9`xy0~IAH&z7UjRv;(WisU~2kQi!36Jp5)IGgqI7V8M`=LOC``@4yi$Tv!Go=+C+5}Q?plLfFnnzlnUMZOVVVDtq)b|y?lq283d44&-TfBtx{5}nP zQ+Dp+SDfek02L`WZxYRMo@YJ4yTN9Q@`+xwqXc`r&y2*9=g<|K6GnqS%$6LD)}H6; zbxq0WFV3H>Cqutji*pq6WyilbyU&iIL5LeQ=iIoV=09;hSCz)rS*KGqaMmqo$CdzR z1#o_lbw)h!5cvjh{%Gkb*6Wf$y1;qS2SL0P9ZO??b3JDR@zCHHx`z0CVer|!JHI07 z5#sZjZ(ezOD#Ix!&V@c0S>-K!8A?Jl&ivHu+ydz9C{cs^&fk->K{tptSE3)XucPe@ zUFb7oRuXBRXZy0ZA8m^T*FDA1?%HqcD`3WKc4MHO4E6OA@C+OB8Fn}Bfu{<7OzQ<3 z>;|q?(}X|;_37(wC%vJf{SC1%ls$;dC4eEec2%2qgPb7y1$1S27oS*cr&Br*#`AS=$_%4?f^M zZdxSKV1}|{J~(XwTO@Hk;4R|?zo9ZW$!YMet=>R`I?hwl5B-UW+Z7b_*jw`89DGiH zLQiZyJ{zm(jSBk4WeQ13>T+Ppvi;@r0P7ApT6pO+d((A`$r_^dg0l(zk?Q?B~o`%j$rEz+<6 z^q*${XE!}77Wz1a-UH{Z`%a6>@+4}68hPIR1!C9s@IeL6hdNi~jSE8l44kvR80A%4 z0cXU4hMgYUj(;0TWvTE_U$@t8|MhS(!x^s9t1U6V6H2Xu@f?G?Nm@P+rfG;F#cjq* zvdaVME9&=mpVvsL@$UX^2JTtDr^NIVd;+1@Icc(J~-!EQrAG=Va(BCNc z-Y02x5ZtF5?sWS20m;~r*xi^9e(mK<$*mLEDZswZtknl4wUO6FV3##yLYkxmdELj+ zDq7tzUh*^xFGv1xeOL6L?SJpV zg1z|p{^lDE3uEc=-@LBHCXt#HXwUO9)uTT+|KGf>g~ug!XIwh=oDc^F+p=d*Q>gn3 z7uwSKmN*+YcLUB}W_yTEZ4>A?aL&#*5y$(-($iC@@16zZz3LW2&rzooZEcXd?qwvs z-;0{Xs<&P3%i&}bfqdH}%T9eglmh$^pG|++S!0*|Qm6}kn%F_|9NHJDCopf_CrE;_ z{pm2cG#9qbk*v7sOJ(Ti&Rf1&GG-tAJFL-jwpU2rd{@(SN9=$n`A9}bsws}SQ>A;L zq)9(DJst@iiS%H}##C_POR#@+DoD}-yPO8N=XkiE#2!7OO2m;~WlBkH#FF(hedv?x zM#)4SFWL_+fN`=8k|}=3{YHmUux(#E&$-a`r+8|*(2|n>=5@*7GdlgvpMS?;Clmd; znpnY;HS`XBubR(2uRs3J^Bnl&4y!vooz4O03FpQ$$Ep-s0-X0p{SxC5H|&9PlPMYE zWZ)bIoNe!{6NjPhp9P#3HM11Wx}ryT*oD~gXL(i6fpZdSaKGR@egQjnz|;1ES>Dhq zq4dn#gAHz9ZjOOtY&z2~XZR;6k_e zu?zF}p*#yYy(&(%8~$8P_ea31v{bY!LH~2WK6lDpQ*7td0X@2&3JRNe%dQb}imIbZ zvaWUCE)R9~_5r}z=C0kl{_vgn=}py&Yj&5=!#|At`^T44c89CrHGc=XjL7Q)+Yz5Z}J6|ZbLJ;TP0nfaXZgE;z6SVxi7Uwq^rT>Yux#TDNvJ(Ag;B4;h&X#>j zp(^0~xVe<&qmPsboM+5x$7B`>v;{bip3#JL4~eB^z*%>t0dq%u_CX(M;w?SqSq+^Z z)PRdl>$4Gv8($GC?zJ#vb#R_HyQ1%O$cTMB8BE`IxX{-{Em)j)AO+%kRCQZ3=~RF0 z+__MD$M!7V)0gx|xMIhz6Dwck173lgW_fgG*`?^ep{JDcq%*q)&P-c5`s@cRSjYNm z3jg6j2Cf}hn=LA`*#@n)Z)QxX!5+a2PqKQ~oGmm&e2(`f(-!*7(GmKZhoEDy^^^GH zB=*XO`_biyBr&CvH}usppXz%)uMd1`Ugf|8rtqzF5B6wlV#1;M^Xc@=)mrq$3U|c{k>wt`I*Ge)*wrP>AU~W=d7&Sb zx{k$)&}Q9&oH=0^vyBNRwX+Lm9}3odK_J!L=|bD>eVAoee-d+$hkFMxKX}tu+Q0{D zdN_MG8?%z%u9QSk%&Zui9abg zZKGh{o5oNYzW>}5U*>#05?<5LF<27DK4wEN6fyrzRvhb-8A?Hqke>`oV~zF%lgV}D z&ttNg1$6XpASZV%5ZPz!gpPTKJp9F37Bj+^tOw)ox4gpE*7KqCJJ{d@+pG#m=J_T4y`-kT2b0wpA+3yRc_+=_WfDs-h*>jX%8cD(kJUqBcd)3Y%BNmM!rn z%2d(9k$Ef$d0i5G9y?!2Wo$n3KJ>6mdro6ZcB*K%Av~K+I`JORq^ZHmS(VA1Z)xMk zJmYKJt4xdYoqdmaLL)8CKe~V5BI?1K@0VQp!fSB-zc_!NpvPt1(#QwA&AhM4@Nxpq zZNV26{g}yzB%0R)+~7iAR%dnswHSmwuHPZ7v^Zc`Tu;Q^% zG;|F3LXC4+qaG31kH!v{>MTn)#|(26xP?!zv*ze;TpWUbEBYb(`V+g+QtYQ>zG13j zf4YWPp?vy_nH}>bz6Sd#0Xls9K-3ejunS|S&xfKO@%RD_Lt{fe&qhrxf}j`S*ocRA zQqw4Cp5#9= zwpK??Ri+kOA-$dEzi=LIt;g+WrqLY}@L-?rXQ@w8s6A%-JB*{5{)l9{H354N=5ef3 zK?0pvf?Bihel}!U98KO1{&{&0Ygl&=%~ZM4*8Ue+$oVL8#oYH|kNfQWz6f%Sg$A$j z8&(t>h8-NtuZwE&ddVR)DIPqcU_E|F9Yo8bz-f1H%BRf5UU?)q!J0z+PBmYD(zxG$;{33V z9uN0T`?C*w*!?h@(kqqLU>?d1QrUy#Wcsqfm5dDzF&)gU>H)*mFEy-3Q5^L<0&HiM zvSrX8HYs+cqNP<#IVPHtDlw<7Uz_J!MbfMH*ufv&kdNsQPOiU@6HzOEyh|wE0d5_d zbmRg!CiQ;8xB6EP9``1Inxhx$&~y<0!u=@XGoE|J5Pldl)~JqhT3I=Uk6x}Om*aBU z6f~JHE(UJju+thijW?)Jkv?`RhIgI8^-R>L(LG7}Y8t;WNlgpdAQ$O1nP)%;{@^V? zT2elW&uoqO10F+8m#%y`daXsCQDksI5B~r?=)FUDvx9!r zT(I;(T65rioE% zaXvZtzt3~qrVaS^S84PTadLe<#)e}js=h0D`=NQP)}pjtQgBrE)4cIEiOLyL`tz_|Ctb%PThp(bM~K@@%`B8=eTDZ!fVARkfAB?BP#m zkYjxHkn`Kviz&md)A%q2fA!vn21D<0xu+N3ybYRN;0fItrs8!FV;AOwOE5>pgP-DE z2vAf1$?z-0j@SwB0Mambt~duB!na|xK6Vq|ITreX9GdL4p7J;Nvo+^=g|4k;V^1y4 zK8vPlR_SOpd@4>&(p;*e{PR5r)x$Ku^|bfkbv2wfx z)(DS$X4(l}Pl%Iw!Dm>{(!CUkI54lnT~>+QW1*#-j?MqZVwWbszY*BBYs7a-;>cwm z^fX?#BwyklRkqaRIA4BW_P&M;o_D}uK6#>{%gG``|T7)6bCqum#l@;P-w z>F5kMlC9s$UqjE$5Hwg8?&JSv3U>QJ=%u~CaLIFix@t>1b5&NcwIdU z=ib|ppVtYam6HN_e|SmPU_0V`JxznhTAb&q25R1q)#AMFwL}x)sXfo{7WUWt!ftrY z_ZO)wH4zH!c{b|NLGyc+_B>lQGx|@Q-?XUDZI$U1GXpyyRcF|h-SCJzjC<<)MK*L& z3LQg@kRyA_I$0%?0`LB$n0oNwPNd)Hz1LmWiX-Qs+|Tfyw(ib)buU#Fu!q9O=F=G-L>s${=+cf4{o>i zH_}+CwQ*ydvbE+uW-m2(cKzB~(-L|oHJ|%+GuE6siXCu#HfzvS6F*jqv(?uI8UrUS z&X-PA|7TuzEUX?+zMoDf7GrNq?;P`~*!QQlS?zk2^(afBLw&KU{`56le=(W%h2Xt* z)#oOs6KVfDIT?Ov%jdFq8ZyR>TvzqyQ*vV|EC!ww8%FcrCu3;fWB4Oy&Ev`wQPj#D zy9f6-^VKIKNFV!Vdb5-~GbfDFHo@!dM*#nEGK7|SxYLgGXucT!t=(5aBRw^RKWZ94 z1A4&YyWwFzUk;7|IJfh{MZUN;dKydIsGH+i-p|sT+!uk1>sibNGw7?JzY`pImA?gF zF`zN@XoTy02y_w}Cc~4ZWjS6Q#^pdl zeVvg;)lab(f3uW*U6e{ev!FMT_L0@zwU_Q(l#^v-BVOQ_M0;^=Z%yvR=OiT18t5dH zcedr-v*Kum4)&CiCi8oI58ZHbr!gB>aJM4x!49~Sdp|iZFN&lEFWo8ev@Z{whZ*8; z_%}_8=8fTn6j>bxATMAvW3BcHw)&6UhlX=cDToKYl8nrr~@#`%dS#D`M$-1$^9m z)^hbXc()DpAgf0n@S~5Wg<+UCnFn#tj0j4}L!RIg&qw9M%L95P{f8Xlfzzo)aRIwJ20Ik(_M+g*S1;jUFY?$tw7gYi96Jvv}$Ne#NPc>-k7~@I9VDb7X{)&tDWn z(uvTpYZSsefmid{TS3;wNqoF1_#W#ObS*EN-wF?=53jL@mRi6EfG>ZJ@GM$f=Z5LN z^fAMoCU&Xd1sB!OxkR6T=}X>jii!fkQ?l>*i7#ve{_Qqss`vZFAHln6;&^yZx38_K zKu=>;w33p0-Q!_?zVxJX5FL4WjIS^Z{DVn{h%}zQUyHL*KrG)iMvL=QbuiC_MqJHV z?YTw4MPDt>N4GBKoBqZPvsQh1*x$I(?3*di`n2VLaF*zQWXmRHkbgAt3Uduh%*CwG z2KATT752_Ljgs#|11SG3yOEPh-CW%Wri1*~z!Vw}zDnBKPQ3HJWC}V9jhZAoe$Oz8 z@|J=hd)a{>z|Y0%pcc+t!}q7f(VY+lJ#M1l&9CpF>BdT0Fd&%M+Y(K>i<%gtJ2Uca$i|Fy{7QcgbBn zFEXg-PH7FaOG@5((X2J_kZpaPtrO^x0-SQ|(Wtqu5hgT(F#t0vFg9I#Z;jVC`%YD56G1TgC*i zx0m5x1HHvhgA!P)IC$2;OFFuK9&39gjE>|%>qI!t#^9Zp3_tF9W>?u$JnOpfZ@M3Q zpIO6WdMxUSx)yI(n4>Ql_k?HJ<63qc=jc+oJ1IkSO1^wUUliKKBVK=H=5KtdAA0)- zAC@wg_o07qn6o#RZR;oD++bn^I}sz{T*pGiWMd?pKkwVjCfZ3joB7UVp;;2n(YnLf zmw#~XqixPQEtPVGFt|S3*l5#VI2SlR7W-Y_3%?@hxAZz7_Sl|7Td;ROWs6#TVVO<& z15s1`t`e($XVTGcuH-cAx2TKQzIwuqE^ahtM$6Kv8#F)nU+T#w;rwVc*PY^*ShHGX z3dMJW4&&F^%>7*weTJ5$NtF{DYm`W?5%AL8@5zqAM^ERo2gRgCvb;9%&U@s6{$MuS zwmzJ~S9;L3Z$)hJ>JS?FRz=53z+uMw(Fs~JRcoqPNBH0!g$CNKyr*p50zcv!U~g^l zCuV-$hgt&Lk88fOL&(EN(8u?f`hXck1k$}OL3Dk(z^XgL>ku*dePuQ)v6FD-CPp(l zEa9AB7QmX$mT+#>*OPh9m2ln}=*&J~XIAsG+ltq+P$Nl$@k+o9HmA+@Kc5?BSBRTN zZTJi4!S-jw&1Z6GdI9#h?*)h*kvD9eP!HP&i@g+CbRYAJUtFpvby0#7RZK9gM&v1b9D+q!)=ut}!jw0Wc_Z7El>^H+lDRJ8}W zjSOU!vOo$8^PooV(ada`KeZ3=poqq)%;ylick!Nc{#Y&gJ#MJTXwV_`g@p#Tu7MAy1oR@XfX6DUru1r*gf<(49(vkeOW83h(9>=JKl!X z?vik3*}80XQwe7r^2OQ<63*xMT@%}+NH`yCbxN$vmT;b|JSZkKk>WYiHcWJ^lyIJH zWi1{zk?M^~$II#=(GuqWt~cI34-^L)>?Hv>zi!Y?RKLll16!c8=hHy6D9WO}n)6*v zJ@Lu5Op2-tpOkhD#ZKtyjk@MW2bW>?RFwvfsFH3R=_EQirIHSh{Ls0sIP;H z%$c>gb!QTJp|`Vk-BfYuqXb%E;7Qj5mW$n=#nCVH+=o5fCGMFJL!Lvu==2hAv8Zb# zU592tZ|ewg(zQ^!(7}tsR-}l7!N&^)C;$C`ePXOtAelpp@P~bYXs-pGido*|vyq8g zJNZ-6dmlQpS`>A*f-m3{NJ)KCMV(vWe{gP5>?tN-KU5Q+t-4MX%j-!vH(1|8TytH* zS;d-)PxnbUe;BGGb}Nu@&abQy&YDU%-q9Vuk*%B21{gP46X5Z3q4AT@A) zwBwq(_GTJQ1I~9#z#vD4E8if`(P zoX;0G`xHl$&P#jHKC5FWcP!3zxveet&y6Iu-HXI8F_zCegpm(+b|MNNTN?TYldY#0 zWjh&L$&P_1X5vi>tpQg2QOvwnLYKd;y_IuQ^h0+0QLnMHt-OZ@(v#l7v|;Q(tL9rI zoDCk9Tk*{#oLBcSk_CQ|`Ww3oy2&alB%JqN>LI&xNW%HFPZ!xcwS=?n*JiRl&|=cu zBY}Hn<@Z#=IkAn8)uzr8&c)ZR4exhTsy+T4&nK*gi5+|HrH#PZZ}wK<(U)ur1kN|i zbktqZFP#aT3s>B?TD?A#G7--Qx-FM+9WrR%RX18@pDD{YnMRkeySQy@mCSB7>W$s- zpdVS6JN+=3TF+3?Wm8kGdKP95&~B)|rayPP1UO^IX~+A~oYu)W>VM1=TF>)2gP}1L zEB7Mb&)YaP{HEV*^P;Xty}0>}P9LnWf%ElBN44kcY>EKRm2F1I)@NqXQ{;_)?M}+vXJ(Rv4*Fx? z8gm2cWYC36(0f`jgu5D-Mq84V^u&8Ux21O~nQnC_{T*&x-+jr%#;K^o>u|0DGu-B# zJ*aE7)E>~Wx~TfZm6)Ta zYL30LyLHLrRTv$84UfEiO(-li1YTplG;nBZYKFcO!RzXTjTt;J0_jaMd?UhZaHbbg0blX*Own-As+(<93`!xyYJ#Kxu)>ox?KJw|Cto>05=g0bYWQ~}F z^VPCrvf_W@dF|pzS=WDXPH`S0Yy1z+p>@oyst5j!bLpx5!siZqDHb?CEPrk_<8C%> z0nYn-pON_mWzituJpNS&ZtpO7gV(`XZ1^m0_)Ta>K&RsBT@UW=nlu_7r-Y_xGN-^i zaR%xQ9~*%iye66Uqu%(EQ^}=QB$8uu?DWEep8BDuj{5EL&3Y7wS$9dp-F_C%0y4x<6GwZ|l!J3cX$wfiTY%c4G6MgNmGY_A7d+5D3?{6bWg z^Plee^XL3KZxkeT6&f1tr4ZnJFy2R2RGv+8;Cz3j5tq9)i&mrFn4&+6d)G3Ps(-`J z>75_9{6IQ6oOPo~<@-2O%QULS8EMkui`@C6DYS7pboXw&;kNXICf5)ZtvAvm1K&i- zgcd(*(~34=h8u>O*7$Qb^6V1_olH;oQVgNysu;3%0T;Z$hSEPpL5Ei}H=RZWA(8Y* zKtE#qV%q8)LB*T>==X~CqjD81D!y&uqhBBN?;Y$?V+6|f@;q;<3g05jlC2@B+ zEri~C2h@wtJ*2bAd8@T#1TMG+&kfrf(*@{jX|6*vBh~HuOZD@{fC91CajAZu`QVT^ zE>EhTztviZmqVqzv7_}8ff`8XLG6Xw>YVOU-l&2feO2P$IO}?ju#7=}LjjyKTq#B;731BxpwAi96ZNxEyBsdqBb~91y`qapfH<6n*B{h8!ru@GH^{jut%+9IEvaoi>1q`&CYAh zpi7u7u7K9Sm~AU)?v*H-AL30fCvB(v&`7!jUS0AbC3QR+K`XJ(wus;*GA05ZAc1rq zyTMDEM$nI4p;R{`k|yd$koNE>x_dGfJ*f!Ni%Ouvgm}ss^bgLKfpidipc-sFV=PIn z;UeL>&+L7&+V@gCpPqV4toK~Pd2(1YmI3aV<~zR(K{Cjx%-+`PYWzHdEDHtlr|tp?63(;IVDso6MF!{_nrBChMmEYb$fH(p0`{*@WD z?}sZLwmHeQUzAP}C*7!L{3~upbt>)gSJKE`4e8Bz^fzWf@1}bPx}KkeU3nGNlvt3C zej+`2r6R5VW2oOO?6wp_&&zc#G_hjI(hvNJ)f;JTdf(qu$tVK8v6%tX(l(bKtP7=q^+QN#c8F58hSG04%zYak zrGlNI)XFWETtDt3FL(}WaL9fE?HC6so^5_>(R=I}Yd$Y&U(7(}qzqQSB!9q{M*=+P!b@7eBeoU>*fkh$EY+U)oJYWqe!%^|`=WLBX>nR*Ib;W%Yx2i%ofX*>51bth0=W#6ENTm!7qt_( z`VRlK7B9F5UD7GE1pY@S8&O$ADmCzgUTw?Hr2L(XT^{%jthFS54m@vLV*fXDGF`|` zAmh8x!9UYb$t%U!&-K+emWo^QK-e(`kQF zILUhYQtjbXLXB*GPJ6nM93^RCI5%0}TyIpb59p|D51Njt}E#A>!G{8J_gDG4v@IXKfdF5rYG1 z=Z77V-bv()eeet5<)81dkNQ@JQ~rMJP8>K&o!5rb0}8;7-U&K`eH}lKVC-t0qa5@u z8f^`yGyYeo4bCGI^kQiLnF@;B5l(NG4XUR~u{xMtv8hMk3Db*S`b z8ub$0XwEMa8oDx-bd=B^TQ-1pl_!(F9W-q(+R@0)NtDzCJ{nCHQ%BUyO5p6~x)VCT zaWwge2krLorcIb=uw}o;)3Hc=};5wun(+D?3I*r6l<0PD?TI>^q^%BmnLs|)b|KyFxnxf*5-K4ysshQ9H*zxCg_;>xh zZv0kmL;JnN6+=rl(TOwpo{il~@K~0HaV^egQI6b|LIMi7sE|w=hh5aAKDW6clQQVd z9r&a#t3$q8>D1&1wDQjy)4jx0dJ6toH_PtSWDs^dH>oJi$C7H#CE@Rd&xF+kvK^dA z4lB_AG+ILK=U~qTv%SV{JE-lfSk!agRDikk^Ka3Z+d>m#TQGPT(Udm^8c7B*WVbh( zHs%MDVMa1`X=13)D`*-UxV$J z#8A53B=FC*BCj894Uyvc@cZ7}q#wS2K93xbFTDJx|LmpzSxCVgR`av7eWQde;5})s zH(u2h3jcWzzB`>#{J;8HgY97LzvH=^!ESEgmK++2eqB%194>Dn`bg-Dj-I!ZGw+l| z`|utdcPor5c#%P&IH%+-&EwYOq>~BGgRWs0xRAMNbQ81omx(XAO**Nx2Ip=I?|M`g zn@ktb7qvKVOs57WQ447HHJ#j_v(y8(N}`SuMC+{pGW0 zD!B5;?R~Kev66bfilRN2{Hc5KHp(`LrXHQ3rBm)o?wg~bkqkYeDV{V5_vM)~hMI)= zk_>ZR4Gy1eHGNYlo~!lX)0-vXd{w9m?e8y1^}9c4EfwIznT zHzk#9b}DJ@jRV{gtrVIHzRs2xr?}zB6<6ECOKeUxH$t9Bw!c+$cIZbg6CUg1p<`|y zs7p+SK2lvTXigc^&h;@AV}*U}qn*eE=alYO(XU_Lk5+b$qP^gcc3f;l$1I{K3*7XE zZ*9mZIf~Y93M1LjiDWh)ns)d{lUL>pYX3HxM)t#QSe^q#BunvpBGiEzs-^t=v}h>x zhi9ecUTmFtgZq9!is#$4WwP6N4{AO?+fgCtI7m3(^Uf9SOp)@&E7w`V*{u@JH#;-6 zsz@4M_=R_xtZ;+b2*kN#bgzi~DitmLMZWm1Q|@N^j0n{$taJ`y+^wm}`a#BJ&H zunFqt$X?v^@o9A6G&tVGae1v%>9e~VwR4)rb-S5NV@AVUYxz1(77Q*!Blu?|xO1L7 z=2I2!lsq7mtE!2os!Vv$^C?`@_&D+n!i;~$A#SsIEZIbOf-70Z-3tb%w-kChcW-l% zA0nxU^Pyp<-*KzDMAC#!e)MRDHgz$Hq&6D^>0(*~TIUl<8M8v^YHveo(J6|SjDSv% zTPsrBi=x%B;F0ubOXh_V&R=J@CHpD~XW2n*(w;5F^Mkix+!f4rHP~`XcFUUSOZ7(c zdea0B@vQkgZ=jb@K23^eHl~+QikQ-TZZbYh{mM{^=anwQijP}J`MEXx=r6bWJ8#%^ z_Ta_}>6lleFPhVmn-rc#S+?M8xPOqnSf5JjchHA@@<{f?E`>7UU1>u7=Q6G>dY%*9 z=zO0qvcAs}$xKH{4XW#L{Qd;$TntZw`G(x^Rq^y-vpcO$>A?APj-zDsIwo=bxQ{Pl zsF?vg)EbWDb`(X^S$Hx%eL08oofAb-y}T&db1Rp62eS`@gN`+%EL@LgVPefD>HnN(txr)+aeu zSE}o5suH=6K2kjQ3hKw@zmnoPyy+vW8UqREvgy5rF8{m-Lv-wgF*78b2ehwMe+7?E zgL6;CH1(m@63#mg7#Fty4^)#wZd6|zmT2~OJkM|K%>`Sg()9@JQ2eaNg&U?&oBq&{ zoUl)}4Lu44I7qFADrDErCQ|uI%vQ7~%7ml@vT6wJ`^5ugi+0A-`+aV-W`1Yc;IVNO zG!>c{JKM{2TH~zu6LocECs|k238C5Ubk?SitQ>Q|r;aN6czKL$+}TLXtl=~9eY5O9 zK?I$4^h9omkQKOq4^|8?BVc-)rRbq}K^4gG1)s$VjL-Ox^P#5v__ z6KoFuEGPVFM${4u|_Xa{(p z8k}##kG{6w-|;-B%9oojLoNp=zxnkyGOcEb)V?`h>!O=am1)8*dyoBw+z;Krbg-&r3g8N9HUM3VaE zx#F4g!f3xTp0+(|Zh0+Sif88no#IE(pw?iL?hv5I6WE-uc#B1LdfMXdWEx0WMZwrf%oD+L`mnP8jF?XAw+5=ba_lf zw}ByK1YM)BIUU8#@QgE`ry@PGZsP8#!Nhw&({EEx(YrK=vKpW!k@XT|dFVxhzqnys z5An^gAZiirM?dCu7IVZP+Fmb+`Z=}{!l8AV+ypr4aaV>P}e zf(BZ`r`RDv-7F!TxLE92C(RSe=7-UpZwhjry;u0yHS*EZs1c)$&t=|(Ri&Bfzdfz-CZjk>-k@tP0%Z8qSBwH+^ZTM$5%2H1ty zoh`N;8$edARb(J67H@S6pd7?`$;4HnIdbWRQSkY;TraLK@`ra0=B)!ah;8b_hij}q z$>Y|Dm(b%oqJ`SeV2Nl|9!MrX!)fWev0{OL=pPK99WfIFzDPI+x4a`9%aw4p6}Jj$ z|Mctjp3hTf4V2>9emvq9xlI$pJ(2?~tFondcB`Lk*$Nz0&F43xtc!Pp&-LdTe)JnQ z{f+a4^Pyaaj}cV15L)vE*JZy}hSOG@4c#1TtZsm}Q`J;K@mr(RPn02)4E}TGrn$lu zaMNa|V;^T)mT>K6An9O!(>(d6@HHub_QN|TdYQHuv=cif4&Xhy7>oYP{m3#1-Y)me z#Mt%Fy*TSiPd^M4OB?vo>w)kosXJ19@d>-GSKP>V@no@wt}jL9;(hvMo~Z2Si{6ks zP!oJT^{X9jTuYTQH!r4V9 z|LDDc&MD_>w5@DJ3Fi&Bo>?C9l;=#0$6bX^>jLQ90&tc#y9>Vh@E7xg zj-U5l;ddc+-VZ{j{pMN0)Y*puE~8cnzAvPr79CTgpzTB73GZfmQF>ih8uC*|ob}6- zCV)FNBf6Pr`NNZ*fq%YZKs#}=sTchQ7ctSNhqwv7(&M|p`Ai-tzCHszu7~i(bFvb% zK6ue2l?T1pH$t?0j(zzBUNj|SgcyMR%XDvF((|?wUybx5?Ms2w#jv|rv>=cw8b;EL z54z&JIO)tXB>99e9`UTf+_`j>;B-bh59ZC;ryd5*nC9~~3Eo9#pyR5!-e9R~we`A$ z^OWl6mhBEn@jNtEQQWMKzd zWM?3;%itS$YFW8qSE-#2@fU z3lUAcXx<|1>@;&10`GXx(IpCMR-qI&TvXAGr3$jl_ZQl1RZ-V)1^s-KAna3s7xq~} z!&@8>7Uy7otHe9E4ion1c+ko@&}!$)h4G%)T@6)|?c_?q-4MRIrr>&6R|^|cy{N%% zPvZOC5;PtPKicA%`FKUJf*$F7+dw+^?SwG1E^^e*P$HwfLSJwR=0wL37vUkS-znid zb=nYNWv~>_1!MQC=T=BK-|@Ls+ylH^P2Rrd*xWL$M#|5>3%6P>yCubQhw`z-UHVD0 zZ;c=QrMrLQ+|?kBdzua3QakuCzS858?gr9m)E+A`RkEMh-N?os&vjq;%|s;r^x8>8##p>`mjEDX6$-vf3{ie%USH)o682U1a4!Ymjr~2A9=eeqcuj z-(U0gu-d+^J6*&6nq6a2{ir`?K-ed;%c@p4M6I3fiGFOSA8L(XXfiacpCvUCT)b3N z{RaCfQ(6msHhEB%g_2sf?Jo2?;7Jc&gOg)2R5-uQn|jRmq+rEpK|actHe$#A*JK;P zf3h#F(Sb(WJ-JYS3$*V#1krTaFrfxY#w+ni_ zQg1sV;hf@ItbPseEzRfti#Mpx)k^WKJ2s`bF6JH@{8!1o6`h?cYa>F zV;@)NA^23I;iop;lpBnFjfbxA_i@aZ&FIj92?xPBF%usv6wLypp12AC>5Vx{&_Z-^HhI z!3S0gy7u*~)#vu8=;=fSeK6moR+g#g_aW@HnS`m;7VsCdgHQU*L+YE?J!l;ECaPU- zsLw!CYV9U)7~1OyS_ix+3-Nr#)=b#`%$vNy$E#S{SBN*ou048z>HdubE98@_{@AYw zs3UlVc~KmAw*|8eg$X~s=m|KH&)akrHm~%i(a;rIH-{5!zCcG1y5T)s<_oLiC9TU{ z+&W<#ejg3a<6hYd&Ff3^!RAAcs^!=_*5I5`=SNYeWfIOBKYEjTf6uM<9xmaW;sdD_ z&NCNp_T^SygznQS>;}>$S;jmc>hA%}U%1QW8G=6?g#DoVU1d9-si@cs{$G32tuEsJ z7H-6@)|fi#o8bGcPH>@$u%YT-;G`XbmW`pVlUfNrkN*pJao&zrPpwwbRx@zdpE30q zLp+b06m;0WMjh|k^XljhuE=Jg zR)n6wdw4=@3l)r^V~{n#i|kHh2y*NekH9XQ*9{+GLIWRaaMPEvy`Dx+cK6tI;dg%Cf!ukTl2Cgs0 zd=Xl{cl-0X$+r-}ZkFqx&-)!RP&aO%`1ASy{OBX_eN9j4e|z+Ezdhh8z%5A_j=kWP zncO2oe~Q9>@z3N!t`xcVhnow1`F)RziiLhfFm_n9n~-Ug2h9QI{#yr9ZJ|2_D4-`C z;XsY??3XIxZ_*!qkT?~UqBk}FQ3&-wzCBb4?`gwyx>f)#GvfKyi9+gmN<~S1f%BAd z>Tp#>?~unzlNYr>Z0)$w zme1AmCjBZE+2-}+zufeuRrL_NZHMv+i0#=!(I+`Fnjcx|MGKe0uVUFu{>=+`;>f_xyuly16rNI!g5iLz|^b_#>F7Xz+id z9?svp0N+Af_sy=)lY`>V_p{cHq>xY0Mf*QE_gIfCxhQ8%(GfTcpqKdq%6cjdS z96u@@9`x&RCNf&UKiUhQ`WyvaE#AcUxT&I8^b{QWD)~_KqAqjb_|^FH>A0rbaJMQELTCRX$O3D zBX-eFFK?>r??MlXQb+(-Yc={IdhIXL$-W-siuz>az2CI!kUM$%K!eWIl;86eewT>t z?v_J&XES(W=fIarHj(dv-o_;CgYUS$j32Wax?JzzsnmB5pA9YHwk8TPYwN>1U=CA0 z3jWQW5&Y>*D%!P8LA@rY@>}rv$b1E@&dcMi*QsdRNAQPLjPHb5deI$td3-(3U+~9H z)i@>Qzqk1%Jv?c>4t&V$U+~IMPwdls(8ea;`IODj&c%CfuSq@m9?VKwU{}zhk%7G2 z8ya}nQ=Yc4x%^7Fk{)03p{Lrd`Jxi zb>ut2NviV@HG2x{I2 zB0N*JHkIch%||QeAL1TZO>e&blr?0=i|^vFNEz=*ZidV`idx4{FQ#C5=_|9QbQDT+H`H zPkIKv|6S$A`&22ZAQJD#y5an=?e6e`!85Wui&r#*#%&d}JDwEs>v6sLJD#a^r}=l- zhj7LI*I4Tt{OCF=>OKZL_<_&(-k(v^ANZ+1^IQXC-=YZP90wKhrHc6cbZ_Tpje%c{3EqHbr^x`rP^|%9Cz|ZK97FiK+Yn7bDDxo{fy*y z_PA5a`wIGH)JFc(0h;~jW6odKS#FKEwQ@zRxTCjxG4{I5UP9OLgoWIFj62<~awi9S zk}t`|J}$W21%`b3JJrFkF#8f^V=09y_V<6q&o2SG%a_ zS|&UhySnn_sF9Xu!`<Tte!sap3BWFr>m~m#cc;ie&Fp@F{NV%a#e-M0N*RWH`_h%Z=Ar*H8D1OMFI0^5rT)vo zS-M$8H+6kz{dRaq`q$%~?|acfPrS=Y+VkEOp41g}h}%F*-u#IN{fb8&(qbmB+Y~&n zbllhGTlh2MRV2fVOv}le4}rJTq63KYfziBuV|Q{v-qstL&5uSeeIoKnx8p^8ML(QN za3*y~I>*ZgDgSt>9GPFuS7E-Nj&r5&{}# zNY11m>-ry@9n#xSWjn;P3F6jxI0f5*E72bw5WjcQJm@0kNB%&!^hMHax^SJ{U@jXAUoIaz5qn%9BngNr7IuUg3K zl&YX14`1VXyZ8b;bA^aur&m7wN+tBjf%&|ODE{qtB`FRgK00Lb3wC2ZQ|v;e&yVoc zh@(ZQ4-YOp#k)iEax>z*|B(v*)lWCd#XI!&$!Gj92b{~%@0zCbi$8@vxAt@e@f#b; z&!14zFXZ-Tr<=*ofEVwq;i#0gl<)TeCjzs%5B1y0^Kqv4%R=4wrmI}H0GyzOu2kK} zTt3nVJsZ?E4z>g28vT{Q?On-YoQ3@5Bb;$Dr;gb-ST2r14!Ecwd(J|Br!Ra7GXCqQ zc}dxVOyL>w!5BEdGAG##_!0C#&D?SYd|v%%t`*){%fcxR+^ei{$lJ$@DbpLCZ*$?P zeDVRU-Q`V3SGrK}wR(JK75H&m;c;wX%7>?8@5B|prP}@ZHn)K@>b>0Eqxsqn;6Vfb zel(8{+l;yzJiEH9HuFzzy3c%Tn{AX~-REU53xxxI%GuS`Q#Y}B~JYQfBtbqTm z{2V^~x*K^N!}-+bD1Xw!jlNxn){X6HJ`88aIoiPa*bUwZ+*4FHv|Z-~@8hMU-D`2K z_x!~xBb2lUaoO>>uKe>%B{{ic9@@Q;{K;!*Co~1lla1wd7h{L7NI{EiOy%!zrY>Kp zptjoWa|j?=N4%#xO(1({1*G?b|2Xnx9Ciw><80o1e+rpEH$cnY1^a#{$qDu5 z)fv!73V%Y=PI}SeMW{)N>+)BhdD4qj@NPcTf?v=cb*vNqoK`(}ckpE%ApRSAS@N<% zc>LjBOt2fzcQt|k3(okv1}x;g-Q8)YFaG@5PJEpo$dSnZzJ^Nv={6-ziGhyLB7c4h zVz@1G{^&JPe5uNfDvIED?2yKvM-6tr$_3tbd3;@O>_%e-_tjnGwd*Ts^c?ie2b|;I zF9F{KeZqsDmHay7<2%S9Z6-bDL!hIu7ICYb@rA#NnN&w$d*ryb{MkfkGU=m+F{mex zMbF@3N7VV9>dW0u;6Arh(7F~4tb9O(2XD`r-?05CtT}BNzMJRckV)aGVHM9>r1BCdpS9CEu}X0q3WK{pv!`$ zuNgS_xEJ?@SSrAd+6esJrUnP-hBNjBalaRiIYGI{@J<|#_v6h9ngo8q8sw$LGivD8 zG8L_pK~JpSFVZ>fPFZ+%zCG3BD+jpKK-51b&kgyEGbmM84v44;VePF6R5 zSE?IjElJ>b~4F7=@|IHglK3BMxL4n98n41jE=Gv?YAXi=V znNJsTN1@SlM<4H_88qZ+=A~b*>_%;%rLe^i zHFh&AO5Ny1y^P>db$Al>L9VvK^`&~t=?Zp$g7BRufxF1Dj|X+e&x#M>`GDH59sbM@ z&}-a&+nq)?#yi41o;p}#KOTRtPjM#w+y^c`uH&unM(+Y`e&jkoFM*Z-=M{L5Z$4E< zU;97{6YrOTn9J1q7b7gYR}h-)!0& zvPV4Ta5$5Od?OoOXr`hM|L?r9!EqnAd`=K;se@eF)Q%hcH2^())Z8IGI8$(SG(O!+ zY`bvx^06z8>+@fFaX~nb$Kray;~`vjDfT0Ak79ytIFAS3l#A;Q`=@i+O}xoP2f1SV za!%XE3x1o>8(g=Id*=bZKt0rNVM@*&=Nc}}qk5Lb=!M%{pO6Qs$RFVdoDVR6WDE~?MGohe;6|tMZ!XNw z<|cOpZmscWx@2$@(_G<&j=#4}5ckC$UWIM&KKk}vHlDDrZGc^~UkQSB7IF)o*=5a} zirrtpZvoGp1N2ml(f^E}tpydJRx)FTgf#J%> z_l2(Dz&8ShZ?1P17oPE>w!m=P_L<_MIA7WX4Bwi#i|MFIbb(>-r3qqVUvIht3@@VzE(#f~Ni49n}j741HF&<|jkoS@5g^!K1(#J_1tb7p~aG2wlYeX9#Q zbjh8z0*C6sL)d(rn@hXG7xF4R%TPZQ_s98h&OGL%ucQ*Z`-QnqEC$@P7vo*%<4t$A z4!e(RF1T&O!`c0J=-UD-c}hOpgJ;2biwk|#y~OOm%`|dCO?>e#D|`hVvQ2mfXS`vV zV^MpoL#MAATuoaVZ}60Wp(*&gww6#;XuD+XPy#2 zLqqIe1OF-C-Nh>G@Pz^MTRjhmqx++$i~Qr0R4w*b!T%HaC#6!Cbpuy$HJ*K^PaW9y zL?1c@Z0!@QSpRw8c_Ai;&z#L(5AmY8h{F|)cd$>r&?`36_(%J(@BJBh%t-%Y+^5*$xxr9#C>C{+}!9)F=DtzzvR5G z8@;;YLdB2Tlyt}bN01)+esTRvJ|I`ewF6(JnY?7pL^pbl^Pa+Sd`Z19?B#S-&}@$x zCGSd*54xk?u9#Eukqki{X)X2fpmOx$2mU(d2W>nKtjR5w^iIeN+|v$c(au6^7wdQwfjh3N6v( zNeEToIxK#OSaBz72ZttEO;@nVQ>WI3fWmp5Y zx(zhw@Qgd}GG`88nSHp3{JF$|ErM407aPCTsZ!y(SyPrQLaE zh;w4e30G?0;S0O_3TJC`H+qm~T#}1=<{-{L(EZW}CH+bowlAU^BFEU;?norNAFeDU6ws(*}6r`f>PY};I6+vF5F4os|4Qv^Tn zL~vf=u@ZDu`28`CUS5IDb`ve}uvZL)m&1>#Npo?(bre~hg75jy?&732*zsZTw|ZkK zreYU(d=b1q9*z@__X?p0=#TF@wm>Xh7(}grf6SHb;<`ft6pUxG$ss@S95f#eS|a~% zNfo=q`qHRCj&y{oZKM(=Apdv{q$V-w-)eX5Y%I|!e>mL7Dc>N@MN)}5C2 zhIi?=w~S#w^#h)ztxdH{WDgb80X?0kyX%#d_d&gbne_7=A6dqyJ+vRYWB+~+Cdk`z z(_S2)+HW|+G|aVH*fW=s5yN&`mO^<}7J0zqV^P>i!F4lsGw;Kz@r!}DW={&00bAP! zL&YF9Jo8$pLM2Q^;|^-yfxPzhnM<`5YgTxiZ%n!(B5g{Mu!Np&WG>S ziNoUD>0wm77r7>l^UY%X%PYK=0)?a!7;MZ^S2o{?vS$ z3k^B@LwpQw=ec9hmOogRZNUsGbUB{=s|}!M;!QuGvAK6o6Bdh}Z3T9br)D){P2OR@ z95wgteeese>p^ev9{q0KifN<2JrgskcZsIV%E6s<=U^V}TZbLM467)|i|S^b5hpK( z{?Jb!dhL2yv`A1={T6;y=le@BVmUPPFhg$`sl`q^KwnwQpBBxj6wg21L!+=K@Nb+~ z^tmAWsFz1+Uy&1fUQwrv&L!J7&d_@=7edRks0U(r)A!C|S$sMT#@s8l`8=^}F7&sd zNxsp1j+e98@CBQZy{ZGiKy7O}@)e5lbS^qD`5X73_UTcQUP&~rS?K}_YJa-;Bt6ImY> zV*5RGoc51nm*PC=40>4Gua06bQdHz(?LijMj-CrXh$VDa#+Q|g(U-7`a2a!GuX@5x zT@Pw34s)GOc(KjyLL!W!V%lprN znz{q;nm2Z`RlV|PDc)HJHuwn#m*!Ff+k-`| zn<+H+BJzgqL2*rI_^OmTQ_~Sw#c^@*WT@~exN5gBc@Nq>|{3Gi2^fPSdnm-V*e6-}}*h z>z=}=U~ml91XIREBW_A2`dx2Apw>QO-Lyj|aGCQ%G!?CB62u})Q3sz%> zUH53rbqq$awgaJ2jM(nGVj4?W6G}C>j|c6Sva{Irn~56Z=9JBB4tloLJJ6f0ab+Ks z0ThmRJiL0?_%Gm@;9Puleh3>9?~5}ec&h87SQGGKn(JX^@F^%{%r& zy8)i`&^(-~8u#UW#HjsG+6ZW(AT z4A_`QhIqdlWH%Sz#N<*D{0EQsTqjz0&nDqEX1BMK#e4NL$lx+CbUH7d>Xb^!h~e76 zFXEFeN#uy!TN2w8&u=_U-S15Edv;;H5@TsPazgIHVQl@%XxbEo{M=_8+dU_etZ*&c zxPV>8?&=QQm-byYv4~=L)8qLn`sK=!>xNJ(8}ua(`?5w;gXlP7*!ycZ3(gB5z0KG) zFi2uWL;UH{0`x3mvzTc!KU#%<^Zir%p`-3g)5^g0op6x7p6Wy4?Nro$;vrTu!<&qj zdr(;0e0FyZ>T~oHhh^n3=Y^isS?x{QAEMccy&jZ^ozMsE`?1=?o-_{p=`+VX1y+L{ zbl|4awN*E=lDHfeneoDRM+xU+K`TVBClbz0vWrBGX5jzDxsewW&IISt=MTsO^M;BK z^KvO1Ik|b3msldprf|eq%Gv^PO8X3|LH+QZdn~Sm9-uE`_`wc+_A&%K%0u|sp&i)Z zmI>sI{4kXbW^u=3=~68GP`=r-@F4gShTwTQw}=_}M$$uk{<7sZW^p8(#+--0-hLHx zcpFOdaW)ybFNm$_9zqG|%l)*CV{6w0K_e3!ys24CbrCv9k1_k~c8I<9^Cx@o?&`i( zv)|BPZPY_aiKZvmr%S%1?d(qN8Xm3Y}q!rA)8 zOwnVKg!9hcMdCX2?lj+NSN2W3x#4e|=i4+ER~^Ws`|nZH>dzGGp3J3HkB~E+qeQ#$ z*)*-fnN*F=h#CE`KX@AckBh&F)#FpK?}J)br#Z{WOrjsT&NO9uAGW?v0=2+Q-CH$^ zncs?~wV|lPx6EZ>2cSzF03R0p&Ft)vNJ>nH--o%1J-Uw_VY~~@3=L)%>V?r=^b=0M zjA!k8htS$Q^bfb~Wu35tVzUNkjj2aj9qb%mpYKYgPA6FhXnd6^-KcK&^DKF&KUrSH z&R5k{76-m%C}!<1tZuNqGkoa+>W6{FRqPq``Cq@rT+OkPxjA{$j$7dU{JzL`VNcW# z|J#&Z$c{DjqN*o>#NQjqUd4LTBq!*f=H!UePf0jWT0UNMsW0KYx~;b;8zkX8aRzz_ zXV4n}&id2qvY%^wW0KA>i^Dz6j^ zEihNJTPDznNX#IECb3gBu{6&Ie&4TG zviqm8yWkHm8*NwiqS2FC|2Ug|NU@nDzv^GH~+7yuZVS8)9GI2fC#tC)mDA$kRWA zX@qJa8w;NrO?`C?l@LpF9n zD*f2!OrtM#VL~x9KTtpD&E#2Qc>=9N+*ZDx&CY*_rLk_fcWXB@``giE9OO)$3_Mxv zqe$9;{KFZBv&-O8)pWw_r&Aj1R|fqI)JC)G9cHrE!BmExWbo4ytak#qKj2{8|8$8B zJ|0Lhvv6J;e4EwO!aEmw6uW;tW-Y;Oo(%3q+5J~6&d8s;R8$W?MF7d;HT;N ziN%bA2I&g$cl|yvpAA05&Vrj_^pZ6UhyP|p0G(c1&bq{-_MQqwq~xV+Osa%)`qFh` z$1n-!uu}m7l}Mb%>RnC5{a+=1oxSBwac+);bJy+u;;11z{=&J)MJ3Lt&Z9|haMo)Y zB2FsHrPAB*9NexJHz7YiJL63Em%S40nr2Wk>e~yfDPwpKK14kxB=usp8NXMYrZg!2r<;ed!&O$qVjlZZ&24Z@(vP#eT3@=2 zyxBxN%@%ZpFE{w;Dm>3`m%$$2r6cqy%yFf+kM8%|Y=f_(l+^!r7VVJLmxOTM@~m- zBUlOccxljUPAmyzmS=sa$_Vw`iCb84m@l1;j3&4I-a;Q}p;fF-4*tkR8H=2sR_oE{ zP?2*`*hngbj#2gOOKWXRcalZUsi`v>=I{UI{B%o%klMcpT1xQQYb%UY&!g|){J!M| zVF)O zP}un~hU&K?!!)X1SdZ+3QfJhZ>RPaNArVxEePCE?Z5C}4Mu$2;*Vj&$edj``*43GQ zOAVQgUl4Ve;zDcNnXwM&Ich%-=iB?1?6o3*7D6MpU$ACQI_QU}cPHE2MeGT3eCAuA zzs10Yb@cQj!&6>#`l>A(bx}_8c<9UTE@O94Ya03l`X0vxEE}FclP}aA1?yPuA0gK*h;~2=5$u4hdD}qh z1g+nX^fs&wvU^5DtFdKzCpMsW069W$ZR6LK^;!WBum#ZEX!Kx{a5lVs$BW)?=)umt zkkcUaFW<22#uO9e)VP%&?P$}9`4srlQ|Jh9wbf##gMI19E98imKM-F36xZa>8=r9b zw?xjWoV~Tsd#kwh3ri{;)^H_XHeDaCE5ZVN)S2U953cP{$I{-E~E*sh8Dpe z3w7o*GTnty*i8(cGRVycgKmkl#tM0BZ+>1 z^9Z$>LNC;x4ZM(RT)9G+{w9WQfb%5nox;|_C_2B#iN*$c2nq1-I*7Hs>xdx1a&Z_H zHO5_!TdXj{B!nb3SVQ_}3e(|9Ft8&uZ6QTM>{NK+LjO}cxI*Y36+q@2+^AKyQn=DN z0DTtjbiwk3Alcwg70cm&9&<|Ao8yNbIxm_iIVJQ!X3b`-vxgFn38&}Csaan?%Iv5R z>Xg3JTL*fe+5+LN#FyT~*HxR`h0DK0&ZX)`bmX?kd7VKa?Z>%W#m(OP0)0fDfNH#> zX&ueHC33#`<~5D`%lZDPrZUx<{C_#G+ny&38&yP6;9ULMM2H6Gm*70gu#K?RGMhS| zccNz-8woq!rjseoCi`SR_@6gZ$Q_)w-uume_>)NeuxI{wwW*M`IG(11v*c?_A^K?y zd4Tit&K-pu`1h@HbfO7Ob%fdQ(s>o@L{~rP3d60!C<*&^_nMKy+aVz|&dQnQ%cR1u z`9XB*o9d3tLhv04JxLQ+(ny#gIPVLfH8_Ly-8)AZ4A18!sIjkExIj?#Kx_r)h@ypp zXRaUZeGUDS-U6Xhy&PUmJ~VoOwGg-fnPPhI75g?t=!ZSV!+wFZzfK~2midyRYbbrr z{KN$r2xpxJ&i_|u zzGy_5AQ@XkSHZd1!3epEc{Bq1XRQg}`RS9g$r_xkeU9_1UZm3!aCW(t#YbF8q21uT z(m$H_`j$wyF`qTY1@nQ{@l=U*W7BYdUhOWP!#hy-J1gh^#6{6+2Yh#%{P?tOn9m_N zv%d-8>$xy`i+}6MyAb|lrw|I1Ay=z5lD9PpqK$RXp!AF5*R{vI!&%#1lE@!l3O$LT z8*ROn#LHj%Q!08>^`+gKte7v9R=3mZVHyZxue16{ksGvth)CTkFL8g%q1>+3QC?;LRsw zlRG%;wvOdZ9;cHnI2ZR`%GaJtAtP|!v%C-Q`7V)+z&SnSnIaLs5Ro3ZtLl)VSamIi zGQn2u=}g6<;3#^r3Fqt1Pb)oFL{KjutTWo}DsK!5qj?ulN4?(2^xThNN*sy1Kivw` z-d%%e^GWm*DLP2(-l4zX4DR*P$4k^GfDYpf)~4xt$=56XbU6+6AT+2x^`jo9DBT7o6MvaZlPX;d+jlYmPyq<2h!j|XIdcNEgg0~0GSEUwt0I? zEqdWTrQVfJ&kB~Nl=+jHFYftnL`us|;5Rc8JrYM^rDM@^+#0n`mrHTd?KkE0GR=pK z3uB~ChG70qPD|g0NoU4mt*8y4?;YgQvmKDHR}?~HdaaN;zCgWtRSdnEqM+)(oV7l8 zlSTgJ{97?nrir_C)!G}ln3LI{2A~>W^q(Soog~&bW?xwJpPVno+!6%TpZ&o()@Y(| zOe2rJfb$~X`}_l0HeCeg9trOJ=G*Bs0-WR8x8~2X6e4h5UN}XOdOwl$!g06Qv&huT zB%ZoqpS-?2T@rXShE`z?o7HPbZT3V_$5r?(oHLaEpa?R@ey{I@rP7|?;0byF_c$9| zrR~atX>uoMC)S2Z?ctUEH5UCxk~C>)P5`xnR{YAmB584BWY}TPdd99&+CSc(YB#x~ zr|hV7d4GR81RZ48(vwn`oql8ne>~Z!Q_{ug%hUHk{~J3dJ=#-FZ;@>?=5nPp+5^5~ z2LecbEnmu^k9!dA;D_!EkeVWcEbCP?87G}3yF21@Fn#wxS<>J2S$&$BtVAwyR*RZ0 zGlMov#d%<-MY0Vk;#%{(%`hjWVacYYQ)e*ovn zvC;hfnsm|v=gVqFd_`pnWnur^vBg2fjN6Iy0yWRef#H?i4CAR0_Q?V9eI)ZM;Rm%I zcLc2tO15o{qB}PDj+$vmee@$}7Us6c!=X~&JE8OvHUGza$q z{=o;Dt}b5E0@Qc{UC;;ABvjh+8EV7uI(TIoC;j2>Pj++NDf~-{bRPV6-u^+qN_B>G z(h5H^kMn}AEK};AFQ=WWeW>T{H0cKoIeo{uW!uCAY0WxcL6>JPziWf65iUG(aWb;2L5JaPu-a^!R`G|Q%LSU>HpPw=MT{0jTJ7wzSI zRe1`v1?P<)%=!5>iCCL)f7-J-KYBzwUD=2I+nPd!a0us=)lSqt%3PsX7e!%8qom>#IS&fg^V@Zvp#dxA^TUPvMpK6L9+Fm*3hNJkE+1A`5S#NyToh$ft;Jhvv z_amb-c;Dgil)c9Zb^Iva`(O+e+TpIMM<8EfA4SvVLVxzeo4?lvHM)7okCuAy53)k3 zXgBuQo^Jd{!h8G|9kE( zQf!mSo{HyggDy_8d08UomX;p>$vNX*8y2&-h*H40>UNmWxoaNvyzfNQ>*fd%lc6gB z=hEq;1ed$%bPb%ns`?1gLJD;N=Z@jsgf4dyX?hUWflb=Nxl!@xk9DHABf1K<$Q<0i z9QR)Z-G$SuqNwLg{4P%R61J&DkjHeK@%0A?&4NP74Qt4ilp(_Vfx&d*j1zq=9Vtwk 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