From b1e276f8df257a7ad190419b349d0d86d104c8ae Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Wed, 21 Aug 2013 17:42:32 +0000 Subject: [PATCH] lavfi/hue: allow changing brightness Signed-off-by: Paul B Mahol --- doc/filters.texi | 9 ++++-- libavfilter/version.h | 2 +- libavfilter/vf_hue.c | 69 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 19d9784277..5d9a2df264 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -4715,12 +4715,16 @@ defaults to "1". @item H Specify the hue angle as a number of radians. It accepts an expression, and defaults to "0". + +@item b +Specify the brightness in the [-10,10] range. It accepts an expression and +defaults to "0". @end table @option{h} and @option{H} are mutually exclusive, and can't be specified at the same time. -The @option{h}, @option{H} and @option{s} option values are +The @option{b}, @option{h}, @option{H} and @option{s} option values are expressions containing the following constants: @table @option @@ -4790,10 +4794,11 @@ hue="s=max(0\, min(1\, (START+DURATION-t)/DURATION))" This filter supports the following commands: @table @option +@item b @item s @item h @item H -Modify the hue and/or the saturation of the input video. +Modify the hue and/or the saturation and/or brightness of the input video. The command accepts the same syntax of the corresponding option. If the specified expression is not valid, it is kept at its current diff --git a/libavfilter/version.h b/libavfilter/version.h index 4d501b0825..20ff257693 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -31,7 +31,7 @@ #define LIBAVFILTER_VERSION_MAJOR 3 #define LIBAVFILTER_VERSION_MINOR 82 -#define LIBAVFILTER_VERSION_MICRO 101 +#define LIBAVFILTER_VERSION_MICRO 102 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/vf_hue.c b/libavfilter/vf_hue.c index d066c4075f..e5017fbb62 100644 --- a/libavfilter/vf_hue.c +++ b/libavfilter/vf_hue.c @@ -68,11 +68,15 @@ typedef struct { float saturation; char *saturation_expr; AVExpr *saturation_pexpr; + float brightness; + char *brightness_expr; + AVExpr *brightness_pexpr; int hsub; int vsub; int32_t hue_sin; int32_t hue_cos; double var_values[VAR_NB]; + uint8_t lut_l[256]; uint8_t lut_u[256][256]; uint8_t lut_v[256][256]; } HueContext; @@ -86,6 +90,8 @@ static const AVOption hue_options[] = { { .str = "1" }, .flags = FLAGS }, { "H", "set the hue angle radians expression", OFFSET(hue_expr), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "b", "set the brightness expression", OFFSET(brightness_expr), AV_OPT_TYPE_STRING, + { .str = "0" }, .flags = FLAGS }, { NULL } }; @@ -102,6 +108,16 @@ static inline void compute_sin_and_cos(HueContext *hue) hue->hue_cos = rint(cos(hue->hue) * (1 << 16) * hue->saturation); } +static inline void create_luma_lut(HueContext *h) +{ + const float b = h->brightness; + int i; + + for (i = 0; i < 256; i++) { + h->lut_l[i] = av_clip_uint8(i + b * 25.5); + } +} + static inline void create_chrominance_lut(HueContext *h, const int32_t c, const int32_t s) { @@ -181,14 +197,15 @@ static av_cold int init(AVFilterContext *ctx) if (ret < 0) \ return ret; \ } while (0) + SET_EXPR(brightness, "b"); SET_EXPR(saturation, "s"); SET_EXPR(hue_deg, "h"); SET_EXPR(hue, "H"); #undef SET_EXPR av_log(ctx, AV_LOG_VERBOSE, - "H_expr:%s h_deg_expr:%s s_expr:%s\n", - hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr); + "H_expr:%s h_deg_expr:%s s_expr:%s b_expr:%s\n", + hue->hue_expr, hue->hue_deg_expr, hue->saturation_expr, hue->brightness_expr); compute_sin_and_cos(hue); return 0; @@ -198,6 +215,7 @@ static av_cold void uninit(AVFilterContext *ctx) { HueContext *hue = ctx->priv; + av_expr_free(hue->brightness_pexpr); av_expr_free(hue->hue_deg_pexpr); av_expr_free(hue->hue_pexpr); av_expr_free(hue->saturation_pexpr); @@ -235,6 +253,22 @@ static int config_props(AVFilterLink *inlink) return 0; } +static void apply_luma_lut(HueContext *s, + uint8_t *ldst, const int dst_linesize, + uint8_t *lsrc, const int src_linesize, + int w, int h) +{ + int i; + + while (h--) { + for (i = 0; i < w; i++) + ldst[i] = s->lut_l[lsrc[i]]; + + lsrc += src_linesize; + ldst += dst_linesize; + } +} + static void apply_lut(HueContext *s, uint8_t *udst, uint8_t *vdst, const int dst_linesize, uint8_t *usrc, uint8_t *vsrc, const int src_linesize, @@ -267,6 +301,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) AVFilterLink *outlink = inlink->dst->outputs[0]; AVFrame *outpic; const int32_t old_hue_sin = hue->hue_sin, old_hue_cos = hue->hue_cos; + const float old_brightness = hue->brightness; int direct = 0; if (av_frame_is_writable(inpic)) { @@ -296,6 +331,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) } } + if (hue->brightness_expr) { + hue->brightness = av_expr_eval(hue->brightness_pexpr, hue->var_values, NULL); + + if (hue->brightness < -10 || hue->brightness > 10) { + hue->brightness = av_clipf(hue->brightness, -10, 10); + av_log(inlink->dst, AV_LOG_WARNING, + "Brightness value not in range [%d,%d]: clipping value to %0.1f\n", + -10, 10, hue->brightness); + } + } + if (hue->hue_deg_expr) { hue->hue_deg = av_expr_eval(hue->hue_deg_pexpr, hue->var_values, NULL); hue->hue = hue->hue_deg * M_PI / 180; @@ -305,18 +351,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) } av_log(inlink->dst, AV_LOG_DEBUG, - "H:%0.1f*PI h:%0.1f s:%0.f t:%0.1f n:%d\n", - hue->hue/M_PI, hue->hue_deg, hue->saturation, + "H:%0.1f*PI h:%0.1f s:%0.f b:%0.f t:%0.1f n:%d\n", + hue->hue/M_PI, hue->hue_deg, hue->saturation, hue->brightness, hue->var_values[VAR_T], (int)hue->var_values[VAR_N]); compute_sin_and_cos(hue); if (old_hue_sin != hue->hue_sin || old_hue_cos != hue->hue_cos) create_chrominance_lut(hue, hue->hue_cos, hue->hue_sin); + if (old_brightness != hue->brightness && hue->brightness) + create_luma_lut(hue); + if (!direct) { - av_image_copy_plane(outpic->data[0], outpic->linesize[0], - inpic->data[0], inpic->linesize[0], - inlink->w, inlink->h); + if (!hue->brightness) + av_image_copy_plane(outpic->data[0], outpic->linesize[0], + inpic->data[0], inpic->linesize[0], + inlink->w, inlink->h); if (inpic->data[3]) av_image_copy_plane(outpic->data[3], outpic->linesize[3], inpic->data[3], inpic->linesize[3], @@ -327,6 +377,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) inpic->data[1], inpic->data[2], inpic->linesize[1], FF_CEIL_RSHIFT(inlink->w, hue->hsub), FF_CEIL_RSHIFT(inlink->h, hue->vsub)); + if (hue->brightness) + apply_luma_lut(hue, outpic->data[0], outpic->linesize[0], + inpic->data[0], inpic->linesize[0], inlink->w, inlink->h); if (!direct) av_frame_free(&inpic); @@ -355,6 +408,8 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar av_freep(&hue->hue_deg_expr); } else if (!strcmp(cmd, "s")) { SET_EXPR(saturation, "s"); + } else if (!strcmp(cmd, "b")) { + SET_EXPR(brightness, "b"); } else return AVERROR(ENOSYS);