From 25a531308fbec3cb3fd01ccdd143621dc3c248c4 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 May 2017 15:43:49 +0200 Subject: [PATCH 1/4] Cleaned pixelshift code and made a speedup for pixelshift without motion correction --- rtengine/pixelshift.cc | 1026 +++++++++------------------------------- 1 file changed, 233 insertions(+), 793 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index a27ea71c8..71212b0b3 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -1,13 +1,8 @@ //////////////////////////////////////////////////////////////// // -// pentax pixelshift algorithm with motion detection +// Algorithm for Pentax Pixel Shift raw files with motion detection // -// -// 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 +// Copyright (C) 2016 - 2017 Ingo Weyrich // // // pixelshift.cc is free software: you can redistribute it and/or modify @@ -31,65 +26,29 @@ #include "procparams.h" #include "gauss.h" #include "median.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, float stddevFactor, float eperIso, float nreadIso, float prnu) { // calculate the difference between two green samples -#ifdef PIXELSHIFTDEV - if(adaptive) { -#endif - float gDiff = a - b; - gDiff *= eperIso; - gDiff *= gDiff; - float avg = (a + b) * 0.5f; - avg *= eperIso; - prnu *= avg; - float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); - return std::max(gDiff - stddev, 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 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); - 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; - } + return gDiff - stddev; } -#endif -float nonGreenDiffCross(float right, float left, float top, float bottom, float centre, float clippedVal, 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) { 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; @@ -101,28 +60,32 @@ float nonGreenDiffCross(float right, float left, float top, float bottom, float avg *= eperIso; prnu *= avg; float stddev = stddevFactor * (avg + nreadIso + prnu * prnu); - return std::max(std::min(hDiff, vDiff) - stddev, 0.f); + return std::min(hDiff, vDiff) - stddev; } -void paintMotionMask(int index, bool showMotion, float gridMax, bool showOnlyMask, float *maskDest, float *nonMaskDest0, float *nonMaskDest1) +void paintMotionMask(int index, bool showMotion, 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; + maskDest[index] = 13500.f; nonMaskDest1[index] = nonMaskDest0[index] = 0.f; } else { - maskDest[index] = nonMaskDest0[index] = nonMaskDest1[index] = 1000.f + 25000.f * gridMax; + maskDest[index] = nonMaskDest0[index] = nonMaskDest1[index] = 65535.f; } } } void invertMask(int xStart, int xEnd, int yStart, int yEnd, const array2D &maskIn, array2D &maskOut) { +#ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) +#endif for(int i = yStart; i < yEnd; ++i) { +#ifdef _OPENMP #pragma omp simd +#endif for(int j = xStart; j < xEnd; ++j) { maskOut[i][j] = ~maskIn[i][j]; @@ -132,10 +95,14 @@ void invertMask(int xStart, int xEnd, int yStart, int yEnd, const array2D &maskIn, array2D &maskOut) { +#ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) +#endif for(int i = yStart; i < yEnd; ++i) { +#ifdef _OPENMP #pragma omp simd +#endif for(int j = xStart; j < xEnd; ++j) { maskOut[i][j] ^= maskIn[i][j]; @@ -152,7 +119,7 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar coordStack.pop(); auto x = coord.first, y = coord.second; - if (mask[y][x] == 255) { + 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; @@ -228,27 +195,37 @@ void floodFill4Impl(int y, int x, int xStart, int xEnd, int yStart, int yEnd, ar void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &mask) { +#ifdef _OPENMP #pragma omp parallel +#endif { std::stack, std::vector>> coordStack; +#ifdef _OPENMP #pragma omp for schedule(dynamic,128) nowait +#endif for(uint16_t i = yStart; i < yEnd; i++) { floodFill4Impl(i, xStart, xStart, xEnd, yStart, yEnd, mask, coordStack); } +#ifdef _OPENMP #pragma omp for schedule(dynamic,128) nowait +#endif for(int16_t i = yEnd - 1; i >= 0 ; i--) { floodFill4Impl(i, xEnd - 1, xStart, xEnd, yStart, yEnd, mask, coordStack); } +#ifdef _OPENMP #pragma omp sections nowait +#endif { +#ifdef _OPENMP #pragma omp section +#endif { uint16_t i = yStart; @@ -257,7 +234,9 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); } } +#ifdef _OPENMP #pragma omp section +#endif { uint16_t i = yStart; @@ -266,7 +245,9 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); } } +#ifdef _OPENMP #pragma omp section +#endif { uint16_t i = yEnd; @@ -275,7 +256,9 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma floodFill4Impl(i, j, xStart, xEnd, yStart, yEnd, mask, coordStack); } } +#ifdef _OPENMP #pragma omp section +#endif { uint16_t i = yEnd; @@ -291,11 +274,12 @@ void floodFill4(int xStart, int xEnd, int yStart, int yEnd, array2D &ma void calcFrameBrightnessFactor(unsigned int frame, uint32_t datalen, LUT *histo[4], float brightnessFactor[4]) { float medians[4]; + for(int i = 0; i < 4; ++i) { //find median of histogram uint32_t median = 0, count = 0; - while (count < datalen / 2) { + while(count < datalen / 2) { count += (*histo[i])[median]; ++median; } @@ -317,20 +301,15 @@ using namespace std; using namespace rtengine; 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); + amaze_demosaic_RT(winx, winy, 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; @@ -344,159 +323,71 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 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(unsigned 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], bayerParams.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; + amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue); + } - // 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; + multi_array2D redTmp(winw, winh); + multi_array2D greenTmp(winw, winh); + multi_array2D blueTmp(winw, winh); - 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; - float stddevFactorGreen = bayerParams.pixelShiftStddevFactorGreen; - float stddevFactorRed = bayerParams.pixelShiftStddevFactorRed; - float stddevFactorBlue = bayerParams.pixelShiftStddevFactorBlue; - 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 + constexpr float stddevFactorGreen = 25.f; + constexpr float stddevFactorRed = 25.f; + constexpr float stddevFactorBlue = 25.f; + constexpr float prnu = 0.01f; + constexpr float redBlueWeight = 0.7f + 1.f; float eperIso = bayerParams.pixelShiftEperIso; const bool checkNonGreenCross = bayerParams.pixelShiftNonGreenCross; const bool checkGreen = bayerParams.pixelShiftGreen; - const float greenWeight = 2.f; + constexpr float greenWeight = 2.f; const bool blurMap = bayerParams.pixelShiftBlur; const float sigma = bayerParams.pixelShiftSigma; -#ifdef PIXELSHIFTDEV - const bool checkNonGreenHorizontal = bayerParams.pixelShiftNonGreenHorizontal; - const bool checkNonGreenVertical = bayerParams.pixelShiftNonGreenVertical; - const bool checkNonGreenAmaze = bayerParams.pixelShiftNonGreenAmaze; - const bool checkNonGreenCross2 = bayerParams.pixelShiftNonGreenCross2; - const float threshold = bayerParams.pixelShiftSum + 9.f; - const bool experimental0 = bayerParams.pixelShiftExp0; -#else constexpr float threshold = 3.f + 9.f; -#endif const bool holeFill = bayerParams.pixelShiftHoleFill; const bool equalBrightness = bayerParams.pixelShiftEqualBright; const bool equalChannel = bayerParams.pixelShiftEqualBrightChannel; @@ -530,8 +421,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 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) + 1.5f // ISO 51200 }; static const float ePerIsoK3II = 0.35f; @@ -574,6 +464,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA static const float ePerIsoK1 = 0.75f; + // currently nReadK70 is used for K-70 and KP static const float nReadK70[] = { 4.0f, // ISO 100 4.0f, // ISO 125 4.0f, // ISO 160 @@ -602,50 +493,28 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 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) + 3.0f, // ISO 64000 + 3.0f, // ISO 80000 + 3.0f, // ISO 102400 + 3.0f, // ISO 128000 + 3.0f, // ISO 160000 + 3.0f, // ISO 204800 + 3.0f, // ISO 256000 + 3.0f, // ISO 320000 + 3.0f, // ISO 409600 + 3.0f, // ISO 512000 + 3.0f, // ISO 640000 + 3.0f // ISO 819200 }; static const float ePerIsoK70 = 0.5f; - if (plistener) { - plistener->setProgressStr (Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); + if(plistener) { + plistener->setProgressStr(Glib::ustring::compose(M("TP_RAW_DMETHOD_PROGRESSBAR"), RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::pixelshift])); plistener->setProgress(0.0); } - -#ifdef PIXELSHIFTDEV - 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; - } -#else - const bool nOf3x3 = true; -#endif - - if(adaptive && blurMap && nOf3x3 && smoothFactor == 0.f && !showMotion) { + if(adaptive && blurMap && smoothFactor == 0.f && !showMotion) { if(plistener) { plistener->setProgress(1.0); } @@ -653,20 +522,6 @@ 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); - - 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; - } - } -#endif - const float scaleGreen = 1.f / scale_mul[1]; float nRead; @@ -680,27 +535,15 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } else if(model.find("K-1") != string::npos) { nRead = nReadK1[nReadIndex]; eperIsoModel = ePerIsoK1; - } else { + } else { // as long as we don't have values for Pentax KP, we use the values from K-70 nRead = nReadK70[nReadIndex]; eperIsoModel = ePerIsoK70; } - 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; -#endif const float eperIsoRed = (eperIso / scale_mul[0]) * (65535.f / (c_white[0] - c_black[0])); const float eperIsoGreen = (eperIso * scaleGreen) * (65535.f / (c_white[1] - c_black[1])); const float eperIsoBlue = (eperIso / scale_mul[2]) * (65535.f / (c_white[2] - c_black[2])); @@ -708,44 +551,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const float clippedRed = 65535.f / scale_mul[0]; const float clippedBlue = 65535.f / scale_mul[2]; - 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 - const float motionThreshold = 1.f - (motion / 100.f); - // For shades of green motion indicators -#ifdef PIXELSHIFTDEV - const float blendFactor = ((adaptive || motion == 0.f) ? 1.f : 1.f / (1.f - motionThreshold)); -#endif - 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; - } - } - // calculate average green brightness for each frame float greenBrightness[4] = {1.f, 1.f, 1.f, 1.f}; float redBrightness[4] = {1.f, 1.f, 1.f, 1.f}; @@ -795,6 +602,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA for(int j = winx + 1, offset = FC(i, j) & 1; j < winw - 1; ++j, offset ^= 1) { (*histogreenThr[1 - offset])[(*rawDataFrames[1 - offset])[i - offset + 1][j]]++; (*histogreenThr[3 - offset])[(*rawDataFrames[3 - offset])[i + offset][j + 1]]++; + if(bluerow) { (*historedThr[2 - offset])[(*rawDataFrames[2 - offset])[i + 1][j - offset + 1]]++; (*histoblueThr[(offset << 1) + offset])[(*rawDataFrames[(offset << 1) + offset])[i][j + offset]]++; @@ -805,7 +613,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } +#ifdef _OPENMP #pragma omp critical +#endif { for(int i = 0; i < 4; ++i) { (*histogreen[i]) += (*histogreenThr[i]); @@ -827,556 +637,150 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA delete histoblue[i]; delete histogreen[i]; } - -#ifdef PIXELSHIFTDEV - std::cout << "blue brightness factors by median : " << blueBrightness[0] << " " << blueBrightness[1] << " " << blueBrightness[2] << " " << blueBrightness[3] << std::endl; - std::cout << "red brightness factors by median : " << redBrightness[0] << " " << redBrightness[1] << " " << redBrightness[2] << " " << redBrightness[3] << std::endl; - std::cout << "green brightness factors by median : " << greenBrightness[0] << " " << greenBrightness[1] << " " << greenBrightness[2] << " " << greenBrightness[3] << std::endl; -#endif - } - 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); - if(!equalChannel) { - for(int i = 0; i < 4; ++i ) { + for(int i = 0; i < 4; ++i) { redBrightness[i] = blueBrightness[i] = greenBrightness[i]; } } -// 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]; - float ngbright[2][4] = {{redBrightness[0],redBrightness[1],redBrightness[2],redBrightness[3]}, - {blueBrightness[0],blueBrightness[1],blueBrightness[2],blueBrightness[3]} - }; - int ng = 0; - int j = winx + 1; - int c = FC(i, j); - - 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); - ng ^= 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; - - for(; j < winw - 1; ++j) { - // 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] * ngbright[ng][(offset << 1) + offset]; - nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * ngbright[ng^1][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}; - float pixelcount = ((winh - (border + offsY) - (winy + border - offsY)) * (winw - (border + offsX) - (winx + border - offsX))) / 2.f; -#endif - - array2D psMask(winw, winh); + if(adaptive) { + // fill channels psRed, psG1, psG2 and psBlue + 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); #ifdef _OPENMP - #pragma omp parallel -#endif - { -#ifdef PIXELSHIFTDEV - int sumThr[2] = {0}; -#endif -#ifdef _OPENMP - #pragma omp for schedule(dynamic,16) nowait -#endif - - for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { -#ifdef PIXELSHIFTDEV - float *greenDest = green[i + offsY]; - float *redDest = red[i + offsY]; - float *blueDest = blue[i + offsY]; -#endif - int j = winx + border - offsX; - -#ifdef PIXELSHIFTDEV - 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), - 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; - bool blueRow = false; + #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]; + float ngbright[2][4] = {{redBrightness[0], redBrightness[1], redBrightness[2], redBrightness[3]}, + {blueBrightness[0], blueBrightness[1], blueBrightness[2], blueBrightness[3]} + }; + int ng = 0; + int j = winx + 1; int c = FC(i, j); -#ifdef PIXELSHIFTDEV - 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 - blueRow = true; + std::swap(nonGreenDest0, nonGreenDest1); + std::swap(greenDest1, greenDest2); + ng ^= 1; } -#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; - for(; j < winw - (border + offsX); ++j) { + for(; j < winw - 1; ++j) { + // 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] * ngbright[ng][(offset << 1) + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * ngbright[ng ^ 1][2 - offset]; + offset ^= 1; // 0 => 1 or 1 => 0 + } + } + + // now that the temporary planes are filled for easy access we do the motion detection + array2D psMask(winw, winh); + + int offsX = 0, offsY = 0; + + if(!bayerParams.pixelShiftMedian) { + // 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 schedule(dynamic,16) +#endif + + for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { psMask[i][j] = 1.f; - offset ^= 1; // 0 => 1 or 1 => 0 - -#ifdef PIXELSHIFTDEV - if(detectMotion || (adaptive && checkGreen)) { - bool skipNext = false; - float gridMax = 0.f; -#else - if(adaptive && checkGreen) { - float gridMax; -#endif - -#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); -#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), - 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]}); - // 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), - 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]}); - // 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), - 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; - } - - if(!adaptive) { - // increase motion detection dependent on brightness - korr = log2Lut[((int)(psG1[i][j] * scaleGreen)) >> 1]; - } - - if (gridMax > thresh - korr) { -#else - if (gridMax > thresh) { - -#endif - -#ifdef PIXELSHIFTDEV - sumThr[offset] ++; - - if(nOf3x3) { -#endif - psMask[i][j] = greenWeight; -#ifdef PIXELSHIFTDEV - } - - 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];; - } - - } 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); - } - } - -#endif - // do not set the motion pixel values. They have already been set by demosaicer or showMotion + if(checkGreen) { + if(greenDiff(psG1[i][j], psG2[i][j], stddevFactorGreen, eperIsoGreen, nRead, prnu) > 0.f) { + psMask[i][j] = greenWeight; + // do not set the motion pixel values. They have already been set by demosaicer continue; } } - 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, clippedRed, stddevFactorRed, eperIsoRed, nRead, prnu, showMotion); + 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, clippedRed, stddevFactorRed, eperIsoRed, nRead, prnu); - 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; - } - - // 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, clippedBlue, 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; - - } + if(redDiff > 0.f) { + psMask[i][j] = redBlueWeight; + continue; } -#ifdef PIXELSHIFTDEV + // 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, clippedBlue, stddevFactorBlue, eperIsoBlue, nRead, prnu); - if(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(blueDiff > 0.f) { + psMask[i][j] = redBlueWeight; + continue; } - - 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; - - 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(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(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] = greenWeight; - } else { - paintMotionMask(j + offsX, showMotion, greenDiffAmaze, showOnlyMask, greenDest, redDest, blueDest); - } - - continue; - } - } - - if(experimental0) { // for experiments - - } - -#endif } 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 - 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 PIXELSHIFTDEV - -#ifdef _OPENMP - #pragma omp critical -#endif - { - sum[0] += sumThr[0]; - sum[1] += sumThr[1]; - } -#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) { +#ifdef _OPENMP #pragma omp parallel +#endif { gaussianBlur(psMask, psMask, winw, winh, sigma); } } - array2D mask(W, H, ARRAY2D_CLEAR_DATA); + array2D mask(winw, winh, ARRAY2D_CLEAR_DATA); +#ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) +#endif for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { int j = winx + border - offsX; @@ -1404,18 +808,20 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } if(holeFill) { - array2D maskInv(W, H); + array2D maskInv(winw, winh); 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); } - +#ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) +#endif for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { #ifdef __SSE2__ + // pow() is expensive => precalculate blend factor using SSE if(smoothTransitions) { // vfloat onev = F2V(1.f); vfloat smoothv = F2V(smoothFactor); @@ -1440,12 +846,13 @@ 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); + paintMotionMask(j + offsX, showMotion, showOnlyMask, greenDest, redDest, blueDest); } else if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black redDest[j + offsX] = greenDest[j + offsX] = blueDest[j + offsX] = 0.f; } else { if(smoothTransitions) { #ifdef __SSE2__ + // use precalculated blend factor const float blend = psMask[i][j]; #else const float blend = smoothFactor == 0.f ? 1.f : pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); @@ -1461,6 +868,39 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } } + } else { + // motion detection off => combine the 4 raw frames + float ngbright[2][4] = {{redBrightness[0], redBrightness[1], redBrightness[2], redBrightness[3]}, + {blueBrightness[0], blueBrightness[1], blueBrightness[2], blueBrightness[3]} + }; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for(int i = winy + 1; i < winh - 1; ++i) { + float *nonGreenDest0 = red[i]; + float *nonGreenDest1 = blue[i]; + int ng = 0; + int j = winx + 1; + int c = FC(i, j); + + if((c + FC(i, j + 1)) == 3) { + // row with blue pixels => swap destination pointers for non green pixels + std::swap(nonGreenDest0, nonGreenDest1); + ng ^= 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; + + for(; j < winw - 1; ++j) { + // set red, green and blue values + green[i][j] = ((*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset] + (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]) * 0.5f; + nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset] * ngbright[ng][(offset << 1) + offset]; + nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * ngbright[ng ^ 1][2 - offset]; + offset ^= 1; // 0 => 1 or 1 => 0 + } + } } if(plistener) { From 486d51e434c96eaf596e4ee9a55b4b32ef3e2dd8 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 May 2017 18:44:03 +0200 Subject: [PATCH 2/4] Reduced memory usage of Pixel Shift motion detection by ~ width * height * 8 byte --- rtengine/pixelshift.cc | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 71212b0b3..89571ec15 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -647,10 +647,8 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA if(adaptive) { - // fill channels psRed, psG1, psG2 and psBlue + // fill channels psRed and psBlue 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); #ifdef _OPENMP @@ -658,8 +656,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #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]; float ngbright[2][4] = {{redBrightness[0], redBrightness[1], redBrightness[2], redBrightness[3]}, @@ -672,7 +668,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 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); ng ^= 1; } @@ -680,9 +675,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA unsigned int offset = c & 1; for(; j < winw - 1; ++j) { - // 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]; + // store the non green values from the 4 frames into 2 temporary planes nonGreenDest0[j] = (*rawDataFrames[(offset << 1) + offset])[i][j + offset] * ngbright[ng][(offset << 1) + offset]; nonGreenDest1[j] = (*rawDataFrames[2 - offset])[i + 1][j - offset + 1] * ngbright[ng ^ 1][2 - offset]; offset ^= 1; // 0 => 1 or 1 => 0 @@ -722,11 +715,14 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA #endif for(int i = winy + border - offsY; i < winh - (border + offsY); ++i) { - for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = FC(i, winx + border - offsX) & 1; + + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j, offset ^= 1) { psMask[i][j] = 1.f; if(checkGreen) { - if(greenDiff(psG1[i][j], psG2[i][j], stddevFactorGreen, eperIsoGreen, nRead, prnu) > 0.f) { + if(greenDiff((*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset], (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset], stddevFactorGreen, eperIsoGreen, nRead, prnu) > 0.f) { psMask[i][j] = greenWeight; // do not set the motion pixel values. They have already been set by demosaicer continue; @@ -844,7 +840,10 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA float *redDest = red[i + offsY]; float *blueDest = blue[i + offsY]; - for(int j = winx + border - offsX; j < winw - (border + offsX); ++j) { + // offset to keep the code short. It changes its value between 0 and 1 for each iteration of the loop + unsigned int offset = FC(i, winx + border - offsX) & 1; + + for(int j = winx + border - offsX; j < winw - (border + offsX); ++j, offset ^= 1) { if(mask[i][j] == 255) { paintMotionMask(j + offsX, showMotion, showOnlyMask, greenDest, redDest, blueDest); } else if(showOnlyMask) { // we want only motion mask => paint areas without motion in pure black @@ -858,11 +857,11 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA const float blend = smoothFactor == 0.f ? 1.f : pow_F(std::max(psMask[i][j] - 1.f, 0.f), smoothFactor); #endif redDest[j + offsX] = intp(blend, redDest[j + offsX], psRed[i][j] ); - greenDest[j + offsX] = intp(blend, greenDest[j + offsX], (psG1[i][j] + psG2[i][j]) * 0.5f); + greenDest[j + offsX] = intp(blend, greenDest[j + offsX], ((*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset] + (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]) * 0.5f); blueDest[j + offsX] = intp(blend, blueDest[j + offsX], psBlue[i][j]); } else { redDest[j + offsX] = psRed[i][j]; - greenDest[j + offsX] = (psG1[i][j] + psG2[i][j]) * 0.5f; + greenDest[j + offsX] = ((*rawDataFrames[1 - offset])[i - offset + 1][j] * greenBrightness[1 - offset] + (*rawDataFrames[3 - offset])[i + offset][j + 1] * greenBrightness[3 - offset]) * 0.5f; blueDest[j + offsX] = psBlue[i][j]; } } From e8e01c3af1d250064a80648dfa47fee620f231b2 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 May 2017 19:45:17 +0200 Subject: [PATCH 3/4] pixelshift: skip demosaic when 'Show only motion mask' is enabled --- rtengine/pixelshift.cc | 74 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 89571ec15..9082ea4c5 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -316,53 +316,57 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA 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 - if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.lmmse_iterations); - } else { - amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue); - } + const bool showMotion = bayerParams.pixelShiftShowMotion; + const bool showOnlyMask = bayerParams.pixelShiftShowMotionMaskOnly && showMotion; - multi_array2D redTmp(winw, winh); - multi_array2D greenTmp(winw, winh); - multi_array2D blueTmp(winw, winh); - - for(int i = 0; i < 3; i++) { + if(bayerParams.pixelShiftAutomatic) { + if(!showOnlyMask) { + if(bayerParams.pixelShiftMedian) { // We need the amaze demosaiced frames for motion correction if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.lmmse_iterations); + lmmse_interpolate_omp(winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.lmmse_iterations); } else { - amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i]); - } - } - -#ifdef _OPENMP - #pragma omp parallel for schedule(dynamic,16) -#endif - - for(int i = winy + border; i < winh - border; i++) { - for(int j = winx + border; j < winw - border; j++) { - red[i][j] = median(red[i][j], redTmp[0][i + 1][j], redTmp[1][i + 1][j + 1], redTmp[2][i][j + 1]); + amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue); } - for(int j = winx + border; j < winw - border; j++) { - green[i][j] = median(green[i][j], greenTmp[0][i + 1][j], greenTmp[1][i + 1][j + 1], greenTmp[2][i][j + 1]); + multi_array2D redTmp(winw, winh); + multi_array2D greenTmp(winw, winh); + multi_array2D blueTmp(winw, winh); + + 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(winx, winy, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i]); + } } - for(int j = winx + border; j < winw - border; j++) { - blue[i][j] = median(blue[i][j], blueTmp[0][i + 1][j], blueTmp[1][i + 1][j + 1], blueTmp[2][i][j + 1]); + #ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) + #endif + + for(int i = winy + border; i < winh - border; i++) { + for(int j = winx + border; j < winw - border; j++) { + red[i][j] = median(red[i][j], redTmp[0][i + 1][j], redTmp[1][i + 1][j + 1], redTmp[2][i][j + 1]); + } + + for(int j = winx + border; j < winw - border; j++) { + green[i][j] = median(green[i][j], greenTmp[0][i + 1][j], greenTmp[1][i + 1][j + 1], greenTmp[2][i][j + 1]); + } + + for(int j = winx + border; j < winw - border; j++) { + blue[i][j] = median(blue[i][j], blueTmp[0][i + 1][j], blueTmp[1][i + 1][j + 1], blueTmp[2][i][j + 1]); + } } - } - } else { - if(bayerParams.pixelShiftLmmse) { - lmmse_interpolate_omp(winw, winh, rawData, red, green, blue, bayerParams.lmmse_iterations); } else { - amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue); + if(bayerParams.pixelShiftLmmse) { + lmmse_interpolate_omp(winw, winh, rawData, red, green, blue, bayerParams.lmmse_iterations); + } else { + amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue); + } } } } else if(bayerParams.pixelShiftMotionCorrectionMethod != RAWParams::BayerSensor::Off) { @@ -373,8 +377,6 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } - const bool showMotion = bayerParams.pixelShiftShowMotion; - const bool showOnlyMask = bayerParams.pixelShiftShowMotionMaskOnly && showMotion; const bool adaptive = bayerParams.pixelShiftAutomatic; constexpr float stddevFactorGreen = 25.f; constexpr float stddevFactorRed = 25.f; From 28fee5e72b02e75aba9acc480b4f222c47cd204f Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 30 May 2017 19:48:06 +0200 Subject: [PATCH 4/4] fix wrong indentation --- rtengine/pixelshift.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 9082ea4c5..e7d6b86c7 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -344,9 +344,9 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA } } - #ifdef _OPENMP +#ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) - #endif +#endif for(int i = winy + border; i < winh - border; i++) { for(int j = winx + border; j < winw - border; j++) {