[FFmpeg-devel] rectification filter

Daniel Oberhoff danieloberhoff at gmail.com
Thu Aug 7 00:15:17 CEST 2014


Am 06.08.2014 um 12:12 schrieb Clément Bœsch <u at pkh.me>:

> On Sun, Aug 03, 2014 at 06:43:18PM +0200, Daniel Oberhoff wrote:
> [...]
>>>> +static av_cold int init(AVFilterContext *ctx)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> 
>>> 
>>> please remove this one, there is no point in having code that does nothing.
>>> 
>>>> +
>>>> +static av_cold void uninit(AVFilterContext *ctx)
>>>> +{
>>>> +}
>>>> +
>>>> 
>>> 
>>> ditto
>>> 
>> 
>> Done thanks!
>> 
> 
> Did you forget to attach the latest version of the patch or I missed it
> somewhere in the thread?

Sorry, think that day I wanted to add something else before resubmit, and then lost the time.

Here also with changes suggested by timothy. Note that the parametrization is now traditional
(i.e. k1 and k2 are zero-based, as for example in the opencv documentation about calibration).

From b798b95eb2f9ec4043b2407970d351cc31dd9e6a Mon Sep 17 00:00:00 2001
From: Daniel Oberhoff <daniel at danieloberhoff.de>
Date: Mon, 28 Jul 2014 23:58:12 +0200
Subject: [PATCH] avfilter: ported lenscorrection filter from frei0r

---
 Changelog                                    |   2 +-
 MAINTAINERS                                  |   1 +
 doc/filters.texi                             |  46 +++++++
 libavfilter/Makefile                         |   1 +
 libavfilter/allfilters.c                     |   1 +
 libavfilter/version.h                        |   4 +-
 libavfilter/vf_lenscorrection.c              | 199 +++++++++++++++++++++++++++
 tests/fate/filter-video.mak                  |   3 +
 tests/ref/fate/filter-pixfmts-lenscorrection |   8 ++
 9 files changed, 262 insertions(+), 3 deletions(-)
 create mode 100644 libavfilter/vf_lenscorrection.c
 create mode 100644 tests/ref/fate/filter-pixfmts-lenscorrection

diff --git a/Changelog b/Changelog
index 25dd210..79393ce 100644
--- a/Changelog
+++ b/Changelog
@@ -3,7 +3,7 @@ releases are sorted from youngest to oldest.
 
 version <next>:
 - Icecast protocol
-
+- ported lenscorrection filter from frei0r filter
 
 version 2.3:
 - AC3 fixed-point decoding
diff --git a/MAINTAINERS b/MAINTAINERS
index c383d3f..6948e22 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -354,6 +354,7 @@ Filters:
   vf_histogram.c                        Paul B Mahol
   vf_hqx.c                              Clément Bœsch
   vf_il.c                               Paul B Mahol
+  vf_lenscorrection.c                   Daniel Oberhoff
   vf_mergeplanes.c                      Paul B Mahol
   vf_psnr.c                             Paul B Mahol
   vf_scale.c                            Michael Niedermayer
diff --git a/doc/filters.texi b/doc/filters.texi
index 86feebc..c4888d3 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -5532,6 +5532,51 @@ kerndeint=map=1
 @end example
 @end itemize
 
+ at section lenscorrection
+
+Correct radial lens distortion
+
+This filter can be used to correct for radial distortion as can result from the use
+of wide angle lenses, and thereby re-rectify the image. To find the right parameters
+one can use tools available for example as part of opencv or simply trial-and-error.
+To use opencv use the calibration sample (under samples/cpp) from the opencv sources
+and extract the k1 and k2 coefficients from the resulting matrix.
+
+Note that effectively the same filter is available in the open-source tools Krita and
+Digikam from the KDE project.
+
+In contrast to the @ref{vignette} filter, which can also be used to compensate lens errors,
+this filter corrects the distortion of the image, whereas @ref{vignette} corrects the
+brightness distribution, so you may want to use both filters together in certain
+cases, though you will have to take care of ordering, i.e. whether vignetting should
+be applied before or after lens correction.
+
+ at subsection Options
+
+The filter accepts the following options:
+
+ at table @option
+ at item cx
+Relative x-coordinate of the focal point of the image, and thereby the center of the
+distrortion. This value has a range [0,1] and is expressed as fractions of the image
+width.
+ at item cy
+Relative y-coordinate of the focal point of the image, and thereby the center of the
+distrortion. This value has a range [0,1] and is expressed as fractions of the image
+height.
+ at item k1
+Coefficient of the quadratic correction term. 0.5 means no correction.
+ at item k2
+Coefficient of the double quadratic correction term. 0.5 means no correction.
+ at end table
+
+The formula that generates the correction is:
+
+ at var{r_src} = @var{r_tgt} * (1 + @var{k1} * (@var{r_tgt} / @var{r_0})^2 + @var{k2} * (@var{r_tgt} / @var{r_0})^4)
+
+where @var{r_0} is halve of the image diagonal and @var{r_src} and @var{r_tgt} are the
+distances from the focal point in the source and target images, respectively.
+
 @anchor{lut3d}
 @section lut3d
 
