[FFmpeg-devel] avfilter: add deconvolve filter

Submitted by Paul B Mahol on Dec. 25, 2017, 8:38 p.m.

Details

Message ID 20171225203841.30573-1-onemda@gmail.com
State New
Headers show

Commit Message

Paul B Mahol Dec. 25, 2017, 8:38 p.m.
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 doc/filters.texi          | 22 +++++++++++
 libavfilter/Makefile      |  1 +
 libavfilter/allfilters.c  |  1 +
 libavfilter/vf_convolve.c | 99 ++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 113 insertions(+), 10 deletions(-)

Comments

Tomas Härdin Dec. 26, 2017, 8:29 p.m.
mån 2017-12-25 klockan 21:38 +0100 skrev Paul B Mahol:
> > 
> -        if (!(s->fft_hdata[i] = av_calloc(s->fft_len[i] + 1, s->fft_len[i] * sizeof(FFTComplex))))
> +        if (!(s->fft_hdata[i] = av_calloc(s->fft_len[i], s->fft_len[i] * sizeof(FFTComplex))))
>              return AVERROR(ENOMEM);
>  

Is there a particular reason these were +1? Maybe something to do with
padding?

> @@ -166,7 +171,7 @@ static int fft_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_job
>      FFTComplex *hdata = td->hdata;
>      const int plane = td->plane;
>      const int n = td->n;
> -    int start = (n *  jobnr   ) / nb_jobs;
> +    int start = (n * jobnr) / nb_jobs;

Don't mix cosmetic and functional changes

Rest of the patch looks OK enough to me

/Tomas
Moritz Barsnick Dec. 26, 2017, 11:22 p.m.
On Mon, Dec 25, 2017 at 21:38:41 +0100, Paul B Mahol wrote:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  doc/filters.texi          | 22 +++++++++++
>  libavfilter/Makefile      |  1 +
>  libavfilter/allfilters.c  |  1 +
>  libavfilter/vf_convolve.c | 99 ++++++++++++++++++++++++++++++++++++++++++-----
>  4 files changed, 113 insertions(+), 10 deletions(-)
[...]
>  OBJS-$(CONFIG_DECIMATE_FILTER)               += vf_decimate.o
> +OBJS-$(CONFIG_DECONVOLVE_FILTER)             += vf_convolve.o framesync.o
>  OBJS-$(CONFIG_DEFLATE_FILTER)                += vf_neighbor.o
[...]
> +AVFilter ff_vf_deconvolve = {
> +    .name          = "deconvolve",

Style question: Do separate filters within one source file not need to
be #ifdef'd with
#if CONFIG_CONVOLVE_FILTER,
#if CONFIG_DECONVOLVE_FILTER
?

Cheers,
Moritz

Patch hide | download patch | download mbox

diff --git a/doc/filters.texi b/doc/filters.texi
index 45515966e8..0220b7786a 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -6937,6 +6937,28 @@  Set whether or not chroma is considered in the metric calculations. Default is
 @code{1}.
 @end table
 
+@section deconvolve
+
+Apply 2D deconvolution of video stream in frequency domain using second stream
+as impulse.
+
+The filter accepts the following options:
+
+@table @option
+@item planes
+Set which planes to process.
+
+@item impulse
+Set which impulse video frames will be processed, can be @var{first}
+or @var{all}. Default is @var{all}.
+
+@item noise
+Set noise when doing divisions. Default is @var{0}. Useful when width and height
+are not same and not power of 2.
+@end table
+
+The @code{deconvolve} filter also supports the @ref{framesync} options.
+
 @section deflate
 
 Apply deflate effect to the video.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 6b06d57234..8bde542163 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -165,6 +165,7 @@  OBJS-$(CONFIG_DATASCOPE_FILTER)              += vf_datascope.o
 OBJS-$(CONFIG_DCTDNOIZ_FILTER)               += vf_dctdnoiz.o
 OBJS-$(CONFIG_DEBAND_FILTER)                 += vf_deband.o
 OBJS-$(CONFIG_DECIMATE_FILTER)               += vf_decimate.o
+OBJS-$(CONFIG_DECONVOLVE_FILTER)             += vf_convolve.o framesync.o
 OBJS-$(CONFIG_DEFLATE_FILTER)                += vf_neighbor.o
 OBJS-$(CONFIG_DEFLICKER_FILTER)              += vf_deflicker.o
 OBJS-$(CONFIG_DEINTERLACE_QSV_FILTER)        += vf_deinterlace_qsv.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 707faad777..67c073091f 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -175,6 +175,7 @@  static void register_all(void)
     REGISTER_FILTER(DCTDNOIZ,       dctdnoiz,       vf);
     REGISTER_FILTER(DEBAND,         deband,         vf);
     REGISTER_FILTER(DECIMATE,       decimate,       vf);
+    REGISTER_FILTER(DECONVOLVE,     deconvolve,     vf);
     REGISTER_FILTER(DEFLATE,        deflate,        vf);
     REGISTER_FILTER(DEFLICKER,      deflicker,      vf);
     REGISTER_FILTER(DEINTERLACE_QSV,deinterlace_qsv,vf);
diff --git a/libavfilter/vf_convolve.c b/libavfilter/vf_convolve.c
index 88ae884a19..1b96848b87 100644
--- a/libavfilter/vf_convolve.c
+++ b/libavfilter/vf_convolve.c
@@ -18,6 +18,8 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include <float.h>
+
 #include "libavutil/imgutils.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
@@ -51,8 +53,11 @@  typedef struct ConvolveContext {
     int depth;
     int planes;
     int impulse;
+    float noise;
     int nb_planes;
     int got_impulse[4];
+
+    int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
 } ConvolveContext;
 
 #define OFFSET(x) offsetof(ConvolveContext, x)
@@ -121,16 +126,16 @@  static int config_input_main(AVFilterLink *inlink)
         s->fft_bits[i] = fft_bits;
         s->fft_len[i] = 1 << s->fft_bits[i];
 
-        if (!(s->fft_hdata[i] = av_calloc(s->fft_len[i] + 1, s->fft_len[i] * sizeof(FFTComplex))))
+        if (!(s->fft_hdata[i] = av_calloc(s->fft_len[i], s->fft_len[i] * sizeof(FFTComplex))))
             return AVERROR(ENOMEM);
 
-        if (!(s->fft_vdata[i] = av_calloc(s->fft_len[i] + 1, s->fft_len[i] * sizeof(FFTComplex))))
+        if (!(s->fft_vdata[i] = av_calloc(s->fft_len[i], s->fft_len[i] * sizeof(FFTComplex))))
             return AVERROR(ENOMEM);
 
-        if (!(s->fft_hdata_impulse[i] = av_calloc(s->fft_len[i] + 1, s->fft_len[i] * sizeof(FFTComplex))))
+        if (!(s->fft_hdata_impulse[i] = av_calloc(s->fft_len[i], s->fft_len[i] * sizeof(FFTComplex))))
             return AVERROR(ENOMEM);
 
-        if (!(s->fft_vdata_impulse[i] = av_calloc(s->fft_len[i] + 1, s->fft_len[i] * sizeof(FFTComplex))))
+        if (!(s->fft_vdata_impulse[i] = av_calloc(s->fft_len[i], s->fft_len[i] * sizeof(FFTComplex))))
             return AVERROR(ENOMEM);
     }
 
