1
mirror of https://code.videolan.org/videolan/vlc synced 2024-07-25 09:41:30 +02:00
vlc/modules/video_filter/antiflicker.c
2011-08-13 01:51:37 +02:00

344 lines
13 KiB
C

/*****************************************************************************
* antiflicker.c : antiflicker video effect plugin for vlc
*****************************************************************************
* Copyright (C) 2000-2011 the VideoLAN team
* $Id:
*
* Authors: Dharani Prabhu <dharani.prabhu.s@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 <vlc_filter.h>
#include "filter_picture.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int GetLuminanceAvg( picture_t * p_pic );
static picture_t *Filter( filter_t *, picture_t * );
static int AntiFlickerCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *p_data );
static int Create ( vlc_object_t * );
static void Destroy ( vlc_object_t * );
#define WINDOW_TEXT N_("Window size")
#define WINDOW_LONGTEXT N_("Number of frames (0 to 100)")
#define SFTN_TEXT N_("Softening value")
#define SFTN_LONGTEXT N_("Number of frames consider for smoothening (0 to 30)")
#define FILTER_PREFIX "antiflicker-"
#define MAX_WINDOW_SZ 100
#define MAX_SOFTENING_SZ 31
#define SCENE_CHANGE_THRESHOLD 100
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin ()
set_description( N_("antiflicker video filter") )
set_shortname( N_( "antiflicker" ))
set_capability( "video filter2", 0 )
set_category( CAT_VIDEO )
set_subcategory( SUBCAT_VIDEO_VFILTER )
add_integer_with_range( FILTER_PREFIX "window-size", 10, 0, MAX_WINDOW_SZ,
WINDOW_TEXT, WINDOW_LONGTEXT, false )
add_integer_with_range( FILTER_PREFIX "softening-size", 10, 0, MAX_SOFTENING_SZ,
SFTN_TEXT, SFTN_LONGTEXT, false )
add_shortcut( "antiflicker" )
set_callbacks( Create, Destroy )
vlc_module_end ()
/*****************************************************************************
* filter_sys_t: Distort video output method descriptor
*****************************************************************************
* This structure is part of the video output thread descriptor.
* It describes the Distort specific properties of an output thread.
*****************************************************************************/
struct filter_sys_t
{
int ia_luminance_data[MAX_WINDOW_SZ];
vlc_mutex_t lock;
int i_window_size;
int i_softening;
uint8_t *p_old_data;
};
/*****************************************************************************
* Create: allocates Distort video thread output method
*****************************************************************************
* This function allocates and initializes a Distort vout method.
*****************************************************************************/
static int Create( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *)p_this;
switch( p_filter->fmt_in.video.i_chroma )
{
CASE_PLANAR_YUV
break;
default:
msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
(char*)&(p_filter->fmt_in.video.i_chroma) );
return VLC_EGENERIC;
}
/* Allocate structure */
p_filter->p_sys = malloc( sizeof( *p_filter->p_sys ) );
if( p_filter->p_sys == NULL )
return VLC_ENOMEM;
p_filter->pf_video_filter = Filter;
/* Initialize the arguments */
p_filter->p_sys->i_window_size = var_CreateGetIntegerCommand( p_filter,
FILTER_PREFIX "window-size" );
p_filter->p_sys->i_softening = var_CreateGetIntegerCommand( p_filter,
FILTER_PREFIX "softening-size" );
p_filter->p_sys->p_old_data = calloc( p_filter->fmt_in.video.i_width *
(p_filter->fmt_in.video.i_height+1),sizeof(*p_filter->p_sys->p_old_data) );
if( p_filter->p_sys->p_old_data == NULL )
return VLC_ENOMEM;
memset( p_filter->p_sys->ia_luminance_data, 0,
sizeof(p_filter->p_sys->ia_luminance_data) );
p_filter->p_sys->ia_luminance_data[p_filter->p_sys->i_window_size - 1] = 256;
vlc_mutex_init( &p_filter->p_sys->lock );
var_AddCallback(p_filter,FILTER_PREFIX "window-size",
AntiFlickerCallback, p_filter->p_sys);
var_AddCallback(p_filter,FILTER_PREFIX "softening-size",
AntiFlickerCallback, p_filter->p_sys);
return VLC_SUCCESS;
}
/*****************************************************************************
* Destroy: destroy Distort video thread output method
*****************************************************************************
* Terminate an output method created by DistortCreateOutputMethod
*****************************************************************************/
static void Destroy( vlc_object_t *p_this )
{
filter_t *p_filter = (filter_t *)p_this;
var_DelCallback(p_filter,FILTER_PREFIX "window-size",
AntiFlickerCallback, p_filter->p_sys);
var_DelCallback(p_filter,FILTER_PREFIX "softening-size",
AntiFlickerCallback, p_filter->p_sys);
vlc_mutex_destroy( &p_filter->p_sys->lock );
free(p_filter->p_sys->p_old_data);
free( p_filter->p_sys );
}
/*****************************************************************************
* GetLuminanceAvg : The funtion returns the luminance average for a picture
*****************************************************************************/
static int GetLuminanceAvg( picture_t *p_pic )
{
uint8_t *p_yplane_out = p_pic->p[Y_PLANE].p_pixels;
int i_num_lines = p_pic->p[Y_PLANE].i_visible_lines;
int i_num_cols = p_pic->p[Y_PLANE].i_visible_pitch;
int i_in_pitch = p_pic->p[Y_PLANE].i_pitch;
if( i_num_lines == 0 || i_num_cols == 0 )
return 0;
unsigned lum_sum = 0;
for( int i_line = 0 ; i_line < i_num_lines ; ++i_line )
{
for( int i_col = 0 ; i_col < i_num_cols; ++i_col )
{
lum_sum += p_yplane_out[i_line*i_in_pitch+i_col];
}
}
unsigned div = i_num_lines * i_num_cols;
return (lum_sum + (div>>1)) / div;
}
/*****************************************************************************
* Filter: adjust the luminance value and renders
*****************************************************************************
* The function uses moving average of past frames to adjust the luminance
* of current frame also applies temporaral smoothening if enabled.
*****************************************************************************/
static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
{
if( !p_pic ) return NULL;
picture_t *p_outpic = filter_NewPicture( p_filter );
if( !p_outpic )
{
picture_Release( p_pic );
return NULL;
}
/****************** Get variables *************************/
int i_window_size;
int i_softening;
vlc_mutex_lock( &p_filter->p_sys->lock );
i_window_size = p_filter->p_sys->i_window_size;
i_softening = p_filter->p_sys->i_softening;
vlc_mutex_unlock( &p_filter->p_sys->lock );
uint8_t *p_yplane_in = p_pic->p[Y_PLANE].p_pixels;
uint8_t *p_yplane_out = p_outpic->p[Y_PLANE].p_pixels;
bool scene_changed = false;
int i_num_lines = p_pic->p[Y_PLANE].i_visible_lines;
int i_num_cols = p_pic->p[Y_PLANE].i_visible_pitch;
int i_in_pitch = p_pic->p[Y_PLANE].i_pitch;
int i_out_pitch = p_outpic->p[Y_PLANE].i_pitch;
/******** Get the luminance average for the current picture ********/
int lum_avg = GetLuminanceAvg(p_pic);
/*Identify as scene change if the luminance average deviates
more than the threshold value or if it is the first frame*/
if( abs(lum_avg - p_filter->p_sys->
ia_luminance_data[i_window_size - 1]) > SCENE_CHANGE_THRESHOLD
|| p_filter->p_sys->ia_luminance_data[i_window_size - 1] == 256)
{
scene_changed = true;
}
if ( scene_changed )
{
//reset the luminance data
for (int i = 0; i < i_window_size; ++i)
p_filter->p_sys->ia_luminance_data[i] = lum_avg;
plane_CopyPixels( &p_outpic->p[Y_PLANE], &p_pic->p[Y_PLANE] );
}
else
{
/******* Compute the adjustment factor using moving average ********/
for (int i = 0; i < i_window_size-1 ; ++i)
p_filter->p_sys->ia_luminance_data[i] =
p_filter->p_sys->ia_luminance_data[i+1];
p_filter->p_sys->ia_luminance_data[i_window_size - 1] = lum_avg;
float scale = 1.0;
if (lum_avg > 0)
{
float filt = 0;
for (int i = 0; i < i_window_size; i++)
filt += (float) p_filter->p_sys->ia_luminance_data[i];
scale = filt/(i_window_size*lum_avg);
}
/******* Apply the adjustment factor to each pixel on Y_PLANE ********/
uint8_t shift = 8;
int scale_num = __MIN(scale,255) * ( 1 << shift );
for( int i_line = 0 ; i_line < i_num_lines ; i_line++ )
{
for( int i_col = 0; i_col < i_num_cols ; i_col++ )
{
uint8_t pixel_data = p_yplane_in[i_line*i_in_pitch+i_col];
int pixel_val = ( scale_num * pixel_data +
(1<<(shift -1)) ) >> shift;
p_yplane_out[i_line*i_out_pitch+i_col] =
(pixel_val>255) ? 255:pixel_val;
}
}
}
/***************** Copy the UV plane as such *****************************/
plane_CopyPixels( &p_outpic->p[U_PLANE], &p_pic->p[U_PLANE] );
plane_CopyPixels( &p_outpic->p[V_PLANE], &p_pic->p[V_PLANE] );
if (scene_changed || i_softening == 0)
{
return CopyInfoAndRelease( p_outpic, p_pic );
}
/******* Temporal softening phase. Adapted from code by Steven Don ******/
uint8_t *p_yplane_out_old = p_filter->p_sys->p_old_data;
int i_video_width = p_filter->fmt_in.video.i_width;
for( int i_line = 0 ; i_line < i_num_lines ; i_line++ )
{
for( int i_col = 0; i_col < i_num_cols ; i_col++ )
{
uint8_t pixel_data = p_yplane_out[i_line*i_out_pitch+i_col];
uint8_t pixel_old = p_yplane_out_old[i_line*i_video_width+i_col];
int diff = abs(pixel_data - pixel_old);
if (diff < i_softening)
{
if (diff > (i_softening >> 1))
{
p_yplane_out_old[i_line*i_video_width+i_col] =
((pixel_data * 2) + pixel_old) /3;
}
}
else
{
p_yplane_out_old[i_line*i_video_width+i_col] = pixel_data;
}
p_yplane_out[i_line*i_out_pitch+i_col] =
p_yplane_out_old[i_line*i_video_width+i_col];
}
}
return CopyInfoAndRelease( p_outpic, p_pic );
}
/*****************************************************************************
* Callback function to set the parameters
*****************************************************************************
* This function sets the parameters necesscary for the filter
*****************************************************************************/
static int AntiFlickerCallback( vlc_object_t *p_this, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *p_data )
{
VLC_UNUSED(p_this); VLC_UNUSED(oldval);
filter_sys_t *p_sys = (filter_sys_t *)p_data;
vlc_mutex_lock( &p_sys->lock );
if( !strcmp( psz_var, FILTER_PREFIX "window-size" ) )
p_sys->i_window_size = newval.i_int;
else if( !strcmp( psz_var, FILTER_PREFIX "softening-size" ) )
p_sys->i_softening = newval.i_int;
vlc_mutex_unlock( &p_sys->lock );
return VLC_SUCCESS;
}