Browse code

extractplanes filter

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

Paul B Mahol authored on 2013/04/30 21:12:12
Showing 6 changed files
... ...
@@ -38,6 +38,7 @@ version <next>:
38 38
 - ffmpeg -t and -ss (output-only) options are now sample-accurate when
39 39
   transcoding audio
40 40
 - Matroska muxer can now put the index at the beginning of the file.
41
+- extractplanes filter
41 42
 
42 43
 
43 44
 version 1.2:
... ...
@@ -3320,6 +3320,44 @@ Example:
3320 3320
 edgedetect=low=0.1:high=0.4
3321 3321
 @end example
3322 3322
 
3323
+@section extractplanes
3324
+
3325
+Extract color channel components from input video stream into
3326
+separate grayscale video streams.
3327
+
3328
+The filter accepts the following option:
3329
+
3330
+@table @option
3331
+@item planes
3332
+Set plane(s) to extract.
3333
+
3334
+Available values for planes are:
3335
+@table @samp
3336
+@item y
3337
+@item u
3338
+@item v
3339
+@item a
3340
+@item r
3341
+@item g
3342
+@item b
3343
+@end table
3344
+
3345
+Choosing planes not available in the input will result in an error.
3346
+That means you cannot select @code{r}, @code{g}, @code{b} planes
3347
+with @code{y}, @code{u}, @code{v} planes at same time.
3348
+@end table
3349
+
3350
+@subsection Examples
3351
+
3352
+@itemize
3353
+@item
3354
+Extract luma, u and v color channel component from input video frame
3355
+into 3 grayscale outputs:
3356
+@example
3357
+ffmpeg -i video.avi -filter_complex 'extractplanes=y+u+v[y][u][v]' -map '[y]' y.avi -map '[u]' u.avi -map '[v]' v.avi
3358
+@end example
3359
+@end itemize
3360
+
3323 3361
 @section fade
3324 3362
 
3325 3363
 Apply fade-in/out effect to input video.
... ...
@@ -121,6 +121,7 @@ OBJS-$(CONFIG_DESHAKE_FILTER)                += vf_deshake.o
121 121
 OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
122 122
 OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
123 123
 OBJS-$(CONFIG_EDGEDETECT_FILTER)             += vf_edgedetect.o
124
+OBJS-$(CONFIG_EXTRACTPLANES_FILTER)          += vf_extractplanes.o
124 125
 OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
125 126
 OBJS-$(CONFIG_FIELD_FILTER)                  += vf_field.o
126 127
 OBJS-$(CONFIG_FIELDMATCH_FILTER)             += vf_fieldmatch.o
... ...
@@ -119,6 +119,7 @@ void avfilter_register_all(void)
119 119
     REGISTER_FILTER(DRAWBOX,        drawbox,        vf);
120 120
     REGISTER_FILTER(DRAWTEXT,       drawtext,       vf);
121 121
     REGISTER_FILTER(EDGEDETECT,     edgedetect,     vf);
122
+    REGISTER_FILTER(EXTRACTPLANES,  extractplanes,  vf);
122 123
     REGISTER_FILTER(FADE,           fade,           vf);
123 124
     REGISTER_FILTER(FIELD,          field,          vf);
124 125
     REGISTER_FILTER(FIELDMATCH,     fieldmatch,     vf);
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  3
32
-#define LIBAVFILTER_VERSION_MINOR  62
32
+#define LIBAVFILTER_VERSION_MINOR  63
33 33
 #define LIBAVFILTER_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