@@ -8744,6 +8789,7 @@ For example, to vertically flip a video with @command{ffmpeg}:
 ffmpeg -i in.avi -vf "vflip" out.avi
 @end example
 
+ at anchor{vignette}
 @section vignette
 
 Make or reverse a natural vignetting effect.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 0f54381..e9c8456 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -138,6 +138,7 @@ OBJS-$(CONFIG_IL_FILTER)                     += vf_il.o
 OBJS-$(CONFIG_INTERLACE_FILTER)              += vf_interlace.o
 OBJS-$(CONFIG_INTERLEAVE_FILTER)             += f_interleave.o
 OBJS-$(CONFIG_KERNDEINT_FILTER)              += vf_kerndeint.o
+OBJS-$(CONFIG_LENSCORRECTION_FILTER)         += vf_lenscorrection.o
 OBJS-$(CONFIG_LUT3D_FILTER)                  += vf_lut3d.o
 OBJS-$(CONFIG_LUT_FILTER)                    += vf_lut.o
 OBJS-$(CONFIG_LUTRGB_FILTER)                 += vf_lut.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 1877557..b1d6ff5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -156,6 +156,7 @@ void avfilter_register_all(void)
     REGISTER_FILTER(INTERLACE,      interlace,      vf);
     REGISTER_FILTER(INTERLEAVE,     interleave,     vf);
     REGISTER_FILTER(KERNDEINT,      kerndeint,      vf);
+    REGISTER_FILTER(LENSCORRECTION, lenscorrection, vf);
     REGISTER_FILTER(LUT3D,          lut3d,          vf);
     REGISTER_FILTER(LUT,            lut,            vf);
     REGISTER_FILTER(LUTRGB,         lutrgb,         vf);
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 1a43dc5..47bac78 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -30,8 +30,8 @@
 #include "libavutil/version.h"
 
 #define LIBAVFILTER_VERSION_MAJOR   4
-#define LIBAVFILTER_VERSION_MINOR  11
-#define LIBAVFILTER_VERSION_MICRO 102
+#define LIBAVFILTER_VERSION_MINOR  12
+#define LIBAVFILTER_VERSION_MICRO 100
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
                                                LIBAVFILTER_VERSION_MINOR, \
