]> git.sesse.net Git - vlc/commitdiff
adding posterize video filter
authorBranko Kokanovic <branko.kokanovic@gmail.com>
Sat, 11 Sep 2010 16:17:56 +0000 (18:17 +0200)
committerRémi Denis-Courmont <remi@remlab.net>
Thu, 16 Sep 2010 03:37:26 +0000 (06:37 +0300)
Acked-by: Jean-Baptiste Kempf <jb@videolan.org>
Signed-off-by: Rémi Denis-Courmont <remi@remlab.net>
NEWS
modules/LIST
modules/video_filter/Modules.am
modules/video_filter/posterize.c [new file with mode: 0644]
po/POTFILES.in

diff --git a/NEWS b/NEWS
index de1ffba7bb666942b7804989b9d44d3f65360cc8..cad6a26424fff3a09b5b2e384c110863e0ecd47b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,7 @@ Audio Filter:
 Video Filter:
  * New gradfun filter for debanding videos using dithering
  * Rewrite of the grain filter, faster and with better quality
+ * New posterize filter for lowering the number of colors
 
 Stream output:
  * New livehttp-module for HTTP Live Streamin (iphone-stuff),
index 7d2996c2abbedfa395c427116361aa3dd1c43204..6ceb6efe9a44abdd1c035e3e996f6ce0df944ffa 100644 (file)
@@ -232,6 +232,7 @@ $Id$
  * png: PNG images decoder
  * podcast: podcast feed parser
  * portaudio: audio output module that uses the portaudio library (www.portaudio.com)
+ * posterize: posterize video filter
  * postproc: Video post processing filter
  * projectm: visualisation using libprojectM
  * ps: input module for MPEG PS decapsulation
index ea46c31c6801474a8be43106df7359dbcedf8123..6028b84dd119b81dc6cfb181589468e13e8b2ae6 100644 (file)
@@ -22,6 +22,7 @@ SOURCES_remoteosd = remoteosd.c remoteosd_rfbproto.h
 SOURCES_magnify = magnify.c
 SOURCES_wave = wave.c
 SOURCES_ripple = ripple.c
+SOURCES_posterize = posterize.c
 SOURCES_psychedelic = psychedelic.c
 SOURCES_gradient = gradient.c
 SOURCES_ball = ball.c
@@ -104,6 +105,7 @@ libvlc_LTLIBRARIES += \
        libmotionblur_plugin.la \
        libmotiondetect_plugin.la \
        libnoise_plugin.la \
+       libposterize_plugin.la \
        libpsychedelic_plugin.la \
        libpuzzle_plugin.la \
        libripple_plugin.la \