new file mode 100644
... ...
@@ -0,0 +1,254 @@
0
+/*
1
+ * Copyright (c) 2013 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/avstring.h"
21
+#include "libavutil/imgutils.h"
22
+#include "libavutil/opt.h"
23
+#include "libavutil/pixdesc.h"
24
+#include "avfilter.h"
25
+#include "internal.h"
26
+
27
+#define PLANE_R 0x01
28
+#define PLANE_G 0x02
29
+#define PLANE_B 0x04
30
+#define PLANE_A 0x08
31
+#define PLANE_Y 0x10
32
+#define PLANE_U 0x20
33
+#define PLANE_V 0x40
34
+
35
+typedef struct {
36
+    const AVClass *class;
37
+    int requested_planes;
38
+    int map[4];
39
+    int linesize[4];
40
+} ExtractPlanesContext;
41
+
42
+#define OFFSET(x) offsetof(ExtractPlanesContext, x)
43
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
44
+static const AVOption extractplanes_options[] = {
45
+    { "planes", "set planes",  OFFSET(requested_planes), AV_OPT_TYPE_FLAGS, {.i64=1}, 1, 0xff, FLAGS, "flags"},
46
+    {      "y", "set luma plane",  0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, "flags"},
47
+    {      "u", "set u plane",     0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, "flags"},
48
+    {      "v", "set v plane",     0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, "flags"},
49
+    {      "g", "set green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, "flags"},
50
+    {      "r", "set red plane",   0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, "flags"},
51
+    {      "b", "set blue plane",  0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, "flags"},
52
+    {      "a", "set alpha plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_A}, 0, 0, FLAGS, "flags"},
53
+    { NULL }
54
+};
55
+
56
+AVFILTER_DEFINE_CLASS(extractplanes);
57
+
58
+static int query_formats(AVFilterContext *ctx)
59
+{
60
+    static const enum AVPixelFormat in_pixfmts[] = {
61
+        AV_PIX_FMT_YUV410P,
62
+        AV_PIX_FMT_YUV411P,
63
+        AV_PIX_FMT_YUV440P,
64
+        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
65
+        AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P,
66
+        AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
67
+        AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
68
+        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P,
69
+        AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUVA420P16LE,
70
+        AV_PIX_FMT_YUV420P16BE, AV_PIX_FMT_YUVA420P16BE,
71
+        AV_PIX_FMT_YUV422P16LE, AV_PIX_FMT_YUVA422P16LE,
72
+        AV_PIX_FMT_YUV422P16BE, AV_PIX_FMT_YUVA422P16BE,
73
+        AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUVA444P16LE,
74
+        AV_PIX_FMT_YUV444P16BE, AV_PIX_FMT_YUVA444P16BE,
75
+        AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A,
76
+        AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16BE,
77
+        AV_PIX_FMT_GBRP,
78
+        AV_PIX_FMT_GBRP16LE, AV_PIX_FMT_GBRP16BE,
79
+        AV_PIX_FMT_NONE,
80
+    };
81
+    static const enum AVPixelFormat out8_pixfmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
82
+    static const enum AVPixelFormat out16le_pixfmts[] = { AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_NONE };
83
+    static const enum AVPixelFormat out16be_pixfmts[] = { AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_NONE };
84
+    const enum AVPixelFormat *out_pixfmts;
85
+    const AVPixFmtDescriptor *desc;
86
+    AVFilterFormats *avff;
87
+    int i, depth = 0, be = 0;
88
+
89
+    if (!ctx->inputs[0]->in_formats ||
90
+        !ctx->inputs[0]->in_formats->format_count) {
91
+        return AVERROR(EAGAIN);
92
+    }
93
+
94
+    if (!ctx->inputs[0]->out_formats)
95
+        ff_formats_ref(ff_make_format_list(in_pixfmts), &ctx->inputs[0]->out_formats);
96
+
97
+    avff = ctx->inputs[0]->in_formats;
98
+    desc = av_pix_fmt_desc_get(avff->formats[0]);
99
+    depth = desc->comp[0].depth_minus1;
100
+    be = desc->flags & PIX_FMT_BE;
101
+    for (i = 1; i < avff->format_count; i++) {
102
+        desc = av_pix_fmt_desc_get(avff->formats[i]);
103
+        if (depth != desc->comp[0].depth_minus1 ||
104
+            be    != (desc->flags & PIX_FMT_BE)) {
105
+            return AVERROR(EAGAIN);
106
+        }
107
+    }
108
+
109
+    if (depth == 7)
110
+        out_pixfmts = out8_pixfmts;
111
+    else if (be)
112
+        out_pixfmts = out16be_pixfmts;
113
+    else
114
+        out_pixfmts = out16le_pixfmts;
115
+
116
+    for (i = 0; i < ctx->nb_outputs; i++)
117
+        ff_formats_ref(ff_make_format_list(out_pixfmts), &ctx->outputs[i]->in_formats);
118
+    return 0;
119
+}
120
+
121
+static int config_input(AVFilterLink *inlink)
122
+{
123
+    AVFilterContext *ctx = inlink->dst;
124
+    ExtractPlanesContext *e = ctx->priv;
125
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
126
+    int plane_avail, ret;
127
+
128
+    plane_avail = ((desc->flags & PIX_FMT_RGB) ? PLANE_R|PLANE_G|PLANE_B :
129
+                                                 PLANE_Y |
130
+                                ((desc->nb_components > 2) ? PLANE_U|PLANE_V : 0)) |
131
+                  ((desc->flags & PIX_FMT_ALPHA) ? PLANE_A : 0);
132
+    if (e->requested_planes & ~plane_avail) {
133
+        av_log(ctx, AV_LOG_ERROR, "Requested planes not available.\n");
134
+        return AVERROR(EINVAL);
135
+    }
136
+    if ((ret = av_image_fill_linesizes(e->linesize, inlink->format, inlink->w)) < 0)
137
+        return ret;
138
+
139
+    return 0;
140
+}
141
+
142
+static int config_output(AVFilterLink *outlink)
143
+{
144
+    AVFilterContext *ctx = outlink->src;
145
+    AVFilterLink *inlink = ctx->inputs[0];
146
+    ExtractPlanesContext *e = ctx->priv;
147
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
148
+    const int output = outlink->srcpad - ctx->output_pads;
149
+
150
+    if (e->map[output] == 1 || e->map[output] == 2) {
151
+        outlink->h = inlink->h >> desc->log2_chroma_h;
152
+        outlink->w = inlink->w >> desc->log2_chroma_w;
153
+    }
154
+
155
+    return 0;
156
+}
157
+
158
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
159
+{
160
+    AVFilterContext *ctx = inlink->dst;
161
+    ExtractPlanesContext *e = ctx->priv;
162
+    int i, eof = 0, ret = 0;
163
+
164
+    for (i = 0; i < ctx->nb_outputs; i++) {
165
+        AVFilterLink *outlink = ctx->outputs[i];
166
+        const int idx = e->map[i];
167
+        AVFrame *out;
168
+
169
+        if (outlink->closed || !frame->data[idx])
170
+            continue;
171
+
172
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
173
+        if (!out) {
174
+            ret = AVERROR(ENOMEM);
175
+            break;
176
+        }
177
+        av_frame_copy_props(out, frame);
178
+
179
+        av_image_copy_plane(out->data[0], out->linesize[0],
180
+                            frame->data[idx], frame->linesize[idx],
181
+                            e->linesize[idx], outlink->h);
182
+        ret = ff_filter_frame(outlink, out);
183
+        if (ret == AVERROR_EOF)
184
+            eof++;
185
+        else if (ret < 0)
186
+            break;
187
+    }
188
+    av_frame_free(&frame);
189
+
190
+    if (eof == ctx->nb_outputs)
191
+        ret = AVERROR_EOF;
192
+    else if (ret == AVERROR_EOF)
193
+        ret = 0;
194
+    return ret;
195
+}
196
+
197
+static int init(AVFilterContext *ctx)
198
+{
199
+    ExtractPlanesContext *e = ctx->priv;
200
+    int planes = (e->requested_planes & 0xf) | (e->requested_planes >> 4);
201
+    int i;
202
+
203
+    for (i = 0; i < 4; i++) {
204
+        char *name;
205
+        AVFilterPad pad = { 0 };
206
+
207
+        if (!(planes & (1 << i)))
208
+            continue;
209
+
210
+        name = av_asprintf("out%d", ctx->nb_outputs);
211
+        if (!name)
212
+            return AVERROR(ENOMEM);
213
+        e->map[ctx->nb_outputs] = i;
214
+        pad.name = name;
215
+        pad.type = AVMEDIA_TYPE_VIDEO;
216
+        pad.config_props = config_output;
217
+
218
+        ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
219
+    }
220
+
221
+    return 0;
222
+}
223
+
224
+static void uninit(AVFilterContext *ctx)
225
+{
226
+    int i;
227
+
228
+    for (i = 0; i < ctx->nb_outputs; i++)
229
+        av_freep(&ctx->output_pads[i].name);
230
+}
231
+
232
+static const AVFilterPad extractplanes_inputs[] = {
233
+    {
234
+        .name         = "default",
235
+        .type         = AVMEDIA_TYPE_VIDEO,
236
+        .filter_frame = filter_frame,
237
+        .config_props = config_input,
238
+    },
239
+    { NULL }
240
+};
241
+
242
+AVFilter avfilter_vf_extractplanes = {
243
+    .name          = "extractplanes",
244
+    .description   = NULL_IF_CONFIG_SMALL("Extract planes as grayscale frames."),
245
+    .priv_size     = sizeof(ExtractPlanesContext),
246
+    .priv_class    = &extractplanes_class,
247
+    .init          = init,
248
+    .uninit        = uninit,
249
+    .query_formats = query_formats,
250
+    .inputs        = extractplanes_inputs,
251
+    .outputs       = NULL,
252
+    .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
253
+};