Browse code

avfilter: add midequalizer filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2017/02/02 21:10:37
Showing 6 changed files
... ...
@@ -20,6 +20,7 @@ version <next>:
20 20
 - abitscope multimedia filter
21 21
 - Scenarist Closed Captions demuxer and muxer
22 22
 - threshold filter
23
+- midequalizer filter
23 24
 
24 25
 version 3.2:
25 26
 - libopenmpt demuxer
... ...
@@ -9643,6 +9643,17 @@ Macroblock size. Default @code{16}.
9643 9643
 Search parameter. Default @code{7}.
9644 9644
 @end table
9645 9645
 
9646
+@section midequalizer
9647
+
9648
+Apply Midway Image Equalization effect using two video streams.
9649
+
9650
+This filter accepts the following option:
9651
+
9652
+@table @option
9653
+@item planes
9654
+Set which planes to process. Default is @code{15}, which is all available planes.
9655
+@end table
9656
+
9646 9657
 @section minterpolate
9647 9658
 
9648 9659
 Convert the video to specified frame rate using motion interpolation.
... ...
@@ -219,6 +219,7 @@ OBJS-$(CONFIG_MCDEINT_FILTER)                += vf_mcdeint.o
219 219
 OBJS-$(CONFIG_MERGEPLANES_FILTER)            += vf_mergeplanes.o framesync.o
220 220
 OBJS-$(CONFIG_MESTIMATE_FILTER)              += vf_mestimate.o motion_estimation.o
221 221
 OBJS-$(CONFIG_METADATA_FILTER)               += f_metadata.o
222
+OBJS-$(CONFIG_MIDEQUALIZER_FILTER)           += vf_midequalizer.o framesync.o
222 223
 OBJS-$(CONFIG_MINTERPOLATE_FILTER)           += vf_minterpolate.o motion_estimation.o
223 224
 OBJS-$(CONFIG_MPDECIMATE_FILTER)             += vf_mpdecimate.o
224 225
 OBJS-$(CONFIG_NEGATE_FILTER)                 += vf_lut.o
... ...
@@ -235,6 +235,7 @@ void avfilter_register_all(void)
235 235
     REGISTER_FILTER(MERGEPLANES,    mergeplanes,    vf);
236 236
     REGISTER_FILTER(MESTIMATE,      mestimate,      vf);
237 237
     REGISTER_FILTER(METADATA,       metadata,       vf);
238
+    REGISTER_FILTER(MIDEQUALIZER,   midequalizer,   vf);
238 239
     REGISTER_FILTER(MINTERPOLATE,   minterpolate,   vf);
239 240
     REGISTER_FILTER(MPDECIMATE,     mpdecimate,     vf);
240 241
     REGISTER_FILTER(NEGATE,         negate,         vf);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  72
