123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- /*
- * Copyright (C) 2007 by Andrew Zabolotny (author of lensfun, from which this filter derives from)
- * Copyright (C) 2018 Stephen Seo
- *
- * This file is part of FFmpeg.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- /**
- * @file
- * Lensfun filter, applies lens correction with parameters from the lensfun database
- *
- * @see https://lensfun.sourceforge.net/
- */
- #include <float.h>
- #include <math.h>
- #include "libavutil/avassert.h"
- #include "libavutil/imgutils.h"
- #include "libavutil/opt.h"
- #include "libswscale/swscale.h"
- #include "avfilter.h"
- #include "formats.h"
- #include "internal.h"
- #include "video.h"
- #include <lensfun.h>
- #define LANCZOS_RESOLUTION 256
- enum Mode {
- VIGNETTING = 0x1,
- GEOMETRY_DISTORTION = 0x2,
- SUBPIXEL_DISTORTION = 0x4
- };
- enum InterpolationType {
- NEAREST,
- LINEAR,
- LANCZOS
- };
- typedef struct VignettingThreadData {
- int width, height;
- uint8_t *data_in;
- int linesize_in;
- int pixel_composition;
- lfModifier *modifier;
- } VignettingThreadData;
- typedef struct DistortionCorrectionThreadData {
- int width, height;
- const float *distortion_coords;
- const uint8_t *data_in;
- uint8_t *data_out;
- int linesize_in, linesize_out;
- const float *interpolation;
- int mode;
- int interpolation_type;
- } DistortionCorrectionThreadData;
- typedef struct LensfunContext {
- const AVClass *class;
- const char *make, *model, *lens_model;
- int mode;
- float focal_length;
- float aperture;
- float focus_distance;
- float scale;
- int target_geometry;
- int reverse;
- int interpolation_type;
- float *distortion_coords;
- float *interpolation;
- lfLens *lens;
- lfCamera *camera;
- lfModifier *modifier;
- } LensfunContext;
- #define OFFSET(x) offsetof(LensfunContext, x)
- #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
- static const AVOption lensfun_options[] = {
- { "make", "set camera maker", OFFSET(make), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
- { "model", "set camera model", OFFSET(model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
- { "lens_model", "set lens model", OFFSET(lens_model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
- { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=GEOMETRY_DISTORTION}, 0, VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION, FLAGS, "mode" },
- { "vignetting", "fix lens vignetting", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING}, 0, 0, FLAGS, "mode" },
- { "geometry", "correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "subpixel", "fix chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "vig_geo", "fix lens vignetting and correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "vig_subpixel", "fix lens vignetting and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "distortion", "correct geometry distortion and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" },
- { "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 },
- { "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 },
- { "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 },
- { "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 },
- { "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" },
- { "rectilinear", "rectilinear lens (default)", 0, AV_OPT_TYPE_CONST, {.i64=LF_RECTILINEAR}, 0, 0, FLAGS, "lens_geometry" },
- { "fisheye", "fisheye lens", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE}, 0, 0, FLAGS, "lens_geometry" },
- { "panoramic", "panoramic (cylindrical)", 0, AV_OPT_TYPE_CONST, {.i64=LF_PANORAMIC}, 0, 0, FLAGS, "lens_geometry" },
- { "equirectangular", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=LF_EQUIRECTANGULAR}, 0, 0, FLAGS, "lens_geometry" },
- { "fisheye_orthographic", "orthographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_ORTHOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
- { "fisheye_stereographic", "stereographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_STEREOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" },
- { "fisheye_equisolid", "equisolid fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_EQUISOLID}, 0, 0, FLAGS, "lens_geometry" },
- { "fisheye_thoby", "fisheye as measured by thoby", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_THOBY}, 0, 0, FLAGS, "lens_geometry" },
- { "reverse", "Does reverse correction (regular image to lens distorted)", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
- { "interpolation", "Type of interpolation", OFFSET(interpolation_type), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, LANCZOS, FLAGS, "interpolation" },
- { "nearest", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interpolation" },
- { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" },
- { "lanczos", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interpolation" },
- { NULL }
- };
- AVFILTER_DEFINE_CLASS(lensfun);
- static av_cold int init(AVFilterContext *ctx)
- {
- LensfunContext *lensfun = ctx->priv;
- lfDatabase *db;
- const lfCamera **cameras;
- const lfLens **lenses;
- if (!lensfun->make) {
- av_log(ctx, AV_LOG_FATAL, "Option \"make\" not specified\n");
- return AVERROR(EINVAL);
- } else if (!lensfun->model) {
- av_log(ctx, AV_LOG_FATAL, "Option \"model\" not specified\n");
- return AVERROR(EINVAL);
- } else if (!lensfun->lens_model) {
- av_log(ctx, AV_LOG_FATAL, "Option \"lens_model\" not specified\n");
- return AVERROR(EINVAL);
- }
- lensfun->lens = lf_lens_new();
- lensfun->camera = lf_camera_new();
- db = lf_db_new();
- if (lf_db_load(db) != LF_NO_ERROR) {
- lf_db_destroy(db);
- av_log(ctx, AV_LOG_FATAL, "Failed to load lensfun database\n");
- return AVERROR_INVALIDDATA;
- }
- cameras = lf_db_find_cameras(db, lensfun->make, lensfun->model);
- if (cameras && *cameras) {
- lf_camera_copy(lensfun->camera, *cameras);
- av_log(ctx, AV_LOG_INFO, "Using camera %s\n", lensfun->camera->Model);
- } else {
- lf_free(cameras);
- lf_db_destroy(db);
- av_log(ctx, AV_LOG_FATAL, "Failed to find camera in lensfun database\n");
- return AVERROR_INVALIDDATA;
- }
- lf_free(cameras);
- lenses = lf_db_find_lenses_hd(db, lensfun->camera, NULL, lensfun->lens_model, 0);
- if (lenses && *lenses) {
- lf_lens_copy(lensfun->lens, *lenses);
- av_log(ctx, AV_LOG_INFO, "Using lens %s\n", lensfun->lens->Model);
- } else {
- lf_free(lenses);
- lf_db_destroy(db);
- av_log(ctx, AV_LOG_FATAL, "Failed to find lens in lensfun database\n");
- return AVERROR_INVALIDDATA;
- }
- lf_free(lenses);
- lf_db_destroy(db);
- return 0;
- }
- static int query_formats(AVFilterContext *ctx)
- {
- // Some of the functions provided by lensfun require pixels in RGB format
- static const enum AVPixelFormat fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
- AVFilterFormats *fmts_list = ff_make_format_list(fmts);
- return ff_set_common_formats(ctx, fmts_list);
- }
- static float lanczos_kernel(float x)
- {
- if (x == 0.0f) {
- return 1.0f;
- } else if (x > -2.0f && x < 2.0f) {
- return (2.0f * sin(M_PI * x) * sin(M_PI / 2.0f * x)) / (M_PI * M_PI * x * x);
- } else {
- return 0.0f;
- }
- }
- static int config_props(AVFilterLink *inlink)
- {
- AVFilterContext *ctx = inlink->dst;
- LensfunContext *lensfun = ctx->priv;
- int index;
- float a;
- int lensfun_mode = 0;
- if (!lensfun->modifier) {
- if (lensfun->camera && lensfun->lens) {
- lensfun->modifier = lf_modifier_new(lensfun->lens,
- lensfun->camera->CropFactor,
- inlink->w,
- inlink->h);
- if (lensfun->mode & VIGNETTING)
- lensfun_mode |= LF_MODIFY_VIGNETTING;
- if (lensfun->mode & GEOMETRY_DISTORTION)
- lensfun_mode |= LF_MODIFY_DISTORTION | LF_MODIFY_GEOMETRY | LF_MODIFY_SCALE;
- if (lensfun->mode & SUBPIXEL_DISTORTION)
- lensfun_mode |= LF_MODIFY_TCA;
- lf_modifier_initialize(lensfun->modifier,
- lensfun->lens,
- LF_PF_U8,
- lensfun->focal_length,
- lensfun->aperture,
- lensfun->focus_distance,
- lensfun->scale,
- lensfun->target_geometry,
- lensfun_mode,
- lensfun->reverse);
- } else {
- // lensfun->camera and lensfun->lens should have been initialized
- return AVERROR_BUG;
- }
- }
- if (!lensfun->distortion_coords) {
- if (lensfun->mode & SUBPIXEL_DISTORTION) {
- lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2 * 3);
- if (!lensfun->distortion_coords)
- return AVERROR(ENOMEM);
- if (lensfun->mode & GEOMETRY_DISTORTION) {
- // apply both geometry and subpixel distortion
- lf_modifier_apply_subpixel_geometry_distortion(lensfun->modifier,
- 0, 0,
- inlink->w, inlink->h,
- lensfun->distortion_coords);
- } else {
- // apply only subpixel distortion
- lf_modifier_apply_subpixel_distortion(lensfun->modifier,
- 0, 0,
- inlink->w, inlink->h,
- lensfun->distortion_coords);
- }
- } else if (lensfun->mode & GEOMETRY_DISTORTION) {
- lensfun->distortion_coords = av_malloc_array(inlink->w * inlink->h, sizeof(float) * 2);
- if (!lensfun->distortion_coords)
- return AVERROR(ENOMEM);
- // apply only geometry distortion
- lf_modifier_apply_geometry_distortion(lensfun->modifier,
- 0, 0,
- inlink->w, inlink->h,
- lensfun->distortion_coords);
- }
- }
- if (!lensfun->interpolation)
- if (lensfun->interpolation_type == LANCZOS) {
- lensfun->interpolation = av_malloc_array(LANCZOS_RESOLUTION, sizeof(float) * 4);
- if (!lensfun->interpolation)
- return AVERROR(ENOMEM);
- for (index = 0; index < 4 * LANCZOS_RESOLUTION; ++index) {
- if (index == 0) {
- lensfun->interpolation[index] = 1.0f;
- } else {
- a = sqrtf((float)index / LANCZOS_RESOLUTION);
- lensfun->interpolation[index] = lanczos_kernel(a);
- }
- }
- }
- return 0;
- }
- static int vignetting_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
- {
- const VignettingThreadData *thread_data = arg;
- const int slice_start = thread_data->height * jobnr / nb_jobs;
- const int slice_end = thread_data->height * (jobnr + 1) / nb_jobs;
- lf_modifier_apply_color_modification(thread_data->modifier,
- thread_data->data_in + slice_start * thread_data->linesize_in,
- 0,
- slice_start,
- thread_data->width,
- slice_end - slice_start,
- thread_data->pixel_composition,
- thread_data->linesize_in);
- return 0;
- }
- static float square(float x)
- {
- return x * x;
- }
- static int distortion_correction_filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
- {
- const DistortionCorrectionThreadData *thread_data = arg;
- const int slice_start = thread_data->height * jobnr / nb_jobs;
- const int slice_end = thread_data->height * (jobnr + 1) / nb_jobs;
- int x, y, i, j, rgb_index;
- float interpolated, new_x, new_y, d, norm;
- int new_x_int, new_y_int;
- for (y = slice_start; y < slice_end; ++y)
- for (x = 0; x < thread_data->width; ++x)
- for (rgb_index = 0; rgb_index < 3; ++rgb_index) {
- if (thread_data->mode & SUBPIXEL_DISTORTION) {
- // subpixel (and possibly geometry) distortion correction was applied, correct distortion
- switch(thread_data->interpolation_type) {
- case NEAREST:
- new_x_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2] + 0.5f;
- new_y_int = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1] + 0.5f;
- if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- 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];
- }
- break;
- case LINEAR:
- interpolated = 0.0f;
- new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
- new_x_int = new_x;
- new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
- new_y_int = new_y;
- if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- 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] * (new_x_int + 1 - new_x) * (new_y_int + 1 - new_y)
- + 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)
- + 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)
- + 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);
- }
- break;
- case LANCZOS:
- interpolated = 0.0f;
- norm = 0.0f;
- new_x = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2];
- new_x_int = new_x;
- new_y = thread_data->distortion_coords[x * 2 * 3 + y * thread_data->width * 2 * 3 + rgb_index * 2 + 1];
- new_y_int = new_y;
- for (j = 0; j < 4; ++j)
- for (i = 0; i < 4; ++i) {
- 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)
- continue;
- d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
- if (d >= 4.0f)
- continue;
- d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
- norm += d;
- interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
- }
- if (norm == 0.0f) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- interpolated /= norm;
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
- }
- break;
- }
- } else if (thread_data->mode & GEOMETRY_DISTORTION) {
- // geometry distortion correction was applied, correct distortion
- switch(thread_data->interpolation_type) {
- case NEAREST:
- new_x_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2] + 0.5f;
- new_y_int = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1] + 0.5f;
- if (new_x_int < 0 || new_x_int >= thread_data->width || new_y_int < 0 || new_y_int >= thread_data->height) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- 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];
- }
- break;
- case LINEAR:
- interpolated = 0.0f;
- new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
- new_x_int = new_x;
- new_y = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2 + 1];
- new_y_int = new_y;
- if (new_x_int < 0 || new_x_int + 1 >= thread_data->width || new_y_int < 0 || new_y_int + 1 >= thread_data->height) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- 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] * (new_x_int + 1 - new_x) * (new_y_int + 1 - new_y)
- + 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)
- + 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)
- + 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);
- }
- break;
- case LANCZOS:
- interpolated = 0.0f;
- norm = 0.0f;
- new_x = thread_data->distortion_coords[x * 2 + y * thread_data->width * 2];
- new_x_int = new_x;
- new_y = thread_data->distortion_coords[x * 2 + 1 + y * thread_data->width * 2];
- new_y_int = new_y;
- for (j = 0; j < 4; ++j)
- for (i = 0; i < 4; ++i) {
- 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)
- continue;
- d = square(new_x - (new_x_int + i - 2)) * square(new_y - (new_y_int + j - 2));
- if (d >= 4.0f)
- continue;
- d = thread_data->interpolation[(int)(d * LANCZOS_RESOLUTION)];
- norm += d;
- interpolated += thread_data->data_in[(new_x_int + i - 2) * 3 + rgb_index + (new_y_int + j - 2) * thread_data->linesize_in] * d;
- }
- if (norm == 0.0f) {
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = 0;
- } else {
- interpolated /= norm;
- thread_data->data_out[x * 3 + rgb_index + y * thread_data->linesize_out] = interpolated < 0.0f ? 0.0f : interpolated > 255.0f ? 255.0f : interpolated;
- }
- break;
- }
- } else {
- // no distortion correction was applied
- 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];
- }
- }
- return 0;
- }
- static int filter_frame(AVFilterLink *inlink, AVFrame *in)
- {
- AVFilterContext *ctx = inlink->dst;
- LensfunContext *lensfun = ctx->priv;
- AVFilterLink *outlink = ctx->outputs[0];
- AVFrame *out;
- VignettingThreadData vignetting_thread_data;
- DistortionCorrectionThreadData distortion_correction_thread_data;
- if (lensfun->mode & VIGNETTING) {
- av_frame_make_writable(in);
- vignetting_thread_data = (VignettingThreadData) {
- .width = inlink->w,
- .height = inlink->h,
- .data_in = in->data[0],
- .linesize_in = in->linesize[0],
- .pixel_composition = LF_CR_3(RED, GREEN, BLUE),
- .modifier = lensfun->modifier
- };
- ctx->internal->execute(ctx,
- vignetting_filter_slice,
- &vignetting_thread_data,
- NULL,
- FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
- }
- if (lensfun->mode & (GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION)) {
- out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
- if (!out) {
- av_frame_free(&in);
- return AVERROR(ENOMEM);
- }
- av_frame_copy_props(out, in);
- distortion_correction_thread_data = (DistortionCorrectionThreadData) {
- .width = inlink->w,
- .height = inlink->h,
- .distortion_coords = lensfun->distortion_coords,
- .data_in = in->data[0],
- .data_out = out->data[0],
- .linesize_in = in->linesize[0],
- .linesize_out = out->linesize[0],
- .interpolation = lensfun->interpolation,
- .mode = lensfun->mode,
- .interpolation_type = lensfun->interpolation_type
- };
- ctx->internal->execute(ctx,
- distortion_correction_filter_slice,
- &distortion_correction_thread_data,
- NULL,
- FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
- av_frame_free(&in);
- return ff_filter_frame(outlink, out);
- } else {
- return ff_filter_frame(outlink, in);
- }
- }
- static av_cold void uninit(AVFilterContext *ctx)
- {
- LensfunContext *lensfun = ctx->priv;
- if (lensfun->camera)
- lf_camera_destroy(lensfun->camera);
- if (lensfun->lens)
- lf_lens_destroy(lensfun->lens);
- if (lensfun->modifier)
- lf_modifier_destroy(lensfun->modifier);
- av_freep(&lensfun->distortion_coords);
- av_freep(&lensfun->interpolation);
- }
- static const AVFilterPad lensfun_inputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .config_props = config_props,
- .filter_frame = filter_frame,
- },
- { NULL }
- };
- static const AVFilterPad lensfun_outputs[] = {
- {
- .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- },
- { NULL }
- };
- AVFilter ff_vf_lensfun = {
- .name = "lensfun",
- .description = NULL_IF_CONFIG_SMALL("Apply correction to an image based on info derived from the lensfun database."),
- .priv_size = sizeof(LensfunContext),
- .init = init,
- .uninit = uninit,
- .query_formats = query_formats,
- .inputs = lensfun_inputs,
- .outputs = lensfun_outputs,
- .priv_class = &lensfun_class,
- .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
- };
|