]> git.sesse.net Git - mlt/commitdiff
Add lift_gamma_gain filter.
authorBrian Matherly <pez4brian@yahoo.com>
Tue, 25 Mar 2014 21:54:56 +0000 (16:54 -0500)
committerBrian Matherly <pez4brian@yahoo.com>
Fri, 28 Mar 2014 03:55:39 +0000 (22:55 -0500)
This filter is equivalent to the movit.lift_gamma_gain filter but does not depend on opengl/movit.

src/modules/plus/Makefile
src/modules/plus/factory.c
src/modules/plus/filter_lift_gamma_gain.c [new file with mode: 0644]
src/modules/plus/filter_lift_gamma_gain.yml [new file with mode: 0644]

index c96f4b99d25c0d40114988d98371590172edca95..012e038a5c4d785fe626f12c08324f1092081948 100644 (file)
@@ -12,6 +12,7 @@ OBJS = consumer_blipflash.o \
           filter_charcoal.o \
           filter_dynamictext.o \
           filter_invert.o \
+          filter_lift_gamma_gain.o \
           filter_loudness.o \
           filter_rgblut.o \
           filter_sepia.o \
index 92989e34aace2d12c0d733d4a548015e56e73e30..8f491ad19d8ea7c43e9d343271c6436e6d55b8c0 100644 (file)
@@ -26,6 +26,7 @@ extern mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_ty
 extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
 extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
 extern mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
 extern mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
 extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
 extern mlt_filter filter_rgblut_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
@@ -48,6 +49,7 @@ MLT_REPOSITORY
        MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init );
        MLT_REGISTER( filter_type, "dynamictext", filter_dynamictext_init );
        MLT_REGISTER( filter_type, "invert", filter_invert_init );
+       MLT_REGISTER( filter_type, "lift_gamma_gain", filter_lift_gamma_gain_init );
        MLT_REGISTER( filter_type, "loudness", filter_loudness_init );
        MLT_REGISTER( filter_type, "rgblut", filter_rgblut_init );
        MLT_REGISTER( filter_type, "sepia", filter_sepia_init );
@@ -60,6 +62,7 @@ MLT_REPOSITORY
        MLT_REGISTER_METADATA( filter_type, "charcoal", metadata, "filter_charcoal.yml" );
        MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" );
        MLT_REGISTER_METADATA( filter_type, "invert", metadata, "filter_invert.yml" );
+       MLT_REGISTER_METADATA( filter_type, "lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" );
        MLT_REGISTER_METADATA( filter_type, "loudness", metadata, "filter_loudness.yml" );
        MLT_REGISTER_METADATA( filter_type, "rgblut", metadata, "filter_rgblut.yml" );
        MLT_REGISTER_METADATA( filter_type, "sepia", metadata, "filter_sepia.yml" );