diff --git a/modules/video_filter/posterize.c b/modules/video_filter/posterize.c
new file mode 100644 (file)
index 0000000..d7a0cbd
--- /dev/null
@@ -0,0 +1,514 @@
+/*****************************************************************************
+ * posterize.c : Posterize video plugin for vlc
+ *****************************************************************************
+ * Copyright (C) 2010 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+
+#include <assert.h>
+#include <vlc_filter.h>
+#include "filter_picture.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  Create      ( vlc_object_t * );
+static void Destroy     ( vlc_object_t * );
+
+static picture_t *Filter( filter_t *, picture_t * );
+static void PlanarYUVPosterize( picture_t *, picture_t *, int);
+static void PackedYUVPosterize( picture_t *, picture_t *, int);
+static void RVPosterize( picture_t *, picture_t *, bool, int );
+static void YuvPosterization( uint8_t *, uint8_t *, uint8_t *, uint8_t *,
+                    uint8_t, uint8_t, uint8_t, uint8_t, int );
+static void yuv2rgb( uint8_t *, uint8_t *, uint8_t *,
+                    uint8_t, uint8_t, uint8_t);
+
+static const char *const ppsz_filter_options[] = {
+    "level", NULL
+};
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+#define POSTERIZE_LEVEL_TEXT N_("Posterize level")
+#define POSTERIZE_LEVEL_LONGTEXT N_("Posterize level "\
+                    "(number of colors is cube of this value)" )
+
+#define CFG_PREFIX "posterize-"
+
+vlc_module_begin ()
+    set_description( N_("Posterize video filter") )
+    set_shortname( N_("Posterize" ) )
+    set_help( N_("Posterize video by lowering the number of colors") )
+    set_category( CAT_VIDEO )
+    set_subcategory( SUBCAT_VIDEO_VFILTER )
+    set_capability( "video filter2", 0 )
+    add_integer_with_range( CFG_PREFIX "level", 6, 2, 256, NULL,
+                           POSTERIZE_LEVEL_TEXT, POSTERIZE_LEVEL_LONGTEXT,
+                           false )
+    set_callbacks( Create, Destroy )
+vlc_module_end ()
+
+/*****************************************************************************
+ * callback prototypes
+ *****************************************************************************/
+static int FilterCallback( vlc_object_t *, char const *,
+                           vlc_value_t, vlc_value_t, void * );
+
+/*****************************************************************************
+ * filter_sys_t: adjust filter method descriptor
+ *****************************************************************************/
+struct filter_sys_t
+{
+    int i_level;
+    vlc_mutex_t lock;
+};
+
+/*****************************************************************************
+ * Create: allocates Posterize video thread output method
+ *****************************************************************************
+ * This function allocates and initializes a Posterize vout method.
+ *****************************************************************************/
+static int Create( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys;
+
+    switch( p_filter->fmt_in.video.i_chroma )
+    {
+        CASE_PLANAR_YUV_SQUARE
+            break;
+        CASE_PACKED_YUV_422
+            break;
+        case VLC_CODEC_RGB24:
+            break;
+        case VLC_CODEC_RGB32:
+            break;
+        default:
+            msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
+                     (char*)&(p_filter->fmt_in.video.i_chroma) );
+            return VLC_EGENERIC;
+    }
+
+    if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
+    {
+        msg_Err( p_filter, "Input and output chromas don't match" );
+        return VLC_EGENERIC;
+    }
+
+    /* Allocate structure */
+    p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
+    if( p_filter->p_sys == NULL )
+        return VLC_ENOMEM;
+
+    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
+                       p_filter->p_cfg );
+    p_sys->i_level = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "level" );
+
+    vlc_mutex_init( &p_sys->lock );
+
+    var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
+
+    p_filter->pf_video_filter = Filter;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Destroy: destroy Posterize video thread output method
+ *****************************************************************************
+ * Terminate an output method created by PosterizeCreateOutputMethod
+ *****************************************************************************/
+static void Destroy( vlc_object_t *p_this )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+
+    var_DelCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
+
+    vlc_mutex_destroy( &p_filter->p_sys->lock );
+    free( p_filter->p_sys );
+}
+
+/*****************************************************************************
+ * Render: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to Posterize image, waits
+ * until it is displayed and switch the two rendering buffers, preparing next
+ * frame.
+ *****************************************************************************/
+static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
+{
+    picture_t *p_outpic;
+    int level;
+
+    if( !p_pic ) return NULL;
+
+    filter_sys_t *p_sys = p_filter->p_sys;
+    vlc_mutex_lock( &p_sys->lock );
+    level = p_sys->i_level;
+    vlc_mutex_unlock( &p_sys->lock );
+
+    p_outpic = filter_NewPicture( p_filter );
+    if( !p_outpic )
+    {
+        msg_Warn( p_filter, "can't get output picture" );
+        picture_Release( p_pic );
+        return NULL;
+    }
+
+    switch( p_pic->format.i_chroma )
+    {
+        case VLC_CODEC_RGB24:
+            RVPosterize( p_pic, p_outpic, false, level );
+            break;
+        case VLC_CODEC_RGB32:
+            RVPosterize( p_pic, p_outpic, true, level );
+            break;
+        CASE_PLANAR_YUV_SQUARE
+            PlanarYUVPosterize( p_pic, p_outpic, level );
+            break;
+        CASE_PACKED_YUV_422
+            PackedYUVPosterize( p_pic, p_outpic, level );
+            break;
+        default:
+            assert( false );
+    }
+
+    return CopyInfoAndRelease( p_outpic, p_pic );
+}
+
+/*****************************************************************************
+ * For a given level, calculates new posterized value for pixel whose value x
+ * is in range of 0-255
+ *****************************************************************************/
+#define POSTERIZE_PIXEL(x, level) \
+       (((( x * level ) >> 8 ) * 255 ) / ( level - 1 ))
+
+/*****************************************************************************
+ * PlanarYUVPosterize: Posterize one frame of the planar YUV video
+ *****************************************************************************
+ * This function posterizes one frame of the video by iterating through video
+ * lines. In every pass, start of Y, U and V planes is calculated and for
+ * every pixel we calculate new values of YUV values.
+ *****************************************************************************/
+static void PlanarYUVPosterize( picture_t *p_pic, picture_t *p_outpic,
+                               int i_level )
+{
+    uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_y, *p_line_end_y, *p_out_y,
+            *p_out_u, *p_out_v;
+    int i_current_line = 0;
+
+    p_in_y = p_pic->p[Y_PLANE].p_pixels;
+    p_in_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_lines
+        * p_pic->p[Y_PLANE].i_pitch;
+    p_out_y = p_outpic->p[Y_PLANE].p_pixels;
+
+    /* iterate for every visible line in the frame */
+    while( p_in_y < p_in_end_y )
+    {
+        p_line_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_pitch;
+        /* calculate start of U plane line */
+        p_in_u = p_pic->p[U_PLANE].p_pixels
+            + p_pic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
+        p_out_u = p_outpic->p[U_PLANE].p_pixels
+            + p_outpic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
+        /* calculate start of V plane line */
+        p_in_v = p_pic->p[V_PLANE].p_pixels
+            + p_pic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
+        p_out_v = p_outpic->p[V_PLANE].p_pixels
+            + p_outpic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
+        /* iterate for every two pixels in line */
+        while( p_in_y < p_line_end_y )
+        {
+            uint8_t y1, y2, u, v;
+            uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
+            /* retrieve original YUV values */
+            y1 = *p_in_y++;
+            y2 = *p_in_y++;
+            u = *p_in_u++;
+            v = *p_in_v++;
+            /* do posterization */
+            YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
+                             &posterized_v, y1, y2, u, v, i_level );
+            /* put posterized valued */
+            *p_out_y++ = posterized_y1;
+            *p_out_y++ = posterized_y2;
+            *p_out_u++ = posterized_u;
+            *p_out_v++ = posterized_v;
+        }
+        p_in_y += p_pic->p[Y_PLANE].i_pitch
+            - p_pic->p[Y_PLANE].i_visible_pitch;
+        p_out_y += p_outpic->p[Y_PLANE].i_pitch
+            - p_outpic->p[Y_PLANE].i_visible_pitch;
+        i_current_line++;
+    }
+}
+
+/*****************************************************************************
+ * PackedYUVPosterize: Posterize one frame of the packed YUV video
+ *****************************************************************************
+ * This function posterizes one frame of the video by iterating through video
+ * lines. In every pass, we calculate new values for pixels (UYVY, VYUY, YUYV
+ * and YVYU formats are supported)
+ *****************************************************************************/
+static void PackedYUVPosterize( picture_t *p_pic, picture_t *p_outpic, int i_level )
+{
+    uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
+    uint8_t y1, y2, u, v;
+
+    p_in = p_pic->p[0].p_pixels;
+    p_in_end = p_in + p_pic->p[0].i_visible_lines
+        * p_pic->p[0].i_pitch;
+    p_out = p_outpic->p[0].p_pixels;
+
+    while( p_in < p_in_end )
+    {
+        p_line_start = p_in;
+        p_line_end = p_in + p_pic->p[0].i_visible_pitch;
+        while( p_in < p_line_end )
+        {
+            uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
+            /* extract proper pixel values */
+            switch( p_pic->format.i_chroma )
+            {
+                case VLC_CODEC_UYVY:
+                    u = *p_in++;
+                    y1 = *p_in++;
+                    v = *p_in++;
+                    y2 = *p_in++;
+                    break;
+                case VLC_CODEC_VYUY:
+                    v = *p_in++;
+                    y1 = *p_in++;
+                    u = *p_in++;
+                    y2 = *p_in++;
+                    break;
+                case VLC_CODEC_YUYV:
+                    y1 = *p_in++;
+                    u = *p_in++;
+                    y2 = *p_in++;
+                    v = *p_in++;
+                    break;
+                case VLC_CODEC_YVYU:
+                    y1 = *p_in++;
+                    v = *p_in++;
+                    y2 = *p_in++;
+                    u = *p_in++;
+                    break;
+                default:
+                    assert( false );
+            }
+            /* do posterization */
+            YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
+                             &posterized_v, y1, y2, u, v, i_level );
+            /* put posterized values in proper place */
+            switch( p_pic->format.i_chroma )
+            {
+                case VLC_CODEC_UYVY:
+                    *p_out++ = posterized_u;
+                    *p_out++ = posterized_y1;
+                    *p_out++ = posterized_v;
+                    *p_out++ = posterized_y2;
+                    break;
+                case VLC_CODEC_VYUY:
+                    *p_out++ = posterized_v;
+                    *p_out++ = posterized_y1;
+                    *p_out++ = posterized_u;
+                    *p_out++ = posterized_y2;
+                    break;
+                case VLC_CODEC_YUYV:
+                    *p_out++ = posterized_y1;
+                    *p_out++ = posterized_u;
+                    *p_out++ = posterized_y2;
+                    *p_out++ = posterized_v;
+                    break;
+                case VLC_CODEC_YVYU:
+                    *p_out++ = posterized_y1;
+                    *p_out++ = posterized_v;
+                    *p_out++ = posterized_y2;
+                    *p_out++ = posterized_u;
+                    break;
+                default:
+                    assert( false );
+            }
+        }
+        p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
+        p_out += p_outpic->p[0].i_pitch
+            - p_outpic->p[0].i_visible_pitch;
+    }
+}
+
+/*****************************************************************************
+ * RVPosterize: Posterize one frame of the RV24/RV32 video
+ *****************************************************************************
+ * This function posterizes one frame of the video by iterating through video
+ * lines and calculating new values for every byte in chunks of 3 (RV24) or
+ * 4 (RV32) bytes
+ *****************************************************************************/
+static void RVPosterize( picture_t *p_pic, picture_t *p_outpic,
+                                 bool rv32, int level )
+{
+    uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out, pixel;
+
+    p_in = p_pic->p[0].p_pixels;
+    p_in_end = p_in + p_pic->p[0].i_visible_lines
+        * p_pic->p[0].i_pitch;
+    p_out = p_outpic->p[0].p_pixels;
+
+    while( p_in < p_in_end )
+    {
+        p_line_start = p_in;
+        p_line_end = p_in + p_pic->p[0].i_visible_pitch;
+        while( p_in < p_line_end )
+        {
+            pixel = *p_in++;
+            *p_out++ = POSTERIZE_PIXEL( pixel, level );
+            pixel = *p_in++;
+            *p_out++ = POSTERIZE_PIXEL( pixel, level );
+            pixel = *p_in++;
+            *p_out++ = POSTERIZE_PIXEL( pixel, level );
+            /* for rv32 we take 4 chunks at the time */
+            if ( rv32 )
+            {
+                pixel = *p_in++;
+                *p_out++ = POSTERIZE_PIXEL( pixel, level );
+            }
+        }
+        p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
+        p_out += p_outpic->p[0].i_pitch
+            - p_outpic->p[0].i_visible_pitch;
+    }
+}
+
+/*****************************************************************************
+ * YuvPosterization: Lowers the color depth of YUV color space
+ *****************************************************************************
+ * This function lowers the color level of YUV color space to specified level
+ * by converting YUV color values to theirs RGB equivalents, calculates new
+ * values and then converts RGB values to YUV values again.
+ *****************************************************************************/
+static void YuvPosterization( uint8_t* posterized_y1, uint8_t* posterized_y2,
+                             uint8_t* posterized_u, uint8_t* posterized_v,
+                             uint8_t y1, uint8_t y2, uint8_t u, uint8_t v,
+                             int i_level ) {
+    uint8_t r1, g1, b1; /* for y1 new value */
+    uint8_t r2, b2, g2; /* for y2 new value */
+    uint8_t r3, g3, b3; /* for new values of u and v */
+    /* fist convert YUV -> RGB */
+    yuv2rgb( &r1, &g1, &b1, y1, u, v );
+    yuv2rgb( &r2, &g2, &b2, y2, u, v );
+    yuv2rgb( &r3, &g3, &b3, ( y1 + y2 ) / 2, u, v );
+    /* round RGB values to specified posterize level */
+    r1 = POSTERIZE_PIXEL( r1, i_level );
+    g1 = POSTERIZE_PIXEL( g1, i_level );
+    b1 = POSTERIZE_PIXEL( b1, i_level );
+    r2 = POSTERIZE_PIXEL( r2, i_level );
+    g2 = POSTERIZE_PIXEL( g2, i_level );
+    b2 = POSTERIZE_PIXEL( b2, i_level );
+    r3 = POSTERIZE_PIXEL( r3, i_level );
+    g3 = POSTERIZE_PIXEL( g3, i_level );
+    b3 = POSTERIZE_PIXEL( b3, i_level );
+    /* convert from calculated RGB -> YUV */
+    *posterized_y1 = ( ( 66 * r1 + 129 * g1 +  25 * b1 + 128 ) >> 8 ) +  16;
+    *posterized_y2 = ( ( 66 * r2 + 129 * g2 +  25 * b2 + 128 ) >> 8 ) +  16;
+    *posterized_u = ( ( -38 * r3 -  74 * g3 + 112 * b3 + 128 ) >> 8 ) + 128;
+    *posterized_v = ( ( 112 * r3 -  94 * g3 -  18 * b3 + 128 ) >> 8 ) + 128;
+}
+
+/*****************************************************************************
+ * yuv2rgb: Converts from YUV to RGB color space
+ *****************************************************************************
+ * This function converts YUV values to RGB values using function defined in:
+ * http://msdn.microsoft.com/en-us/library/ms893078
+ *****************************************************************************/
+static void yuv2rgb( uint8_t* r, uint8_t* g, uint8_t* b, uint8_t y,
+                    uint8_t u, uint8_t v )
+{
+    int16_t c = y - 16;
+    int16_t d = u - 128;
+    int16_t e = v - 128;
+    int16_t noclipped_r = ( 298 * c + 409 * e + 128 ) >> 8;
+    if ( noclipped_r < 0 )
+    {
+        *r=0;
+    }
+    else if ( noclipped_r > 255 )
+    {
+        *r = 255;
+    }
+    else
+    {
+        *r = noclipped_r;
+    }
+    int16_t noclipped_g = ( 298 * c - 100 * d - 208 * e + 128 ) >> 8;
+    if ( noclipped_g < 0 )
+    {
+        *g=0;
+    }
+    else if ( noclipped_g > 255 )
+    {
+        *g = 255;
+    }
+    else
+    {
+        *g = noclipped_g;
+    }
+    int16_t noclipped_b = ( 298 * c + 516 * d + 128 ) >> 8;
+    if ( noclipped_b < 0 )
+    {
+        *b=0;
+    }
+    else if ( noclipped_b > 255 )
+    {
+        *b = 255;
+    }
+    else
+    {
+        *b = noclipped_b;
+    }
+}
+
+static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
+                            vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    (void)oldval;    (void)p_data;
+    filter_t *p_filter = (filter_t*)p_this;
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    if( !strcmp( psz_var, CFG_PREFIX "level" ) )
+    {
+        vlc_mutex_lock( &p_sys->lock );
+        p_sys->i_level = newval.i_int;
+        vlc_mutex_unlock( &p_sys->lock );
+    }
+
+    return VLC_SUCCESS;
+}
index 77533cd3725f221c2652b41390773c7a971e220a..a45b25b9dc3cdf021f2c845e72fe66ab047ba125 100644 (file)
@@ -1113,6 +1113,7 @@ modules/video_filter/opencv_example.c
 modules/video_filter/opencv_wrapper.c
 modules/video_filter/osdmenu.c
 modules/video_filter/panoramix.c
+modules/video_filter/posterize.c
 modules/video_filter/postproc.c
 modules/video_filter/psychedelic.c
 modules/video_filter/puzzle.c