FFmpeg  4.4.8
vf_lensfun.c
Go to the documentation of this file.
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 /**
22  * @file
23  * Lensfun filter, applies lens correction with parameters from the lensfun database
24  *
25  * @see https://lensfun.sourceforge.net/
26  */
27 
28 #include <float.h>
29 #include <math.h>
30 
31 #include "libavutil/avassert.h"
32 #include "libavutil/imgutils.h"
33 #include "libavutil/opt.h"
34 #include "libswscale/swscale.h"
35 #include "avfilter.h"
36 #include "filters.h"
37 #include "formats.h"
38 #include "internal.h"
39 #include "video.h"
40 
41 #include <lensfun.h>
42 
43 #define LANCZOS_RESOLUTION 256
44 
45 enum Mode {
46  VIGNETTING = 0x1,
49 };
50 
54  LANCZOS
55 };
56 
57 typedef struct VignettingThreadData {
58  int width, height;
62  lfModifier *modifier;
64 
66  int width, height;
67  const float *distortion_coords;
68  const uint8_t *data_in;
71  const float *interpolation;
72  int mode;
75 
76 typedef struct LensfunContext {
77  const AVClass *class;
78  const char *make, *model, *lens_model;
79  int mode;
80  float focal_length;
81  float aperture;
83  float scale;
85  int reverse;
87 
89  float *interpolation;
90 
91  lfLens *lens;
92  lfCamera *camera;
93  lfModifier *modifier;
95 
96 #define OFFSET(x) offsetof(LensfunContext, x)
97 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
98 static const AVOption lensfun_options[] = {
99  { "make", "set camera maker", OFFSET(make), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
100  { "model", "set camera model", OFFSET(model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
101  { "lens_model", "set lens model", OFFSET(lens_model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
102  { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=GEOMETRY_DISTORTION}, 0, VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION, FLAGS, "mode" },
103  { "vignetting", "fix lens vignetting", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING}, 0, 0, FLAGS, "mode" },
104  { "geometry", "correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
105  { "subpixel", "fix chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
106  { "vig_geo", "fix lens vignetting and correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
107  { "vig_subpixel", "fix lens vignetting and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
108  { "distortion", "correct geometry distortion and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
109  { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
110  { "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 },
111  { "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 },
112  { "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 },
113  { "scale", "scale factor applied after corrections (0.0 means automatic scaling)", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, DBL_MAX, FLAGS },
114  { "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" },
115  { "rectilinear", "rectilinear lens (default)", 0, AV_OPT_TYPE_CONST, {.i64=LF_RECTILINEAR}, 0, 0, FLAGS, "lens_geometry" },
116  { "fisheye", "fisheye lens", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE}, 0, 0, FLAGS, "lens_geometry" },
117  { "panoramic", "panoramic (cylindrical)", 0, AV_OPT_TYPE_CONST, {.i64=LF_PANORAMIC}, 0, 0, FLAGS, "lens_geometry" },
118  { "equirectangular", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=LF_EQUIRECTANGULAR}, 0, 0, FLAGS, "lens_geometry" },
119  { "fisheye_orthographic", "orthographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_ORTHOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
120  { "fisheye_stereographic", "stereographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_STEREOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
121  { "fisheye_equisolid", "equisolid fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_EQUISOLID}, 0, 0, FLAGS, "lens_geometry" },
122  { "fisheye_thoby", "fisheye as measured by thoby", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_THOBY}, 0, 0, FLAGS, "lens_geometry" },
123  { "reverse", "Does reverse correction (regular image to lens distorted)", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
124  { "interpolation", "Type of interpolation", OFFSET(interpolation_type), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, LANCZOS, FLAGS, "interpolation" },
125  { "nearest", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interpolation" },
126  { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" },
127  { "lanczos", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interpolation" },
128  { NULL }
129 };
130 
132 
134 {
135  LensfunContext *lensfun = ctx->priv;
136  lfDatabase *db;
137  const lfCamera **cameras;
138  const lfLens **lenses;
139 
140  db = lf_db_create();
141  if (lf_db_load(db) != LF_NO_ERROR) {
142  lf_db_destroy(db);
143  av_log(ctx, AV_LOG_FATAL, "Failed to load lensfun database\n");
144  return AVERROR_INVALIDDATA;
145  }
146 
147  if (!lensfun->make || !lensfun->model) {
148  const lfCamera *const *cameras = lf_db_get_cameras(db);
149 
150  av_log(ctx, AV_LOG_FATAL, "Option \"make\" or option \"model\" not specified\n");
151  av_log(ctx, AV_LOG_INFO, "Available values for \"make\" and \"model\":\n");
152  for (int i = 0; cameras && cameras[i]; i++)
153  av_log(ctx, AV_LOG_INFO, "\t%s\t%s\n", cameras[i]->Maker, cameras[i]->Model);
154  lf_db_destroy(db);
155  return AVERROR(EINVAL);
156  } else if (!lensfun->lens_model) {
157  const lfLens *const *lenses = lf_db_get_lenses(db);
158 
159  av_log(ctx, AV_LOG_FATAL, "Option \"lens_model\" not specified\n");
160  av_log(ctx, AV_LOG_INFO, "Available values for \"lens_model\":\n");
161  for (int i = 0; lenses && lenses[i]; i++)
162  av_log(ctx, AV_LOG_INFO, "\t%s\t(make %s)\n", lenses[i]->Model, lenses[i]->Maker);
163  lf_db_destroy(db);
164  return AVERROR(EINVAL);
165  }
166 
167  lensfun->lens = lf_lens_create();
168  lensfun->camera = lf_camera_create();
169 
170  cameras = lf_db_find_cameras(db, lensfun->make, lensfun->model);
171  if (cameras && *cameras) {
172  lf_camera_copy(lensfun->camera, *cameras);
173  av_log(ctx, AV_LOG_INFO, "Using camera %s\n", lensfun->camera->Model);
174  } else {
175  lf_free(cameras);
176  lf_db_destroy(db);
177  av_log(ctx, AV_LOG_FATAL, "Failed to find camera in lensfun database\n");
178  return AVERROR_INVALIDDATA;
179  }
180  lf_free(cameras);
181 
182  lenses = lf_db_find_lenses(db, lensfun->camera, NULL, lensfun->lens_model, 0);
183  if (lenses && *lenses) {
184  lf_lens_copy(lensfun->lens, *lenses);
185  av_log(ctx, AV_LOG_INFO, "Using lens %s\n", lensfun->lens->Model);
186  } else {
187  lf_free(lenses);
188  lf_db_destroy(db);
189  av_log(ctx, AV_LOG_FATAL, "Failed to find lens in lensfun database\n");
190  return AVERROR_INVALIDDATA;
191  }
192  lf_free(lenses);
193 
194  lf_db_destroy(db);
195  return 0;
196 }
197 
199 {
200  // Some of the functions provided by lensfun require pixels in RGB format
201  static const enum AVPixelFormat fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
202  AVFilterFormats *fmts_list = ff_make_format_list(fmts);
203  return ff_set_common_formats(ctx, fmts_list);
204 }
205 
206 static float lanczos_kernel(float x)
207 {
208  if (x == 0.0f) {
209  return 1.0f;
210  } else if (x > -2.0f && x < 2.0f) {
211  return (2.0f * sin(M_PI * x) * sin(M_PI / 2.0f * x)) / (M_PI * M_PI * x * x);
212  } else {
213  return 0.0f;
214  }
215 }
216 
217 static int config_props(AVFilterLink *inlink)
218 {
219  AVFilterContext *ctx = inlink->dst;
220  LensfunContext *lensfun = ctx->priv;
221  int index;
222  float a;
223 
224  if (!lensfun->modifier) {
225  if (lensfun->camera && lensfun->lens) {
226  lensfun->modifier = lf_modifier_create(lensfun->lens,
227  lensfun->focal_length,
228  lensfun->camera->CropFactor,
229  inlink->w,
230  inlink->h, LF_PF_U8, lensfun->reverse);
231  if (lensfun->mode & VIGNETTING)
232  lf_modifier_enable_vignetting_correction(lensfun->modifier, lensfun->aperture, lensfun->focus_distance);
233  if (lensfun->mode & GEOMETRY_DISTORTION) {
234  lf_modifier_enable_distortion_correction(lensfun->modifier);
235  lf_modifier_enable_projection_transform(lensfun->modifier, lensfun->target_geometry);
236  lf_modifier_enable_scaling(lensfun->modifier, lensfun->scale);
237  }
238  if (lensfun->mode & SUBPIXEL_DISTORTION)
239  lf_modifier_enable_tca_correction(lensfun->modifier);
240  } else {
241  // lensfun->camera and lensfun->lens should have been initialized
242  return AVERROR_BUG;
243  }
244  }
245 
246  if (!lensfun->distortion_coords) {
247  if (lensfun->mode & SUBPIXEL_DISTORTION) {
248  lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2 * 3);
249  if (!lensfun->distortion_coords)
250  return AVERROR(ENOMEM);
251  if (lensfun->mode & GEOMETRY_DISTORTION) {
252  // apply both geometry and subpixel distortion
253  lf_modifier_apply_subpixel_geometry_distortion(lensfun->modifier,
254  0, 0,
255  inlink->w, inlink->h,
256  lensfun->distortion_coords);
257  } else {
258  // apply only subpixel distortion
259  lf_modifier_apply_subpixel_distortion(lensfun->modifier,
260  0, 0,
261  inlink->w, inlink->h,
262  lensfun->distortion_coords);
263  }
264  } else if (lensfun->mode & GEOMETRY_DISTORTION) {
265  lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2);
266  if (!lensfun->distortion_coords)
267  return AVERROR(ENOMEM);
268  // apply only geometry distortion
269  lf_modifier_apply_geometry_distortion(lensfun->modifier,
270  0, 0,
271  inlink->w, inlink->h,
272  lensfun->distortion_coords);
273  }
274  }
275 
276  if (!lensfun->interpolation)
277  if (lensfun->interpolation_type == LANCZOS) {
278  lensfun->interpolation = av_malloc_array(LANCZOS_RESOLUTION, sizeof(float) * 4);
279  if (!lensfun->interpolation)
280  return AVERROR(ENOMEM);
281  for (index = 0; index < 4 * LANCZOS_RESOLUTION; ++index) {
282  if (index == 0) {
283  lensfun->interpolation[index] = 1.0f;
284  } else {
285  a = sqrtf((float)index / LANCZOS_RESOLUTION);
286  lensfun->interpolation[index] = lanczos_kernel(a);
287  }
288  }
289  }
290 
291  return 0;
292 }
293 
294 static int vignetting_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
295 {
297  const int slice_start = ff_slice_pos(thread_data->height, jobnr, nb_jobs);
298  const int slice_end = ff_slice_pos(thread_data->height, jobnr + 1, nb_jobs);
299 
300  lf_modifier_apply_color_modification(thread_data->modifier,
301  thread_data->data_in + slice_start * thread_data->linesize_in,
302  0,
303  slice_start,
304  thread_data->width,
305  slice_end - slice_start,
306  thread_data->pixel_composition,
307  thread_data->linesize_in);
308 
309  return 0;
310 }
311 
312 static float square(float x)
313 {
314  return x * x;
315 }
316 
317 static int distortion_correction_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
318 {
320  const int slice_start = ff_slice_pos(thread_data->height, jobnr, nb_jobs);
321  const int slice_end = ff_slice_pos(thread_data->height, jobnr + 1, nb_jobs);
322 
323  int x, y, i, j, rgb_index;
324  float interpolated, new_x, new_y, d, norm;
325  int new_x_int, new_y_int;
326  for (y = slice_start; y < slice_end; ++y)
327  for (x = 0; x < thread_data->width; ++x)
328  for (rgb_index = 0; rgb_index < 3; ++rgb_index) {
329  if (thread_data->mode & SUBPIXEL_DISTORTION) {
330  // subpixel (and possibly geometry) distortion correction was applied, correct distortion
331  switch(thread_data->interpolation_type) {
332  case NEAREST:
333  new_x_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2] + 0.5f;
334  new_y_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1] + 0.5f;
335  if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
336  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
337  } else {
338  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];
339  }
340  break;
341  case LINEAR:
342  interpolated = 0.0f;
343  new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
344  new_x_int = new_x;
345  new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
346  new_y_int = new_y;
347  if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
348  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
349  } else {
350  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] =
351  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)
352  + 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)
353  + 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)
354  + 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);
355  }
356  break;
357  case LANCZOS:
358  interpolated = 0.0f;
359  norm = 0.0f;
360  new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
361  new_x_int = new_x;
362  new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
363  new_y_int = new_y;
364  for (j = 0; j < 4; ++j)
365  for (i = 0; i < 4; ++i) {
366  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)
367  continue;
368  d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
369  if (d >= 4.0f)
370  continue;
371  d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
372  norm += d;
373  interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
374  }
375  if (norm == 0.0f) {
376  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
377  } else {
378  interpolated /= norm;
379  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
380  }
381  break;
382  }
383  } else if (thread_data->mode & GEOMETRY_DISTORTION) {
384  // geometry distortion correction was applied, correct distortion
385  switch(thread_data->interpolation_type) {
386  case NEAREST:
387  new_x_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2] + 0.5f;
388  new_y_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1] + 0.5f;
389  if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
390  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
391  } else {
392  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];
393  }
394  break;
395  case LINEAR:
396  interpolated = 0.0f;
397  new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
398  new_x_int = new_x;
399  new_y = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1];
400  new_y_int = new_y;
401  if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
402  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
403  } else {
404  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] =
405  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)
406  + 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)
407  + 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)
408  + 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);
409  }
410  break;
411  case LANCZOS:
412  interpolated = 0.0f;
413  norm = 0.0f;
414  new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
415  new_x_int = new_x;
416  new_y = thread_data->distortion_coords[x * 2 + 1 + y * thread_data->width * 2];
417  new_y_int = new_y;
418  for (j = 0; j < 4; ++j)
419  for (i = 0; i < 4; ++i) {
420  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)
421  continue;
422  d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
423  if (d >= 4.0f)
424  continue;
425  d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
426  norm += d;
427  interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
428  }
429  if (norm == 0.0f) {
430  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
431  } else {
432  interpolated /= norm;
433  thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
434  }
435  break;
436  }
437  } else {
438  // no distortion correction was applied
439  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];
440  }
441  }
442 
443  return 0;
444 }
445 
446 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
447 {
448  AVFilterContext *ctx = inlink->dst;
449  LensfunContext *lensfun = ctx->priv;
450  AVFilterLink *outlink = ctx->outputs[0];
451  AVFrame *out;
452  VignettingThreadData vignetting_thread_data;
453  DistortionCorrectionThreadData distortion_correction_thread_data;
454 
455  if (lensfun->mode & VIGNETTING) {
457 
458  vignetting_thread_data = (VignettingThreadData) {
459  .width = inlink->w,
460  .height = inlink->h,
461  .data_in = in->data[0],
462  .linesize_in = in->linesize[0],
463  .pixel_composition = LF_CR_3(RED, GREEN, BLUE),
464  .modifier = lensfun->modifier
465  };
466 
467  ctx->internal->execute(ctx,
469  &vignetting_thread_data,
470  NULL,
471  FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
472  }
473 
474  if (lensfun->mode & (GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION)) {
475  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
476  if (!out) {
477  av_frame_free(&in);
478  return AVERROR(ENOMEM);
479  }
481 
482  distortion_correction_thread_data = (DistortionCorrectionThreadData) {
483  .width = inlink->w,
484  .height = inlink->h,
485  .distortion_coords = lensfun->distortion_coords,
486  .data_in = in->data[0],
487  .data_out = out->data[0],
488  .linesize_in = in->linesize[0],
489  .linesize_out = out->linesize[0],
490  .interpolation = lensfun->interpolation,
491  .mode = lensfun->mode,
492  .interpolation_type = lensfun->interpolation_type
493  };
494 
495  ctx->internal->execute(ctx,
497  &distortion_correction_thread_data,
498  NULL,
499  FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
500 
501  av_frame_free(&in);
502  return ff_filter_frame(outlink, out);
503  } else {
504  return ff_filter_frame(outlink, in);
505  }
506 }
507 
509 {
510  LensfunContext *lensfun = ctx->priv;
511 
512  if (lensfun->camera)
513  lf_camera_destroy(lensfun->camera);
514  if (lensfun->lens)
515  lf_lens_destroy(lensfun->lens);
516  if (lensfun->modifier)
517  lf_modifier_destroy(lensfun->modifier);
518  av_freep(&lensfun->distortion_coords);
519  av_freep(&lensfun->interpolation);
520 }
521 
522 static const AVFilterPad lensfun_inputs[] = {
523  {
524  .name = "default",
525  .type = AVMEDIA_TYPE_VIDEO,
526  .config_props = config_props,
527  .filter_frame = filter_frame,
528  },
529  { NULL }
530 };
531 
532 static const AVFilterPad lensfun_outputs[] = {
533  {
534  .name = "default",
535  .type = AVMEDIA_TYPE_VIDEO,
536  },
537  { NULL }
538 };
539 
541  .name = "lensfun",
542  .description = NULL_IF_CONFIG_SMALL("Apply correction to an image based on info derived from the lensfun database."),
543  .priv_size = sizeof(LensfunContext),
544  .init = init,
545  .uninit = uninit,
549  .priv_class = &lensfun_class,
551 };
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
Mode
Frame type (Table 1a in 3GPP TS 26.101)
Definition: amrnbdata.h:39
#define av_cold
Definition: attributes.h:88
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31)))) #define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac) { } void ff_audio_convert_free(AudioConvert **ac) { if(! *ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);} AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map) { AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method !=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2) { ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc) { av_free(ac);return NULL;} return ac;} in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar) { ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar ? ac->channels :1;} else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;} int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic=1;int len=in->nb_samples;int p;if(ac->dc) { av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
uint8_t
simple assert() macros that are a bit more flexible than ISO C assert().
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
Main libavfilter public API header.
#define flags(name, subs,...)
Definition: cbs_av1.c:572
#define FFMIN(a, b)
Definition: common.h:105
#define NULL
Definition: coverity.c:32
mode
Use these values in ebur128_init (or'ed).
Definition: ebur128.h:83
int
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition: filters.h:271
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:587
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:286
@ AV_OPT_TYPE_CONST
Definition: opt.h:234
@ AV_OPT_TYPE_INT
Definition: opt.h:225
@ AV_OPT_TYPE_FLOAT
Definition: opt.h:228
@ AV_OPT_TYPE_BOOL
Definition: opt.h:242
@ AV_OPT_TYPE_STRING
Definition: opt.h:229
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
Definition: avfilter.h:126
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
#define AVERROR(e)
Definition: error.h:43
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:658
int av_frame_make_writable(AVFrame *frame)
Ensure that the frame data is writable, avoiding data copy if possible.
Definition: frame.c:611
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:188
#define AV_LOG_INFO
Standard information.
Definition: log.h:205
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
int index
Definition: gxfenc.c:89
misc image utilities
int i
Definition: input.c:407
const char * arg
Definition: jacosubdec.c:66
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
#define M_PI
Definition: mathematics.h:52
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2033
AVOptions.
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:65
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:68
@ BLUE
Definition: rpzaenc.c:55
@ GREEN
Definition: rpzaenc.c:54
@ RED
Definition: rpzaenc.c:53
static uint32_t reverse(uint32_t num, int bits)
Definition: speedhqenc.c:51
Describe the class of an AVClass context structure.
Definition: log.h:67
An instance of a filter.
Definition: avfilter.h:341
A list of supported formats for one end of a filter link.
Definition: formats.h:65
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
AVFormatInternal * internal
An opaque field for libavformat internal usage.
Definition: avformat.h:1699
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
AVOption.
Definition: opt.h:248
const float * distortion_coords
Definition: vf_lensfun.c:67
float * distortion_coords
Definition: vf_lensfun.c:88
int target_geometry
Definition: vf_lensfun.c:84
lfLens * lens
Definition: vf_lensfun.c:91
const char * make
Definition: vf_lensfun.c:78
lfCamera * camera
Definition: vf_lensfun.c:92
int interpolation_type
Definition: vf_lensfun.c:86
lfModifier * modifier
Definition: vf_lensfun.c:93
const char * lens_model
Definition: vf_lensfun.c:78
float focus_distance
Definition: vf_lensfun.c:82
float focal_length
Definition: vf_lensfun.c:80
float * interpolation
Definition: vf_lensfun.c:89
float aperture
Definition: vf_lensfun.c:81
const char * model
Definition: vf_lensfun.c:78
Definition: mss12.h:40
lfModifier * modifier
Definition: vf_lensfun.c:62
external API header
#define av_malloc_array(a, b)
#define av_freep(p)
#define av_log(a,...)
FILE * out
Definition: movenc.c:54
AVFormatContext * ctx
Definition: movenc.c:48
static int vignetting_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_lensfun.c:294
AVFilter ff_vf_lensfun
Definition: vf_lensfun.c:540
static float lanczos_kernel(float x)
Definition: vf_lensfun.c:206
static int distortion_correction_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_lensfun.c:317
static const AVOption lensfun_options[]
Definition: vf_lensfun.c:98
@ VIGNETTING
Definition: vf_lensfun.c:46
@ GEOMETRY_DISTORTION
Definition: vf_lensfun.c:47
@ SUBPIXEL_DISTORTION
Definition: vf_lensfun.c:48
static int query_formats(AVFilterContext *ctx)
Definition: vf_lensfun.c:198
static const AVFilterPad lensfun_outputs[]
Definition: vf_lensfun.c:532
#define FLAGS
Definition: vf_lensfun.c:97
InterpolationType
Definition: vf_lensfun.c:51
@ NEAREST
Definition: vf_lensfun.c:52
@ LANCZOS
Definition: vf_lensfun.c:54
@ LINEAR
Definition: vf_lensfun.c:53
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_lensfun.c:446
AVFILTER_DEFINE_CLASS(lensfun)
static const AVFilterPad lensfun_inputs[]
Definition: vf_lensfun.c:522
static int config_props(AVFilterLink *inlink)
Definition: vf_lensfun.c:217
static float square(float x)
Definition: vf_lensfun.c:312
static av_cold int init(AVFilterContext *ctx)
Definition: vf_lensfun.c:133
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_lensfun.c:508
#define OFFSET(x)
Definition: vf_lensfun.c:96
#define LANCZOS_RESOLUTION
Definition: vf_lensfun.c:43
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:104