diff --git a/libavfilter/vf_lenscorrection.c b/libavfilter/vf_lenscorrection.c
new file mode 100644
index 0000000..c2346db
--- /dev/null
+++ b/libavfilter/vf_lenscorrection.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2007 Richard Spindler (author of frei0r plugin from which this was derived)
+ * Copyright (C) 2014 Daniel Oberhoff
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Lenscorrection filter, algorithm from the frei0r plugin with the same name
+*/
+#include <stdlib.h>
+#include <math.h>
+
+#include "libavutil/opt.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/pixdesc.h"
+
+#include "avfilter.h"
+#include "internal.h"
+#include "video.h"
+
+typedef struct LenscorrectionCtx {
+    const AVClass *av_class;
+    unsigned int width;
+    unsigned int height;
+    int hsub, vsub;
+    int nb_planes;
+    double cx, cy, k1, k2;
+} LenscorrectionCtx;
+
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+static const AVOption lenscorrection_options[] = {
+    { "cx",     "set relative center x", offsetof(LenscorrectionCtx, cx), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 1, .flags=FLAGS },
+    { "cy",     "set relative center y", offsetof(LenscorrectionCtx, cy), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 1, .flags=FLAGS },
+    { "k1",     "set quadratic distortion factor", offsetof(LenscorrectionCtx, k1), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, -1, 1, .flags=FLAGS },
+    { "k2",     "set double quadratic distortion factor", offsetof(LenscorrectionCtx, k2), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, -1, 1, .flags=FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(lenscorrection);
+
+typedef struct ThreadData {
+    AVFrame *in, *out;
+    float w, h;
+    int plane;
+    float xcenter, ycenter;
+    float k1, k2;
+} ThreadData;
+
+static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs)
+{
+    ThreadData *td = (ThreadData*)arg;
+    AVFrame *in = td->in;
+    AVFrame *out = td->out;
+
+    const float w = td->w, h = td->h;
+    const float xcenter = td->xcenter;
+    const float ycenter = td->ycenter;
+    const float r2inv = 4.0 / (w * w + h * h);
+    const float k1 = td->k1;
+    const float k2 = td->k2;
+    const int start = (h *  job   ) / nb_jobs;
+    const int end   = (h * (job+1)) / nb_jobs;
+    const int plane = td->plane;
+    const int inlinesize = in->linesize[plane];
+    const int outlinesize = out->linesize[plane];
+    const uint8_t *indata = in->data[plane];
+    uint8_t *outrow = out->data[plane] + start * outlinesize;
+    int i;
+    for (i = start; i < end; i++, outrow += outlinesize) {
+        const float off_y = i - ycenter;
+        const float off_y2 = off_y * off_y;
+        uint8_t *out = outrow;
+        int j;
+        for (j = 0; j < w; j++) {
+            const float off_x = j - xcenter;
+            const float r2 = (off_x * off_x + off_y2) * r2inv;
+            const float radius_mult = 1.0f + r2 * k1 + r2 * r2 * k2;
+            const int x = xcenter + radius_mult * off_x + 0.5f;
+            const int y = ycenter + radius_mult * off_y + 0.5f;
+            const char isvalid = x > 0 && x < w - 1 && y > 0 && y < h - 1;
+            *out++ =  isvalid ? indata[y * inlinesize + x] : 0;
+        }
+    }
+    return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static enum AVPixelFormat pix_fmts[] = {
+        AV_PIX_FMT_YUV410P,
+        AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUVJ444P,
+        AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUVJ420P,
+        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA420P,
+        AV_PIX_FMT_YUV422P,
+        AV_PIX_FMT_NONE
+    };
+
+    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
+    return 0;
+}
+
+static int config_props(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    LenscorrectionCtx *rect = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format);
+    rect->hsub = pixdesc->log2_chroma_w;
+    rect->vsub = pixdesc->log2_chroma_h;
+    outlink->w = rect->width = inlink->w;
+    outlink->h = rect->height = inlink->h;
+    rect->nb_planes = av_pix_fmt_count_planes(inlink->format);
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    LenscorrectionCtx *rect = (LenscorrectionCtx*)ctx->priv;
+    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    int plane;
+
+    if (!out) {
+        av_frame_free(&in);
+        return AVERROR(ENOMEM);
+    }
+
+    av_frame_copy_props(out, in);
+
+    for (plane = 0; plane < rect->nb_planes; ++plane) {
+        int hsub = plane == 1 || plane == 2 ? rect->hsub : 0;
+        int vsub = plane == 1 || plane == 2 ? rect->vsub : 0;
+        float hdiv = 1 << hsub;
+        float vdiv = 1 << vsub;
+        float w = rect->width / hdiv;
+        float h = rect->height / vdiv;
+        ThreadData td = {
+            .in = in,
+            .out  = out,
+            .w  = w,
+            .h  = h,
+            .xcenter = rect->cx * w,
+            .ycenter = rect->cy * h,
+            .k1 = rect->k1,
+            .k2 = rect->k2,
+            .plane = plane};
+        ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(h, ctx->graph->nb_threads));
+    }
+
+    av_frame_free(&in);
+    return ff_filter_frame(outlink, out);
+}
+
+static const AVFilterPad lenscorrection_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad lenscorrection_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_props,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_lenscorrection = {
+    .name          = "lenscorrection",
+    .description   = NULL_IF_CONFIG_SMALL("Rectify the image by correcting for lens distortion."),
+    .priv_size     = sizeof(LenscorrectionCtx),
+    .query_formats = query_formats,
+    .inputs        = lenscorrection_inputs,
+    .outputs       = lenscorrection_outputs,
+    .priv_class    = &lenscorrection_class,
+    .flags         = AVFILTER_FLAG_SLICE_THREADS,
+};
+    
\ No newline at end of file
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 053f3e7..f08edf6 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -333,6 +333,9 @@ fate-filter-pixfmts-il:    CMD = pixfmts "luma_mode=d:chroma_mode=d:alpha_mode=d
 FATE_FILTER_PIXFMTS-$(CONFIG_KERNDEINT_FILTER) += fate-filter-pixfmts-kerndeint
 fate-filter-pixfmts-kerndeint: CMD = pixfmts "" "tinterlace=interleave_top,"
 
+FATE_FILTER_PIXFMTS-$(CONFIG_LENSCORRECTION_FILTER) += fate-filter-pixfmts-lenscorrection
+fate-filter-pixfmts-lenscorrection: CMD = pixfmts "0.6:0.4:0.65:0.4"
+
 FATE_FILTER_PIXFMTS-$(CONFIG_LUT_FILTER) += fate-filter-pixfmts-lut
 fate-filter-pixfmts-lut: CMD = pixfmts "c0=2*val:c1=2*val:c2=val/2:c3=negval+40"
 
diff --git a/tests/ref/fate/filter-pixfmts-lenscorrection b/tests/ref/fate/filter-pixfmts-lenscorrection
new file mode 100644
index 0000000..18af6fb
--- /dev/null
+++ b/tests/ref/fate/filter-pixfmts-lenscorrection
@@ -0,0 +1,8 @@
+yuv410p             e7d59dbdb1afab7e2a8f770d563e28c4
+yuv420p             bc45b4762d5271410ff825317c85af64
+yuv422p             5cce0c299322634d65e6b32c976e2c12
+yuv444p             03de9a93ab3045a523b234ea93f21c91
+yuva420p            d1fa6735c4e7fbbf3a501cec1f0b4ac1
+yuva444p            713ddf5861d3df11c70a242a13c5e92e
+yuvj420p            1d5cccaf4ef568ae9fa36f9a28e71c34
+yuvj444p            aef1db29848e3b1dcaf4309255c38cbd
-- 
1.8.2



More information about the ffmpeg-devel mailing list