lavfi/hue: allow changing brightness
authorPaul B Mahol <onemda@gmail.com>
Wed, 21 Aug 2013 17:42:32 +0000 (17:42 +0000)
committerPaul B Mahol <onemda@gmail.com>
Thu, 22 Aug 2013 14:56:13 +0000 (14:56 +0000)
Signed-off-by: Paul B Mahol <onemda@gmail.com>
doc/filters.texi
libavfilter/version.h
libavfilter/vf_hue.c

index 19d9784..5d9a2df 100644 (file)
@@ -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
index 4d501b0..20ff257 100644 (file)
@@ -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, \
index d066c40..e5017fb 100644 (file)
@@ -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);