00001 /** 00002 \file Robots/LoBot/lgmd/rind/LoStafford.C 00003 \brief Stafford's LGMD model. 00004 */ 00005 00006 // //////////////////////////////////////////////////////////////////// // 00007 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00008 // by the University of Southern California (USC) and the iLab at USC. // 00009 // See http://iLab.usc.edu for information about this project. // 00010 // //////////////////////////////////////////////////////////////////// // 00011 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00012 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00013 // in Visual Environments, and Applications'' by Christof Koch and // 00014 // Laurent Itti, California Institute of Technology, 2001 (patent // 00015 // pending; application number 09/912,225 filed July 23, 2001; see // 00016 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00017 // //////////////////////////////////////////////////////////////////// // 00018 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00019 // // 00020 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00021 // redistribute it and/or modify it under the terms of the GNU General // 00022 // Public License as published by the Free Software Foundation; either // 00023 // version 2 of the License, or (at your option) any later version. // 00024 // // 00025 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00026 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00027 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00028 // PURPOSE. See the GNU General Public License for more details. // 00029 // // 00030 // You should have received a copy of the GNU General Public License // 00031 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00032 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00033 // Boston, MA 02111-1307 USA. // 00034 // //////////////////////////////////////////////////////////////////// // 00035 // 00036 // Primary maintainer for this file: mviswana usc edu 00037 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/lgmd/rind/LoStafford.C $ 00038 // $Id: LoStafford.C 13037 2010-03-23 01:00:53Z mviswana $ 00039 // 00040 00041 //------------------------------ HEADERS -------------------------------- 00042 00043 // lobot headers 00044 #include "Robots/LoBot/lgmd/rind/LoStafford.H" 00045 #include "Robots/LoBot/config/LoConfig.H" 00046 #include "Robots/LoBot/misc/LoRegistry.H" 00047 #include "Robots/LoBot/util/LoMath.H" 00048 00049 // INVT image support 00050 #include "Image/Convolver.H" 00051 #include "Image/CutPaste.H" 00052 #include "Image/MathOps.H" 00053 #include "Image/Rectangle.H" 00054 00055 // Standard C++ headers 00056 #include <numeric> 00057 #include <algorithm> 00058 #include <functional> 00059 #include <utility> 00060 #include <cmath> 00061 00062 //----------------------------- NAMESPACE ------------------------------- 00063 00064 namespace lobot { 00065 00066 //------------------------ STATIC DATA MEMBERS -------------------------- 00067 00068 /* 00069 All instances of the Stafford model share the neural net layers used 00070 to compute LGMD spikes so as to save on the amount of overall 00071 computation required. 00072 00073 Each instance represents a separate (virtual) locust looking in a 00074 different direction with a limited field of view. Thus, each instance 00075 is setup to read a subportion of the composited source image. These 00076 subportions are just the source image divided into consecutive 00077 vertical strips. For example, if we have a single camera grabbing 00078 320x240 images and use the default value of 32 pixels for each virtual 00079 locust's FOV, we will get 320/32 = 10 vertical strips and, therefore, 00080 10 virtual locusts. 00081 00082 Each virtual locust "monitors" the strip right in front of it (foveal 00083 vision) as well as the strips to the left and right (peripheral 00084 vision). If we were to treat each locust separately, we would perform 00085 subtraction, convolution, etc. for its strips and then repeat the 00086 operations for the overlapping strips of the neighbouring locusts. 00087 00088 This is extremely wasteful. So, to avoid repeating the same 00089 computations over and over again, we use the following static data 00090 members to store the layers of the neural net for the entire 00091 composited input image. When the update() method of the first instance 00092 of the Stafford model is invoked, we perform the necessary operations 00093 for the entire composited image. But then, each instance reads only 00094 its assigned subportion of the S-layer to compute its LGMD membrane 00095 potential. 00096 */ 00097 StaffordModel::layer StaffordModel::l_layer ; 00098 StaffordModel::layer StaffordModel::p_layer ; 00099 StaffordModel::layer StaffordModel::i_layer ; 00100 StaffordModel::layer StaffordModel::s_layer ; 00101 00102 // To enable the above-mentioned computational savings via neural net 00103 // layer sharing, we have to keep track of the total number of instances 00104 // of the Stafford model and the number for which the layer computations 00105 // have been performed during each instance's update cycle. 00106 int StaffordModel::m_instances ; 00107 int StaffordModel::m_layers_computed ; 00108 00109 //-------------------------- INITIALIZATION ----------------------------- 00110 00111 // Whenever a virtual locust based on the Stafford model is created, we 00112 // need to bump up the number of instances to ensure that the sharing 00113 // described above works properly. 00114 StaffordModel::StaffordModel(const LocustModel::InitParams& p) 00115 : base(p) 00116 { 00117 ++m_instances ; 00118 } 00119 00120 //------------------------ LAYER COMPUTATIONS --------------------------- 00121 00122 // This method performs the necessary layer computations, taking care to 00123 // do them only once, i.e., for the first instance for which the LGMD 00124 // update cycle is invoked and then skipping them for the rest. 00125 void StaffordModel::compute_layers() 00126 { 00127 if (m_layers_computed == 0) 00128 { 00129 prime_previous_layers() ; 00130 00131 l_layer.current = m_source->get_grayscale_image() ; 00132 00133 p_layer.current = absDiff(l_layer.current, l_layer.previous) ; 00134 00135 GrayImage kernel(3, 3, NO_INIT) ; 00136 std::fill(kernel.beginw(), kernel.endw(), 1/9.0f) ; 00137 Convolver C(kernel, i_layer.previous.getDims()) ; 00138 i_layer.current = 00139 C.spatialConvolve((p_layer.current + p_layer.previous) * .25) ; 00140 00141 s_layer.current = p_layer.current - i_layer.previous * 2 ; 00142 std::transform(s_layer.current.begin(), s_layer.current.end(), 00143 s_layer.current.beginw(), 00144 std::bind2nd(max<float>(), 0)) ; // discard -ve values 00145 } 00146 ++m_layers_computed ; 00147 } 00148 00149 // Once all instances have finished with their LGMD update, we need to 00150 // move the current layers into the previous time-step's slot. And, to 00151 // ensure smooth operation of the sharing described at the start of this 00152 // file, we need to reset the number of instances for which the LGMD 00153 // computations have been done. Without this, the next update cycle will 00154 // fail. 00155 void StaffordModel::reset_layers() 00156 { 00157 if (m_layers_computed == m_instances) 00158 { 00159 l_layer.previous = l_layer.current ; 00160 p_layer.previous = p_layer.current ; 00161 i_layer.previous = i_layer.current ; 00162 s_layer.previous = s_layer.current ; 00163 00164 m_layers_computed = 0 ; 00165 } 00166 } 00167 00168 // We need to prime the pump for the very first update cycle. At this 00169 // point, there are no previous time-steps for any of the layers. As a 00170 // starting point, we use the current image as the input for the 00171 // L-layer's previous time-step and just keep all the other layers empty. 00172 void StaffordModel::prime_previous_layers() 00173 { 00174 if (! l_layer.previous.initialized()) 00175 l_layer.previous = m_source->get_grayscale_image() ; 00176 00177 if (! p_layer.previous.initialized()) 00178 p_layer.previous.resize(l_layer.previous.getDims(), true) ; 00179 00180 if (! i_layer.previous.initialized()) 00181 i_layer.previous.resize(l_layer.previous.getDims(), true) ; 00182 00183 if (! s_layer.previous.initialized()) 00184 s_layer.previous.resize(l_layer.previous.getDims(), true) ; 00185 } 00186 00187 //------------------------- DSMD COMPUTATION ---------------------------- 00188 00189 // This helper routine returns the appropriate block size to use given 00190 // the total number of pixels along the desired dimension (i.e., x- or 00191 // y-direction). 00192 static int dsmd_block_size(int dim, int ideal, int alt) 00193 { 00194 int block_size = ideal ; 00195 int num_emds = dim/block_size ; 00196 if (num_emds < 2) 00197 block_size = alt ; // just hope this works! 00198 return block_size ; 00199 } 00200 00201 // This method uses three functions or function objects to compute the 00202 // DSMD membrane potential for the desired direction. 00203 // 00204 // The first function/function object returns the appropriate dimension 00205 // of the locust's "viewing" rectangle/window to the world. For 00206 // horizontal DSMDs, this function would return the width of the 00207 // rectangle; for vertical DSMDs, the height. 00208 // 00209 // The second parameter is a function/function object that computes the 00210 // DSMD block along the appropriate dimension. For horizontal DSMDs, it 00211 // should compute the bounds of the ith DSMD block running horizontally 00212 // along the center of the image. For vertical DSMDs, it should compute 00213 // the bounds of the ith DSMD block running vertically along the center 00214 // of the image. 00215 // 00216 // The third parameter computes the membrane potential using the 00217 // rectangular DSMD blocks returned by the second parameter and the 00218 // current and previous S-layer contents of those blocks. 00219 template<typename rect_dim, typename rect_comp, typename pot_comp> 00220 float StaffordModel::compute_dsmd_potential(rect_dim dimension, 00221 rect_comp compute_dsmd_rect, 00222 pot_comp compute_potential) 00223 { 00224 int d = dimension(m_rect) ; // width or height of this locust's FOV 00225 int b = dsmd_block_size(d, Params::ideal_dsmd_block_size(), 00226 Params::alt_dsmd_block_size()) ; 00227 int n = d/b ; // number of EMDs for the DSMD under consideration 00228 00229 float P = 0 ; // DSMD membrane potential 00230 Rectangle R1 = compute_dsmd_rect(0, b, m_rect) ; 00231 for (int i = 1; i < n; ++i) { 00232 Rectangle R2 = compute_dsmd_rect(i, b, m_rect) ; 00233 P += compute_potential(s_layer.current, s_layer.previous, R1, R2) ; 00234 R1 = R2 ; 00235 } 00236 return P ; 00237 } 00238 00239 // rect_dim functions for above template method 00240 static int rect_width (const Rectangle& R) {return R.width() ;} 00241 static int rect_height(const Rectangle& R) {return R.height() ;} 00242 00243 // rect_comp functions for above template method 00244 static Rectangle 00245 compute_horz_dsmd_rect(int i, int block_size, const Rectangle& R) 00246 { 00247 int left = std::max(i * block_size, 0) ; 00248 int right = std::min(left + block_size, R.width()) ; 00249 int top = std::max((R.height() - block_size)/2, 0) ; 00250 int bottom = std::min(top + block_size, R.height()) ; 00251 return Rectangle::tlbrI(top, left, bottom, right) ; 00252 } 00253 00254 static Rectangle 00255 compute_vert_dsmd_rect(int i, int block_size, const Rectangle& R) 00256 { 00257 int left = std::max((R.width() - block_size)/2, 0) ; 00258 int right = std::min(left + block_size, R.width()) ; 00259 int top = std::max(i * block_size, 0) ; 00260 int bottom = std::min(top + block_size, R.height()) ; 00261 return Rectangle::tlbrI(top, left, bottom, right) ; 00262 } 00263 00264 // A quick helper to compute the membrane potential corresponding to the 00265 // specified rectangular portion of the supplied image (usually, the 00266 // S-layer). 00267 static float membrane_potential(const GrayImage& I, const Rectangle& R) 00268 { 00269 return sum(crop(I, R.topLeft(), R.dims() - 1)) ; 00270 } 00271 00272 // The following two functions are for use as the third pot_comp 00273 // parameter of the compute_dsmd_potential() template method defined 00274 // above. 00275 static float 00276 compute_dsmd_potential_R1_current(const GrayImage& current, 00277 const GrayImage& previous, 00278 const Rectangle& R1, const Rectangle& R2) 00279 { 00280 return membrane_potential(current, R1) * membrane_potential(previous, R2) ; 00281 } 00282 00283 static float 00284 compute_dsmd_potential_R2_current(const GrayImage& current, 00285 const GrayImage& previous, 00286 const Rectangle& R1, const Rectangle& R2) 00287 { 00288 return compute_dsmd_potential_R1_current(current, previous, R2, R1) ; 00289 } 00290 00291 // Given the spike count of some DSMD (e.g., left or up) and its opposite 00292 // (i.e., right or down), this function determines whether there has been 00293 // only lateral motion rather than motion in both directions. 00294 // 00295 // Since the spike counts are either 0 or 1, checking for lateral motion 00296 // is a simple matter of an XOR operation. For example, motion in the 00297 // left DSMD will register as 1 and none in the right DSMD will be zero. 00298 // If XOR these two values, we will get 1, indicating that motion was 00299 // detected in only one direction. However, if there was motion in both 00300 // directions (or in neither), the XOR will return false. 00301 // 00302 // To make it easy to compute the final spike count from the individual 00303 // spike counts of the LGMD and DSMDs and their corresponding weights, 00304 // the counts are stored as floats rather than ints. So before we can 00305 // apply the XOR operator, we convert the supplied counts to ints. 00306 /* 00307 static bool lateral_motion(float d1, float d2) 00308 { 00309 int a = (d1 > .5) ; 00310 int b = (d2 > .5) ; 00311 return a ^ b ; 00312 } 00313 00314 static inline bool uniform_expansion(float d1, float d2) 00315 { 00316 return ! lateral_motion(d1, d2) ; 00317 } 00318 //*/ 00319 00320 //* 00321 static bool lateral_motion(float p1, float p2, float threshold) 00322 { 00323 float p = p1/p2 ; 00324 return (p > threshold) || (p < 1/threshold) ; 00325 } 00326 //*/ 00327 00328 //---------------------------- LGMD UPDATE ------------------------------ 00329 00330 // The following function object uses the sigmoid formula described 00331 // earlier (see comment preceding definition of the AREA_MAGNIFIERS 00332 // array) to scale raw membrane potentials to the [.5,1] range. 00333 namespace { 00334 00335 class sigmoid { 00336 float area ; 00337 const float* magnifiers ; 00338 mutable int i ; 00339 public: 00340 sigmoid(float, const float*) ; 00341 float operator()(float) const ; 00342 } ; 00343 00344 sigmoid::sigmoid(float A, const float* M) 00345 : area(A), magnifiers(M), i(0) 00346 {} 00347 00348 float sigmoid::operator()(float potential) const 00349 { 00350 return 1/(1 + exp(-potential/(area * magnifiers[i++]))) ; 00351 } 00352 00353 } 00354 00355 // Quick helper macro for debug support 00356 #define LOSTU_DUMP(array) \ 00357 dump(array, array + NUM_NEURONS, "StaffordUpdate", #array) 00358 00359 // This is the main interface routine for updating the LGMD "value" based 00360 // on the latest frame acquired from the input video sources. After doing 00361 // the layer computations as described in the Stafford paper, it computes 00362 // the raw membrane potential of the LGMD by summing all the pixel values 00363 // in the current S-layer. Then, it scales this potential down to [.5, 1] 00364 // by applying a sigmoid function. Whenever this scaled down potential 00365 // exceeds a predetermined (manual) threshold, it is counted as an LGMD 00366 // spike. 00367 // 00368 // In addition to the LGMD spike, we also compute the FFI and DSMD 00369 // potentials, scale them using the same sigmoid and the count them as 00370 // spikes if they exceed their respective spiking thresholds. 00371 // 00372 // The final instantaneous spike rate is computed as a running average of 00373 // a weighted sum of the LGMD and DSMD spike counts. 00374 // 00375 // The running average is computed using the usual formula, i.e.: 00376 // running average = weight * current + (1 - weight) * previous 00377 // 00378 // But before we can apply the above formula, we need to first normalize 00379 // the previous spike rate value, i.e., scale it down to [0,1], because 00380 // the current spike, being a weighted sum of either zeros or ones, will 00381 // be a number in the range [0,1]. We can't blithely compute a running 00382 // average using one number in [min, max] and another in [0,1]; the 00383 // result will be horribly wrong. 00384 // 00385 // Once the previous spike rate value has been normalized, we compute the 00386 // running average using it and the current spike count. Then, we scale 00387 // the result back to its usual min/max range. 00388 // 00389 // NOTE: The sigmoid thingummy is not described in the Stafford paper. 00390 // Rather it is in the following paper: 00391 // 00392 // Yue, S., Santer, R. D., Yamawaki, Y., Rind, F. C. 00393 // Reactive direction control for a mobile robot: A locust-like 00394 // control of escape direction emerges when a bilateral pair of model 00395 // locust visual neurons are integrated. 00396 // Submitted to Autonmous Robots in 2008. 00397 // 00398 // ALSO: This function implements feed-forward and lateral motion 00399 // inhibition as described in the Stafford paper as well as the one 00400 // mentioned above. However, this inhibition does not work very well. So 00401 // we setup the weights and other relevant parameters defined at the 00402 // beginning of this file to ignore the FFI and DSMD neurons. We could 00403 // just as well remove all this unused/dead code. But it's left alone 00404 // just in case a magic wand shows up and enables us to figure out how to 00405 // get this weird and bogus model to actually behave like the LGMD (i.e., 00406 // ignore lateral motion and respond preferentially to collisions). 00407 void StaffordModel::update() 00408 { 00409 compute_layers() ; 00410 00411 float potentials[NUM_NEURONS] = {0} ; 00412 potentials[LGMD] = membrane_potential(s_layer.current, m_rect) ; 00413 potentials[FFI] = membrane_potential(p_layer.previous, m_rect) ; 00414 potentials[DSMD_LEFT] = 00415 compute_dsmd_potential(rect_width, compute_horz_dsmd_rect, 00416 compute_dsmd_potential_R1_current) ; 00417 potentials[DSMD_RIGHT] = 00418 compute_dsmd_potential(rect_width, compute_horz_dsmd_rect, 00419 compute_dsmd_potential_R2_current) ; 00420 potentials[DSMD_UP] = 00421 compute_dsmd_potential(rect_height, compute_vert_dsmd_rect, 00422 compute_dsmd_potential_R1_current) ; 00423 potentials[DSMD_DOWN] = 00424 compute_dsmd_potential(rect_height, compute_vert_dsmd_rect, 00425 compute_dsmd_potential_R2_current) ; 00426 //LOSTU_DUMP(potentials) ; 00427 00428 float scaled_potentials[NUM_NEURONS] = {0} ; 00429 std::transform(potentials, potentials + NUM_NEURONS, scaled_potentials, 00430 sigmoid(m_rect.area(), Params::area_magnifiers())) ; 00431 //LOSTU_DUMP(scaled_potentials) ; 00432 00433 float spikes[NUM_NEURONS] = {0} ; 00434 std::transform(scaled_potentials, scaled_potentials + NUM_NEURONS, 00435 Params::spike_thresholds(), spikes, std::greater<float>()) ; 00436 //LOSTU_DUMP(spikes) ; 00437 00438 if (suppress_lgmd(spikes, potentials)) { 00439 update_lgmd(0) ; 00440 std::fill(spikes, spikes + NUM_NEURONS, 0) ; 00441 } 00442 00443 // Even if the LGMD has been suppressed, the FFI and DSMD neurons may 00444 // still be able to produce some spiking from the LGMD network. The 00445 // following dot product computes a weighted sum of each neuron's 00446 // spike to produce the final spiking decision. 00447 // 00448 // NOTE: This weighted sum procedure is not described in any of the 00449 // LGMD related papers by Stafford, Blanchard, Yue, Rind and gang. 00450 // It's just something the author of this class made up to try and get 00451 // this model to work properly (i.e., suppress lateral motions and 00452 // respond only to collisional motion). It can be turned off by 00453 // setting the LGMD spike weight to 1 and the FFI and DSMD spike 00454 // weights to zero. Then, only the LGMD spike will be considered in 00455 // the final decision regarding whether or not the LGMD network emits 00456 // a spike or not (subject, of course, to the suppression signal 00457 // emitted by the FFI or DSMD). 00458 float spike = std::inner_product(spikes, spikes + NUM_NEURONS, 00459 Params::spike_weights(), 0.0f) ; 00460 00461 const float min = get_range().min() ; 00462 const float max = get_range().max() ; 00463 00464 float normalized_lgmd = (get_lgmd() - min)/(max - min) ; 00465 float w = Params::running_average_weight() ; 00466 float lgmd_avg = w * spike + (1 - w) * normalized_lgmd ; 00467 update_lgmd(min + lgmd_avg * (max - min)) ;//rescale from [0,1] to [min,max] 00468 //LINFO("final spike = %g, old spike = %g, new spike = %g", 00469 //spike, normalized_lgmd, lgmd_avg) ; 00470 //LINFO("LGMD rate = %g", getLGMD()) ; 00471 00472 reset_layers() ; 00473 } 00474 00475 // Check if LGMD suppression is on via either the FFI or DSMDs and their 00476 // corresponding suppression tests. 00477 bool StaffordModel::suppress_lgmd(const float spikes[], 00478 const float potentials[]) 00479 { 00480 if (Params::ffi_on()) // check for whole field motion 00481 return spikes[FFI] > 0.5 ; 00482 00483 if (Params::dsmd_on()) //chk directionally sensitive neurons for lat. motion 00484 return lateral_motion(potentials[DSMD_LEFT], potentials[DSMD_RIGHT], 00485 Params::horizontal_motion_threshold()) 00486 || lateral_motion(potentials[DSMD_UP], potentials[DSMD_DOWN], 00487 Params::vertical_motion_threshold()) ; 00488 00489 return false ; 00490 } 00491 00492 //----------------------------- CLEAN-UP -------------------------------- 00493 00494 StaffordModel::~StaffordModel() 00495 { 00496 --m_instances ; 00497 --m_layers_computed ; 00498 if (m_layers_computed < 0) 00499 m_layers_computed = 0 ; 00500 } 00501 00502 //-------------------------- KNOB TWIDDLING ----------------------------- 00503 00504 // Quick helper to retrieve configuration settings from the stafford 00505 // section of the config file. 00506 template<typename T> 00507 static T conf(const std::string& key, const T& default_value) 00508 { 00509 return Configuration::get<T>(LOLM_STAFFORD, key, default_value) ; 00510 } 00511 00512 // Quick helper macro to retrieve lists from the stafford section of the 00513 // config file. 00514 #define LOST_GETCONF_ARRAY(key) \ 00515 Configuration::get(LOLM_STAFFORD, #key, m_##key, \ 00516 default_##key, NUM_NEURONS) 00517 00518 // Parameters initialization 00519 StaffordModel::Params::Params() 00520 : m_running_average_weight(conf("running_average_weight", 0.25)), 00521 m_ffi_on(conf("ffi", false)), 00522 m_dsmd_on(conf("dsmd", false)), 00523 m_horizontal_motion_threshold(conf("horizontal_motion_threshold", 1.5)), 00524 m_vertical_motion_threshold(conf("vertical_motion_threshold", 2.5)), 00525 m_ideal_dsmd_block_size(conf("ideal_dsmd_block_size", 10)), 00526 m_alt_dsmd_block_size(conf("alt_dsmd_block_size", 4)) 00527 { 00528 float default_spike_thresholds[] = {.99, 1.5, 1.5, 1.5, 1.5, 1.5} ; 00529 LOST_GETCONF_ARRAY(spike_thresholds) ; 00530 00531 float default_spike_weights[] = {1, 0, 0, 0, 0, 0} ; 00532 LOST_GETCONF_ARRAY(spike_weights) ; 00533 00534 float default_area_magnifiers[] = {2, 5, 500, 500, 500, 500} ; 00535 LOST_GETCONF_ARRAY(area_magnifiers) ; 00536 } 00537 00538 // Parameters clean-up 00539 StaffordModel::Params::~Params(){} 00540 00541 // Parameters access 00542 const float* StaffordModel::Params::spike_thresholds() 00543 { 00544 return instance().m_spike_thresholds ; 00545 } 00546 00547 const float* StaffordModel::Params::spike_weights() 00548 { 00549 return instance().m_spike_weights ; 00550 } 00551 00552 const float* StaffordModel::Params::area_magnifiers() 00553 { 00554 return instance().m_area_magnifiers ; 00555 } 00556 00557 float StaffordModel::Params::running_average_weight() 00558 { 00559 return instance().m_running_average_weight ; 00560 } 00561 00562 bool StaffordModel::Params::ffi_on() 00563 { 00564 return instance().m_ffi_on ; 00565 } 00566 00567 bool StaffordModel::Params::dsmd_on() 00568 { 00569 return instance().m_dsmd_on ; 00570 } 00571 00572 float StaffordModel::Params::horizontal_motion_threshold() 00573 { 00574 return instance().m_horizontal_motion_threshold ; 00575 } 00576 00577 float StaffordModel::Params::vertical_motion_threshold() 00578 { 00579 return instance().m_vertical_motion_threshold ; 00580 } 00581 00582 int StaffordModel::Params::ideal_dsmd_block_size() 00583 { 00584 return instance().m_ideal_dsmd_block_size ; 00585 } 00586 00587 int StaffordModel::Params::alt_dsmd_block_size() 00588 { 00589 return instance().m_alt_dsmd_block_size ; 00590 } 00591 00592 //----------------------------------------------------------------------- 00593 00594 } // end of namespace encapsulating this file's definitions 00595 00596 /* So things look consistent in everyone's emacs... */ 00597 /* Local Variables: */ 00598 /* indent-tabs-mode: nil */ 00599 /* End: */