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.contrast = 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
// 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
curve1 (65536);

View File

@@ -21,6 +21,19 @@
#include "alpha.h"
#include "procparams.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
{
@@ -58,271 +71,597 @@ class SpotBox {
public:
enum class Type {
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:
Type type;
Imagefloat* image;
public:
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;
Imagefloat* img;
// top/left and bottom/right coordinates of the spot in image space (at some point divided by scale factor)
Rectangle spotArea;
// top/left and bottom/right coordinates of the spot in scaled image space (on borders, imgArea won't cover spotArea)
Rectangle imgArea;
// 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),
topLeftX(tl_x),
topLeftY(tl_y),
bottomRightX(br_x),
bottomRightY(br_y),
img(nullptr)
image(image),
spotArea(tl_x, tl_y, br_x, br_y),
imgArea(spotArea),
intersectionArea(),
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),
topLeftX(tl_x),
topLeftY(tl_y),
bottomRightX(image ? tl_x + image->getWidth() - 1 : 0),
bottomRightY(image ? tl_y + image->getHeight() - 1 : 0),
img(image)
image(image),
spotArea(tl_x, tl_y, image ? tl_x + image->getWidth() - 1 : 0, image ? tl_y + image->getHeight() - 1 : 0),
imgArea(spotArea),
intersectionArea(),
radius(radius),
featherRadius(feather_radius)
{}
SpotBox (SpotEntry &spot, 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);
topLeftX = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) - featherRadius);
bottomRightX = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) + featherRadius);
topLeftY = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) - featherRadius);
bottomRightY = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) + featherRadius);
spotArea.x1 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) - featherRadius);
spotArea.x2 = int ((type == Type::SOURCE ? spot.sourcePos.x : spot.targetPos.x) + featherRadius);
spotArea.y1 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) - featherRadius);
spotArea.y2 = int ((type == Type::SOURCE ? spot.sourcePos.y : spot.targetPos.y) + featherRadius);
imgArea = spotArea;
}
void translate(int dx, int dy) {
topLeftX += dx;
topLeftY += dy;
bottomRightX += dx;
bottomRightY += dy;
~SpotBox() {
if (image && type != Type::FINAL) {
delete image;
}
}
void operator /(float v) {
topLeftX = int(topLeftX / v + 0.5f);
topLeftY = int(topLeftY / v + 0.5f);
bottomRightX = int(bottomRightX / v + 0.5f);
bottomRightY = int(bottomRightY / v + 0.5f);
}
void operator *(float v) {
topLeftX *= v;
topLeftY *= v;
bottomRightX *= v;
bottomRightY *= v;
}
bool intersects(const SpotBox &other) const {
return (other.topLeftX <= bottomRightX && other.bottomRightX >= topLeftX)
&& (other.topLeftY <= bottomRightY && other.bottomRightY >= topLeftY);
SpotBox& operator /=(const int& v) {
if (v == 1) {
return *this;
}
spotArea /= v;
imgArea /= v;
radius = radius / float(v);
featherRadius = getWidth() / 2.f;
// intersectionArea doesn't need resize, because it's set after resizing
return *this;
}
int getWidth() {
return bottomRightX - topLeftX + 1;
return spotArea.x2 - spotArea.x1 + 1;
}
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)
{
// ---------- Get the image areas (src & dst) from the source image
int getImageHeight() {
return imgArea.y2 - imgArea.y1 + 1;
}
printf("\n=======================================================================\n\n");
int getIntersectionWidth() {
return intersectionArea.x2 - intersectionArea.x1 + 1;
}
std::vector< std::shared_ptr<SpotBox> > srcSpotBoxs;
std::vector< std::shared_ptr<SpotBox> > dstSpotBoxs;
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);
int getIntersectionHeight() {
return intersectionArea.y2 - intersectionArea.y1 + 1;
}
// ------ Source area
Imagefloat *currSrcSpot = new Imagefloat(scaledSize, scaledSize);
for (int y = 0; y < currSrcSpot->getHeight(); ++y) {
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;
}
bool checkImageSize() {
if (!image || getImageWidth() != image->getWidth() || getImageHeight() != image->getHeight()) {
return false;
}
entry.sourcePos.get(origin.x, origin.y);
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());
return true;
}
std::shared_ptr<SpotBox> srcSpotBox(new SpotBox(origin.x / pp.getSkip(), origin.y / pp.getSkip(), currSrcSpot, SpotBox::Type::SOURCE));
srcSpotBoxs.push_back(srcSpotBox);
// ------ 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;
}
void tuneImageSize() {
if (!image) {
return;
}
entry.targetPos.get(origin.x, origin.y);
origin.x -= entry.getFeatherRadius();
origin.y -= entry.getFeatherRadius();
spp.set(origin.x, origin.y, size, size, pp.getSkip());
imgsrc->getImage(currWB, tr, currDstSpot, spp, params->toneCurve, params->raw);
//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);
}
}
if (getImageWidth() > image->getWidth()) {
imgArea.x2 = imgArea.x1 + image->getWidth() - 1;
}
if (getImageHeight() > image->getHeight()) {
imgArea.y2 = imgArea.y1 + image->getHeight() - 1;
}
//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
SpotBox imgSpotBox(pp.getX() / pp.getSkip(), pp.getY() / pp.getSkip(), img, SpotBox::Type::TARGET);
/*
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()
);
*/
void allocImage() {
int newW = imgArea.x2 - imgArea.x1 + 1;
int newH = imgArea.y2 - imgArea.y1 + 1;
for (size_t i = 0; i < entries.size(); ++i) {
// 1. copy src to dst
std::shared_ptr<SpotBox> dstSpotBox = dstSpotBoxs.at(i);
Imagefloat *dstImg = dstSpotBox->img;
if (image && type != Type::FINAL && (image->getWidth() != newW || image->getHeight() != newH)) {
delete image;
image = nullptr;
}
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,
dstSpotBox->topLeftX, dstSpotBox->topLeftY, dstSpotBox->bottomRightX, dstSpotBox->bottomRightY,
dstSpotBox->getWidth(), dstSpotBox->getHeight(),
dstImg->getWidth(), dstImg->getHeight()
spotArea.x1, spotArea.y1, spotArea.x2, spotArea.y2,
getWidth(), getHeight(),
destBox.getWidth(), destBox.getHeight()
);
*/
if (dstSpotBox->intersects(imgSpotBox)) {
int beginX = rtengine::max(dstSpotBox->topLeftX, imgSpotBox.topLeftX);
int endX = rtengine::min(dstSpotBox->bottomRightX, imgSpotBox.bottomRightX);
int beginY = rtengine::max(dstSpotBox->topLeftY, imgSpotBox.topLeftY);
int endY = rtengine::min(dstSpotBox->bottomRightY, imgSpotBox.bottomRightY);
#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
//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 imgOffsetY = beginY - imgSpotBox.topLeftY;
int srcImgX = intersectionArea.x1 - imgArea.x1;
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) {
int dstSpotOffsetX = beginX - dstSpotBox->topLeftX;
int imgOffsetX = beginX - imgSpotBox.topLeftX;
for (int x = beginX; x <= endX; ++x) {
/*
if (y == beginY && x == beginX) {
printf("--- dstSpotOffsetX = beginX - dstSpotBox->topLeftX = %d - %d = %d\n", beginX, dstSpotBox->topLeftX, dstSpotOffsetX);
printf("--- dstSpotOffsetY = beginY - dstSpotBox->topLeftY = %d - %d = %d\n", beginY, dstSpotBox->topLeftY, dstSpotOffsetY);
printf("--- imgOffsetX = beginX - imgSpotBox.topLeftX = %d - %d = %d\n", beginX, imgSpotBox.topLeftX, imgOffsetX);
printf("--- imgOffsetX = beginY - imgSpotBox.topLeftY = %d - %d = %d\n", beginY, imgSpotBox.topLeftY, imgOffsetY);
}
*/
img->r(imgOffsetY, imgOffsetX) = dstImg->r(dstSpotOffsetY, dstSpotOffsetX);
img->g(imgOffsetY, imgOffsetX) = dstImg->g(dstSpotOffsetY, dstSpotOffsetX);
img->b(imgOffsetY, imgOffsetX) = dstImg->b(dstSpotOffsetY, dstSpotOffsetX);
++imgOffsetX;
++dstSpotOffsetX;
if (r >= featherRadius) {
++srcImgX;
++dstImgX;
continue;
}
if (r <= radius) {
dstImg->r(dstImgY, dstImgX) = image->r(srcImgY, srcImgX);
dstImg->g(dstImgY, dstImgX) = image->g(srcImgY, srcImgX);
dstImg->b(dstImgY, dstImgX) = image->b(srcImgY, srcImgX);
} else {
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);
dstImg->b(dstImgY, dstImgX) = (image->b(srcImgY, srcImgX) - dstImg->b(dstImgY, dstImgX)) * opacity + dstImg->b(dstImgY,dstImgX);
}
++srcImgX;
++dstImgX;
}
++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) {
delete srcSpotBox->img;
// 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<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

View File

@@ -1,6 +1,22 @@
/*
* 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 "spot.h"
#include "rtimage.h"

View File

@@ -1,6 +1,22 @@
/*
* 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_
#define _SPOT_H_