@@ -166,7 +171,7 @@  static int fft_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_job
     FFTComplex *hdata = td->hdata;
     const int plane = td->plane;
     const int n = td->n;
-    int start = (n *  jobnr   ) / nb_jobs;
+    int start = (n * jobnr) / nb_jobs;
     int end = (n * (jobnr+1)) / nb_jobs;
     int y;
 
@@ -261,7 +266,7 @@  static int fft_vertical(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
     FFTComplex *vdata = td->vdata;
     const int plane = td->plane;
     const int n = td->n;
-    int start = (n *  jobnr   ) / nb_jobs;
+    int start = (n * jobnr) / nb_jobs;
     int end = (n * (jobnr+1)) / nb_jobs;
     int y, x;
 
@@ -286,7 +291,7 @@  static int ifft_vertical(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs
     FFTComplex *vdata = td->vdata;
     const int plane = td->plane;
     const int n = td->n;
-    int start = (n *  jobnr   ) / nb_jobs;
+    int start = (n * jobnr) / nb_jobs;
     int end = (n * (jobnr+1)) / nb_jobs;
     int y, x;
 
@@ -310,7 +315,7 @@  static int ifft_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo
     FFTComplex *hdata = td->hdata;
     const int plane = td->plane;
     const int n = td->n;
-    int start = (n *  jobnr   ) / nb_jobs;
+    int start = (n * jobnr) / nb_jobs;
     int end = (n * (jobnr+1)) / nb_jobs;
     int y;
 
@@ -383,7 +388,7 @@  static int complex_multiply(AVFilterContext *ctx, void *arg, int jobnr, int nb_j
     FFTComplex *input = td->hdata;
     FFTComplex *filter = td->vdata;
     const int n = td->n;
-    int start = (n *  jobnr   ) / nb_jobs;
+    int start = (n * jobnr) / nb_jobs;
     int end = (n * (jobnr+1)) / nb_jobs;
     int y, x;
 
@@ -406,6 +411,38 @@  static int complex_multiply(AVFilterContext *ctx, void *arg, int jobnr, int nb_j
     return 0;
 }
 
+static int complex_divide(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    ConvolveContext *s = ctx->priv;
+    ThreadData *td = arg;
+    FFTComplex *input = td->hdata;
+    FFTComplex *filter = td->vdata;
+    const float noise = s->noise;
+    const int n = td->n;
+    int start = (n * jobnr) / nb_jobs;
+    int end = (n * (jobnr+1)) / nb_jobs;
+    int y, x;
+
+    for (y = start; y < end; y++) {
+        int yn = y * n;
+
+        for (x = 0; x < n; x++) {
+            FFTSample re, im, ire, iim, div;
+
+            re = input[yn + x].re;
+            im = input[yn + x].im;
+            ire = filter[yn + x].re;
+            iim = filter[yn + x].im;
+            div = ire * ire + iim * iim + noise;
+
+            input[yn + x].re = (ire * re + iim * im) / div;
+            input[yn + x].im = (ire * im - iim * re) / div;
+        }
+    }
+
+    return 0;
+}
+
 static int do_convolve(FFFrameSync *fs)
 {
     AVFilterContext *ctx = fs->parent;
@@ -474,7 +511,7 @@  static int do_convolve(FFFrameSync *fs)
         td.hdata = input;
         td.vdata = filter;
 
-        ctx->internal->execute(ctx, complex_multiply, &td, NULL, FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx)));
+        ctx->internal->execute(ctx, s->filter, &td, NULL, FFMIN3(MAX_THREADS, n, ff_filter_get_nb_threads(ctx)));
 
         td.hdata = s->fft_hdata[plane];
         td.vdata = s->fft_vdata[plane];
@@ -525,6 +562,21 @@  static int activate(AVFilterContext *ctx)
     return ff_framesync_activate(&s->fs);
 }
 
+static av_cold int init(AVFilterContext *ctx)
+{
+    ConvolveContext *s = ctx->priv;
+
+    if (!strcmp(ctx->filter->name, "convolve")) {
+        s->filter = complex_multiply;
+    } else if (!strcmp(ctx->filter->name, "deconvolve")) {
+        s->filter = complex_divide;
+    } else {
+        return AVERROR_BUG;
+    }
+
+    return 0;
+}
+
 static av_cold void uninit(AVFilterContext *ctx)
 {
     ConvolveContext *s = ctx->priv;
@@ -571,6 +623,7 @@  AVFilter ff_vf_convolve = {
     .name          = "convolve",
     .description   = NULL_IF_CONFIG_SMALL("Convolve first video stream with second video stream."),
     .preinit       = convolve_framesync_preinit,
+    .init          = init,
     .uninit        = uninit,
     .query_formats = query_formats,
     .activate      = activate,
@@ -580,3 +633,29 @@  AVFilter ff_vf_convolve = {
     .outputs       = convolve_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
 };
+
+static const AVOption deconvolve_options[] = {
+    { "planes",  "set planes to deconvolve",                OFFSET(planes),   AV_OPT_TYPE_INT,   {.i64=7}, 0, 15, FLAGS },
+    { "impulse", "when to process impulses",                OFFSET(impulse),  AV_OPT_TYPE_INT,   {.i64=1}, 0,  1, FLAGS, "impulse" },
+    {   "first", "process only first impulse, ignore rest", 0,                AV_OPT_TYPE_CONST, {.i64=0}, 0,  0, FLAGS, "impulse" },
+    {   "all",   "process all impulses",                    0,                AV_OPT_TYPE_CONST, {.i64=1}, 0,  0, FLAGS, "impulse" },
+    { "noise",   "set noise",                               OFFSET(noise),    AV_OPT_TYPE_FLOAT, {.dbl=0}, 0,  1, FLAGS },
+    { NULL },
+};
+
+FRAMESYNC_DEFINE_CLASS(deconvolve, ConvolveContext, fs);
+
+AVFilter ff_vf_deconvolve = {
+    .name          = "deconvolve",
+    .description   = NULL_IF_CONFIG_SMALL("Deconvolve first video stream with second video stream."),
+    .preinit       = deconvolve_framesync_preinit,
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .activate      = activate,
+    .priv_size     = sizeof(ConvolveContext),
+    .priv_class    = &deconvolve_class,
+    .inputs        = convolve_inputs,
+    .outputs       = convolve_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
+};