Spot-Removal (#2239): Adding recusivity and border handling

Warning: Debug builds will be slow due to the amount of debug output (no problem for Release builds). Code cleanup will be done when after testing phase.
This commit is contained in:
Hombre
2019-08-14 15:52:22 +02:00
parent 122e0b89be
commit 4d4f54cbc2
4 changed files with 586 additions and 216 deletions

View File

@@ -781,7 +781,12 @@ private:
params.toneCurve.brightness = 0; params.toneCurve.brightness = 0;
params.toneCurve.contrast = 0; params.toneCurve.contrast = 0;
params.toneCurve.black = 0; params.toneCurve.black = 0;
} }
// Spot Removal
if (params.spot.enabled && !params.spot.entries.empty ()) {
ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, tr);
}
// at this stage, we can flush the raw data to free up quite an important amount of memory // at this stage, we can flush the raw data to free up quite an important amount of memory
// commented out because it makes the application crash when batch processing... // commented out because it makes the application crash when batch processing...
@@ -924,12 +929,6 @@ private:
} }
} }
// Spot Removal
if (params.spot.enabled && !params.spot.entries.empty ()) {
ipf.removeSpots (baseImg, imgsrc, params.spot.entries, pp, currWB, tr);
}
// RGB processing // RGB processing
curve1 (65536); curve1 (65536);

View File

