2012-05-09 09:58:33 -03:00
/*
* Functions for auto gain .
*
* Copyright ( C ) 2010 - 2012 Hans de Goede < hdegoede @ redhat . 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 .
*/
# include "gspca.h"
/* auto gain and exposure algorithm based on the knee algorithm described here:
http : //ytse.tricolour.net/docs/LowLightOptimization.html
Returns 0 if no changes were made , 1 if the gain and or exposure settings
where changed . */
int gspca_expo_autogain (
struct gspca_dev * gspca_dev ,
int avg_lum ,
int desired_avg_lum ,
int deadzone ,
int gain_knee ,
int exposure_knee )
{
s32 gain , orig_gain , exposure , orig_exposure ;
int i , steps , retval = 0 ;
if ( v4l2_ctrl_g_ctrl ( gspca_dev - > autogain ) = = 0 )
return 0 ;
orig_gain = gain = v4l2_ctrl_g_ctrl ( gspca_dev - > gain ) ;
orig_exposure = exposure = v4l2_ctrl_g_ctrl ( gspca_dev - > exposure ) ;
/* If we are of a multiple of deadzone, do multiple steps to reach the
desired lumination fast ( with the risc of a slight overshoot ) */
steps = abs ( desired_avg_lum - avg_lum ) / deadzone ;
PDEBUG ( D_FRAM , " autogain: lum: %d, desired: %d, steps: %d " ,
avg_lum , desired_avg_lum , steps ) ;
for ( i = 0 ; i < steps ; i + + ) {
if ( avg_lum > desired_avg_lum ) {
if ( gain > gain_knee )
gain - - ;
else if ( exposure > exposure_knee )
exposure - - ;
else if ( gain > gspca_dev - > gain - > default_value )
gain - - ;
else if ( exposure > gspca_dev - > exposure - > minimum )
exposure - - ;
else if ( gain > gspca_dev - > gain - > minimum )
gain - - ;
else
break ;
} else {
if ( gain < gspca_dev - > gain - > default_value )
gain + + ;
else if ( exposure < exposure_knee )
exposure + + ;
else if ( gain < gain_knee )
gain + + ;
else if ( exposure < gspca_dev - > exposure - > maximum )
exposure + + ;
else if ( gain < gspca_dev - > gain - > maximum )
gain + + ;
else
break ;
}
}
if ( gain ! = orig_gain ) {
v4l2_ctrl_s_ctrl ( gspca_dev - > gain , gain ) ;
retval = 1 ;
}
if ( exposure ! = orig_exposure ) {
v4l2_ctrl_s_ctrl ( gspca_dev - > exposure , exposure ) ;
retval = 1 ;
}
if ( retval )
PDEBUG ( D_FRAM , " autogain: changed gain: %d, expo: %d " ,
gain , exposure ) ;
return retval ;
}
EXPORT_SYMBOL ( gspca_expo_autogain ) ;
/* Autogain + exposure algorithm for cameras with a coarse exposure control
( usually this means we can only control the clockdiv to change exposure )
As changing the clockdiv so that the fps drops from 30 to 15 fps for
example , will lead to a huge exposure change ( it effectively doubles ) ,
this algorithm normally tries to only adjust the gain ( between 40 and
80 % ) and if that does not help , only then changes exposure . This leads
to a much more stable image then using the knee algorithm which at
certain points of the knee graph will only try to adjust exposure ,
which leads to oscilating as one exposure step is huge .
Returns 0 if no changes were made , 1 if the gain and or exposure settings
where changed . */
int gspca_coarse_grained_expo_autogain (
struct gspca_dev * gspca_dev ,
int avg_lum ,
int desired_avg_lum ,
int deadzone )
{
s32 gain_low , gain_high , gain , orig_gain , exposure , orig_exposure ;
int steps , retval = 0 ;
if ( v4l2_ctrl_g_ctrl ( gspca_dev - > autogain ) = = 0 )
return 0 ;
orig_gain = gain = v4l2_ctrl_g_ctrl ( gspca_dev - > gain ) ;
orig_exposure = exposure = v4l2_ctrl_g_ctrl ( gspca_dev - > exposure ) ;
2014-07-17 12:31:23 -03:00
gain_low = ( s32 ) ( gspca_dev - > gain - > maximum - gspca_dev - > gain - > minimum ) /
2012-05-09 09:58:33 -03:00
5 * 2 + gspca_dev - > gain - > minimum ;
2014-07-17 12:31:23 -03:00
gain_high = ( s32 ) ( gspca_dev - > gain - > maximum - gspca_dev - > gain - > minimum ) /
2012-05-09 09:58:33 -03:00
5 * 4 + gspca_dev - > gain - > minimum ;
/* If we are of a multiple of deadzone, do multiple steps to reach the
desired lumination fast ( with the risc of a slight overshoot ) */
steps = ( desired_avg_lum - avg_lum ) / deadzone ;
PDEBUG ( D_FRAM , " autogain: lum: %d, desired: %d, steps: %d " ,
avg_lum , desired_avg_lum , steps ) ;
if ( ( gain + steps ) > gain_high & &
exposure < gspca_dev - > exposure - > maximum ) {
gain = gain_high ;
gspca_dev - > exp_too_low_cnt + + ;
gspca_dev - > exp_too_high_cnt = 0 ;
} else if ( ( gain + steps ) < gain_low & &
exposure > gspca_dev - > exposure - > minimum ) {
gain = gain_low ;
gspca_dev - > exp_too_high_cnt + + ;
gspca_dev - > exp_too_low_cnt = 0 ;
} else {
gain + = steps ;
if ( gain > gspca_dev - > gain - > maximum )
gain = gspca_dev - > gain - > maximum ;
else if ( gain < gspca_dev - > gain - > minimum )
gain = gspca_dev - > gain - > minimum ;
gspca_dev - > exp_too_high_cnt = 0 ;
gspca_dev - > exp_too_low_cnt = 0 ;
}
if ( gspca_dev - > exp_too_high_cnt > 3 ) {
exposure - - ;
gspca_dev - > exp_too_high_cnt = 0 ;
} else if ( gspca_dev - > exp_too_low_cnt > 3 ) {
exposure + + ;
gspca_dev - > exp_too_low_cnt = 0 ;
}
if ( gain ! = orig_gain ) {
v4l2_ctrl_s_ctrl ( gspca_dev - > gain , gain ) ;
retval = 1 ;
}
if ( exposure ! = orig_exposure ) {
v4l2_ctrl_s_ctrl ( gspca_dev - > exposure , exposure ) ;
retval = 1 ;
}
if ( retval )
PDEBUG ( D_FRAM , " autogain: changed gain: %d, expo: %d " ,
gain , exposure ) ;
return retval ;
}
EXPORT_SYMBOL ( gspca_coarse_grained_expo_autogain ) ;