Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * Copyright (c) 2003 Michael Niedermayer
3
 * Copyright (c) 2012 Jeremy Tran
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
 
22
/**
23
 * @file
24
 * Apply a hue/saturation filter to the input video
25
 * Ported from MPlayer libmpcodecs/vf_hue.c.
26
 */
27
 
28
#include 
29
#include "libavutil/eval.h"
30
#include "libavutil/imgutils.h"
31
#include "libavutil/opt.h"
32
#include "libavutil/pixdesc.h"
33
 
34
#include "avfilter.h"
35
#include "formats.h"
36
#include "internal.h"
37
#include "video.h"
38
 
39
#define SAT_MIN_VAL -10
40
#define SAT_MAX_VAL 10
41
 
42
static const char *const var_names[] = {
43
    "n",   // frame count
44
    "pts", // presentation timestamp expressed in AV_TIME_BASE units
45
    "r",   // frame rate
46
    "t",   // timestamp expressed in seconds
47
    "tb",  // timebase
48
    NULL
49
};
50
 
51
enum var_name {
52
    VAR_N,
53
    VAR_PTS,
54
    VAR_R,
55
    VAR_T,
56
    VAR_TB,
57
    VAR_NB
58
};
59
 
60
typedef struct {
61
    const    AVClass *class;
62
    float    hue_deg; /* hue expressed in degrees */
63
    float    hue; /* hue expressed in radians */
64
    char     *hue_deg_expr;
65
    char     *hue_expr;
66
    AVExpr   *hue_deg_pexpr;
67
    AVExpr   *hue_pexpr;
68
    float    saturation;
69
    char     *saturation_expr;
70
    AVExpr   *saturation_pexpr;
71
    float    brightness;
72
    char     *brightness_expr;
73
    AVExpr   *brightness_pexpr;
74
    int      hsub;
75
    int      vsub;
76
    int32_t hue_sin;
77
    int32_t hue_cos;
78
    double   var_values[VAR_NB];
79
    uint8_t  lut_l[256];
80
    uint8_t  lut_u[256][256];
81
    uint8_t  lut_v[256][256];
82
} HueContext;
83
 
84
#define OFFSET(x) offsetof(HueContext, x)
85
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
86
static const AVOption hue_options[] = {
87
    { "h", "set the hue angle degrees expression", OFFSET(hue_deg_expr), AV_OPT_TYPE_STRING,
88
      { .str = NULL }, .flags = FLAGS },
89
    { "s", "set the saturation expression", OFFSET(saturation_expr), AV_OPT_TYPE_STRING,
90
      { .str = "1" }, .flags = FLAGS },
91
    { "H", "set the hue angle radians expression", OFFSET(hue_expr), AV_OPT_TYPE_STRING,
92
      { .str = NULL }, .flags = FLAGS },
93
    { "b", "set the brightness expression", OFFSET(brightness_expr), AV_OPT_TYPE_STRING,
94
      { .str = "0" }, .flags = FLAGS },
95
    { NULL }
96
};
97
 
98
AVFILTER_DEFINE_CLASS(hue);
99
 
100
static inline void compute_sin_and_cos(HueContext *hue)
101
{
102
    /*
103
     * Scale the value to the norm of the resulting (U,V) vector, that is
104
     * the saturation.
105
     * This will be useful in the apply_lut function.
106
     */
107
    hue->hue_sin = rint(sin(hue->hue) * (1 << 16) * hue->saturation);
108
    hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation);
109
}
110
 
111
static inline void create_luma_lut(HueContext *h)
112
{
113
    const float b = h->brightness;
114
    int i;
115
 
116
    for (i = 0; i < 256; i++) {
117
        h->lut_l[i] = av_clip_uint8(i + b * 25.5);
118
    }
119
}
120
 
121
static inline void create_chrominance_lut(HueContext *h, const int32_t c,
122
                                          const int32_t s)
