Browse code

colorchannelmixer filter

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

Paul B Mahol authored on 2013/04/16 01:56:29
Showing 6 changed files
... ...
@@ -24,6 +24,7 @@ version <next>:
24 24
 - smptehdbars source
25 25
 - inverse telecine filters (fieldmatch and decimate)
26 26
 - colorbalance filter
27
+- colorchannelmixer filter
27 28
 
28 29
 
29 30
 version 1.2:
... ...
@@ -2091,6 +2091,61 @@ colorbalance=rs=.3
2091 2091
 @end example
2092 2092
 @end itemize
2093 2093
 
2094
+@section colorchannelmixer
2095
+
2096
+Adjust video input frames by re-mixing color channels.
2097
+
2098
+This filter modifies a color channel by adding the values associated to
2099
+the other channels of the same pixels. For example if the value to
2100
+modify is red, the output value will be:
2101
+@example
2102
+@var{red}=@var{red}*@var{rr} + @var{blue}*@var{rb} + @var{green}*@var{rg} + @var{alpha}*@var{ra}
2103
+@end example
2104
+
2105
+The filter accepts the following options:
2106
+
2107
+@table @option
2108
+@item rr
2109
+@item rg
2110
+@item rb
2111
+@item ra
2112
+Adjust contribution of input red, green, blue and alpha channels for output red channel.
2113
+Default is @code{1} for @var{rr}, and @code{0} for @var{rg}, @var{rb} and @var{ra}.
2114
+
2115
+@item gr
2116
+@item gg
2117
+@item gb
2118
+@item ga
2119
+Adjust contribution of input red, green, blue and alpha channels for output green channel.
2120
+Default is @code{1} for @var{gg}, and @code{0} for @var{gr}, @var{gb} and @var{ga}.
2121
+
2122
+@item br
2123
+@item bg
2124
+@item bb
2125
+@item ba
2126
+Adjust contribution of input red, green, blue and alpha channels for output blue channel.
2127
+Default is @code{1} for @var{bb}, and @code{0} for @var{br}, @var{bg} and @var{ba}.
2128
+
2129
+@item ar
2130
+@item ag
2131
+@item ab
2132
+@item aa
2133
+Adjust contribution of input red, green, blue and alpha channels for output alpha channel.
2134
+Default is @code{1} for @var{aa}, and @code{0} for @var{ar}, @var{ag} and @var{ab}.
2135
+
2136
+Allowed ranges for options are @code{[-2.0, 2.0]}.
2137
+@end table
2138
+
2139
+@subsection Examples
2140
+
2141
+@itemize
2142
+@item
2143
+Convert source to grayscale:
2144
+@example
2145
+colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3
2146
+@end example
2147
+@end itemize
2148
+
2094 2149
 @section colormatrix
2095 2150
 
2096 2151
 Convert color matrix.
... ...
@@ -105,6 +105,7 @@ OBJS-$(CONFIG_BLACKFRAME_FILTER)             += vf_blackframe.o
105 105
 OBJS-$(CONFIG_BLEND_FILTER)                  += vf_blend.o
106 106
 OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o
107 107
 OBJS-$(CONFIG_COLORBALANCE_FILTER)           += vf_colorbalance.o
108
+OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
108 109
 OBJS-$(CONFIG_COLORMATRIX_FILTER)            += vf_colormatrix.o
109 110
 OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
110 111
 OBJS-$(CONFIG_CROP_FILTER)                   += vf_crop.o
... ...
@@ -103,6 +103,7 @@ void avfilter_register_all(void)
103 103
     REGISTER_FILTER(BLEND,          blend,          vf);
104 104
     REGISTER_FILTER(BOXBLUR,        boxblur,        vf);
105 105
     REGISTER_FILTER(COLORBALANCE,   colorbalance,   vf);
106
+    REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
106 107
     REGISTER_FILTER(COLORMATRIX,    colormatrix,    vf);
