00001 /*!@file Image/Transforms.C Transformations on Image 00002 */ 00003 00004 // //////////////////////////////////////////////////////////////////// // 00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00006 // University of Southern California (USC) and the iLab at USC. // 00007 // See http://iLab.usc.edu for information about this project. // 00008 // //////////////////////////////////////////////////////////////////// // 00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00011 // in Visual Environments, and Applications'' by Christof Koch and // 00012 // Laurent Itti, California Institute of Technology, 2001 (patent // 00013 // pending; application number 09/912,225 filed July 23, 2001; see // 00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00015 // //////////////////////////////////////////////////////////////////// // 00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00017 // // 00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00019 // redistribute it and/or modify it under the terms of the GNU General // 00020 // Public License as published by the Free Software Foundation; either // 00021 // version 2 of the License, or (at your option) any later version. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00026 // PURPOSE. See the GNU General Public License for more details. // 00027 // // 00028 // You should have received a copy of the GNU General Public License // 00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00031 // Boston, MA 02111-1307 USA. // 00032 // //////////////////////////////////////////////////////////////////// // 00033 // 00034 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/Transforms.C $ 00036 // $Id: Transforms.C 14592 2011-03-11 23:19:12Z jshen $ 00037 // 00038 00039 #ifndef IMAGE_TRANSFORMS_C_DEFINED 00040 #define IMAGE_TRANSFORMS_C_DEFINED 00041 00042 #include "Image/Transforms.H" 00043 00044 #include "Image/CutPaste.H" 00045 #include "Image/Image.H" 00046 #include "Image/MathOps.H" 00047 #include "Image/Pixels.H" 00048 #include "Image/Rectangle.H" 00049 #include "Util/Assert.H" 00050 #include "Util/log.H" 00051 00052 #include <algorithm> 00053 #include <cmath> 00054 #include <vector> 00055 #include <cstdio> // for printf() 00056 00057 #define SEGOBJ_START 0.9 00058 #define SEGOBJ_STEP 0.05 00059 #define SEGOBJ_MINA 0.005 00060 #define SEGOBJ_EXPL 1.05 00061 00062 // ###################################################################### 00063 template <class T> 00064 Image<byte> segmentObject(const Image<T>& src, 00065 const Point2D<int>& seed) 00066 { 00067 ASSERT(src.initialized()); 00068 Image<T> tmp; 00069 bool explode = false; int prev = 0; double thresh = 1.0; 00070 double step = double(src.getVal(seed.i, seed.j)) * SEGOBJ_STEP; 00071 int minadiff = int(float(src.getSize()) * SEGOBJ_MINA); 00072 double efac = SEGOBJ_EXPL; 00073 int iter; 00074 00075 while(explode == false && minadiff > 0 && efac > 1) 00076 { 00077 thresh = double(src.getVal(seed.i, seed.j)) * SEGOBJ_START; 00078 iter = 0; 00079 while(explode == false && thresh > 0) 00080 { 00081 int area = flood(src, tmp, seed, (T)thresh, (T)255.0); 00082 LDEBUG("prev=%d area=%d minadiff=%d thresh=%f efac=%f\n", 00083 prev, area, minadiff, thresh, efac); 00084 if (iter > 2 && area > prev + minadiff && 00085 area > int(double(prev)*efac)) 00086 explode = true; 00087 else 00088 { prev = area; thresh -= step; } 00089 iter ++; 00090 } 00091 minadiff -= minadiff / 4; efac *= 0.95; 00092 } 00093 // write out segmented object: 255 in object, zero otherwise 00094 thresh += step; // back 1 step before explosion 00095 flood(src, tmp, seed, (T)thresh, (T)255.0); 00096 return Image<byte>(tmp); 00097 } 00098 00099 // ###################################################################### 00100 template <class T> 00101 int segmentLandmark(const Image<T>& src, 00102 const Point2D<int>& seed, Image<byte>& target, 00103 double& activity, double& standout, 00104 float& area_percentage) 00105 { 00106 ASSERT(src.initialized()); 00107 Image<T> tmp; 00108 00109 bool explode = false; int prev = 0; 00110 double thresh = 1.0; 00111 double step = double(src.getVal(seed.i, seed.j)) * SEGOBJ_STEP; 00112 int minadiff = int(float(src.getSize()) * SEGOBJ_MINA); 00113 double efac = SEGOBJ_EXPL; 00114 double valdiff = 0.0, maxvaldiff = 0.0, sumvaldiff = 0.0, 00115 prev_thresh = 0.0, maxthresh = 0.0; 00116 int iter = 0; int flood_count = 0; 00117 bool gradual_descent = false; 00118 00119 thresh = double(src.getVal(seed.i, seed.j)) * SEGOBJ_START; 00120 prev_thresh = thresh; activity = 0.0; 00121 while(minadiff > 0 && thresh > 0) 00122 { 00123 explode = false; 00124 while(explode == false && thresh > 0) 00125 { 00126 int area = flood(src, tmp, seed, (T)thresh, (T)255.0); 00127 00128 // NOTE: g++4.3 warns about precedence of && over ||; should 00129 // this expression be (A&&B)||C or A&&(B||C)? 00130 if ((area > prev + minadiff && 00131 area > int(double(prev)*efac)) 00132 || area > 2 * prev) 00133 { 00134 explode = true; 00135 gradual_descent = false; 00136 flood_count++; 00137 valdiff = prev_thresh - thresh; 00138 sumvaldiff += valdiff; 00139 00140 if(maxvaldiff < valdiff) 00141 { 00142 maxthresh = thresh + step; 00143 maxvaldiff = valdiff; 00144 } 00145 prev_thresh = thresh; 00146 } 00147 else if(area > prev + minadiff/4) gradual_descent = true; 00148 prev = area; thresh -= step; 00149 iter ++; 00150 } 00151 } 00152 // write out segmented object: 255.0 inside object, zero otherwise 00153 if(maxvaldiff == 0.0) 00154 { 00155 // flooded the first time and then no more floods 00156 00157 // is this because of gradual descent? 00158 if(gradual_descent == true) 00159 standout = 0.0; 00160 // this is the only component and the rest is empty 00161 else standout = double(src.getVal(seed.i, seed.j)) * SEGOBJ_START; 00162 maxthresh = step; // back one step 00163 } 00164 else standout = maxvaldiff; 00165 00166 if(maxthresh <= 0) return -1; 00167 // maxthresh is the threshold where valdiff is max 00168 00169 if(flood_count != 0) 00170 if(maxvaldiff <= sumvaldiff/(double)flood_count) 00171 thresh = step; 00172 else thresh = maxthresh; 00173 00174 else thresh = step; 00175 00176 int area = flood(src, tmp, seed, (T)thresh, (T)255.0); 00177 if (area == -1) return -1; 00178 target = (Image<byte>)tmp; 00179 00180 // find activity (measure of mean activity in this component) 00181 double threshold = thresh; double sum = 0.0; 00182 thresh = double(src.getVal(seed.i, seed.j)) * SEGOBJ_START; 00183 prev = 0; int sum_area = 0; 00184 activity = thresh; 00185 00186 while(thresh >= threshold) 00187 { 00188 area = flood(src, tmp, seed, (T)thresh, (T)255.0); 00189 if(area > prev) 00190 activity = thresh; 00191 00192 sum += activity * (area - prev); 00193 sum_area += area - prev; 00194 thresh -= step; prev = area; 00195 } 00196 00197 if( sum_area != 0)activity = sum/(double)sum_area; 00198 else activity = threshold; 00199 00200 if(standout > activity) 00201 standout = activity; 00202 00203 area_percentage = (area * 100.0f)/ (float)src.getSize(); 00204 00205 return area; 00206 } 00207 00208 // ###################################################################### 00209 template <class T> 00210 Image<byte> contour2D(const Image<T>& src, const byte onval, const byte offval) 00211 { 00212 ASSERT(src.initialized()); 00213 00214 Image<byte> result(src.getDims(), NO_INIT); 00215 typename Image<T>::const_iterator sptr = src.begin(); 00216 Image<byte>::iterator dptr = result.beginw(); 00217 T z = T(); const int h = src.getHeight(), w = src.getWidth(); 00218 00219 // This is a 4-connected contour algorithm. We are on the contour if we are not zero but at least: either 1) one of 00220 // our 4-neighbors is zero, or 2) we are on an edge of the image. Here we unfold the algo for max speed, i.e., we 00221 // treat the borders of the image explicitly and then the bulk: 00222 00223 // first row: 00224 for (int i = 0; i < w; ++i) if (*sptr++) *dptr++ = onval; else *dptr++ = offval; 00225 00226 // done if the image only has 1 row: 00227 if (h == 1) return result; 00228 00229 // bulk, will only run if image has >2 rows: 00230 for (int j = 1; j < h-1; ++j) 00231 { 00232 // leftmost pixel of current row: 00233 if (*sptr++) *dptr++ = onval; else *dptr++ = offval; 00234 00235 // done if image has only 1 column: 00236 if (w == 1) continue; 00237 00238 // bulk of current row, will run only if image has >2 columns: 00239 for (int i = 1; i < w-1; ++i) { 00240 if (*sptr && (sptr[-1] == z || sptr[1] == z || sptr[-w] == z || sptr[w] == z)) 00241 *dptr++ = onval; else *dptr++ = offval; 00242 ++sptr; 00243 } 00244 00245 // rightmost pixel of current row, we know it exists since image has at least 2 columns: 00246 if (*sptr++) *dptr++ = onval; else *dptr++ = offval; 00247 } 00248 00249 // last row (we know it exists since image has at least 2 rows): 00250 for (int i = 0; i < w; ++i) if (*sptr++) *dptr++ = onval; else *dptr++ = offval; 00251 00252 return result; 00253 } 00254 00255 // ###################################################################### 00256 template <class T> 00257 Image<T> chamfer34(const Image<T>& src, const T dmax) 00258 { 00259 00260 ASSERT(src.initialized()); 00261 ASSERT(src.getWidth() >= 3 && src.getHeight() >= 3); 00262 00263 Image<T> result = src; // init dest array and copy input to it 00264 00265 const Dims dims = src.getDims(); 00266 const int size = src.getSize(); 00267 const int w = dims.w(); 00268 const T zero = T(); 00269 00270 typename Image<T>::const_iterator sptr = src.begin(); 00271 typename Image<T>::iterator dptr = result.beginw(); 00272 00273 // first, set distance to 0 inside object and max distance outside: 00274 for (int i = 0; i < size; i ++) 00275 if (*sptr++) *dptr++ = zero; else *dptr++ = dmax; 00276 dptr = result.beginw(); 00277 00278 const float tmaxf = float(dmax); 00279 00280 // now let's propagate the distance first forward, and then backwards: 00281 float d1, d2, d3, d4; 00282 00283 /* first point is untouched ... */ 00284 ++ dptr; 00285 00286 /* first line : y=0 x=1..MAXX-1 */ 00287 for (int i = 1; i < w; ++i) 00288 { 00289 d1 = std::min(dptr[-1] + 3.0F, dptr[-1 + w] + 4.0F); 00290 d2 = std::min(float(*dptr), d1); 00291 *dptr++ = clamped_convert<T>(std::min(d2, tmaxf)); 00292 } 00293 00294 /* y=1..MAXY-2, x=0..MAXX-1 */ 00295 for (int j = 1; j < dims.h() - 1; ++j) 00296 { 00297 /* x=0 point on each line : x=0 y=1..MAXY-2 */ 00298 d1 = std::min(dptr[-w] + 3.0F, float(*dptr)); 00299 *dptr++ = clamped_convert<T>(std::min(d1, tmaxf)); 00300 00301 /* here : x=1..MAXX-1 y=1..MAXY-2 */ 00302 for (int k = 1; k < w; k++) 00303 { 00304 d1 = std::min(dptr[-w - 1] + 4.0F, dptr[-w] + 3.0F); 00305 d2 = std::min(dptr[-1] + 3.0F, float(*dptr)); 00306 d3 = std::min(dptr[-1 + w] + 4.0F, d1); 00307 d4 = std::min(d2, d3); 00308 *dptr++ = clamped_convert<T>(std::min(d4, tmaxf)); 00309 } 00310 } 00311 00312 /* last line : x=0..MAXX-1 y=MAXY-1 */ 00313 /* first point */ 00314 d1 = std::min(dptr[-w] + 3.0F, float(*dptr)); 00315 *dptr++ = clamped_convert<T>(std::min(d1, tmaxf)); 00316 00317 /* x=1..MAXX-1 y=MAXY-1 */ 00318 for (int j = 1; j < w; ++j) 00319 { 00320 d1 = std::min(dptr[-w - 1] + 4.0F, dptr[-w] + 3.0F); 00321 d2 = std::min(dptr[-1] + 3.0F, float(*dptr)); 00322 d3 = std::min(d1, d2); 00323 *dptr++ = clamped_convert<T>(std::min(d3, tmaxf)); 00324 } 00325 --dptr; // back to last point 00326 00327 /******************* backward */ 00328 /* last point is untouched ... */ 00329 --dptr; 00330 00331 /* last line : y=MAXY-1 x=MAXX-2..0 */ 00332 for (int j = w-2; j >= 0; --j) 00333 { 00334 d1 = std::min(dptr[1] + 3.0F, dptr[-w + 1] + 4.0F); 00335 d2 = std::min(float(*dptr), d1); 00336 *dptr-- = clamped_convert<T>(std::min(d2, tmaxf)); 00337 } 00338 00339 /* y=MAXY-2..1, x=MAXX-1..0 */ 00340 for (int j = dims.h() - 2; j >= 1; --j) 00341 { 00342 /* x=MAXX-1 point on each line : y=MAXY-2..1 */ 00343 d1 = std::min(dptr[w] + 3.0F, float(*dptr)); 00344 *dptr-- = clamped_convert<T>(std::min(d1, tmaxf)); 00345 00346 /* here : x=MAXX-2..0 y=MAXY-2..1 z=MAXZ-1 */ 00347 for (int k = w - 2; k >= 0; k--) 00348 { 00349 d1 = std::min(float(*dptr), dptr[-w + 1] + 4.0F); 00350 d2 = std::min(dptr[1] + 3.0F, dptr[w] + 3.0F); 00351 d3 = std::min(dptr[w + 1] + 4.0F, d1); 00352 d4 = std::min(d2, d3); 00353 *dptr-- = clamped_convert<T>(std::min(d4, tmaxf)); 00354 } 00355 } 00356 00357 /* first line : x=MAXX-1..0 y=0 */ 00358 /* last point */ 00359 d1 = std::min(dptr[w] + 3.0F, float(*dptr)); 00360 *dptr-- = clamped_convert<T>(std::min(d1, tmaxf)); 00361 00362 /* x=MAXX-2..0 y=0 z=MAXZ-1 */ 00363 for (int j = w-2; j >= 0; --j) 00364 { 00365 d1 = std::min(dptr[1] + 3.0F, float(*dptr)); 00366 d2 = std::min(dptr[w] + 3.0F, dptr[w + 1] + 4.0F); 00367 d3 = std::min(d1, d2); 00368 *dptr-- = clamped_convert<T>(std::min(d3, tmaxf)); 00369 } 00370 00371 return result; 00372 } 00373 00374 // ###################################################################### 00375 template <class T> 00376 Image<T> saliencyChamfer34(const Image<T>& src, const T dmax) 00377 { 00378 00379 ASSERT(src.initialized()); 00380 ASSERT(src.getWidth() >= 3 && src.getHeight() >= 3); 00381 00382 Image<T> result = src; // init dest array and copy input to it 00383 00384 const Dims dims = src.getDims(); 00385 const int size = src.getSize(); 00386 const int w = dims.w(); 00387 const int h = dims.h(); 00388 const T zero = T(); 00389 00390 Image<T> magImg = src; 00391 00392 #define ALGORITHM CHAMFER34 00393 #if ALGORITHM == CHAMFER34 00394 int MASK_SIZE = 5; 00395 const int x_offset[] = { -1, 0, 1, -1, 0 }; 00396 const int y_offset[] = { -1, -1, -1, 0, 0 }; 00397 const int i_offset[] = { 4, 3, 4, 3, 0 }; 00398 #elif ALGORITHM == CHAMFER5711 00399 int MASK_SIZE = 9; 00400 int x_offset[] = { -1, 1, -2, -1, 0, 1, 2, -1, 0 }; 00401 int y_offset[] = { -2, -2, -1, -1, -1, -1, -1, 0, 0 }; 00402 int i_offset[] = { 11, 11, 11, 7, 5, 7, 11, 5, 0 }; 00403 #endif 00404 00405 typename Image<T>::iterator sptr = magImg.beginw(); 00406 typename Image<T>::iterator dptr = result.beginw(); 00407 00408 float K = 1.0F; 00409 00410 // first, set distance to 0 inside object and max distance outside: 00411 for (int i = 0; i < size; i ++) 00412 if (*sptr++ > 0) *dptr++ = zero; else *dptr++ = dmax; 00413 00414 double prev_measure = 0, min_measure = 0, measure = 0; 00415 double min_dist,min_mag; 00416 int d[MASK_SIZE],m[MASK_SIZE]; 00417 00418 int max_iter = 10; 00419 00420 //Reset the pointers 00421 sptr = magImg.beginw(); 00422 dptr = result.beginw(); 00423 00424 /* propagate distance etc */ 00425 int j = 0; 00426 bool change = false; 00427 do { 00428 j++; 00429 change = false; 00430 00431 /* forward pass */ 00432 for (int y = 1; y < h-1; y++) { 00433 for (int x = 1; x < w-1; x++) { 00434 00435 prev_measure = (double) (dptr[x + w*y]+K) / 00436 (double) ((unsigned int)sptr[x + w*y]+K); 00437 for (int i = 0; i < MASK_SIZE; i++) { 00438 int xn = x + x_offset[i]; int yn = y + y_offset[i]; 00439 d[i] = (int)(dptr[xn + w*yn] + i_offset[i]); 00440 m[i] = (unsigned int)sptr[xn + yn*w]; 00441 } 00442 00443 min_dist = d[0]; 00444 min_mag = m[0]; 00445 min_measure = (double) (d[0]+K) / (double) (m[0]+K); 00446 for (int i = 1; i < MASK_SIZE; i++) { 00447 measure = (double) (d[i]+K) / (double) (m[i]+K); 00448 if (measure < min_measure) { 00449 min_measure = measure; 00450 min_dist = d[i]; 00451 min_mag = m[i]; 00452 } 00453 } 00454 dptr[x + y*w] = (T)min_dist; 00455 sptr[x + y*w] = (T)min_mag; //TODO ? 00456 00457 if (prev_measure != min_measure) 00458 change = true; 00459 } 00460 } 00461 00462 /* backward pass */ 00463 for (int y = h-2; y >= 1; y--) { 00464 for (int x = w-2; x >= 1; x--) { 00465 prev_measure = (double) (dptr[x + y*w]+K) / 00466 (double) ((unsigned int)sptr[x + y*w]+K); 00467 for (int i = 0; i < MASK_SIZE; i++) { 00468 int xn = x - x_offset[i]; int yn = y - y_offset[i]; 00469 d[i] = (int)( dptr[xn + yn*w] + i_offset[i]); 00470 m[i] = (unsigned int)sptr[xn + yn*w]; 00471 } 00472 00473 min_dist = d[0]; 00474 min_mag = m[0]; 00475 min_measure = (double) (d[0]+K) / (double) (m[0]+K); 00476 for (int i = 1; i < MASK_SIZE; i++) { 00477 measure = (double) (d[i]+K) / (double) (m[i]+K); 00478 if (measure < min_measure) { 00479 min_measure = measure; 00480 min_dist = d[i]; 00481 min_mag = m[i]; 00482 } 00483 } 00484 dptr[x + y*w] = (T)min_dist; 00485 sptr[x + y*w] = (T)min_mag; //TODO ? 00486 00487 if (prev_measure != min_measure) 00488 change = true; 00489 } 00490 } 00491 } while (change && j < max_iter); 00492 00493 /* calculate measure values */ 00494 for (int y = 1; y < h-1; y++) { 00495 for (int x = 1; x < w-1; x++) { 00496 dptr[x + y*w] = (T) ((double) (dptr[x +y*w]+K) / 00497 (double) ((unsigned int)sptr[x + y*w]+K)); 00498 } 00499 } 00500 00501 /* rescale */ 00502 T max_val = dptr[0]; 00503 for (int y = 1; y < h-1; y++) 00504 for (int x = 1; x < w-1; x++) 00505 if (dptr[x + y*w] > max_val) 00506 max_val = dptr[x + y*w]; 00507 00508 for (int y = 1; y < h-1; y++) 00509 for (int x = 1; x < w-1; x++) 00510 dptr[x + y*w] = (unsigned char)(dptr[x + y*w] * 255 / max_val); 00511 00512 ///* reset borders */ 00513 //for (y = 0; y < height; y++) 00514 // tmp[0][y] = tmp[width-1][y] = 255; 00515 //for (x = 0; x < width; x++) 00516 // tmp[x][0] = tmp[x][height-1] = 255; 00517 00518 00519 return result; 00520 } 00521 00522 // ###################################################################### 00523 template <class T> 00524 Image<typename promote_trait<T, float>::TP> 00525 dct(const Image<T>& src, const int offx, const int offy, const int size) 00526 { 00527 ASSERT(src.initialized()); 00528 ASSERT(offx + size <= src.getWidth() && offy + size <= src.getHeight()); 00529 00530 int m = (int)(log((double)size) / log(2.0) + 0.001); 00531 ASSERT((1 << m) == size); // size must be a power of two 00532 int N = 1 << m; 00533 00534 float x[512][512], ct[512], s[512][512]; 00535 for (int ii = 0; ii < N; ii ++) 00536 for (int jj = 0; jj < N; jj ++) 00537 x[ii][jj] = (float)src.getVal(ii+offx, jj+offy); 00538 00539 /* Implementation of 2D FCT through the 1D decimation in 00540 frequency (DIF) split radix algorithm from paper: 00541 00542 1)Skodras A.N., and Christopoulos C.A., Split-radix fast cosine 00543 transform, Int. J. Electronics, Vol. 74, No. 4, pp. 513-522, 1993. 00544 00545 2)Christopoulos C.A., Skodras A.N. and Cornelis J., Comparative 00546 performance evaluation of algorithms for fast computation of the 00547 two-dimensional DCT, Proceedings of the IEEE Benelux & ProRISC 00548 Workshop on Circuits, Systems and Signal Processing, Papendal, 00549 Arnhem (The Netherlands), March 24, 1994, pp. 75-79. 00550 00551 3)Christopoulos C.A., and Skodras A.N., On the computation of the fast 00552 cosine transform, Proceedings of the European Conference on Circuit 00553 Theory and Design (ECCTD' 93), Davos, Switzerland, Aug.30 - Sept.3, 00554 1993, pp. 1037-1042. 00555 00556 First written: September 1994 00557 Last modified: January 1996. 00558 00559 Author: 00560 Charilaos Christopoulos 00561 Ericsson Telecom AB 00562 Compression Lab, HF/ETX/ML 00563 126 24 Stockholm, Sweden 00564 00565 ch.christopoulos@clab.ericsson.se 00566 00567 (c) Please notice that you are allowed to use the algorithms for your 00568 research provided that you always give a reference to the corresponding 00569 paper (which is not always written by myself as you will see) and a 00570 reference to the author of the algorithms. You can do any modifications, 00571 (and add your name in the list of authors that did modifications), 00572 improvements, etc in the programs and you can distribute the programs to 00573 other researchers provided that the name of the authors of the first 00574 version will remain in the software. 00575 */ 00576 { 00577 int is,id,i0,i1,i2,i3; 00578 00579 //makecosinetable() /* 1D cosine table for N points */ 00580 { 00581 int n2,n4; 00582 double e; 00583 float *p; 00584 00585 n2 = N << 1; p = ct; 00586 00587 for (int k = 1; k < m; k++) { 00588 e = M_PI / n2; 00589 n2 = n2 >> 1; 00590 n4 = n2 >> 2; 00591 00592 for (int j = 0; j < n4; j++) { 00593 *p++ = cos(((j<<2)+1)*e); 00594 *p++ = sin(((j<<2)+1)*e); 00595 *p++ = cos((((j<<2)+1)<<1)*e); 00596 00597 } 00598 } 00599 *p = cos(M_PI/4.0); 00600 } 00601 00602 // rowsinputmapping() 00603 { 00604 int n1,n2,n; 00605 00606 for(n1=0; n1 <= N; n1++) 00607 for (n2=0; n2<N; n2++ ) 00608 s[n1][n2] = x[n1][n2]; 00609 00610 for (int cols=0; cols<N; cols++) { 00611 for(n=0; n < N/2; n++) { 00612 x[cols][n] = s[cols][2*n]; 00613 x[cols][N-n-1] = s[cols][2*n+1]; 00614 } 00615 } 00616 } 00617 00618 for (int rows=0; rows<N; rows++) { 00619 int p=0; 00620 int n2 = N << 1; 00621 for (int k = 1; k < m; k++) { 00622 n2 = n2 >> 1; 00623 const int n4 = n2 >> 2; 00624 00625 for (int j = 0; j < n4; j++) { 00626 is = j; 00627 id = n2 << 1; 00628 00629 again1: 00630 for (i0 = is; i0 < N; i0 +=id) { 00631 i1 = i0 + n4; 00632 i2 = i1 + n4; 00633 i3 = i2 + n4; 00634 00635 float t1 = 2 * (x[rows][i0] - x[rows][i2]) *ct[p] ; 00636 float t2 = (-2) * (x[rows][i1] - x[rows][i3]) * ct[p+1]; 00637 00638 x[rows][i0] = x[rows][i0] + x[rows][i2]; 00639 x[rows][i1] = x[rows][i1] + x[rows][i3]; 00640 x[rows][i2] = t1 + t2; 00641 x[rows][i3] = 2 * (t1 - t2) * ct[p+2]; 00642 00643 /* if at the m-1 stage, divide by 2 */ 00644 if (k==m-1) { 00645 x[rows][i2] = x[rows][i2]/2; 00646 x[rows][i3] = x[rows][i3]/2; 00647 } 00648 } 00649 is = (id << 1) - n2 + j; 00650 id = id << 2; 00651 if ( is < N ) goto again1; 00652 p+=3; 00653 } 00654 } 00655 /* last stage */ 00656 is = 0; 00657 id = 4; 00658 l_again1: 00659 for( i0 = is; i0 < N; i0 += id) { 00660 i1 = i0 + 1; 00661 float t1 = x[rows][i0]; 00662 x[rows][i0] = t1 + x[rows][i1]; 00663 /* divide the upper element by 2 */ 00664 if (i0 != 0) x[rows][i0] = x[rows][i0]/2; 00665 x[rows][i1] = (t1 - x[rows][i1]) * ct[p]; 00666 } 00667 is = (id << 1) - 2; 00668 id = id << 2; 00669 if( is < N ) goto l_again1; 00670 }/* end of for rows */ 00671 00672 //rowsbitreversal() /* 2d bit reversal */ 00673 { 00674 int v1, v2, v3,i,k; 00675 float xt; 00676 00677 /* revesre rows */ 00678 for (int cols =0; cols<N; cols ++) { 00679 v1 = (m+1)/2; 00680 v2 = 1 << v1; 00681 v3 = N-1-v2; 00682 int j=0; 00683 for(i=1; i<=v3; i++){ 00684 k= N>>1; 00685 while(k<=j){ 00686 j=j-k; 00687 k=k>>1; 00688 } 00689 j +=k; 00690 if(i<j){ 00691 xt=x[cols][j]; 00692 x[cols][j]=x[cols][i]; 00693 x[cols][i]=xt; 00694 } 00695 } 00696 } 00697 } 00698 //rowspostadditions() 00699 { 00700 int step,loops,ep,i,l; 00701 00702 for (int rows=0; rows<N; rows++) { 00703 step =N; 00704 loops = 1; 00705 for (int k=1; k<m; k++) { 00706 step = step>>1; 00707 ep = step>>1; 00708 loops = loops <<1; 00709 for (int j=0; j<(step>>1); j++) { 00710 l=ep; 00711 for (i=1; i<loops;i++) { 00712 x[rows][l+step] = x[rows][l+step]-x[rows][l]; 00713 l =l+step; 00714 } 00715 ep +=1; 00716 } 00717 } 00718 } 00719 } 00720 // columnsinputmapping() /* 2d input mapping */ 00721 { 00722 int n1,n2,n; 00723 00724 for(n1=0; n1 <= N; n1++) 00725 for (n2=0; n2<N; n2++ ) 00726 s[n1][n2] = x[n1][n2]; 00727 00728 for (int rows=0; rows<N; rows++) { 00729 for(n=0; n < N/2; n++) { 00730 x[n][rows] = s[2*n][rows]; 00731 x[N-n-1][rows] = s[2*n+1][rows]; 00732 } 00733 } 00734 } 00735 00736 for (int cols=0; cols<N; cols++) { 00737 int p=0; 00738 00739 int n2 = N << 1; 00740 for (int k = 1; k < m; k++) { 00741 n2 = n2 >> 1; 00742 const int n4 = n2 >> 2; 00743 00744 for (int j = 0; j < n4; j++) { 00745 is = j; 00746 id = n2 << 1; 00747 again2: 00748 for (i0 = is; i0 < N; i0 +=id) { 00749 i1 = i0 + n4; 00750 i2 = i1 + n4; 00751 i3 = i2 + n4; 00752 00753 float t1 = 2 * (x[i0][cols] - x[i2][cols]) *ct[p] ; 00754 float t2 = (-2) * (x[i1][cols]-x[i3][cols])* ct[p+1]; 00755 00756 x[i0][cols] = x[i0][cols] + x[i2][cols]; 00757 x[i1][cols] = x[i1][cols] + x[i3][cols]; 00758 x[i2][cols] = t1 + t2; 00759 x[i3][cols] = 2 * (t1 - t2) * ct[p+2]; 00760 00761 /* if at the m-1 stage, divide by 2 */ 00762 if (k==m-1) { 00763 x[i2][cols] = x[i2][cols]/2; 00764 x[i3][cols] = x[i3][cols]/2; 00765 } 00766 } 00767 00768 is = (id << 1) - n2 + j; 00769 id = id << 2; 00770 if ( is < N ) goto again2; 00771 p+=3; 00772 } 00773 } 00774 00775 /* last stage */ 00776 is = 0; 00777 id = 4; 00778 l_again2: 00779 for( i0 = is; i0 < N; i0 += id) { 00780 i1 = i0 + 1; 00781 00782 float t1 = x[i0][cols]; 00783 x[i0][cols] = t1 + x[i1][cols]; 00784 /* divide the upper element by 2 */ 00785 if (i0 != 0) x[i0][cols] = x[i0][cols]/2; 00786 x[i1][cols] = (t1 - x[i1][cols]) * ct[p]; 00787 } 00788 00789 is = (id << 1) - 2; 00790 id = id << 2; 00791 if( is < N ) goto l_again2; 00792 00793 }/* end of for cols */ 00794 00795 // columnsbitreversal() 00796 { 00797 int v1, v2, v3,i,k; 00798 float xt; 00799 /* reverse columns */ 00800 for (int rows =0; rows<N; rows ++) { 00801 v1 = (m+1)/2; 00802 v2 = 1 << v1; 00803 v3 = N-1-v2; 00804 int j=0; 00805 for(i=1; i<=v3; i++){ 00806 k= N>>1; 00807 while(k<=j){ 00808 j=j-k; 00809 k=k>>1; 00810 } 00811 j +=k; 00812 if(i<j){ 00813 xt=x[j][rows]; 00814 x[j][rows]=x[i][rows]; 00815 x[i][rows]=xt; 00816 } 00817 } 00818 } 00819 } 00820 00821 //columnspostadditions() 00822 { 00823 int step,loops,ep,i,l; 00824 00825 for (int cols=0; cols<N; cols++) { 00826 step =N; 00827 loops = 1; 00828 for (int k=1; k<m; k++) { 00829 step = step>>1; 00830 ep = step>>1; 00831 loops = loops <<1; 00832 for (int j=0; j<(step>>1); j++) { 00833 l=ep; 00834 for (i=1; i<loops; i++) { 00835 x[l+step][cols] = x[l+step][cols] - x[l][cols]; 00836 l =l+step; 00837 } 00838 ep +=1; 00839 } 00840 } 00841 } 00842 } /* end of rowspostadditions */ 00843 00844 } /* end of SR_FCT */ 00845 typedef typename promote_trait<T, float>::TP TF; 00846 Image<TF> result(N, N, NO_INIT); 00847 for (int ii = 0; ii < N; ii ++) 00848 for (int jj = 0; jj < N; jj ++) 00849 result.setVal(ii, jj, x[ii][jj] * 4.0F); 00850 00851 return result; 00852 } 00853 00854 // ###################################################################### 00855 template <class T> 00856 float infoFFT(const Image<T>& src, const float eps, const Rectangle& rect) 00857 { 00858 Image<float> tmp = crop(src, rect); 00859 Image<float> tmp2 = infoFFT(tmp, eps); 00860 float mini, maxi; 00861 getMinMax(tmp2, mini, maxi); 00862 return maxi; 00863 } 00864 00865 // ###################################################################### 00866 template <class T> 00867 Image<float> infoFFT(const Image<T>& src, const float eps) 00868 { 00869 Image<float> result(src.getWidth() / 4, src.getHeight() / 4, NO_INIT); 00870 // look at 16x16 patches every 4 pix in x and y 00871 00872 const Dims dims = src.getDims(); 00873 00874 for (int cx = 0; cx < dims.w(); cx += 4) // center of patch 00875 for (int cy = 0; cy < dims.h(); cy += 4) 00876 { 00877 int offx = cx - 8, offy = cy - 8; 00878 if (dims.w() > 32 && dims.h() > 32) 00879 { 00880 // kill last column: 00881 if (offx == dims.w() - 16) offx = dims.w(); 00882 00883 // last line;for symmetry of display: 00884 if (offy == dims.h() - 16) offy = dims.h(); 00885 } 00886 00887 if (offx >= 0 && 00888 offy >= 0 && 00889 offx+16 <= dims.w() && 00890 offy+16 <= dims.h()) 00891 { 00892 // take 2D fft of a 16x16 patch starting at offx, 00893 // offy, compute abs, and compute nb coeffs > epsn 00894 float data[16][33]; 00895 for (int row = 0; row < 16; row ++) 00896 for (int col = 0; col < 16; col ++) 00897 { 00898 data[row][col*2+2] = 0.0; 00899 data[row][col*2+1] = (float) src.getVal(offx+col, offy+row); 00900 } 00901 00902 for (int row = 0; row < 16; row ++) 00903 { 00904 // 1D FFT routine in x 00905 // takes abs fft of data[row][1..32] 00906 int j = 1; 00907 for (int i = 1; i < 32; i += 2) // bit reversal procedure 00908 { 00909 if (j > i) 00910 { float tmp = data[row][i]; data[row][i] = data[row][j]; 00911 data[row][j] = tmp; tmp = data[row][i+1]; 00912 data[row][i+1] = data[row][j+1]; data[row][j+1] = tmp; } 00913 int m = 16; while (m >= 2 && j > m) { j -= m; m >>= 1; } 00914 j += m; 00915 } 00916 int mmax = 2; 00917 while (32 > mmax) 00918 { 00919 int istep = mmax << 1; float theta = -2*M_PI/(float)mmax; 00920 float wtemp = sin(0.5*theta); float wpr = -2.0*wtemp*wtemp; 00921 float wpi = sin(theta); float wr = 1.0; float wi = 0.0; 00922 for (int m = 1; m < mmax; m += 2) 00923 { 00924 for (int i = m; i <= 32; i += istep) 00925 { 00926 j = i + mmax; 00927 float tempr=wr*data[row][j]-wi*data[row][j+1]; 00928 float tempi = wr*data[row][j+1]+wi*data[row][j]; 00929 data[row][j] = data[row][i]-tempr; 00930 data[row][j+1] = data[row][i+1]-tempi; 00931 data[row][i]+=tempr; 00932 data[row][i+1]+=tempi; 00933 } 00934 wr=(wtemp=wr)*wpr-wi*wpi+wr; wi=wi*wpr+wtemp*wpi+wi; 00935 } 00936 mmax = istep; 00937 } 00938 } 00939 00940 // transpose to do the same thing in y 00941 for (int row = 0; row < 16; row ++) 00942 for (int col = 0; col < row; col ++) 00943 { 00944 // transpose real and imaginary parts 00945 float tt = data[row][col*2+1]; 00946 data[row][col*2+1] = data[col][row*2+1]; 00947 data[col][row*2+1] = tt; 00948 tt = data[row][col*2+2]; 00949 data[row][col*2+2] = data[col][row*2+2]; 00950 data[col][row*2+2] = tt; 00951 } 00952 00953 // redo the same stuff 00954 for (int row = 0; row < 16; row ++) 00955 { 00956 // 1D FFT routine in x 00957 // takes abs fft of data[row][1..32] 00958 int j = 1; 00959 for (int i = 1; i < 32; i += 2) // bit reversal procedure 00960 { 00961 if (j > i) 00962 { float tmp = data[row][i]; data[row][i] = data[row][j]; 00963 data[row][j] = tmp; tmp = data[row][i+1]; 00964 data[row][i+1] = data[row][j+1]; data[row][j+1] = tmp; } 00965 int m = 16; while (m >= 2 && j > m) { j -= m; m >>= 1; } 00966 j += m; 00967 } 00968 int mmax = 2; 00969 while (32 > mmax) 00970 { 00971 int istep = mmax << 1; float theta = -2*M_PI/(float)mmax; 00972 float wtemp = sin(0.5*theta); float wpr = -2.0*wtemp*wtemp; 00973 float wpi = sin(theta); float wr = 1.0; float wi = 0.0; 00974 for (int m = 1; m < mmax; m += 2) 00975 { 00976 for (int i = m; i <= 32; i += istep) 00977 { 00978 j = i + mmax; 00979 float tempr=wr*data[row][j]-wi*data[row][j+1]; 00980 float tempi = wr*data[row][j+1]+wi*data[row][j]; 00981 data[row][j] = data[row][i]-tempr; 00982 data[row][j+1] = data[row][i+1]-tempi; 00983 data[row][i]+=tempr; 00984 data[row][i+1]+=tempi; 00985 } 00986 wr=(wtemp=wr)*wpr-wi*wpi+wr; wi=wi*wpr+wtemp*wpi+wi; 00987 } 00988 mmax = istep; 00989 } 00990 } 00991 00992 // WARNING: at this point we have the transpose of the fft 00993 00994 // count # coeffs > epsn 00995 float nbsup = 0.0; 00996 float epsn = (eps * 256.0 * 127.5)*(eps * 256.0 * 127.5); 00997 // epsn is normalized by patch size and ^2 00998 for (int row = 0; row < 16; row ++) 00999 for (int col = 1; col <= 32; col += 2) 01000 { 01001 float val = data[row][col]*data[row][col] + 01002 data[row][col+1]*data[row][col+1]; // this is |..|^2 01003 if (val > epsn) nbsup += 1.0; 01004 } 01005 result.setVal(cx/4, cy/4, nbsup / 256.0); // ratio>eps, in [0..1] 01006 } 01007 else 01008 result.setVal(cx/4, cy/4, 0.0); 01009 } 01010 01011 return result; 01012 } 01013 01014 // ###################################################################### 01015 double learningCoeff(const Image<float>& featureMap, 01016 const Image<byte>& targetDMap, 01017 const bool softmask, 01018 const int in_thresh, const int out_thresh) 01019 { 01020 ASSERT(featureMap.initialized()); ASSERT(targetDMap.initialized()); 01021 ASSERT(featureMap.isSameSize(targetDMap)); 01022 01023 const bool havein = (in_thresh >= 0 && in_thresh <= 255); 01024 const bool haveou = (out_thresh >= 0 && out_thresh <= 255); 01025 01026 if (softmask) 01027 { 01028 // remember that in the dmap zero means in the target and 255 01029 // outside. In the soft version, we just take the product of the 01030 // feature map by the mask (or 255-mask) and look for the avg 01031 // resulting value: 01032 float av = mean(featureMap) / float(featureMap.getSize()); 01033 float oav = sum(featureMap * targetDMap) / (255.0f * sum(targetDMap)); 01034 Image<float> mask = binaryReverse(targetDMap, byte(255)); 01035 float iav = sum(featureMap * mask) / (255.0f * sum(mask)); 01036 01037 if (!havein && !haveou) 01038 { LFATAL("At least one threshold must be valid"); return 0.0;} 01039 else if (!havein && haveou) // bg only: strong is bad 01040 return double((av - oav) / av); 01041 else if (havein && !haveou) // target only: strong good 01042 return double((iav - av) / av); 01043 else // target+bg: higher inside than outside is good (>0), opposite bad 01044 return double((iav - oav) / av); 01045 } 01046 else 01047 { 01048 // Hard mask approach: examine local maxs in/out of targets and 01049 // compute learning weight convention: in the given distance 01050 // map, distance 0 is inside targets; is considered target 01051 // everything for which map value <= in_thresh; is considered 01052 // non-target everything for which map value >= out_thresh; the 01053 // transition between target and non-target is not used for 01054 // computation 01055 01056 Image<byte>::const_iterator dmapptr = targetDMap.begin(); 01057 Image<float>::const_iterator sptr = featureMap.begin(), 01058 stop = featureMap.end(); 01059 01060 float glob_min = *sptr; float glob_max = *sptr; 01061 float in_max = 0.0f; float out_max = 0.0f; 01062 01063 while (sptr != stop) 01064 { 01065 const int mapval = *dmapptr++; 01066 const float val = *sptr++; 01067 if (mapval <= in_thresh && val > in_max) in_max = val; 01068 if (mapval >= out_thresh && val > out_max) out_max = val; 01069 if (val < glob_min) glob_min = val; 01070 if (val > glob_max) glob_max = val; 01071 } 01072 01073 if (!havein && !haveou) 01074 { LFATAL("At least one threshold must be valid"); return 0.0;} 01075 else if (!havein && haveou) // bg only: strong is bad 01076 return -double(in_max) / (double(glob_max) - double(glob_min)); 01077 else if (havein && !haveou) // target only: strong good 01078 return double(in_max) / (double(glob_max) - double(glob_min)); 01079 else // target+bg: higher inside than outside is good (>0), opposite bad 01080 return (double(in_max) - double(out_max)) / 01081 (double(glob_max) - double(glob_min)); 01082 } 01083 } 01084 01085 // ###################################################################### 01086 template <class T> 01087 Image<T> scaleBlock(const Image<T>& src, const Dims newDims) 01088 { 01089 int new_w = newDims.w(), new_h = newDims.h(); 01090 std::vector<int> col_idx(new_w), row_idx(new_h); 01091 int w = src.getWidth(), h = src.getHeight(); 01092 01093 for (int x = 0; x < new_w; x++) 01094 col_idx[x] = (int)((float)x / (float)new_w * (float)w); 01095 for (int y = 0; y < new_h; y++) 01096 row_idx[y] = (int)((float)y / (float)new_h * (float)h); 01097 01098 Image<T> result(new_w, new_h, NO_INIT); 01099 typename Image<T>::iterator rptr = result.beginw(); 01100 for (int y = 0; y < new_h; y++) 01101 for (int x = 0; x < new_w; x++) 01102 { 01103 *rptr++ = src.getVal(col_idx[x],row_idx[y]); 01104 } 01105 return result; 01106 } 01107 01108 // ###################################################################### 01109 template <class T> 01110 int floodClean(const Image<T>& src, Image<T>& dest, const Point2D<int>& seed, 01111 const T thresh, const T val, int numConn) 01112 { 01113 ASSERT(src.initialized()); ASSERT(src.coordsOk(seed)); 01114 01115 // if dest is not initialized, initilize it to ZEROS 01116 // ATTENTION: the old behavior is initialization to src 01117 // If you want to have this behavior, you have to explicitely 01118 // initialize dest to src BEFORE calling floodClean 01119 if (!dest.initialized()) dest.resize(src.getDims(),true); 01120 01121 if (src.getVal(seed) < thresh) return -1; // starting point not in object 01122 01123 // Allocate space for the recursion stack 01124 std::vector<Point2D<int> > stk; 01125 const int STACKSIZE = 256 * 256 + 20; 01126 stk.reserve(STACKSIZE); 01127 stk.push_back(seed); 01128 01129 // relative directions 01130 std::vector<Point2D<int> > dirs(8); 01131 dirs[0] = Point2D<int>(1,0); dirs[1] = Point2D<int>(0,1); 01132 dirs[2] = Point2D<int>(0,-1); dirs[3] = Point2D<int>(-1,0); 01133 dirs[4] = Point2D<int>(1,1); dirs[5] = Point2D<int>(1,-1); 01134 dirs[6] = Point2D<int>(-1,-1); dirs[7] = Point2D<int>(-1,1); 01135 01136 // mask for the region already visited 01137 Image<byte> visited(src.getDims(), ZEROS); 01138 int nbpix = 0; 01139 01140 while (! stk.empty()) 01141 { 01142 Point2D<int> index = stk.back(); stk.pop_back(); 01143 if (!visited.getVal(index)) // we have not yet visited this point 01144 { 01145 // we know that the current point IS in our object: 01146 dest.setVal(index, val); // flood output 01147 visited.setVal(index,1); // don't count this one again 01148 ++nbpix; // one more flooded pixel 01149 01150 for (int i = 0; i < numConn; i++) 01151 { 01152 Point2D<int> idx2 = index + dirs[i]; 01153 if ((src.coordsOk(idx2)) && 01154 (!visited.getVal(idx2)) && 01155 (src.getVal(idx2) >= thresh) 01156 ) 01157 stk.push_back(idx2); 01158 } 01159 } 01160 } 01161 return nbpix; 01162 } 01163 01164 // ###################################################################### 01165 template <class T> 01166 int floodCleanBB(const Image<T>& src, Image<T>& dest, const Point2D<int>& seed, 01167 const T thresh, const T val, Rectangle& bbox) 01168 { 01169 ASSERT(src.initialized()); ASSERT(src.coordsOk(seed)); 01170 01171 int ll = src.getWidth(), rr = -1, tt = src.getHeight(), bb = -1; 01172 01173 // if dest is not initialized, initilize it to ZEROS 01174 // ATTENTION: the old behavior is initialization to src 01175 // If you want to have this behavior, you have to explicitely 01176 // initialize dest to src BEFORE calling floodClean 01177 if (!dest.initialized()) dest.resize(src.getDims(),true); 01178 01179 if (src.getVal(seed) < thresh) return -1; // starting point not in object 01180 01181 // Allocate space for the recursion stack 01182 std::vector<Point2D<int> > stk; 01183 const int STACKSIZE = 256 * 256 + 20; 01184 stk.reserve(STACKSIZE); 01185 stk.push_back(seed); 01186 01187 // relativ directions 01188 std::vector<Point2D<int> > dirs(8); 01189 dirs[0] = Point2D<int>(1,0); dirs[1] = Point2D<int>(0,1); 01190 dirs[2] = Point2D<int>(1,1); dirs[3] = Point2D<int>(1,-1); 01191 dirs[4] = Point2D<int>(0,-1); dirs[5] = Point2D<int>(-1,0); 01192 dirs[6] = Point2D<int>(-1,-1); dirs[7] = Point2D<int>(-1,1); 01193 01194 // mask for the region already visited 01195 Image<byte> visited(src.getDims(), ZEROS); 01196 int nbpix = 0; 01197 01198 while (! stk.empty()) 01199 { 01200 Point2D<int> index = stk.back(); stk.pop_back(); 01201 if (visited.getVal(index) == 0) // we have not yet visited this point 01202 { 01203 // we know that the current point IS in our object: 01204 dest.setVal(index, val); // flood output 01205 visited.setVal(index,1); // don't count this one again 01206 ++nbpix; // one more flooded pixel 01207 01208 // set the bbox values if necessary 01209 if (index.i < ll) ll = index.i; 01210 if (index.i > rr) rr = index.i; 01211 if (index.j < tt) tt = index.j; 01212 if (index.j > bb) bb = index.j; 01213 01214 for (int i = 0; i < 8; i++) 01215 { 01216 Point2D<int> idx2 = index + dirs[i]; 01217 if ((src.coordsOk(idx2)) && 01218 (src.getVal(idx2) >= thresh) && 01219 (visited.getVal(idx2) == 0)) 01220 stk.push_back(idx2); 01221 } 01222 } 01223 } 01224 if (nbpix > 0) bbox = Rectangle::tlbrI(tt,ll,bb,rr); 01225 return nbpix; 01226 } 01227 01228 // ###################################################################### 01229 template <class T> 01230 Image<float> distDegrade(const Image<T>& src, const Point2D<int>& foa, const float area) 01231 { 01232 float s2 = area / 100.0; 01233 float dist2,dx,dy; 01234 const T zero = T(); 01235 Image<float> result(src.getDims(),ZEROS); 01236 typename Image<float>::iterator rptr = result.beginw(); 01237 typename Image<T>::const_iterator sptr = src.begin(); 01238 01239 for (int y = 0; y < src.getHeight(); y++) 01240 for (int x = 0; x < src.getWidth(); x++) 01241 { 01242 if (*sptr > zero) 01243 { 01244 dx = x - foa.i; 01245 dy = y - foa.j; 01246 dist2 = dx * dx + dy * dy; 01247 if (dist2 == 0.0) 01248 *rptr = 1.0; 01249 else 01250 *rptr = 1.0 - exp( - s2 / dist2); 01251 } 01252 sptr++; rptr++; 01253 } 01254 return result; 01255 } 01256 01257 // ###################################################################### 01258 template <class T> 01259 Image<byte> segmentObjectClean(const Image<T>& src, 01260 const Point2D<int>& seed, int numConn) 01261 { 01262 float start = 0.5, pstep = 0.05, mina = 0.05, expl = 1.05; 01263 float stop = 0.1; 01264 ASSERT(src.initialized()); 01265 Image<T> tmp; 01266 bool explode = false; int prev = 0; double thresh = 1.0, minThresh = 0.0; 01267 double step = double(src.getVal(seed.i, seed.j)) * pstep; 01268 int minadiff = int(float(src.getSize()) * mina); 01269 double efac = expl; 01270 int iter = 0; 01271 01272 // while still not explode and growth factor still above 1.0 01273 while(!explode && minadiff > 0 && efac > 1.0) 01274 { 01275 // update current threshold and minimum threshold 01276 thresh = double(src.getVal(seed.i, seed.j)) * start; 01277 minThresh = double(src.getVal(seed.i, seed.j)) * stop; 01278 iter = 0; 01279 01280 // while still not explode and threshold still above minimum 01281 while(!explode && thresh > minThresh) 01282 { 01283 tmp.freeMem(); 01284 int area = floodClean(src, tmp, seed, (T)thresh, (T)255.0, numConn); 01285 LDEBUG("%d->%d[%d] t=%.3f efac=%.3f", 01286 prev, area, minadiff, thresh, efac); 01287 01288 // make sure at least 1 iteration occur 01289 // make sure that the increase in area 01290 // does not go beyond "expl" 01291 if (iter > 1 && area > prev + minadiff && 01292 area > int(double(prev)*efac)) 01293 explode = true; 01294 else 01295 { prev = area; thresh -= step; } 01296 iter ++; 01297 } 01298 01299 minadiff -= minadiff / 4; efac *= 0.95; 01300 } 01301 01302 // write out segmented object: 255 in object, zero otherwise 01303 thresh += step; // back 1 step before explosion 01304 tmp.freeMem(); 01305 floodClean(src, tmp, seed, (T)thresh, (T)255.0, numConn); 01306 01307 return Image<byte>(tmp); 01308 } 01309 01310 // ###################################################################### 01311 template <class T> 01312 Image<T> highThresh(const Image<T>& src, const T thresh, const T val) 01313 { 01314 ASSERT(src.initialized()); 01315 Image<T> res(src); 01316 typename Image<T>::iterator aptr = res.beginw(), stop = res.endw(); 01317 01318 while (aptr != stop) 01319 { 01320 if (*aptr >= thresh) *aptr = val; 01321 ++aptr; 01322 } 01323 return res; 01324 } 01325 01326 // ###################################################################### 01327 template <class T_or_RGB> 01328 Image<T_or_RGB> replaceVals(const Image<T_or_RGB>& src, 01329 const T_or_RGB from, 01330 const T_or_RGB to, 01331 const T_or_RGB other) 01332 { 01333 Image<T_or_RGB> result(src.getDims(), NO_INIT); 01334 typename Image<T_or_RGB>::const_iterator sptr = src.begin(); 01335 typename Image<T_or_RGB>::iterator rptr = result.beginw(); 01336 while (rptr != result.endw()) 01337 *rptr++ = (*sptr++ == from) ? to : other; 01338 return result; 01339 } 01340 01341 // ###################################################################### 01342 template <class T_or_RGB> 01343 Image<T_or_RGB> replaceVals(const Image<T_or_RGB>& src, 01344 const T_or_RGB from, 01345 const T_or_RGB to) 01346 { 01347 if (from == to) return src; 01348 Image<T_or_RGB> result(src.getDims(), NO_INIT); 01349 typename Image<T_or_RGB>::const_iterator sptr = src.begin(); 01350 typename Image<T_or_RGB>::iterator rptr; 01351 for (rptr = result.beginw(); rptr != result.endw(); ++rptr, ++sptr) 01352 *rptr = (*sptr == from) ? to : *sptr; 01353 return result; 01354 } 01355 01356 // ###################################################################### 01357 template <class T_or_RGB> 01358 Image<T_or_RGB> composite(const Image<T_or_RGB>& fg, 01359 const Image<T_or_RGB>& bg, 01360 const T_or_RGB transparent) 01361 { 01362 ASSERT(fg.isSameSize(bg)); 01363 01364 Image<T_or_RGB> result(fg.getDims(), NO_INIT); 01365 typename Image<T_or_RGB>::const_iterator 01366 fgptr = fg.begin(), bgptr = bg.begin(), stop = fg.end(); 01367 typename Image<T_or_RGB>::iterator rptr = result.beginw(); 01368 01369 while(fgptr != stop) 01370 { 01371 if (*fgptr == transparent) *rptr++ = *bgptr; 01372 else *rptr++ = *fgptr; 01373 01374 ++fgptr; ++bgptr; 01375 } 01376 01377 return result; 01378 } 01379 01380 // ###################################################################### 01381 template <class T_or_RGB> 01382 Image<T_or_RGB> createMask(const Image<T_or_RGB>& fg, 01383 const Image<bool> mask, 01384 const T_or_RGB transparent = T_or_RGB()) 01385 { 01386 ASSERT(fg.isSameSize(mask)); 01387 Image<T_or_RGB> result(fg.getDims(), NO_INIT); 01388 typename Image<T_or_RGB>::const_iterator 01389 fgptr = fg.begin(), stop = fg.end(); 01390 typename Image<bool>::const_iterator 01391 maskptr = mask.begin(); 01392 typename Image<T_or_RGB>::iterator rptr = result.beginw(); 01393 01394 while(fgptr != stop) 01395 { 01396 if (*maskptr) *rptr++ = *fgptr; //non-zero 01397 else *rptr++ = transparent; 01398 01399 ++fgptr; ++maskptr; 01400 } 01401 01402 return result; 01403 01404 } 01405 01406 // ###################################################################### 01407 template <class T_or_RGB> 01408 Image<T_or_RGB> mosaic(const Image<T_or_RGB>& fg, 01409 const Image<T_or_RGB>* bg, 01410 const T_or_RGB* transvalues, 01411 const uint Nimages) 01412 { 01413 uint i; 01414 for(i = 0; i < Nimages; i++) 01415 ASSERT(fg.isSameSize(bg[i])); 01416 01417 Image<T_or_RGB> result(fg.getDims(), NO_INIT); 01418 typename Image<T_or_RGB>::const_iterator 01419 fgptr = fg.begin(), stop = fg.end(); 01420 01421 typename Image<T_or_RGB>::const_iterator bgptr[Nimages]; 01422 for(i = 0; i < Nimages; i++) 01423 bgptr[i] = bg[i].begin(); 01424 01425 typename Image<T_or_RGB>::iterator rptr = result.beginw(); 01426 01427 while(fgptr != stop) 01428 { 01429 i = 0; 01430 while(*fgptr != transvalues[i] && i < Nimages) i++; 01431 if (i < Nimages) 01432 *rptr++ = *bgptr[i]; 01433 else 01434 *rptr++ = *fgptr; 01435 01436 ++fgptr; 01437 for(i = 0; i < Nimages; i++) 01438 bgptr[i]++; 01439 } 01440 01441 return result; 01442 } 01443 01444 // ###################################################################### 01445 template <class T_or_RGB> 01446 Image<T_or_RGB> alphaBlend(const Image<T_or_RGB>& fg, 01447 const Image<T_or_RGB>& bg, 01448 const double alpha, 01449 const T_or_RGB transparent) 01450 { 01451 ASSERT(fg.isSameSize(bg)); 01452 ASSERT(alpha>=0 && alpha<=1); 01453 01454 Image<T_or_RGB> result(fg.getDims(), NO_INIT); 01455 typename Image<T_or_RGB>::const_iterator 01456 fgptr = fg.begin(), bgptr = bg.begin(), stop = fg.end(); 01457 typename Image<T_or_RGB>::iterator rptr = result.beginw(); 01458 01459 while(fgptr != stop) 01460 { 01461 if (*fgptr == transparent) *rptr++ = *bgptr; 01462 else *rptr++ = T_or_RGB((*fgptr) * (1-alpha) + T_or_RGB(*bgptr) * alpha); 01463 01464 ++fgptr; ++bgptr; 01465 } 01466 01467 return result; 01468 } 01469 01470 // ###################################################################### 01471 template <class T> 01472 Image<byte> makeBinary(const Image<T>& src, const T& threshold, 01473 const byte lowVal, const byte highVal) 01474 { 01475 Image<byte> result(src.getDims(), NO_INIT); 01476 typename Image<T>::const_iterator sptr = src.begin(); 01477 Image<byte>::iterator rptr = result.beginw(), stop = result.endw(); 01478 while (rptr != stop) 01479 *rptr++ = (*sptr++ <= threshold) ? lowVal : highVal; 01480 return result; 01481 } 01482 01483 // ###################################################################### 01484 template <class T> 01485 Image<byte> makeBinary2(const Image<T>& src, 01486 const T& lowThresh, const T& highThresh, 01487 const byte lowVal, const byte highVal) 01488 { 01489 Image<byte> result(src.getDims(), NO_INIT); 01490 typename Image<T>::const_iterator sptr = src.begin(); 01491 Image<byte>::iterator rptr = result.beginw(), stop = result.endw(); 01492 while (rptr != stop) 01493 { 01494 *rptr++ = (*sptr >= lowThresh && *sptr <= highThresh) ? highVal : lowVal; 01495 sptr++; 01496 } 01497 return result; 01498 } 01499 01500 // ###################################################################### 01501 template <class T_or_RGB> 01502 void pasteImage(Image<T_or_RGB>& background, 01503 const Image<T_or_RGB>& foreground, 01504 const T_or_RGB& transparent, 01505 const Point2D<int> location, 01506 float opacity) 01507 { 01508 float op2 = 1.0F - opacity; 01509 int bw = background.getWidth(); 01510 int bh = background.getHeight(); 01511 int fw = foreground.getWidth(); 01512 int fh = foreground.getHeight(); 01513 01514 Point2D<int> bStart = location; 01515 Point2D<int> fStart(0,0); 01516 Point2D<int> bEnd(location.i + fw, location.j + fh); 01517 Point2D<int> fEnd(fw,fh); 01518 01519 if (bStart.i < 0) { fStart.i -= bStart.i; bStart.i = 0; } 01520 if (bStart.j < 0) { fStart.j -= bStart.j; bStart.j = 0; } 01521 if (bEnd.i > bw) { fEnd.i -= (bEnd.i - bw); bEnd.i = bw; } 01522 if (bEnd.j > bh) { fEnd.j -= (bEnd.j - bh); bEnd.j = bh; } 01523 01524 // initialize the iterators 01525 typename Image<T_or_RGB>::iterator bPtr1, bPtr2; 01526 typename Image<T_or_RGB>::const_iterator fPtr1, fPtr2; 01527 bPtr1 = background.beginw() + bStart.j * bw + bStart.i; 01528 fPtr1 = foreground.begin() + fStart.j * fw + fStart.i; 01529 01530 // loop over the image patch and paste it in 01531 for (int y = bStart.j; y < bEnd.j; ++y) 01532 { 01533 bPtr2 = bPtr1; fPtr2 = fPtr1; 01534 for (int x = bStart.i; x < bEnd.i; ++x) 01535 { 01536 if (*fPtr2 != transparent) 01537 *bPtr2 = T_or_RGB(*bPtr2 * op2 + *fPtr2 * opacity); 01538 ++bPtr2; ++fPtr2; 01539 } 01540 bPtr1 += bw; fPtr1 += fw; 01541 } 01542 } 01543 01544 // ###################################################################### 01545 template <class T> 01546 void inplacePasteGabor(Image<T>& dst, 01547 const Image<T>& gabor, 01548 const Point2D<int>& pos, const T background) 01549 { 01550 int w = dst.getWidth(), h = dst.getHeight(); 01551 int iw = gabor.getWidth(), ih = gabor.getHeight(); 01552 ASSERT(pos.i + iw <= w && pos.j + ih <= h); 01553 T dist, idist; 01554 01555 typename Image<T>::const_iterator sptr = gabor.begin(); 01556 typename Image<T>::iterator dptr = dst.beginw() + pos.i + pos.j * w; 01557 for (int j = 0; j < ih; j ++) 01558 { 01559 for (int i = 0; i < iw; i ++) 01560 { 01561 dist = T(*dptr - background); 01562 if (dist < 0.0F) dist = T(dist * -1); 01563 if (*sptr < 0.0F) idist = T(*sptr * -1); 01564 else idist = *sptr; 01565 //take the value furthest from grey (background) 01566 if (dist < idist) 01567 { 01568 *dptr = T(*sptr+background); 01569 } 01570 dptr++; 01571 sptr++; 01572 } 01573 dptr += w - iw; 01574 } 01575 } 01576 01577 // ###################################################################### 01578 template <class T> 01579 int flood(const Image<T>& src, 01580 Image<T>& dest, const Point2D<int>& seed, 01581 const T thresh, const T val) 01582 { 01583 ASSERT(src.initialized()); 01584 ASSERT(src.coordsOk(seed)); 01585 01586 // clear destination image if necessary 01587 if (!dest.initialized()) dest.resize(src.getDims(), true); 01588 01589 if (src.getVal(seed) < thresh) return -1; // starting point not in object 01590 01591 const T zero = T(); 01592 01593 const int size = src.getSize(); 01594 const int w = src.getWidth(); 01595 01596 // Allocate space for the recursion stack 01597 std::vector<int> stk; 01598 01599 const int STACKSIZE = 256 * 256 + 20; 01600 stk.reserve(STACKSIZE); 01601 01602 stk.push_back(seed.i + w * seed.j); 01603 01604 Image<T> input = src; // copy input because we will modify it 01605 typename Image<T>::iterator const inptr = input.beginw(); 01606 01607 int nbpix = 0; 01608 01609 while (! stk.empty()) 01610 { 01611 int index = stk.back(); stk.pop_back(); 01612 if (inptr[index] != zero) // we have not already visited this point 01613 { 01614 // we know that the current point IS in our object: 01615 dest.setVal(index, val); // flood output 01616 inptr[index] = zero; // clear this point in input 01617 ++nbpix; // one more flooded pixel 01618 01619 // explore recursively the 8-connected neighbors: 01620 if ((index+1 < size) && (inptr[index+1] >= thresh)) 01621 stk.push_back( index+1 ); 01622 01623 if ((index+w < size) && (inptr[index+w] >= thresh)) 01624 stk.push_back( index+w ); 01625 01626 if ((index+w+1 < size) && (inptr[index+w+1] >= thresh)) 01627 stk.push_back( index+w+1 ); 01628 01629 if ((index+w-1 < size) && (inptr[index+w-1] >= thresh)) 01630 stk.push_back( index+w-1 ); 01631 01632 if ((index-1 >= 0) && (inptr[index-1] >= thresh)) 01633 stk.push_back( index-1 ); 01634 01635 if ((index-w >= 0) && (inptr[index-w] >= thresh)) 01636 stk.push_back( index-w ); 01637 01638 if ((index-w-1 >= 0) && (inptr[index-w-1] >= thresh)) 01639 stk.push_back( index-w-1 ); 01640 01641 if ((index-w+1 >= 0) && (inptr[index-w+1] >= thresh)) 01642 stk.push_back( index-w+1 ); 01643 } 01644 } 01645 return nbpix; 01646 } 01647 01648 // ###################################################################### 01649 template <class T> 01650 int countParticles(const Image<T>& src, const T thresh) 01651 { 01652 Point2D<int> p; 01653 int nbpart = 0; 01654 Image<T> tmp = src; 01655 T fill = thresh; 01656 const T zero = T(); 01657 if (fill >= zero + (T)1.0) fill -= (T)1.0; // fill with val below thresh 01658 else LFATAL("Cannot count particles with such low threshold"); 01659 01660 for (p.i = 0; p.i < src.getWidth(); ++p.i) 01661 for (p.j = 0; p.j < src.getHeight(); ++p.j) 01662 if (tmp.getVal(p) >= thresh) // found one particle 01663 { 01664 ++nbpart; 01665 Image<T> tmp2 = tmp; 01666 flood(tmp2, tmp, p, thresh, fill); // discard it 01667 } 01668 return nbpart; 01669 } 01670 01671 // ###################################################################### 01672 template <class T> 01673 void inplaceAddBGnoise(Image<T>& src, const float range) 01674 { 01675 // background noise level: as coeff of map full dynamic range: 01676 #define BGNOISELEVEL 0.00001 01677 01678 inplaceAddBGnoise2(src, range * BGNOISELEVEL); 01679 } 01680 01681 // ###################################################################### 01682 template <class T> 01683 void inplaceAddBGnoise2(Image<T>& src, const float range) 01684 { 01685 ASSERT(src.initialized()); 01686 const int w = src.getWidth(), h = src.getHeight(); 01687 01688 // do not put noise very close to image borders: 01689 int siz = std::min(w, h) / 10; 01690 01691 typename Image<T>::iterator sptr = src.beginw() + siz + siz * w; 01692 01693 for (int j = siz; j < h - siz; ++j) 01694 { 01695 for (int i = siz; i < w - siz; ++i) 01696 *sptr++ += clamped_convert<T>(range * randomDouble()); 01697 sptr += siz + siz; 01698 } 01699 } 01700 01701 // Include the explicit instantiations 01702 #include "inst/Image/Transforms.I" 01703 01704 // ###################################################################### 01705 /* So things look consistent in everyone's emacs... */ 01706 /* Local Variables: */ 01707 /* indent-tabs-mode: nil */ 01708 /* End: */ 01709 01710 #endif // !IMAGE_TRANSFORMS_C_DEFINED