123
{
124
    int32_t i, j, u, v, new_u, new_v;
125
 
126
    /*
127
     * If we consider U and V as the components of a 2D vector then its angle
128
     * is the hue and the norm is the saturation
129
     */
130
    for (i = 0; i < 256; i++) {
131
        for (j = 0; j < 256; j++) {
132
            /* Normalize the components from range [16;140] to [-112;112] */
133
            u = i - 128;
134
            v = j - 128;
135
            /*
136
             * Apply the rotation of the vector : (c * u) - (s * v)
137
             *                                    (s * u) + (c * v)
138
             * De-normalize the components (without forgetting to scale 128
139
             * by << 16)
140
             * Finally scale back the result by >> 16
141
             */
142
            new_u = ((c * u) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
143
            new_v = ((s * u) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
144
 
145
            /* Prevent a potential overflow */
146
            h->lut_u[i][j] = av_clip_uint8_c(new_u);
147
            h->lut_v[i][j] = av_clip_uint8_c(new_v);
148
        }
149
    }
150
}
151
 
152
static int set_expr(AVExpr **pexpr_ptr, char **expr_ptr,
153
                    const char *expr, const char *option, void *log_ctx)
154
{
155
    int ret;
156
    AVExpr *new_pexpr;
157
    char *new_expr;
158
 
159
    new_expr = av_strdup(expr);
160
    if (!new_expr)
161
        return AVERROR(ENOMEM);
162
    ret = av_expr_parse(&new_pexpr, expr, var_names,
163
                        NULL, NULL, NULL, NULL, 0, log_ctx);
164
    if (ret < 0) {
165
        av_log(log_ctx, AV_LOG_ERROR,
166
               "Error when evaluating the expression '%s' for %s\n",
167
               expr, option);
168
        av_free(new_expr);
169
        return ret;
170
    }
171
 
172
    if (*pexpr_ptr)
173
        av_expr_free(*pexpr_ptr);
174
    *pexpr_ptr = new_pexpr;
175
    av_freep(expr_ptr);
176
    *expr_ptr = new_expr;
177
 
178
    return 0;
179
}
180
 
181
static av_cold int init(AVFilterContext *ctx)
182
{
183
    HueContext *hue = ctx->priv;
184
    int ret;
185
 
186
    if (hue->hue_expr && hue->hue_deg_expr) {
187
        av_log(ctx, AV_LOG_ERROR,
188
               "H and h options are incompatible and cannot be specified "
189
               "at the same time\n");
190
        return AVERROR(EINVAL);
191
    }
192
 
193
#define SET_EXPR(expr, option)                                          \
194
    if (hue->expr##_expr) do {                                          \
195
        ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr,           \
196
                       hue->expr##_expr, option, ctx);                  \
197
        if (ret < 0)                                                    \
198
            return ret;                                                 \
199
    } while (0)
200
    SET_EXPR(brightness, "b");
201
    SET_EXPR(saturation, "s");
202
    SET_EXPR(hue_deg,    "h");
203
    SET_EXPR(hue,        "H");
204
#undef SET_EXPR
205
 
206
    av_log(ctx, AV_LOG_VERBOSE,
207
           "H_expr:%s h_deg_expr:%s s_expr:%s b_expr:%s\n",
208
           hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr, hue->brightness_expr);
209
    compute_sin_and_cos(hue);
210
 
211
    return 0;
212
}
213
 
214
static av_cold void uninit(AVFilterContext *ctx)
215
{
216
    HueContext *hue = ctx->priv;
217
 
218
    av_expr_free(hue->brightness_pexpr);
219
    av_expr_free(hue->hue_deg_pexpr);
220
    av_expr_free(hue->hue_pexpr);
221
    av_expr_free(hue->saturation_pexpr);
222
}
223
 
224
static int query_formats(AVFilterContext *ctx)
225
{
226
    static const enum AVPixelFormat pix_fmts[] = {
227
        AV_PIX_FMT_YUV444P,      AV_PIX_FMT_YUV422P,
228
        AV_PIX_FMT_YUV420P,      AV_PIX_FMT_YUV411P,
229
        AV_PIX_FMT_YUV410P,      AV_PIX_FMT_YUV440P,
230
        AV_PIX_FMT_YUVA444P,     AV_PIX_FMT_YUVA422P,
231
        AV_PIX_FMT_YUVA420P,
232
        AV_PIX_FMT_NONE
233
    };
234
 
235
    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
236
 
237
    return 0;
238
}
239
 
240
static int config_props(AVFilterLink *inlink)
241
{
242
    HueContext *hue = inlink->dst->priv;
243
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
244
 
245
    hue->hsub = desc->log2_chroma_w;
246
    hue->vsub = desc->log2_chroma_h;
247
 
248
    hue->var_values[VAR_N]  = 0;
249
    hue->var_values[VAR_TB] = av_q2d(inlink->time_base);
250
    hue->var_values[VAR_R]  = inlink->frame_rate.num == 0 || inlink->frame_rate.den == 0 ?
251
        NAN : av_q2d(inlink->frame_rate);
252
 
253
    return 0;
254
}
255
 
256
static void apply_luma_lut(HueContext *s,
257
                           uint8_t *ldst, const int dst_linesize,
258
                           uint8_t *lsrc, const int src_linesize,
259
                           int w, int h)
260
{
261
    int i;
262
 
263
    while (h--) {
264
        for (i = 0; i < w; i++)
265
            ldst[i] = s->lut_l[lsrc[i]];
266
 
267
        lsrc += src_linesize;
268
        ldst += dst_linesize;
269
    }
270
}
271
 
272
static void apply_lut(HueContext *s,
273
                      uint8_t *udst, uint8_t *vdst, const int dst_linesize,
274
                      uint8_t *usrc, uint8_t *vsrc, const int src_linesize,
275
                      int w, int h)
276
{
277
    int i;
278
 
279
    while (h--) {
280
        for (i = 0; i < w; i++) {
281
            const int u = usrc[i];
282
            const int v = vsrc[i];
283
 
284
            udst[i] = s->lut_u[u][v];
285
            vdst[i] = s->lut_v[u][v];
286
        }
287
 
288
        usrc += src_linesize;
289
        vsrc += src_linesize;
290
        udst += dst_linesize;
291
        vdst += dst_linesize;
292
    }
293
}
294
 
295
#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
296
#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
297
 
298
static int filter_frame(AVFilterLink *inlink, AVFrame *inpic)
299
{
300
    HueContext *hue = inlink->dst->priv;
301
    AVFilterLink *outlink = inlink->dst->outputs[0];
302
    AVFrame *outpic;
303
    const int32_t old_hue_sin = hue->hue_sin, old_hue_cos = hue->hue_cos;
304
    const float old_brightness = hue->brightness;
305
    int direct = 0;
306
 
307
    if (av_frame_is_writable(inpic)) {
308
        direct = 1;
309
        outpic = inpic;
310
    } else {
311
        outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h);
312
        if (!outpic) {
313
            av_frame_free(&inpic);
314
            return AVERROR(ENOMEM);
315
        }
316
        av_frame_copy_props(outpic, inpic);
317
    }
318
 
319
    hue->var_values[VAR_N]   = inlink->frame_count;
320
    hue->var_values[VAR_T]   = TS2T(inpic->pts, inlink->time_base);
321
    hue->var_values[VAR_PTS] = TS2D(inpic->pts);
322
 
323
    if (hue->saturation_expr) {
324
        hue->saturation = av_expr_eval(hue->saturation_pexpr, hue->var_values, NULL);
325
 
326
        if (hue->saturation < SAT_MIN_VAL || hue->saturation > SAT_MAX_VAL) {
327
            hue->saturation = av_clip(hue->saturation, SAT_MIN_VAL, SAT_MAX_VAL);
328
            av_log(inlink->dst, AV_LOG_WARNING,
329
                   "Saturation value not in range [%d,%d]: clipping value to %0.1f\n",
330
                   SAT_MIN_VAL, SAT_MAX_VAL, hue->saturation);
331
        }
332
    }
333
 
334
    if (hue->brightness_expr) {
335
        hue->brightness = av_expr_eval(hue->brightness_pexpr, hue->var_values, NULL);
336
 
337
        if (hue->brightness < -10 || hue->brightness > 10) {
338
            hue->brightness = av_clipf(hue->brightness, -10, 10);
339
            av_log(inlink->dst, AV_LOG_WARNING,
340
                   "Brightness value not in range [%d,%d]: clipping value to %0.1f\n",
341
                   -10, 10, hue->brightness);
342
        }
343
    }
344
 
345
    if (hue->hue_deg_expr) {
346
        hue->hue_deg = av_expr_eval(hue->hue_deg_pexpr, hue->var_values, NULL);
347
        hue->hue = hue->hue_deg * M_PI / 180;
348
    } else if (hue->hue_expr) {
349
        hue->hue = av_expr_eval(hue->hue_pexpr, hue->var_values, NULL);
350
        hue->hue_deg = hue->hue * 180 / M_PI;
351
    }
352
 
353
    av_log(inlink->dst, AV_LOG_DEBUG,
354
           "H:%0.1f*PI h:%0.1f s:%0.f b:%0.f t:%0.1f n:%d\n",
355
           hue->hue/M_PI, hue->hue_deg, hue->saturation, hue->brightness,
356
           hue->var_values[VAR_T], (int)hue->var_values[VAR_N]);
357
 
358
    compute_sin_and_cos(hue);
359
    if (old_hue_sin != hue->hue_sin || old_hue_cos != hue->hue_cos)
360
        create_chrominance_lut(hue, hue->hue_cos, hue->hue_sin);
361
 
362
    if (old_brightness != hue->brightness && hue->brightness)
363
        create_luma_lut(hue);
364
 
365
    if (!direct) {
366
        if (!hue->brightness)
367
            av_image_copy_plane(outpic->data[0], outpic->linesize[0],
368
                                inpic->data[0],  inpic->linesize[0],
369
                                inlink->w, inlink->h);
370
        if (inpic->data[3])
371
            av_image_copy_plane(outpic->data[3], outpic->linesize[3],
372
                                inpic->data[3],  inpic->linesize[3],
373
                                inlink->w, inlink->h);
374
    }
375
 
376
    apply_lut(hue, outpic->data[1], outpic->data[2], outpic->linesize[1],
377
              inpic->data[1],  inpic->data[2],  inpic->linesize[1],
378
              FF_CEIL_RSHIFT(inlink->w, hue->hsub),
379
              FF_CEIL_RSHIFT(inlink->h, hue->vsub));
380
    if (hue->brightness)
381
        apply_luma_lut(hue, outpic->data[0], outpic->linesize[0],
382
                       inpic->data[0], inpic->linesize[0], inlink->w, inlink->h);
383
 
384
    if (!direct)
385
        av_frame_free(&inpic);
386
    return ff_filter_frame(outlink, outpic);
387
}
388
 
389
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
390
                           char *res, int res_len, int flags)
