00001 /*!@file Envision/env_image_ops.c Fixed-point integer math versions of some of our floating-point image functions */ 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_image_ops.c $ 00035 // $Id: env_image_ops.c 11874 2009-10-21 01:03:31Z dparks $ 00036 // 00037 00038 #ifndef ENVISION_ENV_IMAGE_OPS_C_DEFINED 00039 #define ENVISION_ENV_IMAGE_OPS_C_DEFINED 00040 00041 #include "Envision/env_image_ops.h" 00042 00043 #include "Envision/env_c_math_ops.h" 00044 #include "Envision/env_log.h" 00045 00046 00047 // ###################################################################### 00048 void env_dec_xy(const struct env_image* src, struct env_image* result) 00049 { 00050 // do not go smaller than 1x1: 00051 if (src->dims.w <= 1 && src->dims.h <= 1) 00052 { 00053 env_img_copy_src_dst(src, result); 00054 return; 00055 } 00056 00057 if (src->dims.w == 1) // only thinout vertic 00058 { 00059 env_dec_y(src, result); 00060 return; 00061 } 00062 00063 if (src->dims.h == 1) 00064 { 00065 env_dec_x(src, result); 00066 return; 00067 } 00068 00069 const struct env_dims dims2 = { src->dims.w / 2, src->dims.h / 2 }; 00070 00071 env_img_resize_dims(result, dims2); 00072 00073 const intg32* sptr = env_img_pixels(src); 00074 intg32* dptr = env_img_pixelsw(result); 00075 const env_size_t skip = src->dims.w % 2 + src->dims.w; 00076 00077 for (env_size_t j = 0; j < dims2.h; ++j) 00078 { 00079 for (env_size_t i = 0; i < dims2.w; ++i) 00080 { 00081 *dptr++ = *sptr; // copy one pixel 00082 sptr += 2; // skip some pixels 00083 } 00084 sptr += skip; // skip to start of next line 00085 } 00086 } 00087 00088 // ###################################################################### 00089 void env_dec_x(const struct env_image* src, struct env_image* result) 00090 { 00091 if (src->dims.w <= 1) // do not go smaller than 1 pixel wide 00092 { 00093 env_img_copy_src_dst(src, result); 00094 return; 00095 } 00096 00097 const struct env_dims dims2 = { src->dims.w / 2, src->dims.h }; 00098 const env_size_t skip = src->dims.w % 2; 00099 ENV_ASSERT(dims2.w > 0); 00100 00101 env_img_resize_dims(result, dims2); 00102 00103 const intg32* sptr = env_img_pixels(src); 00104 intg32* dptr = env_img_pixelsw(result); 00105 00106 for (env_size_t j = 0; j < dims2.h; ++j) 00107 { 00108 for (env_size_t i = 0; i < dims2.w; ++i) 00109 { 00110 *dptr++ = *sptr; // copy one point 00111 sptr += 2; // skip a few points 00112 } 00113 sptr += skip; 00114 } 00115 } 00116 00117 // ###################################################################### 00118 void env_dec_y(const struct env_image* src, struct env_image* result) 00119 { 00120 if (src->dims.h <= 1) // do not go smaller than 1 pixel high 00121 { 00122 env_img_copy_src_dst(src, result); 00123 return; 00124 } 00125 00126 const struct env_dims dims2 = { src->dims.w, src->dims.h / 2 }; 00127 ENV_ASSERT(dims2.h > 0); 00128 00129 env_img_resize_dims(result, dims2); 00130 00131 const intg32* sptr = env_img_pixels(src); 00132 intg32* dptr = env_img_pixelsw(result); 00133 const env_size_t skip = dims2.w * 2; 00134 00135 for (env_size_t j = 0; j < dims2.h; ++j) 00136 { 00137 for (env_size_t i = 0; i < dims2.w; ++i) 00138 dptr[i] = sptr[i]; 00139 00140 dptr += dims2.w; 00141 sptr += skip; 00142 } 00143 } 00144 00145 // ###################################################################### 00146 // Anderson's separable kernel: 1/16 * [1 4 6 4 1] 00147 void env_lowpass_5_x_dec_x(const struct env_image* src, 00148 const struct env_math* imath, 00149 struct env_image* result) 00150 { 00151 const env_size_t w = src->dims.w; 00152 const env_size_t h = src->dims.h; 00153 00154 if (w < 2) // nothing to smooth 00155 { 00156 env_img_copy_src_dst(src, result); 00157 return; 00158 } 00159 00160 const struct env_dims dims2 = { w / 2, h }; 00161 ENV_ASSERT(dims2.w > 0); 00162 00163 env_img_resize_dims(result, dims2); 00164 00165 #ifndef ENV_NO_DEBUG 00166 const env_size_t filterbits = 4; // log2(16) 00167 const env_size_t accumbits = 3; // ceil(log2(5)) 00168 #endif 00169 00170 ENV_ASSERT((imath->nbits + filterbits + accumbits + 1) < (8*sizeof(intg32))); 00171 00172 env_c_lowpass_5_x_dec_x_fewbits_optim 00173 (env_img_pixels(src), w, h, 00174 env_img_pixelsw(result), dims2.w); 00175 } 00176 00177 // ###################################################################### 00178 // Anderson's separable kernel: 1/16 * [1 4 6 4 1] 00179 void env_lowpass_5_y_dec_y(const struct env_image* src, 00180 const struct env_math* imath, 00181 struct env_image* result) 00182 { 00183 const env_size_t w = src->dims.w; 00184 const env_size_t h = src->dims.h; 00185 00186 if (h < 2) // nothing to smooth 00187 { 00188 env_img_copy_src_dst(src, result); 00189 return; 00190 } 00191 00192 const struct env_dims dims2 = { w, h / 2 }; 00193 ENV_ASSERT(dims2.h > 0); 00194 00195 env_img_resize_dims(result, dims2); 00196 00197 #ifndef ENV_NO_DEBUG 00198 const env_size_t filterbits = 4; // log2(16) 00199 const env_size_t accumbits = 3; // ceil(log2(5)) 00200 #endif 00201 00202 ENV_ASSERT((imath->nbits + filterbits + accumbits + 1) < (8*sizeof(intg32))); 00203 00204 env_c_lowpass_5_y_dec_y_fewbits_optim 00205 (env_img_pixels(src), w, h, 00206 env_img_pixelsw(result), dims2.h); 00207 } 00208 00209 // ###################################################################### 00210 void env_lowpass_9_x(const struct env_image* source, 00211 const struct env_math* imath, 00212 struct env_image* result) 00213 { 00214 ENV_ASSERT(env_dims_equal(result->dims, source->dims)); 00215 00216 #ifndef ENV_NO_DEBUG 00217 const env_size_t filterbits = 8; // log2(256) 00218 const env_size_t accumbits = 4; // ceil(log2(9)) 00219 #endif 00220 00221 ENV_ASSERT((imath->nbits + filterbits + accumbits + 1) < (8*sizeof(intg32))); 00222 00223 const env_size_t w = source->dims.w; 00224 const env_size_t h = source->dims.h; 00225 00226 if (w < 2) // nothing to smooth 00227 { 00228 env_img_copy_src_dst(source, result); 00229 return; 00230 } 00231 00232 if (w < 9) // use inefficient implementation for small images 00233 { 00234 const intg32 hf_flipped[9] = { 1, 8, 28, 56, 70, 56, 28, 8, 1 }; 00235 const env_size_t hfs = 9; 00236 00237 const intg32* src = env_img_pixels(source); 00238 intg32* dst = env_img_pixelsw(result); 00239 00240 ENV_ASSERT(hfs & 1); // filter size must be odd 00241 const env_size_t hfs2 = (hfs - 1) / 2; 00242 00243 for (env_size_t j = 0; j < h; ++j) 00244 for (env_size_t i = 0; i < w; ++i) 00245 { 00246 intg32 sum = 0; 00247 intg32 val = 0; 00248 for (env_size_t k = 0; k < hfs; ++k) 00249 { 00250 if (i + k < hfs2 00251 || i + k >= w + hfs2) 00252 continue; 00253 00254 // convert to signed integers 00255 // to avoid wraparound when 00256 // k<hfs2 00257 val += src[(env_ssize_t) k 00258 - (env_ssize_t) hfs2] 00259 * hf_flipped[k]; 00260 sum += hf_flipped[k]; 00261 } 00262 00263 *dst++ = val / sum; 00264 ++src; 00265 } 00266 return; 00267 } 00268 00269 env_c_lowpass_9_x_fewbits_optim(env_img_pixels(source), w, h, 00270 env_img_pixelsw(result)); 00271 } 00272 00273 // ###################################################################### 00274 void env_lowpass_9_y(const struct env_image* source, 00275 const struct env_math* imath, 00276 struct env_image* result) 00277 { 00278 ENV_ASSERT(env_dims_equal(result->dims, source->dims)); 00279 00280 #ifndef ENV_NO_DEBUG 00281 const env_size_t filterbits = 8; // log2(256) 00282 const env_size_t accumbits = 4; // ceil(log2(9)) 00283 #endif 00284 00285 ENV_ASSERT((imath->nbits + filterbits + accumbits + 1) < (8*sizeof(intg32))); 00286 00287 const env_size_t w = source->dims.w; 00288 const env_size_t h = source->dims.h; 00289 00290 // if the height is less than 2, then the caller should handle 00291 // that condition differently since no smoothing need be done 00292 // (so the caller could either copy or swap the source into 00293 // the result location) 00294 ENV_ASSERT(h >= 2); 00295 00296 if (h < 9) // use inefficient implementation for small images 00297 { 00298 const intg32 vf_flipped[9] = { 1, 8, 28, 56, 70, 56, 28, 8, 1 }; 00299 const env_size_t vfs = 9; 00300 00301 const intg32* src = env_img_pixels(source); 00302 intg32* dst = env_img_pixelsw(result); 00303 00304 ENV_ASSERT(vfs & 1); // filter size must be odd 00305 const env_size_t vfs2 = (vfs - 1) / 2; 00306 00307 for (env_size_t j = 0; j < h; ++j) 00308 for (env_size_t i = 0; i < w; ++i) 00309 { 00310 intg32 sum = 0; 00311 intg32 val = 0; 00312 for (env_size_t k = 0; k < vfs; ++k) 00313 { 00314 if (j + k < vfs2 00315 || j + k >= h + vfs2) 00316 continue; 00317 00318 // convert to signed integers 00319 // to avoid wraparound when 00320 // k<vfs2 00321 val += src[w * 00322 ((env_ssize_t) k 00323 - (env_ssize_t) vfs2)] 00324 * vf_flipped[k]; 00325 sum += vf_flipped[k]; 00326 } 00327 00328 *dst++ = val / sum; 00329 ++src; 00330 } 00331 00332 return; 00333 } 00334 00335 env_c_lowpass_9_y_fewbits_optim(env_img_pixels(source), w, h, 00336 env_img_pixelsw(result)); 00337 } 00338 00339 // ###################################################################### 00340 void env_lowpass_9(const struct env_image* src, 00341 const struct env_math* imath, 00342 struct env_image* result) 00343 { 00344 ENV_ASSERT(env_dims_equal(result->dims, src->dims)); 00345 00346 struct env_image tmp1; 00347 env_img_init(&tmp1, src->dims); 00348 env_lowpass_9_x(src, imath, &tmp1); 00349 if (tmp1.dims.h >= 2) 00350 env_lowpass_9_y(&tmp1, imath, result); 00351 else 00352 env_img_swap(&tmp1, result); 00353 env_img_make_empty(&tmp1); 00354 } 00355 00356 // ###################################################################### 00357 void env_quad_energy(const struct env_image* img1, 00358 const struct env_image* img2, 00359 struct env_image* result) 00360 { 00361 ENV_ASSERT(env_dims_equal(img1->dims, img2->dims)); 00362 ENV_ASSERT(env_dims_equal(img1->dims, result->dims)); 00363 00364 const intg32* s1ptr = env_img_pixels(img1); 00365 const intg32* s2ptr = env_img_pixels(img2); 00366 intg32* dptr = env_img_pixelsw(result); 00367 00368 const env_size_t sz = env_img_size(img1); 00369 00370 for (env_size_t i = 0; i < sz; ++i) 00371 { 00372 const intg32 s1 = ENV_ABS(s1ptr[i]); 00373 const intg32 s2 = ENV_ABS(s2ptr[i]); 00374 00375 /* "A Fast Approximation to the Hypotenuse" by Alan Paeth, from 00376 "Graphics Gems", Academic Press, 1990 00377 00378 http://www.acm.org/pubs/tog/GraphicsGems/gems/HypotApprox.c 00379 00380 gives approximate value of sqrt(s1*s1+s2*s2) with only 00381 overestimations, and then never by more than (9/8) + one bit 00382 uncertainty 00383 */ 00384 dptr[i] = (s1 > s2) ? (s1 + (s2 >> 1)) : ((s1 >> 1) + s2); 00385 } 00386 } 00387 00388 // ###################################################################### 00389 void env_steerable_filter(const struct env_image* src, 00390 const intg32 kxnumer, const intg32 kynumer, 00391 const env_size_t kdenombits, 00392 const struct env_math* imath, 00393 struct env_image* result) 00394 { 00395 ENV_ASSERT(env_dims_equal(result->dims, src->dims)); 00396 00397 struct env_image re; env_img_init(&re, src->dims); 00398 struct env_image im; env_img_init(&im, src->dims); 00399 const intg32* sptr = env_img_pixels(src); 00400 intg32* reptr = env_img_pixelsw(&re); 00401 intg32* imptr = env_img_pixelsw(&im); 00402 00403 // (x,y) = (0,0) at center of image: 00404 const env_ssize_t w2l = ((env_ssize_t) src->dims.w) / 2; 00405 const env_ssize_t w2r = ((env_ssize_t) src->dims.w) - w2l; 00406 const env_ssize_t h2l = ((env_ssize_t) src->dims.h) / 2; 00407 const env_ssize_t h2r = ((env_ssize_t) src->dims.h) - h2l; 00408 00409 // let's do a conservative check to make sure that we won't overflow 00410 // when we compute "arg" later on -- as a very rough estimate, 00411 // kxnumer and kynumer are on the order of 2^16 (8 bits from 00412 // kdenombits=8, 8 bits from ENV_TRIG_TABSIZ=256), which gives 00413 // room for w+h to be up to about 2^15 00414 ENV_ASSERT((INTG32_MAX / (ENV_ABS(kxnumer) + ENV_ABS(kynumer))) > (w2r + h2r)); 00415 00416 ENV_ASSERT((2 * ENV_TRIG_NBITS + 1) < 8*sizeof(intg32)); 00417 00418 #ifndef ENV_NO_DEBUG 00419 const intg32 mdcutoff = INTG32_MAX >> (ENV_TRIG_NBITS+1); 00420 #endif 00421 00422 for (env_ssize_t j = -h2l; j < h2r; ++j) 00423 for (env_ssize_t i = -w2l; i < w2r; ++i) 00424 { 00425 const intg32 arg = (i * kxnumer + j * kynumer) >> kdenombits; 00426 00427 env_ssize_t idx = arg % ENV_TRIG_TABSIZ; 00428 if (idx < 0) idx += ENV_TRIG_TABSIZ; 00429 00430 const intg32 sval = *sptr++; 00431 00432 ENV_ASSERT(ENV_ABS(sval) < mdcutoff); 00433 00434 *reptr++ = (sval * imath->costab[idx]) >> (ENV_TRIG_NBITS+1); 00435 *imptr++ = (sval * imath->sintab[idx]) >> (ENV_TRIG_NBITS+1); 00436 } 00437 00438 env_lowpass_9(&re, imath, result); 00439 env_img_swap(&re, result); 00440 00441 env_lowpass_9(&im, imath, result); 00442 env_img_swap(&im, result); 00443 00444 env_quad_energy(&re, &im, result); 00445 00446 env_img_make_empty(&re); 00447 env_img_make_empty(&im); 00448 } 00449 00450 // ###################################################################### 00451 void env_attenuate_borders_inplace(struct env_image* a, env_size_t size) 00452 { 00453 ENV_ASSERT(env_img_initialized(a)); 00454 00455 struct env_dims dims = a->dims; 00456 00457 if (size * 2 > dims.w) size = dims.w / 2; 00458 if (size * 2 > dims.h) size = dims.h / 2; 00459 if (size < 1) return; // forget it 00460 00461 const intg32 size_plus_1 = (intg32) (size+1); 00462 00463 // top lines: 00464 intg32 coeff = 1; 00465 intg32* aptr = env_img_pixelsw(a); 00466 for (env_size_t y = 0; y < size; y ++) 00467 { 00468 for (env_size_t x = 0; x < dims.w; x ++) 00469 { 00470 *aptr = (*aptr / size_plus_1) * coeff; 00471 ++aptr; 00472 } 00473 ++coeff; 00474 } 00475 // normal lines: start again from beginning to attenuate corners twice: 00476 aptr = env_img_pixelsw(a); 00477 for (env_size_t y = 0; y < dims.h; y ++) 00478 { 00479 coeff = 1; 00480 for (env_size_t x = 0; x < size; x ++) 00481 { 00482 *(aptr + dims.w - 1 - x * 2) = 00483 (*(aptr + dims.w - 1 - x * 2) / size_plus_1) * coeff; 00484 00485 *aptr = (*aptr / size_plus_1) * coeff; 00486 ++aptr; 00487 ++coeff; 00488 } 00489 aptr += dims.w - size; 00490 } 00491 // bottom lines 00492 aptr = env_img_pixelsw(a) + (dims.h - size) * dims.w; 00493 coeff = size; 00494 for (env_size_t y = dims.h - size; y < dims.h; y ++) 00495 { 00496 for (env_size_t x = 0; x < dims.w; ++x) 00497 { 00498 *aptr = (*aptr / size_plus_1) * coeff; 00499 ++aptr; 00500 } 00501 --coeff; 00502 } 00503 } 00504 00505 // ###################################################################### 00506 void env_pyr_build_hipass_9(const struct env_image* image, 00507 env_size_t firstlevel, 00508 const struct env_math* imath, 00509 struct env_pyr* result) 00510 { 00511 ENV_ASSERT(env_img_initialized(image)); 00512 00513 // compute hipass as image - lowpass(image) 00514 00515 const env_size_t depth = env_pyr_depth(result); 00516 00517 if (depth == 0) 00518 return; 00519 00520 struct env_image lpfima = env_img_initializer; 00521 00522 // special case for the zero'th pyramid level so that we don't 00523 // have to make an extra copy of the input image at its 00524 // largest resolution 00525 env_img_resize_dims(&lpfima, image->dims); 00526 env_lowpass_9(image, imath, &lpfima); 00527 00528 if (0 == firstlevel) 00529 { 00530 env_img_resize_dims(env_pyr_imgw(result, 0), 00531 image->dims); 00532 env_c_image_minus_image 00533 (env_img_pixels(image), 00534 env_img_pixels(&lpfima), 00535 env_img_size(image), 00536 env_img_pixelsw(env_pyr_imgw(result, 0))); 00537 } 00538 00539 // now do the rest of the pyramid levels starting from level 1: 00540 for (env_size_t lev = 1; lev < depth; ++lev) 00541 { 00542 struct env_image dec = env_img_initializer; 00543 env_dec_xy(&lpfima, &dec); 00544 env_img_resize_dims(&lpfima, dec.dims); 00545 env_lowpass_9(&dec, imath, &lpfima); 00546 00547 if (lev >= firstlevel) 00548 { 00549 env_img_resize_dims(env_pyr_imgw(result, lev), 00550 dec.dims); 00551 env_c_image_minus_image 00552 (env_img_pixels(&dec), 00553 env_img_pixels(&lpfima), 00554 env_img_size(&dec), 00555 env_img_pixelsw(env_pyr_imgw(result, lev))); 00556 } 00557 00558 env_img_make_empty(&dec); 00559 } 00560 00561 env_img_make_empty(&lpfima); 00562 } 00563 00564 // ###################################################################### 00565 void env_pyr_build_steerable_from_hipass_9(const struct env_pyr* hipass, 00566 const intg32 kxnumer, 00567 const intg32 kynumer, 00568 const env_size_t kdenombits, 00569 const struct env_math* imath, 00570 struct env_pyr* out) 00571 { 00572 const env_size_t attenuation_width = 5; 00573 const env_size_t depth = env_pyr_depth(hipass); 00574 00575 struct env_pyr result; 00576 env_pyr_init(&result, depth); 00577 00578 for (env_size_t lev = 0; lev < depth; ++lev) 00579 { 00580 // if the hipass is empty at a given level, then just 00581 // leave the output empty at that level, too 00582 if (!env_img_initialized(env_pyr_img(hipass, lev))) 00583 continue; 00584 00585 env_img_resize_dims(env_pyr_imgw(&result, lev), 00586 env_pyr_img(hipass, lev)->dims); 00587 00588 env_steerable_filter(env_pyr_img(hipass, lev), 00589 kxnumer, kynumer, kdenombits, 00590 imath, env_pyr_imgw(&result, lev)); 00591 // attenuate borders that are overestimated due to filter trunctation: 00592 env_attenuate_borders_inplace(env_pyr_imgw(&result, lev), 00593 attenuation_width); 00594 } 00595 00596 env_pyr_swap(out, &result); 00597 00598 env_pyr_make_empty(&result); 00599 } 00600 00601 00602 // ###################################################################### 00603 void env_pyr_build_lowpass_5(const struct env_image* image, 00604 env_size_t firstlevel, 00605 const struct env_math* imath, 00606 struct env_pyr* result) 00607 { 00608 ENV_ASSERT(env_img_initialized(image)); 00609 ENV_ASSERT(env_pyr_depth(result) > 0); 00610 00611 if (firstlevel == 0) 00612 env_img_copy_src_dst(image, env_pyr_imgw(result, 0)); 00613 00614 const env_size_t depth = env_pyr_depth(result); 00615 00616 for (env_size_t lev = 1; lev < depth; ++lev) 00617 { 00618 struct env_image tmp1 = env_img_initializer; 00619 00620 const struct env_image* prev = 00621 lev == 1 00622 ? image 00623 : env_pyr_img(result, lev-1); 00624 00625 env_lowpass_5_x_dec_x(prev, 00626 imath, &tmp1); 00627 env_lowpass_5_y_dec_y(&tmp1, 00628 imath, env_pyr_imgw(result, lev)); 00629 00630 if ((lev - 1) < firstlevel) 00631 { 00632 env_img_make_empty(env_pyr_imgw(result, lev-1)); 00633 } 00634 00635 env_img_make_empty(&tmp1); 00636 } 00637 } 00638 00639 // ###################################################################### 00640 void env_downsize_9_inplace(struct env_image* src, const env_size_t depth, 00641 const struct env_math* imath) 00642 { 00643 for (env_size_t i = 0; i < depth; ++i) 00644 { 00645 { 00646 struct env_image tmp1; 00647 env_img_init(&tmp1, src->dims); 00648 env_lowpass_9_x(src, imath, &tmp1); 00649 env_dec_x(&tmp1, src); 00650 env_img_make_empty(&tmp1); 00651 } 00652 { 00653 struct env_image tmp2; 00654 env_img_init(&tmp2, src->dims); 00655 if (src->dims.h >= 2) 00656 env_lowpass_9_y(src, imath, &tmp2); 00657 else 00658 env_img_swap(src, &tmp2); 00659 env_dec_y(&tmp2, src); 00660 env_img_make_empty(&tmp2); 00661 } 00662 } 00663 } 00664 00665 // ###################################################################### 00666 void env_rescale(const struct env_image* src, struct env_image* result) 00667 { 00668 const env_ssize_t new_w = (env_ssize_t) result->dims.w; 00669 const env_ssize_t new_h = (env_ssize_t) result->dims.h; 00670 00671 ENV_ASSERT(env_img_initialized(src)); 00672 ENV_ASSERT(new_w > 0 && new_h > 0); 00673 00674 const env_ssize_t orig_w = (env_ssize_t) src->dims.w; 00675 const env_ssize_t orig_h = (env_ssize_t) src->dims.h; 00676 00677 // check if same size already 00678 if (new_w == orig_w && new_h == orig_h) 00679 { 00680 env_img_copy_src_dst(src, result); 00681 return; 00682 } 00683 00684 intg32* dptr = env_img_pixelsw(result); 00685 const intg32* const sptr = env_img_pixels(src); 00686 00687 // code inspired from one of the Graphics Gems book: 00688 /* 00689 (1) (x,y) are the original coords corresponding to scaled coords (i,j) 00690 (2) (x0,y0) are the greatest lower bound integral coords from (x,y) 00691 (3) (x1,y1) are the least upper bound integral coords from (x,y) 00692 (4) d00, d10, d01, d11 are the values of the original image at the corners 00693 of the rect (x0,y0),(x1,y1) 00694 (5) the value in the scaled image is computed from bilinear interpolation 00695 among d00,d10,d01,d11 00696 */ 00697 for (env_ssize_t j = 0; j < new_h; ++j) 00698 { 00699 const env_ssize_t y_numer = ENV_MAX(((env_ssize_t) 0), j*2*orig_h+orig_h-new_h); 00700 const env_ssize_t y_denom = 2*new_h; 00701 00702 const env_ssize_t y0 = y_numer / y_denom; 00703 const env_ssize_t y1 = ENV_MIN(y0 + 1, orig_h - 1); 00704 00705 const env_ssize_t fy_numer = y_numer - y0 * y_denom; 00706 const env_ssize_t fy_denom = y_denom; 00707 ENV_ASSERT(fy_numer == (y_numer % y_denom)); 00708 00709 const env_ssize_t wy0 = orig_w * y0; 00710 const env_ssize_t wy1 = orig_w * y1; 00711 00712 for (env_ssize_t i = 0; i < new_w; ++i) 00713 { 00714 const env_ssize_t x_numer = ENV_MAX(((env_ssize_t) 0), i*2*orig_w+orig_w-new_w); 00715 const env_ssize_t x_denom = 2*new_w; 00716 00717 const env_ssize_t x0 = x_numer / x_denom; 00718 const env_ssize_t x1 = ENV_MIN(x0 + 1, orig_w - 1); 00719 00720 const env_ssize_t fx_numer = x_numer - x0 * x_denom; 00721 const env_ssize_t fx_denom = x_denom; 00722 ENV_ASSERT(fx_numer == (x_numer % x_denom)); 00723 00724 const intg32 d00 = sptr[x0 + wy0]; 00725 const intg32 d10 = sptr[x1 + wy0]; 00726 00727 const intg32 d01 = sptr[x0 + wy1]; 00728 const intg32 d11 = sptr[x1 + wy1]; 00729 00730 const intg32 dx0 = 00731 d00 + ((d10 - d00) / fx_denom) * fx_numer; 00732 const intg32 dx1 = 00733 d01 + ((d11 - d01) / fx_denom) * fx_numer; 00734 00735 // no need to clamp 00736 *dptr++ = dx0 + ((dx1 - dx0) / fy_denom) * fy_numer; 00737 } 00738 } 00739 } 00740 00741 // ###################################################################### 00742 void env_max_normalize_inplace(struct env_image* src, 00743 const intg32 mi, const intg32 ma, 00744 const enum env_maxnorm_type normtyp, 00745 const intg32 rangeThresh) 00746 { 00747 // do normalization depending on desired type: 00748 switch(normtyp) 00749 { 00750 case ENV_VCXNORM_NONE: 00751 env_max_normalize_none_inplace(src, mi, ma, rangeThresh); 00752 break; 00753 case ENV_VCXNORM_MAXNORM: 00754 env_max_normalize_std_inplace(src, mi, ma, rangeThresh); 00755 break; 00756 default: 00757 ENV_ASSERT2(0, "Invalid normalization type"); 00758 } 00759 } 00760 00761 // ###################################################################### 00762 void env_max_normalize_none_inplace(struct env_image* src, 00763 const intg32 nmi, const intg32 nma, 00764 const intg32 rangeThresh) 00765 { 00766 // first clamp negative values to zero 00767 env_c_inplace_rectify(env_img_pixelsw(src), env_img_size(src)); 00768 00769 // then, normalize between mi and ma if not zero 00770 intg32 mi = nmi; 00771 intg32 ma = nma; 00772 if (mi != 0 || ma != 0) 00773 env_c_inplace_normalize(env_img_pixelsw(src), 00774 env_img_size(src), 00775 nmi, nma, &mi, &ma, rangeThresh); 00776 } 00777 00778 // ###################################################################### 00779 void env_max_normalize_std_inplace(struct env_image* src, 00780 const intg32 nmi, const intg32 nma, 00781 const intg32 rangeThresh) 00782 { 00783 if (!env_img_initialized(src)) 00784 return; 00785 00786 // first clamp negative values to zero 00787 env_c_inplace_rectify(env_img_pixelsw(src), env_img_size(src)); 00788 00789 // then, normalize between mi and ma if not zero 00790 intg32 mi = nmi; 00791 intg32 ma = nma; 00792 if (nmi != 0 || nma != 0) 00793 env_c_inplace_normalize(env_img_pixelsw(src), 00794 env_img_size(src), 00795 nmi, nma, &mi, &ma, rangeThresh); 00796 00797 const env_size_t w = src->dims.w; 00798 const env_size_t h = src->dims.h; 00799 00800 // normalize between mi and ma and multiply by (max - mean)^2 00801 00802 // we want to detect quickly local maxes, but avoid getting local mins 00803 const intg32 thresh = mi + (ma - mi) / 10; 00804 00805 // then get the mean value of the local maxima: 00806 const intg32* const dptr = env_img_pixels(src); 00807 intg32 lm_mean = 0; 00808 env_size_t numlm = 0; 00809 for (env_size_t j = 1; j+1 < h; ++j) 00810 for (env_size_t i = 1; i+1 < w; ++i) 00811 { 00812 const env_size_t index = i + w * j; 00813 const intg32 val = dptr[index]; 00814 if (val >= thresh && 00815 val >= dptr[index - w] && 00816 val >= dptr[index + w] && 00817 val >= dptr[index - 1] && 00818 val >= dptr[index + 1]) // local max 00819 { 00820 ++numlm; 00821 ENV_ASSERT2(INTG32_MAX - val >= lm_mean, 00822 "integer overflow"); 00823 lm_mean += val; 00824 } 00825 } 00826 00827 if (numlm > 0) 00828 lm_mean /= numlm; 00829 00830 ENV_ASSERT(ma >= lm_mean); 00831 00832 intg32 factor = 1; 00833 00834 // scale factor is (max - mean_local_max)^2: 00835 if (numlm > 1) 00836 { 00837 // make sure that (ma - lm_mean)^2 won't overflow: 00838 ENV_ASSERT((ma == lm_mean) || 00839 ((INTG32_MAX / (ma - lm_mean)) > (ma - lm_mean))); 00840 00841 factor = ((ma - lm_mean) * (ma - lm_mean)) / ma; 00842 } 00843 else if (numlm == 1) // a single narrow peak 00844 { 00845 factor = ma; 00846 } 00847 else 00848 { 00849 /* LERROR("No local maxes found !!"); */ 00850 } 00851 00852 if (factor != 1) 00853 { 00854 intg32* const itr = env_img_pixelsw(src); 00855 const env_size_t sz = env_img_size(src); 00856 for (env_size_t i = 0; i < sz; ++i) 00857 itr[i] *= factor; 00858 } 00859 } 00860 00861 // ###################################################################### 00862 void env_center_surround(const struct env_image* center, 00863 const struct env_image* surround, 00864 const int absol, 00865 struct env_image* result) 00866 { 00867 // result has the size of the larger image: 00868 ENV_ASSERT(env_dims_equal(result->dims, center->dims)); 00869 00870 const env_size_t lw = center->dims.w, lh = center->dims.h; 00871 const env_size_t sw = surround->dims.w, sh = surround->dims.h; 00872 00873 ENV_ASSERT2(lw >= sw && lh >= sh, 00874 "center must be larger than surround"); 00875 00876 const env_size_t scalex = lw / sw, remx = lw - 1 - (lw % sw); 00877 const env_size_t scaley = lh / sh, remy = lh - 1 - (lh % sh); 00878 00879 // scan large image and subtract corresponding pixel from small image: 00880 env_size_t ci = 0, cj = 0; 00881 const intg32* lptr = env_img_pixels(center); 00882 const intg32* sptr = env_img_pixels(surround); 00883 intg32* dptr = env_img_pixelsw(result); 00884 00885 if (absol) // compute abs(hires - lowres): 00886 { 00887 for (env_size_t j = 0; j < lh; ++j) 00888 { 00889 for (env_size_t i = 0; i < lw; ++i) 00890 { 00891 if (*lptr > *sptr) 00892 *dptr++ = (*lptr++ - *sptr); 00893 else 00894 *dptr++ = (*sptr - *lptr++); 00895 00896 if ((++ci) == scalex && i != remx) { ci = 0; ++sptr; } 00897 } 00898 if (ci) { ci = 0; ++sptr; } // in case the reduction is not round 00899 if ((++cj) == scaley && j != remy) cj = 0; else sptr -= sw; 00900 } 00901 } 00902 else // compute hires - lowres, clamped to 0: 00903 { 00904 for (env_size_t j = 0; j < lh; ++j) 00905 { 00906 for (env_size_t i = 0; i < lw; ++i) 00907 { 00908 if (*lptr > *sptr) 00909 *dptr++ = (*lptr++ - *sptr); 00910 else 00911 { *dptr++ = 0; lptr++; } 00912 00913 if ((++ci) == scalex && i != remx) { ci = 0; ++sptr; } 00914 } 00915 if (ci) { ci = 0; ++sptr; } // in case the reduction is not round 00916 if ((++cj) == scaley && j != remy) cj = 0; else sptr -= sw; 00917 } 00918 } 00919 00920 // attenuate borders: 00921 env_attenuate_borders_inplace 00922 (result, ENV_MAX(result->dims.w, result->dims.h) / 20); 00923 } 00924 00925 // ###################################################################### 00926 void env_get_rgby(const struct env_rgb_pixel* const src, 00927 const struct env_rgb_pixel* const src2, 00928 const env_size_t sz, 00929 struct env_image* rg, 00930 struct env_image* by, const intg32 thresh, 00931 const env_size_t inputbits) 00932 { 00933 // red = [r - (g+b)/2] [.] = clamp between 0 and 255 00934 // green = [g - (r+b)/2] 00935 // blue = [b - (r+g)/2] 00936 // yellow = [2*((r+g)/2 - |r-g| - b)] 00937 00938 ENV_ASSERT(env_img_size(rg) == sz); 00939 ENV_ASSERT(env_img_size(by) == sz); 00940 00941 intg32* rgptr = env_img_pixelsw(rg); 00942 intg32* byptr = env_img_pixelsw(by); 00943 00944 const env_ssize_t lshift = ((env_ssize_t)inputbits) - 3; 00945 00946 for (env_size_t i = 0; i < sz; ++i) 00947 { 00948 intg32 r, g, b; 00949 00950 if (src2 == 0) 00951 { 00952 r = (intg32) src[i].p[0]; 00953 g = (intg32) src[i].p[1]; 00954 b = (intg32) src[i].p[2]; 00955 } 00956 else 00957 { 00958 r = ((intg32) src[i].p[0] + (intg32) src2[i].p[0]) / 2; 00959 g = ((intg32) src[i].p[1] + (intg32) src2[i].p[1]) / 2; 00960 b = ((intg32) src[i].p[2] + (intg32) src2[i].p[2]) / 2; 00961 } 00962 00963 // first do the luminanceNormalization: 00964 const intg32 lum = r + g + b; 00965 00966 if (lum < thresh) // too dark... no response from anybody 00967 { 00968 rgptr[i] = byptr[i] = 0; 00969 } 00970 else 00971 { 00972 // now compute color opponencies: 00973 intg32 red = (2*r - g - b); 00974 intg32 green = (2*g - r - b); 00975 intg32 blue = (2*b - r - g); 00976 intg32 yellow = (-2*blue - 4*ENV_ABS(r-g)); 00977 00978 if (red < 0) red = 0; 00979 if (green < 0) green = 0; 00980 if (blue < 0) blue = 0; 00981 if (yellow < 0) yellow=0; 00982 00983 // compute differences and normalize chroma by luminance: 00984 if (lshift > 0) 00985 { 00986 rgptr[i] = (3*(red - green) << lshift) / lum; 00987 byptr[i] = (3*(blue - yellow) << lshift) / lum; 00988 } 00989 else if (lshift < 0) 00990 { 00991 rgptr[i] = ((3*(red - green)) / lum) >> (-lshift); 00992 byptr[i] = ((3*(blue - yellow)) / lum) >> (-lshift); 00993 } 00994 else // lshift == 0 00995 { 00996 rgptr[i] = (3*(red - green)) / lum; 00997 byptr[i] = (3*(blue - yellow)) / lum; 00998 } 00999 } 01000 } 01001 } 01002 01003 // ###################################################################### 01004 void env_merge_range(const struct env_image* src, 01005 intg32* mi, intg32* ma) 01006 { 01007 if (src == 0) 01008 return; 01009 01010 const intg32* sptr = env_img_pixels(src); 01011 const env_size_t sz = env_img_size(src); 01012 01013 if (sz == 0) 01014 return; 01015 01016 if (sptr[0] < *mi) *mi = sptr[0]; 01017 if (sptr[0] > *ma) *ma = sptr[0]; 01018 01019 for (env_size_t i = 1; i < sz; ++i) 01020 { 01021 if (sptr[i] < *mi) *mi = sptr[i]; 01022 else if (sptr[i] > *ma) *ma = sptr[i]; 01023 } 01024 } 01025 01026 // ###################################################################### 01027 void env_rescale_range_inplace(struct env_image* src, 01028 const intg32 mi, const intg32 ma) 01029 { 01030 if (src == 0) 01031 return; 01032 01033 intg32* sptr = env_img_pixelsw(src); 01034 const env_size_t sz = env_img_size(src); 01035 01036 ENV_ASSERT(ma >= mi); 01037 01038 const intg32 scale = ma - mi; 01039 01040 if (scale < 1) // image is uniform 01041 { 01042 for (env_size_t i = 0; i < sz; ++i) 01043 sptr[i] = 0; 01044 } 01045 else if ((INTG32_MAX / 255) > scale) 01046 { 01047 for (env_size_t i = 0; i < sz; ++i) 01048 { 01049 sptr[i] = ((sptr[i] - mi) * 255) / scale; 01050 } 01051 } 01052 else 01053 { 01054 const intg32 div = scale / 255; 01055 ENV_ASSERT(div > 0); 01056 for (env_size_t i = 0; i < sz; ++i) 01057 { 01058 sptr[i] = (sptr[i] - mi) / div; 01059 } 01060 } 01061 } 01062 01063 #ifdef ENV_WITH_DYNAMIC_CHANNELS 01064 01065 // ###################################################################### 01066 void env_shift_clean(const struct env_image* srcImg, 01067 const env_ssize_t dx, const env_ssize_t dy, 01068 struct env_image* result) 01069 { 01070 if (!env_img_initialized(srcImg)) 01071 { 01072 env_img_make_empty(result); 01073 return; 01074 } 01075 01076 ENV_ASSERT(env_dims_equal(result->dims, srcImg->dims)); 01077 01078 const env_ssize_t w = (env_ssize_t) srcImg->dims.w; 01079 const env_ssize_t h = (env_ssize_t) srcImg->dims.h; 01080 01081 if (ENV_ABS(dx) >= w || ENV_ABS(dy) >= h) 01082 // the shifts are so large that the resulting image 01083 // will just be empty: 01084 return; 01085 01086 // find range of pixels to copy: 01087 const env_ssize_t startx = ENV_MAX(((env_ssize_t) 0), -dx); 01088 const env_ssize_t endx = ENV_MIN(w, w - dx); 01089 ENV_ASSERT(startx < w); 01090 ENV_ASSERT(endx > 0); 01091 const env_ssize_t starty = ENV_MAX(((env_ssize_t) 0), -dy); 01092 const env_ssize_t endy = ENV_MIN(h, h - dy); 01093 ENV_ASSERT(starty < h); 01094 ENV_ASSERT(endy > 0); 01095 01096 // create the source and destination pointers 01097 const intg32* src = env_img_pixels(srcImg); 01098 intg32* dst = env_img_pixelsw(result); 01099 01100 src += startx + starty * w; 01101 dst += (startx + dx) + (starty + dy) * w; 01102 01103 const env_ssize_t skip = w - endx + startx; 01104 01105 // do the copy: 01106 for (env_ssize_t j = starty; j < endy; ++j) 01107 { 01108 for (env_ssize_t i = startx; i < endx; ++i) 01109 *dst++ = *src++; 01110 01111 // ready for next row of pixels: 01112 src += skip; dst += skip; 01113 } 01114 } 01115 01116 // ###################################################################### 01117 void env_shift_image(const struct env_image* srcImg, 01118 const env_ssize_t dxnumer, const env_ssize_t dynumer, 01119 const env_size_t denombits, 01120 struct env_image* result) 01121 { 01122 if (!env_img_initialized(srcImg)) 01123 { 01124 env_img_make_empty(result); 01125 return; 01126 } 01127 01128 ENV_ASSERT(env_dims_equal(result->dims, srcImg->dims)); 01129 01130 ENV_ASSERT(denombits < 8*sizeof(intg32)); 01131 01132 const env_ssize_t denom = (1 << denombits); 01133 01134 const env_ssize_t w = (env_ssize_t) srcImg->dims.w; 01135 const env_ssize_t h = (env_ssize_t) srcImg->dims.h; 01136 01137 // prepare a couple of variable for the x direction 01138 env_ssize_t xt = dxnumer >= 0 01139 ? (dxnumer >> denombits) 01140 : - ((-dxnumer + denom-1) >> denombits); 01141 env_ssize_t xfrac_numer = dxnumer - (xt << denombits); 01142 const env_ssize_t startx = ENV_MAX(((env_ssize_t) 0),xt); 01143 const env_ssize_t endx = ENV_MIN(((env_ssize_t) 0),xt) + w - 1; 01144 01145 // prepare a couple of variable for the y direction 01146 env_ssize_t yt = dynumer >= 0 01147 ? (dynumer >> denombits) 01148 : - ((-dynumer + denom-1) >> denombits); 01149 env_ssize_t yfrac_numer = dynumer - (yt << denombits); 01150 const env_ssize_t starty = ENV_MAX(((env_ssize_t) 0),yt); 01151 const env_ssize_t endy = ENV_MIN(((env_ssize_t) 0),yt) + h - 1; 01152 01153 // clear the return image 01154 { 01155 const env_size_t sz = w * h; 01156 intg32* const rptr = env_img_pixelsw(result); 01157 for (env_size_t i = 0; i < sz; ++i) 01158 rptr[i] = 0; 01159 } 01160 01161 // dispatch to faster env_shift_clean() if displacements are 01162 // roughly integer: 01163 if (xfrac_numer == 0 && yfrac_numer == 0) 01164 { 01165 env_shift_clean(srcImg, xt, yt, result); 01166 return; 01167 } 01168 01169 if (xfrac_numer > 0) 01170 { 01171 xfrac_numer = denom - xfrac_numer; 01172 ++xt; 01173 } 01174 01175 if (yfrac_numer > 0) 01176 { 01177 yfrac_numer = denom - yfrac_numer; 01178 ++yt; 01179 } 01180 01181 // prepare the pointers 01182 const intg32* src2 = env_img_pixels(srcImg); 01183 intg32* ret2 = env_img_pixelsw(result); 01184 if (xt > 0) ret2 += xt; 01185 else if (xt < 0) src2 -= xt; 01186 if (yt > 0) ret2 += yt * w; 01187 else if (yt < 0) src2 -= yt * w; 01188 01189 // now loop over the images 01190 for (env_ssize_t y = starty; y < endy; ++y) 01191 { 01192 const intg32* src = src2; 01193 intg32* ret = ret2; 01194 for (env_ssize_t x = startx; x < endx; ++x) 01195 { 01196 *ret = (((src[0] >> denombits) * (denom - xfrac_numer)) >> denombits) * (denom - yfrac_numer); 01197 *ret += (((src[1] >> denombits) * xfrac_numer) >> denombits) * (denom - yfrac_numer); 01198 *ret += (((src[w] >> denombits) * (denom - xfrac_numer)) >> denombits) * yfrac_numer; 01199 *ret += (((src[w+1] >> denombits) * xfrac_numer) >> denombits) * yfrac_numer; 01200 ++src; ++ret; 01201 } 01202 src2 += w; ret2 += w; 01203 } 01204 } 01205 01206 #endif // ENV_WITH_DYNAMIC_CHANNELS 01207 01208 // ###################################################################### 01209 /* So things look consistent in everyone's emacs... */ 01210 /* Local Variables: */ 01211 /* indent-tabs-mode: nil */ 01212 /* c-file-style: "linux" */ 01213 /* End: */ 01214 01215 #endif // ENVISION_ENV_IMAGE_OPS_C_DEFINED