libv4l: speedup software autogain settling
authorHans de Goede <hdegoede@redhat.com>
Sun, 10 Jan 2010 07:40:17 +0000 (08:40 +0100)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 24 Feb 2010 00:44:40 +0000 (21:44 -0300)
On camera's with a gine grained exposure control like some mr97310a models,
the autogain algorithm would take ages to get to the desired point. This
patch considerably speeds up the autogain algorithm.

Priority: normal

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
lib/libv4lconvert/processing/autogain.c
lib/libv4lconvert/processing/libv4lprocessing.c

index abe5159..8a9bf27 100644 (file)
@@ -39,6 +39,30 @@ static int autogain_active(struct v4lprocessing_data *data) {
   return autogain;
 }
 
+/* Adjust ctrl value with steps steps, while not crossing limit */
+static int autogain_adjust(struct v4l2_queryctrl *ctrl, int *value,
+  int steps, int limit)
+{
+  int ctrl_range = (ctrl->maximum - ctrl->minimum) / ctrl->step;
+
+  /* If we are of 2 * deadzone or more, and we have a very fine grained
+     control, take larger steps, otherwise we take ages to get to the
+     right setting point. We use 256 as tripping point for determineing fine
+     grained controls here, as avg_lum has a range of 0 - 255. */
+  if (abs(steps) >= 2 && ctrl_range > 256)
+    *value += steps * ctrl->step * (ctrl_range / 256);
+  else
+    *value += steps * ctrl->step;
+
+  if (steps > 0) {
+    if (*value > limit)
+      *value = limit;
+  } else {
+    if (*value < limit)
+      *value = limit;
+  }
+}
+
 /* auto gain and exposure algorithm based on the knee algorithm described here:
    http://ytse.tricolour.net/docs/LowLightOptimization.html */
 static int autogain_calculate_lookup_tables(
@@ -63,9 +87,10 @@ static int autogain_calculate_lookup_tables(
      has hit this value */
   exposure_low = expoctrl.maximum / 10;
   /* If we have a fine grained exposure control only avoid the last 10 steps */
-  if (exposure_low > 10)
-    exposure_low = 10;
-  exposure_low += expoctrl.minimum;
+  steps = exposure_low / expoctrl.step;
+  if (steps > 10)
+    steps = 10;
+  exposure_low = steps * expoctrl.step + expoctrl.minimum;
 
   ctrl.id = V4L2_CID_GAIN;
   gainctrl.id = V4L2_CID_GAIN;
@@ -119,38 +144,43 @@ static int autogain_calculate_lookup_tables(
       (steps < 0 && data->last_gain_correction > 0))
     steps /= 2;
 
-  for (x = 0; x < abs(steps); x++) {
-    if (avg_lum > target) {
-      if (exposure > expoctrl.default_value)
-       exposure--;
-      else if (gain > gainctrl.default_value)
-       gain--;
-      else if (exposure > exposure_low)
-       exposure--;
-      else if (gain > gainctrl.minimum)
-       gain--;
-      else if (exposure > expoctrl.minimum)
-       exposure--;
-      else
-       break;
-    } else {
-      if (exposure < exposure_low)
-       exposure++;
-      else if (gain < gainctrl.default_value)
-       gain++;
-      else if (exposure < expoctrl.default_value)
-       exposure++;
-      else if (gain < gainctrl.maximum)
-       gain++;
-      else if (exposure < expoctrl.maximum)
-       exposure++;
-      else
-       break;
-    }
+  if (steps == 0)
+    return 0; /* Nothing to do */
+
+  if (steps < 0) {
+    if (exposure > expoctrl.default_value)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
+    else if (gain > gainctrl.default_value)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
+    else if (exposure > exposure_low)
+      autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
+    else if (gain > gainctrl.minimum)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.minimum);
+    else if (exposure > expoctrl.minimum)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.minimum);
+    else
+      steps = 0;
+  } else {
+    if (exposure < exposure_low)
+      autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
+    else if (gain < gainctrl.default_value)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
+    else if (exposure < expoctrl.default_value)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
+    else if (gain < gainctrl.maximum)
+      autogain_adjust(&gainctrl, &gain, steps, gainctrl.maximum);
+    else if (exposure < expoctrl.maximum)
+      autogain_adjust(&expoctrl, &exposure, steps, expoctrl.maximum);
+    else
+      steps = 0;
   }
 
-  if (steps)
+  if (steps) {
     data->last_gain_correction = steps;
+    /* We are still settling down, force the next update sooner. Note we
+       skip the next frame as that is still captured with the old settings. */
+    data->lookup_table_update_counter = V4L2PROCESSING_UPDATE_RATE - 1;
+  }
 
   if (gain != orig_gain) {
     ctrl.id = V4L2_CID_GAIN;
index 424173e..7ee6b0f 100644 (file)
@@ -173,9 +173,11 @@ void v4lprocessing_processing(struct v4lprocessing_data *data,
 
   if (data->controls_changed ||
       data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) {
-    v4lprocessing_update_lookup_tables(data, buf, fmt);
     data->controls_changed = 0;
     data->lookup_table_update_counter = 0;
+    /* Do this after resetting lookup_table_update_counter so that filters can
+       force the next update to be sooner when they changed camera settings */
+    v4lprocessing_update_lookup_tables(data, buf, fmt);
   } else
     data->lookup_table_update_counter++;