391
{
392
    HueContext *hue = ctx->priv;
393
    int ret;
394
 
395
#define SET_EXPR(expr, option)                                          \
396
    do {                                                                \
397
        ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr,           \
398
                       args, option, ctx);                              \
399
        if (ret < 0)                                                    \
400
            return ret;                                                 \
401
    } while (0)
402
 
403
    if (!strcmp(cmd, "h")) {
404
        SET_EXPR(hue_deg, "h");
405
        av_freep(&hue->hue_expr);
406
    } else if (!strcmp(cmd, "H")) {
407
        SET_EXPR(hue, "H");
408
        av_freep(&hue->hue_deg_expr);
409
    } else if (!strcmp(cmd, "s")) {
410
        SET_EXPR(saturation, "s");
411
    } else if (!strcmp(cmd, "b")) {
412
        SET_EXPR(brightness, "b");
413
    } else
414
        return AVERROR(ENOSYS);
415
 
416
    return 0;
417
}
418
 
419
static const AVFilterPad hue_inputs[] = {
420
    {
421
        .name         = "default",
422
        .type         = AVMEDIA_TYPE_VIDEO,
423
        .filter_frame = filter_frame,
424
        .config_props = config_props,
425
    },
426
    { NULL }
427
};
428
 
429
static const AVFilterPad hue_outputs[] = {
430
    {
431
        .name = "default",
432
        .type = AVMEDIA_TYPE_VIDEO,
433
    },
434
    { NULL }
435
};
436
 
437
AVFilter avfilter_vf_hue = {
438
    .name            = "hue",
439
    .description     = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
440
    .priv_size       = sizeof(HueContext),
441
    .init            = init,
442
    .uninit          = uninit,
443
    .query_formats   = query_formats,
444
    .process_command = process_command,
445
    .inputs          = hue_inputs,
446
    .outputs         = hue_outputs,
447
    .priv_class      = &hue_class,
448
    .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
449
};