Histogram Based Bias for FLARM Auto Threshold Class

Here’s the auto threshold class I created for the FLAR Manager that I promised in my previous post. It uses a histogram of the source image to figure out what the bias level should be but is otherwise identical to the “drunk walk” thresholder Eric provides. It should adapt better to varying lighting conditions.

The bias calculation takes the histogram of the source and sums the lowest and highest 20 values for the 3 channels. It then uses these sums to decide if the bias should be negative, zero or positive. I did consider scaling the difference to give more resolution to the bias value but in my experiments, the main improvement was in getting the direction right and changes in the absolute bias value did not seem too significant.

To use it, save the code as DrunkWalkHistoThresholdAdapter.as in the src\com\transmote\flar\utils\threshold directory (note this is a modified version of a transmote class I wrote and should not be considered part of the “official” FLAR Manager library.  It is provided with the same license terms)

Follow the FLAR Manager instructions on customization or the alternative process outlined in my prior post.

/*
 * PROJECT: FLARManager
 * http://transmote.com/flar
 * Copyright 2009, Eric Socolofsky
 * --------------------------------------------------------------------------------
 * This work complements FLARToolkit, developed by Saqoosha as part of the Libspark project.
 *  http://www.libspark.org/wiki/saqoosha/FLARToolKit
 * FLARToolkit is Copyright (C)2008 Saqoosha,
 * and is ported from NYARToolkit, which is ported from ARToolkit.
 *
 * ******************* MODIFIED *************************
 * This version adds histogram for bias calculation
 * Author: Matt Reynolds
 * https://mattreyuk.wordpress.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 framework; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package com.transmote.flar.utils.threshold {
  import flash.display.BitmapData;

  /**
   * DrunkWalkHistoThresholdAdapter calculates a new threshold using weighted randomization.
   * when marker detection is poor, DrunkWalkHistoThresholdAdapter adjusts the threshold by moving a random amount
   * away from the current threshold value based on this.speed and a bias calculated from the source histogram.
   *
   * see the following URLs for more information:
   * http://blog.jactionscripters.com/2009/05/18/adaptive-thresholding-experiment/comment-page-1/#comment-367
   * http://makc3d.wordpress.com/2009/07/03/alternative-to-adaptive-thresholding/
   * https://mattreyuk.wordpress.com/2009/08/10/augmented-reality-via-flart-flarm/
   */
  public class DrunkWalkHistoThresholdAdapter implements IThresholdAdapter {
    private static const MIN_VARIANCE:Number = 5;
    private static const MAX_VARIANCE:Number = 50;

    private var _speed:Number;

    private var adaptiveThresholdingStep:Number = MIN_VARIANCE;

    public function DrunkWalkHistoThresholdAdapter(speed:Number = 0.3){
      this._speed = speed;
    }

    /**
     * init from a name-value paired object that contains parameters parsed from XML.
     */
    public function initFromXML(paramsObj:Object):void {
      if (!isNaN(paramsObj.speed)){
        this.speed = parseFloat(paramsObj.speed);
      }
    }

    /**
     * calculate a new threshold.
     * @param  source      used to calculate bias.
     * @param  currentThreshold  current threshold value.
     * @return            new threshold value.
     */
    public function calculateThreshold(source:BitmapData, currentThreshold:Number):Number {
      var thresholdAdaptationMod:Number = (Math.random() - 0.5 + 0.5 * calculateBias(source));
      this.adaptiveThresholdingStep = Math.min(Math.pow(this.adaptiveThresholdingStep, 1 + this._speed), MAX_VARIANCE);

      var newThreshold:Number = currentThreshold + (thresholdAdaptationMod * this.adaptiveThresholdingStep);
      newThreshold = Math.max(0, Math.min(newThreshold, 255));
      return newThreshold;
    }

    /**
     * reset calculations.
     */
    public function resetCalculations(currentThreshold:Number):void {
      this.adaptiveThresholdingStep = MIN_VARIANCE;
    }

    /**
     * free this instance for garbage collection.
     */
    public function dispose():void {
      //
    }

    /**
     * returns false;
     * DrunkWalkThresholdAdapter runs only when confidence is low (poor marker detection).
     */
    public function get runsEveryFrame():Boolean {
      return false;
    }

    /**
     * the speed at which the threshold changes during adaptive thresholding.
     * larger values may increase the speed at which the markers in uneven illumination are detected,
     * but may also result in instability in marker detection.
     *
     * value must be zero or greater.  the default is 0.3.
     * a value of zero will disable adaptive thresholding.
     */
    public function get speed():Number {
      return this._speed;
    }

    public function set speed(val:Number):void {
      this._speed = Math.max(0, val);
    }

    private function calculateBias(source:BitmapData):Number {
      var histogram:Vector.<Vector.<Number>> = source.histogram();
      var darkSum:Number = 0;
      var lightSum:Number = 0;
      var bias:Number = 0;

      //sum the darkest and lightest values
      for (var c:int = 0; c < 20; c++){
        darkSum += histogram[0][c] + histogram[1][c] + histogram[2][c];
        lightSum += histogram[0][c + 235] + histogram[1][c + 235] + histogram[2][c + 235];
      }
      //calculate bias
      if (darkSum > lightSum){
        //scene is dark, set threshold bias -ve
        bias = -0.5;
      } else if (lightSum > darkSum){
        //scene is light, set threshold bias +ve
        bias = 0.5;
      } else {
        //even - set threshold bias 0
        bias = 0;
      }
      return bias;
    }
  }
}

In case you’re wondering how my code presentation suddenly got so much better, it’s all down to this great tool

Advertisements
Posted in Flex. 7 Comments »

7 Responses to “Histogram Based Bias for FLARM Auto Threshold Class”

  1. ericsoco Says:

    thanks for this, matt!

  2. Ziom Says:

    This adapter causes the detector not to pick up any marker. Doesn’t work, thanks though!

    • Ziom Says:

      After some tinkering, the adapter seems to work when the marker is really close to the camera, otherwise if it’s few feet away it no worky. 😉 God luck!

      • mattreyuk Says:

        Ziom, It seems to work ok for me at various distances and lighting conditions. Eric added a version of this technique as an option in a later version of his FLAR Manager tool so you might try his version too.

    • Ziom Says:

      I have tried all of them, the one that works the best under my illumination conditions if the HistogramThresholdAdapter, it does the job well. Thanks for input!

  3. Sérgio Silva Says:

    I have made some experiences with your filter and IntegralImageThresholdAdapter, you can check a short resume in youtube:


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: