diff --git a/rtdata/images/svg/spot-normal.svg b/rtdata/images/svg/spot-normal.svg index ca9a933c2..d0320e31f 100644 --- a/rtdata/images/svg/spot-normal.svg +++ b/rtdata/images/svg/spot-normal.svg @@ -9,62 +9,65 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16px" - height="16px" - id="svg2985" + width="11" + height="11" + id="svg2" version="1.1" - inkscape:version="0.48.4 r9939" - sodipodi:docname="New document 2"> + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="spot-normal.svg"> + id="defs4" /> + id="metadata7"> image/svg+xml - + + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1041.3622)"> + style="fill:#000000;fill-opacity:0.66666667;stroke:none" + d="M 6,1043.3622 5.378906,1046.7411 2,1047.3622 v 1 l 3.378906,0.6211 L 6,1052.3622 H 7 L 7.621094,1048.9833 11,1048.3622 v -1 L 7.621094,1046.7411 7,1043.3622 Z" + id="path818" + inkscape:connector-curvature="0" /> + d="m 5,1042.3622 h 1 l 0.8286408,4.5 L 6,1051.3622 H 5 l -0.8286408,-4.5 z" + id="rect2989" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccccc" /> + diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 72f5c6d72..488a56b46 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -623,7 +623,7 @@ void Crop::update(int todo) spotsDone = true; PreviewProps pp(trafx, trafy, trafw * skip, trafh * skip, skip); //parent->imgsrc->getImage(parent->currWB, tr, origCrop, pp, params.toneCurve, params.raw); - parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, tr); + parent->ipf.removeSpots(origCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, nullptr, tr); } DirPyrDenoiseParams denoiseParams = params.dirpyrDenoise; @@ -709,7 +709,7 @@ void Crop::update(int todo) baseCrop->copyData (spotCrop); PreviewProps pp (trafx, trafy, trafw * skip, trafh * skip, skip); int tr = getCoarseBitMask(params.coarse); - parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, tr); + parent->ipf.removeSpots (spotCrop, parent->imgsrc, params.spot.entries, pp, parent->currWB, ¶ms.icm, tr); } else { if (spotCrop) { delete spotCrop; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 2c49bdef1..22bafd844 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -540,7 +540,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) allocCache(spotprev); orig_prev->copyData(spotprev); PreviewProps pp(0, 0, fw, fh, scale); - ipf.removeSpots(spotprev, imgsrc, params->spot.entries, pp, currWB, tr); + ipf.removeSpots(spotprev, imgsrc, params->spot.entries, pp, currWB, ¶ms->icm, tr); } else { if (spotprev) { delete spotprev; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 5700a354b..a1b79fc38 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -333,7 +333,7 @@ public: float MadRgb(const float * DataList, int datalen); // spot removal tool - void removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector &entries, const PreviewProps &pp, const ColorTemp &currWB, int tr); + void removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector &entries, const PreviewProps &pp, const ColorTemp &currWB, const ColorManagementParams *cmp, int tr); // pyramid wavelet void dirpyr_equalizer(float ** src, float ** dst, int srcwidth, int srcheight, float ** l_a, float ** l_b, const double * mult, const double dirpyrThreshold, const double skinprot, float b_l, float t_l, float t_r, int scale); //Emil's directional pyramid wavelet diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 6934cc8e8..c50306f6f 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -785,7 +785,7 @@ private: // Spot Removal if (params.spot.enabled && !params.spot.entries.empty ()) { - ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, tr); + ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, nullptr, tr); } // at this stage, we can flush the raw data to free up quite an important amount of memory diff --git a/rtengine/spot.cc b/rtengine/spot.cc index 2090324dd..b12f5bc40 100644 --- a/rtengine/spot.cc +++ b/rtengine/spot.cc @@ -38,34 +38,6 @@ constexpr T skips(T a, T b) namespace rtengine { -/* Code taken from Gimp 2.8.10 and converted for RawTherapee by Jean-Christophe FRISCH (aka Hombre) on 02.19.2014 - * - * ORIGINAL NOTES - * - * The method used here is similar to the lighting invariant correction - * method but slightly different: we do not divide the RGB components, - * but substract them I2 = I0 - I1, where I0 is the sample image to be - * corrected, I1 is the reference pattern. Then we solve DeltaI=0 - * (Laplace) with I2 Dirichlet conditions at the borders of the - * mask. The solver is a unoptimized red/black checker Gauss-Siedel - * with an over-relaxation factor of 1.8. It can benefit from a - * multi-grid evaluation of an initial solution before the main - * iteration loop. - * - * I reduced the convergence criteria to 0.1% (0.001) as we are - * dealing here with RGB integer components, more is overkill. - * - * Jean-Yves Couleaud cjyves@free.fr - */ - -/* Original Algorithm Design: - * - * T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning - * http://www.tgeorgiev.net/Photoshop_Healing.pdf - */ - -#if 1 - class SpotBox { public: @@ -329,6 +301,9 @@ public: return true; } +#define ALGO 1 + +#if ALGO==1 bool processIntersectionWith(SpotBox &destBox) { Imagefloat *dstImg = destBox.image; @@ -337,30 +312,6 @@ public: return false; } - /* - printf("#%llu: spotBox(X1:%d, Y1:%d, X2:%d, Y2:%d, W:%d, H:%d) img(W:%d, H:%d)\n", i, - spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2, - getWidth(), getHeight(), - destBox.getWidth(), destBox.getHeight() - ); - */ - -#ifndef NDEBUG - printf("[processIntersectionWith] srcSpotBox @ %p(%d, %d) - img @ %p(%d, %d) / dstSpotBox @ %p(%d, %d) - img @ %p(%d, %d)\n", - this, getWidth(), getHeight(), image, destBox.getWidth(), destBox.getHeight(), - &destBox, destBox.getWidth(), destBox.getHeight(), destBox.image, dstImg->getWidth(), dstImg->getHeight()); - printf(" [spot] srcSpotBox (%d, %d) -> (%d, %d) / dstSpotBox (%d, %d) -> (%d, %d)\n", - spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2, - destBox.spotArea.x1, destBox.spotArea.y1, destBox.spotArea.x2, destBox.spotArea.y2); - printf(" [img ] srcSpotBox (%d, %d) -> (%d, %d) / dstSpotBox (%d, %d) -> (%d, %d)\n", - imgArea.x1, imgArea.y1, imgArea.x2, imgArea.y2, - destBox.imgArea.x1, destBox.imgArea.y1, destBox.imgArea.x2, destBox.imgArea.y2); - printf(" [intr] srcSpotBox (%d, %d) -> (%d, %d) / dstSpotBox (%d, %d) -> (%d, %d)\n", - intersectionArea.x1, intersectionArea.y1, intersectionArea.x2, intersectionArea.y2, - destBox.intersectionArea.x1, destBox.intersectionArea.y1, destBox.intersectionArea.x2, destBox.intersectionArea.y2); - printf(" radius = %.3f\n", radius); -#endif - int srcImgY = intersectionArea.y1 - imgArea.y1; int dstImgY = destBox.intersectionArea.y1 - destBox.imgArea.y1; for (int y = intersectionArea.y1; y <= intersectionArea.y2; ++y) { @@ -396,358 +347,44 @@ public: return true; } - - // Copy the intersecting part - bool copyImgTo(SpotBox &destBox) { - Imagefloat *destImg = destBox.image; - - if (image == nullptr || destImg == nullptr) { - std::cerr << "One of the source or destination SpotBox image is missing !" << std::endl; - return false; - } - - /* - printf("#%llu: spotBox(X1:%d, Y1:%d, X2:%d, Y2:%d, W:%d, H:%d) img(W:%d, H:%d)\n", i, - spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2, - getWidth(), getHeight(), - destBox.getWidth(), destBox.getHeight() - ); - */ - - std::unique_ptr intersection; - - if (!intersectionArea.getIntersection(destBox.intersectionArea, intersection)) { - return false; - } - - Imagefloat *srcImg = image; - Imagefloat *dstImg = destBox.image; - -#ifndef NDEBUG - printf("[copyImgTo] srcSpotBox @ %p(%d, %d) - img @ %p(%d, %d) / dstSpotBox @ %p(%d, %d) - img @ %p(%d, %d)\n", - this, getWidth(), getHeight(), image, srcImg->getWidth(), srcImg->getHeight(), - &destBox, destBox.getWidth(), destBox.getHeight(), destBox.image, dstImg->getWidth(), dstImg->getHeight()); - printf(" [spot] srcSpotBox (%d, %d) -> (%d, %d) / dstSpotBox (%d, %d) -> (%d, %d)\n", - spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2, - destBox.spotArea.x1, destBox.spotArea.y1, destBox.spotArea.x2, destBox.spotArea.y2); - printf(" [img ] srcSpotBox (%d, %d) -> (%d, %d) / dstSpotBox (%d, %d) -> (%d, %d)\n", - imgArea.x1, imgArea.y1, imgArea.x2, imgArea.y2, - destBox.imgArea.x1, destBox.imgArea.y1, destBox.imgArea.x2, destBox.imgArea.y2); - printf(" [intr] intersection (%d, %d) -> (%d, %d)\n", - intersection->x1, intersection->y1, intersection->x2, intersection->y2); #endif - int srcImgY = intersection->y1 - imgArea.y1; - int dstImgY = intersection->y1 - destBox.imgArea.y1; - for (int y = intersection->y1; y <= intersection->y2; ++y) { - int srcImgX = intersection->x1 - imgArea.x1; - int dstImgX = intersection->x1 - destBox.imgArea.x1; - - for (int x = intersection->x1; x <= intersection->x2; ++x) { - dstImg->r(dstImgY, dstImgX) = srcImg->r(srcImgY, srcImgX); - dstImg->g(dstImgY, dstImgX) = srcImg->g(srcImgY, srcImgX); - dstImg->b(dstImgY, dstImgX) = srcImg->b(srcImgY, srcImgX); - ++srcImgX; - ++dstImgX; - } - ++srcImgY; - ++dstImgY; - } - - return true; - } -}; - -void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector &entries, const PreviewProps &pp, const ColorTemp &currWB, int tr) -{ - //Get the clipped image areas (src & dst) from the source image - - std::vector< std::shared_ptr > srcSpotBoxs; - std::vector< std::shared_ptr > dstSpotBoxs; - int fullImgWidth = 0; - int fullImgHeight = 0; - imgsrc->getFullSize(fullImgWidth, fullImgHeight, tr); - SpotBox fullImageBox(0, 0, fullImgWidth - 1, fullImgHeight - 1, 0, 0, nullptr, SpotBox::Type::FINAL); - SpotBox cropBox(pp.getX(), pp.getY(), - pp.getX() + pp.getWidth() - 1, pp.getY() + pp.getHeight() - 1, - 0, 0, img, SpotBox::Type::FINAL); - - std::set visibleSpots; // list of dest spots intersecting the preview's crop - int i = 0; - - for (auto entry : params->spot.entries) { - std::shared_ptr srcSpotBox(new SpotBox(entry, SpotBox::Type::SOURCE)); - std::shared_ptr dstSpotBox(new SpotBox(entry, SpotBox::Type::TARGET)); - if ( !srcSpotBox->setIntersectionWith(fullImageBox) - || !dstSpotBox->setIntersectionWith(fullImageBox) - || !srcSpotBox->imageIntersects(*dstSpotBox, true)) - { - continue; - ++i; - } - - // If spot intersect the preview image, add it to the visible spots - if (dstSpotBox->spotIntersects(cropBox)) { -#ifndef NDEBUG - printf("Spot visible insere: %d\n", i); -#endif - visibleSpots.insert(i); - } - ++i; - - // Source area - PreviewProps spp(srcSpotBox->imgArea.x1, srcSpotBox->imgArea.y1, - srcSpotBox->getImageWidth(), srcSpotBox->getImageHeight(), pp.getSkip()); - int w = 0; - int h = 0; - imgsrc->getSize(spp, w, h); - *srcSpotBox /= pp.getSkip(); - srcSpotBox->allocImage(); - Imagefloat *_image_ = srcSpotBox->getImage(); - for (int y = 0; y < (int)_image_->getHeight(); ++y) { - for (int x = 0; x < (int)_image_->getWidth(); ++x) { - _image_->r(y, x) = 60000.f; - _image_->g(y, x) = 500.f; - _image_->b(y, x) = 500.f; - } - } - -#ifndef NDEBUG - printf("~~~~~ Image Size Before : %d, %d\n", _image_->getWidth(), _image_->getHeight()); -#endif - imgsrc->getImage(currWB, tr, srcSpotBox->getImage(), spp, params->toneCurve, params->raw); -#ifndef NDEBUG - printf("~~~~~ Image Size After : %d, %d\n", _image_->getWidth(), _image_->getHeight()); -#endif - assert(srcSpotBox->checkImageSize()); - //printf(" / src size: %d,%d", currSrcSpot->getWidth(), currSrcSpot->getHeight()); +#if ALGO==2 + bool processIntersectionWith(SpotBox &destBox) { + /* The following disabled code has been taken from Gimp 2.8.10 and converted + * for RawTherapee by Jean-Christophe FRISCH (aka Hombre) on 02.19.2014. + * It has not been tested, at all, an can (does) contain bugs. Feel free to + * replace the actual working code by this one, if results are better. + */ - // Destination area - spp.set(dstSpotBox->imgArea.x1, dstSpotBox->imgArea.y1, dstSpotBox->getImageWidth(), - dstSpotBox->getImageHeight(), pp.getSkip()); - *dstSpotBox /= pp.getSkip(); - dstSpotBox->allocImage(); - _image_ = dstSpotBox->getImage(); - for (int y = 0; y < (int)_image_->getHeight(); ++y) { - for (int x = 0; x < (int)_image_->getWidth(); ++x) { - _image_->r(y, x) = 500.f; - _image_->g(y, x) = 500.f; - _image_->b(y, x) = 60000.f; - } - } -#ifndef NDEBUG - printf("~~~~~ Image Size Before : %d, %d\n", _image_->getWidth(), _image_->getHeight()); -#endif - imgsrc->getImage(currWB, tr, dstSpotBox->getImage(), spp, params->toneCurve, params->raw); -#ifndef NDEBUG - printf("~~~~~ Image Size After : %d, %d\n", _image_->getWidth(), _image_->getHeight()); -#endif - assert(dstSpotBox->checkImageSize()); - //printf(" / src size: %d,%d", currDstSpot->getWidth(), currDstSpot->getHeight()); - - // Update the intersectionArea between src and dest - if (srcSpotBox->mutuallyClipImageArea(*dstSpotBox)) { - srcSpotBoxs.push_back(srcSpotBox); - dstSpotBoxs.push_back(dstSpotBox); - -#ifndef NDEBUG - printf("Taille de l'image pour spot #%d: src(%d, %d) / dst(%d, %d)\n", i-1, - srcSpotBox->getImageWidth(), srcSpotBox->getImageHeight(), - dstSpotBox->getImageWidth(), dstSpotBox->getImageHeight() - ); -#endif - } - - } - - // Construct list of upstream dependancies - - std::set requiredSpots = visibleSpots; // starting point, visible spots are necessarilly required spots -#ifndef NDEBUG - printf("Required spots: "); -#endif - for (auto i = requiredSpots.rbegin(); i != requiredSpots.rend(); i++) { - int spotNbr = *i; -#ifndef NDEBUG - printf("%d ", spotNbr); -#endif - requiredSpots.insert(spotNbr); - if (spotNbr > 0) { - for (int j = spotNbr - 1; j >= 0; --j) { - if ((srcSpotBoxs.at(spotNbr))->imageIntersects(*dstSpotBoxs.at(j))) { - requiredSpots.insert(spotNbr); -#ifndef NDEBUG - printf("%d ", spotNbr); -#endif - } - } - } - } -#ifndef NDEBUG - printf("\n"); -#endif - - // Process spots and copy them downstream - - for (auto i = requiredSpots.begin(); i != requiredSpots.end(); i++) { - // Process -#ifndef NDEBUG - printf("========>>> Processing spot #%d\n", *i); -#endif - srcSpotBoxs.at(*i)->processIntersectionWith(*dstSpotBoxs.at(*i)); - - // Propagate - std::set positiveSpots; // For DEBUG purpose only ! - auto j = i; - ++j; - while (j != requiredSpots.end()) { - bool intersectionFound = false; - int i_ = *i; - int j_ = *j; -#ifndef NDEBUG - printf("Propagating to SRC #%d ?\n", j_); -#endif - intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*srcSpotBoxs.at(j_)); -#ifndef NDEBUG - printf("Propagating to DSTC #%d ?\n", j_); -#endif - intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*dstSpotBoxs.at(j_)); - if (intersectionFound) { - positiveSpots.insert(j_); - } - ++j; - } -#ifndef NDEBUG - printf("||| DEST spot #%d propagated to : ", *i); - for (auto g : positiveSpots) { - printf("%d ", g); - } - printf("\n"); -#endif - } - - // Copy the dest spot to the preview image -#ifndef NDEBUG - printf("cropBox (%d, %d) -> (%d, %d)", - cropBox.imgArea.x1, cropBox.imgArea.y1, - cropBox.imgArea.x2, cropBox.imgArea.y2); -#endif - cropBox /= pp.getSkip(); -#ifndef NDEBUG - printf(" -> [/skip] (%d, %d) -> (%d, %d)", - cropBox.imgArea.x1, cropBox.imgArea.y1, - cropBox.imgArea.x2, cropBox.imgArea.y2); -#endif - cropBox.tuneImageSize(); - cropBox.intersectionArea = cropBox.imgArea; -#ifndef NDEBUG - printf(" -> [/tuned] (%d, %d) -> (%d, %d)\n", - cropBox.imgArea.x1, cropBox.imgArea.y1, - cropBox.imgArea.x2, cropBox.imgArea.y2); -#endif - - int f = 0; - for (auto i : visibleSpots) { - f += dstSpotBoxs.at(i)->copyImgTo(cropBox) ? 1 : 0; - } -#ifndef NDEBUG - printf("Nombre de copie finale : %d\n", f); -#endif - - /* - printf("#--: spotBox(X1:%d, Y1:%d, X2:%d, Y2:%d, W:%d, H:%d) img(W:%d, H:%d)\n\n", - cropBox.spotArea.x1, cropBox.spotArea.y1, cropBox.spotArea.x2, cropBox.spotArea.y2, - cropBox.getWidth(), cropBox.getHeight(), - cropBox.image->getWidth(), cropBox.image->getHeight() - ); - */ -} - -#endif + /* ORIGINAL NOTES + * + * The method used here is similar to the lighting invariant correction + * method but slightly different: we do not divide the RGB components, + * but substract them I2 = I0 - I1, where I0 is the sample image to be + * corrected, I1 is the reference pattern. Then we solve DeltaI=0 + * (Laplace) with I2 Dirichlet conditions at the borders of the + * mask. The solver is a unoptimized red/black checker Gauss-Siedel + * with an over-relaxation factor of 1.8. It can benefit from a + * multi-grid evaluation of an initial solution before the main + * iteration loop. + * + * I reduced the convergence criteria to 0.1% (0.001) as we are + * dealing here with RGB integer components, more is overkill. + * + * Jean-Yves Couleaud cjyves@free.fr + */ - - - -#if 0 -void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector &entries, const PreviewProps &pp, const ColorTemp &currWB, int tr) -{ - - Alpha mask; - - - for (const auto entry : entries) { - float featherRadius = entry.getFeatherRadius(); - int scaledFeatherRadius = featherRadius / pp.getSkip (); - - SpotBox srcBox(entry, SpotBox::Type::SOURCE); - srcBox.translate(-pp.getX(), -pp.getY()); - srcBox /= float (pp.getSkip()); - - SpotBox dstBox(entry, SpotBox::Type::TARGET); - dstBox.translate(-pp.getX(), -pp.getY()); - dstBox /= float (pp.getSkip()); - - //printf(" -> X: %04d > %04d\n -> Y: %04d > %04d\n", dst_XMin, dst_XMax, dst_YMin, dst_YMax); - - // scaled spot is too small, we do not preview it - if (scaledFeatherRadius < 2 && pp.getSkip() != 1) { -#ifndef NDEBUG - - if (options.rtSettings.verbose) { - printf ("Skipping spot located at %d x %d, too small for the preview zoom rate\n", entry.sourcePos.x, entry.sourcePos.y); - } - -#endif - continue; - } - - // skipping entries totally transparent - if (entry.opacity == 0.) { -#ifndef NDEBUG - - if (options.rtSettings.verbose) { - printf ("Skipping spot located at %d x %d: opacity=%.3f\n", entry.sourcePos.x, entry.sourcePos.y, entry.opacity); - } - - continue; -#endif - } - - // skipping entries where the source circle isn't inside the image bounds, even partially - if (src_XMin < 0 || src_XMax >= img->getWidth() || src_YMin < 0 || src_YMax >= img->getHeight()) { -#ifndef NDEBUG - - if (options.rtSettings.verbose) { - printf ("Skipping spot located at %d x %d, from the data at %d x %d, radius=%d, feather=%.3f, opacity=%.3f: source out of bounds\n", entry.sourcePos.x, entry.sourcePos.y, entry.targetPos.x, entry.targetPos.y, entry.radius, entry.feather, entry.opacity); - printf ("%d < 0 || %d >= %d || %d < 0 || %d >= %d\n", - src_XMin, src_XMax, img->getWidth(), src_YMin, src_YMax, img->getHeight()); - } - -#endif - continue; - } - - // skipping entries where the dest circle is completely outside the image bounds - /* - if (dst_XMin >= img->getWidth() || dst_XMax <= 0 || dst_YMin >= img->getHeight() || dst_YMax <= 0) { -#ifndef NDEBUG - - if (options.rtSettings.verbose) { - printf ("Skipping spot located at %d x %d, from the data at %d x %d, radius=%d, feather=%.3f, opacity=%.3f: source out of bounds\n", entry.sourcePos.x, entry.sourcePos.y, entry.targetPos.x, entry.targetPos.y, entry.radius, entry.feather, entry.opacity); - printf ("%d >= %d || %d <= 0 || %d >= %d || %d <= 0\n", - dst_XMin, img->getWidth(), dst_XMax, dst_YMin, img->getHeight(), dst_YMax); - } - -#endif - continue; - } - */ + /* Original Algorithm Design: + * + * T. Georgiev, "Photoshop Healing Brush: a Tool for Seamless Cloning + * http://www.tgeorgiev.net/Photoshop_Healing.pdf + */ // ----------------- Core function ----------------- -#if 0 int scaledPPX = pp.getX() / pp.skip; int scaledPPY = pp.getY() / pp.skip; int scaledPPW = pp.getWidth() / pp.skip + (pp.getWidth() % pp.getSkip() > 0); @@ -810,7 +447,6 @@ void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const s // repeat until convergence or max iterations for (int n = 0; n < MAX_ITER; ++n) { - printf ("<<< n=#%d\n", n); // ---------------------------------------------------------------- /* Perform one iteration of the Laplace solver for matrix. Store the @@ -829,8 +465,6 @@ void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const s // do reds for (i = 0; i < matrix.getHeight(); ++i) { for (j = i % 2; j < matrix.getWidth(); j += 2) { - printf ("/%d,%d", j, i); - if ((0 == mask (i, j)) || (i == 0) || (i == (matrix.getHeight() - 1)) || (j == 0) || (j == (matrix.getWidth() - 1))) { // do nothing at the boundary or outside mask solution.r (i, j) = matrix.r (i, j); @@ -893,8 +527,6 @@ void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const s */ for (i = 0; i < matrix.getHeight(); i++) { for (j = (i % 2) ? 0 : 1; j < matrix.getWidth(); j += 2) { - printf (":%d,%d", j, i); - if ((0 == mask (i, j)) || (i == 0) || (i == (matrix.getHeight() - 1)) || (j == 0) || (j == (matrix.getWidth() - 1))) { // do nothing at the boundary or outside mask solution.r (i, j) = matrix.r (i, j); @@ -956,16 +588,8 @@ void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const s if (sqr_err_r < EPSILON && sqr_err_g < EPSILON && sqr_err_b < EPSILON) { break; } - - printf ("\n>>> n=#%d\n", n); } - printf ("\n"); -#endif - - - - // add solution to original image and store in tempPR for (int i = 0, i2 = dst_YMin; i2 < dst_YMax - 1; ++i, ++i2) { if (i2 < 0 || i2 >= img->getHeight()) { @@ -977,25 +601,189 @@ void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const s continue; } - //float c2 = float (mask (i, j)) / 255.f; - //float c1 = 1.f - c2; - //resultPR->r(i,j) = (unsigned char) CLAMP0255 ( ROUND( double(first->r(i,j)) + double(secondPR->r(i,j)) ) ); - - - img->r (i2, j2) = 65535.0f; //img->r(i2,j2)*c1 + srcBuff->r(i,j)*c2; - img->g (i2, j2) = 0.0f; //img->g(i2,j2)*c1 + srcBuff->g(i,j)*c2; - img->b (i2, j2) = 0.0f; //img->b(i2,j2)*c1 + srcBuff->b(i,j)*c2; - /* + float c2 = float (mask (i, j)) / 255.f; + float c1 = 1.f - c2; + resultPR->r(i,j) = (unsigned char) CLAMP0255 ( ROUND( double(first->r(i,j)) + double(secondPR->r(i,j)) ) ); img->r(i2,j2) = img->r(i2,j2)*c1 + (solution.r(i,j) + srcBuff->r(i,j))*c2; img->g(i2,j2) = img->g(i2,j2)*c1 + (solution.g(i,j) + srcBuff->g(i,j))*c2; img->b(i2,j2) = img->b(i2,j2)*c1 + (solution.b(i,j) + srcBuff->b(i,j))*c2; - */ + } + } + } +#endif + + // Copy the intersecting part + bool copyImgTo(SpotBox &destBox) { + Imagefloat *destImg = destBox.image; + + if (image == nullptr || destImg == nullptr) { + std::cerr << "One of the source or destination SpotBox image is missing !" << std::endl; + return false; + } + + std::unique_ptr intersection; + + if (!intersectionArea.getIntersection(destBox.intersectionArea, intersection)) { + return false; + } + + Imagefloat *srcImg = image; + Imagefloat *dstImg = destBox.image; + + int srcImgY = intersection->y1 - imgArea.y1; + int dstImgY = intersection->y1 - destBox.imgArea.y1; + for (int y = intersection->y1; y <= intersection->y2; ++y) { + int srcImgX = intersection->x1 - imgArea.x1; + int dstImgX = intersection->x1 - destBox.imgArea.x1; + + for (int x = intersection->x1; x <= intersection->x2; ++x) { + dstImg->r(dstImgY, dstImgX) = srcImg->r(srcImgY, srcImgX); + dstImg->g(dstImgY, dstImgX) = srcImg->g(srcImgY, srcImgX); + dstImg->b(dstImgY, dstImgX) = srcImg->b(srcImgY, srcImgX); + ++srcImgX; + ++dstImgX; + } + ++srcImgY; + ++dstImgY; + } + + return true; + } +}; + +void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector &entries, const PreviewProps &pp, const ColorTemp &currWB, const ColorManagementParams *cmp, int tr) +{ + //Get the clipped image areas (src & dst) from the source image + + std::vector< std::shared_ptr > srcSpotBoxs; + std::vector< std::shared_ptr > dstSpotBoxs; + int fullImgWidth = 0; + int fullImgHeight = 0; + imgsrc->getFullSize(fullImgWidth, fullImgHeight, tr); + SpotBox fullImageBox(0, 0, fullImgWidth - 1, fullImgHeight - 1, 0, 0, nullptr, SpotBox::Type::FINAL); + SpotBox cropBox(pp.getX(), pp.getY(), + pp.getX() + pp.getWidth() - 1, pp.getY() + pp.getHeight() - 1, + 0, 0, img, SpotBox::Type::FINAL); + + std::set visibleSpots; // list of dest spots intersecting the preview's crop + int i = 0; + + for (auto entry : params->spot.entries) { + std::shared_ptr srcSpotBox(new SpotBox(entry, SpotBox::Type::SOURCE)); + std::shared_ptr dstSpotBox(new SpotBox(entry, SpotBox::Type::TARGET)); + if ( !srcSpotBox->setIntersectionWith(fullImageBox) + || !dstSpotBox->setIntersectionWith(fullImageBox) + || !srcSpotBox->imageIntersects(*dstSpotBox, true)) + { + continue; + ++i; + } + + // If spot intersect the preview image, add it to the visible spots + if (dstSpotBox->spotIntersects(cropBox)) { + visibleSpots.insert(i); + } + ++i; + + // Source area + PreviewProps spp(srcSpotBox->imgArea.x1, srcSpotBox->imgArea.y1, + srcSpotBox->getImageWidth(), srcSpotBox->getImageHeight(), pp.getSkip()); + int w = 0; + int h = 0; + imgsrc->getSize(spp, w, h); + *srcSpotBox /= pp.getSkip(); + srcSpotBox->allocImage(); + Imagefloat *srcImage = srcSpotBox->getImage(); + for (int y = 0; y < (int)srcImage->getHeight(); ++y) { + for (int x = 0; x < (int)srcImage->getWidth(); ++x) { + srcImage->r(y, x) = 60000.f; + srcImage->g(y, x) = 500.f; + srcImage->b(y, x) = 500.f; } } + imgsrc->getImage(currWB, tr, srcSpotBox->getImage(), spp, params->toneCurve, params->raw); + if (cmp) { + imgsrc->convertColorSpace(srcImage, *cmp, currWB); + } + assert(srcSpotBox->checkImageSize()); + + + // Destination area + spp.set(dstSpotBox->imgArea.x1, dstSpotBox->imgArea.y1, dstSpotBox->getImageWidth(), + dstSpotBox->getImageHeight(), pp.getSkip()); + *dstSpotBox /= pp.getSkip(); + dstSpotBox->allocImage(); + Imagefloat *dstImage = dstSpotBox->getImage(); + for (int y = 0; y < (int)dstImage->getHeight(); ++y) { + for (int x = 0; x < (int)dstImage->getWidth(); ++x) { + dstImage->r(y, x) = 500.f; + dstImage->g(y, x) = 500.f; + dstImage->b(y, x) = 60000.f; + } + } + imgsrc->getImage(currWB, tr, dstSpotBox->getImage(), spp, params->toneCurve, params->raw); + if (cmp) { + imgsrc->convertColorSpace(dstImage, *cmp, currWB); + } + assert(dstSpotBox->checkImageSize()); + + // Update the intersectionArea between src and dest + if (srcSpotBox->mutuallyClipImageArea(*dstSpotBox)) { + srcSpotBoxs.push_back(srcSpotBox); + dstSpotBoxs.push_back(dstSpotBox); + } + + } + + // Construct list of upstream dependancies + + std::set requiredSpots = visibleSpots; // starting point, visible spots are necessarilly required spots + for (auto i = requiredSpots.rbegin(); i != requiredSpots.rend(); i++) { + int spotNbr = *i; + requiredSpots.insert(spotNbr); + if (spotNbr > 0) { + for (int j = spotNbr - 1; j >= 0; --j) { + if ((srcSpotBoxs.at(spotNbr))->imageIntersects(*dstSpotBoxs.at(j))) { + requiredSpots.insert(spotNbr); + } + } + } + } + + // Process spots and copy them downstream + + for (auto i = requiredSpots.begin(); i != requiredSpots.end(); i++) { + // Process + srcSpotBoxs.at(*i)->processIntersectionWith(*dstSpotBoxs.at(*i)); + + // Propagate + std::set positiveSpots; // For DEBUG purpose only ! + auto j = i; + ++j; + while (j != requiredSpots.end()) { + bool intersectionFound = false; + int i_ = *i; + int j_ = *j; + intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*srcSpotBoxs.at(j_)); + intersectionFound |= dstSpotBoxs.at(i_)->copyImgTo(*dstSpotBoxs.at(j_)); + if (intersectionFound) { + positiveSpots.insert(j_); + } + ++j; + } + } + + // Copy the dest spot to the preview image + cropBox /= pp.getSkip(); + cropBox.tuneImageSize(); + cropBox.intersectionArea = cropBox.imgArea; + + int f = 0; + for (auto i : visibleSpots) { + f += dstSpotBoxs.at(i)->copyImgTo(cropBox) ? 1 : 0; } } -#endif }