diff --git a/rtdata/languages/default b/rtdata/languages/default
index c6c7d08ba..60b68bbd7 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -731,6 +731,8 @@ HISTORY_MSG_LOCALCONTRAST_ENABLED;Local Contrast
HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness
HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius
HISTORY_MSG_METADATA_MODE;Metadata copy mode
+HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction
+HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
HISTORY_MSG_TM_FATTAL_ANCHOR;HDR TM - Anchor
HISTORY_NEWSNAPSHOT;Add
HISTORY_NEWSNAPSHOT_TOOLTIP;Shortcut: Alt-s
@@ -1724,7 +1726,14 @@ TP_PREPROCESS_HOTPIXFILT;Hot pixel filter
TP_PREPROCESS_HOTPIXFILT_TOOLTIP;Tries to suppress hot pixels.
TP_PREPROCESS_LABEL;Preprocessing
TP_PREPROCESS_LINEDENOISE;Line noise filter
+TP_PREPROCESS_LINEDENOISE_DIRECTION;Direction
+TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH;Both
+TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL;Horizontal
+TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Horizontal only on PDAF rows
+TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Vertical
TP_PREPROCESS_NO_FOUND;None found
+TP_PREPROCESS_PDAFLINESFILTER;PDAF lines filter
+TP_PREPROCESS_PDAFLINESFILTER_TOOLTIP;Tries to suppress stripe noise caused by on-sensor PDAF pixels, occurring with some Sony mirrorless cameras on some backlit scenes with visible flare.
TP_PRSHARPENING_LABEL;Post-Resize Sharpening
TP_PRSHARPENING_TOOLTIP;Sharpens the image after resizing. Only works when the "Lanczos" resizing method is used. It is impossible to preview the effects of this tool. See RawPedia for usage instructions.
TP_RAWCACORR_AUTO;Auto-correction
diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt
index 230c0957a..db0411e6c 100644
--- a/rtengine/CMakeLists.txt
+++ b/rtengine/CMakeLists.txt
@@ -118,6 +118,7 @@ set(RTENGINESOURCEFILES
tmo_fattal02.cc
iplocalcontrast.cc
histmatching.cc
+ pdaflinesfilter.cc
)
if(LENSFUN_HAS_LOAD_DIRECTORY)
diff --git a/rtengine/camconst.cc b/rtengine/camconst.cc
index 469d18571..3d4342ed9 100644
--- a/rtengine/camconst.cc
+++ b/rtengine/camconst.cc
@@ -19,7 +19,7 @@ namespace rtengine
extern const Settings* settings;
-CameraConst::CameraConst()
+CameraConst::CameraConst() : pdafOffset(0)
{
memset(dcraw_matrix, 0, sizeof(dcraw_matrix));
memset(raw_crop, 0, sizeof(raw_crop));
@@ -310,6 +310,35 @@ CameraConst::parseEntry(void *cJSON_, const char *make_model)
}
}
+ ji = cJSON_GetObjectItem(js, "pdafPattern");
+
+ if (ji) {
+ if (ji->type != cJSON_Array) {
+ fprintf(stderr, "\"pdafPattern\" must be an array\n");
+ goto parse_error;
+ }
+
+ for (ji = ji->child; ji != nullptr; ji = ji->next) {
+ if (ji->type != cJSON_Number) {
+ fprintf(stderr, "\"pdafPattern\" array must contain numbers\n");
+ goto parse_error;
+ }
+
+ cc->pdafPattern.push_back(ji->valueint);
+ }
+ }
+
+ ji = cJSON_GetObjectItem(js, "pdafOffset");
+
+ if (ji) {
+ if (ji->type != cJSON_Number) {
+ fprintf(stderr, "\"pdafOffset\" must contain a number\n");
+ goto parse_error;
+ }
+
+ cc->pdafOffset = ji->valueint;
+ }
+
return cc;
parse_error:
@@ -345,6 +374,36 @@ CameraConst::get_dcrawMatrix()
return dcraw_matrix;
}
+bool
+CameraConst::has_pdafPattern()
+{
+ return pdafPattern.size() > 0;
+}
+
+std::vector
+CameraConst::get_pdafPattern()
+{
+ return pdafPattern;
+}
+
+void
+CameraConst::update_pdafPattern(const std::vector &other)
+{
+ if (other.empty()) {
+ return;
+ }
+ pdafPattern = other;
+}
+
+void
+CameraConst::update_pdafOffset(int other)
+{
+ if (other == 0) {
+ return;
+ }
+ pdafOffset = other;
+}
+
bool
CameraConst::has_rawCrop()
{
@@ -678,6 +737,8 @@ CameraConstantsStore::parse_camera_constants_file(Glib::ustring filename_)
// deleting all the existing levels, replaced by the new ones
existingcc->update_Levels(cc);
existingcc->update_Crop(cc);
+ existingcc->update_pdafPattern(cc->get_pdafPattern());
+ existingcc->update_pdafOffset(cc->get_pdafOffset());
if (settings->verbose) {
printf("Merging camera constants for \"%s\"\n", make_model.c_str());
diff --git a/rtengine/camconst.h b/rtengine/camconst.h
index 47c8d8bee..60e17201b 100644
--- a/rtengine/camconst.h
+++ b/rtengine/camconst.h
@@ -24,7 +24,8 @@ private:
int white_max;
std::map mLevels[2];
std::map mApertureScaling;
-
+ std::vector pdafPattern;
+ int pdafOffset;
CameraConst();
static bool parseLevels(CameraConst *cc, int bw, void *ji);
static bool parseApertureScaling(CameraConst *cc, void *ji);
@@ -33,8 +34,11 @@ private:
public:
static CameraConst *parseEntry(void *cJSON, const char *make_model);
bool has_dcrawMatrix(void);
+ bool has_pdafPattern(void);
void update_dcrawMatrix(const short *other);
const short *get_dcrawMatrix(void);
+ std::vector get_pdafPattern();
+ int get_pdafOffset() {return pdafOffset;}
bool has_rawCrop(void);
void get_rawCrop(int& left_margin, int& top_margin, int& width, int& height);
bool has_rawMask(int idx);
@@ -43,6 +47,8 @@ public:
int get_WhiteLevel(int idx, int iso_speed, float fnumber);
void update_Levels(const CameraConst *other);
void update_Crop(CameraConst *other);
+ void update_pdafPattern(const std::vector &other);
+ void update_pdafOffset(int other);
};
class CameraConstantsStore
diff --git a/rtengine/camconst.json b/rtengine/camconst.json
index bd4931ad7..cfe7412d8 100644
--- a/rtengine/camconst.json
+++ b/rtengine/camconst.json
@@ -82,6 +82,11 @@ Examples:
// It is useful after detecting the masked areas, to not fully use these areas but leave a border of 2-4 pixels
// instead, to take care of possible light leaks from the light sensing area to the optically black (masked)
// area or sensor imperfections at the outer borders.
+
+ // list of indices of the rows with on-sensor PDAF pixels, for cameras that have such features. The indices here form a pattern that is repeated for the whole height of the sensor. The values are relative to the "pdafOffset" value (see below)
+ "pdafPattern" : [ 0,12,36,54,72,90,114,126,144,162,180,204,216,240,252,270,294,306,324,342,366,384,396,414,432,450,474,492,504,522,540,564,576,594,606,630 ],
+ // index of the first row of the PDAF pattern in the sensor (0 is the topmost row). Allowed to be negative for convenience (this means that the first repetition of the pattern doesn't start from the first row)
+ "pdafOffset" : 3
},
{
@@ -2236,14 +2241,33 @@ Camera constants:
"make_model": "Sony ILCE-6000",
"dcraw_matrix": [ 5991,-1456,-455,-4764,12135,2980,-707,1425,6701 ], // adobe dcp d65
"raw_crop": [ 0, 0, 6024, 4024 ],
- "ranges": { "black": 512, "white": 16300 }
+ "ranges": { "black": 512, "white": 16300 },
+ // detected by hand, using the picture from https://www.dpreview.com/forums/thread/3923513
+ // P 11 P 23 P 17 P 17 P 17 P 23 P 11 P 17 P 17 P 17 P 23 P 11 P 23 P 11 P 17 P 23 P 11 P 17 P 17 P 23 P 17 P 11 P 17 P 17 P 17 P 23 P 17 P 11 P 17 P 17 P 23 P 11 P 17 P 11 P 23
+ "pdafPattern" : [ 0,12,36,54,72,90,114,126,144,162,180,204,216,240,252,270,294,306,324,342,366,384,396,414,432,450,474,492,504,522,540,564,576,594,606,630 ],
+ "pdafOffset" : 3
},
{ // Quality A
"make_model": [ "Sony ILCE-6300","Sony ILCE-6500" ],
"dcraw_matrix": [ 5973,-1695,-419,-3826,11797,2293,-639,1398,5789 ], // DNG_v9.8 D65
"raw_crop": [ 0, 0, 6024, 4024 ],
- "ranges": { "black": 512, "white": 16300 }
+ "ranges": { "black": 512, "white": 16300 },
+ // contributed by Horshak from https://www.dpreview.com/forums/post/60873077
+ "pdafPattern" : [ 0,12,36,54,72,90,114,126,144,162,180,204,216,240,252,270,294,306,324,342,366,384,396,414,432,450,474,492,504,522,540,564,576,594,606,630 ],
+ "pdafOffset" : 3
+ },
+
+ { // Quality C, only pdaf data
+ "make_model": "Sony ILCE-7M3",
+ // A7III, from https://www.dpreview.com/forums/post/60843139
+ // in the original post:
+ // P 5 P 17 P 11 P 11 P 17 P 11 P 5 P 11 P 11 P 11 P 17 P 11 P 5 P 11 P 11 P 17 P 5 P 11 P 17 P 5 P 17 P 5 P 11 P 11 P 11 P 17 P 5 P 11 P 11 P 11 P 5 P 17 P 5 P 17 P 11
+ //
+ // rotated to match the start of the frame
+ // P 11 P 11 P 11 P 17 P 11 P 5 P 11 P 11 P 17 P 5 P 11 P 17 P 5 P 17 P 5 P 11 P 11 P 11 P 17 P 5 P 11 P 11 P 11 P 5 P 17 P 5 P 17 P 11 P 5 P 17 P 11 P 11 P 17 P 11 P 5
+ "pdafPattern" : [ 0,12,24,36,54,66,72,84,96,114,120,132,150,156,174,180,192,204,216,234,240,252,264,276,282,300,306,324,336,342,360,372,384,402,414,420],
+ "pdafOffset" : 9
},
{ // Quality A, correction for frame width
@@ -2263,7 +2287,10 @@ Camera constants:
"make_model": [ "Sony ILCE-7RM2", "Sony DSC-RX1RM2" ],
"dcraw_matrix": [ 6629,-1900,-483,-4618,12349,2550,-622,1381,6514 ], // DNG_v9.1.1 D65
"raw_crop": [ 0, 0, -36, 0 ], // full raw frame 8000x5320 - 36 rightmost columns are garbage
- "ranges": { "black": 512, "white": 16300 }
+ "ranges": { "black": 512, "white": 16300 },
+ // PDAF info provided by Horshack with the rawshack tool (http://testcams.com/rawshack/)
+ "pdafPattern" : [ 0,24,36,60,84,120,132,156,192,204,240,252,276,300,324,360,372,396,420 ],
+ "pdafOffset" : 31
},
{ // Quality C, color matrix copied from ILCE-9, LongExposures 2-3sec only
@@ -2299,7 +2326,12 @@ Camera constants:
"make_model": "Sony ILCE-9",
"dcraw_matrix": [ 6389,-1703,-378,-4562,12265,2587,-670,1489,6550 ], // DNG_v9.12 D65
"raw_crop": [ 8, 8, 6008, 4008 ], // full raw frame 6048x4024 Dcraw auto identify 6024x4024, jpeg 12,12,6000x4000
- "ranges": { "black": 512, "white": 16300 }
+ "ranges": { "black": 512, "white": 16300 },
+ // the A9 is the same as the A7III, rotated of 1 position
+ // source: https://www.dpreview.com/forums/post/60857788
+ // P 11 P 11 P 11 P 17 P 11 P 5 P 11 P 11 P 17 P 5 P 11 P 17 P 5 P 17 P 5 P 11 P 11 P 11 P 17 P 5 P 11 P 11 P 11 P 5 P 17 P 5 P 17 P 11 P 5 P 17 P 11 P 11 P 17 P 11 P 5
+ "pdafPattern" : [ 0,12,24,36,54,66,72,84,96,114,120,132,150,156,174,180,192,204,216,234,240,252,264,276,282,300,306,324,336,342,360,372,384,402,414,420 ],
+ "pdafOffset" : -7
},
{ // Quality B, correction for frame width
diff --git a/rtengine/cfa_linedn_RT.cc b/rtengine/cfa_linedn_RT.cc
index 0f9e27634..2bb3649b6 100644
--- a/rtengine/cfa_linedn_RT.cc
+++ b/rtengine/cfa_linedn_RT.cc
@@ -39,7 +39,7 @@ using namespace rtengine;
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-void RawImageSource::CLASS cfa_linedn(float noise)
+void RawImageSource::CLASS cfa_linedn(float noise, bool horizontal, bool vertical, const CFALineDenoiseRowBlender &rowblender)
{
// local variables
int height = H, width = W;
@@ -68,11 +68,10 @@ void RawImageSource::CLASS cfa_linedn(float noise)
{
// allocate memory and assure the arrays don't have same 64 byte boundary to avoid L1 conflict misses
- float *cfain = (float*)malloc(4 * TS * TS * sizeof(float) + 3 * 16 * sizeof(float));
- float *cfablur = (cfain + (TS * TS) + 1 * 16);
- float *cfadiff = (cfain + (2 * TS * TS) + 2 * 16);
- float *cfadn = (cfain + (3 * TS * TS) + 3 * 16);
-
+ float *cfain = (float*)malloc(3 * TS * TS * sizeof(float) + 2 * 16 * sizeof(float));
+ float *cfadiff = (cfain + (1 * TS * TS) + 1 * 16);
+ float *cfadn = (cfain + (2 * TS * TS) + 2 * 16);
+ float cfablur[TS];
float linehvar[4], linevvar[4], noisefactor[4][8][2], coeffsq;
float dctblock[4][8][8];
@@ -130,19 +129,19 @@ void RawImageSource::CLASS cfa_linedn(float noise)
//gaussian blur of CFA data
for (int rr = 8; rr < numrows - 8; rr++) {
- for (int indx = rr * TS; indx < rr * TS + numcols; indx++) {
- cfablur[indx] = gauss[0] * cfain[indx];
+ for (int indx = rr * TS, indxb = 0; indx < rr * TS + numcols; indx++, indxb++) {
+ cfablur[indxb] = gauss[0] * cfain[indx];
for (int i = 1; i < 5; i++) {
- cfablur[indx] += gauss[i] * (cfain[indx - (2 * i) * TS] + cfain[indx + (2 * i) * TS]);
+ cfablur[indxb] += gauss[i] * (cfain[indx - (2 * i) * TS] + cfain[indx + (2 * i) * TS]);
}
}
- for (int indx = rr * TS + 8; indx < rr * TS + numcols - 8; indx++) {
- cfadn[indx] = gauss[0] * cfablur[indx];
+ for (int indx = rr * TS + 8, indxb = 8; indx < rr * TS + numcols - 8; indx++, indxb++) {
+ cfadn[indx] = gauss[0] * cfablur[indxb];
for (int i = 1; i < 5; i++) {
- cfadn[indx] += gauss[i] * (cfablur[indx - 2 * i] + cfablur[indx + 2 * i]);
+ cfadn[indx] += gauss[i] * (cfablur[indxb - 2 * i] + cfablur[indxb + 2 * i]);
}
cfadiff[indx] = cfain[indx] - cfadn[indx]; // hipass cfa data
@@ -183,14 +182,14 @@ void RawImageSource::CLASS cfa_linedn(float noise)
}
//horizontal lines
- if (noisevarm4 > (linehvar[0] + linehvar[1])) { //horizontal lines
+ if (horizontal && noisevarm4 > (linehvar[0] + linehvar[1])) { //horizontal lines
for (int i = 1; i < 8; i++) {
dctblock[0][0][i] *= 0.5f * (noisefactor[0][i][1] + noisefactor[1][i][1]); //or should we use MIN???
dctblock[1][0][i] *= 0.5f * (noisefactor[0][i][1] + noisefactor[1][i][1]); //or should we use MIN???
}
}
- if (noisevarm4 > (linehvar[2] + linehvar[3])) { //horizontal lines
+ if (horizontal && noisevarm4 > (linehvar[2] + linehvar[3])) { //horizontal lines
for (int i = 1; i < 8; i++) {
dctblock[2][0][i] *= 0.5f * (noisefactor[2][i][1] + noisefactor[3][i][1]); //or should we use MIN???
dctblock[3][0][i] *= 0.5f * (noisefactor[2][i][1] + noisefactor[3][i][1]); //or should we use MIN???
@@ -198,14 +197,14 @@ void RawImageSource::CLASS cfa_linedn(float noise)
}
//vertical lines
- if (noisevarm4 > (linevvar[0] + linevvar[2])) { //vertical lines
+ if (vertical && noisevarm4 > (linevvar[0] + linevvar[2])) { //vertical lines
for (int i = 1; i < 8; i++) {
dctblock[0][i][0] *= 0.5f * (noisefactor[0][i][0] + noisefactor[2][i][0]); //or should we use MIN???
dctblock[2][i][0] *= 0.5f * (noisefactor[0][i][0] + noisefactor[2][i][0]); //or should we use MIN???
}
}
- if (noisevarm4 > (linevvar[1] + linevvar[3])) { //vertical lines
+ if (vertical && noisevarm4 > (linevvar[1] + linevvar[3])) { //vertical lines
for (int i = 1; i < 8; i++) {
dctblock[1][i][0] *= 0.5f * (noisefactor[1][i][0] + noisefactor[3][i][0]); //or should we use MIN???
dctblock[3][i][0] *= 0.5f * (noisefactor[1][i][0] + noisefactor[3][i][0]); //or should we use MIN???
@@ -252,12 +251,17 @@ void RawImageSource::CLASS cfa_linedn(float noise)
free(cfain);
// copy temporary buffer back to image matrix
- #pragma omp for
+ #pragma omp for schedule(dynamic,16)
- for(int i = 0; i < height; i++)
- for(int j = 0; j < width; j++) {
- rawData[i][j] = RawDataTmp[i * width + j];
+ for(int i = 0; i < height; i++) {
+ float f = rowblender(i);
+ if (f > 0.f) {
+ float f2 = 1.f - f;
+ for(int j = 0; j < width; j++) {
+ rawData[i][j] = f * RawDataTmp[i * width + j] + f2 * rawData[i][j];
+ }
}
+ }
} // end of parallel processing
@@ -318,14 +322,14 @@ void RawImageSource::CLASS cfa_linedn(float noise)
/* Cn_kI = sqrt(2.0/n) * sin(pi/2*k/n) */
/* Wn_kR = cos(pi/2*k/n) */
/* Wn_kI = sin(pi/2*k/n) */
-#define C8_1R 0.49039264020161522456
-#define C8_1I 0.09754516100806413392
-#define C8_2R 0.46193976625564337806
-#define C8_2I 0.19134171618254488586
-#define C8_3R 0.41573480615127261854
-#define C8_3I 0.27778511650980111237
-#define C8_4R 0.35355339059327376220
-#define W8_4R 0.70710678118654752440
+#define C8_1R 0.49039264020161522456f
+#define C8_1I 0.09754516100806413392f
+#define C8_2R 0.46193976625564337806f
+#define C8_2I 0.19134171618254488586f
+#define C8_3R 0.41573480615127261854f
+#define C8_3I 0.27778511650980111237f
+#define C8_4R 0.35355339059327376220f
+#define W8_4R 0.70710678118654752440f
void RawImageSource::ddct8x8s(int isgn, float a[8][8])
diff --git a/rtengine/green_equil_RT.cc b/rtengine/green_equil_RT.cc
index 90c412871..934b7df4a 100644
--- a/rtengine/green_equil_RT.cc
+++ b/rtengine/green_equil_RT.cc
@@ -89,7 +89,7 @@ void RawImageSource::green_equilibrate_global(array2D &rawData)
}
//void green_equilibrate()//for dcraw implementation
-void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
+void RawImageSource::green_equilibrate(const GreenEqulibrateThreshold &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
@@ -119,7 +119,7 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
}
constexpr float eps = 1.f; //tolerance to avoid dividing by zero
- const float thresh6 = 6 * thresh;
+ // const float thresh6 = 6 * thresh;
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Fill G interpolated values with border interpolation and input values
@@ -136,8 +136,8 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
#ifdef __SSE2__
vfloat zd5v = F2V(0.5f);
vfloat onev = F2V(1.f);
- vfloat threshv = F2V(thresh);
- vfloat thresh6v = F2V(thresh6);
+ // vfloat threshv = F2V(thresh);
+ // vfloat thresh6v = F2V(thresh6);
vfloat epsv = F2V(eps);
#endif
#ifdef _OPENMP
@@ -165,7 +165,13 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
vfloat c1 = (vabsf(o1_1 - o1_2) + vabsf(o1_1 - o1_3) + vabsf(o1_1 - o1_4) + vabsf(o1_2 - o1_3) + vabsf(o1_3 - o1_4) + vabsf(o1_2 - o1_4));
vfloat c2 = (vabsf(o2_1 - o2_2) + vabsf(o2_1 - o2_3) + vabsf(o2_1 - o2_4) + vabsf(o2_2 - o2_3) + vabsf(o2_3 - o2_4) + vabsf(o2_2 - o2_4));
- vmask mask1 = vmaskf_lt(c1 + c2, thresh6v * vabsf(d1 - d2));
+ vfloat tfv;
+ for (int k = 0; k < 4; ++k) {
+ tfv[k] = thresh(rr, cc + 2 * k);
+ }
+ vfloat tf6v = F2V(6.f) * tfv;
+
+ vmask mask1 = vmaskf_lt(c1 + c2, tf6v * vabsf(d1 - d2));
if (_mm_movemask_ps((vfloat)mask1)) { // if for any of the 4 pixels the condition is true, do the maths for all 4 pixels and mask the unused out at the end
//pixel interpolation
@@ -188,7 +194,7 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
vfloat ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw);
- vfloat val = vself(vmaskf_lt(ginterp - gin, threshv * (ginterp + gin)), zd5v * (ginterp + gin), gin);
+ vfloat val = vself(vmaskf_lt(ginterp - gin, tfv * (ginterp + gin)), zd5v * (ginterp + gin), gin);
val = vself(mask1, val, gin);
STC2VFU(rawData[rr][cc], val);
}
@@ -213,7 +219,9 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
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));
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));
- if (c1 + c2 < thresh6 * fabs(d1 - d2)) {
+ float tf = thresh(rr, cc);
+
+ if (c1 + c2 < 6 * tf * fabs(d1 - d2)) {
//pixel interpolation
float gin = cfa[rr][cc >> 1];
@@ -234,7 +242,7 @@ void RawImageSource::green_equilibrate(float thresh, array2D &rawData)
float ginterp = (gse * wtse + gnw * wtnw + gne * wtne + gsw * wtsw) / (wtse + wtnw + wtne + wtsw);
- if (ginterp - gin < thresh * (ginterp + gin)) {
+ if (ginterp - gin < tf * (ginterp + gin)) {
rawData[rr][cc] = 0.5f * (ginterp + gin);
}
}
diff --git a/rtengine/pdaflinesfilter.cc b/rtengine/pdaflinesfilter.cc
new file mode 100644
index 000000000..92fd68b41
--- /dev/null
+++ b/rtengine/pdaflinesfilter.cc
@@ -0,0 +1,302 @@
+/* -*- C++ -*-
+ *
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2018 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 "pdaflinesfilter.h"
+#include "settings.h"
+#include
+#include "camconst.h"
+
+namespace rtengine {
+
+extern const Settings *settings;
+
+
+namespace {
+
+class PDAFGreenEqulibrateThreshold: public RawImageSource::GreenEqulibrateThreshold {
+ static constexpr float BASE_THRESHOLD = 0.5f;
+ static constexpr int TILE_SIZE = 200;
+ static constexpr float AREA = TILE_SIZE * TILE_SIZE;
+ static constexpr int PIXEL_COUNT_FACTOR = 12;
+
+public:
+ PDAFGreenEqulibrateThreshold(int w, int h):
+ RawImageSource::GreenEqulibrateThreshold(BASE_THRESHOLD),
+ w_(w),
+ h_(h)
+ {
+ int ctiles = w_ / TILE_SIZE;
+ int rtiles = h_ / TILE_SIZE;
+ tiles_.resize(rtiles+1, std::vector(ctiles+1));
+ }
+
+ void processTiles()
+ {
+ for(size_t i = 0; i < tiles_.size(); ++i) {
+ for(size_t j = 0; j < tiles_[i].size(); ++j) {
+ tiles_[i][j] = tiles_[i][j] * PIXEL_COUNT_FACTOR / (AREA * AREA);
+ }
+ }
+ }
+
+ void increment(int row, int col)
+ {
+ auto &r = tiles_[row / TILE_SIZE];
+ ++r[col / TILE_SIZE];
+ }
+
+ float operator()(int row, int col) const
+ {
+ int y = row / TILE_SIZE;
+ int x = col / TILE_SIZE;
+
+ int cy = y * TILE_SIZE + TILE_SIZE/2;
+ int cx = x * TILE_SIZE + TILE_SIZE/2;
+
+ int x1 = col > cx ? x+1 : x-1;
+ int y1 = row > cy ? y+1 : y-1;
+
+ float fxy = tile_factor(y, x);
+ float f = 0.f;
+
+ if (x1 >= 0 && size_t(x1) < tiles_[y].size()) {
+ if (y1 >= 0 && size_t(y1) < tiles_.size()) {
+ // bilinear interpolation
+ float fx1y = tile_factor(y, x1);
+ float fx1y1 = tile_factor(y1, x1);
+ float fxy1 = tile_factor(y1, x);
+
+ // x direction
+ int d = std::abs(cx - col);
+ float f1 = fxy * (TILE_SIZE - d) + fx1y * float(d);
+ float f2 = fxy1 * (TILE_SIZE - d) + fx1y1 * float(d);
+ // y direction
+ d = std::abs(cy - row);
+ f = (f1 * (TILE_SIZE - d) + f2 * float(d));
+ } else {
+ float f2 = tile_factor(y, x1);
+ int d = std::abs(cx - col);
+ f = (fxy * float(TILE_SIZE - d) + f2 * float(d)) * TILE_SIZE;
+ }
+ } else if (y1 >= 0 && size_t(y1) < tiles_.size()) {
+ float f2 = tile_factor(y1, x);
+ int d = std::abs(cy - row);
+ f = (fxy * float(TILE_SIZE - d) + f2 * float(d)) * TILE_SIZE;
+ } else {
+ f = fxy * AREA;
+ }
+
+ return thresh_ * f;
+ }
+
+ void print() const
+ {
+ std::cout << "PDAFGreenEqulibrateThreshold:\n";
+ for (size_t row = 0; row < tiles_.size(); ++row) {
+ for (size_t col = 0; col < tiles_.size(); ++col) {
+ std::cout << " " << tile_factor(row, col);
+ }
+ std::cout << std::endl;
+ }
+ }
+
+private:
+ float tile_factor(int y, int x) const
+ {
+ return tiles_[y][x];
+ }
+
+ int w_;
+ int h_;
+ std::vector> tiles_;
+};
+
+
+class PDAFLineDenoiseRowFilter: public RawImageSource::CFALineDenoiseRowBlender {
+public:
+ PDAFLineDenoiseRowFilter(const std::vector &pattern, int offset):
+ pattern_(pattern),
+ offset_(offset)
+ {}
+
+ float operator()(int row) const
+ {
+ static constexpr float BORDER[] = { 1.f, 1.f, 0.8f, 0.5f, 0.2f };
+ static constexpr int BORDER_WIDTH = sizeof(BORDER)/sizeof(float) - 1;
+
+ if (!pattern_.empty()) {
+ int key = (row - offset_) % pattern_.back();
+ auto it = std::lower_bound(pattern_.begin(), pattern_.end(), key);
+
+ int b = *it;
+ int d = b - key;
+
+ if (it > pattern_.begin()) {
+ int b2 = *(it-1);
+ int d2 = key - b2;
+ float f = BORDER[std::min(std::min(d, d2), BORDER_WIDTH)];
+ return f;
+ } else {
+ float f = BORDER[std::min(d, BORDER_WIDTH)];
+ return f;
+ }
+ }
+ return 0.f;
+ }
+
+private:
+ std::vector pattern_;
+ int offset_;
+};
+
+} // namespace
+
+
+
+PDAFLinesFilter::PDAFLinesFilter(RawImage *ri):
+ ri_(ri),
+ W_(ri->get_width()),
+ H_(ri->get_height())
+{
+ gthresh_ = new PDAFGreenEqulibrateThreshold(W_, H_);
+
+ CameraConstantsStore* ccs = CameraConstantsStore::getInstance();
+ CameraConst *cc = ccs->get(ri_->get_maker().c_str(), ri_->get_model().c_str());
+
+ if (cc) {
+ pattern_ = cc->get_pdafPattern();
+ if(!pattern_.empty()) {
+ offset_ = cc->get_pdafOffset();
+ }
+ }
+}
+
+
+PDAFLinesFilter::~PDAFLinesFilter()
+{
+ delete gthresh_;
+}
+
+
+RawImageSource::GreenEqulibrateThreshold &PDAFLinesFilter::greenEqThreshold()
+{
+ return *gthresh_;
+}
+
+
+std::unique_ptr PDAFLinesFilter::lineDenoiseRowBlender()
+{
+ return std::unique_ptr(new PDAFLineDenoiseRowFilter(pattern_, offset_));
+}
+
+
+int PDAFLinesFilter::markLine(array2D &rawData, PixelsMap &bpMap, int y)
+{
+ rowmap_.clear();
+ rowmap_.resize((W_+1)/2, false);
+ int marked = 0;
+
+ for (int x = 1 + (ri_->FC(y, 0) & 1); x < W_-1; x += 2) {
+ const float
+ g0 = rawData[y][x],
+ g1 = rawData[y-1][x+1],
+ g2 = rawData[y+1][x+1],
+ g3 = rawData[y-1][x-1],
+ g4 = rawData[y+1][x-1];
+ if (g0 > max(g1, g2, g3, g4)) {
+ const float gu = g2 + g4;
+ const float gd = g1 + g3;
+ const float gM = max(gu, gd);
+ const float gm = min(gu, gd);
+ const float d = (gM - gm) / gM;
+ if (d < 0.2f && (1.f - (gm + gM)/(4.f * g0)) > std::min(d, 0.1f)) {
+ rowmap_[x/2] = true;
+ }
+ }
+ }
+
+ PDAFGreenEqulibrateThreshold *m = static_cast(gthresh_);
+
+ for (int x = 2 + (ri_->FC(y, 1) & 1); x < W_-2; x += 2) {
+ const int i = x/2;
+ if (rowmap_[i+1]) {
+ if (rowmap_[i]) {
+ if (rowmap_[i-1]) {
+ for (int xx = x-2; xx <= x+2; ++xx) {
+ if (!bpMap.get(xx, y)) {
+ bpMap.set(xx, y);
+ m->increment(y, xx);
+ ++marked;
+ }
+ }
+ }
+ } else {
+ x += 2;
+ }
+ } else {
+ x += 4;
+ }
+ }
+
+ return marked;
+}
+
+
+int PDAFLinesFilter::mark(array2D &rawData, PixelsMap &bpMap)
+{
+
+ if (pattern_.empty()) {
+ if (settings->verbose) {
+ std::cout << "no PDAF pattern known for " << ri_->get_maker() << " " << ri_->get_model() << std::endl;
+ }
+ return 0;
+ }
+
+ size_t idx = 0;
+ int off = offset_;
+
+ int found = 0;
+ for (int y = 1; y < H_-1; ++y) {
+ int yy = pattern_[idx] + off;
+ if (y == yy) {
+ int n = markLine(rawData, bpMap, y) + markLine(rawData, bpMap, y-1) + markLine(rawData, bpMap, y+1);
+ if (n) {
+ found += n;
+ if (settings->verbose) {
+ std::cout << "marked " << n << " pixels in PDAF line at " << y << std::endl;
+ }
+ }
+ } else if (y > yy) {
+ ++idx;
+ if (idx >= pattern_.size()) {
+ idx = 0;
+ off += pattern_.back();
+ }
+ }
+ }
+
+ if (settings->verbose) {
+ static_cast(gthresh_)->print();
+ }
+ static_cast(gthresh_)->processTiles();
+ return found;
+}
+
+} // namespace rtengine
diff --git a/rtengine/pdaflinesfilter.h b/rtengine/pdaflinesfilter.h
new file mode 100644
index 000000000..9837db5b6
--- /dev/null
+++ b/rtengine/pdaflinesfilter.h
@@ -0,0 +1,49 @@
+/* -*- C++ -*-
+ *
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2018 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 .
+ */
+
+#pragma once
+
+#include "rawimagesource.h"
+#include
+
+namespace rtengine {
+
+class PDAFLinesFilter {
+public:
+ PDAFLinesFilter(RawImage *ri);
+ ~PDAFLinesFilter();
+
+ int mark(array2D &rawData, PixelsMap &bpMap);
+ RawImageSource::GreenEqulibrateThreshold &greenEqThreshold();
+ std::unique_ptr lineDenoiseRowBlender();
+
+private:
+ int markLine(array2D &rawData, PixelsMap &bpMap, int y);
+
+ RawImage *ri_;
+ int W_;
+ int H_;
+ std::vector pattern_;
+ int offset_;
+ std::vector rowmap_;
+ RawImageSource::GreenEqulibrateThreshold *gthresh_;
+};
+
+} // namespace rtengine
diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 35b176ba1..0a87d9614 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -2349,6 +2349,7 @@ RAWParams::BayerSensor::BayerSensor() :
black3(0.0),
twogreen(true),
linenoise(0),
+ linenoiseDirection(LineNoiseDirection::BOTH),
greenthresh(0),
dcb_iterations(2),
lmmse_iterations(2),
@@ -2383,7 +2384,8 @@ RAWParams::BayerSensor::BayerSensor() :
pixelShiftNonGreenCross(true),
pixelShiftNonGreenCross2(false),
pixelShiftNonGreenAmaze(false),
- dcb_enhance(true)
+ dcb_enhance(true),
+ pdafLinesFilter(false)
{
}
@@ -2399,6 +2401,7 @@ bool RAWParams::BayerSensor::operator ==(const BayerSensor& other) const
&& black3 == other.black3
&& twogreen == other.twogreen
&& linenoise == other.linenoise
+ && linenoiseDirection == other.linenoiseDirection
&& greenthresh == other.greenthresh
&& dcb_iterations == other.dcb_iterations
&& lmmse_iterations == other.lmmse_iterations
@@ -2433,7 +2436,8 @@ bool RAWParams::BayerSensor::operator ==(const BayerSensor& other) const
&& pixelShiftNonGreenCross == other.pixelShiftNonGreenCross
&& pixelShiftNonGreenCross2 == other.pixelShiftNonGreenCross2
&& pixelShiftNonGreenAmaze == other.pixelShiftNonGreenAmaze
- && dcb_enhance == other.dcb_enhance;
+ && dcb_enhance == other.dcb_enhance
+ && pdafLinesFilter == other.pdafLinesFilter;
}
bool RAWParams::BayerSensor::operator !=(const BayerSensor& other) const
@@ -3366,6 +3370,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->raw.bayersensor.exBlack3, "RAW Bayer", "PreBlack3", raw.bayersensor.black3, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.exTwoGreen, "RAW Bayer", "PreTwoGreen", raw.bayersensor.twogreen, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.linenoise, "RAW Bayer", "LineDenoise", raw.bayersensor.linenoise, keyFile);
+ saveToKeyfile(!pedited || pedited->raw.bayersensor.linenoise, "RAW Bayer", "LineDenoiseDirection", toUnderlying(raw.bayersensor.linenoiseDirection), keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.greenEq, "RAW Bayer", "GreenEqThreshold", raw.bayersensor.greenthresh, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.dcbIterations, "RAW Bayer", "DCBIterations", raw.bayersensor.dcb_iterations, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.dcbEnhance, "RAW Bayer", "DCBEnhance", raw.bayersensor.dcb_enhance, keyFile);
@@ -3401,6 +3406,7 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross, "RAW Bayer", "pixelShiftNonGreenCross", raw.bayersensor.pixelShiftNonGreenCross, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftNonGreenCross2, "RAW Bayer", "pixelShiftNonGreenCross2", raw.bayersensor.pixelShiftNonGreenCross2, keyFile);
saveToKeyfile(!pedited || pedited->raw.bayersensor.pixelShiftNonGreenAmaze, "RAW Bayer", "pixelShiftNonGreenAmaze", raw.bayersensor.pixelShiftNonGreenAmaze, keyFile);
+ saveToKeyfile(!pedited || pedited->raw.bayersensor.pdafLinesFilter, "RAW Bayer", "PDAFLinesFilter", raw.bayersensor.pdafLinesFilter, keyFile);
saveToKeyfile(!pedited || pedited->raw.xtranssensor.method, "RAW X-Trans", "Method", raw.xtranssensor.method, keyFile);
saveToKeyfile(!pedited || pedited->raw.xtranssensor.ccSteps, "RAW X-Trans", "CcSteps", raw.xtranssensor.ccSteps, keyFile);
saveToKeyfile(!pedited || pedited->raw.xtranssensor.exBlackRed, "RAW X-Trans", "PreBlackRed", raw.xtranssensor.blackred, keyFile);
@@ -4684,6 +4690,12 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
assignFromKeyfile(keyFile, "RAW Bayer", "PreBlack3", pedited, raw.bayersensor.black3, pedited->raw.bayersensor.exBlack3);
assignFromKeyfile(keyFile, "RAW Bayer", "PreTwoGreen", pedited, raw.bayersensor.twogreen, pedited->raw.bayersensor.exTwoGreen);
assignFromKeyfile(keyFile, "RAW Bayer", "LineDenoise", pedited, raw.bayersensor.linenoise, pedited->raw.bayersensor.linenoise);
+ if (keyFile.has_key("RAW Bayer", "LineDenoiseDirection")) {
+ raw.bayersensor.linenoiseDirection = RAWParams::BayerSensor::LineNoiseDirection(keyFile.get_integer("RAW Bayer", "LineDenoiseDirection"));
+ if (pedited) {
+ pedited->raw.bayersensor.linenoiseDirection = true;
+ }
+ }
assignFromKeyfile(keyFile, "RAW Bayer", "GreenEqThreshold", pedited, raw.bayersensor.greenthresh, pedited->raw.bayersensor.greenEq);
assignFromKeyfile(keyFile, "RAW Bayer", "DCBIterations", pedited, raw.bayersensor.dcb_iterations, pedited->raw.bayersensor.dcbIterations);
assignFromKeyfile(keyFile, "RAW Bayer", "DCBEnhance", pedited, raw.bayersensor.dcb_enhance, pedited->raw.bayersensor.dcbEnhance);
@@ -4734,6 +4746,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftNonGreenCross", pedited, raw.bayersensor.pixelShiftNonGreenCross, pedited->raw.bayersensor.pixelShiftNonGreenCross);
assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftNonGreenCross2", pedited, raw.bayersensor.pixelShiftNonGreenCross2, pedited->raw.bayersensor.pixelShiftNonGreenCross2);
assignFromKeyfile(keyFile, "RAW Bayer", "pixelShiftNonGreenAmaze", pedited, raw.bayersensor.pixelShiftNonGreenAmaze, pedited->raw.bayersensor.pixelShiftNonGreenAmaze);
+ assignFromKeyfile(keyFile, "RAW Bayer", "PDAFLinesFilter", pedited, raw.bayersensor.pdafLinesFilter, pedited->raw.bayersensor.pdafLinesFilter);
}
if (keyFile.has_group ("RAW X-Trans")) {
diff --git a/rtengine/procparams.h b/rtengine/procparams.h
index 8e0515e7f..b158d7d1f 100644
--- a/rtengine/procparams.h
+++ b/rtengine/procparams.h
@@ -1260,6 +1260,13 @@ struct RAWParams {
double black3;
bool twogreen;
int linenoise;
+ enum class LineNoiseDirection {
+ HORIZONTAL = 1,
+ VERTICAL,
+ BOTH,
+ PDAF_LINES = 5
+ };
+ LineNoiseDirection linenoiseDirection;
int greenthresh;
int dcb_iterations;
int lmmse_iterations;
@@ -1295,6 +1302,7 @@ struct RAWParams {
bool pixelShiftNonGreenCross2;
bool pixelShiftNonGreenAmaze;
bool dcb_enhance;
+ bool pdafLinesFilter;
BayerSensor();
diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc
index d6ae6a199..081ac3661 100644
--- a/rtengine/rawimagesource.cc
+++ b/rtengine/rawimagesource.cc
@@ -34,6 +34,7 @@
#include "rt_math.h"
#include "improcfun.h"
#include "rtlensfun.h"
+#include "pdaflinesfilter.h"
#ifdef _OPENMP
#include
#endif
@@ -1919,6 +1920,32 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
}
}
+ if (ri->getSensorType() == ST_BAYER && raw.bayersensor.pdafLinesFilter) {
+ PDAFLinesFilter f(ri);
+
+ if (!bitmapBads) {
+ bitmapBads = new PixelsMap(W, H);
+ }
+
+ int n = f.mark(rawData, *bitmapBads);
+ totBP += n;
+
+ if (n > 0) {
+ if (settings->verbose) {
+ printf("Marked %d hot pixels from PDAF lines\n", n);
+ }
+
+ auto &thresh = f.greenEqThreshold();
+ if (numFrames == 4) {
+ for (int i = 0; i < 4; ++i) {
+ green_equilibrate(thresh, *rawDataFrames[i]);
+ }
+ } else {
+ green_equilibrate(thresh, rawData);
+ }
+ }
+ }
+
// check if it is an olympus E camera or green equilibration is enabled. If yes, compute G channel pre-compensation factors
if ( ri->getSensorType() == ST_BAYER && (raw.bayersensor.greenthresh || (((idata->getMake().size() >= 7 && idata->getMake().substr(0, 7) == "OLYMPUS" && idata->getModel()[0] == 'E') || (idata->getMake().size() >= 9 && idata->getMake().substr(0, 9) == "Panasonic")) && raw.bayersensor.method != RAWParams::BayerSensor::getMethodString( RAWParams::BayerSensor::Method::VNG4))) ) {
// global correction
@@ -1937,12 +1964,14 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
plistener->setProgress (0.0);
}
+ GreenEqulibrateThreshold thresh(0.01 * raw.bayersensor.greenthresh);
+
if(numFrames == 4) {
for(int i = 0; i < 4; ++i) {
- green_equilibrate(0.01 * raw.bayersensor.greenthresh, *rawDataFrames[i]);
+ green_equilibrate(thresh, *rawDataFrames[i]);
}
} else {
- green_equilibrate(0.01 * raw.bayersensor.greenthresh, rawData);
+ green_equilibrate(thresh, rawData);
}
}
@@ -1969,7 +1998,15 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le
plistener->setProgress (0.0);
}
- cfa_linedn(0.00002 * (raw.bayersensor.linenoise));
+ std::unique_ptr line_denoise_rowblender;
+ if (raw.bayersensor.linenoiseDirection == RAWParams::BayerSensor::LineNoiseDirection::PDAF_LINES) {
+ PDAFLinesFilter f(ri);
+ line_denoise_rowblender = f.lineDenoiseRowBlender();
+ } else {
+ line_denoise_rowblender.reset(new CFALineDenoiseRowBlender());
+ }
+
+ cfa_linedn(0.00002 * (raw.bayersensor.linenoise), int(raw.bayersensor.linenoiseDirection) & int(RAWParams::BayerSensor::LineNoiseDirection::VERTICAL), int(raw.bayersensor.linenoiseDirection) & int(RAWParams::BayerSensor::LineNoiseDirection::HORIZONTAL), *line_denoise_rowblender);
}
if ( (raw.ca_autocorrect || fabs(raw.cared) > 0.001 || fabs(raw.cablue) > 0.001) && ri->getSensorType() == ST_BAYER ) { // Auto CA correction disabled for X-Trans, for now...
diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h
index 78cfb86b8..884c32dbe 100644
--- a/rtengine/rawimagesource.h
+++ b/rtengine/rawimagesource.h
@@ -217,6 +217,21 @@ public:
}
int getFrameCount() {return numFrames;}
+ class GreenEqulibrateThreshold {
+ public:
+ explicit GreenEqulibrateThreshold(float thresh): thresh_(thresh) {}
+ virtual ~GreenEqulibrateThreshold() {}
+ virtual float operator()(int row, int column) const { return thresh_; }
+ protected:
+ const float thresh_;
+ };
+
+ class CFALineDenoiseRowBlender {
+ public:
+ virtual ~CFALineDenoiseRowBlender() {}
+ virtual float operator()(int row) const { return 1.f; }
+ };
+
protected:
typedef unsigned short ushort;
void processFalseColorCorrection (Imagefloat* i, const int steps);
@@ -238,10 +253,10 @@ protected:
int interpolateBadPixelsXtrans( PixelsMap &bitmapBads );
int findHotDeadPixels( PixelsMap &bpMap, float thresh, bool findHotPixels, bool findDeadPixels );
- void cfa_linedn (float linenoiselevel);//Emil's line denoise
+ void cfa_linedn (float linenoiselevel, bool horizontal, bool vertical, const CFALineDenoiseRowBlender &rowblender);//Emil's line denoise
void green_equilibrate_global (array2D &rawData);
- void green_equilibrate (float greenthresh, array2D &rawData);//Emil's green equilibration
+ void green_equilibrate (const GreenEqulibrateThreshold &greenthresh, array2D &rawData);//Emil's green equilibration
void nodemosaic(bool bw);
void eahd_demosaic();
diff --git a/rtgui/bayerpreprocess.cc b/rtgui/bayerpreprocess.cc
index 51a0cb62a..c43bdb4d8 100644
--- a/rtgui/bayerpreprocess.cc
+++ b/rtgui/bayerpreprocess.cc
@@ -18,6 +18,7 @@
*/
#include "bayerpreprocess.h"
#include "guiutils.h"
+#include "eventmapper.h"
#include
using namespace rtengine;
@@ -25,6 +26,10 @@ using namespace rtengine::procparams;
BayerPreProcess::BayerPreProcess () : FoldableToolPanel(this, "bayerpreprocess", M("TP_PREPROCESS_LABEL"), true)
{
+ auto m = ProcEventMapper::getInstance();
+ EvLineDenoiseDirection = m->newEvent(DARKFRAME, "HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION");
+ EvPDAFLinesFilter = m->newEvent(DARKFRAME, "HISTORY_MSG_PREPROCESS_PDAFLINESFILTER");
+
lineDenoise = Gtk::manage(new Adjuster (M("TP_PREPROCESS_LINEDENOISE"), 0, 1000, 1, 0));
lineDenoise->setAdjusterListener (this);
@@ -43,12 +48,32 @@ BayerPreProcess::BayerPreProcess () : FoldableToolPanel(this, "bayerpreprocess",
greenEqThreshold->show();
+ Gtk::HBox *hb = Gtk::manage(new Gtk::HBox());
+ hb->pack_start(*Gtk::manage(new Gtk::Label(M("TP_PREPROCESS_LINEDENOISE_DIRECTION") + ": ")), Gtk::PACK_SHRINK, 0);
+ lineDenoiseDirection = Gtk::manage(new MyComboBoxText());
+ lineDenoiseDirection->append(M("TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL"));
+ lineDenoiseDirection->append(M("TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL"));
+ lineDenoiseDirection->append(M("TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH"));
+ lineDenoiseDirection->append(M("TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES"));
+ lineDenoiseDirection->show();
+ lineDenoiseDirection->signal_changed().connect(sigc::mem_fun(*this, &BayerPreProcess::lineDenoiseDirectionChanged));
+
+ hb->pack_start(*lineDenoiseDirection);
+
pack_start( *lineDenoise, Gtk::PACK_SHRINK, 4);
+ pack_start(*hb, Gtk::PACK_SHRINK, 4);
pack_start( *Gtk::manage (new Gtk::HSeparator()));
pack_start( *greenEqThreshold, Gtk::PACK_SHRINK, 4);
+ pdafLinesFilter = Gtk::manage(new Gtk::CheckButton((M("TP_PREPROCESS_PDAFLINESFILTER"))));
+ pdafLinesFilter->set_tooltip_markup(M("TP_PREPROCESS_PDAFLINESFILTER_TOOLTIP"));
+ pdafLinesFilter->show();
+ pdafLinesFilter->signal_toggled().connect(sigc::mem_fun(*this, &BayerPreProcess::pdafLinesFilterChanged), true);
+
+ pack_start(*Gtk::manage(new Gtk::HSeparator()));
+ pack_start(*pdafLinesFilter, Gtk::PACK_SHRINK, 4);
}
void BayerPreProcess::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited)
@@ -58,10 +83,20 @@ void BayerPreProcess::read(const rtengine::procparams::ProcParams* pp, const Par
if(pedited ) {
lineDenoise->setEditedState( pedited->raw.bayersensor.linenoise ? Edited : UnEdited );
greenEqThreshold->setEditedState( pedited->raw.bayersensor.greenEq ? Edited : UnEdited );
+ if (!pedited->raw.bayersensor.linenoiseDirection) {
+ lineDenoiseDirection->set_active(3);
+ }
+ pdafLinesFilter->set_inconsistent(!pedited->raw.bayersensor.pdafLinesFilter);
}
lineDenoise->setValue (pp->raw.bayersensor.linenoise);
+ int d = int(pp->raw.bayersensor.linenoiseDirection)-1;
+ if (d == 4) {
+ --d;
+ }
+ lineDenoiseDirection->set_active(d);
greenEqThreshold->setValue (pp->raw.bayersensor.greenthresh);
+ pdafLinesFilter->set_active(pp->raw.bayersensor.pdafLinesFilter);
enableListener ();
}
@@ -69,11 +104,19 @@ void BayerPreProcess::read(const rtengine::procparams::ProcParams* pp, const Par
void BayerPreProcess::write( rtengine::procparams::ProcParams* pp, ParamsEdited* pedited)
{
pp->raw.bayersensor.linenoise = lineDenoise->getIntValue();
+ int d = lineDenoiseDirection->get_active_row_number() + 1;
+ if (d == 4) {
+ ++d;
+ }
+ pp->raw.bayersensor.linenoiseDirection = RAWParams::BayerSensor::LineNoiseDirection(d);
pp->raw.bayersensor.greenthresh = greenEqThreshold->getIntValue();
+ pp->raw.bayersensor.pdafLinesFilter = pdafLinesFilter->get_active();
if (pedited) {
pedited->raw.bayersensor.linenoise = lineDenoise->getEditedState ();
pedited->raw.bayersensor.greenEq = greenEqThreshold->getEditedState ();
+ pedited->raw.bayersensor.linenoise = lineDenoiseDirection->get_active_row_number() != 3;
+ pedited->raw.bayersensor.pdafLinesFilter = !pdafLinesFilter->get_inconsistent();
}
}
@@ -96,6 +139,9 @@ void BayerPreProcess::setBatchMode(bool batchMode)
ToolPanel::setBatchMode (batchMode);
lineDenoise->showEditedCB ();
greenEqThreshold->showEditedCB ();
+ if (batchMode) {
+ lineDenoiseDirection->append(M("GENERAL_UNCHANGED"));
+ }
}
void BayerPreProcess::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited)
@@ -125,3 +171,18 @@ void BayerPreProcess::trimValues (rtengine::procparams::ProcParams* pp)
lineDenoise->trimValue(pp->raw.bayersensor.linenoise);
greenEqThreshold->trimValue(pp->raw.bayersensor.greenthresh);
}
+
+
+void BayerPreProcess::lineDenoiseDirectionChanged()
+{
+ if (listener) {
+ listener->panelChanged(EvLineDenoiseDirection, lineDenoiseDirection->get_active_text());
+ }
+}
+
+void BayerPreProcess::pdafLinesFilterChanged()
+{
+ if (listener) {
+ listener->panelChanged(EvPDAFLinesFilter, pdafLinesFilter->get_active() ? M("GENERAL_ENABLED") : M("GENERAL_DISABLED"));
+ }
+}
diff --git a/rtgui/bayerpreprocess.h b/rtgui/bayerpreprocess.h
index 15b2ef7e5..c27e76e00 100644
--- a/rtgui/bayerpreprocess.h
+++ b/rtgui/bayerpreprocess.h
@@ -30,8 +30,13 @@ class BayerPreProcess : public ToolParamBlock, public AdjusterListener, public F
protected:
Adjuster* lineDenoise;
+ MyComboBoxText *lineDenoiseDirection;
Adjuster* greenEqThreshold;
+ Gtk::CheckButton *pdafLinesFilter;
+ rtengine::ProcEvent EvLineDenoiseDirection;
+ rtengine::ProcEvent EvPDAFLinesFilter;
+
public:
BayerPreProcess ();
@@ -45,6 +50,8 @@ public:
void hotDeadPixelChanged();
void setAdjusterBehavior (bool linedenoiseadd, bool greenequiladd);
void trimValues (rtengine::procparams::ProcParams* pp);
+ void lineDenoiseDirectionChanged();
+ void pdafLinesFilterChanged();
};
#endif
diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc
index 0833da4e8..868507ad0 100644
--- a/rtgui/paramsedited.cc
+++ b/rtgui/paramsedited.cc
@@ -433,6 +433,8 @@ void ParamsEdited::set (bool v)
raw.bayersensor.pixelShiftNonGreenAmaze = v;
raw.bayersensor.greenEq = v;
raw.bayersensor.linenoise = v;
+ raw.bayersensor.linenoiseDirection = v;
+ raw.bayersensor.pdafLinesFilter = v;
raw.xtranssensor.method = v;
raw.xtranssensor.ccSteps = v;
raw.xtranssensor.exBlackRed = v;
@@ -994,6 +996,8 @@ void ParamsEdited::initFrom (const std::vector
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.bayersensor.linenoiseDirection = raw.bayersensor.linenoiseDirection && p.raw.bayersensor.linenoiseDirection == other.raw.bayersensor.linenoiseDirection;
+ raw.bayersensor.pdafLinesFilter = raw.bayersensor.pdafLinesFilter && p.raw.bayersensor.pdafLinesFilter == other.raw.bayersensor.pdafLinesFilter;
raw.xtranssensor.method = raw.xtranssensor.method && p.raw.xtranssensor.method == other.raw.xtranssensor.method;
raw.xtranssensor.ccSteps = raw.xtranssensor.ccSteps && p.raw.xtranssensor.ccSteps == other.raw.xtranssensor.ccSteps;
raw.xtranssensor.exBlackRed = raw.xtranssensor.exBlackRed && p.raw.xtranssensor.blackred == other.raw.xtranssensor.blackred;
@@ -2640,6 +2644,14 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
toEdit.raw.bayersensor.linenoise = dontforceSet && options.baBehav[ADDSET_PREPROCESS_LINEDENOISE] ? toEdit.raw.bayersensor.linenoise + mods.raw.bayersensor.linenoise : mods.raw.bayersensor.linenoise;
}
+ if (raw.bayersensor.linenoiseDirection) {
+ toEdit.raw.bayersensor.linenoiseDirection = mods.raw.bayersensor.linenoiseDirection;
+ }
+
+ if (raw.bayersensor.pdafLinesFilter) {
+ toEdit.raw.bayersensor.pdafLinesFilter = mods.raw.bayersensor.pdafLinesFilter;
+ }
+
if (raw.xtranssensor.method) {
toEdit.raw.xtranssensor.method = mods.raw.xtranssensor.method;
}
@@ -3148,7 +3160,7 @@ bool RAWParamsEdited::BayerSensor::isUnchanged() const
&& 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 && pixelShiftOneGreen && pixelShiftEqualBright && pixelShiftEqualBrightChannel
- && linenoise && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen;
+ && linenoise && linenoiseDirection && pdafLinesFilter && exBlack0 && exBlack1 && exBlack2 && exBlack3 && exTwoGreen;
}
bool RAWParamsEdited::XTransSensor::isUnchanged() const
diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h
index ad0b15e8c..12c8ee86e 100644
--- a/rtgui/paramsedited.h
+++ b/rtgui/paramsedited.h
@@ -763,6 +763,8 @@ public:
//bool allEnhance;
bool greenEq;
bool linenoise;
+ bool linenoiseDirection;
+ bool pdafLinesFilter;
bool isUnchanged() const;
};
diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc
index 27a56e38e..4828e90dc 100644
--- a/rtgui/partialpastedlg.cc
+++ b/rtgui/partialpastedlg.cc
@@ -114,6 +114,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
raw_greenthresh = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_GREENEQUIL")));
raw_hotpix_filt = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_HOTPIXFILT")));
raw_deadpix_filt = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_DEADPIXFILT")));
+ raw_pdaf_lines_filter = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCESS_PDAFLINESFILTER")));
//---
raw_expos = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAWEXPOS_LINEAR")));
raw_preser = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAWEXPOS_PRESER")));
@@ -225,6 +226,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
vboxes[7]->pack_start (*raw_greenthresh, Gtk::PACK_SHRINK, 2);
vboxes[7]->pack_start (*raw_hotpix_filt, Gtk::PACK_SHRINK, 2);
vboxes[7]->pack_start (*raw_deadpix_filt, Gtk::PACK_SHRINK, 2);
+ vboxes[7]->pack_start (*raw_pdaf_lines_filter, Gtk::PACK_SHRINK, 2);
vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0);
vboxes[7]->pack_start (*raw_expos, Gtk::PACK_SHRINK, 2);
vboxes[7]->pack_start (*raw_preser, Gtk::PACK_SHRINK, 2);
@@ -368,6 +370,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren
raw_greenthreshConn = raw_greenthresh->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
raw_hotpix_filtConn = raw_hotpix_filt->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
raw_deadpix_filtConn = raw_deadpix_filt->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
+ raw_pdaf_lines_filterConn = raw_pdaf_lines_filter->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
//---
raw_exposConn = raw_expos->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
raw_preserConn = raw_preser->signal_toggled().connect (sigc::bind (sigc::mem_fun(*raw, &Gtk::CheckButton::set_inconsistent), true));
@@ -441,6 +444,7 @@ void PartialPasteDlg::rawToggled ()
ConnectionBlocker raw_greenthreshBlocker(raw_greenthreshConn);
ConnectionBlocker raw_hotpix_filtBlocker(raw_hotpix_filtConn);
ConnectionBlocker raw_deadpix_filtBlocker(raw_deadpix_filtConn);
+ ConnectionBlocker raw_pdaf_lines_filterBlocker(raw_pdaf_lines_filterConn);
ConnectionBlocker raw_exposBlocker(raw_exposConn);
ConnectionBlocker raw_preserBlocker(raw_preserConn);
ConnectionBlocker raw_blackBlocker(raw_blackConn);
@@ -467,6 +471,7 @@ void PartialPasteDlg::rawToggled ()
raw_greenthresh->set_active (raw->get_active ());
raw_hotpix_filt->set_active (raw->get_active ());
raw_deadpix_filt->set_active (raw->get_active ());
+ raw_pdaf_lines_filter->set_active (raw->get_active ());
raw_expos->set_active (raw->get_active ());
raw_preser->set_active (raw->get_active ());
raw_black->set_active (raw->get_active ());
@@ -875,6 +880,7 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
if (!raw_linenoise->get_active ()) {
filterPE.raw.bayersensor.linenoise = falsePE.raw.bayersensor.linenoise;
+ filterPE.raw.bayersensor.linenoiseDirection = falsePE.raw.bayersensor.linenoiseDirection;
}
if (!raw_greenthresh->get_active ()) {
@@ -910,6 +916,10 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param
filterPE.raw.hotdeadpix_thresh = falsePE.raw.hotdeadpix_thresh;
}
+ if (!raw_pdaf_lines_filter->get_active ()) {
+ filterPE.raw.bayersensor.pdafLinesFilter = falsePE.raw.bayersensor.pdafLinesFilter;
+ }
+
if (!df_file->get_active ()) {
filterPE.raw.darkFrame = falsePE.raw.darkFrame;
}
diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h
index e270a1e6e..b404db156 100644
--- a/rtgui/partialpastedlg.h
+++ b/rtgui/partialpastedlg.h
@@ -106,6 +106,7 @@ public:
Gtk::CheckButton* raw_caredblue;
Gtk::CheckButton* raw_hotpix_filt;
Gtk::CheckButton* raw_deadpix_filt;
+ Gtk::CheckButton* raw_pdaf_lines_filter;
Gtk::CheckButton* raw_linenoise;
Gtk::CheckButton* raw_greenthresh;
Gtk::CheckButton* raw_method;
@@ -133,7 +134,7 @@ public:
sigc::connection coarserotConn, finerotConn, cropConn, resizeConn, prsharpeningConn, perspectiveConn, commonTransConn;
sigc::connection metadataConn, exifchConn, iptcConn, icmConn;
sigc::connection df_fileConn, df_AutoSelectConn, ff_fileConn, ff_AutoSelectConn, ff_BlurRadiusConn, ff_BlurTypeConn, ff_ClipControlConn;
- sigc::connection raw_caredblueConn, 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_pixelshiftConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn;
+ sigc::connection raw_caredblueConn, raw_ca_autocorrectConn, raw_hotpix_filtConn, raw_deadpix_filtConn, raw_pdaf_lines_filterConn, raw_linenoiseConn, raw_greenthreshConn, raw_ccStepsConn, raw_methodConn, raw_imagenumConn, raw_dcb_iterationsConn, raw_lmmse_iterationsConn, raw_pixelshiftConn, raw_dcb_enhanceConn, raw_exposConn, raw_preserConn, raw_blackConn;
public:
PartialPasteDlg (const Glib::ustring &title, Gtk::Window* parent);