00001 /*!@file Envision/env_channel.c Base class for channels that will use integer math */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Envision/env_channel.c $ 00035 // $Id: env_channel.c 9830 2008-06-18 18:50:22Z lior $ 00036 // 00037 00038 #ifndef ENVISION_ENV_CHANNEL_C_DEFINED 00039 #define ENVISION_ENV_CHANNEL_C_DEFINED 00040 00041 #include "Envision/env_channel.h" 00042 00043 #include "Envision/env_c_math_ops.h" 00044 #include "Envision/env_image_ops.h" 00045 #include "Envision/env_log.h" 00046 #include "Envision/env_params.h" 00047 00048 #ifndef ENV_NO_DEBUG 00049 00050 //! Check whether the pyramid is dyadic. 00051 /*! A dyadic pyramid is one in which each level is one half 00052 the width and one half the height of the preceding level. */ 00053 static int is_dyadic(const struct env_pyr* pyr, 00054 const env_size_t first, const env_size_t last) 00055 { 00056 if (first == last) return 0; 00057 00058 for (env_size_t i = first + 1; i < last; ++i) 00059 { 00060 const struct env_dims prevdims = env_pyr_img(pyr, i-1)->dims; 00061 const struct env_dims curdims = env_pyr_img(pyr, i)->dims; 00062 00063 // make sure we don't go below 1 00064 const env_size_t pw2 = ENV_MAX(prevdims.w/2,((env_size_t) 1)); 00065 const env_size_t ph2 = ENV_MAX(prevdims.h/2,((env_size_t) 1)); 00066 00067 if (curdims.w != pw2) return 0; 00068 if (curdims.h != ph2) return 0; 00069 } 00070 00071 return 1; 00072 } 00073 00074 #endif 00075 00076 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00077 00078 // ###################################################################### 00079 static void abs_diff_thresh(const struct env_image* b, 00080 const struct env_image* c, 00081 const intg32 thresh, 00082 struct env_image* result) 00083 { 00084 ENV_ASSERT(env_dims_equal(b->dims, c->dims)); 00085 ENV_ASSERT(env_dims_equal(b->dims, result->dims)); 00086 00087 const intg32* const bptr = env_img_pixels(b); 00088 const intg32* const cptr = env_img_pixels(c); 00089 intg32* const dptr = env_img_pixelsw(result); 00090 00091 const env_size_t sz = env_img_size(b); 00092 00093 for (env_size_t i = 0; i < sz; ++i) 00094 { 00095 dptr[i] = 00096 (bptr[i] < cptr[i]) 00097 ? cptr[i] - bptr[i] 00098 : bptr[i] - cptr[i]; 00099 00100 if (dptr[i] < thresh) dptr[i] = 0; 00101 } 00102 } 00103 00104 // ###################################################################### 00105 static void abs_diff_thresh_pyr(const struct env_pyr* b, 00106 const struct env_pyr* c, 00107 const intg32 thresh, 00108 struct env_pyr* result) 00109 { 00110 ENV_ASSERT(env_pyr_depth(b) == env_pyr_depth(c)); 00111 ENV_ASSERT(env_pyr_depth(c) == env_pyr_depth(result)); 00112 00113 const env_size_t n = env_pyr_depth(b); 00114 00115 if (env_pyr_depth(result) != n) 00116 { 00117 env_pyr_make_empty(result); 00118 env_pyr_init(result, n); 00119 } 00120 00121 for (env_size_t i = 0; i < n; ++i) 00122 { 00123 const struct env_image* bimg = env_pyr_img(b, i); 00124 00125 if (!env_img_initialized(bimg)) 00126 continue; 00127 00128 // else... 00129 00130 const struct env_image* cimg = env_pyr_img(c, i); 00131 struct env_image* rimg = env_pyr_imgw(result, i); 00132 00133 env_img_resize_dims(rimg, bimg->dims); 00134 00135 abs_diff_thresh(bimg, cimg, thresh, rimg); 00136 } 00137 } 00138 00139 #endif // ENV_WITH_DYNAMIC_CHANNELS 00140 00141 // ###################################################################### 00142 void env_chan_process_pyr(const char* tagName, 00143 const struct env_dims inputDims, 00144 const struct env_pyr* pyr, 00145 const struct env_params* envp, 00146 const struct env_math* imath, 00147 const int takeAbs, 00148 const int normalizeOutput, 00149 struct env_image* result) 00150 { 00151 const struct env_dims mapDims = 00152 { ENV_MAX(inputDims.w / (1 << envp->output_map_level), 1), 00153 ENV_MAX(inputDims.h / (1 << envp->output_map_level), 1) }; 00154 00155 if (env_pyr_depth(pyr) == 0) 00156 // OK, our pyramid wasn't ready to give us any output 00157 // yet, so just return an empty output image: 00158 { 00159 env_img_make_empty(result); 00160 return; 00161 } 00162 00163 // We only want dyadic pyramids here: 00164 ENV_ASSERT(is_dyadic(pyr, envp->cs_lev_min, env_max_pyr_depth(envp))); 00165 00166 env_img_resize_dims(result, mapDims); 00167 00168 { 00169 const env_size_t mapSize = mapDims.w * mapDims.h; 00170 intg32* const rptr = env_img_pixelsw(result); 00171 for (env_size_t i = 0; i < mapSize; ++i) 00172 rptr[i] = 0; 00173 } 00174 00175 // compute max-normalized weighted sum of center-surround at all levels: 00176 for (env_size_t clev = envp->cs_lev_min; clev <= envp->cs_lev_max; ++clev) 00177 for (env_size_t delta = envp->cs_del_min; delta <= envp->cs_del_max; ++delta) 00178 { 00179 const env_size_t slev = clev + delta; 00180 00181 // submap is computed from a center-surround difference: 00182 struct env_image submap; 00183 env_img_init(&submap, env_pyr_img(pyr, clev)->dims); 00184 env_center_surround(env_pyr_img(pyr, clev), 00185 env_pyr_img(pyr, slev), 00186 takeAbs, &submap); 00187 #ifdef ENV_WITH_VISIT_CHANNEL 00188 if (envp->submapPreProc != 0) 00189 (*envp->submapPreProc)(tagName, clev, slev, &submap, 00190 env_pyr_img(pyr, clev), env_pyr_img(pyr, slev) ); 00191 #endif 00192 00193 // resize submap to fixed scale if necessary: 00194 if (submap.dims.w > mapDims.w 00195 || submap.dims.h > mapDims.h) 00196 { 00197 // how many levels to we need to 00198 // downscale the current submap to get 00199 // to the output map resolution? 00200 const env_size_t n = 00201 envp->output_map_level - clev; 00202 00203 env_downsize_9_inplace(&submap, n, imath); 00204 } 00205 else if (submap.dims.w < mapDims.w 00206 || submap.dims.h < mapDims.h) 00207 { 00208 struct env_image tmp; 00209 env_img_init(&tmp, mapDims); 00210 env_rescale(&submap, &tmp); 00211 env_img_swap(&submap, &tmp); 00212 } 00213 00214 // make sure that the resizing came out 00215 // precisely: 00216 ENV_ASSERT(env_dims_equal(submap.dims, mapDims)); 00217 00218 // first normalize the submap to a fixed 00219 // dynamic range and then apply spatial 00220 // competition for salience to the submap: 00221 env_max_normalize_inplace 00222 (&submap, INTMAXNORMMIN, INTMAXNORMMAX, 00223 envp->maxnorm_type, 00224 envp->range_thresh); 00225 00226 #ifdef ENV_WITH_VISIT_CHANNEL 00227 if (envp->submapPostNormProc != 0) 00228 (*envp->submapPostNormProc)(tagName, clev, slev, &submap, 00229 env_pyr_img(pyr, clev), env_pyr_img(pyr, slev) ); 00230 #endif 00231 00232 // add submap to our sum 00233 env_c_image_div_scalar_accum 00234 (env_img_pixels(&submap), 00235 env_img_size(&submap), 00236 (intg32) env_max_cs_index(envp), 00237 env_img_pixelsw(result)); 00238 00239 env_img_make_empty(&submap); 00240 } 00241 00242 #ifdef ENV_WITH_VISIT_CHANNEL 00243 if (envp->submapPostProc != 0) 00244 (*envp->submapPostProc)(tagName, result); 00245 #endif 00246 00247 // apply max-normalization on the result as needed: 00248 if (normalizeOutput) 00249 env_max_normalize_inplace 00250 (result, INTMAXNORMMIN, INTMAXNORMMAX, 00251 envp->maxnorm_type, 00252 envp->range_thresh); 00253 } 00254 00255 // ###################################################################### 00256 void env_chan_intensity(const char* tagName, 00257 const struct env_params* envp, 00258 const struct env_math* imath, 00259 const struct env_dims inputdims, 00260 const struct env_pyr* lowpass5, 00261 const int normalizeOutput, 00262 env_chan_status_func* status_func, 00263 void* status_userdata, 00264 struct env_image* result) 00265 { 00266 env_chan_process_pyr(tagName, inputdims, lowpass5, 00267 envp, 00268 imath, 00269 1, // takeAbs 00270 normalizeOutput, 00271 result); 00272 00273 if (status_func) 00274 (*status_func)(status_userdata, tagName, result); 00275 } 00276 00277 // ###################################################################### 00278 void env_chan_color(const char* tagName, 00279 const struct env_params* envp, 00280 const struct env_math* imath, 00281 const struct env_rgb_pixel* const colimg, 00282 const struct env_rgb_pixel* const prev_colimg, 00283 const struct env_dims dims, 00284 env_chan_status_func* status_func, 00285 void* status_userdata, 00286 struct env_image* result) 00287 { 00288 struct env_image rg; env_img_init(&rg, dims); 00289 struct env_image by; env_img_init(&by, dims); 00290 00291 const intg32 lumthresh = (3*255) / 10; 00292 env_get_rgby(colimg, prev_colimg, dims.w * dims.h, 00293 &rg, &by, lumthresh, imath->nbits); 00294 00295 const env_size_t firstlevel = envp->cs_lev_min; 00296 const env_size_t depth = env_max_pyr_depth(envp); 00297 00298 { 00299 struct env_pyr rgpyr; 00300 env_pyr_init(&rgpyr, depth); 00301 env_pyr_build_lowpass_5(&rg, firstlevel, imath, &rgpyr); 00302 00303 env_chan_intensity("red/green", envp, imath, 00304 rg.dims, &rgpyr, 0, 00305 status_func, status_userdata, result); 00306 00307 env_pyr_make_empty(&rgpyr); 00308 } 00309 00310 struct env_image byOut = env_img_initializer; 00311 00312 { 00313 struct env_pyr bypyr; 00314 env_pyr_init(&bypyr, depth); 00315 env_pyr_build_lowpass_5(&by, firstlevel, imath, &bypyr); 00316 00317 env_chan_intensity("blue/yellow", envp, imath, 00318 by.dims, &bypyr, 0, 00319 status_func, status_userdata, &byOut); 00320 env_pyr_make_empty(&bypyr); 00321 } 00322 00323 env_img_make_empty(&rg); 00324 env_img_make_empty(&by); 00325 00326 const intg32* const byptr = env_img_pixels(&byOut); 00327 intg32* const dptr = env_img_pixelsw(result); 00328 const env_size_t sz = env_img_size(result); 00329 00330 for (env_size_t i = 0; i < sz; ++i) 00331 dptr[i] = (dptr[i] / 2) + (byptr[i] / 2); 00332 00333 env_max_normalize_inplace(result, INTMAXNORMMIN, INTMAXNORMMAX, 00334 envp->maxnorm_type, 00335 envp->range_thresh); 00336 if (status_func) 00337 (*status_func)(status_userdata, tagName, result); 00338 00339 env_img_make_empty(&byOut); 00340 } 00341 00342 // ###################################################################### 00343 void env_chan_steerable(const char* tagName, 00344 const struct env_params* envp, 00345 const struct env_math* imath, 00346 const struct env_dims inputdims, 00347 const struct env_pyr* hipass9, 00348 const env_size_t thetaidx, 00349 env_chan_status_func* status_func, 00350 void* status_userdata, 00351 struct env_image* result) 00352 { 00353 const env_size_t kdenombits = ENV_TRIG_NBITS; 00354 00355 // spatial_freq = 2.6 / (2*pi) ~= 0.41380285203892792 ~= 2069/5000 00356 00357 const intg32 sfnumer = 2069; 00358 const intg32 sfdenom = 5000; 00359 00360 const intg32 kxnumer = ((intg32) (sfnumer * imath->costab[thetaidx] * ENV_TRIG_TABSIZ)) / sfdenom; 00361 const intg32 kynumer = ((intg32) (sfnumer * imath->sintab[thetaidx] * ENV_TRIG_TABSIZ)) / sfdenom; 00362 00363 // Compute our pyramid: 00364 struct env_pyr pyr = env_pyr_initializer; 00365 env_pyr_build_steerable_from_hipass_9(hipass9, 00366 kxnumer, kynumer, kdenombits, 00367 imath, 00368 &pyr); 00369 00370 env_chan_process_pyr(tagName, inputdims, &pyr, 00371 envp, 00372 imath, 00373 0, // takeAbs 00374 1, // normalizeOutput 00375 result); 00376 00377 if (status_func) 00378 (*status_func)(status_userdata, tagName, result); 00379 00380 env_pyr_make_empty(&pyr); 00381 } 00382 00383 // ###################################################################### 00384 void env_chan_orientation(const char* tagName, 00385 const struct env_params* envp, 00386 const struct env_math* imath, 00387 const struct env_image* img, 00388 env_chan_status_func* status_func, 00389 void* status_userdata, 00390 struct env_image* result) 00391 { 00392 env_img_make_empty(result); 00393 00394 if (envp->num_orientations == 0) 00395 return; 00396 00397 struct env_pyr hipass9; 00398 env_pyr_init(&hipass9, env_max_pyr_depth(envp)); 00399 env_pyr_build_hipass_9(img, 00400 envp->cs_lev_min, 00401 imath, 00402 &hipass9); 00403 00404 struct env_image chanOut = env_img_initializer; 00405 00406 char buf[17] = 00407 { 00408 's', 't', 'e', 'e', 'r', 'a', 'b', 'l', 'e', // 0--8 00409 '(', '_', '_', // 9--11 00410 '/', '_', '_', ')', '\0' // 12--16 00411 }; 00412 00413 ENV_ASSERT(envp->num_orientations <= 99); 00414 00415 buf[13] = '0' + (envp->num_orientations / 10); 00416 buf[14] = '0' + (envp->num_orientations % 10); 00417 00418 for (env_size_t i = 0; i < envp->num_orientations; ++i) 00419 { 00420 // theta = (180.0 * i) / envp->num_orientations + 00421 // 90.0, where ENV_TRIG_TABSIZ is equivalent to 360.0 00422 // or 2*pi 00423 const env_size_t thetaidx = 00424 (ENV_TRIG_TABSIZ * i) 00425 / (2 * envp->num_orientations) 00426 + (ENV_TRIG_TABSIZ / 4); 00427 00428 ENV_ASSERT(thetaidx < ENV_TRIG_TABSIZ); 00429 00430 buf[10] = '0' + ((i+1) / 10); 00431 buf[11] = '0' + ((i+1) % 10); 00432 00433 env_chan_steerable 00434 (buf, envp, imath, img->dims, 00435 &hipass9, thetaidx, 00436 status_func, status_userdata, &chanOut); 00437 00438 ENV_ASSERT(env_img_initialized(&chanOut)); 00439 00440 if (!env_img_initialized(result)) 00441 { 00442 env_img_resize_dims(result, chanOut.dims); 00443 env_c_image_div_scalar 00444 (env_img_pixels(&chanOut), 00445 env_img_size(&chanOut), 00446 (intg32) envp->num_orientations, 00447 env_img_pixelsw(result)); 00448 } 00449 else 00450 { 00451 ENV_ASSERT(env_dims_equal(chanOut.dims, 00452 result->dims)); 00453 env_c_image_div_scalar_accum 00454 (env_img_pixels(&chanOut), 00455 env_img_size(&chanOut), 00456 (intg32) envp->num_orientations, 00457 env_img_pixelsw(result)); 00458 } 00459 } 00460 00461 env_img_make_empty(&chanOut); 00462 env_pyr_make_empty(&hipass9); 00463 00464 ENV_ASSERT(env_img_initialized(result)); 00465 00466 env_max_normalize_inplace(result, INTMAXNORMMIN, INTMAXNORMMAX, 00467 envp->maxnorm_type, 00468 envp->range_thresh); 00469 00470 if (status_func) 00471 (*status_func)(status_userdata, tagName, result); 00472 } 00473 00474 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00475 00476 // ###################################################################### 00477 void env_chan_flicker(const char* tagName, 00478 const struct env_params* envp, 00479 const struct env_math* imath, 00480 const struct env_image* prev, 00481 const struct env_image* cur, 00482 env_chan_status_func* status_func, 00483 void* status_userdata, 00484 struct env_image* result) 00485 { 00486 // If this is the first time the flicker channel has seen input, 00487 // then prev will be uninitialized; obviously we can't compute any 00488 // flicker with only one frame, so we just store the current input 00489 // as the next iteration's previous input 00490 if (!env_img_initialized(prev)) 00491 { 00492 env_img_make_empty(result); 00493 } 00494 else 00495 { 00496 const intg32 lowthresh = 00497 (envp->scale_bits > 8) 00498 ? (envp->flicker_thresh << (envp->scale_bits - 8)) 00499 : (envp->flicker_thresh >> (8 - envp->scale_bits)); 00500 00501 // take thresholded abs difference between current and 00502 // previous frame: 00503 struct env_image fli; 00504 env_img_init(&fli, prev->dims); 00505 abs_diff_thresh(cur, prev, lowthresh, &fli); 00506 00507 const env_size_t firstlevel = envp->cs_lev_min; 00508 const env_size_t depth = env_max_pyr_depth(envp); 00509 00510 // Compute our pyramid: 00511 struct env_pyr pyr; 00512 env_pyr_init(&pyr, depth); 00513 env_pyr_build_lowpass_5(&fli, firstlevel, imath, &pyr); 00514 00515 env_chan_process_pyr(tagName, fli.dims, &pyr, 00516 envp, 00517 imath, 00518 1, // takeAbs 00519 1, // normalizeOutput 00520 result); 00521 00522 if (status_func) 00523 (*status_func)(status_userdata, tagName, result); 00524 00525 env_img_make_empty(&fli); 00526 env_pyr_make_empty(&pyr); 00527 } 00528 } 00529 00530 // ###################################################################### 00531 void env_chan_msflicker(const char* tagName, 00532 const struct env_params* envp, 00533 const struct env_math* imath, 00534 const struct env_dims inputDims, 00535 const struct env_pyr* prev_lowpass5, 00536 const struct env_pyr* cur_lowpass5, 00537 env_chan_status_func* status_func, 00538 void* status_userdata, 00539 struct env_image* result) 00540 { 00541 // If this is the first time the flicker channel has seen 00542 // input, then prev will be uninitialized; obviously we can't 00543 // compute any flicker with only one frame, so we just store 00544 // the current input as the next iteration's previous input 00545 if (env_pyr_depth(prev_lowpass5) == 0) 00546 { 00547 env_img_make_empty(result); 00548 } 00549 else 00550 { 00551 const intg32 lowthresh = 00552 (envp->scale_bits > 8) 00553 ? (envp->flicker_thresh << (envp->scale_bits - 8)) 00554 : (envp->flicker_thresh >> (8 - envp->scale_bits)); 00555 00556 // take thresholded abs difference between current and 00557 // previous frame: 00558 struct env_pyr fli; 00559 env_pyr_init(&fli, env_pyr_depth(cur_lowpass5)); 00560 abs_diff_thresh_pyr(cur_lowpass5, prev_lowpass5, 00561 lowthresh, &fli); 00562 00563 env_chan_process_pyr(tagName, inputDims, &fli, 00564 envp, 00565 imath, 00566 1, // takeAbs 00567 1, // normalizeOutput 00568 result); 00569 00570 if (status_func) 00571 (*status_func)(status_userdata, tagName, result); 00572 00573 env_pyr_make_empty(&fli); 00574 } 00575 } 00576 00577 // ###################################################################### 00578 void env_chan_direction(const char* tagName, 00579 const struct env_params* envp, 00580 const struct env_math* imath, 00581 const struct env_dims inputdims, 00582 const struct env_pyr* unshiftedPrev, 00583 const struct env_pyr* unshiftedCur, 00584 const struct env_pyr* shiftedPrev, 00585 const struct env_pyr* shiftedCur, 00586 env_chan_status_func* status_func, 00587 void* status_userdata, 00588 struct env_image* result) 00589 { 00590 const env_size_t firstlevel = envp->cs_lev_min; 00591 const env_size_t depth = env_max_pyr_depth(envp); 00592 00593 const env_size_t nshift = (imath->nbits+1)/2; 00594 00595 if (env_pyr_depth(unshiftedPrev) == 0) 00596 { 00597 // it's our first time, so just return an empty image: 00598 env_img_make_empty(result); 00599 } 00600 else 00601 { 00602 struct env_pyr pyr; 00603 env_pyr_init(&pyr, depth); 00604 00605 const intg32 lowthresh = 00606 (envp->scale_bits > 8) 00607 ? (envp->motion_thresh << (envp->scale_bits - 8)) 00608 : (envp->motion_thresh >> (8 - envp->scale_bits)); 00609 00610 // compute the Reichardt maps 00611 for (env_size_t i = firstlevel; i < depth; i++) 00612 { 00613 env_img_resize_dims 00614 (env_pyr_imgw(&pyr, i), 00615 env_pyr_img(unshiftedCur, i)->dims); 00616 00617 const intg32* const ucurr = env_img_pixels(env_pyr_img(unshiftedCur, i)); 00618 const intg32* const uprev = env_img_pixels(env_pyr_img(unshiftedPrev, i)); 00619 const intg32* const scurr = env_img_pixels(env_pyr_img(shiftedCur, i)); 00620 const intg32* const sprev = env_img_pixels(env_pyr_img(shiftedPrev, i)); 00621 intg32* const dptr = env_img_pixelsw(env_pyr_imgw(&pyr, i)); 00622 00623 const env_size_t sz = env_img_size(env_pyr_img(&pyr, i)); 00624 00625 for (env_size_t c = 0; c < sz; ++c) 00626 { 00627 dptr[c] = 00628 ((ucurr[c] >> nshift) * (sprev[c] >> nshift)) - 00629 ((uprev[c] >> nshift) * (scurr[c] >> nshift)); 00630 00631 if (dptr[c] < lowthresh) dptr[c] = 0; 00632 } 00633 } 00634 00635 env_chan_process_pyr(tagName, inputdims, &pyr, 00636 envp, 00637 imath, 00638 1, // takeAbs 00639 1, // normalizeOutput 00640 result); 00641 00642 if (status_func) 00643 (*status_func)(status_userdata, tagName, result); 00644 00645 env_pyr_make_empty(&pyr); 00646 } 00647 } 00648 00649 #endif // ENV_WITH_DYNAMIC_CHANNELS 00650 00651 // ###################################################################### 00652 /* So things look consistent in everyone's emacs... */ 00653 /* Local Variables: */ 00654 /* indent-tabs-mode: nil */ 00655 /* c-file-style: "linux" */ 00656 /* End: */ 00657 00658 #endif // ENVISION_ENV_CHANNEL_C_DEFINED