Take auto perspective correction files from ART

This commit is contained in:
Lawrence 2020-01-13 17:10:25 -08:00
parent f83a62be5b
commit d9886d58f9
5 changed files with 8239 additions and 0 deletions

4991
rtengine/ashift_dt.c Normal file

File diff suppressed because it is too large Load Diff

2335
rtengine/ashift_lsd.c Normal file

File diff suppressed because it is too large Load Diff

425
rtengine/ashift_nmsimplex.c Normal file
View File

@ -0,0 +1,425 @@
/*
This file is part of darktable,
copyright (c) 2016 Ulrich Pegelow.
darktable 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.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/
/* For parameter optimization we are using the Nelder-Mead simplex method
* implemented by Michael F. Hutt.
* Changes versus the original code:
* do not include "nmsimplex.h" (not needed)
* renamed configuration variables to NMS_*
* add additional argument to objfun for arbitrary parameters
* simplex() returns number of used iterations instead of min value
* maximum number of iterations as function parameter
* make interface function simplex() static
* initialize i and j to avoid compiler warnings
* comment out printing of status inormation
* reformat according to darktable's clang standards
*/
/*==================================================================================
* begin nmsimplex code downloaded from http://www.mikehutt.com/neldermead.html
* on February 6, 2016
*==================================================================================*/
/*
* Program: nmsimplex.c
* Author : Michael F. Hutt
* http://www.mikehutt.com
* 11/3/97
*
* An implementation of the Nelder-Mead simplex method.
*
* Copyright (c) 1997-2011 <Michael F. Hutt>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* Jan. 6, 1999
* Modified to conform to the algorithm presented
* in Margaret H. Wright's paper on Direct Search Methods.
*
* Jul. 23, 2007
* Fixed memory leak.
*
* Mar. 1, 2011
* Added constraints.
*/
//#include "nmsimplex.h"
static int simplex(double (*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale,
int maxiter, void (*constrain)(double[], int n), void *params)
{
int vs; /* vertex with smallest value */
int vh; /* vertex with next smallest value */
int vg; /* vertex with largest value */
int i = 0, j = 0, m, row;
int k; /* track the number of function evaluations */
int itr; /* track the number of iterations */
double **v; /* holds vertices of simplex */
double pn, qn; /* values used to create initial simplex */
double *f; /* value of function at each vertex */
double fr; /* value of function at reflection point */
double fe; /* value of function at expansion point */
double fc; /* value of function at contraction point */
double *vr; /* reflection - coordinates */
double *ve; /* expansion - coordinates */
double *vc; /* contraction - coordinates */
double *vm; /* centroid - coordinates */
//double min;
double fsum, favg, s, cent;
/* dynamically allocate arrays */
/* allocate the rows of the arrays */
v = (double **)malloc((n + 1) * sizeof(double *));
f = (double *)malloc((n + 1) * sizeof(double));
vr = (double *)malloc(n * sizeof(double));
ve = (double *)malloc(n * sizeof(double));
vc = (double *)malloc(n * sizeof(double));
vm = (double *)malloc(n * sizeof(double));
/* allocate the columns of the arrays */
for(i = 0; i <= n; i++)
{
v[i] = (double *)malloc(n * sizeof(double));
}
/* create the initial simplex */
/* assume one of the vertices is 0,0 */
pn = scale * (sqrt(n + 1) - 1 + n) / (n * sqrt(2));
qn = scale * (sqrt(n + 1) - 1) / (n * sqrt(2));
for(i = 0; i < n; i++)
{
v[0][i] = start[i];
}
for(i = 1; i <= n; i++)
{
for(j = 0; j < n; j++)
{
if(i - 1 == j)
{
v[i][j] = pn + start[j];
}
else
{
v[i][j] = qn + start[j];
}
}
}
if(constrain != NULL)
{
constrain(v[j], n);
}
/* find the initial function values */
for(j = 0; j <= n; j++)
{
f[j] = objfunc(v[j], params);
}
k = n + 1;
#if 0
/* print out the initial values */
printf("Initial Values\n");
for(j = 0; j <= n; j++)
{
for(i = 0; i < n; i++)
{
printf("%f %f\n", v[j][i], f[j]);
}
}
#endif
/* begin the main loop of the minimization */
for(itr = 1; itr <= maxiter; itr++)
{
/* find the index of the largest value */
vg = 0;
for(j = 0; j <= n; j++)
{
if(f[j] > f[vg])
{
vg = j;
}
}
/* find the index of the smallest value */
vs = 0;
for(j = 0; j <= n; j++)
{
if(f[j] < f[vs])
{
vs = j;
}
}
/* find the index of the second largest value */
vh = vs;
for(j = 0; j <= n; j++)
{
if(f[j] > f[vh] && f[j] < f[vg])
{
vh = j;
}
}
/* calculate the centroid */
for(j = 0; j <= n - 1; j++)
{
cent = 0.0;
for(m = 0; m <= n; m++)
{
if(m != vg)
{
cent += v[m][j];
}
}
vm[j] = cent / n;
}
/* reflect vg to new vertex vr */
for(j = 0; j <= n - 1; j++)
{
/*vr[j] = (1+NMS_ALPHA)*vm[j] - NMS_ALPHA*v[vg][j];*/
vr[j] = vm[j] + NMS_ALPHA * (vm[j] - v[vg][j]);
}
if(constrain != NULL)
{
constrain(vr, n);
}
fr = objfunc(vr, params);
k++;
if(fr < f[vh] && fr >= f[vs])
{
for(j = 0; j <= n - 1; j++)
{
v[vg][j] = vr[j];
}
f[vg] = fr;
}
/* investigate a step further in this direction */
if(fr < f[vs])
{
for(j = 0; j <= n - 1; j++)
{
/*ve[j] = NMS_GAMMA*vr[j] + (1-NMS_GAMMA)*vm[j];*/
ve[j] = vm[j] + NMS_GAMMA * (vr[j] - vm[j]);
}
if(constrain != NULL)
{
constrain(ve, n);
}
fe = objfunc(ve, params);
k++;
/* by making fe < fr as opposed to fe < f[vs],
Rosenbrocks function takes 63 iterations as opposed
to 64 when using double variables. */
if(fe < fr)
{
for(j = 0; j <= n - 1; j++)
{
v[vg][j] = ve[j];
}
f[vg] = fe;
}
else
{
for(j = 0; j <= n - 1; j++)
{
v[vg][j] = vr[j];
}
f[vg] = fr;
}
}
/* check to see if a contraction is necessary */
if(fr >= f[vh])
{
if(fr < f[vg] && fr >= f[vh])
{
/* perform outside contraction */
for(j = 0; j <= n - 1; j++)
{
/*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
vc[j] = vm[j] + NMS_BETA * (vr[j] - vm[j]);
}
if(constrain != NULL)
{
constrain(vc, n);
}
fc = objfunc(vc, params);
k++;
}
else
{
/* perform inside contraction */
for(j = 0; j <= n - 1; j++)
{
/*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/
vc[j] = vm[j] - NMS_BETA * (vm[j] - v[vg][j]);
}
if(constrain != NULL)
{
constrain(vc, n);
}
fc = objfunc(vc, params);
k++;
}
if(fc < f[vg])
{
for(j = 0; j <= n - 1; j++)
{
v[vg][j] = vc[j];
}
f[vg] = fc;
}
/* at this point the contraction is not successful,
we must halve the distance from vs to all the
vertices of the simplex and then continue.
10/31/97 - modified to account for ALL vertices.
*/
else
{
for(row = 0; row <= n; row++)
{
if(row != vs)
{
for(j = 0; j <= n - 1; j++)
{
v[row][j] = v[vs][j] + (v[row][j] - v[vs][j]) / 2.0;
}
}
}
if(constrain != NULL)
{
constrain(v[vg], n);
}
f[vg] = objfunc(v[vg], params);
k++;
if(constrain != NULL)
{
constrain(v[vh], n);
}
f[vh] = objfunc(v[vh], params);
k++;
}
}
#if 0
/* print out the value at each iteration */
printf("Iteration %d\n", itr);
for(j = 0; j <= n; j++)
{
for(i = 0; i < n; i++)
{
printf("%f %f\n", v[j][i], f[j]);
}
}
#endif
/* test for convergence */
fsum = 0.0;
for(j = 0; j <= n; j++)
{
fsum += f[j];
}
favg = fsum / (n + 1);
s = 0.0;
for(j = 0; j <= n; j++)
{
s += pow((f[j] - favg), 2.0) / (n);
}
s = sqrt(s);
if(s < EPSILON) break;
}
/* end main loop of the minimization */
/* find the index of the smallest value */
vs = 0;
for(j = 0; j <= n; j++)
{
if(f[j] < f[vs])
{
vs = j;
}
}
#if 0
printf("The minimum was found at\n");
for(j = 0; j < n; j++)
{
printf("%e\n", v[vs][j]);
start[j] = v[vs][j];
}
double min = objfunc(v[vs], params);
k++;
printf("The minimum value is %f\n", min);
printf("%d Function Evaluations\n", k);
printf("%d Iterations through program\n", itr);
#else
for(j = 0; j < n; j++)
{
start[j] = v[vs][j];
}
#endif
free(f);
free(vr);
free(ve);
free(vc);
free(vm);
for(i = 0; i <= n; i++)
{
free(v[i]);
}
free(v);
return itr;
}
/*==================================================================================
* end of nmsimplex code
*==================================================================================*/
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;

View File

@ -0,0 +1,433 @@
/* -*- C++ -*-
*
* This file is part of RawTherapee.
*
* Copyright (c) 2019 Alberto Griggio <alberto.griggio@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/>.
*/
// taken from darktable (src/iop/ashift.c)
/*
This file is part of darktable,
copyright (c) 2016 Ulrich Pegelow.
darktable 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.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/
// Inspiration to this module comes from the program ShiftN (http://www.shiftn.de) by
// Marcus Hebel.
// Thanks to Marcus for his support when implementing part of the ShiftN functionality
// to darktable.
#include "perspectivecorrection.h"
#include "improcfun.h"
#include "rt_math.h"
#include <string.h>
#include <math.h>
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "../rtgui/threadutils.h"
#include "settings.h"
namespace rtengine { extern const Settings *settings; }
#define _(msg) (msg)
#define dt_control_log(msg) \
if (settings->verbose) { \
printf("%s\n", msg); \
fflush(stdout); \
}
namespace rtengine {
namespace {
inline int mat3inv(float *const dst, const float *const src)
{
std::array<std::array<float, 3>, 3> tmpsrc;
std::array<std::array<float, 3>, 3> tmpdst;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
tmpsrc[i][j] = src[3 * i + j];
}
}
if (invertMatrix(tmpsrc, tmpdst)) {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
dst[3 * i + j] = tmpdst[i][j];
}
}
return 0;
} else {
return 1;
}
}
// the darktable ashift iop (adapted to RT), which does most of the work
#include "ashift_dt.c"
procparams::PerspectiveParams import_meta(const procparams::PerspectiveParams &pp, const FramesMetaData *metadata)
{
procparams::PerspectiveParams ret(pp);
if (metadata && ret.flength == 0) {
double f = metadata->getFocalLen();
double f35 = metadata->getFocalLen35mm();
if (f > 0 && f35 > 0) {
ret.flength = f;
ret.cropfactor = f35 / f;
} else if (f > 0) {
ret.flength = f;
}
}
return ret;
}
} // namespace
PerspectiveCorrection::PerspectiveCorrection():
ok_(false),
scale_(1.0),
offx_(0.0),
offy_(0.0)
{
}
void PerspectiveCorrection::init(int width, int height, const procparams::PerspectiveParams &params, bool fill, const FramesMetaData *metadata)
{
if (params.enabled) {
auto pp = import_meta(params, metadata);
homography((float *)ihomograph_, params.angle, params.vertical / 100.0, -params.horizontal / 100.0, params.shear / 100.0, params.flength * params.cropfactor, 100.f, params.aspect, width, height, ASHIFT_HOMOGRAPH_INVERTED);
ok_ = true;
calc_scale(width, height, pp, fill);
} else {
ok_ = false;
}
}
inline void PerspectiveCorrection::correct(double &x, double &y, double scale, double offx, double offy)
{
if (ok_) {
float pin[3], pout[3];
pout[0] = x;
pout[1] = y;
pout[0] *= scale;
pout[1] *= scale;
pout[0] += offx;
pout[1] += offy;
pout[2] = 1.f;
mat3mulv(pin, (float *)ihomograph_, pout);
pin[0] /= pin[2];
pin[1] /= pin[2];
x = pin[0];
y = pin[1];
}
}
void PerspectiveCorrection::operator()(double &x, double &y)
{
correct(x, y, scale_, offx_, offy_);
}
namespace {
std::vector<Coord2D> get_corners(int w, int h)
{
int x1 = 0, y1 = 0;
int x2 = w, y2 = h;
std::vector<Coord2D> corners = {
Coord2D(x1, y1),
Coord2D(x1, y2),
Coord2D(x2, y2),
Coord2D(x2, y1)
};
return corners;
}
void init_dt_structures(dt_iop_ashift_params_t *p, dt_iop_ashift_gui_data_t *g,
const procparams::PerspectiveParams *params)
{
dt_iop_ashift_params_t dp = {
0.0f,
0.0f,
0.0f,
0.0f,
DEFAULT_F_LENGTH,
1.f,
0.0f,
1.0f,
ASHIFT_MODE_SPECIFIC,
0,
ASHIFT_CROP_OFF,
0.0f,
1.0f,
0.0f,
1.0f
};
*p = dp;
g->buf = NULL;
g->buf_width = 0;
g->buf_height = 0;
g->buf_x_off = 0;
g->buf_y_off = 0;
g->buf_scale = 1.0f;
g->buf_hash = 0;
g->isflipped = 0;
g->lastfit = ASHIFT_FIT_NONE;
g->fitting = 0;
g->lines = NULL;
g->lines_count =0;
g->horizontal_count = 0;
g->vertical_count = 0;
g->grid_hash = 0;
g->lines_hash = 0;
g->rotation_range = ROTATION_RANGE_SOFT;
g->lensshift_v_range = LENSSHIFT_RANGE_SOFT;
g->lensshift_h_range = LENSSHIFT_RANGE_SOFT;
g->shear_range = SHEAR_RANGE_SOFT;
g->lines_suppressed = 0;
g->lines_version = 0;
g->show_guides = 0;
g->isselecting = 0;
g->isdeselecting = 0;
g->isbounding = ASHIFT_BOUNDING_OFF;
g->near_delta = 0;
g->selecting_lines_version = 0;
g->points = NULL;
g->points_idx = NULL;
g->points_lines_count = 0;
g->points_version = 0;
g->jobcode = ASHIFT_JOBCODE_NONE;
g->jobparams = 0;
g->adjust_crop = FALSE;
g->lastx = g->lasty = -1.0f;
g->crop_cx = g->crop_cy = 1.0f;
if (params) {
p->rotation = params->angle;
p->lensshift_v = params->vertical / 100.0;
p->lensshift_h = -params->horizontal / 100.0;
p->shear = params->shear / 100.0;
p->f_length = params->flength;
p->crop_factor = params->cropfactor;
p->aspect = params->aspect;
}
}
void get_view_size(int w, int h, const procparams::PerspectiveParams &params, double &cw, double &ch)
{
double min_x = RT_INFINITY, max_x = -RT_INFINITY;
double min_y = RT_INFINITY, max_y = -RT_INFINITY;
auto corners = get_corners(w, h);
float homo[3][3];
homography((float *)homo, params.angle, params.vertical / 100.0, -params.horizontal / 100.0, params.shear / 100.0, params.flength * params.cropfactor, 100.f, params.aspect, w, h, ASHIFT_HOMOGRAPH_FORWARD);
for (auto &c : corners) {
float pin[3] = { float(c.x), float(c.y), 1.f };
float pout[3];
mat3mulv(pout, (float *)homo, pin);
double x = pout[0] / pout[2];
double y = pout[1] / pout[2];
min_x = min(min_x, x);
max_x = max(max_x, x);
min_y = min(min_y, y);
max_y = max(max_y, y);
}
cw = max_x - min_x;
ch = max_y - min_y;
}
} // namespace
void PerspectiveCorrection::calc_scale(int w, int h, const procparams::PerspectiveParams &params, bool fill)
{
double cw, ch;
get_view_size(w, h, params, cw, ch);
if (!fill) {
scale_ = max(cw / double(w), ch / double(h));
offx_ = (cw - w * scale_) * 0.5;
offy_ = (ch - h * scale_) * 0.5;
} else {
dt_iop_ashift_params_t p;
dt_iop_ashift_gui_data_t g;
init_dt_structures(&p, &g, &params);
dt_iop_module_t module = { &g, false };
g.buf_width = w;
g.buf_height = h;
p.cropmode = ASHIFT_CROP_ASPECT;
do_crop(&module, &p);
offx_ = p.cl * cw;
offy_ = p.ct * ch;
scale_ = (p.cr - p.cl) * cw/double(w);
}
}
procparams::PerspectiveParams PerspectiveCorrection::autocompute(ImageSource *src, Direction dir, const procparams::ProcParams *pparams, const FramesMetaData *metadata)
{
auto pcp = import_meta(pparams->perspective, metadata);
procparams::PerspectiveParams dflt;
pcp.horizontal = dflt.horizontal;
pcp.vertical = dflt.vertical;
pcp.angle = dflt.angle;
pcp.shear = dflt.shear;
dt_iop_ashift_params_t p;
dt_iop_ashift_gui_data_t g;
init_dt_structures(&p, &g, &pcp);
dt_iop_module_t module;
module.gui_data = &g;
module.is_raw = src->isRAW();
int tr = getCoarseBitMask(pparams->coarse);
int fw, fh;
src->getFullSize(fw, fh, tr);
int skip = max(float(max(fw, fh)) / 900.f + 0.5f, 1.f);
PreviewProps pp(0, 0, fw, fh, skip);
int w, h;
src->getSize(pp, w, h);
std::unique_ptr<Imagefloat> img(new Imagefloat(w, h));
ProcParams neutral;
neutral.raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
neutral.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
neutral.icm.outputProfile = ColorManagementParams::NoICMString;
src->getImage(src->getWB(), tr, img.get(), pp, neutral.exposure, neutral.raw);
src->convertColorSpace(img.get(), pparams->icm, src->getWB());
neutral.rotate = pparams->rotate;
neutral.distortion = pparams->distortion;
neutral.lensProf = pparams->lensProf;
ImProcFunctions ipf(&neutral, true);
if (ipf.needsTransform()) {
Imagefloat *tmp = new Imagefloat(w, h);
ipf.transform(img.get(), tmp, 0, 0, 0, 0, w, h, w, h,
src->getMetaData(), src->getRotateDegree(), false);
img.reset(tmp);
}
// allocate the gui buffer
g.buf = static_cast<float *>(malloc(sizeof(float) * w * h * 4));
g.buf_width = w;
g.buf_height = h;
img->normalizeFloatTo1();
#ifdef _OPENMP
# pragma omp parallel for
#endif
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
int i = (y * w + x) * 4;
g.buf[i] = img->r(y, x);
g.buf[i+1] = img->g(y, x);
g.buf[i+2] = img->b(y, x);
g.buf[i+3] = 1.f;
}
}
dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE;
switch (dir) {
case HORIZONTAL:
fitaxis = ASHIFT_FIT_HORIZONTALLY;
break;
case VERTICAL:
fitaxis = ASHIFT_FIT_VERTICALLY;
break;
default:
fitaxis = ASHIFT_FIT_BOTH_SHEAR;
break;
}
// reset the pseudo-random seed for repeatability -- ashift_dt uses rand()
// internally!
srand(1);
auto res = do_get_structure(&module, &p, ASHIFT_ENHANCE_EDGES) && do_fit(&module, &p, fitaxis);
procparams::PerspectiveParams retval = pparams->perspective;
// cleanup the gui
if (g.lines) free(g.lines);
if (g.points) free(g.points);
if (g.points_idx) free(g.points_idx);
free(g.buf);
if (res) {
retval.horizontal = -p.lensshift_h * 100;
retval.vertical = p.lensshift_v * 100;
retval.angle = p.rotation;
retval.shear = p.shear * 100;
}
return retval;
}
void PerspectiveCorrection::autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams &params, const FramesMetaData *metadata, int &x, int &y, int &w, int &h)
{
auto pp = import_meta(params, metadata);
double cw, ch;
get_view_size(width, height, params, cw, ch);
double s = min(double(width)/cw, double(height)/ch);
dt_iop_ashift_params_t p;
dt_iop_ashift_gui_data_t g;
init_dt_structures(&p, &g, &pp);
dt_iop_module_t module = { &g, false };
g.buf_width = width;
g.buf_height = height;
p.cropmode = fixratio ? ASHIFT_CROP_ASPECT : ASHIFT_CROP_LARGEST;
do_crop(&module, &p);
cw *= s;
ch *= s;
double ox = p.cl * cw;
double oy = p.ct * ch;
x = ox - (cw - width)/2.0 + 0.5;
y = oy - (ch - height)/2.0 + 0.5;
w = (p.cr - p.cl) * cw;
h = (p.cb - p.ct) * ch;
}
} // namespace rtengine

View File

@ -0,0 +1,55 @@
/* -*- C++ -*-
*
* This file is part of RawTherapee.
*
* Copyright (c) 2019 Alberto Griggio <alberto.griggio@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/>.
*/
#pragma once
#include "coord2d.h"
#include "procparams.h"
#include "imagesource.h"
namespace rtengine {
class PerspectiveCorrection {
public:
PerspectiveCorrection();
void init(int width, int height, const procparams::PerspectiveParams &params, bool fill, const FramesMetaData *meta);
void operator()(double &x, double &y);
enum Direction {
HORIZONTAL,
VERTICAL,
BOTH
};
static procparams::PerspectiveParams autocompute(ImageSource *src, Direction dir, const procparams::ProcParams *pparams, const FramesMetaData *metadata);
static void autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams &params, const FramesMetaData *metadata, int &x, int &y, int &w, int &h);
private:
void correct(double &x, double &y, double scale, double offx, double offy);
void calc_scale(int w, int h, const procparams::PerspectiveParams &params, bool fill);
bool ok_;
double scale_;
double offx_;
double offy_;
float ihomograph_[3][3];
};
} // namespace rtengine