107 108
     REGISTER_FILTER(COPY,           copy,           vf);
108 109
     REGISTER_FILTER(CROP,           crop,           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  57
32
+#define LIBAVFILTER_VERSION_MINOR  58
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,370 @@
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/opt.h"
21
+#include "avfilter.h"
22
+#include "drawutils.h"
23
+#include "formats.h"
24
+#include "internal.h"
25
+#include "video.h"
26
+
27
+#define R 0
28
+#define G 1
29
+#define B 2
30
+#define A 3
31
+
32
+typedef struct {
33
+    const AVClass *class;
34
+    double rr, rg, rb, ra;
35
+    double gr, gg, gb, ga;
36
+    double br, bg, bb, ba;
37
+    double ar, ag, ab, aa;
38
+
39
+    int *lut[4][4];
40
+
41
+    int *buffer;
42
+
43
+    uint8_t rgba_map[4];
44
+} ColorChannelMixerContext;
45
+
46
+#define OFFSET(x) offsetof(ColorChannelMixerContext, x)
47
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
48
+static const AVOption colorchannelmixer_options[] = {
49
+    { "rr", "set the red gain for the red channel",     OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
50
+    { "rg", "set the green gain for the red channel",   OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
51
+    { "rb", "set the blue gain for the red channel",    OFFSET(rb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
52
+    { "ra", "set the alpha gain for the red channel",   OFFSET(ra), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
53
+    { "gr", "set the red gain for the green channel",   OFFSET(gr), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
54
+    { "gg", "set the green gain for the green channel", OFFSET(gg), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
55
+    { "gb", "set the blue gain for the green channel",  OFFSET(gb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
56
+    { "ga", "set the alpha gain for the green channel", OFFSET(ga), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
57
+    { "br", "set the red gain for the blue channel",    OFFSET(br), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
58
+    { "bg", "set the green gain for the blue channel",  OFFSET(bg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
59
+    { "bb", "set the blue gain for the blue channel",   OFFSET(bb), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
60
+    { "ba", "set the alpha gain for the blue channel",  OFFSET(ba), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
61
+    { "ar", "set the red gain for the alpha channel",   OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
62
+    { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
63
+    { "ab", "set the blue gain for the alpha channel",  OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
64
+    { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
65
+    { NULL }
66
+};
67
+
68
+AVFILTER_DEFINE_CLASS(colorchannelmixer);
69
+
70
+static int query_formats(AVFilterContext *ctx)
71
+{
72
+    static const enum AVPixelFormat pix_fmts[] = {
73
+        AV_PIX_FMT_RGB24,  AV_PIX_FMT_BGR24,
74
+        AV_PIX_FMT_RGBA,   AV_PIX_FMT_BGRA,
75
+        AV_PIX_FMT_ARGB,   AV_PIX_FMT_ABGR,
76
+        AV_PIX_FMT_0RGB,   AV_PIX_FMT_0BGR,
77
+        AV_PIX_FMT_RGB0,   AV_PIX_FMT_BGR0,
78
+        AV_PIX_FMT_RGB48,  AV_PIX_FMT_BGR48,
79
+        AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
80
+        AV_PIX_FMT_NONE
81
+    };
82
+
83
+    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
84
+    return 0;
85
+}
86
+
87
+static int config_output(AVFilterLink *outlink)
88
+{
89
+    AVFilterContext *ctx = outlink->src;
90
+    ColorChannelMixerContext *cm = ctx->priv;
91
+    int i, j, size, *buffer;
92
+
93
+    switch (outlink->format) {
94
+    case AV_PIX_FMT_RGB48:
95
+    case AV_PIX_FMT_BGR48:
96
+    case AV_PIX_FMT_RGBA64:
97
+    case AV_PIX_FMT_BGRA64:
98
+        if (outlink->format == AV_PIX_FMT_RGB48 ||
99
+            outlink->format == AV_PIX_FMT_RGBA64) {
100
+            cm->rgba_map[R] = 0;
101
+            cm->rgba_map[G] = 1;
102
+            cm->rgba_map[B] = 2;
103
+            cm->rgba_map[A] = 3;
104
+        } else {
105
+            cm->rgba_map[R] = 2;
106
+            cm->rgba_map[G] = 1;
107
+            cm->rgba_map[B] = 0;
108
+            cm->rgba_map[A] = 3;
109
+        }
110
+        size = 65536;
111
+        break;
112
+    default:
113
+        ff_fill_rgba_map(cm->rgba_map, outlink->format);
114
+        size = 256;
115
+    }
116
+
117
+    cm->buffer = buffer = av_malloc(16 * size * sizeof(*cm->buffer));
118
+    if (!cm->buffer)
119
+        return AVERROR(ENOMEM);
120
+
121
+    for (i = 0; i < 4; i++)
122
+        for (j = 0; j < 4; j++, buffer += size)
123
+            cm->lut[i][j] = buffer;
124
+
125
+    for (i = 0; i < size; i++) {
126
+        cm->lut[R][R][i] = i * cm->rr;
127
+        cm->lut[R][G][i] = i * cm->rg;
128
+        cm->lut[R][B][i] = i * cm->rb;
129
+        cm->lut[R][A][i] = i * cm->ra;
130
+
131
+        cm->lut[G][R][i] = i * cm->gr;
132
+        cm->lut[G][G][i] = i * cm->gg;
133
+        cm->lut[G][B][i] = i * cm->gb;
134
+        cm->lut[G][A][i] = i * cm->ga;
135
+
136
+        cm->lut[B][R][i] = i * cm->br;
137
+        cm->lut[B][G][i] = i * cm->bg;
138
+        cm->lut[B][B][i] = i * cm->bb;
139
+        cm->lut[B][A][i] = i * cm->ba;
140
+
141
+        cm->lut[A][R][i] = i * cm->ar;
142
+        cm->lut[A][G][i] = i * cm->ag;
143
+        cm->lut[A][B][i] = i * cm->ab;
144
+        cm->lut[A][A][i] = i * cm->aa;
145
+    }
146
+
147
+    return 0;
148
+}
149
+
150
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
151
+{
152
+    AVFilterContext *ctx = inlink->dst;
153
+    ColorChannelMixerContext *cm = ctx->priv;
154
+    AVFilterLink *outlink = ctx->outputs[0];
155
+    const uint8_t roffset = cm->rgba_map[R];
156
+    const uint8_t goffset = cm->rgba_map[G];
157
+    const uint8_t boffset = cm->rgba_map[B];
158
+    const uint8_t aoffset = cm->rgba_map[A];
159
+    const uint8_t *srcrow = in->data[0];
160
+    uint8_t *dstrow;
161
+    AVFrame *out;
162
+    int i, j;
163
+
164
+    if (av_frame_is_writable(in)) {
165
+        out = in;
166
+    } else {
167
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
168
+        if (!out) {
169
+            av_frame_free(&in);
170
+            return AVERROR(ENOMEM);
171
+        }
172
+        av_frame_copy_props(out, in);
173
+    }
174
+
175
+    dstrow = out->data[0];
176
+    switch (outlink->format) {
177
+    case AV_PIX_FMT_BGR24:
178
+    case AV_PIX_FMT_RGB24:
179
+        for (i = 0; i < outlink->h; i++) {
180
+            const uint8_t *src = srcrow;
181
+            uint8_t *dst = dstrow;
182
+
183
+            for (j = 0; j < outlink->w * 3; j += 3) {
184
+                const uint8_t rin = src[j + roffset];
185
+                const uint8_t gin = src[j + goffset];
186
+                const uint8_t bin = src[j + boffset];
187
+
188
+                dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
189
+                                                 cm->lut[R][G][gin] +
190
+                                                 cm->lut[R][B][bin]);
191
+                dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
192
+                                                 cm->lut[G][G][gin] +
193
+                                                 cm->lut[G][B][bin]);
194
+                dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
195
+                                                 cm->lut[B][G][gin] +
196
+                                                 cm->lut[B][B][bin]);
197
+            }
198
+
199
+            srcrow += in->linesize[0];
200
+            dstrow += out->linesize[0];
201
+        }
202
+        break;
203
+    case AV_PIX_FMT_0BGR:
204
+    case AV_PIX_FMT_0RGB:
205
+    case AV_PIX_FMT_BGR0:
206
+    case AV_PIX_FMT_RGB0:
207
+        for (i = 0; i < outlink->h; i++) {
208
+            const uint8_t *src = srcrow;
209
+            uint8_t *dst = dstrow;
210
+
211
+            for (j = 0; j < outlink->w * 4; j += 4) {
212
+                const uint8_t rin = src[j + roffset];
213
+                const uint8_t gin = src[j + goffset];
214
+                const uint8_t bin = src[j + boffset];
215
+
216
+                dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
217
+                                                 cm->lut[R][G][gin] +
218
+                                                 cm->lut[R][B][bin]);
219
+                dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
220
+                                                 cm->lut[G][G][gin] +
221
+                                                 cm->lut[G][B][bin]);
222
+                dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
223
+                                                 cm->lut[B][G][gin] +
224
+                                                 cm->lut[B][B][bin]);
225
+                if (in != out)
226
+                    dst[j + aoffset] = 0;
227
+            }
228
+
229
+            srcrow += in->linesize[0];
230
+            dstrow += out->linesize[0];
231
+        }
232
+        break;
233
+    case AV_PIX_FMT_ABGR:
234
+    case AV_PIX_FMT_ARGB:
235
+    case AV_PIX_FMT_BGRA:
236
+    case AV_PIX_FMT_RGBA:
237
+        for (i = 0; i < outlink->h; i++) {
238
+            const uint8_t *src = srcrow;
239
+            uint8_t *dst = dstrow;
240
+
241
+            for (j = 0; j < outlink->w * 4; j += 4) {
242
+                const uint8_t rin = src[j + roffset];
243
+                const uint8_t gin = src[j + goffset];
244
+                const uint8_t bin = src[j + boffset];
245
+                const uint8_t ain = src[j + aoffset];
246
+
247
+                dst[j + roffset] = av_clip_uint8(cm->lut[R][R][rin] +
248
+                                                 cm->lut[R][G][gin] +
249
+                                                 cm->lut[R][B][bin] +
250
+                                                 cm->lut[R][A][ain]);
251
+                dst[j + goffset] = av_clip_uint8(cm->lut[G][R][rin] +
252
+                                                 cm->lut[G][G][gin] +
253
+                                                 cm->lut[G][B][bin] +
254
+                                                 cm->lut[G][A][ain]);
255
+                dst[j + boffset] = av_clip_uint8(cm->lut[B][R][rin] +
256
+                                                 cm->lut[B][G][gin] +
257
+                                                 cm->lut[B][B][bin] +
258
+                                                 cm->lut[B][A][ain]);
259
+                dst[j + aoffset] = av_clip_uint8(cm->lut[A][R][rin] +
260
+                                                 cm->lut[A][G][gin] +
261
+                                                 cm->lut[A][B][bin] +
262
+                                                 cm->lut[A][A][ain]);
263
+            }
264
+
265
+            srcrow += in->linesize[0];
266
+            dstrow += out->linesize[0];
267
+        }
268
+        break;
269
+    case AV_PIX_FMT_BGR48:
270
+    case AV_PIX_FMT_RGB48:
271
+        for (i = 0; i < outlink->h; i++) {
272
+            const uint16_t *src = (const uint16_t *)srcrow;
273
+            uint16_t *dst = (uint16_t *)dstrow;
274
+
275
+            for (j = 0; j < outlink->w * 3; j += 3) {
276
+                const uint16_t rin = src[j + roffset];
277
+                const uint16_t gin = src[j + goffset];
278
+                const uint16_t bin = src[j + boffset];
279
+
280
+                dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
281
+                                                  cm->lut[R][G][gin] +
282
+                                                  cm->lut[R][B][bin]);
283
+                dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
284
+                                                  cm->lut[G][G][gin] +
285
+                                                  cm->lut[G][B][bin]);
286
+                dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
287
+                                                  cm->lut[B][G][gin] +
288
+                                                  cm->lut[B][B][bin]);
289
+            }
290
+
291
+            srcrow += in->linesize[0];
292
+            dstrow += out->linesize[0];
293
+        }
294
+        break;
295
+    case AV_PIX_FMT_BGRA64:
296
+    case AV_PIX_FMT_RGBA64:
297
+        for (i = 0; i < outlink->h; i++) {
298
+            const uint16_t *src = (const uint16_t *)srcrow;
299
+            uint16_t *dst = (uint16_t *)dstrow;
300
+
301
+            for (j = 0; j < outlink->w * 4; j += 4) {
302
+                const uint16_t rin = src[j + roffset];
303
+                const uint16_t gin = src[j + goffset];
304
+                const uint16_t bin = src[j + boffset];
305
+                const uint16_t ain = src[j + aoffset];
306
+
307
+                dst[j + roffset] = av_clip_uint16(cm->lut[R][R][rin] +
308
+                                                  cm->lut[R][G][gin] +
309
+                                                  cm->lut[R][B][bin] +
310
+                                                  cm->lut[R][A][ain]);
311
+                dst[j + goffset] = av_clip_uint16(cm->lut[G][R][rin] +
312
+                                                  cm->lut[G][G][gin] +
313
+                                                  cm->lut[G][B][bin] +
314
+                                                  cm->lut[G][A][ain]);
315
+                dst[j + boffset] = av_clip_uint16(cm->lut[B][R][rin] +
316
+                                                  cm->lut[B][G][gin] +
317
+                                                  cm->lut[B][B][bin] +
318
+                                                  cm->lut[B][A][ain]);
319
+                dst[j + aoffset] = av_clip_uint16(cm->lut[A][R][rin] +
320
+                                                  cm->lut[A][G][gin] +
321
+                                                  cm->lut[A][B][bin] +
322
+                                                  cm->lut[A][A][ain]);
323
+            }
324
+
325
+            srcrow += in->linesize[0];
326
+            dstrow += out->linesize[0];
327
+        }
328
+    }
329
+
330
+    if (in != out)
331
+        av_frame_free(&in);
332
+    return ff_filter_frame(ctx->outputs[0], out);
333
+}
334
+
335
+static av_cold void uninit(AVFilterContext *ctx)
336
+{
337
+    ColorChannelMixerContext *cm = ctx->priv;
338
+
339
+    av_freep(&cm->buffer);
340
+}
341
+
342
+static const AVFilterPad colorchannelmixer_inputs[] = {
343
+    {
344
+        .name           = "default",
345
+        .type           = AVMEDIA_TYPE_VIDEO,
346
+        .filter_frame   = filter_frame,
347
+    },
348
+    { NULL }
349
+};
350
+
351
+static const AVFilterPad colorchannelmixer_outputs[] = {
352
+     {
353
+         .name         = "default",
354
+         .type         = AVMEDIA_TYPE_VIDEO,
355
+         .config_props = config_output,
356
+     },
357
+     { NULL }
358
+};
359
+
360
+AVFilter avfilter_vf_colorchannelmixer = {
361
+    .name          = "colorchannelmixer",
362
+    .description   = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
363
+    .priv_size     = sizeof(ColorChannelMixerContext),
364
+    .priv_class    = &colorchannelmixer_class,
365
+    .uninit        = uninit,
366
+    .query_formats = query_formats,
367
+    .inputs        = colorchannelmixer_inputs,
368
+    .outputs       = colorchannelmixer_outputs,
369
+};