@@ -21,6 +21,19 @@
#include "alpha.h" #include "alpha.h"
#include "procparams.h" #include "procparams.h"
#include "imagesource.h" #include "imagesource.h"
#include <iostream>
namespace
{
// "ceil" rounding
template<typename T>
constexpr T skips(T a, T b)
{
return a / b + static_cast<bool>(a % b);
}
}
namespace rtengine namespace rtengine
{ {
@@ -58,271 +71,597 @@ class SpotBox {
public: public:
enum class Type { enum class Type {
SOURCE, SOURCE,
TARGET TARGET,
FINAL
}; };
struct Rectangle {
public:
int x1;
int y1;
int x2;
int y2;
Rectangle() : x1(0), y1(0), x2(0), y2(0) {}
Rectangle(const Rectangle &other) : x1(other.x1), y1(other.y1), x2(other.x2), y2(other.y2) {}
Rectangle(int X1, int Y1, int X2, int Y2) : x1(X1), y1(Y1), x2(X2), y2(Y2) {}
bool intersects(const Rectangle &other) const {
return (other.x1 <= x2 && other.x2 >= x1)
&& (other.y1 <= y2 && other.y2 >= y1);
}
bool getIntersection(const Rectangle &other, std::unique_ptr<Rectangle> &intersection) const {
if (intersects(other)) {
if (!intersection) {
intersection.reset(new Rectangle());
}
intersection->x1 = rtengine::max(x1, other.x1);
intersection->x2 = rtengine::min(x2, other.x2);
intersection->y1 = rtengine::max(y1, other.y1);
intersection->y2 = rtengine::min(y2, other.y2);
if (intersection->x1 > intersection->x2 || intersection->y1 > intersection->y2) {
intersection.release();
return false;
}
return true;
}
if (intersection) {
intersection.release();
}
return false;
}
Rectangle& operator+=(const Coord &v) {
x1 += v.x;
y1 += v.y;
x2 += v.x;
y2 += v.y;
return *this;
}
Rectangle& operator-=(const Coord &v) {
x1 -= v.x;
y1 -= v.y;
x2 -= v.x;
y2 -= v.y;
return *this;
}
Rectangle& operator/=(const int &v) {
if (v == 1) {
return *this;
}
/*
float fv = float(v);
x1 = int(float(x1) / fv + 0.5f);
y1 = int(float(y1) / fv + 0.5f);
x2 = int(float(x2) / fv + 0.5f);
y2 = int(float(y2) / fv + 0.5f);
*/
// Aletrnate rounding possibility
int w = x2 - x1 + 1;
int h = x2 - x1 + 1;
w = w / v + (w % v > 0);
h = h / v + (h % v > 0);
x1 /= v;
y1 /= v;
x2 = x1 + w - 1;
y2 = y1 + h - 1;
return *this;
}
};
private: private:
Type type; Type type;
Imagefloat* image;
public: public:
int topLeftX; // top/left and bottom/right coordinates of the spot in image space (at some point divided by scale factor)
int topLeftY; Rectangle spotArea;
int bottomRightX; // top/left and bottom/right coordinates of the spot in scaled image space (on borders, imgArea won't cover spotArea)
int bottomRightY; Rectangle imgArea;
Imagefloat* img; // top/left and bottom/right coordinates of useful part of the image in scaled image space (rounding error workaround)
Rectangle intersectionArea;
float radius;
float featherRadius;
SpotBox (int tl_x, int tl_y, int br_x, int br_y, Type type) : SpotBox (int tl_x, int tl_y, int br_x, int br_y, int radius, int feather_radius, Imagefloat* image, Type type) :
type(type), type(type),
topLeftX(tl_x), image(image),
topLeftY(tl_y), spotArea(tl_x, tl_y, br_x, br_y),
bottomRightX(br_x), imgArea(spotArea),
bottomRightY(br_y), intersectionArea(),
img(nullptr) radius(radius),
featherRadius(feather_radius)
{} {}
SpotBox (int tl_x, int tl_y, Imagefloat* image, Type type) : SpotBox (int tl_x, int tl_y, int radius, int feather_radius, Imagefloat* image, Type type) :
type(type), type(type),
topLeftX(tl_x), image(image),
topLeftY(tl_y), spotArea(tl_x, tl_y, image ? tl_x + image->getWidth() - 1 : 0, image ? tl_y + image->getHeight() - 1 : 0),
bottomRightX(image ? tl_x + image->getWidth() - 1 : 0), imgArea(spotArea),
bottomRightY(image ? tl_y + image->getHeight() - 1 : 0), intersectionArea(),
img(image) radius(radius),
featherRadius(feather_radius)
{} {}
SpotBox (SpotEntry &spot, Type type) : SpotBox (SpotEntry &spot, Type type) :
type(type), type(type),
img(nullptr) image(nullptr),
intersectionArea(),
radius(spot.radius),
featherRadius(int(spot.getFeatherRadius() + 0.5f)) // rounding to int before resizing
{ {
float featherRadius = spot.radius * (1.f + spot.feather); spotArea.x1 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) - featherRadius);
topLeftX = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) - featherRadius); spotArea.x2 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) + featherRadius);
bottomRightX = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) + featherRadius); spotArea.y1 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) - featherRadius);
topLeftY = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) - featherRadius); spotArea.y2 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) + featherRadius);
bottomRightY = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) + featherRadius); imgArea = spotArea;
} }
void translate(int dx, int dy) { ~SpotBox() {
topLeftX += dx; if (image && type != Type::FINAL) {
topLeftY += dy; delete image;
bottomRightX += dx; }
bottomRightY += dy;
} }
void operator /(float v) { SpotBox& operator /=(const int& v) {
topLeftX = int(topLeftX / v + 0.5f); if (v == 1) {
topLeftY = int(topLeftY / v + 0.5f); return *this;
bottomRightX = int(bottomRightX / v + 0.5f); }
bottomRightY = int(bottomRightY / v + 0.5f); spotArea /= v;
} imgArea /= v;
radius = radius / float(v);
void operator *(float v) { featherRadius = getWidth() / 2.f;
topLeftX *= v; // intersectionArea doesn't need resize, because it's set after resizing
topLeftY *= v; return *this;
bottomRightX *= v;
bottomRightY *= v;
}
bool intersects(const SpotBox &other) const {
return (other.topLeftX <= bottomRightX && other.bottomRightX >= topLeftX)
&& (other.topLeftY <= bottomRightY && other.bottomRightY >= topLeftY);
} }
int getWidth() { int getWidth() {
return bottomRightX - topLeftX + 1; return spotArea.x2 - spotArea.x1 + 1;
} }
int getHeight() { int getHeight() {
return bottomRightY - topLeftY + 1; return spotArea.y2 - spotArea.y1 + 1;
} }
};
int getImageWidth() {
return imgArea.x2 - imgArea.x1 + 1;
}
void ImProcFunctions::removeSpots (Imagefloat* img, ImageSource* imgsrc, const std::vector<SpotEntry> &entries, const PreviewProps &pp, const ColorTemp &currWB, int tr) int getImageHeight() {
{ return imgArea.y2 - imgArea.y1 + 1;
// ---------- Get the image areas (src & dst) from the source image }
printf("\n=======================================================================\n\n"); int getIntersectionWidth() {
return intersectionArea.x2 - intersectionArea.x1 + 1;
}
std::vector< std::shared_ptr<SpotBox> > srcSpotBoxs; int getIntersectionHeight() {
std::vector< std::shared_ptr<SpotBox> > dstSpotBoxs; return intersectionArea.y2 - intersectionArea.y1 + 1;
for (auto entry : params->spot.entries) { }
Coord origin;
int size = int(entry.getFeatherRadius() * 2.f + 0.5f);
int scaledSize = int(entry.getFeatherRadius() * 2.f / float(pp.getSkip()) + 0.5f);
//printf("size: %d - skip: %d -> scaledSize: %d", size, pp.getSkip(), scaledSize);
// ------ Source area bool checkImageSize() {
Imagefloat *currSrcSpot = new Imagefloat(scaledSize, scaledSize); if (!image || getImageWidth() != image->getWidth() || getImageHeight() != image->getHeight()) {
for (int y = 0; y < currSrcSpot->getHeight(); ++y) { return false;
for (int x = 0; x < currSrcSpot->getWidth(); ++x) {
currSrcSpot->r(y,x) = 0.f;
currSrcSpot->g(y,x) = 0.f;
currSrcSpot->b(y,x) = 0.f;
}
} }
entry.sourcePos.get(origin.x, origin.y); return true;
origin.x -= entry.getFeatherRadius(); }
origin.y -= entry.getFeatherRadius();
PreviewProps spp(origin.x, origin.y, size, size, pp.getSkip());
imgsrc->getImage(currWB, tr, currSrcSpot, spp, params->toneCurve, params->raw);
//printf(" / src size: %d,%d", currSrcSpot->getWidth(), currSrcSpot->getHeight());
std::shared_ptr<SpotBox> srcSpotBox(new SpotBox(origin.x / pp.getSkip(), origin.y / pp.getSkip(), currSrcSpot, SpotBox::Type::SOURCE)); void tuneImageSize() {
srcSpotBoxs.push_back(srcSpotBox); if (!image) {
return;
// ------ Destination area
Imagefloat *currDstSpot = new Imagefloat(scaledSize, scaledSize);
for (int y = 0; y < currDstSpot->getHeight(); ++y) {
for (int x = 0; x < currDstSpot->getWidth(); ++x) {
currDstSpot->r(y,x) = 0.f;
currDstSpot->g(y,x) = 0.f;
currDstSpot->b(y,x) = 0.f;
}
} }
entry.targetPos.get(origin.x, origin.y); if (getImageWidth() > image->getWidth()) {
origin.x -= entry.getFeatherRadius(); imgArea.x2 = imgArea.x1 + image->getWidth() - 1;
origin.y -= entry.getFeatherRadius(); }
spp.set(origin.x, origin.y, size, size, pp.getSkip()); if (getImageHeight() > image->getHeight()) {
imgsrc->getImage(currWB, tr, currDstSpot, spp, params->toneCurve, params->raw); imgArea.y2 = imgArea.y1 + image->getHeight() - 1;
//printf(" / dst size: %d,%d\n", currDstSpot->getWidth(), currDstSpot->getHeight());
std::shared_ptr<SpotBox> dstSpotBox(new SpotBox(origin.x / pp.getSkip(), origin.y / pp.getSkip(), currDstSpot, SpotBox::Type::TARGET));
dstSpotBoxs.push_back(dstSpotBox);
}
// Filter out out of preview Spots
/*
for (size_t i = entries.size(); i >= 0; ++i) {
float featherRadius = entries.at(i).radius * (1.f + entries.at(i).feather);
SpotBox srcBox(entries.at(i), SpotBox::Type::SOURCE);
srcBox.translate(-pp.getX(), -pp.getY());
srcBox /= float (pp.getSkip());
SpotBox dstBox(entries.at(i), SpotBox::Type::TARGET);
dstBox.translate(-pp.getX(), -pp.getY());
dstBox /= float (pp.getSkip());
}
*/
// ---------- Copy spots from src to dst
for (int i = entries.size() - 1; i >= 0; --i) {
// 1. copy src to dst
std::shared_ptr<SpotBox> srcSpotBox = srcSpotBoxs.at(i);
std::shared_ptr<SpotBox> dstSpotBox = dstSpotBoxs.at(i);
float scaledRadius = float(entries.at(i).radius) / float(pp.getSkip());
float scaledFeatherRadius = entries.at(i).getFeatherRadius() / float(pp.getSkip());
Imagefloat *srcImg = srcSpotBox->img;
Imagefloat *dstImg = dstSpotBox->img;
//printf("#%d: srcSpotBox @ %p - img @ %p / dstSpotBox @ %p - img @ %p\n", i,
// srcSpotBox.get(), srcSpotBox->img, dstSpotBox.get(), dstSpotBox->img );
//printf("#%d: srcSpotBox(%d,%d) srcImg(%d,%d) / dstSpotBox(%d,%d) dstImg(%d,%d)\n", i,
// srcSpotBox->getWidth(), srcSpotBox->getHeight(), srcImg->getWidth(), srcImg->getHeight(),
// dstSpotBox->getWidth(), dstSpotBox->getHeight(), dstImg->getWidth(), dstImg->getHeight()
// );
for (int y = 0; y < srcSpotBox->getHeight(); ++y) {
float dy = float(y - float(srcSpotBox->getHeight()) / 2.f);
for (int x = 0; x < srcSpotBox->getWidth(); ++x) {
float dx = float(x - float(srcSpotBox->getWidth()) / 2.f);
float r = sqrt(dx * dx + dy * dy);
if (r >= scaledFeatherRadius) {
continue;
}
if (r <= scaledRadius) {
dstImg->r(y, x) = srcImg->r(y, x);
dstImg->g(y, x) = srcImg->g(y, x);
dstImg->b(y, x) = srcImg->b(y, x);
} else {
float opacity = (scaledFeatherRadius - r) / (scaledFeatherRadius - scaledRadius);
dstImg->r(y, x) = (srcImg->r(y, x) - dstImg->r(y, x)) * opacity + dstImg->r(y,x);
dstImg->g(y, x) = (srcImg->g(y, x) - dstImg->g(y, x)) * opacity + dstImg->g(y,x);
dstImg->b(y, x) = (srcImg->b(y, x) - dstImg->b(y, x)) * opacity + dstImg->b(y,x);
}
}
} }
//printf("\n\n");
// 2. copy dst to later src and dst
} }
// 3. copy all dst to the finale image Imagefloat *getImage() { // TODO: this should send back a const value, but getImage don't want it to be const...
return image;
}
// Putting the dest image in a SpotBox void allocImage() {
SpotBox imgSpotBox(pp.getX() / pp.getSkip(), pp.getY() / pp.getSkip(), img, SpotBox::Type::TARGET); int newW = imgArea.x2 - imgArea.x1 + 1;
/* int newH = imgArea.y2 - imgArea.y1 + 1;
printf("#--: spotBox(X1:%d, Y1:%d, X2:%d, Y2:%d, W:%d, H:%d) img(W:%d, H:%d)\n\n",
imgSpotBox.topLeftX, imgSpotBox.topLeftY, imgSpotBox.bottomRightX, imgSpotBox.bottomRightY,
imgSpotBox.getWidth(), imgSpotBox.getHeight(),
imgSpotBox.img->getWidth(), imgSpotBox.img->getHeight()
);
*/
for (size_t i = 0; i < entries.size(); ++i) { if (image && type != Type::FINAL && (image->getWidth() != newW || image->getHeight() != newH)) {
// 1. copy src to dst delete image;
std::shared_ptr<SpotBox> dstSpotBox = dstSpotBoxs.at(i); image = nullptr;
Imagefloat *dstImg = dstSpotBox->img; }
if (image == nullptr) {
image = new Imagefloat(newW, newH);
}
}
bool spotIntersects(const SpotBox &other) const {
return spotArea.intersects(other.spotArea);
}
bool getSpotIntersection(const SpotBox &other, std::unique_ptr<Rectangle> &intersection) const {
return spotArea.getIntersection(other.spotArea, intersection);
}
bool imageIntersects(const SpotBox &other, bool atDestLocation=false) const {
if (atDestLocation) {
Coord v(other.spotArea.x1 - spotArea.x1, other.spotArea.y1 - spotArea.y1);
Rectangle imgArea2(imgArea.x1, imgArea.y1, imgArea.x2, imgArea.y2);
imgArea2 += v;
return imgArea2.intersects(other.imgArea);
}
return imgArea.intersects(other.imgArea);
}
bool mutuallyClipImageArea(SpotBox &other) {
Coord v(other.spotArea.x1 - spotArea.x1, other.spotArea.y1 - spotArea.y1);
Rectangle imgArea2 = imgArea;
imgArea2 += v;
std::unique_ptr<Rectangle> intersection;
if (!imgArea2.getIntersection(other.imgArea, intersection)) {
return false;
}
other.intersectionArea = *intersection;
Coord v2(-v.x, -v.y);
*intersection -= v;
intersectionArea = *intersection;
return true;
}
bool setIntersectionWith(const SpotBox &other) {
if (!spotIntersects(other)) {
return false;
}
imgArea.x1 = rtengine::max(spotArea.x1, other.spotArea.x1);
imgArea.x2 = rtengine::min(spotArea.x2, other.spotArea.x2);
imgArea.y1 = rtengine::max(spotArea.y1, other.spotArea.y1);
imgArea.y2 = rtengine::min(spotArea.y2, other.spotArea.y2);
if (imgArea.x1 > imgArea.x2 || imgArea.y1 > imgArea.y2) {
return false;
}
return true;
}
bool processIntersectionWith(SpotBox &destBox) {
Imagefloat *dstImg = destBox.image;
if (image == nullptr || dstImg == 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, printf("#%llu: spotBox(X1:%d, Y1:%d, X2:%d, Y2:%d, W:%d, H:%d) img(W:%d, H:%d)\n", i,
dstSpotBox->topLeftX, dstSpotBox->topLeftY, dstSpotBox->bottomRightX, dstSpotBox->bottomRightY, spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2,
dstSpotBox->getWidth(), dstSpotBox->getHeight(), getWidth(), getHeight(),
dstImg->getWidth(), dstImg->getHeight() destBox.getWidth(), destBox.getHeight()
); );
*/ */
if (dstSpotBox->intersects(imgSpotBox)) { #ifndef NDEBUG
int beginX = rtengine::max(dstSpotBox->topLeftX, imgSpotBox.topLeftX); printf("[processIntersectionWith] srcSpotBox @ %p(%d, %d) - img @ %p(%d, %d) / dstSpotBox @ %p(%d, %d) - img @ %p(%d, %d)\n",
int endX = rtengine::min(dstSpotBox->bottomRightX, imgSpotBox.bottomRightX); this, getWidth(), getHeight(), image, destBox.getWidth(), destBox.getHeight(),
int beginY = rtengine::max(dstSpotBox->topLeftY, imgSpotBox.topLeftY); &destBox, destBox.getWidth(), destBox.getHeight(), destBox.image, dstImg->getWidth(), dstImg->getHeight());
int endY = rtengine::min(dstSpotBox->bottomRightY, imgSpotBox.bottomRightY); 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
//printf("--- Intersection: X1:%d, Y1:%d -> X2:%d, Y2:%d\n", beginX, beginY, endX, endY); int srcImgY = intersectionArea.y1 - imgArea.y1;
int dstImgY = destBox.intersectionArea.y1 - destBox.imgArea.y1;
for (int y = intersectionArea.y1; y <= intersectionArea.y2; ++y) {
float dy = float(y - spotArea.y1) - featherRadius;
int dstSpotOffsetY = beginY - dstSpotBox->topLeftY; int srcImgX = intersectionArea.x1 - imgArea.x1;
int imgOffsetY = beginY - imgSpotBox.topLeftY; int dstImgX = destBox.intersectionArea.x1 - destBox.imgArea.x1;
for (int x = intersectionArea.x1; x <= intersectionArea.x2; ++x) {
float dx = float(x - spotArea.x1) - featherRadius;
float r = sqrt(dx * dx + dy * dy);
for (int y = beginY; y <= endY; ++y) { if (r >= featherRadius) {
int dstSpotOffsetX = beginX - dstSpotBox->topLeftX; ++srcImgX;
int imgOffsetX = beginX - imgSpotBox.topLeftX; ++dstImgX;
continue;
for (int x = beginX; x <= endX; ++x) { }
/* if (r <= radius) {
if (y == beginY && x == beginX) { dstImg->r(dstImgY, dstImgX) = image->r(srcImgY, srcImgX);
printf("--- dstSpotOffsetX = beginX - dstSpotBox->topLeftX = %d - %d = %d\n", beginX, dstSpotBox->topLeftX, dstSpotOffsetX); dstImg->g(dstImgY, dstImgX) = image->g(srcImgY, srcImgX);
printf("--- dstSpotOffsetY = beginY - dstSpotBox->topLeftY = %d - %d = %d\n", beginY, dstSpotBox->topLeftY, dstSpotOffsetY); dstImg->b(dstImgY, dstImgX) = image->b(srcImgY, srcImgX);
printf("--- imgOffsetX = beginX - imgSpotBox.topLeftX = %d - %d = %d\n", beginX, imgSpotBox.topLeftX, imgOffsetX); } else {
printf("--- imgOffsetX = beginY - imgSpotBox.topLeftY = %d - %d = %d\n", beginY, imgSpotBox.topLeftY, imgOffsetY); float opacity = (featherRadius - r) / (featherRadius - radius);
} dstImg->r(dstImgY, dstImgX) = (image->r(srcImgY, srcImgX) - dstImg->r(dstImgY, dstImgX)) * opacity + dstImg->r(dstImgY,dstImgX);
*/ dstImg->g(dstImgY, dstImgX) = (image->g(srcImgY, srcImgX) - dstImg->g(dstImgY, dstImgX)) * opacity + dstImg->g(dstImgY,dstImgX);
img->r(imgOffsetY, imgOffsetX) = dstImg->r(dstSpotOffsetY, dstSpotOffsetX); dstImg->b(dstImgY, dstImgX) = (image->b(srcImgY, srcImgX) - dstImg->b(dstImgY, dstImgX)) * opacity + dstImg->b(dstImgY,dstImgX);
img->g(imgOffsetY, imgOffsetX) = dstImg->g(dstSpotOffsetY, dstSpotOffsetX); }
img->b(imgOffsetY, imgOffsetX) = dstImg->b(dstSpotOffsetY, dstSpotOffsetX); ++srcImgX;
++imgOffsetX; ++dstImgX;
++dstSpotOffsetX; }
++srcImgY;
++dstImgY;
}
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<Rectangle> 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<SpotEntry> &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<SpotBox> > srcSpotBoxs;
std::vector< std::shared_ptr<SpotBox> > 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<int> visibleSpots; // list of dest spots intersecting the preview's crop
int i = 0;
for (auto entry : params->spot.entries) {
std::shared_ptr<SpotBox> srcSpotBox(new SpotBox(entry, SpotBox::Type::SOURCE));
std::shared_ptr<SpotBox> 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());
// 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<int> 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
} }
++imgOffsetY;
++dstSpotOffsetY;
} }
//} else {
// printf("#%llu: No intersection !\n", i);
} }
} }
#ifndef NDEBUG
printf("\n");
#endif
for (auto srcSpotBox : srcSpotBoxs) { // Process spots and copy them downstream
delete srcSpotBox->img;
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<int> 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
} }
for (auto dstSpotBox : dstSpotBoxs) {
delete dstSpotBox->img; // 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 #endif

View File

@@ -1,6 +1,22 @@
/* /*
* This file is part of RawTherapee. * This file is part of RawTherapee.
*
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/ */
#include "editcallbacks.h" #include "editcallbacks.h"
#include "spot.h" #include "spot.h"
#include "rtimage.h" #include "rtimage.h"

View File

@@ -1,6 +1,22 @@
/* /*
* This file is part of RawTherapee. * This file is part of RawTherapee.
*
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/ */
#ifndef _SPOT_H_ #ifndef _SPOT_H_
#define _SPOT_H_ #define _SPOT_H_