You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

547 lines
26KB

  1. /*
  2. * Copyright (C) 2007 by Andrew Zabolotny (author of lensfun, from which this filter derives from)
  3. * Copyright (C) 2018 Stephen Seo
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. */
  20. /**
  21. * @file
  22. * Lensfun filter, applies lens correction with parameters from the lensfun database
  23. *
  24. * @see https://lensfun.sourceforge.net/
  25. */
  26. #include <float.h>
  27. #include <math.h>
  28. #include "libavutil/avassert.h"
  29. #include "libavutil/imgutils.h"
  30. #include "libavutil/opt.h"
  31. #include "libswscale/swscale.h"
  32. #include "avfilter.h"
  33. #include "formats.h"
  34. #include "internal.h"
  35. #include "video.h"
  36. #include <lensfun.h>
  37. #define LANCZOS_RESOLUTION 256
  38. enum Mode {
  39. VIGNETTING = 0x1,
  40. GEOMETRY_DISTORTION = 0x2,
  41. SUBPIXEL_DISTORTION = 0x4
  42. };
  43. enum InterpolationType {
  44. NEAREST,
  45. LINEAR,
  46. LANCZOS
  47. };
  48. typedef struct VignettingThreadData {
  49. int width, height;
  50. uint8_t *data_in;
  51. int linesize_in;
  52. int pixel_composition;
  53. lfModifier *modifier;
  54. } VignettingThreadData;
  55. typedef struct DistortionCorrectionThreadData {
  56. int width, height;
  57. const float *distortion_coords;
  58. const uint8_t *data_in;
  59. uint8_t *data_out;
  60. int linesize_in, linesize_out;
  61. const float *interpolation;
  62. int mode;
  63. int interpolation_type;
  64. } DistortionCorrectionThreadData;
  65. typedef struct LensfunContext {
  66. const AVClass *class;
  67. const char *make, *model, *lens_model;
  68. int mode;
  69. float focal_length;
  70. float aperture;
  71. float focus_distance;
  72. int target_geometry;
  73. int reverse;
  74. int interpolation_type;
  75. float *distortion_coords;
  76. float *interpolation;
  77. lfLens *lens;
  78. lfCamera *camera;
  79. lfModifier *modifier;
  80. } LensfunContext;
  81. #define OFFSET(x) offsetof(LensfunContext, x)
  82. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  83. static const AVOption lensfun_options[] = {
  84. { "make", "set camera maker", OFFSET(make), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
  85. { "model", "set camera model", OFFSET(model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
  86. { "lens_model", "set lens model", OFFSET(lens_model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
  87. { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=GEOMETRY_DISTORTION}, 0, VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION, FLAGS, "mode" },
  88. { "vignetting", "fix lens vignetting", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING}, 0, 0, FLAGS, "mode" },
  89. { "geometry", "correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
  90. { "subpixel", "fix chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
  91. { "vig_geo", "fix lens vignetting and correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
  92. { "vig_subpixel", "fix lens vignetting and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
  93. { "distortion", "correct geometry distortion and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
  94. { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
  95. { "focal_length", "focal length of video (zoom; constant for the duration of the use of this filter)", OFFSET(focal_length), AV_OPT_TYPE_FLOAT, {.dbl=18}, 0.0, DBL_MAX, FLAGS },
  96. { "aperture", "aperture (constant for the duration of the use of this filter)", OFFSET(aperture), AV_OPT_TYPE_FLOAT, {.dbl=3.5}, 0.0, DBL_MAX, FLAGS },
  97. { "focus_distance", "focus distance (constant for the duration of the use of this filter)", OFFSET(focus_distance), AV_OPT_TYPE_FLOAT, {.dbl=1000.0f}, 0.0, DBL_MAX, FLAGS },
  98. { "target_geometry", "target geometry of the lens correction (only when geometry correction is enabled)", OFFSET(target_geometry), AV_OPT_TYPE_INT, {.i64=LF_RECTILINEAR}, 0, INT_MAX, FLAGS, "lens_geometry" },
  99. { "rectilinear", "rectilinear lens (default)", 0, AV_OPT_TYPE_CONST, {.i64=LF_RECTILINEAR}, 0, 0, FLAGS, "lens_geometry" },
  100. { "fisheye", "fisheye lens", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE}, 0, 0, FLAGS, "lens_geometry" },
  101. { "panoramic", "panoramic (cylindrical)", 0, AV_OPT_TYPE_CONST, {.i64=LF_PANORAMIC}, 0, 0, FLAGS, "lens_geometry" },
  102. { "equirectangular", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=LF_EQUIRECTANGULAR}, 0, 0, FLAGS, "lens_geometry" },
  103. { "fisheye_orthographic", "orthographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_ORTHOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
  104. { "fisheye_stereographic", "stereographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_STEREOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
  105. { "fisheye_equisolid", "equisolid fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_EQUISOLID}, 0, 0, FLAGS, "lens_geometry" },
  106. { "fisheye_thoby", "fisheye as measured by thoby", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_THOBY}, 0, 0, FLAGS, "lens_geometry" },
  107. { "reverse", "Does reverse correction (regular image to lens distorted)", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
  108. { "interpolation", "Type of interpolation", OFFSET(interpolation_type), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, LANCZOS, FLAGS, "interpolation" },
  109. { "nearest", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interpolation" },
  110. { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" },
  111. { "lanczos", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interpolation" },
  112. { NULL }
  113. };
  114. AVFILTER_DEFINE_CLASS(lensfun);
  115. static av_cold int init(AVFilterContext *ctx)
  116. {
  117. LensfunContext *lensfun = ctx->priv;
  118. lfDatabase *db;
  119. const lfCamera **cameras;
  120. const lfLens **lenses;
  121. if (!lensfun->make) {
  122. av_log(ctx, AV_LOG_FATAL, "Option \"make\" not specified\n");
  123. return AVERROR(EINVAL);
  124. } else if (!lensfun->model) {
  125. av_log(ctx, AV_LOG_FATAL, "Option \"model\" not specified\n");
  126. return AVERROR(EINVAL);
  127. } else if (!lensfun->lens_model) {
  128. av_log(ctx, AV_LOG_FATAL, "Option \"lens_model\" not specified\n");
  129. return AVERROR(EINVAL);
  130. }
  131. lensfun->lens = lf_lens_new();
  132. lensfun->camera = lf_camera_new();
  133. db = lf_db_new();
  134. if (lf_db_load(db) != LF_NO_ERROR) {
  135. lf_db_destroy(db);
  136. av_log(ctx, AV_LOG_FATAL, "Failed to load lensfun database\n");
  137. return AVERROR_INVALIDDATA;
  138. }
  139. cameras = lf_db_find_cameras(db, lensfun->make, lensfun->model);
  140. if (cameras && *cameras) {
  141. lf_camera_copy(lensfun->camera, *cameras);
  142. av_log(ctx, AV_LOG_INFO, "Using camera %s\n", lensfun->camera->Model);
  143. } else {
  144. lf_free(cameras);
  145. lf_db_destroy(db);
  146. av_log(ctx, AV_LOG_FATAL, "Failed to find camera in lensfun database\n");
  147. return AVERROR_INVALIDDATA;
  148. }
  149. lf_free(cameras);
  150. lenses = lf_db_find_lenses_hd(db, lensfun->camera, NULL, lensfun->lens_model, 0);
  151. if (lenses && *lenses) {
  152. lf_lens_copy(lensfun->lens, *lenses);
  153. av_log(ctx, AV_LOG_INFO, "Using lens %s\n", lensfun->lens->Model);
  154. } else {
  155. lf_free(lenses);
  156. lf_db_destroy(db);
  157. av_log(ctx, AV_LOG_FATAL, "Failed to find lens in lensfun database\n");
  158. return AVERROR_INVALIDDATA;
  159. }
  160. lf_free(lenses);
  161. lf_db_destroy(db);
  162. return 0;
  163. }
  164. static int query_formats(AVFilterContext *ctx)
  165. {
  166. // Some of the functions provided by lensfun require pixels in RGB format
  167. static const enum AVPixelFormat fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
  168. AVFilterFormats *fmts_list = ff_make_format_list(fmts);
  169. return ff_set_common_formats(ctx, fmts_list);
  170. }
  171. static float lanczos_kernel(float x)
  172. {
  173. if (x == 0.0f) {
  174. return 1.0f;
  175. } else if (x > -2.0f && x < 2.0f) {
  176. return (2.0f * sin(M_PI * x) * sin(M_PI / 2.0f * x)) / (M_PI * M_PI * x * x);
  177. } else {
  178. return 0.0f;
  179. }
  180. }
  181. static int config_props(AVFilterLink *inlink)
  182. {
  183. AVFilterContext *ctx = inlink->dst;
  184. LensfunContext *lensfun = ctx->priv;
  185. int index;
  186. float a;
  187. int lensfun_mode = 0;
  188. if (!lensfun->modifier) {
  189. if (lensfun->camera && lensfun->lens) {
  190. lensfun->modifier = lf_modifier_new(lensfun->lens,
  191. lensfun->camera->CropFactor,
  192. inlink->w,
  193. inlink->h);
  194. if (lensfun->mode & VIGNETTING)
  195. lensfun_mode |= LF_MODIFY_VIGNETTING;
  196. if (lensfun->mode & GEOMETRY_DISTORTION)
  197. lensfun_mode |= LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE;
  198. if (lensfun->mode & SUBPIXEL_DISTORTION)
  199. lensfun_mode |= LF_MODIFY_TCA;
  200. lf_modifier_initialize(lensfun->modifier,
  201. lensfun->lens,
  202. LF_PF_U8,
  203. lensfun->focal_length,
  204. lensfun->aperture,
  205. lensfun->focus_distance,
  206. 0.0,
  207. lensfun->target_geometry,
  208. lensfun_mode,
  209. lensfun->reverse);
  210. } else {
  211. // lensfun->camera and lensfun->lens should have been initialized
  212. return AVERROR_BUG;
  213. }
  214. }
  215. if (!lensfun->distortion_coords) {
  216. if (lensfun->mode & SUBPIXEL_DISTORTION) {
  217. lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2 * 3);
  218. if (!lensfun->distortion_coords)
  219. return AVERROR(ENOMEM);
  220. if (lensfun->mode & GEOMETRY_DISTORTION) {
  221. // apply both geometry and subpixel distortion
  222. lf_modifier_apply_subpixel_geometry_distortion(lensfun->modifier,
  223. 0, 0,
  224. inlink->w, inlink->h,
  225. lensfun->distortion_coords);
  226. } else {
  227. // apply only subpixel distortion
  228. lf_modifier_apply_subpixel_distortion(lensfun->modifier,
  229. 0, 0,
  230. inlink->w, inlink->h,
  231. lensfun->distortion_coords);
  232. }
  233. } else if (lensfun->mode & GEOMETRY_DISTORTION) {
  234. lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2);
  235. if (!lensfun->distortion_coords)
  236. return AVERROR(ENOMEM);
  237. // apply only geometry distortion
  238. lf_modifier_apply_geometry_distortion(lensfun->modifier,
  239. 0, 0,
  240. inlink->w, inlink->h,
  241. lensfun->distortion_coords);
  242. }
  243. }
  244. if (!lensfun->interpolation)
  245. if (lensfun->interpolation_type == LANCZOS) {
  246. lensfun->interpolation = av_malloc_array(LANCZOS_RESOLUTION, sizeof(float) * 4);
  247. if (!lensfun->interpolation)
  248. return AVERROR(ENOMEM);
  249. for (index = 0; index < 4 * LANCZOS_RESOLUTION; ++index) {
  250. if (index == 0) {
  251. lensfun->interpolation[index] = 1.0f;
  252. } else {
  253. a = sqrtf((float)index / LANCZOS_RESOLUTION);
  254. lensfun->interpolation[index] = lanczos_kernel(a);
  255. }
  256. }
  257. }
  258. return 0;
  259. }
  260. static int vignetting_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  261. {
  262. const VignettingThreadData *thread_data = arg;
  263. const int slice_start = thread_data->height * jobnr / nb_jobs;
  264. const int slice_end = thread_data->height * (jobnr + 1) / nb_jobs;
  265. lf_modifier_apply_color_modification(thread_data->modifier,
  266. thread_data->data_in + slice_start * thread_data->linesize_in,
  267. 0,
  268. slice_start,
  269. thread_data->width,
  270. slice_end - slice_start,
  271. thread_data->pixel_composition,
  272. thread_data->linesize_in);
  273. return 0;
  274. }
  275. static float square(float x)
  276. {
  277. return x * x;
  278. }
  279. static int distortion_correction_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  280. {
  281. const DistortionCorrectionThreadData *thread_data = arg;
  282. const int slice_start = thread_data->height * jobnr / nb_jobs;
  283. const int slice_end = thread_data->height * (jobnr + 1) / nb_jobs;
  284. int x, y, i, j, rgb_index;
  285. float interpolated, new_x, new_y, d, norm;
  286. int new_x_int, new_y_int;
  287. for (y = slice_start; y < slice_end; ++y)
  288. for (x = 0; x < thread_data->width; ++x)
  289. for (rgb_index = 0; rgb_index < 3; ++rgb_index) {
  290. if (thread_data->mode & SUBPIXEL_DISTORTION) {
  291. // subpixel (and possibly geometry) distortion correction was applied, correct distortion
  292. switch(thread_data->interpolation_type) {
  293. case NEAREST:
  294. new_x_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2] + 0.5f;
  295. new_y_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1] + 0.5f;
  296. if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
  297. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  298. } else {
  299. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = thread_data->data_in[new_x_int * 3 + rgb_index + new_y_int * thread_data->linesize_in];
  300. }
  301. break;
  302. case LINEAR:
  303. interpolated = 0.0f;
  304. new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
  305. new_x_int = new_x;
  306. new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
  307. new_y_int = new_y;
  308. if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
  309. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  310. } else {
  311. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] =
  312. thread_data->data_in[ new_x_int * 3 + rgb_index + new_y_int * thread_data->linesize_in] * (new_x_int + 1 - new_x) * (new_y_int + 1 - new_y)
  313. + thread_data->data_in[(new_x_int + 1) * 3 + rgb_index + new_y_int * thread_data->linesize_in] * (new_x - new_x_int) * (new_y_int + 1 - new_y)
  314. + thread_data->data_in[ new_x_int * 3 + rgb_index + (new_y_int + 1) * thread_data->linesize_in] * (new_x_int + 1 - new_x) * (new_y - new_y_int)
  315. + thread_data->data_in[(new_x_int + 1) * 3 + rgb_index + (new_y_int + 1) * thread_data->linesize_in] * (new_x - new_x_int) * (new_y - new_y_int);
  316. }
  317. break;
  318. case LANCZOS:
  319. interpolated = 0.0f;
  320. norm = 0.0f;
  321. new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
  322. new_x_int = new_x;
  323. new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
  324. new_y_int = new_y;
  325. for (j = 0; j < 4; ++j)
  326. for (i = 0; i < 4; ++i) {
  327. if (new_x_int + i - 2 < 0 || new_x_int + i - 2 >= thread_data->width || new_y_int + j - 2 < 0 || new_y_int + j - 2 >= thread_data->height)
  328. continue;
  329. d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
  330. if (d >= 4.0f)
  331. continue;
  332. d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
  333. norm += d;
  334. interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
  335. }
  336. if (norm == 0.0f) {
  337. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  338. } else {
  339. interpolated /= norm;
  340. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
  341. }
  342. break;
  343. }
  344. } else if (thread_data->mode & GEOMETRY_DISTORTION) {
  345. // geometry distortion correction was applied, correct distortion
  346. switch(thread_data->interpolation_type) {
  347. case NEAREST:
  348. new_x_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2] + 0.5f;
  349. new_y_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1] + 0.5f;
  350. if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
  351. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  352. } else {
  353. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = thread_data->data_in[new_x_int * 3 + rgb_index + new_y_int * thread_data->linesize_in];
  354. }
  355. break;
  356. case LINEAR:
  357. interpolated = 0.0f;
  358. new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
  359. new_x_int = new_x;
  360. new_y = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1];
  361. new_y_int = new_y;
  362. if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
  363. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  364. } else {
  365. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] =
  366. thread_data->data_in[ new_x_int * 3 + rgb_index + new_y_int * thread_data->linesize_in] * (new_x_int + 1 - new_x) * (new_y_int + 1 - new_y)
  367. + thread_data->data_in[(new_x_int + 1) * 3 + rgb_index + new_y_int * thread_data->linesize_in] * (new_x - new_x_int) * (new_y_int + 1 - new_y)
  368. + thread_data->data_in[ new_x_int * 3 + rgb_index + (new_y_int + 1) * thread_data->linesize_in] * (new_x_int + 1 - new_x) * (new_y - new_y_int)
  369. + thread_data->data_in[(new_x_int + 1) * 3 + rgb_index + (new_y_int + 1) * thread_data->linesize_in] * (new_x - new_x_int) * (new_y - new_y_int);
  370. }
  371. break;
  372. case LANCZOS:
  373. interpolated = 0.0f;
  374. norm = 0.0f;
  375. new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
  376. new_x_int = new_x;
  377. new_y = thread_data->distortion_coords[x * 2 + 1 + y * thread_data->width * 2];
  378. new_y_int = new_y;
  379. for (j = 0; j < 4; ++j)
  380. for (i = 0; i < 4; ++i) {
  381. if (new_x_int + i - 2 < 0 || new_x_int + i - 2 >= thread_data->width || new_y_int + j - 2 < 0 || new_y_int + j - 2 >= thread_data->height)
  382. continue;
  383. d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
  384. if (d >= 4.0f)
  385. continue;
  386. d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
  387. norm += d;
  388. interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
  389. }
  390. if (norm == 0.0f) {
  391. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
  392. } else {
  393. interpolated /= norm;
  394. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
  395. }
  396. break;
  397. }
  398. } else {
  399. // no distortion correction was applied
  400. thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = thread_data->data_in[x * 3 + rgb_index + y * thread_data->linesize_in];
  401. }
  402. }
  403. return 0;
  404. }
  405. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  406. {
  407. AVFilterContext *ctx = inlink->dst;
  408. LensfunContext *lensfun = ctx->priv;
  409. AVFilterLink *outlink = ctx->outputs[0];
  410. AVFrame *out;
  411. VignettingThreadData vignetting_thread_data;
  412. DistortionCorrectionThreadData distortion_correction_thread_data;
  413. if (lensfun->mode & VIGNETTING) {
  414. av_frame_make_writable(in);
  415. vignetting_thread_data = (VignettingThreadData) {
  416. .width = inlink->w,
  417. .height = inlink->h,
  418. .data_in = in->data[0],
  419. .linesize_in = in->linesize[0],
  420. .pixel_composition = LF_CR_3(RED, GREEN, BLUE),
  421. .modifier = lensfun->modifier
  422. };
  423. ctx->internal->execute(ctx,
  424. vignetting_filter_slice,
  425. &vignetting_thread_data,
  426. NULL,
  427. FFMIN(outlink->h, ctx->graph->nb_threads));
  428. }
  429. if (lensfun->mode & (GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION)) {
  430. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  431. if (!out) {
  432. av_frame_free(&in);
  433. return AVERROR(ENOMEM);
  434. }
  435. av_frame_copy_props(out, in);
  436. distortion_correction_thread_data = (DistortionCorrectionThreadData) {
  437. .width = inlink->w,
  438. .height = inlink->h,
  439. .distortion_coords = lensfun->distortion_coords,
  440. .data_in = in->data[0],
  441. .data_out = out->data[0],
  442. .linesize_in = in->linesize[0],
  443. .linesize_out = out->linesize[0],
  444. .interpolation = lensfun->interpolation,
  445. .mode = lensfun->mode,
  446. .interpolation_type = lensfun->interpolation_type
  447. };
  448. ctx->internal->execute(ctx,
  449. distortion_correction_filter_slice,
  450. &distortion_correction_thread_data,
  451. NULL,
  452. FFMIN(outlink->h, ctx->graph->nb_threads));
  453. av_frame_free(&in);
  454. return ff_filter_frame(outlink, out);
  455. } else {
  456. return ff_filter_frame(outlink, in);
  457. }
  458. }
  459. static av_cold void uninit(AVFilterContext *ctx)
  460. {
  461. LensfunContext *lensfun = ctx->priv;
  462. if (lensfun->camera)
  463. lf_camera_destroy(lensfun->camera);
  464. if (lensfun->lens)
  465. lf_lens_destroy(lensfun->lens);
  466. if (lensfun->modifier)
  467. lf_modifier_destroy(lensfun->modifier);
  468. av_freep(&lensfun->distortion_coords);
  469. av_freep(&lensfun->interpolation);
  470. }
  471. static const AVFilterPad lensfun_inputs[] = {
  472. {
  473. .name = "default",
  474. .type = AVMEDIA_TYPE_VIDEO,
  475. .config_props = config_props,
  476. .filter_frame = filter_frame,
  477. },
  478. { NULL }
  479. };
  480. static const AVFilterPad lensfun_outputs[] = {
  481. {
  482. .name = "default",
  483. .type = AVMEDIA_TYPE_VIDEO,
  484. },
  485. { NULL }
  486. };
  487. AVFilter ff_vf_lensfun = {
  488. .name = "lensfun",
  489. .description = NULL_IF_CONFIG_SMALL("Apply correction to an image based on info derived from the lensfun database."),
  490. .priv_size = sizeof(LensfunContext),
  491. .init = init,
  492. .uninit = uninit,
  493. .query_formats = query_formats,
  494. .inputs = lensfun_inputs,
  495. .outputs = lensfun_outputs,
  496. .priv_class = &lensfun_class,
  497. .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
  498. };