LCOV - code coverage report
Current view: top level - libavfilter - vf_hue.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 110 163 67.5 %
Date: 2017-12-15 02:19:58 Functions: 10 12 83.3 %

          Line data    Source code
       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 <float.h>
      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 HueContext {
      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             :     int is_first;
      77             :     int32_t hue_sin;
      78             :     int32_t hue_cos;
      79             :     double   var_values[VAR_NB];
      80             :     uint8_t  lut_l[256];
      81             :     uint8_t  lut_u[256][256];
      82             :     uint8_t  lut_v[256][256];
      83             : } HueContext;
      84             : 
      85             : #define OFFSET(x) offsetof(HueContext, x)
      86             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      87             : static const AVOption hue_options[] = {
      88             :     { "h", "set the hue angle degrees expression", OFFSET(hue_deg_expr), AV_OPT_TYPE_STRING,
      89             :       { .str = NULL }, .flags = FLAGS },
      90             :     { "s", "set the saturation expression", OFFSET(saturation_expr), AV_OPT_TYPE_STRING,
      91             :       { .str = "1" }, .flags = FLAGS },
      92             :     { "H", "set the hue angle radians expression", OFFSET(hue_expr), AV_OPT_TYPE_STRING,
      93             :       { .str = NULL }, .flags = FLAGS },
      94             :     { "b", "set the brightness expression", OFFSET(brightness_expr), AV_OPT_TYPE_STRING,
      95             :       { .str = "0" }, .flags = FLAGS },
      96             :     { NULL }
      97             : };
      98             : 
      99             : AVFILTER_DEFINE_CLASS(hue);
     100             : 
     101          21 : static inline void compute_sin_and_cos(HueContext *hue)
     102             : {
     103             :     /*
     104             :      * Scale the value to the norm of the resulting (U,V) vector, that is
     105             :      * the saturation.
     106             :      * This will be useful in the apply_lut function.
     107             :      */
     108          21 :     hue->hue_sin = lrint(sin(hue->hue) * (1 << 16) * hue->saturation);
     109          21 :     hue->hue_cos = lrint(cos(hue->hue) * (1 << 16) * hue->saturation);
     110          21 : }
     111             : 
     112           1 : static inline void create_luma_lut(HueContext *h)
     113             : {
     114           1 :     const float b = h->brightness;
     115             :     int i;
     116             : 
     117         257 :     for (i = 0; i < 256; i++) {
     118         256 :         h->lut_l[i] = av_clip_uint8(i + b * 25.5);
     119             :     }
     120           1 : }
     121             : 
     122          20 : static inline void create_chrominance_lut(HueContext *h, const int32_t c,
     123             :                                           const int32_t s)
     124             : {
     125             :     int32_t i, j, u, v, new_u, new_v;
     126             : 
     127             :     /*
     128             :      * If we consider U and V as the components of a 2D vector then its angle
     129             :      * is the hue and the norm is the saturation
     130             :      */
     131        5140 :     for (i = 0; i < 256; i++) {
     132     1315840 :         for (j = 0; j < 256; j++) {
     133             :             /* Normalize the components from range [16;140] to [-112;112] */
     134     1310720 :             u = i - 128;
     135     1310720 :             v = j - 128;
     136             :             /*
     137             :              * Apply the rotation of the vector : (c * u) - (s * v)
     138             :              *                                    (s * u) + (c * v)
     139             :              * De-normalize the components (without forgetting to scale 128
     140             :              * by << 16)
     141             :              * Finally scale back the result by >> 16
     142             :              */
     143     1310720 :             new_u = ((c * u) - (s * v) + (1 << 15) + (128 << 16)) >> 16;
     144     1310720 :             new_v = ((s * u) + (c * v) + (1 << 15) + (128 << 16)) >> 16;
     145             : 
     146             :             /* Prevent a potential overflow */
     147     1310720 :             h->lut_u[i][j] = av_clip_uint8(new_u);
     148     1310720 :             h->lut_v[i][j] = av_clip_uint8(new_v);
     149             :         }
     150             :     }
     151          20 : }
     152             : 
     153           2 : static int set_expr(AVExpr **pexpr_ptr, char **expr_ptr,
     154             :                     const char *expr, const char *option, void *log_ctx)
     155             : {
     156             :     int ret;
     157             :     AVExpr *new_pexpr;
     158             :     char *new_expr;
     159             : 
     160           2 :     new_expr = av_strdup(expr);
     161           2 :     if (!new_expr)
     162           0 :         return AVERROR(ENOMEM);
     163           2 :     ret = av_expr_parse(&new_pexpr, expr, var_names,
     164             :                         NULL, NULL, NULL, NULL, 0, log_ctx);
     165           2 :     if (ret < 0) {
     166           0 :         av_log(log_ctx, AV_LOG_ERROR,
     167             :                "Error when evaluating the expression '%s' for %s\n",
     168             :                expr, option);
     169           0 :         av_free(new_expr);
     170           0 :         return ret;
     171             :     }
     172             : 
     173           2 :     if (*pexpr_ptr)
     174           0 :         av_expr_free(*pexpr_ptr);
     175           2 :     *pexpr_ptr = new_pexpr;
     176           2 :     av_freep(expr_ptr);
     177           2 :     *expr_ptr = new_expr;
     178             : 
     179           2 :     return 0;
     180             : }
     181             : 
     182           1 : static av_cold int init(AVFilterContext *ctx)
     183             : {
     184           1 :     HueContext *hue = ctx->priv;
     185             :     int ret;
     186             : 
     187           1 :     if (hue->hue_expr && hue->hue_deg_expr) {
     188           0 :         av_log(ctx, AV_LOG_ERROR,
     189             :                "H and h options are incompatible and cannot be specified "
     190             :                "at the same time\n");
     191           0 :         return AVERROR(EINVAL);
     192             :     }
     193             : 
     194             : #define SET_EXPR(expr, option)                                          \
     195             :     if (hue->expr##_expr) do {                                          \
     196             :         ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr,           \
     197             :                        hue->expr##_expr, option, ctx);                  \
     198             :         if (ret < 0)                                                    \
     199             :             return ret;                                                 \
     200             :     } while (0)
     201           1 :     SET_EXPR(brightness, "b");
     202           1 :     SET_EXPR(saturation, "s");
     203           1 :     SET_EXPR(hue_deg,    "h");
     204           1 :     SET_EXPR(hue,        "H");
     205             : #undef SET_EXPR
     206             : 
     207           1 :     av_log(ctx, AV_LOG_VERBOSE,
     208             :            "H_expr:%s h_deg_expr:%s s_expr:%s b_expr:%s\n",
     209             :            hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr, hue->brightness_expr);
     210           1 :     compute_sin_and_cos(hue);
     211           1 :     hue->is_first = 1;
     212             : 
     213           1 :     return 0;
     214             : }
     215             : 
     216           1 : static av_cold void uninit(AVFilterContext *ctx)
     217             : {
     218           1 :     HueContext *hue = ctx->priv;
     219             : 
     220           1 :     av_expr_free(hue->brightness_pexpr);
     221           1 :     av_expr_free(hue->hue_deg_pexpr);
     222           1 :     av_expr_free(hue->hue_pexpr);
     223           1 :     av_expr_free(hue->saturation_pexpr);
     224           1 : }
     225             : 
     226           1 : static int query_formats(AVFilterContext *ctx)
     227             : {
     228             :     static const enum AVPixelFormat pix_fmts[] = {
     229             :         AV_PIX_FMT_YUV444P,      AV_PIX_FMT_YUV422P,
     230             :         AV_PIX_FMT_YUV420P,      AV_PIX_FMT_YUV411P,
     231             :         AV_PIX_FMT_YUV410P,      AV_PIX_FMT_YUV440P,
     232             :         AV_PIX_FMT_YUVA444P,     AV_PIX_FMT_YUVA422P,
     233             :         AV_PIX_FMT_YUVA420P,
     234             :         AV_PIX_FMT_NONE
     235             :     };
     236           1 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
     237           1 :     if (!fmts_list)
     238           0 :         return AVERROR(ENOMEM);
     239           1 :     return ff_set_common_formats(ctx, fmts_list);
     240             : }
     241             : 
     242           1 : static int config_props(AVFilterLink *inlink)
     243             : {
     244           1 :     HueContext *hue = inlink->dst->priv;
     245           1 :     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     246             : 
     247           1 :     hue->hsub = desc->log2_chroma_w;
     248           1 :     hue->vsub = desc->log2_chroma_h;
     249             : 
     250           1 :     hue->var_values[VAR_N]  = 0;
     251           1 :     hue->var_values[VAR_TB] = av_q2d(inlink->time_base);
     252           3 :     hue->var_values[VAR_R]  = inlink->frame_rate.num == 0 || inlink->frame_rate.den == 0 ?
     253           2 :         NAN : av_q2d(inlink->frame_rate);
     254             : 
     255           1 :     return 0;
     256             : }
     257             : 
     258           0 : static void apply_luma_lut(HueContext *s,
     259             :                            uint8_t *ldst, const int dst_linesize,
     260             :                            uint8_t *lsrc, const int src_linesize,
     261             :                            int w, int h)
     262             : {
     263             :     int i;
     264             : 
     265           0 :     while (h--) {
     266           0 :         for (i = 0; i < w; i++)
     267           0 :             ldst[i] = s->lut_l[lsrc[i]];
     268             : 
     269           0 :         lsrc += src_linesize;
     270           0 :         ldst += dst_linesize;
     271             :     }
     272           0 : }
     273             : 
     274          20 : static void apply_lut(HueContext *s,
     275             :                       uint8_t *udst, uint8_t *vdst, const int dst_linesize,
     276             :                       uint8_t *usrc, uint8_t *vsrc, const int src_linesize,
     277             :                       int w, int h)
     278             : {
     279             :     int i;
     280             : 
     281        2920 :     while (h--) {
     282      509760 :         for (i = 0; i < w; i++) {
     283      506880 :             const int u = usrc[i];
     284      506880 :             const int v = vsrc[i];
     285             : 
     286      506880 :             udst[i] = s->lut_u[u][v];
     287      506880 :             vdst[i] = s->lut_v[u][v];
     288             :         }
     289             : 
     290        2880 :         usrc += src_linesize;
     291        2880 :         vsrc += src_linesize;
     292        2880 :         udst += dst_linesize;
     293        2880 :         vdst += dst_linesize;
     294             :     }
     295          20 : }
     296             : 
     297             : #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
     298             : #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts) * av_q2d(tb))
     299             : 
     300          20 : static int filter_frame(AVFilterLink *inlink, AVFrame *inpic)
     301             : {
     302          20 :     HueContext *hue = inlink->dst->priv;
     303          20 :     AVFilterLink *outlink = inlink->dst->outputs[0];
     304             :     AVFrame *outpic;
     305          20 :     const int32_t old_hue_sin = hue->hue_sin, old_hue_cos = hue->hue_cos;
     306          20 :     const float old_brightness = hue->brightness;
     307          20 :     int direct = 0;
     308             : 
     309          20 :     if (av_frame_is_writable(inpic)) {
     310          20 :         direct = 1;
     311          20 :         outpic = inpic;
     312             :     } else {
     313           0 :         outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     314           0 :         if (!outpic) {
     315           0 :             av_frame_free(&inpic);
     316           0 :             return AVERROR(ENOMEM);
     317             :         }
     318           0 :         av_frame_copy_props(outpic, inpic);
     319             :     }
     320             : 
     321          20 :     hue->var_values[VAR_N]   = inlink->frame_count_out;
     322          20 :     hue->var_values[VAR_T]   = TS2T(inpic->pts, inlink->time_base);
     323          20 :     hue->var_values[VAR_PTS] = TS2D(inpic->pts);
     324             : 
     325          20 :     if (hue->saturation_expr) {
     326          20 :         hue->saturation = av_expr_eval(hue->saturation_pexpr, hue->var_values, NULL);
     327             : 
     328          20 :         if (hue->saturation < SAT_MIN_VAL || hue->saturation > SAT_MAX_VAL) {
     329           0 :             hue->saturation = av_clip(hue->saturation, SAT_MIN_VAL, SAT_MAX_VAL);
     330           0 :             av_log(inlink->dst, AV_LOG_WARNING,
     331             :                    "Saturation value not in range [%d,%d]: clipping value to %0.1f\n",
     332           0 :                    SAT_MIN_VAL, SAT_MAX_VAL, hue->saturation);
     333             :         }
     334             :     }
     335             : 
     336          20 :     if (hue->brightness_expr) {
     337          20 :         hue->brightness = av_expr_eval(hue->brightness_pexpr, hue->var_values, NULL);
     338             : 
     339          20 :         if (hue->brightness < -10 || hue->brightness > 10) {
     340           0 :             hue->brightness = av_clipf(hue->brightness, -10, 10);
     341           0 :             av_log(inlink->dst, AV_LOG_WARNING,
     342             :                    "Brightness value not in range [%d,%d]: clipping value to %0.1f\n",
     343           0 :                    -10, 10, hue->brightness);
     344             :         }
     345             :     }
     346             : 
     347          20 :     if (hue->hue_deg_expr) {
     348           0 :         hue->hue_deg = av_expr_eval(hue->hue_deg_pexpr, hue->var_values, NULL);
     349           0 :         hue->hue = hue->hue_deg * M_PI / 180;
     350          20 :     } else if (hue->hue_expr) {
     351           0 :         hue->hue = av_expr_eval(hue->hue_pexpr, hue->var_values, NULL);
     352           0 :         hue->hue_deg = hue->hue * 180 / M_PI;
     353             :     }
     354             : 
     355         100 :     av_log(inlink->dst, AV_LOG_DEBUG,
     356             :            "H:%0.1f*PI h:%0.1f s:%0.1f b:%0.f t:%0.1f n:%d\n",
     357          80 :            hue->hue/M_PI, hue->hue_deg, hue->saturation, hue->brightness,
     358          20 :            hue->var_values[VAR_T], (int)hue->var_values[VAR_N]);
     359             : 
     360          20 :     compute_sin_and_cos(hue);
     361          20 :     if (hue->is_first || (old_hue_sin != hue->hue_sin || old_hue_cos != hue->hue_cos))
     362          20 :         create_chrominance_lut(hue, hue->hue_cos, hue->hue_sin);
     363             : 
     364          20 :     if (hue->is_first || (old_brightness != hue->brightness && hue->brightness))
     365           1 :         create_luma_lut(hue);
     366             : 
     367          20 :     if (!direct) {
     368           0 :         if (!hue->brightness)
     369           0 :             av_image_copy_plane(outpic->data[0], outpic->linesize[0],
     370           0 :                                 inpic->data[0],  inpic->linesize[0],
     371             :                                 inlink->w, inlink->h);
     372           0 :         if (inpic->data[3])
     373           0 :             av_image_copy_plane(outpic->data[3], outpic->linesize[3],
     374           0 :                                 inpic->data[3],  inpic->linesize[3],
     375             :                                 inlink->w, inlink->h);
     376             :     }
     377             : 
     378         100 :     apply_lut(hue, outpic->data[1], outpic->data[2], outpic->linesize[1],
     379          60 :               inpic->data[1],  inpic->data[2],  inpic->linesize[1],
     380          20 :               AV_CEIL_RSHIFT(inlink->w, hue->hsub),
     381          20 :               AV_CEIL_RSHIFT(inlink->h, hue->vsub));
     382          20 :     if (hue->brightness)
     383           0 :         apply_luma_lut(hue, outpic->data[0], outpic->linesize[0],
     384           0 :                        inpic->data[0], inpic->linesize[0], inlink->w, inlink->h);
     385             : 
     386          20 :     if (!direct)
     387           0 :         av_frame_free(&inpic);
     388             : 
     389          20 :     hue->is_first = 0;
     390          20 :     return ff_filter_frame(outlink, outpic);
     391             : }
     392             : 
     393           0 : static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
     394             :                            char *res, int res_len, int flags)
     395             : {
     396           0 :     HueContext *hue = ctx->priv;
     397             :     int ret;
     398             : 
     399             : #define SET_EXPR(expr, option)                                          \
     400             :     do {                                                                \
     401             :         ret = set_expr(&hue->expr##_pexpr, &hue->expr##_expr,           \
     402             :                        args, option, ctx);                              \
     403             :         if (ret < 0)                                                    \
     404             :             return ret;                                                 \
     405             :     } while (0)
     406             : 
     407           0 :     if (!strcmp(cmd, "h")) {
     408           0 :         SET_EXPR(hue_deg, "h");
     409           0 :         av_freep(&hue->hue_expr);
     410           0 :     } else if (!strcmp(cmd, "H")) {
     411           0 :         SET_EXPR(hue, "H");
     412           0 :         av_freep(&hue->hue_deg_expr);
     413           0 :     } else if (!strcmp(cmd, "s")) {
     414           0 :         SET_EXPR(saturation, "s");
     415           0 :     } else if (!strcmp(cmd, "b")) {
     416           0 :         SET_EXPR(brightness, "b");
     417             :     } else
     418           0 :         return AVERROR(ENOSYS);
     419             : 
     420           0 :     return 0;
     421             : }
     422             : 
     423             : static const AVFilterPad hue_inputs[] = {
     424             :     {
     425             :         .name         = "default",
     426             :         .type         = AVMEDIA_TYPE_VIDEO,
     427             :         .filter_frame = filter_frame,
     428             :         .config_props = config_props,
     429             :     },
     430             :     { NULL }
     431             : };
     432             : 
     433             : static const AVFilterPad hue_outputs[] = {
     434             :     {
     435             :         .name = "default",
     436             :         .type = AVMEDIA_TYPE_VIDEO,
     437             :     },
     438             :     { NULL }
     439             : };
     440             : 
     441             : AVFilter ff_vf_hue = {
     442             :     .name            = "hue",
     443             :     .description     = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."),
     444             :     .priv_size       = sizeof(HueContext),
     445             :     .init            = init,
     446             :     .uninit          = uninit,
     447             :     .query_formats   = query_formats,
     448             :     .process_command = process_command,
     449             :     .inputs          = hue_inputs,
     450             :     .outputs         = hue_outputs,
     451             :     .priv_class      = &hue_class,
     452             :     .flags           = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
     453             : };

Generated by: LCOV version 1.13