diff --git a/src/modules/plus/filter_lift_gamma_gain.c b/src/modules/plus/filter_lift_gamma_gain.c
new file mode 100644 (file)
index 0000000..5c1f747
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * filter_lift_gamma_gain.cpp
+ * Copyright (C) 2014 Brian Matherly <pez4brian@yahoo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <framework/mlt.h>
+#include <stdlib.h>
+#include <math.h>
+
+typedef struct
+{
+       uint8_t rlut[256];
+       uint8_t glut[256];
+       uint8_t blut[256];
+       double rlift, glift, blift;
+       double rgamma, ggamma, bgamma;
+       double rgain, ggain, bgain;
+} private_data;
+
+static void refresh_lut( mlt_filter filter, mlt_frame frame )
+{
+       private_data* private = (private_data*)filter->child;
+       mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+       mlt_position position = mlt_filter_get_position( filter, frame );
+       mlt_position length = mlt_filter_get_length2( filter, frame );
+       double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length );
+       double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length );
+       double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length );
+       double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length );
+       double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length );
+       double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length );
+       double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length );
+       double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length );
+       double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length );
+
+       // Only regenerate the LUT if something changed.
+       if( private->rlift != rlift || private->glift != glift || private->blift != blift ||
+               private->rgamma != rgamma || private->ggamma != ggamma || private->bgamma != bgamma ||
+               private->rgain != rgain || private->ggain != ggain || private->bgain != bgain )
+       {
+               int i = 0;
+               for( i = 0; i < 256; i++ )
+               {
+                       // Convert to gamma 2.2
+                       double gamma22 = pow( (double)i / 255.0, 1.0f / 2.2f );
+                       double r = gamma22;
+                       double g = gamma22;
+                       double b = gamma22;
+
+                       // Apply lift
+                       r += rlift * ( 1 - r );
+                       g += glift * ( 1 - g );
+                       b += blift * ( 1 - b );
+
+                       // Apply gamma
+                       r = pow( r, 2.2f / rgamma );
+                       g = pow( g, 2.2f / ggamma );
+                       b = pow( b, 2.2f / bgamma );
+
+                       // Apply gain
+                       r *= pow( rgain, 1.0f / rgamma );
+                       g *= pow( ggain, 1.0f / ggamma );
+                       b *= pow( bgain, 1.0f / bgamma );
+
+                       // Update LUT
+                       private->rlut[ i ] = (int)(r * 255.0);
+                       private->glut[ i ] = (int)(g * 255.0);
+                       private->blut[ i ] = (int)(b * 255.0);
+               }
+
+               // Store the values that created the LUT so that
+               // changes can be detected.
+               private->rlift = rlift;
+               private->glift = glift;
+               private->blift = blift;
+               private->rgamma = rgamma;
+               private->ggamma = ggamma;
+               private->bgamma = bgamma;
+               private->rgain = rgain;
+               private->ggain = ggain;
+               private->bgain = bgain;
+       }
+}
+
+static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height )
+{
+       private_data* private = (private_data*)filter->child;
+       int total = width * height + 1;
+       uint8_t* sample = image;
+
+       switch( format )
+       {
+       case mlt_image_rgb24:
+               while( --total )
+               {
+                       *sample = private->rlut[ *sample ];
+                       sample++;
+                       *sample = private->glut[ *sample ];
+                       sample++;
+                       *sample = private->blut[ *sample ];
+                       sample++;
+               }
+               break;
+       case mlt_image_rgb24a:
+               while( --total )
+               {
+                       *sample = private->rlut[ *sample ];
+                       sample++;
+                       *sample = private->glut[ *sample ];
+                       sample++;
+                       *sample = private->blut[ *sample ];
+                       sample++;
+                       sample++; // Skip alpha
+               }
+               break;
+       default:
+               mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) );
+               break;
+       }
+}
+
+static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+       mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame );
+       int error = 0;
+
+       mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
+
+       // Regenerate the LUT if necessary
+       refresh_lut( filter, frame );
+
+       // Make sure the format is acceptable
+       if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a )
+       {
+               *format = mlt_image_rgb24;
+       }
+
+       // Get the image
+       writable = 1;
+       error = mlt_frame_get_image( frame, image, format, width, height, writable );
+
+       // Apply the LUT
+       if( !error )
+       {
+               apply_lut( filter, *image, *format, *width, *height );
+       }
+
+       mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
+
+       return error;
+}
+
+static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
+{
+       mlt_frame_push_service( frame, filter );
+       mlt_frame_push_get_image( frame, filter_get_image );
+       return frame;
+}
+
+static void filter_close( mlt_filter filter )
+{
+       private_data* private = (private_data*)filter->child;
+
+       if ( private )
+       {
+               free( private );
+       }
+       filter->child = NULL;
+       filter->close = NULL;
+       filter->parent.close = NULL;
+       mlt_service_close( &filter->parent );
+}
+
+mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+       mlt_filter filter = mlt_filter_new();
+       private_data* private = (private_data*)calloc( 1, sizeof(private_data) );
+       int i = 0;
+
+       if ( filter && private )
+       {
+               mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
+
+               // Initialize private data
+               for( i = 0; i < 256; i++ )
+               {
+                       private->rlut[i] = i;
+                       private->glut[i] = i;
+                       private->blut[i] = i;
+               }
+               private->rlift = private->glift = private->blift = 0.0;
+               private->rgamma = private->ggamma = private->bgamma = 1.0;
+               private->rgain = private->ggain = private->bgain = 1.0;
+
+               // Initialize filter properties
+               mlt_properties_set_double( properties, "lift_r", private->rlift );
+               mlt_properties_set_double( properties, "lift_g", private->glift );
+               mlt_properties_set_double( properties, "lift_b", private->blift );
+               mlt_properties_set_double( properties, "gamma_r", private->rgamma );
+               mlt_properties_set_double( properties, "gamma_g", private->ggamma );
+               mlt_properties_set_double( properties, "gamma_b", private->bgamma );
+               mlt_properties_set_double( properties, "gain_r", private->rgain );
+               mlt_properties_set_double( properties, "gain_g", private->ggain );
+               mlt_properties_set_double( properties, "gain_b", private->bgain );
+
+               filter->close = filter_close;
+               filter->process = filter_process;
+               filter->child = private;
+       }
+       else
+       {
+               mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" );
+
+               if( filter )
+               {
+                       mlt_filter_close( filter );
+                       filter = NULL;
+               }
+
+               if( private )
+               {
+                       free( private );
+               }
+       }
+
+       return filter;
+}
diff --git a/src/modules/plus/filter_lift_gamma_gain.yml b/src/modules/plus/filter_lift_gamma_gain.yml
new file mode 100644 (file)
index 0000000..a4a3be3
--- /dev/null
@@ -0,0 +1,89 @@
+schema_version: 0.1
+type: filter
+identifier: lift_gamma_gain
+title: Lift, Gamma, and Gain
+version: 1
+copyright: Brian Matherly
+creator: Brian Matherly
+license: LGPLv2.1
+language: en
+tags:
+  - Video
+description: >
+  A simple lift/gamma/gain effect, used for color grading.
+notes: >
+  Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights,
+  although all parameters affect the entire curve. Mathematically speaking,
+  it is a bit unusual to look at gamma as a color, but it works pretty well
+  in practice.
+  The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma).
+  The lift is a case where we actually would _not_ want linear light;
+  since black by definition becomes equal to the lift color, we want lift to
+  be pretty close to black, but in linear light that means lift affects the
+  rest of the curve relatively little. Thus, we actually convert to gamma 2.2
+  before lift, and then back again afterwards. (Gain and gamma are,
+  up to constants, commutative with the de-gamma operation.)
+
+parameters:
+  - identifier: lift_r
+    title: Lift Red
+    type: float
+    minimum: 0.0
+    default: 0.0
+    mutable: yes
+
+  - identifier: lift_g
+    title: Lift Green
+    type: float
+    minimum: 0.0
+    default: 0.0
+    mutable: yes
+
+  - identifier: lift_b
+    title: Lift Blue
+    type: float
+    minimum: 0.0
+    default: 0.0
+    mutable: yes
+
+  - identifier: gamma_r
+    title: Gamma Red
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes
+
+  - identifier: gamma_g
+    title: Gamma Green
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes
+
+  - identifier: gamma_b
+    title: Gamma Blue
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes
+
+  - identifier: gain_r
+    title: Gain Red
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes
+
+  - identifier: gain_g
+    title: Gain Green
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes
+
+  - identifier: gain_b
+    title: Gain Blue
+    type: float
+    minimum: 0.0
+    default: 1.0
+    mutable: yes