forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
4.9 KiB
165 lines
4.9 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Functions for auto gain. |
|
* |
|
* Copyright (C) 2010-2012 Hans de Goede <[email protected]> |
|
*/ |
|
#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; |
|
|
|
gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", |
|
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) |
|
gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n", |
|
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 oscillating 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); |
|
|
|
gain_low = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) / |
|
5 * 2 + gspca_dev->gain->minimum; |
|
gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) / |
|
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; |
|
|
|
gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", |
|
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) |
|
gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n", |
|
gain, exposure); |
|
return retval; |
|
} |
|
EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);
|
|
|