Take auto perspective correction files from ART
This commit is contained in:
parent
f83a62be5b
commit
d9886d58f9
4991
rtengine/ashift_dt.c
Normal file
4991
rtengine/ashift_dt.c
Normal file
File diff suppressed because it is too large
Load Diff
2335
rtengine/ashift_lsd.c
Normal file
2335
rtengine/ashift_lsd.c
Normal file
File diff suppressed because it is too large
Load Diff
425
rtengine/ashift_nmsimplex.c
Normal file
425
rtengine/ashift_nmsimplex.c
Normal 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;
|
433
rtengine/perspectivecorrection.cc
Normal file
433
rtengine/perspectivecorrection.cc
Normal 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 ¶ms, 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 ¶ms, 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 ¶ms, 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, ¶ms);
|
||||||
|
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 ¶ms, 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
|
55
rtengine/perspectivecorrection.h
Normal file
55
rtengine/perspectivecorrection.h
Normal 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 ¶ms, 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 ¶ms, 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 ¶ms, bool fill);
|
||||||
|
|
||||||
|
bool ok_;
|
||||||
|
double scale_;
|
||||||
|
double offx_;
|
||||||
|
double offy_;
|
||||||
|
float ihomograph_[3][3];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rtengine
|
Loading…
x
Reference in New Issue
Block a user