33
+#define LIBAVFILTER_VERSION_MINOR  73
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
new file mode 100644
... ...
@@ -0,0 +1,391 @@
0
+/*
1
+ * Copyright (c) 2017 Paul B Mahol
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#include "libavutil/imgutils.h"
21
+#include "libavutil/pixdesc.h"
22
+#include "libavutil/opt.h"
23
+#include "avfilter.h"
24
+#include "formats.h"
25
+#include "internal.h"
26
+#include "video.h"
27
+#include "avfilter.h"
28
+#include "framesync.h"
29
+
30
+typedef struct MidEqualizerContext {
31
+    const AVClass *class;
32
+    int width[2][4], height[2][4];
33
+    int nb_planes;
34
+    int planes;
35
+    int histogram_size;
36
+    float *histogram[2];
37
+    unsigned *cchange;
38
+    FFFrameSync fs;
39
+
40
+    void (*midequalizer)(const uint8_t *in0, const uint8_t *in1,
41
+                         uint8_t *dst,
42
+                         ptrdiff_t linesize1, ptrdiff_t linesize2,
43
+                         ptrdiff_t dlinesize,
44
+                         int w0, int h0,
45
+                         int w1, int h1,
46
+                         float *histogram1, float *histogram2,
47
+                         unsigned *cchange, size_t hsize);
48
+} MidEqualizerContext;
49
+
50
+#define OFFSET(x) offsetof(MidEqualizerContext, x)
51
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
52
+
53
+static const AVOption midequalizer_options[] = {
54
+    { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, FLAGS },
55
+    { NULL }
56
+};
57
+
58
+AVFILTER_DEFINE_CLASS(midequalizer);
59
+
60
+static int query_formats(AVFilterContext *ctx)
61
+{
62
+    static const enum AVPixelFormat pix_fmts[] = {
63
+        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
64
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
65
+        AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
66
+        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
67
+        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
68
+        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
69
+        AV_PIX_FMT_GRAY8,
70
+        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
71
+        AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
72
+        AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12,
73
+        AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
74
+        AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14,
75
+        AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
76
+        AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
77
+        AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12,
78
+        AV_PIX_FMT_NONE
79
+    };
80
+
81
+    return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
82
+}
83
+
84
+static int process_frame(FFFrameSync *fs)
85
+{
86
+    AVFilterContext *ctx = fs->parent;
87
+    MidEqualizerContext *s = fs->opaque;
88
+    AVFilterLink *outlink = ctx->outputs[0];
89
+    AVFrame *out, *in0, *in1;
90
+    int ret;
91
+
92
+    if ((ret = ff_framesync_get_frame(&s->fs, 0, &in0, 0)) < 0 ||
93
+        (ret = ff_framesync_get_frame(&s->fs, 1, &in1, 0)) < 0)
94
+        return ret;
95
+
96
+    if (ctx->is_disabled) {
97
+        out = av_frame_clone(in0);
98
+        if (!out)
99
+            return AVERROR(ENOMEM);
100
+    } else {
101
+        int p;
102
+
103
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
104
+        if (!out)
105
+            return AVERROR(ENOMEM);
106
+        av_frame_copy_props(out, in0);
107
+
108
+        for (p = 0; p < s->nb_planes; p++) {
109
+            if (!((1 << p) & s->planes)) {
110
+                av_image_copy_plane(out->data[p], out->linesize[p], in0->data[p], in0->linesize[p],
111
+                                    s->width[0][p] * (1 + (s->histogram_size > 256)), s->height[0][p]);
112
+                continue;
113
+            }
114
+
115
+            s->midequalizer(in0->data[p], in1->data[p],
116
+                            out->data[p],
117
+                            in0->linesize[p], in1->linesize[p],
118
+                            out->linesize[p],
119
+                            s->width[0][p], s->height[0][p],
120
+                            s->width[1][p], s->height[1][p],
121
+                            s->histogram[0], s->histogram[1],
122
+                            s->cchange, s->histogram_size);
123
+        }
124
+    }
125
+    out->pts = av_rescale_q(in0->pts, s->fs.time_base, outlink->time_base);
126
+
127
+    return ff_filter_frame(outlink, out);
128
+}
129
+
130
+static void compute_histogram8(const uint8_t *src, ptrdiff_t linesize,
131
+                               int w, int h, float *histogram, size_t hsize)
132
+{
133
+    int y, x;
134
+
135
+    memset(histogram, 0, hsize * sizeof(*histogram));
136
+
137
+    for (y = 0; y < h; y++) {
138
+        for (x = 0; x < w; x++) {
139
+            histogram[src[x]] += 1;
140
+        }
141
+        src += linesize;
142
+    }
143
+
144
+    for (x = 0; x < hsize - 1; x++) {
145
+        histogram[x + 1] += histogram[x];
146
+        histogram[x] /= hsize;
147
+    }
148
+    histogram[x] /= hsize;
149
+}
150
+
151
+static void compute_histogram16(const uint16_t *src, ptrdiff_t linesize,
152
+                                int w, int h, float *histogram, size_t hsize)
153
+{
154
+    int y, x;
155
+
156
+    memset(histogram, 0, hsize * sizeof(*histogram));
157
+
158
+    for (y = 0; y < h; y++) {
159
+        for (x = 0; x < w; x++) {
160
+            histogram[src[x]] += 1;
161
+        }
162
+        src += linesize;
163
+    }
164
+
165
+    for (x = 0; x < hsize - 1; x++) {
166
+        histogram[x + 1] += histogram[x];
167
+        histogram[x] /= hsize;
168
+    }
169
+    histogram[x] /= hsize;
170
+}
171
+
172
+static void compute_contrast_change(float *histogram1, float *histogram2,
173
+                                    unsigned *cchange, size_t hsize)
174
+{
175
+    int i;
176
+
177
+    for (i = 0; i < hsize; i++) {
178
+        int j;
179
+
180
+        for (j = 0; j < hsize && histogram2[j] < histogram1[i]; j++);
181
+
182
+        cchange[i] = (i + j) / 2;
183
+    }
184
+}
185
+
186
+static void midequalizer8(const uint8_t *in0, const uint8_t *in1,
187
+                          uint8_t *dst,
188
+                          ptrdiff_t linesize1, ptrdiff_t linesize2,
189
+                          ptrdiff_t dlinesize,
190
+                          int w0, int h0,
191
+                          int w1, int h1,
192
+                          float *histogram1, float *histogram2,
193
+                          unsigned *cchange,
194
+                          size_t hsize)
195
+{
196
+    int x, y;
197
+
198
+    compute_histogram8(in0, linesize1, w0, h0, histogram1, hsize);
199
+    compute_histogram8(in1, linesize2, w1, h1, histogram2, hsize);
200
+
201
+    compute_contrast_change(histogram1, histogram2, cchange, hsize);
202
+
203
+    for (y = 0; y < h0; y++) {
204
+        for (x = 0; x < w0; x++) {
205
+            dst[x] = av_clip_uint8(cchange[in0[x]]);
206
+        }
207
+        dst += dlinesize;
208
+        in0 += linesize1;
209
+    }
210
+}
211
+
212
+static void midequalizer16(const uint8_t *in0, const uint8_t *in1,
213
+                           uint8_t *dst,
214
+                           ptrdiff_t linesize1, ptrdiff_t linesize2,
215
+                           ptrdiff_t dlinesize,
216
+                           int w0, int h0,
217
+                           int w1, int h1,
218
+                           float *histogram1, float *histogram2,
219
+                           unsigned *cchange,
220
+                           size_t hsize)
221
+{
222
+    const uint16_t *i = (const uint16_t *)in0;
223
+    uint16_t *d = (uint16_t *)dst;
224
+    int x, y;
225
+
226
+    compute_histogram16(i, linesize1 / 2, w0, h0, histogram1, hsize);
227
+    compute_histogram16((const uint16_t *)in1, linesize2 / 2, w1, h1, histogram2, hsize);
228
+
229
+    compute_contrast_change(histogram1, histogram2, cchange, hsize);
230
+
231
+    for (y = 0; y < h0; y++) {
232
+        for (x = 0; x < w0; x++) {
233
+            d[x] = cchange[i[x]];
234
+        }
235
+        d += dlinesize / 2;
236
+        i += linesize1 / 2;
237
+    }
238
+}
239
+
240
+static int config_input0(AVFilterLink *inlink)
241
+{
242
+    AVFilterContext *ctx = inlink->dst;
243
+    MidEqualizerContext *s = ctx->priv;
244
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
245
+    int vsub, hsub;
246
+
247
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
248
+
249
+    hsub = desc->log2_chroma_w;
250
+    vsub = desc->log2_chroma_h;
251
+
252
+    s->height[0][0] = s->height[0][3] = inlink->h;
253
+    s->width[0][0]  = s->width[0][3]  = inlink->w;
254
+    s->height[0][1] = s->height[0][2] = AV_CEIL_RSHIFT(inlink->h, vsub);
255
+    s->width[0][1]  = s->width[0][2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
256
+
257
+    s->histogram_size = 1 << desc->comp[0].depth;
258
+
259
+    s->histogram[0] = av_calloc(s->histogram_size, sizeof(float));
260
+    s->histogram[1] = av_calloc(s->histogram_size, sizeof(float));
261
+    s->cchange      = av_calloc(s->histogram_size, sizeof(unsigned));
262
+    if (!s->histogram[0] || !s->histogram[1] || !s->cchange)
263
+        return AVERROR(ENOMEM);
264
+
265
+    if (s->histogram_size == 256) {
266
+        s->midequalizer = midequalizer8;
267
+    } else {
268
+        s->midequalizer = midequalizer16;
269
+    }
270
+
271
+    return 0;
272
+}
273
+
274
+static int config_input1(AVFilterLink *inlink)
275
+{
276
+    AVFilterContext *ctx = inlink->dst;
277
+    MidEqualizerContext *s = ctx->priv;
278
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
279
+    int vsub, hsub;
280
+
281
+    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
282
+
283
+    hsub = desc->log2_chroma_w;
284
+    vsub = desc->log2_chroma_h;
285
+
286
+    s->height[1][0] = s->height[1][3] = inlink->h;
287
+    s->width[1][0]  = s->width[1][3]  = inlink->w;
288
+    s->height[1][1] = s->height[1][2] = AV_CEIL_RSHIFT(inlink->h, vsub);
289
+    s->width[1][1]  = s->width[1][2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
290
+
291
+    return 0;
292
+}
293
+
294
+static int config_output(AVFilterLink *outlink)
295
+{
296
+    AVFilterContext *ctx = outlink->src;
297
+    MidEqualizerContext *s = ctx->priv;
298
+    AVFilterLink *in0 = ctx->inputs[0];
299
+    AVFilterLink *in1 = ctx->inputs[1];
300
+    FFFrameSyncIn *in;
301
+    int ret;
302
+
303
+    if (in0->format != in1->format) {
304
+        av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
305
+        return AVERROR(EINVAL);
306
+    }
307
+
308
+    outlink->w = in0->w;
309
+    outlink->h = in0->h;
310
+    outlink->time_base = in0->time_base;
311
+    outlink->sample_aspect_ratio = in0->sample_aspect_ratio;
312
+    outlink->frame_rate = in0->frame_rate;
313
+
314
+    if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)
315
+        return ret;
316
+
317
+    in = s->fs.in;
318
+    in[0].time_base = in0->time_base;
319
+    in[1].time_base = in1->time_base;
320
+    in[0].sync   = 1;
321
+    in[0].before = EXT_STOP;
322
+    in[0].after  = EXT_INFINITY;
323
+    in[1].sync   = 1;
324
+    in[1].before = EXT_STOP;
325
+    in[1].after  = EXT_INFINITY;
326
+    s->fs.opaque   = s;
327
+    s->fs.on_event = process_frame;
328
+
329
+    return ff_framesync_configure(&s->fs);
330
+}
331
+
332
+static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
333
+{
334
+    MidEqualizerContext *s = inlink->dst->priv;
335
+    return ff_framesync_filter_frame(&s->fs, inlink, buf);
336
+}
337
+
338
+static int request_frame(AVFilterLink *outlink)
339
+{
340
+    MidEqualizerContext *s = outlink->src->priv;
341
+    return ff_framesync_request_frame(&s->fs, outlink);
342
+}
343
+
344
+static av_cold void uninit(AVFilterContext *ctx)
345
+{
346
+    MidEqualizerContext *s = ctx->priv;
347
+
348
+    ff_framesync_uninit(&s->fs);
349
+    av_freep(&s->histogram[0]);
350
+    av_freep(&s->histogram[1]);
351
+    av_freep(&s->cchange);
352
+}
353
+
354
+static const AVFilterPad midequalizer_inputs[] = {
355
+    {
356
+        .name         = "in0",
357
+        .type         = AVMEDIA_TYPE_VIDEO,
358
+        .filter_frame = filter_frame,
359
+        .config_props = config_input0,
360
+    },
361
+    {
362
+        .name         = "in1",
363
+        .type         = AVMEDIA_TYPE_VIDEO,
364
+        .filter_frame = filter_frame,
365
+        .config_props = config_input1,
366
+    },
367
+    { NULL }
368
+};
369
+
370
+static const AVFilterPad midequalizer_outputs[] = {
371
+    {
372
+        .name          = "default",
373
+        .type          = AVMEDIA_TYPE_VIDEO,
374
+        .config_props  = config_output,
375
+        .request_frame = request_frame,
376
+    },
377
+    { NULL }
378
+};
379
+
380
+AVFilter ff_vf_midequalizer = {
381
+    .name          = "midequalizer",
382
+    .description   = NULL_IF_CONFIG_SMALL("Apply Midway Equalization."),
383
+    .priv_size     = sizeof(MidEqualizerContext),
384
+    .uninit        = uninit,
385
+    .query_formats = query_formats,
386
+    .inputs        = midequalizer_inputs,
387
+    .outputs       = midequalizer_outputs,
388
+    .priv_class    = &midequalizer_class,
389
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
390
+};