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 000000000..be996e119 Binary files /dev/null and b/rtdata/rt_splash_5.png differ 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;