94#define ROTATION_RANGE 10
95#define ROTATION_RANGE_SOFT 180
96#define LENSSHIFT_RANGE 1.0
97#define LENSSHIFT_RANGE_SOFT 2.0
98#define SHEAR_RANGE 0.2
99#define SHEAR_RANGE_SOFT 0.5
100#define MIN_LINE_LENGTH 5
101#define MAX_TANGENTIAL_DEVIATION 30
102#define LSD_SCALE 0.99
103#define LSD_SIGMA_SCALE 0.6
105#define LSD_ANG_TH 22.5
106#define LSD_LOG_EPS 0.0
107#define LSD_DENSITY_TH 0.7
108#define LSD_N_BINS 1024
109#define LSD_GAMMA 0.45
110#define RANSAC_RUNS 400
111#define RANSAC_EPSILON 2
112#define RANSAC_EPSILON_STEP 1
113#define RANSAC_ELIMINATION_RATIO 60
114#define RANSAC_OPTIMIZATION_STEPS 5
115#define RANSAC_OPTIMIZATION_DRY_RUNS 50
116#define RANSAC_HURDLE 5
117#define MINIMUM_FITLINES 2
118#define NMS_EPSILON 1e-3
120#define NMS_ITERATIONS 400
121#define NMS_CROP_EPSILON 100.0
122#define NMS_CROP_SCALE 0.5
123#define NMS_CROP_ITERATIONS 100
127#define DEFAULT_F_LENGTH 28.0
132#define SQR(a) ((a) * (a))
136#define MAX_SAVED_LINES 50
160 return _(
"Horizon and _perspective");
165 return _(
"rotation|keystone|distortion|crop|reframe|horizon");
171 _(
"corrective or creative"),
172 _(
"linear, RGB, scene-referred"),
174 _(
"linear, RGB, scene-referred"));
565 void *new_params,
const int new_version)
567 if(old_version == 1 && new_version == 5)
576 new->crop_factor = 1.0f;
577 new->orthocorr = 100.0f;
586 for(
int i = 0;
i < 8;
i++) new->last_quad_lines[
i] = 0.0f;
587 new->last_drawn_lines_count = 0;
590 if(old_version == 2 && new_version == 5)
601 new->aspect = old->
aspect;
602 new->mode = old->
mode;
609 for(
int i = 0;
i < 8;
i++) new->last_quad_lines[
i] = 0.0f;
610 new->last_drawn_lines_count = 0;
613 if(old_version == 3 && new_version == 5)
624 new->aspect = old->
aspect;
625 new->mode = old->
mode;
632 for(
int i = 0;
i < 8;
i++) new->last_quad_lines[
i] = 0.0f;
633 new->last_drawn_lines_count = 0;
636 if(old_version == 4 && new_version == 5)
643 new->shear = old->
shear;
647 new->aspect = old->
aspect;
648 new->mode = old->
mode;
655 for(
int i = 0;
i < 8;
i++) new->last_quad_lines[
i] = 0.0f;
656 new->last_drawn_lines_count = 0;
670 return &
g->new_params;
678static inline void vec3prodn(
float *dst,
const float *
const v1,
const float *
const v2)
680 const float l1 = v1[1] * v2[2] - v1[2] * v2[1];
681 const float l2 = v1[2] * v2[0] - v1[0] * v2[2];
682 const float l3 = v1[0] * v2[1] - v1[1] * v2[0];
685 const float sq = sqrtf(
l1 *
l1 +
l2 *
l2 + l3 * l3);
687 const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
696static inline void vec3norm(
float *dst,
const float *
const v)
698 const float sq = sqrtf(
v[0] *
v[0] +
v[1] *
v[1] +
v[2] *
v[2]);
701 const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
711static inline void vec3lnorm(
float *dst,
const float *
const v)
713 const float sq = sqrtf(
v[0] *
v[0] +
v[1] *
v[1]);
716 const float f = sq > 0.0f ? 1.0f / sq : 1.0f;
725static inline float vec3scalar(
const float *
const v1,
const float *
const v2)
727 return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]);
733 const float eps = 1e-10f;
734 return (fabsf(
v[0]) <
eps && fabsf(
v[1]) <
eps && fabsf(
v[2]) <
eps);
738static void print_roi(
const dt_iop_roi_t *roi,
const char *label)
740 printf(
"{ %5d %5d %5d %5d %.6f } %s\n", roi->
x, roi->
y, roi->
width, roi->
height, roi->
scale, label);
760#define MAT3SWAP(a, b) { float (*tmp)[3] = (a); (a) = (b); (b) = tmp; }
763static void homography(
float *homograph,
const float angle,
const float shift_v,
const float shift_h,
764 const float shear,
const float f_length_kb,
const float orthocorr,
const float aspect,
774 const float u =
width;
777 const float phi =
M_PI * angle / 180.0f;
778 const float cosi = cosf(phi);
779 const float sini = sinf(phi);
780 const float ascale = sqrtf(aspect);
783 const float f_global = f_length_kb;
784 const float horifac = 1.0f - orthocorr / 100.0f;
785 const float exppa_v = expf(shift_v);
786 const float fdb_v = f_global / (14.4f + (
v / u - 1) * 7.2f);
787 const float rad_v = fdb_v * (exppa_v - 1.0f) / (exppa_v + 1.0f);
788 const float alpha_v = CLAMP(atanf(rad_v), -1.5f, 1.5f);
789 const float rt_v = sinf(0.5f * alpha_v);
790 const float r_v = fmaxf(0.1f, 2.0f * (horifac - 1.0f) * rt_v * rt_v + 1.0f);
792 const float vertifac = 1.0f - orthocorr / 100.0f;
793 const float exppa_h = expf(shift_h);
794 const float fdb_h = f_global / (14.4f + (u /
v - 1) * 7.2f);
795 const float rad_h = fdb_h * (exppa_h - 1.0f) / (exppa_h + 1.0f);
796 const float alpha_h = CLAMP(atanf(rad_h), -1.5f, 1.5f);
797 const float rt_h = sinf(0.5f * alpha_h);
798 const float r_h = fmaxf(0.1f, 2.0f * (vertifac - 1.0f) * rt_h * rt_h + 1.0f);
802 float m1[3][3], m2[3][3], m3[3][3];
805 float (*mwork)[3] = m1;
806 float (*minput)[3] = m2;
807 float (*moutput)[3] = m3;
810 memset(minput, 0,
sizeof(
float) * 9);
817 memset(mwork, 0,
sizeof(
float) * 9);
822 mwork[0][2] = -0.5f *
v * cosi + 0.5f * u * sini + 0.5f *
v;
823 mwork[1][2] = -0.5f *
v * sini - 0.5f * u * cosi + 0.5f * u;
827 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
831 memset(mwork, 0,
sizeof(
float) * 9);
841 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
845 memset(mwork, 0,
sizeof(
float) * 9);
846 mwork[0][0] = exppa_v;
847 mwork[1][0] = 0.5f * ((exppa_v - 1.0f) * u) /
v;
848 mwork[1][1] = 2.0f * exppa_v / (exppa_v + 1.0f);
849 mwork[1][2] = -0.5f * ((exppa_v - 1.0f) * u) / (exppa_v + 1.0f);
850 mwork[2][0] = (exppa_v - 1.0f) /
v;
856 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
860 memset(mwork, 0,
sizeof(
float) * 9);
863 mwork[1][2] = 0.5f * u * (1.0f - r_v);
869 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
873 memset(mwork, 0,
sizeof(
float) * 9);
881 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
887 memset(mwork, 0,
sizeof(
float) * 9);
888 mwork[0][0] = exppa_h;
889 mwork[1][0] = 0.5f * ((exppa_h - 1.0f) *
v) / u;
890 mwork[1][1] = 2.0f * exppa_h / (exppa_h + 1.0f);
891 mwork[1][2] = -0.5f * ((exppa_h - 1.0f) *
v) / (exppa_h + 1.0f);
892 mwork[2][0] = (exppa_h - 1.0f) / u;
898 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
902 memset(mwork, 0,
sizeof(
float) * 9);
905 mwork[1][2] = 0.5f *
v * (1.0f - r_h);
911 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
915 memset(mwork, 0,
sizeof(
float) * 9);
916 mwork[0][0] = 1.0f * ascale;
917 mwork[1][1] = 1.0f / ascale;
923 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
928 float umin = FLT_MAX, vmin = FLT_MAX;
939 umin = fmin(umin, po[0] / po[2]);
940 vmin = fmin(vmin, po[1] / po[2]);
943 memset(mwork, 0,
sizeof(
float) * 9);
953 mat3mul((
float *)moutput, (
float *)mwork, (
float *)minput);
961 memcpy(homograph, moutput,
sizeof(
float) * 9);
966 if(
mat3inv((
float *)homograph, (
float *)moutput))
969 memset(mwork, 0,
sizeof(
float) * 9);
973 memcpy(homograph, mwork,
sizeof(
float) * 9);
985 const float eps = 1.0e-4f;
993 1.0f - data->
cr <
eps &&
995 1.0f - data->
cb <
eps);
1000 float *
const restrict points,
size_t points_count)
1012 const float fullwidth = (float)piece->
buf_out.
width / (data->
cr - data->
cl);
1014 const float cx = fullwidth * data->
cl;
1015 const float cy = fullheight * data->
ct;
1017 for(
size_t i = 0;
i < points_count * 2;
i += 2)
1021 mat3mulv(po, (
float *)homograph, pi);
1022 points[
i] = po[0] / po[2] - cx;
1023 points[
i + 1] = po[1] / po[2] - cy;
1031 float *points,
size_t points_count)
1043 const float fullwidth = (float)piece->
buf_out.
width / (data->
cr - data->
cl);
1045 const float cx = fullwidth * data->
cl;
1046 const float cy = fullheight * data->
ct;
1048 for(
size_t i = 0;
i < points_count * 2;
i += 2)
1052 mat3mulv(po, (
float *)ihomograph, pi);
1053 points[
i] = po[0] / po[2];
1054 points[
i + 1] = po[1] / po[2];
1061 const float *
const in,
float *
const out,
const dt_iop_roi_t *
const roi_in,
1076 float ihomograph[3][3];
1081 const float fullwidth = (float)piece->
buf_out.
width / (data->
cr - data->
cl);
1083 const float cx = roi_out->
scale * fullwidth * data->
cl;
1084 const float cy = roi_out->
scale * fullheight * data->
ct;
1087 for(
int j = 0; j < roi_out->
height; j++)
1089 float *
const restrict _out =
out + (size_t)j * roi_out->
width;
1090 for(
int i = 0;
i < roi_out->
width;
i++)
1092 float pin[4], pout[4];
1095 pout[0] = roi_out->
x +
i + cx;
1096 pout[1] = roi_out->
y + j + cy;
1097 pout[0] /= roi_out->
scale;
1098 pout[1] /= roi_out->
scale;
1102 mat3mulv(pin, (
float *)ihomograph, pout);
1107 pin[0] *= roi_in->
scale;
1108 pin[1] *= roi_in->
scale;
1109 pin[0] -= roi_in->
x;
1110 pin[1] -= roi_in->
y;
1129 float homograph[3][3];
1133 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
1136 for(
int y = 0; y < roi_in->
height; y += roi_in->
height - 1)
1140 float pin[3], pout[3];
1143 pin[0] = roi_in->
x +
x;
1144 pin[1] = roi_in->
y + y;
1145 pin[0] /= roi_in->
scale;
1146 pin[1] /= roi_in->
scale;
1150 mat3mulv(pout, (
float *)homograph, pin);
1155 pout[0] *= roi_out->
scale;
1156 pout[1] *= roi_out->
scale;
1157 xm =
MIN(xm, pout[0]);
1158 xM =
MAX(xM, pout[0]);
1159 ym =
MIN(ym, pout[1]);
1160 yM =
MAX(yM, pout[1]);
1164 float width = xM - xm + 1;
1165 float height = yM - ym + 1;
1175 print_roi(roi_in,
"roi_in (going into modify_roi_out)");
1176 print_roi(roi_out,
"roi_out (after modify_roi_out)");
1195 float ihomograph[3][3];
1203 const float fullwidth = (float)piece->
buf_out.
width / (data->
cr - data->
cl);
1205 const float cx = roi_out->
scale * fullwidth * data->
cl;
1206 const float cy = roi_out->
scale * fullheight * data->
ct;
1208 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
1211 for(
int y = 0; y < roi_out->
height; y += roi_out->
height - 1)
1215 float pin[3], pout[3];
1218 pout[0] = roi_out->
x +
x + cx;
1219 pout[1] = roi_out->
y + y + cy;
1220 pout[0] /= roi_out->
scale;
1221 pout[1] /= roi_out->
scale;
1225 mat3mulv(pin, (
float *)ihomograph, pout);
1230 pin[0] *= roi_in->
scale;
1231 pin[1] *= roi_in->
scale;
1232 xm =
MIN(xm, pin[0]);
1233 xM =
MAX(xM, pin[0]);
1234 ym =
MIN(ym, pin[1]);
1235 yM =
MAX(yM, pin[1]);
1240 roi_in->
x = fmaxf(0.0f, xm - interpolation->
width);
1241 roi_in->
y = fmaxf(0.0f, ym - interpolation->
width);
1242 roi_in->
width = fminf(ceilf(orig_w) - roi_in->
x, xM - roi_in->
x + 1 + interpolation->
width);
1243 roi_in->
height = fminf(ceilf(orig_h) - roi_in->
y, yM - roi_in->
y + 1 + interpolation->
width);
1246 roi_in->
x = CLAMP(roi_in->
x, 0, (
int)floorf(orig_w));
1247 roi_in->
y = CLAMP(roi_in->
y, 0, (
int)floorf(orig_h));
1248 roi_in->
width = CLAMP(roi_in->
width, 1, (
int)floorf(orig_w) - roi_in->
x);
1249 roi_in->
height = CLAMP(roi_in->
height, 1, (
int)floorf(orig_h) - roi_in->
y);
1251 print_roi(roi_out,
"roi_out (going into modify_roi_in)");
1252 print_roi(roi_in,
"roi_in (after modify_roi_in)");
1262 for(
int index = 0; index < npixels; index++)
1264 out[index] = (0.3f * in[4*index+0] + 0.59f * in[4*index+1] + 0.11f * in[4*index+2]) * 256.0;
1273 const double hkernel[3][3] = { { 1.0, 0.0, -1.0 }, { 2.0, 0.0, -2.0 }, { 1.0, 0.0, -1.0 } };
1274 const double vkernel[3][3] = { { 1.0, 2.0, 1.0 }, { 0.0, 0.0, 0.0 }, { -1.0, -2.0, -1.0 } };
1275 const int kwidth = 3;
1276 const int khwidth = kwidth / 2;
1282 for(
int j = khwidth; j <
height - khwidth; j++)
1284 const double *inp = in + (size_t)j *
width + khwidth;
1285 double *outp =
out + (size_t)j *
width + khwidth;
1286 for(
int i = khwidth;
i <
width - khwidth;
i++, inp++, outp++)
1289 for(
int jj = 0; jj < kwidth; jj++)
1291 const int k = jj * kwidth;
1292 const int l = (jj - khwidth) *
width;
1293 for(
int ii = 0; ii < kwidth; ii++)
1295 sum += inp[l + ii - khwidth] *
kernel[
k + ii];
1303 for(
int j = 0; j <
height; j++)
1310 else if(j >=
height - khwidth)
1312 else if(
i < khwidth)
1314 else if(
i >=
width - khwidth)
1320 if(
i == khwidth && j >= khwidth && j <
height - khwidth)
i =
width - khwidth;
1344 out[
k] = sqrt(Gx[
k] * Gx[
k] + Gy[
k] * Gy[
k]);
1360 sRGB[0] = 3.1338561f *
XYZ[0] - 1.6168667f *
XYZ[1] - 0.4906146f *
XYZ[2];
1361 sRGB[1] = -0.9787684f *
XYZ[0] + 1.9161415f *
XYZ[1] + 0.0334540f *
XYZ[2];
1362 sRGB[2] = 0.0719453f *
XYZ[0] - 0.2289914f *
XYZ[1] + 1.4052427f *
XYZ[2];
1368 XYZ[0] = 0.4360747f *
sRGB[0] + 0.3850649f *
sRGB[1] + 0.1430804f *
sRGB[2];
1369 XYZ[1] = 0.2225045f *
sRGB[0] + 0.7168786f *
sRGB[1] + 0.0606169f *
sRGB[2];
1370 XYZ[2] = 0.0139322f *
sRGB[0] + 0.0971045f *
sRGB[1] + 0.7141733f *
sRGB[2];
1378 const float detail = 10.0f;
1387 for(
size_t index = 0; index < 4*npixels; index += 4)
1409 for(
size_t index = 0; index < 4*npixels; index += 4)
1424 for(
int index = 0; index < 4*npixels; index += 4)
1426 for(
int c = 0; c < 3; c++)
1437 double *greyscale = NULL;
1438 double *lsd_lines = NULL;
1441 int vertical_count = 0;
1442 int horizontal_count = 0;
1443 float vertical_weight = 0.0f;
1444 float horizontal_weight = 0.0f;
1459 greyscale = malloc(
sizeof(
double) *
width *
height);
1489 for(
int n = 0;
n < lines_count;
n++)
1491 const float x1 = lsd_lines[
n * 7 + 0];
1492 const float y1 = lsd_lines[
n * 7 + 1];
1493 const float x2 = lsd_lines[
n * 7 + 2];
1494 const float y2 = lsd_lines[
n * 7 + 3];
1499 if((fabsf(x1 - x2) < 1 && fmaxf(x1, x2) < 2)
1500 || (fabsf(x1 - x2) < 1 && fminf(x1, x2) >
width - 3)
1501 || (fabsf(y1 - y2) < 1 && fmaxf(y1, y2) < 2)
1502 || (fabsf(y1 - y2) < 1 && fminf(y1, y2) >
height - 3))
1506 float px1 = x_off + x1;
1507 float py1 = y_off + y1;
1508 float px2 = x_off + x2;
1509 float py2 = y_off + y2;
1518 ashift_lines[lct].
p1[0] = px1;
1519 ashift_lines[lct].
p1[1] = py1;
1520 ashift_lines[lct].
p1[2] = 1.0f;
1521 ashift_lines[lct].
p2[0] = px2;
1522 ashift_lines[lct].
p2[1] = py2;
1523 ashift_lines[lct].
p2[2] = 1.0f;
1526 vec3prodn(ashift_lines[lct].
L, ashift_lines[lct].p1, ashift_lines[lct].p2);
1530 vec3lnorm(ashift_lines[lct].
L, ashift_lines[lct].
L);
1533 ashift_lines[lct].
length = sqrt((px2 - px1) * (px2 - px1) + (py2 - py1) * (py2 - py1));
1534 ashift_lines[lct].
width = lsd_lines[
n * 7 + 4] / scale;
1537 const float weight = ashift_lines[lct].
length * ashift_lines[lct].
width * lsd_lines[
n * 7 + 5];
1541 const float angle = atan2f(py2 - py1, px2 - px1) /
M_PI * 180.0f;
1549 if(vertical && relevant)
1553 vertical_weight +=
weight;
1555 else if(horizontal && relevant)
1559 horizontal_weight +=
weight;
1568 printf(
"%d lines (vertical %d, horizontal %d, not relevant %d)\n", lines_count, vertical_count,
1569 horizontal_count, lct - vertical_count - horizontal_count);
1570 float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
1571 for(
int n = 0;
n < lct;
n++)
1573 xmin = fmin(xmin, fmin(ashift_lines[
n].p1[0], ashift_lines[
n].p2[0]));
1574 xmax = fmax(xmax, fmax(ashift_lines[
n].p1[0], ashift_lines[
n].p2[0]));
1575 ymin = fmin(ymin, fmin(ashift_lines[
n].p1[1], ashift_lines[
n].p2[1]));
1576 ymax = fmax(ymax, fmax(ashift_lines[
n].p1[1], ashift_lines[
n].p2[1]));
1577 printf(
"x1 %.0f, y1 %.0f, x2 %.0f, y2 %.0f, length %.0f, width %f, X %f, Y %f, Z %f, type %d, scalars %f %f\n",
1578 ashift_lines[
n].p1[0], ashift_lines[
n].p1[1], ashift_lines[
n].p2[0], ashift_lines[
n].p2[1],
1579 ashift_lines[
n].length, ashift_lines[
n].
width,
1580 ashift_lines[
n].
L[0], ashift_lines[
n].
L[1], ashift_lines[
n].
L[2], ashift_lines[
n].
type,
1584 printf(
"xmin %.0f, xmax %.0f, ymin %.0f, ymax %.0f\n", xmin, xmax, ymin, ymax);
1589 *vcount = vertical_count;
1590 *vweight = vertical_weight;
1591 *hcount = horizontal_count;
1592 *hweight = horizontal_weight;
1593 *alines = ashift_lines;
1611 float *buffer = NULL;
1624 x_off =
g->buf_x_off;
1625 y_off =
g->buf_y_off;
1626 scale =
g->buf_scale;
1629 buffer = malloc(
sizeof(
float) * 4 * (
size_t)
width *
height);
1643 g->vertical_count = 0;
1644 g->horizontal_count = 0;
1651 int horizontal_count;
1652 float vertical_weight;
1653 float horizontal_weight;
1659 dt_iop_fmt_log(module,
"structure: class=%s raw_origin=%d enhance=%d",
1661 raw_origin, enhance);
1663 &vertical_count, &horizontal_count, &vertical_weight, &horizontal_weight,
1664 enhance, raw_origin))
1670 const float inv_scale = (scale > 0.f) ? (1.f / scale) : 1.f;
1671 g->lines_in_width = (int)roundf(
width * inv_scale);
1672 g->lines_in_height = (int)roundf(
height * inv_scale);
1673 g->lines_x_off = (int)roundf(x_off * inv_scale);
1674 g->lines_y_off = (int)roundf(y_off * inv_scale);
1675 g->lines_count = lines_count;
1676 g->vertical_count = vertical_count;
1677 g->horizontal_count = horizontal_count;
1678 g->vertical_weight = vertical_weight;
1679 g->horizontal_weight = horizontal_weight;
1694static inline void swap(
int *a,
int *b)
1707 int j = (*
i % 2 == 1) ?
p[*
i] : 0;
1708 swap(&a[j], &a[*
i]);
1721 for(
int i = 0;
i <
N;
i++)
1723 int j =
i + rand() % (
N -
i);
1731 return (
n == 1 ? 1 :
n *
fact(
n - 1));
1751 const int set_count,
const float total_weight,
const int xmin,
const int xmax,
1752 const int ymin,
const int ymax)
1754 if(set_count < 3)
return;
1756 const size_t set_size = set_count *
sizeof(int);
1757 int *best_set = malloc(set_size);
1758 memcpy(best_set, index_set, set_size);
1759 int *best_inout = calloc(1, set_size);
1761 float best_quality = 0.0f;
1768 int lines_eliminated = 0;
1777 int *
perm = malloc(
sizeof(
int) * (set_count + 1));
1778 for(
int n = 0;
n < set_count + 1;
n++)
perm[
n] =
n;
1782 int *inout = malloc(set_size);
1784 for(
int r = 0;
r < optiruns + riter;
r++)
1788 shuffle(index_set, set_count);
1793 float quality = 0.0f;
1796 const float *L1 = lines[index_set[0]].L;
1797 const float *L2 = lines[index_set[1]].L;
1807 (fabsf(
V[2]) > 0.0f &&
1808 V[0]/
V[2] >= xmin &&
1809 V[1]/
V[2] >= ymin &&
1810 V[0]/
V[2] <= xmax &&
1830 for(
int n = 2;
n < set_count;
n++)
1833 const float *L3 = lines[index_set[
n]].L;
1842 inout[
n] = (
d < epsilon) ? 1 : 0;
1850 q = 0.33f / (float)set_count
1851 + 0.33f * lines[index_set[
n]].
weight / total_weight
1852 + 0.33f * (1.0f -
d / epsilon) * (float)set_count * lines[index_set[
n]].
weight / total_weight;
1871 printf(
"ransac self-tuning (run %d): epsilon %f",
r, epsilon);
1874 float ratio = 100.0f * (float)lines_eliminated / ((
float)set_count * valid_runs);
1877 epsilon = powf(10.0f, log10(epsilon) - epsilon_step);
1879 epsilon = powf(10.0f, log10(epsilon) + epsilon_step);
1881 printf(
" (elimination ratio %f) -> %f\n", ratio, epsilon);
1884 epsilon_step /= 2.0f;
1885 lines_eliminated = 0;
1892 if(quality > best_quality)
1894 memcpy(best_set, index_set, set_size);
1895 memcpy(best_inout, inout, set_size);
1896 best_quality = quality;
1902 int count = 0, lastcount = 0;
1903 for(
int n = 0;
n < set_count;
n++) count += best_inout[
n];
1904 for(
int n = 0;
n < set_count;
n++) lastcount += inout[
n];
1905 printf(
"ransac run %d: best qual %.6f, eps %.6f, line count %d of %d (this run: qual %.5f, count %d (%2f%%))\n",
r,
1906 best_quality, epsilon, count, set_count, quality, lastcount, 100.0f * lastcount / (
float)set_count);
1911 memcpy(index_set, best_set, set_size);
1912 memcpy(inout_set, best_inout, set_size);
1927 const int width =
g->lines_in_width;
1928 const int height =
g->lines_in_height;
1929 const int xmin =
g->lines_x_off;
1930 const int ymin =
g->lines_y_off;
1931 const int xmax = xmin +
width;
1932 const int ymax = ymin +
height;
1935 int *lines_set = malloc(
sizeof(
int) *
g->lines_count);
1937 int *inout_set = malloc(
sizeof(
int) *
g->lines_count);
1940 int vnb = 0, vcount = 0;
1941 int hnb = 0, hcount = 0;
1947 for(
int n = 0;
n <
g->lines_count;
n++)
1960 ransac(
g->lines, lines_set, inout_set, vnb,
g->vertical_weight,
1961 xmin, xmax, ymin, ymax);
1964 for(
int n = 0;
n < vnb;
n++)
1966 const int m = lines_set[
n];
1967 if(inout_set[
n] == 1)
1973 g->lines[
m].type &= ~ASHIFT_LINE_SELECTED;
1976 g->vertical_count = vcount;
1980 for(
int n = 0;
n <
g->lines_count;
n++)
1993 ransac(
g->lines, lines_set, inout_set, hnb,
g->horizontal_weight,
1994 xmin, xmax, ymin, ymax);
1997 for(
int n = 0;
n < hnb;
n++)
1999 const int m = lines_set[
n];
2000 if(inout_set[
n] == 1)
2006 g->lines[
m].type &= ~ASHIFT_LINE_SELECTED;
2009 g->horizontal_count = hcount;
2026 const double eps = 1.0e-6;
2031 return (2.0 * atanh(2.0 *
p - 1.0));
2037 double p = 0.5 * (1.0 + tanh(0.5 *
L));
2060 const float aspect = fit->
aspect;
2065 float shear = fit->
shear;
2076 rotation =
ilogit(params[pcount], -rotation_range, rotation_range);
2080 if(isnan(lensshift_v))
2082 lensshift_v =
ilogit(params[pcount], -lensshift_v_range, lensshift_v_range);
2086 if(isnan(lensshift_h))
2088 lensshift_h =
ilogit(params[pcount], -lensshift_h_range, lensshift_h_range);
2094 shear =
ilogit(params[pcount], -shear_range, shear_range);
2101 const float Av[3] = { 1.0f, 0.0f, 0.0f };
2102 const float Ah[3] = { 0.0f, 1.0f, 0.0f };
2105 float homograph[3][3];
2106 homography((
float *)homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb,
2110 double sumsq_v = 0.0;
2111 double sumsq_h = 0.0;
2112 double weight_v = 0.0;
2113 double weight_h = 0.0;
2119 for(
int n = 0;
n < lines_count;
n++)
2129 const float *
A = isvertical ? Ah : Av;
2133 mat3mulv(P1, (
float *)homograph, lines[
n].p1);
2134 mat3mulv(P2, (
float *)homograph, lines[
n].p2);
2148 sumsq_v += isvertical ? s * s * lines[
n].weight : 0.0;
2149 weight_v += isvertical ? lines[
n].weight : 0.0;
2150 count_v += isvertical ? 1 : 0;
2151 sumsq_h += !isvertical ? s * s * lines[
n].weight : 0.0;
2152 weight_h += !isvertical ? lines[
n].weight : 0.0;
2153 count_h += !isvertical ? 1 : 0;
2157 const double v = weight_v > 0.0f && count > 0 ? sumsq_v / weight_v * (float)count_v / count : 0.0;
2158 const double h = weight_h > 0.0f && count > 0 ? sumsq_h / weight_h * (float)count_h / count : 0.0;
2160 double sum = sqrt(1.0 - (1.0 -
v) * (1.0 - h)) * 1.0e6;
2164 printf(
"fitness with rotation %f, lensshift_v %f, lensshift_h %f, shear %f -> lines %d, quality %10f\n",
2165 rotation, lensshift_v, lensshift_h, shear, count, sum);
2181 int enough_lines =
TRUE;
2187 fit.
width =
g->lines_in_width;
2188 fit.
height =
g->lines_in_height;
2263 fit.
weight +=
g->vertical_weight;
2271 fit.
weight +=
g->horizontal_weight;
2288 printf(
"optimization not possible: insufficient number of lines\n");
2300 printf(
"optimization not successful: maximum number of iterations reached (%d)\n", iter);
2312 printf(
"params after optimization (%d iterations): rotation %f, lensshift_v %f, lensshift_h %f, shear %f\n",
2318 float homograph[3][3];
2323 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
2331 mat3mulv(po, (
float *)homograph, pi);
2334 xm = fmin(xm, po[0]);
2335 ym = fmin(ym, po[1]);
2336 xM = fmax(xM, po[0]);
2337 yM = fmax(yM, po[1]);
2340 if((xM - xm) * (yM - ym) > 4.0f * fit.
width * fit.
height)
2343 printf(
"optimization not successful: degenerate case with area growth factor (%f) exceeding limits\n",
2344 (xM - xm) * (yM - ym) / (fit.
width * fit.
height));
2368 int enough_lines =
TRUE;
2374 fit.
width =
g->lines_in_width;
2375 fit.
height =
g->lines_in_height;
2404 fit.
weight +=
g->vertical_weight;
2412 fit.
weight +=
g->horizontal_weight;
2427 printf(
"model fitness: %.8f (rotation %f, lensshift_v %f, lensshift_h %f, shear %f)\n",
2428 quality,
p->rotation,
p->lensshift_v,
p->lensshift_h,
p->shear);
2435 if(pcount > 0) params[0] = fabs(params[0]);
2436 if(pcount > 1) params[1] = fabs(params[1]);
2437 if(pcount > 2) params[2] = fabs(params[2]);
2439 if(pcount > 0 && params[0] > 1.0) params[0] = 1.0 - params[0];
2440 if(pcount > 1 && params[1] > 1.0) params[1] = 1.0 - params[1];
2441 if(pcount > 2 && params[2] > 0.5*
M_PI) params[2] = 0.5*
M_PI - params[2];
2454 const float wd = cropfit->
width;
2455 const float ht = cropfit->
height;
2458 const float x = isnan(cropfit->
x) ? params[0] : cropfit->
x;
2459 const float y = isnan(cropfit->
y) ? params[1] : cropfit->
y;
2460 const float alpha = isnan(cropfit->
alpha) ? params[2] : cropfit->
alpha;
2463 const float Pc[3] = {
x * wd, y * ht, 1.0f };
2473 const float Pa[2][3] = { {
P[0] + 10.0f * cosf(alpha),
P[1] + 10.0f * sinf(alpha), 1.0f },
2474 {
P[0] + 10.0f * cosf(alpha),
P[1] - 10.0f * sinf(alpha), 1.0f } };
2484 float d2min = FLT_MAX;
2485 for(
int k = 0;
k < 4;
k++)
2486 for(
int l = 0; l < 2; l++)
2508 const float d2 =
SQR(
P[0] - I[0]) +
SQR(
P[1] - I[1]);
2511 d2min =
MIN(d2min, d2);
2515 const float A = 2.0f * d2min * sinf(2.0f * alpha);
2518 printf(
"crop fitness with x %f, y %f, angle %f -> distance %f, area %f\n",
2519 x, y, alpha, d2min,
A);
2553 int crop_width = 0, crop_height = 0;
2557 crop_width = crop_in.
width;
2558 crop_height = crop_in.
height;
2563 crop_width =
g->buf_width;
2564 crop_height =
g->buf_height;
2568 if(crop_width == 0 || crop_height == 0)
return;
2571 if(
g->fitting)
return;
2587 const float rotation =
p->rotation;
2588 const float lensshift_v =
p->lensshift_v;
2589 const float lensshift_h =
p->lensshift_h;
2590 const float shear =
p->shear;
2594 cropfit.
width = crop_width;
2595 cropfit.
height = crop_height;
2596 homography((
float *)cropfit.
homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb,
2599 const float wd = cropfit.
width;
2600 const float ht = cropfit.
height;
2604 const float crop_alpha = atan2f(ht, wd);
2607 const float Vc[4][3] = { { 0.0f, 0.0f, 1.0f },
2610 { wd, 0.0f, 1.0f } };
2614 for(
int n = 0;
n < 4;
n++)
2618 float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
2619 for(
int n = 0;
n < 4;
n++)
2625 xmin =
MIN(xmin,
V[
n][0]);
2626 xmax =
MAX(xmax,
V[
n][0]);
2627 ymin =
MIN(ymin,
V[
n][1]);
2628 ymax =
MAX(ymax,
V[
n][1]);
2630 const float owd = xmax - xmin;
2631 const float oht = ymax - ymin;
2634 for(
int n = 0;
n < 4;
n++)
2643 params[2] = crop_alpha;
2646 cropfit.
alpha = NAN;
2655 cropfit.
alpha = crop_alpha;
2664 const double crop_epsilon = fmax(
NMS_CROP_EPSILON, 1e-4 * (
double)crop_width * (
double)crop_height);
2673 cropfit.
x = isnan(cropfit.
x) ? params[0] : cropfit.
x;
2674 cropfit.
y = isnan(cropfit.
y) ? params[1] : cropfit.
y;
2678 const float A = fabs(
crop_fitness(params, (
void*)&cropfit));
2681 if(
A == 0.0f)
goto failed;
2685 const float d = sqrtf(
A / (2.0f * sinf(2.0f * cropfit.
alpha)));
2688 const float Pc[3] = { cropfit.
x * wd, cropfit.
y * ht, 1.0f };
2697 p->cl = CLAMP((
P[0] -
d * cosf(cropfit.
alpha)) / owd, 0.0f, 1.0f);
2698 p->cr = CLAMP((
P[0] +
d * cosf(cropfit.
alpha)) / owd, 0.0f, 1.0f);
2699 p->ct = CLAMP((
P[1] -
d * sinf(cropfit.
alpha)) / oht, 0.0f, 1.0f);
2700 p->cb = CLAMP((
P[1] +
d * sinf(cropfit.
alpha)) / oht, 0.0f, 1.0f);
2703 if(
p->cr -
p->cl <= 0.0f ||
p->cb -
p->ct <= 0.0f)
goto failed;
2708 printf(
"margins after crop fitting: iter %d, x %f, y %f, angle %f, crop area (%f %f %f %f), width %f, height %f\n",
2709 iter, cropfit.
x, cropfit.
y, cropfit.
alpha,
p->cl,
p->cr,
p->ct,
p->cb, wd, ht);
2726 const gboolean identity_transform = (
p->rotation == 0.0f &&
p->lensshift_v == 0.0f &&
p->lensshift_h == 0.0f);
2727 if(!identity_transform)
2728 dt_control_log(_(
"Automatic cropping failed. Keeping previous margins."));
2737 if(fabsf(line->
p1[0] - line->
p2[0]) > fabsf(line->
p1[1] - line->
p2[1]))
2739 line->
type = linetype;
2762 line->
length = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
2773 gtk_widget_set_sensitive(
g->fit_v,
enable);
2774 gtk_widget_set_sensitive(
g->fit_h,
enable);
2775 gtk_widget_set_sensitive(
g->fit_both,
enable);
2790 float pts[8] = {
g->lines[0].p1[0],
g->lines[0].p1[1],
g->lines[0].p2[0],
2791 g->lines[0].p2[1],
g->lines[1].p1[0],
g->lines[1].p1[1],
2792 g->lines[1].p2[0],
g->lines[1].p2[1] };
2796 for(
int i = 0;
i < 8;
i++)
p->last_quad_lines[
i] = pts[
i];
2802 p->last_drawn_lines_count = 0;
2804 for(
int i = 0;
i <
g->lines_count;
i++)
2810 p->last_drawn_lines[
p->last_drawn_lines_count * 4 ] =
g->lines[
i].p1[0];
2811 p->last_drawn_lines[
p->last_drawn_lines_count * 4 + 1] =
g->lines[
i].p1[1];
2812 p->last_drawn_lines[
p->last_drawn_lines_count * 4 + 2] =
g->lines[
i].p2[0];
2813 p->last_drawn_lines[
p->last_drawn_lines_count * 4 + 3] =
g->lines[
i].p2[1];
2814 p->last_drawn_lines_count++;
2820 p->last_drawn_lines_count * 2);
2837 int32_t raw_width = 0;
2838 int32_t raw_height = 0;
2842 &&
p->last_quad_lines[0] > 0.0f &&
p->last_quad_lines[1] > 0.0f
2843 &&
p->last_quad_lines[2] > 0.0f &&
p->last_quad_lines[3] > 0.0f)
2845 float pts[8] = {
p->last_quad_lines[0],
p->last_quad_lines[1],
2846 p->last_quad_lines[2],
p->last_quad_lines[3],
2847 p->last_quad_lines[4],
p->last_quad_lines[5],
2848 p->last_quad_lines[6],
p->last_quad_lines[7] };
2870 g->vertical_count = 2;
2871 g->horizontal_count = 2;
2872 g->vertical_weight = 2.0;
2873 g->horizontal_weight = 2.0;
2874 g->lines_in_width = raw_width;
2875 g->lines_in_height = raw_height;
2876 g->current_structure_method = method;
2885 for(
int i = 0;
i <
p->last_drawn_lines_count * 4;
i++)
2886 pts[
i] =
p->last_drawn_lines[
i];
2899 for(
int i = 0;
i <
p->last_drawn_lines_count;
i++)
2903 if(fabsf(pts[
i * 4] - pts[
i * 4 + 2]) > fabsf(pts[
i * 4 + 1] - pts[
i * 4 + 3]))
2913 g->lines_count =
p->last_drawn_lines_count;
2914 g->vertical_count = vnb;
2915 g->horizontal_count = hnb;
2916 g->vertical_weight = (float)vnb;
2917 g->horizontal_weight = (float)hnb;
2918 g->lines_in_width = raw_width;
2919 g->lines_in_height = raw_height;
2920 g->current_structure_method = method;
2932 if(
g->fitting)
return FALSE;
2939 g->vertical_count = 0;
2940 g->horizontal_count = 0;
2958 if(
g->fitting)
return FALSE;
2975 g->jobparams = enhance;
2984 printf(
"do_get_structure: buf %p, buf_hash %" PRIu64
", buf_width %d, buf_height %d, lines %p, lines_count %d\n",
2985 g->buf,
g->buf_hash,
g->buf_width,
g->buf_height,
g->lines,
g->lines_count);
2995 printf(
"_remove_outliers: buf %p, buf_hash %" PRIu64
", buf_width %d, buf_height %d, lines %p, lines_count %d\n",
2996 g->buf,
g->buf_hash,
g->buf_width,
g->buf_height,
g->lines,
g->lines_count);
3022 int32_t raw_width = 0, raw_height = 0;
3024 if(raw_width <= 0 || raw_height <= 0)
return;
3029 g->lines_in_width = raw_width;
3030 g->lines_in_height = raw_height;
3051 int32_t raw_width = 0, raw_height = 0;
3053 if(raw_width <= 0 || raw_height <= 0)
return;
3066 const float wd =
geometry.processed_width;
3067 const float ht =
geometry.processed_height;
3068 float pts[8] = { wd * 0.2, ht * 0.2, wd * 0.2, ht * 0.8, wd * 0.8, ht * 0.2, wd * 0.8, ht * 0.8 };
3088 g->lines_in_width = raw_width;
3089 g->lines_in_height = raw_height;
3092 g->vertical_count = 2;
3093 g->horizontal_count = 2;
3094 g->vertical_weight = 2.0;
3095 g->horizontal_weight = 2.0;
3109 if(
g->fitting)
return;
3115 if(
g->lines_in_width > 0 &&
g->lines_in_height > 0)
3121 float homograph[3][3];
3122 homography((
float *)homograph,
p->rotation,
p->lensshift_v,
p->lensshift_h,
p->shear, f_length_kb,
3125 const float ivec[2] = { (float)
g->lines_in_width, (
float)
g->lines_in_height };
3126 const float ivecl = sqrtf(ivec[0] * ivec[0] + ivec[1] * ivec[1]);
3128 const float pin0[3] = { 0.0f, 0.0f, 1.0f };
3129 const float pin1[3] = { (float)
g->lines_in_width, (
float)
g->lines_in_height, 1.0f };
3132 mat3mulv(pout0, (
float *)homograph, pin0);
3133 mat3mulv(pout1, (
float *)homograph, pin1);
3134 pout0[0] /= pout0[2];
3135 pout0[1] /= pout0[2];
3136 pout1[0] /= pout1[2];
3137 pout1[1] /= pout1[2];
3139 const float ovec[2] = { pout1[0] - pout0[0], pout1[1] - pout0[1] };
3140 const float ovecl = sqrtf(ovec[0] * ovec[0] + ovec[1] * ovec[1]);
3141 const float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f));
3142 g->isflipped = fabs(fmod(alpha +
M_PI,
M_PI) -
M_PI / 2.0f) <
M_PI / 4.0f ? 1 : 0;
3155 _(
"not enough structure for automatic correction\nminimum %d lines in each relevant direction"),
3160 dt_control_log(_(
"automatic correction failed, please correct manually"));
3188 const int ch_width =
ch * roi_in->
width;
3199 const int x_off = roi_in->
x;
3200 const int y_off = roi_in->
y;
3205 const float scale = 0.5f * (scale_x + scale_y);
3209 float ivec[2] = { points[2] - points[0], points[3] - points[1] };
3210 float ivecl = sqrtf(ivec[0] * ivec[0] + ivec[1] * ivec[1]);
3230 float ovec[2] = { points[2] - points[0], points[3] - points[1] };
3231 float ovecl = sqrtf(ovec[0] * ovec[0] + ovec[1] * ovec[1]);
3234 float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f));
3237 const int isflipped = fabs(fmod(alpha +
M_PI,
M_PI) -
M_PI / 2.0f) <
M_PI / 4.0f ? 1 : 0;
3243 g->isflipped = isflipped;
3266 g->buf_x_off = x_off;
3267 g->buf_y_off = y_off;
3268 g->buf_scale = scale;
3284 float ihomograph[3][3];
3289 const float fullwidth = (float)piece->
buf_out.
width / (data->
cr - data->
cl);
3291 const float cx = roi_out->
scale * fullwidth * data->
cl;
3292 const float cy = roi_out->
scale * fullheight * data->
ct;
3295 for(
int j = 0; j < roi_out->
height; j++)
3297 float *
const restrict
out = ((
float *)
ovoid) + (size_t)
ch * j * roi_out->
width;
3298 for(
int i = 0;
i < roi_out->
width;
i++)
3300 float pin[3], pout[3];
3303 pout[0] = roi_out->
x +
i + cx;
3304 pout[1] = roi_out->
y + j + cy;
3305 pout[0] /= roi_out->
scale;
3306 pout[1] /= roi_out->
scale;
3310 mat3mulv(pin, (
float *)ihomograph, pout);
3315 pin[0] *= roi_in->
scale;
3316 pin[1] *= roi_in->
scale;
3317 pin[0] -= roi_in->
x;
3318 pin[1] -= roi_in->
y;
3322 roi_in->
height, ch_width);
3337 const int devid = pipe->
devid;
3338 const int iwidth = roi_in->
width;
3339 const int iheight = roi_in->
height;
3344 cl_mem dev_homo = NULL;
3353 const int x_off = roi_in->
x;
3354 const int y_off = roi_in->
y;
3359 const float scale = 0.5f * (scale_x + scale_y);
3363 const float ivec[2] = { points[2] - points[0], points[3] - points[1] };
3364 const float ivecl = sqrtf(ivec[0] * ivec[0] + ivec[1] * ivec[1]);
3384 const float ovec[2] = { points[2] - points[0], points[3] - points[1] };
3385 const float ovecl = sqrtf(ovec[0] * ovec[0] + ovec[1] * ovec[1]);
3388 const float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f));
3391 const int isflipped = fabs(fmod(alpha +
M_PI,
M_PI) -
M_PI / 2.0f) <
M_PI / 4.0f ? 1 : 0;
3397 g->isflipped = isflipped;
3400 if(
IS_NULL_PTR(
g->buf) || (
size_t)
g->buf_width *
g->buf_height < (
size_t)iwidth * iheight)
3405 g->buf = malloc(
sizeof(
float) * 4 * iwidth * iheight);
3413 g->buf_width = iwidth;
3414 g->buf_height = iheight;
3415 g->buf_x_off = x_off;
3416 g->buf_y_off = y_off;
3417 g->buf_scale = scale;
3421 if(err != CL_SUCCESS)
goto error;
3427 size_t origin[] = { 0, 0, 0 };
3430 if(err != CL_SUCCESS)
goto error;
3434 float ihomograph[3][3];
3435 homography((
float *)ihomograph,
d->rotation,
d->lensshift_v,
d->lensshift_h,
d->shear,
d->f_length_kb,
3439 const float fullwidth = (float)piece->
buf_out.
width / (
d->cr -
d->cl);
3440 const float fullheight = (float)piece->
buf_out.
height / (
d->cb -
d->ct);
3441 const float cx = roi_out->
scale * fullwidth *
d->cl;
3442 const float cy = roi_out->
scale * fullheight *
d->ct;
3447 const int iroi[2] = { roi_in->
x, roi_in->
y };
3448 const int oroi[2] = { roi_out->
x, roi_out->
y };
3449 const float in_scale = roi_in->
scale;
3450 const float out_scale = roi_out->
scale;
3451 const float clip[2] = { cx, cy };
3459 switch(interpolation->
id)
3487 if(err != CL_SUCCESS)
goto error;
3501 float pzy,
float delta, gboolean multiple)
3505 for(
int n = 0;
n < lines_count;
n++)
3514 if(pzx < points_idx[
n].bbx -
delta &&
3515 pzx > points_idx[
n].bbX +
delta &&
3516 pzy < points_idx[
n].bby -
delta &&
3517 pzy > points_idx[
n].bbY +
delta)
3521 size_t offset = points_idx[
n].
offset;
3522 const int length = points_idx[
n].
length;
3525 if(length < 2)
continue;
3528 for(
int l = 0; l < length; l++, offset++)
3530 float dx = pzx - points[offset * 2];
3531 float dy = pzy - points[offset * 2 + 1];
3533 if(dx * dx + dy * dy < delta2)
3540 if(!multiple && points_idx[
n].is_near)
break;
3546 const int points_lines_count,
float pzx,
float pzy,
3569 for(
int n = 0;
n < points_lines_count;
n++)
3580 if(points_idx[
n].bbx >= ax && points_idx[
n].bbx <= bx && points_idx[
n].bbX >= ax
3581 && points_idx[
n].bbX <= bx && points_idx[
n].bby >= ay && points_idx[
n].bby <= by
3582 && points_idx[
n].bbY >= ay && points_idx[
n].bbY <= by)
3595 for(
int n = 0;
n < lines_count;
n++)
3606 int points_lines_count)
3611 const int isflipped =
g->isflipped;
3614 for(
int n = 0;
n < points_lines_count;
n++)
3638 const int lines_version,
float **points,
float **extremas,
3645 float *my_points = NULL;
3646 float *my_extremas = NULL;
3649 const int isflipped =
g->isflipped;
3656 size_t total_points = 0;
3659 for(
int n = 0;
n < lines_count;
n++)
3661 const int length =
MAX(lines[
n].length, 2);
3663 total_points += length;
3665 my_points_idx[
n].
length = length;
3688 my_points = (
float *)malloc(
sizeof(
float) * 2 * total_points);
3689 my_extremas = (
float *)malloc(
sizeof(
float) * 2 * 2 * lines_count);
3693 for(
int n = 0, offset = 0;
n < lines_count;
n++)
3695 my_extremas[4 *
n] = lines[
n].p1[0];
3696 my_extremas[4 *
n + 1] = lines[
n].p1[1];
3697 my_extremas[4 *
n + 2] = lines[
n].p2[0];
3698 my_extremas[4 *
n + 3] = lines[
n].p2[1];
3700 my_points_idx[
n].
offset = offset;
3702 float x = lines[
n].p1[0];
3703 float y = lines[
n].p1[1];
3704 const int length = lines[
n].length;
3706 const float dx = (lines[
n].p2[0] -
x) / (
float)(length - 1);
3707 const float dy = (lines[
n].p2[1] - y) / (
float)(length - 1);
3712 my_points[2 * offset] =
x;
3713 my_points[2 * offset + 1] = y;
3715 my_points[2 * offset] = lines[
n].p2[0];
3716 my_points[2 * offset + 1] = lines[
n].p2[1];
3721 for(
int l = 0; l < length && offset < total_points; l++, offset++)
3723 my_points[2 * offset] =
x;
3724 my_points[2 * offset + 1] = y;
3736 my_extremas, 2 * lines_count))
3740 const float gui_scale = (scale > 0.f) ? scale : 1.f;
3741 if(gui_scale != 1.f)
3743 for(
size_t i = 0;
i < total_points * 2;
i++)
3744 my_points[
i] *= gui_scale;
3745 for(
int i = 0;
i < 4 * lines_count;
i++)
3746 my_extremas[
i] *= gui_scale;
3750 for(
int n = 0;
n < lines_count;
n++)
3752 float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
3754 const size_t offset = my_points_idx[
n].
offset;
3755 const int length = my_points_idx[
n].
length;
3759 for(
int l = 0; l < length; l++)
3761 const size_t point = offset + l;
3762 xmin = fmin(xmin, my_points[2 *
point]);
3763 xmax = fmax(xmax, my_points[2 *
point]);
3764 ymin = fmin(ymin, my_points[2 *
point + 1]);
3765 ymax = fmax(ymax, my_points[2 *
point + 1]);
3768 my_points_idx[
n].
bbx = xmin;
3769 my_points_idx[
n].
bbX = xmax;
3770 my_points_idx[
n].
bby = ymin;
3771 my_points_idx[
n].
bbY = ymax;
3775 if(
g->lines_version > lines_version)
3778 *points = my_points;
3779 *points_idx = my_points_idx;
3780 *points_lines_count = lines_count;
3781 *extremas = my_extremas;
3810 int32_t pointerx, int32_t pointery)
3820 if(wd < 1.0 || ht < 1.0)
return;
3836 if(
g->straightening && !
g->editing)
3844 float pzxpy[2] = { (float)pointerx, (
float)pointery };
3846 const float pzx = pzxpy[0];
3847 const float pzy = pzxpy[1];
3850 PangoLayout *layout;
3851 PangoFontDescription *desc = pango_font_description_copy_static(
dt_bauhaus_get_global()->pango_font_desc);
3853 pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
3854 pango_font_description_set_absolute_size(desc, fontsize * PANGO_SCALE / zoom_scale);
3855 layout = pango_cairo_create_layout(cr);
3856 pango_layout_set_font_description(layout, desc);
3857 const float bzx =
g->straighten_x;
3858 const float bzy =
g->straighten_y;
3863 cairo_move_to(cr, bzx * wd, bzy * ht);
3864 cairo_line_to(cr, pzx * wd, pzy * ht);
3868 float dx = pzx * wd - bzx * wd;
3869 float dy = pzy * ht - bzy * ht;
3875 float angle = atan2f(dy, dx);
3876 angle = angle * 180 /
M_PI;
3877 if(angle > 45.0) angle -= 90;
3878 if(angle < -45.0) angle += 90;
3880 gchar *view_angle = NULL;
3881 view_angle = g_strdup_printf(
"%.2f\302\260", angle);
3882 pango_layout_set_text(layout, view_angle ? view_angle :
"-1\302\260", -1);
3885 PangoRectangle logic;
3886 pango_layout_get_pixel_extents(layout, &ink, &logic);
3887 const float text_w = logic.width;
3888 const float text_h = logic.height;
3890 cairo_set_source_rgba(cr, .5, .5, .5, .9);
3892 const float yp = pzy * ht - logic.height;
3894 const double rectangle_x = xp - margin;
3895 const double rectangle_y = yp - margin;
3896 const double rectangle_w = text_w + 2 * margin;
3897 const double rectangle_h = text_h + 2 * margin;
3900 cairo_set_source_rgba(cr, .7, .7, .7, .7);
3901 cairo_move_to(cr, xp, yp);
3902 pango_cairo_show_layout(cr, layout);
3903 pango_font_description_free(desc);
3904 g_object_unref(layout);
3909 if(!
g->editing)
return;
3922 const float iwd = mod_in.
width;
3923 const float iht = mod_in.
height;
3924 const float ixo = mod_in.
x;
3925 const float iyo = mod_in.
y;
3928 float V[4][2] = { { ixo, iyo },
3930 { ixo + iwd, iyo + iht },
3931 { ixo + iwd, iyo } };
3938 float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
3939 for(
int n = 0;
n < 4;
n++)
3941 xmin =
MIN(xmin,
V[
n][0]);
3942 xmax =
MAX(xmax,
V[
n][0]);
3943 ymin =
MIN(ymin,
V[
n][1]);
3944 ymax =
MAX(ymax,
V[
n][1]);
3975 const float owd = xmax - xmin;
3976 const float oht = ymax - ymin;
3979 float C[4][2] = { { xmin +
p->cl * owd, ymin +
p->ct * oht },
3980 { xmin +
p->cl * owd, ymin +
p->cb * oht },
3981 { xmin +
p->cr * owd, ymin +
p->cb * oht },
3982 { xmin +
p->cr * owd, ymin +
p->ct * oht } };
3993 cairo_set_dash(cr, &dashes, 0, 0);
3997 for(
size_t i = 0;
i < 4;
i++)
3999 V[
i][0] *= scale_factor;
4000 V[
i][1] *= scale_factor;
4001 C[
i][0] *= scale_factor;
4002 C[
i][1] *= scale_factor;
4006 cairo_set_source_rgba(cr, .2, .2, .2, .8);
4007 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
4008 cairo_rectangle(cr, 0.0, 0.0, wd, ht);
4009 cairo_move_to(cr,
C[0][0],
C[0][1]);
4010 cairo_line_to(cr,
C[1][0],
C[1][1]);
4011 cairo_line_to(cr,
C[2][0],
C[2][1]);
4012 cairo_line_to(cr,
C[3][0],
C[3][1]);
4013 cairo_close_path(cr);
4017 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
4018 cairo_rectangle(cr, 0.0, 0.0, wd, ht);
4019 cairo_move_to(cr,
V[0][0],
V[0][1]);
4020 cairo_line_to(cr,
V[1][0],
V[1][1]);
4021 cairo_line_to(cr,
V[2][0],
V[2][1]);
4022 cairo_line_to(cr,
V[3][0],
V[3][1]);
4023 cairo_close_path(cr);
4024 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
4027 cairo_move_to(cr,
C[0][0],
C[0][1]);
4028 cairo_line_to(cr,
C[1][0],
C[1][1]);
4029 cairo_line_to(cr,
C[2][0],
C[2][1]);
4030 cairo_line_to(cr,
C[3][0],
C[3][1]);
4031 cairo_close_path(cr);
4037 const float cx = fminf(
C[0][0], fminf(
C[1][0], fminf(
C[2][0],
C[3][0])));
4038 const float cy = fminf(
C[0][1], fminf(
C[1][1], fminf(
C[2][1],
C[3][1])));
4039 const float cw = fmaxf(
C[0][0], fmaxf(
C[1][0], fmaxf(
C[2][0],
C[3][0]))) - cx;
4040 const float ch = fmaxf(
C[0][1], fmaxf(
C[1][1], fmaxf(
C[2][1],
C[3][1]))) - cy;
4059 ||
g->points_lines_count !=
g->lines_count
4060 || (
g->lines_version >
g->points_version
4061 &&
g->lines_hash != lines_hash))
4067 g->points_lines_count = 0;
4071 if(!
get_points(self,
g->lines,
g->lines_count,
g->lines_version, &
g->points, &
g->draw_points, &
g->points_idx,
4072 &
g->points_lines_count, scale))
4075 g->points_version =
g->lines_version;
4076 g->grid_hash = hash;
4077 g->lines_hash = lines_hash;
4079 else if(
g->lines_hash == lines_hash)
4082 for(
int n = 0;
n <
g->points_lines_count;
n++)
4083 g->points_idx[
n].type =
g->lines[
n].type;
4089 g->points_version =
g->lines_version;
4099 const float line_colors[5][4] =
4100 { { 0.3f, 0.3f, 0.3f, 0.8f },
4101 { 0.0f, 1.0f, 0.0f, 0.8f },
4102 { 0.8f, 0.0f, 0.0f, 0.8f },
4103 { 0.0f, 0.0f, 1.0f, 0.8f },
4104 { 0.8f, 0.8f, 0.0f, 0.8f } };
4106 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
4109 for(
int n = 0;
n <
g->points_lines_count;
n++)
4117 if(
g->points_idx[
n].is_near)
4123 const float *
color = line_colors[
g->points_idx[
n].color];
4126 size_t offset =
g->points_idx[
n].offset;
4127 const int length =
g->points_idx[
n].length;
4130 if(length < 2)
continue;
4133 cairo_move_to(cr,
g->points[offset * 2],
g->points[offset * 2 + 1]);
4137 for(
int l = 1; l < length; l++, offset++)
4139 cairo_line_to(cr,
g->points[offset * 2],
g->points[offset * 2 + 1]);
4152 for(
int i = 0;
i < nb;
i++)
4158 if(
g->draw_near_point ==
i)
4171 float pzxpy[2] = { (float)pointerx, (
float)pointery };
4173 const float pzx = pzxpy[0];
4174 const float pzy = pzxpy[1];
4177 dashed[0] /= zoom_scale;
4178 dashed[1] /= zoom_scale;
4179 const int len =
sizeof(dashed) /
sizeof(dashed[0]);
4181 cairo_rectangle(cr,
g->lastx * wd,
g->lasty * ht, (pzx -
g->lastx) * wd,
4182 (pzy -
g->lasty) * ht);
4183 cairo_set_source_rgba(cr, .3, .3, .3, .8);
4184 cairo_set_line_width(cr, 1.0 / zoom_scale);
4185 cairo_set_dash(cr, dashed, len, 0);
4186 cairo_stroke_preserve(cr);
4187 cairo_set_source_rgba(cr, .8, .8, .8, .8);
4188 cairo_set_dash(cr, dashed, len, 4);
4193 if(
g->near_delta > 0)
4195 float pzxpy[2] = { (float)pointerx, (
float)pointery };
4197 const float pzx = pzxpy[0];
4198 const float pzy = pzxpy[1];
4201 dashed[0] /= zoom_scale;
4202 dashed[1] /= zoom_scale;
4203 const int len =
sizeof(dashed) /
sizeof(dashed[0]);
4205 cairo_arc(cr, pzx * wd, pzy * ht,
g->near_delta, 0, 2.0 *
M_PI);
4207 cairo_set_source_rgba(cr, .3, .3, .3, .8);
4208 cairo_set_line_width(cr, 1.0 / zoom_scale);
4209 cairo_set_dash(cr, dashed, len, 0);
4210 cairo_stroke_preserve(cr);
4211 cairo_set_source_rgba(cr, .8, .8, .8, .8);
4212 cairo_set_dash(cr, dashed, len, 4);
4221 int *vertical_count,
int *horizontal_count)
4226 for(
int n = 0;
n < lines_count;
n++)
4234 *vertical_count = vlines;
4235 *horizontal_count = hlines;
4244 for(
int i = 0;
i < limit;
i++)
4247 && y - points[
i * 2 + 1] > -
delta)
4255 line->
length = sqrt((line->
p2[0] - line->
p1[0]) * (line->
p2[0] - line->
p1[0])
4256 + (line->
p2[1] - line->
p1[1]) * (line->
p2[1] - line->
p1[1]));
4264 if(
g->straightening)
4270 gboolean handled =
FALSE;
4274 if(wd < 1.0 || ht < 1.0)
return 1;
4276 float pzxpy[2] = { (float)
x, (
float)y };
4278 float pzx = pzxpy[0];
4279 float pzy = pzxpy[1];
4289 ?
MIN(
g->points_lines_count,
g->lines_count)
4293 if(
g->draw_point_move)
4295 const int line =
g->draw_near_point / 2;
4296 if(
g->draw_near_point < 0 || line >=
g->lines_count)
4298 g->draw_point_move =
FALSE;
4299 g->draw_near_point = -1;
4304 const float pd_w =
geometry.processed_width;
4305 const float pd_h =
geometry.processed_height;
4306 float pts[2] = { pzx * pd_w, pzy * pd_h };
4311 if(
g->draw_near_point >= 0)
4313 if(
g->draw_near_point % 2 == 0)
4315 g->lines[line].p1[0] = pts[0];
4316 g->lines[line].p1[1] = pts[1];
4320 g->lines[line].p2[0] = pts[0];
4321 g->lines[line].p2[1] = pts[1];
4329 if(
g->draw_near_point == 0)
4331 g->lines[2].p1[0] = pts[0];
4332 g->lines[2].p1[1] = pts[1];
4335 else if(
g->draw_near_point == 1)
4337 g->lines[3].p1[0] = pts[0];
4338 g->lines[3].p1[1] = pts[1];
4341 else if(
g->draw_near_point == 2)
4343 g->lines[2].p2[0] = pts[0];
4344 g->lines[2].p2[1] = pts[1];
4347 else if(
g->draw_near_point == 3)
4349 g->lines[3].p2[0] = pts[0];
4350 g->lines[3].p2[1] = pts[1];
4362 if(
g->draw_line_move >= 0)
4364 if(
g->draw_line_move >=
g->lines_count)
4366 g->draw_line_move = -1;
4371 const float pd_w =
geometry.processed_width;
4372 const float pd_h =
geometry.processed_height;
4373 float pts[2] = { pzx * pd_w, pzy * pd_h };
4377 const float dx = (pts[0] -
g->draw_pointmove_x);
4378 const float dy = (pts[1] -
g->draw_pointmove_y);
4379 const int n =
g->draw_line_move;
4380 g->draw_pointmove_x = pts[0];
4381 g->draw_pointmove_y = pts[1];
4384 g->lines[
n].p1[0] += dx;
4385 g->lines[
n].p1[1] += dy;
4386 g->lines[
n].p2[0] += dx;
4387 g->lines[
n].p2[1] += dy;
4389 g->lines[
n].p1[0] =
CLAMPF(
g->lines[
n].p1[0], 0.0f,
g->lines_in_width);
4390 g->lines[
n].p1[1] =
CLAMPF(
g->lines[
n].p1[1], 0.0f,
g->lines_in_height);
4391 g->lines[
n].p2[0] =
CLAMPF(
g->lines[
n].p2[0], 0.0f,
g->lines_in_width);
4392 g->lines[
n].p2[1] =
CLAMPF(
g->lines[
n].p2[1], 0.0f,
g->lines_in_height);
4401 g->lines[2].p1[0] =
g->lines[
n].p1[0];
4402 g->lines[2].p1[1] =
g->lines[
n].p1[1];
4403 g->lines[3].p1[0] =
g->lines[
n].p2[0];
4404 g->lines[3].p1[1] =
g->lines[
n].p2[1];
4410 g->lines[2].p2[0] =
g->lines[
n].p1[0];
4411 g->lines[2].p2[1] =
g->lines[
n].p1[1];
4412 g->lines[3].p2[0] =
g->lines[
n].p2[0];
4413 g->lines[3].p2[1] =
g->lines[
n].p2[1];
4419 g->lines[0].p1[0] =
g->lines[
n].p1[0];
4420 g->lines[0].p1[1] =
g->lines[
n].p1[1];
4421 g->lines[1].p1[0] =
g->lines[
n].p2[0];
4422 g->lines[1].p1[1] =
g->lines[
n].p2[1];
4428 g->lines[0].p2[0] =
g->lines[
n].p1[0];
4429 g->lines[0].p2[1] =
g->lines[
n].p1[1];
4430 g->lines[1].p2[0] =
g->lines[
n].p2[0];
4431 g->lines[1].p2[1] =
g->lines[
n].p2[1];
4456 if(wd >= 1.0 && ht >= 1.0 && selectable_lines_count > 0)
4460 g->lasty * ht,
g->isbounding);
4468 if(selectable_lines_count > 0)
4470 g->points,
g->points_idx, selectable_lines_count, pzx * wd, pzy * ht,
g->near_delta,
4474 if(
g->isdeselecting ||
g->isselecting)
4477 for(
int n = 0;
g->selecting_lines_version ==
g->lines_version &&
n < selectable_lines_count;
n++)
4479 if(
g->points_idx[
n].is_near == 0)
4482 if(
g->isdeselecting)
4484 g->lines[
n].type &= ~ASHIFT_LINE_SELECTED;
4499 g->selecting_lines_version++;
4505 return (
g->isdeselecting ||
g->isselecting);
4513 gboolean handled =
FALSE;
4516 if(
type == GDK_2BUTTON_PRESS && which == 1)
4519 float pzxpy[2] = { (float)
x, (
float)y };
4521 float pzx = pzxpy[0];
4522 float pzy = pzxpy[1];
4526 if(wd < 1.0 || ht < 1.0)
return 1;
4532 g->straightening =
TRUE;
4535 g->straighten_x = pzx;
4536 g->straighten_y = pzy;
4543 &&
g->draw_near_point >= 0)
4546 const int line =
g->draw_near_point / 2;
4549 g->draw_point_move =
TRUE;
4557 g->selecting_lines_version =
g->lines_version;
4581 const int selectable_lines_count = (!
IS_NULL_PTR(
g->lines)
4584 ?
MIN(
g->points_lines_count,
g->lines_count)
4588 const int take_control = (cur_scale == min_scale) && (selectable_lines_count > 0);
4596 if(selectable_lines_count > 0)
4597 _get_near(
g->points,
g->points_idx, selectable_lines_count,
4598 pzx * wd, pzy * ht,
g->near_delta,
4606 for(
int n = 0;
n < selectable_lines_count;
n++)
4608 if(
g->points_idx[
n].is_near)
4611 const float pd_w =
geometry.processed_width;
4612 const float pd_h =
geometry.processed_height;
4613 float pts[2] = { pzx * pd_w, pzy * pd_h };
4616 g->draw_line_move =
n;
4617 g->draw_pointmove_x = pts[0];
4618 g->draw_pointmove_y = pts[1];
4630 for(
int n = 0;
g->selecting_lines_version ==
g->lines_version &&
n < selectable_lines_count;
n++)
4632 if(
g->points_idx[
n].is_near == 0)
continue;
4637 g->lines[
n].type &= ~ASHIFT_LINE_SELECTED;
4643 g->horizontal_count--;
4644 g->horizontal_weight -= 1.0f;
4648 g->vertical_count--;
4649 g->vertical_weight -= 1.0f;
4652 const int count =
g->lines_count - 1;
4661 g->horizontal_count++;
4662 g->horizontal_weight += 1.0f;
4666 g->vertical_count++;
4667 g->vertical_weight += 1.0f;
4673 for(
int i = 0;
i <
g->lines_count;
i++)
4677 lines[pos] =
g->lines[
i];
4686 g->lines_count = count;
4705 g->draw_point_move =
TRUE;
4712 const float pd_w =
geometry.processed_width;
4713 const float pd_h =
geometry.processed_height;
4714 float pts[2] = { pzx * pd_w, pzy * pd_h };
4717 const int count =
g->lines_count + 1;
4723 for(
int i = 0;
i <
g->lines_count;
i++)
4725 lines[
i] =
g->lines[
i];
4732 g->lines_count = count;
4735 g->vertical_count++;
4736 g->vertical_weight += 1.0f;
4737 g->draw_near_point =
g->lines_count * 2 - 1;
4744 if((take_control || handled) && which == 3)
4747 g->isdeselecting = 1;
4749 else if(take_control || handled)
4759 g->selecting_lines_version++;
4762 return (take_control || handled);
4775 if(
g->straightening && !
g->editing)
4777 g->straightening =
FALSE;
4779 float pzxpy[2] = { (float)
x, (
float)y };
4781 const float pzx = pzxpy[0];
4782 const float pzy = pzxpy[1];
4784 const float pd_w =
geometry.processed_width;
4785 const float pd_h =
geometry.processed_height;
4786 float pts[4] = { pzx * pd_w, pzy * pd_h,
g->lastx * pd_w,
g->lasty * pd_h };
4791 float dx = pts[0] - pts[2];
4792 float dy = pts[1] - pts[3];
4799 float angle = atan2f(dy, dx);
4800 if(!(angle >= -
M_PI / 2.0 && angle <=
M_PI / 2.0)) angle = 0.0f;
4801 float close = angle;
4802 if(close >
M_PI / 4.0)
4803 close =
M_PI / 2.0 - close;
4804 else if(close < -
M_PI / 4.0)
4805 close = -
M_PI / 2.0 - close;
4809 float a = 180.0 /
M_PI * close;
4810 if(a < -180.0) a += 360.0;
4811 if(a > 180.0) a -= 360.0;
4826 if(
g->draw_line_move >= 0)
4828 g->draw_line_move = -1;
4835 if(
g->draw_point_move)
4839 g->draw_point_move =
FALSE;
4840 g->draw_near_point = -1;
4847 for(
int l = 0; l <
g->lines_count; l++)
4854 g->vertical_count++;
4855 g->vertical_weight += 1.0f;
4856 g->horizontal_count--;
4857 g->horizontal_weight -= 1.0f;
4859 else if(
g->lines[l].type != old_linetype)
4861 g->horizontal_count++;
4862 g->horizontal_weight += 1.0f;
4863 g->vertical_count--;
4864 g->vertical_weight -= 1.0f;
4869 g->draw_point_move =
FALSE;
4870 g->draw_near_point = -1;
4883 gboolean handled =
FALSE;
4886 float pzxpy[2] = { (float)
x, (
float)y };
4888 const float pzx = pzxpy[0];
4889 const float pzy = pzxpy[1];
4891 if(wd >= 1.0 && ht >= 1.0)
4893 const int selectable_lines_count = (!
IS_NULL_PTR(
g->lines)
4896 ?
MIN(
g->points_lines_count,
g->lines_count)
4900 if(selectable_lines_count > 0)
4902 g->lasty * ht,
g->isbounding);
4905 for(
int n = 0;
g->selecting_lines_version ==
g->lines_version &&
n < selectable_lines_count;
n++)
4907 if(
g->points_idx[
n].bounded == 0)
continue;
4911 g->lines[
n].type &= ~ASHIFT_LINE_SELECTED;
4925 g->selecting_lines_version++;
4933 g->isselecting =
g->isdeselecting = 0;
4936 g->lastx =
g->lasty = -1.0f;
4937 g->crop_cx =
g->crop_cy = -1.0f;
4957 if(
g->near_delta > 0 && (
g->isdeselecting ||
g->isselecting))
4959 gboolean handled =
FALSE;
4961 float pzxpy[2] = { (float)
x, (
float)y };
4963 const float pzx = pzxpy[0];
4964 const float pzy = pzxpy[1];
4968 float near_delta = 5.0f;
4973 const float amount = up ? 0.8f : 1.25f;
4974 near_delta =
MAX(4.0f,
MIN(near_delta * amount, 100.0f));
4979 g->near_delta = near_delta;
4987 ?
MIN(
g->points_lines_count,
g->lines_count)
4989 if(selectable_lines_count > 0)
4990 _get_near(
g->points,
g->points_idx, selectable_lines_count, pzx * wd, pzy * ht,
g->near_delta,
TRUE);
4993 for(
int n = 0;
g->selecting_lines_version ==
g->lines_version &&
n < selectable_lines_count;
n++)
4995 if(
g->points_idx[
n].is_near == 0)
4998 if(
g->isdeselecting)
5000 g->lines[
n].type &= ~ASHIFT_LINE_SELECTED;
5016 g->selecting_lines_version++;
5030 gtk_widget_set_sensitive(
g->structure_auto, sensitive);
5031 gtk_widget_set_sensitive(
g->structure_lines, sensitive);
5032 gtk_widget_set_sensitive(
g->structure_quad, sensitive);
5043 model_probe(self,
p,
g->lastfit);
5046 if(w ==
g->rotation)
5048 else if(w ==
g->lensshift_h)
5050 else if(w ==
g->lensshift_v)
5052 else if(w ==
g->shear)
5054 else if(w ==
g->mode)
5059 else if(w ==
g->f_length)
5061 else if(w ==
g->crop_factor)
5063 else if(w ==
g->orthocorr)
5065 else if(w ==
g->aspect)
5086 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->edit_button),
FALSE);
5087 gtk_button_set_label(GTK_BUTTON(
g->edit_button), _(
"Edit"));
5088 gtk_widget_set_sensitive(
g->commit_button,
FALSE);
5114 p->cropmode = crop_mode;
5149 if(event->button == 1)
5156 switch(
g->fitting_mode)
5184 g->jobparams =
g->lastfit = fitaxis;
5197 if(event->button == 1)
5204 switch(
g->fitting_mode)
5232 g->jobparams =
g->lastfit = fitaxis;
5245 if(event->button == 1)
5251 switch(
g->fitting_mode)
5281 g->jobparams =
g->lastfit = fitaxis;
5294 if(event->button == 1)
5310 if(enhance ==
ASHIFT_ENHANCE_NONE && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
5318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
FALSE);
5335 g->jobparams = enhance;
5349 if(!
g->editing)
return FALSE;
5374 if(!
g->editing)
return FALSE;
5400 g->editing = gtk_toggle_button_get_active(button);
5415 gtk_button_set_label(GTK_BUTTON(button), _(
"Cancel"));
5416 gtk_widget_set_sensitive(
g->commit_button,
TRUE);
5434 gtk_button_set_label(GTK_BUTTON(button), _(
"Edit"));
5435 gtk_widget_set_sensitive(
g->commit_button,
FALSE);
5455 gtk_widget_set_sensitive(
g->commit_button,
FALSE);
5471 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->edit_button),
FALSE);
5472 gtk_button_set_label(GTK_BUTTON(
g->edit_button), _(
"Edit"));
5482 int jobparams =
g->jobparams;
5584 d->lensshift_v =
p->lensshift_v;
5585 d->lensshift_h =
p->lensshift_h;
5586 d->shear =
p->shear;
5623 size_t points_count,
const int direction)
5629 homography((
float *)h,
d->rotation,
d->lensshift_v,
d->lensshift_h,
d->shear,
d->f_length_kb,
d->orthocorr,
5633 const float fullwidth = (float)record->
out.
width / (
d->cr -
d->cl);
5634 const float fullheight = (float)record->
out.
height / (
d->cb -
d->ct);
5635 const float cx = fullwidth *
d->cl;
5636 const float cy = fullheight *
d->ct;
5639 for(
size_t i = 0;
i < points_count * 2;
i += 2)
5641 float DT_ALIGNED_PIXEL pi[3] = { points[
i] + (forward ? 0.f : cx), points[
i + 1] + (forward ? 0.f : cy),
5645 points[
i] = po[0] / po[2] - (forward ? cx : 0.f);
5646 points[
i + 1] = po[1] / po[2] - (forward ? cy : 0.f);
5658 homography((
float *)h,
d->rotation,
d->lensshift_v,
d->lensshift_h,
d->shear,
d->f_length_kb,
d->orthocorr,
5661 float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX;
5667 float pin[3] = { (in->
x +
x) / in->
scale, (in->
y + y) / in->
scale, 1.0f };
5670 pout[0] = pout[0] / pout[2] *
out->scale;
5671 pout[1] = pout[1] / pout[2] *
out->scale;
5672 xm =
MIN(xm, pout[0]);
5673 xM =
MAX(xM, pout[0]);
5674 ym =
MIN(ym, pout[1]);
5675 yM =
MAX(yM, pout[1]);
5679 out->width = floorf((xM - xm + 1) * (
d->cr -
d->cl));
5680 out->height = floorf((yM - ym + 1) * (
d->cb -
d->ct));
5708 record->
data = data;
5726 piece->
data = (
void *)
d;
5758 snprintf(string_v,
sizeof(string_v), _(
"lens shift (%s)"), isflipped ? _(
"horizontal") : _(
"vertical"));
5759 snprintf(string_h,
sizeof(string_h), _(
"lens shift (%s)"), isflipped ? _(
"vertical") : _(
"horizontal"));
5774 g->buf_scale = 1.0f;
5785 g->horizontal_count = 0;
5786 g->vertical_count = 0;
5793 g->lines_version = 0;
5795 g->isdeselecting = 0;
5798 g->selecting_lines_version = 0;
5803 g->points_idx = NULL;
5804 g->points_lines_count = 0;
5805 g->points_version = 0;
5809 g->lastx =
g->lasty = -1.0f;
5810 g->crop_cx =
g->crop_cy = 1.0f;
5813 g->draw_line_move = -1;
5814 g->draw_near_point = -1;
5815 g->draw_point_move =
FALSE;
5831 module->default_enabled = 0;
5834 float crop_factor = 1.0f;
5839 const dt_image_t *img = &
module->dev->image_storage;
5865 const int program = 2;
5887 const int isflipped =
g->isflipped;
5890 if(isflipped == -1)
return FALSE;
5895 snprintf(string_v,
sizeof(string_v), _(
"lens shift (%s)"), isflipped ? _(
"horizontal") : _(
"vertical"));
5896 snprintf(string_h,
sizeof(string_h), _(
"lens shift (%s)"), isflipped ? _(
"vertical") : _(
"horizontal"));
5916 g->buf_scale = 1.0f;
5927 g->vertical_count = 0;
5928 g->horizontal_count = 0;
5929 g->lines_version = 0;
5931 g->points_idx = NULL;
5932 g->points_lines_count = 0;
5933 g->points_version = 0;
5941 g->isdeselecting = 0;
5944 g->selecting_lines_version = 0;
5948 g->lastx =
g->lasty = -1.0f;
5949 g->crop_cx =
g->crop_cy = 1.0f;
5953 g->draw_near_point = -1;
5954 g->draw_line_move = -1;
5960 g->edit_button = gtk_toggle_button_new_with_label(_(
"Edit"));
5961 g_signal_connect(GTK_TOGGLE_BUTTON(
g->edit_button),
"toggled", G_CALLBACK(
_enter_edit_mode), self);
5962 gtk_box_pack_start(GTK_BOX(box),
g->edit_button,
TRUE,
TRUE, 0);
5965 gtk_box_pack_start(GTK_BOX(box),
g->commit_button,
TRUE,
TRUE, 0);
5966 gtk_widget_set_sensitive(
g->commit_button,
FALSE);
5974 "plugins/darkroom/ashift/expand_values",
5975 _(
"Manual settings"),
5976 GTK_BOX(main_box), GTK_PACK_END);
5978 self->
gui->
widget = GTK_WIDGET(
g->cs.container);
6011 gtk_widget_set_no_show_all(
g->orthocorr,
TRUE);
6012 gtk_widget_set_visible(
g->orthocorr,
FALSE);
6016 gtk_box_pack_start(GTK_BOX(
g->cs.container),
g->specifics,
TRUE,
TRUE, 0);
6020 GtkGrid *auto_grid = GTK_GRID(gtk_grid_new());
6024 gtk_grid_attach(auto_grid,
dt_ui_label_new(_(
"Mark reference lines")), 0, 0, 1, 1);
6027 gtk_widget_set_hexpand(GTK_WIDGET(
g->structure_lines),
TRUE);
6028 gtk_grid_attach(auto_grid,
g->structure_lines, 1, 0, 1, 1);
6031 gtk_widget_set_hexpand(GTK_WIDGET(
g->structure_quad),
TRUE);
6032 gtk_grid_attach(auto_grid,
g->structure_quad, 2, 0, 1, 1);
6035 gtk_widget_set_hexpand(GTK_WIDGET(
g->structure_auto),
TRUE);
6036 gtk_grid_attach(auto_grid,
g->structure_auto, 3, 0, 1, 1);
6038 gtk_grid_attach(auto_grid,
dt_ui_label_new(_(
"Fit perspective transform")), 0, 1, 1, 1);
6041 gtk_widget_set_hexpand(GTK_WIDGET(
g->fit_v),
TRUE);
6042 gtk_grid_attach(auto_grid,
g->fit_v, 1, 1, 1, 1);
6045 gtk_widget_set_hexpand(GTK_WIDGET(
g->fit_h),
TRUE);
6046 gtk_grid_attach(auto_grid,
g->fit_h, 2, 1, 1, 1);
6049 gtk_widget_set_hexpand(GTK_WIDGET(
g->fit_both),
TRUE);
6050 gtk_grid_attach(auto_grid,
g->fit_both, 3, 1, 1, 1);
6052 gtk_widget_show_all(GTK_WIDGET(auto_grid));
6053 gtk_box_pack_start(GTK_BOX(self->
gui->
widget), GTK_WIDGET(auto_grid),
TRUE,
TRUE, 0);
6058 const gchar *option_labels[] = { _(
"rotation, lens shift, shear"),
6059 _(
"rotation, lens shift"),
6061 _(
"lens shift only"), NULL };
6067 const gchar *crop_labels[] = { _(
"off"), _(
"largest area"), _(
"original format"), NULL };
6078 gtk_widget_set_tooltip_text(
g->rotation, _(
"rotate image\nright-click and drag to define a horizontal or vertical line by drawing on the image"));
6079 gtk_widget_set_tooltip_text(
g->lensshift_v, _(
"apply lens shift correction in one direction"));
6080 gtk_widget_set_tooltip_text(
g->lensshift_h, _(
"apply lens shift correction in one direction"));
6081 gtk_widget_set_tooltip_text(
g->shear, _(
"shear the image along one diagonal"));
6082 gtk_widget_set_tooltip_text(
g->cropmode, _(
"automatically crop to avoid black edges"));
6083 gtk_widget_set_tooltip_text(
g->mode, _(
"lens model of the perspective correction: "
6084 "generic or according to the focal length"));
6085 gtk_widget_set_tooltip_text(
g->f_length, _(
"focal length of the lens, "
6086 "default value set from EXIF data if available"));
6087 gtk_widget_set_tooltip_text(
g->crop_factor, _(
"crop factor of the camera sensor, "
6088 "default value set from EXIF data if available, "
6089 "manual setting is often required"));
6090 gtk_widget_set_tooltip_text(
g->orthocorr, _(
"the level of lens dependent correction, set to maximum for full lens dependency, "
6091 "set to zero for the generic case"));
6092 gtk_widget_set_tooltip_text(
g->aspect, _(
"adjust aspect ratio of image by horizontal and vertical scaling"));
6093 gtk_widget_set_tooltip_text(
g->fit_v, _(
"automatically correct for vertical perspective distortion\n"
6094 "ctrl+click to only fit rotation\n"
6095 "shift+click to only fit lens shift"));
6096 gtk_widget_set_tooltip_text(
g->fit_h, _(
"automatically correct for horizontal perspective distortion\n"
6097 "ctrl+click to only fit rotation\n"
6098 "shift+click to only fit lens shift"));
6099 gtk_widget_set_tooltip_text(
g->fit_both, _(
"automatically correct for vertical and "
6100 "horizontal perspective distortions; fitting rotation,"
6101 "lens shift in both directions, and shear\n"
6102 "ctrl+click to only fit rotation\n"
6103 "shift+click to only fit lens shift\n"
6104 "ctrl+shift+click to only fit rotation and lens shift"));
6105 gtk_widget_set_tooltip_text(
g->structure_auto, _(
"automatically analyse line structure in image\n"
6106 "ctrl+click for an additional edge enhancement\n"
6107 "shift+click for an additional detail enhancement\n"
6108 "ctrl+shift+click for a combination of both methods"));
6109 gtk_widget_set_tooltip_text(
g->structure_quad, _(
"manually define perspective rectangle"));
6110 gtk_widget_set_tooltip_text(
g->structure_lines, _(
"manually draw structure lines"));
Handle default and user-set shortcuts (accelerators)
static int _draw_near_point(dt_develop_t *dev, const float x, const float y, const float *points, const int limit)
int operation_tags_filter()
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static double logit(double x, double min, double max)
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
static void vec3norm(float *dst, const float *const v)
const char ** description(struct dt_iop_module_t *self)
static int _remove_outliers(dt_iop_module_t *module)
static void sRGB_to_XYZ(const dt_aligned_pixel_t sRGB, dt_aligned_pixel_t XYZ)
static float vec3scalar(const float *const v1, const float *const v2)
int scrolled(struct dt_iop_module_t *self, double x, double y, int up, uint32_t state)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
#define RANSAC_OPTIMIZATION_STEPS
static void _gui_update_structure_states(dt_iop_module_t *self, gboolean enable)
static int update_colors(struct dt_iop_module_t *self, dt_iop_ashift_points_idx_t *points_idx, int points_lines_count)
static double ilogit(double L, double min, double max)
@ ASHIFT_BOUNDING_DESELECT
static void _update_lines_count(const dt_iop_ashift_line_t *lines, const int lines_count, int *vertical_count, int *horizontal_count)
void reload_defaults(dt_iop_module_t *module)
static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir)
static void vec3prodn(float *dst, const float *const v1, const float *const v2)
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
static void _event_process_after_preview_callback(gpointer instance, gpointer user_data)
static void vec3lnorm(float *dst, const float *const v)
static int fact(const int n)
static gboolean _draw_retrieve_lines_from_params(dt_iop_module_t *self, dt_iop_ashift_method_t method)
static void _get_near(const float *points, dt_iop_ashift_points_idx_t *points_idx, const int lines_count, float pzx, float pzy, float delta, gboolean multiple)
static int _event_structure_auto_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static int _get_structure(dt_iop_module_t *module, dt_iop_ashift_enhance_t enhance)
dt_iop_ashift_params_t * _get_ashift_params(dt_iop_module_t *self)
dt_iop_ashift_linecolor_t
@ ASHIFT_LINECOLOR_YELLOW
static int _event_structure_lines_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static void XYZ_to_sRGB(const dt_aligned_pixel_t XYZ, dt_aligned_pixel_t sRGB)
@ ASHIFT_FIT_BOTH_NO_ROTATION
@ ASHIFT_FIT_HORIZONTALLY_NO_ROTATION
@ ASHIFT_FIT_ROTATION_HORIZONTAL_LINES
@ ASHIFT_FIT_ROTATION_VERTICAL_LINES
@ ASHIFT_FIT_HORIZONTALLY
@ ASHIFT_FIT_ROTATION_BOTH_LINES
@ ASHIFT_FIT_VERTICALLY_NO_ROTATION
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _clear_crop_box(dt_iop_ashift_params_t *p)
Reset the active crop rectangle to the complete transformed image.
static int call_distort_transform(struct dt_iop_module_t *self, float *points, size_t points_count)
static void shuffle(int *a, const int N)
static double model_fitness(double *params, void *data)
static int _event_fit_h_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
dt_iop_ashift_nmsresult_t
static void _event_process_after_ui_callback(gpointer instance, gpointer user_data)
Refresh ashift overlay geometry once the displayed pipe published its new output.
void gui_reset(struct dt_iop_module_t *self)
static void _ashift_resolve(struct dt_iop_module_t *self, const dt_iop_ashift_params_t *const p1, dt_iop_ashift_data_t *d)
void gui_update(struct dt_iop_module_t *self)
static int quickperm(int *a, int *p, const int N, int *i)
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count)
void gui_init(struct dt_iop_module_t *self)
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
static int _do_clean_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p, gboolean save_drawn)
static int _do_get_structure_auto(dt_iop_module_t *self, dt_iop_ashift_params_t *p, dt_iop_ashift_enhance_t enhance)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
static double crop_fitness(double *params, void *data)
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
static void _run_pending_preview_job(dt_iop_module_t *self)
static void gamma_correct(const float *const in, float *const out, const int width, const int height)
static int _event_fit_v_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static int isneutral(const dt_iop_ashift_data_t *data)
static int edge_enhance(const double *in, double *out, const int width, const int height)
void _make_controls_sensitive(dt_iop_module_t *self, const gboolean sensitive)
#define RANSAC_EPSILON_STEP
void cleanup_global(dt_iop_module_so_t *module)
@ ASHIFT_JOBCODE_GET_STRUCTURE
@ ASHIFT_JOBCODE_GET_STRUCTURE_LINES
@ ASHIFT_JOBCODE_GET_STRUCTURE_QUAD
static uint64_t _get_lines_hash(const dt_iop_ashift_line_t *lines, const int lines_count)
static __DT_CLONE_TARGETS__ void homography(float *homograph, const float angle, const float shift_v, const float shift_h, const float shear, const float f_length_kb, const float orthocorr, const float aspect, const int width, const int height, dt_iop_ashift_homodir_t dir)
static void _get_bounded_inside(const float *points, dt_iop_ashift_points_idx_t *points_idx, const int points_lines_count, float pzx, float pzy, float pzx2, float pzy2, dt_iop_ashift_bounding_t mode)
static void _enter_edit_mode(GtkToggleButton *button, struct dt_iop_module_t *self)
static void crop_constraint(double *params, int pcount)
static gboolean _event_draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
#define LENSSHIFT_RANGE_SOFT
static const dt_geometry_vtable_t _ashift_geometry_vtable
static int _ashift_geometry_backtransform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
@ ASHIFT_FITTING_LENS_ROTATION
@ ASHIFT_FITTING_ROTATION
#define MAX_TANGENTIAL_DEVIATION
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in)
void gui_cleanup(struct dt_iop_module_t *self)
static void _draw_recompute_line_length(dt_iop_ashift_line_t *line)
static gboolean _ashift_orientation_swaps_axes(dt_iop_module_t *self)
static void _draw_retrieve_line_type(dt_iop_ashift_line_t *line)
#define ROTATION_RANGE_SOFT
#define RANSAC_ELIMINATION_RATIO
static void edge_enhance_1d(const double *in, double *out, const int width, const int height, dt_iop_ashift_enhance_t dir)
static int detail_enhance(const float *const in, float *const out, const int width, const int height)
static void ransac(const dt_iop_ashift_line_t *lines, int *index_set, int *inout_set, const int set_count, const float total_weight, const int xmin, const int xmax, const int ymin, const int ymax)
#define RANSAC_OPTIMIZATION_DRY_RUNS
static int vec3isnull(const float *const v)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _ashift_geometry_map_size(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
static int _event_structure_quad_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
@ ASHIFT_ENHANCE_HORIZONTAL
@ ASHIFT_ENHANCE_VERTICAL
@ ASHIFT_LINE_VERTICAL_NOT_SELECTED
@ ASHIFT_LINE_HORIZONTAL_NOT_SELECTED
@ ASHIFT_LINE_HORIZONTAL_SELECTED
@ ASHIFT_LINE_VERTICAL_SELECTED
static void _do_get_structure_lines(dt_iop_module_t *self)
static void _draw_save_lines_to_params(dt_iop_module_t *self)
static void do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir)
void init_global(dt_iop_module_so_t *module)
gboolean geometry_record(struct dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
static int line_detect(float *in, const int width, const int height, const int x_off, const int y_off, const float scale, dt_iop_ashift_line_t **alines, int *lcount, int *vcount, int *hcount, float *vweight, float *hweight, dt_iop_ashift_enhance_t enhance, const int is_raw)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
static void rgb2grey256(const float *const in, double *const out, const int width, const int height)
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
static int _ashift_geometry_apply(const void *data, const dt_geometry_record_t *const record, float *points, size_t points_count, const int direction)
Apply the homography plus the clipping offset, in whichever direction.
static void _do_get_structure_quad(dt_iop_module_t *self)
@ ASHIFT_HOMOGRAPH_FORWARD
@ ASHIFT_HOMOGRAPH_INVERTED
static void fitting_option_changed(GtkWidget *widget, gpointer user_data)
static void _event_commit_clicked(GtkButton *button, dt_iop_module_t *self)
static int _event_fit_both_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static int get_points(struct dt_iop_module_t *self, const dt_iop_ashift_line_t *lines, const int lines_count, const int lines_version, float **points, float **extremas, dt_iop_ashift_points_idx_t **points_idx, int *points_lines_count, float scale)
static void cropmode_callback(GtkWidget *widget, gpointer user_data)
static void do_crop(dt_iop_module_t *self, dt_iop_ashift_params_t *p)
static void swap(int *a, int *b)
void modify_roi_out(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static int _ashift_geometry_transform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
gboolean runtime_data_hash(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static void _draw_basic_line(dt_iop_ashift_line_t *line, float x1, float y1, float x2, float y2, dt_iop_ashift_linetype_t type)
#define NMS_CROP_ITERATIONS
static double * LineSegmentDetection(int *n_out, double *img, int X, int Y, double scale, double sigma_scale, double quant, double ang_th, double log_eps, double density_th, int n_bins, int **reg_img, int *reg_x, int *reg_y)
static void error(char *msg)
GtkWidget * dt_bauhaus_combobox_new_full(dt_bauhaus_t *bh, dt_gui_module_t *self, const char *label, const char *tip, int pos, GtkCallback callback, gpointer data, const char **texts)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
float dt_bauhaus_slider_get(GtkWidget *widget)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bilateral_free(dt_bilateral_t *b)
__DT_CLONE_TARGETS__ void dt_bilateral_splat(const dt_bilateral_t *b, const float *const in)
dt_bilateral_t * dt_bilateral_init(const int width, const int height, const float sigma_s, const float sigma_r)
__DT_CLONE_TARGETS__ void dt_bilateral_slice_to_output(const dt_bilateral_t *const b, const float *const in, float *out, const float detail)
void dt_bilateral_blur(const dt_bilateral_t *b)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_gui_new_collapsible_section(dt_gui_collapsible_section_t *cs, const char *confname, const char *label, GtkBox *parent, GtkPackType pack)
Create a collapsible section and pack it into the parent box.
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
int mat3inv(float *const dst, const float *const src)
Thin alias of mat3inv_float(), same contract.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t sRGB
const dt_colormatrix_t dt_aligned_pixel_t out
static const float const float C
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
dt_image_orientation_t dt_image_get_effective_orientation(const dt_image_t *img)
void dt_control_mouse_is_dragging(gboolean state)
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
void dt_control_queue_redraw()
Request a redraw of the whole workspace.
#define dt_control_change_cursor(cursor)
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
gboolean dt_dev_geometry_get_raw_size(const dt_develop_t *dev, int32_t *width, int32_t *height)
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
#define dt_dev_add_history_item(dev, module, enable, redraw)
#define dt_dev_pixelpipe_resync_history_all(dev)
#define dt_dev_pixelpipe_update_zoom_preview(dev)
#define dt_dev_pixelpipe_update_zoom_main(dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
int32_t dt_dev_roi_request_preview_height(const dt_develop_t *dev)
int32_t dt_dev_roi_request_preview_width(const dt_develop_t *dev)
float dt_dev_get_natural_scale(dt_develop_t *dev)
int dt_dev_get_thumbnail_size(dt_develop_t *dev)
int dt_dev_distort_backtransform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction, float *points, size_t points_count)
The inverse of dt_dev_distort_transform_gui(), same rules.
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
gboolean dt_dev_module_geometry_gui(dt_develop_t *dev, dt_iop_module_t *module, dt_iop_roi_t *in, dt_iop_roi_t *out)
One module's own input and output rectangles at full resolution, from the geometry service.
int dt_dev_distort_transform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction, float *points, size_t points_count)
The GUI's bounded transform folds, composed by the geometry service.
gboolean dt_dev_clip_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Clip the view to the ROI. WARNING: this must be done before any translation.
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
GtkWidget * geometry
its size, under the preview
static void dt_draw_set_color_overlay(cairo_t *cr, gboolean bright, double alpha)
static void dt_gui_draw_rounded_rectangle(cairo_t *cr, float width, float height, float x, float y)
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
int dt_geometry_module_transform(dt_develop_t *dev, const dt_iop_module_t *module, float *points, const size_t points_count)
void dt_geometry_chain_rebuild(dt_develop_t *dev)
Rebuild the chain from the dev's current modules and history. GUI thread only.
Where things are on the image, answered without a pipeline.
void dt_guides_draw(cairo_t *cr, const float left, const float top, const float width, const float height, const float zoom_scale)
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
static gboolean enable(const dt_image_t *image)
float dt_boundingbox_t[4]
@ ORIENTATION_ROTATE_CCW_90_DEG
@ ORIENTATION_ROTATE_CW_90_DEG
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
@ IOP_FLAGS_GUIDES_SPECIAL_DRAW
@ IOP_FLAGS_TILING_FULL_ROI
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
__DT_CLONE_TARGETS__ void dt_interpolation_compute_pixel4c(const struct dt_interpolation *itor, const float *in, float *out, const float x, const float y, const int width, const int height, const int linestride)
@ DT_INTERPOLATION_BICUBIC
@ DT_INTERPOLATION_BILINEAR
@ DT_INTERPOLATION_MITCHELL
@ DT_INTERPOLATION_USERPREF_WARP
static float kernel(const float *x, const float *y)
GtkWidget * dt_ui_section_label_new(const gchar *str)
GtkWidget * dt_ui_label_new(const gchar *str)
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
_lib_location_type_t type
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void mat3mul(float *const __restrict__ dest, const float *const __restrict__ m1, const float *const __restrict__ m2)
#define CLAMPF(a, mn, mx)
static void mat3mulv(float *const __restrict__ dest, const float *const mat, const float *const __restrict__ v)
#define DT_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define DT_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
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 dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
int dt_opencl_copy_device_to_host(const int devid, void *host, void *device, const int width, const int height, const int bpp)
int dt_opencl_create_kernel(const int prog, const char *name)
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
void dt_opencl_release_mem_object(cl_mem mem)
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define DT_PIXELPIPE_CACHE_HASH_INVALID
static uint64_t dt_dev_pixelpipe_get_hash(const dt_dev_pixelpipe_t *pipe)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
@ DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED
This signal is raised when pipe is finished and the gui is attached no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
const float uint32_t state[4]
unsigned __int64 uint64_t
Objective facts about the image a dev is working on.
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_dev_pixelpipe_t * preview_pipe
One module instance's contribution, as data.
const dt_geometry_vtable_t * vtable
void(* free_data)(void *data)
A module's geometry, evaluated. Pure functions of the record's own data.
void(* map_size)(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Full-resolution input rect -> output rect. Mirrors modify_roi_out() at scale 1.
dt_image_orientation_t orientation
enum dt_interpolation_type id
dt_iop_ashift_linetype_t linemask
dt_iop_ashift_line_t * lines
dt_iop_ashift_linetype_t linetype
int kernel_ashift_bilinear
int kernel_ashift_bicubic
int kernel_ashift_mitchell
GtkWidget * commit_button
dt_iop_ashift_jobcode_t jobcode
GtkWidget * structure_auto
dt_iop_ashift_fitting_t fitting_mode
GtkWidget * structure_quad
dt_iop_ashift_points_idx_t * points_idx
int selecting_lines_version
GtkWidget * structure_lines
dt_iop_ashift_line_t * lines
dt_gui_collapsible_section_t cs
dt_iop_ashift_bounding_t isbounding
dt_iop_ashift_fitaxis_t lastfit
dt_iop_ashift_params_t previous_params
GtkWidget * fitting_option
dt_iop_ashift_params_t new_params
dt_iop_ashift_method_t current_structure_method
dt_iop_ashift_linetype_t type
dt_iop_ashift_mode_t mode
dt_iop_ashift_mode_t mode
dt_iop_ashift_crop_t cropmode
dt_iop_ashift_mode_t mode
dt_iop_ashift_crop_t cropmode
int last_drawn_lines_count
float last_drawn_lines[50 *4]
dt_iop_ashift_crop_t cropmode
dt_iop_ashift_mode_t mode
dt_iop_ashift_linecolor_t color
dt_iop_ashift_linetype_t type
dt_iop_global_data_t * data
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
dt_iop_global_data_t * global_data
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__