00001 /*!@file Envision/env_visual_cortex.c */ 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_visual_cortex.c $ 00035 // $Id: env_visual_cortex.c 8054 2007-03-07 00:47:08Z rjpeters $ 00036 // 00037 00038 #ifndef ENVISION_ENV_VISUAL_CORTEX_C_DEFINED 00039 #define ENVISION_ENV_VISUAL_CORTEX_C_DEFINED 00040 00041 #include "Envision/env_visual_cortex.h" 00042 00043 #include "Envision/env_c_math_ops.h" 00044 #include "Envision/env_channel.h" 00045 #include "Envision/env_image_ops.h" 00046 #include "Envision/env_log.h" 00047 #include "Envision/env_params.h" 00048 00049 #define WEIGHT_SCALEBITS ((env_size_t) 8) 00050 00051 static void combine_output(struct env_image* chanOut, 00052 const intg32 iweight, 00053 struct env_image* result) 00054 { 00055 if (!env_img_initialized(chanOut)) 00056 return; 00057 00058 intg32* const sptr = env_img_pixelsw(chanOut); 00059 const env_size_t sz = env_img_size(chanOut); 00060 00061 if (!env_img_initialized(result)) 00062 { 00063 env_img_resize_dims(result, chanOut->dims); 00064 intg32* const dptr = env_img_pixelsw(result); 00065 for (env_size_t i = 0; i < sz; ++i) 00066 { 00067 sptr[i] = (sptr[i] >> WEIGHT_SCALEBITS) * iweight; 00068 dptr[i] = sptr[i]; 00069 } 00070 } 00071 else 00072 { 00073 ENV_ASSERT(env_dims_equal(chanOut->dims, result->dims)); 00074 intg32* const dptr = env_img_pixelsw(result); 00075 const env_size_t sz = env_img_size(result); 00076 for (env_size_t i = 0; i < sz; ++i) 00077 { 00078 sptr[i] = (sptr[i] >> WEIGHT_SCALEBITS) * iweight; 00079 dptr[i] += sptr[i]; 00080 } 00081 } 00082 } 00083 00084 // ###################################################################### 00085 void env_visual_cortex_init(struct env_visual_cortex* vcx, 00086 const struct env_params* envp) 00087 { 00088 env_params_validate(envp); 00089 00090 env_init_integer_math(&vcx->imath, envp); 00091 00092 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00093 env_img_init_empty(&vcx->prev_input); 00094 env_pyr_init(&vcx->prev_lowpass5, 0); 00095 #endif 00096 00097 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00098 env_motion_channel_init(&vcx->motion_chan, envp); 00099 #endif 00100 00101 } 00102 00103 // ###################################################################### 00104 void env_visual_cortex_destroy(struct env_visual_cortex* vcx) 00105 { 00106 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00107 env_img_make_empty(&vcx->prev_input); 00108 env_pyr_make_empty(&vcx->prev_lowpass5); 00109 env_motion_channel_destroy(&vcx->motion_chan); 00110 #endif 00111 } 00112 00113 // ###################################################################### 00114 void env_visual_cortex_input( 00115 struct env_visual_cortex* vcx, 00116 const struct env_params* envp, 00117 const char* tagName, 00118 const struct env_rgb_pixel* const colimg, 00119 const struct env_rgb_pixel* const prev_colimg, 00120 const struct env_dims dims, 00121 env_chan_status_func* status_func, 00122 void* status_userdata, 00123 struct env_image* result, 00124 struct env_image* intens_result, 00125 struct env_image* color_result, 00126 struct env_image* ori_result 00127 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00128 , 00129 struct env_image* flicker_result, 00130 struct env_image* motion_result 00131 #endif 00132 ) 00133 { 00134 env_img_make_empty(result); 00135 00136 const intg32 total_weight = env_total_weight(envp); 00137 00138 ENV_ASSERT(total_weight > 0); 00139 00140 /* We want to compute 00141 00142 * weight 00143 * img * ------------ 00144 * total_weight 00145 * 00146 * 00147 * To do that without overflowing, we compute it as 00148 * 00149 * 00150 * weight 256 00151 * img * ------------ * --- 00152 * total_weight 256 00153 * 00154 * img weight * 256 00155 * = ( --- ) * ( ------------ ) 00156 * 256 total_weight 00157 * 00158 * where 256 is an example of (1<<WEIGHT_SCALEBITS) for 00159 * WEIGHT_SCALEBITS=8. 00160 */ 00161 00162 if (envp->chan_c_weight > 0) 00163 { 00164 const intg32 color_weight = 00165 envp->chan_c_weight*(1<<WEIGHT_SCALEBITS) / total_weight; 00166 00167 struct env_image colorOut = env_img_initializer; 00168 env_chan_color 00169 ("color", envp, &vcx->imath, colimg, prev_colimg, 00170 dims, status_func, status_userdata, &colorOut); 00171 combine_output(&colorOut, color_weight, result); 00172 if (color_result != 0) 00173 env_img_swap(&colorOut, color_result); 00174 env_img_make_empty(&colorOut); 00175 } 00176 00177 // don't compute the luminance image and luminance lowpass5 00178 // pyramid until AFTER we've done the color channel, so that 00179 // we minimize the number of simultaneous temporary images at 00180 // the highest resolution 00181 00182 struct env_image bwimg; 00183 env_img_init(&bwimg, dims); 00184 env_c_luminance_from_byte(colimg, dims.w * dims.h, 00185 vcx->imath.nbits, env_img_pixelsw(&bwimg)); 00186 00187 struct env_pyr lowpass5; 00188 env_pyr_init(&lowpass5, env_max_pyr_depth(envp)); 00189 env_pyr_build_lowpass_5(&bwimg, 00190 envp->cs_lev_min, 00191 &vcx->imath, 00192 &lowpass5); 00193 00194 if (envp->chan_i_weight > 0) 00195 { 00196 const intg32 intensity_weight = 00197 envp->chan_i_weight*(1<<WEIGHT_SCALEBITS) / total_weight; 00198 00199 struct env_image intensityOut = env_img_initializer; 00200 env_chan_intensity 00201 ("intensity", envp, &vcx->imath, bwimg.dims, 00202 &lowpass5, 1, status_func, status_userdata, 00203 &intensityOut); 00204 combine_output(&intensityOut, intensity_weight, result); 00205 if (intens_result != 0) 00206 env_img_swap(&intensityOut, intens_result); 00207 env_img_make_empty(&intensityOut); 00208 } 00209 00210 if (envp->chan_o_weight > 0) 00211 { 00212 const intg32 orientation_weight = 00213 envp->chan_o_weight*(1<<WEIGHT_SCALEBITS) / total_weight; 00214 00215 struct env_image orientationOut = env_img_initializer; 00216 env_chan_orientation 00217 ("orientation", envp, &vcx->imath, 00218 &bwimg, status_func, status_userdata, 00219 &orientationOut); 00220 combine_output(&orientationOut, orientation_weight, result); 00221 if (ori_result != 0) 00222 env_img_swap(&orientationOut, ori_result); 00223 env_img_make_empty(&orientationOut); 00224 } 00225 00226 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00227 00228 if (envp->chan_f_weight > 0) 00229 { 00230 const intg32 flicker_weight = 00231 envp->chan_f_weight*(1<<WEIGHT_SCALEBITS) / total_weight; 00232 00233 struct env_image flickerOut = env_img_initializer; 00234 if (envp->multiscale_flicker) 00235 env_chan_msflicker 00236 ("flicker", envp, &vcx->imath, 00237 bwimg.dims, 00238 &vcx->prev_lowpass5, &lowpass5, 00239 status_func, status_userdata, 00240 &flickerOut); 00241 else 00242 env_chan_flicker 00243 ("flicker", envp, &vcx->imath, 00244 &vcx->prev_input, &bwimg, 00245 status_func, status_userdata, 00246 &flickerOut); 00247 combine_output(&flickerOut, flicker_weight, result); 00248 if (flicker_result != 0) 00249 env_img_swap(&flickerOut, flicker_result); 00250 env_img_make_empty(&flickerOut); 00251 00252 if (envp->multiscale_flicker) 00253 env_pyr_copy_src_dst 00254 (&lowpass5, &vcx->prev_lowpass5); 00255 else 00256 env_pyr_make_empty(&vcx->prev_lowpass5); 00257 } 00258 00259 if (envp->chan_m_weight > 0) 00260 { 00261 const intg32 motion_weight = 00262 envp->chan_m_weight*(1<<WEIGHT_SCALEBITS) / total_weight; 00263 00264 struct env_image motionOut = env_img_initializer; 00265 env_motion_channel_input_and_consume_pyr 00266 (&vcx->motion_chan, 00267 "motion", envp, &vcx->imath, bwimg.dims, 00268 &lowpass5, status_func, status_userdata, 00269 &motionOut); 00270 combine_output(&motionOut, motion_weight, result); 00271 if (motion_result != 0) 00272 env_img_swap(&motionOut, motion_result); 00273 env_img_make_empty(&motionOut); 00274 } 00275 00276 if (!envp->multiscale_flicker) 00277 env_img_swap(&vcx->prev_input, &bwimg); 00278 else 00279 env_img_make_empty(&vcx->prev_input); 00280 00281 #endif 00282 00283 if (status_func) 00284 (*status_func)(status_userdata, tagName, result); 00285 00286 env_pyr_make_empty(&lowpass5); 00287 env_img_make_empty(&bwimg); 00288 } 00289 00290 // ###################################################################### 00291 void env_visual_cortex_merge_ranges( 00292 const struct env_image* intens_result, 00293 const struct env_image* color_result, 00294 const struct env_image* ori_result, 00295 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00296 const struct env_image* flicker_result, 00297 const struct env_image* motion_result, 00298 #endif 00299 intg32* mi, 00300 intg32* ma 00301 ) 00302 { 00303 env_merge_range(intens_result, mi, ma); 00304 env_merge_range(color_result, mi, ma); 00305 env_merge_range(ori_result, mi, ma); 00306 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00307 env_merge_range(flicker_result, mi, ma); 00308 env_merge_range(motion_result, mi, ma); 00309 #endif 00310 } 00311 00312 // ###################################################################### 00313 void env_visual_cortex_rescale_ranges( 00314 struct env_image* result, 00315 struct env_image* intens_result, 00316 struct env_image* color_result, 00317 struct env_image* ori_result 00318 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00319 , 00320 struct env_image* flicker_result, 00321 struct env_image* motion_result 00322 #endif 00323 ) 00324 { 00325 intg32 mi = INTG32_MAX; 00326 intg32 ma = INTG32_MIN; 00327 00328 // first rescale the overall output on its own: 00329 env_merge_range(result, &mi, &ma); 00330 env_rescale_range_inplace(result, mi, ma); 00331 00332 // now compute a single min/max range covering ALL of the 00333 // individual channel outputs, and rescale the each of the 00334 // outputs using that same range: 00335 mi = INTG32_MAX; 00336 ma = INTG32_MIN; 00337 00338 env_visual_cortex_merge_ranges(intens_result, 00339 color_result, 00340 ori_result, 00341 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00342 flicker_result, 00343 motion_result, 00344 #endif 00345 &mi, &ma); 00346 00347 env_rescale_range_inplace(intens_result, mi, ma); 00348 env_rescale_range_inplace(color_result, mi, ma); 00349 env_rescale_range_inplace(ori_result, mi, ma); 00350 #ifdef ENV_WITH_DYNAMIC_CHANNELS 00351 env_rescale_range_inplace(flicker_result, mi, ma); 00352 env_rescale_range_inplace(motion_result, mi, ma); 00353 #endif 00354 00355 } 00356 00357 // ###################################################################### 00358 /* So things look consistent in everyone's emacs... */ 00359 /* Local Variables: */ 00360 /* indent-tabs-mode: nil */ 00361 /* c-file-style: "linux" */ 00362 /* End: */ 00363 00364 #endif // ENVISION_ENV_VISUAL_CORTEX_C_DEFINED