00001 /*!@file Channels/IntegerInput.C */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/IntegerInput.C $ 00035 // $Id: IntegerInput.C 8234 2007-04-03 22:51:59Z rjpeters $ 00036 // 00037 00038 #ifndef CHANNELS_INTEGERINPUT_C_DEFINED 00039 #define CHANNELS_INTEGERINPUT_C_DEFINED 00040 00041 #include "Channels/IntegerInput.H" 00042 00043 #include "Image/IntegerMathOps.H" 00044 #include "Raster/GenericFrame.H" 00045 #include "Util/MathFunctions.H" 00046 #include "Video/VideoFrame.H" 00047 00048 #include <pthread.h> 00049 00050 namespace 00051 { 00052 // ###################################################################### 00053 void checkBufferLength(const size_t actual, const size_t expected) 00054 { 00055 if (actual < expected) 00056 LFATAL("input buffer is too short (got %"ZU", expected %"ZU")", 00057 actual, expected); 00058 00059 if (actual > expected) 00060 LINFO("input buffer is longer than expected (got %"ZU", expected %"ZU")\n" 00061 "(this is not a fatal error, but make sure the width and height are correct)", 00062 actual, expected); 00063 } 00064 00065 // Use this many extra bits in our fixed-point approximation to 00066 // floating-point arithmetic: 00067 const uint BITS_OUT = 16; 00068 00069 // rgb lookup tables 00070 00071 int SCALED_Y_tab[256]; 00072 int SCALED_UV_tab[256]; 00073 int RGB_Y_tab[256]; 00074 int B_U_tab[256]; 00075 int G_U_tab[256]; 00076 int G_V_tab[256]; 00077 int R_V_tab[256]; 00078 00079 pthread_once_t colorspace_init_once = PTHREAD_ONCE_INIT; 00080 00081 void colorspace_init() 00082 { 00083 // so that we get proper rounding in fixed-point integer arithmetic: 00084 const int half = 00085 BITS_OUT > 0 00086 ? 1<<(BITS_OUT-1) 00087 : 0; 00088 00089 const int scale = 1<<BITS_OUT; 00090 00091 for (int i = 0; i < 256; i++) 00092 { 00093 const int y = i-VIDEOYUV_Y_OFFSET; 00094 const int uv = i-VIDEOYUV_UV_OFFSET; 00095 00096 SCALED_Y_tab[i] = half+int(0.5 + scale * ((256.0 * y) / VIDEOYUV_Y_RANGE)); 00097 SCALED_UV_tab[i] = 00098 uv >= 0 00099 ? half+int(0.5 + scale * ((256.0 * uv) / VIDEOYUV_UV_RANGE)) 00100 : -half-int(0.5 + scale * ((256.0 * (-uv)) / VIDEOYUV_UV_RANGE)); 00101 RGB_Y_tab[i] = half+int(0.5 + y * VIDEOYUV_RGB_Y * scale); 00102 R_V_tab[i] = int(0.5 + uv * VIDEOYUV_R_V * scale); 00103 // FIXME should probably have -0.5 instead of +0.5 here and 00104 // flip the sign of G_U_tab and G_V_tab: 00105 G_U_tab[i] = int(0.5 - uv * VIDEOYUV_G_U * scale); 00106 G_V_tab[i] = int(0.5 - uv * VIDEOYUV_G_V * scale); 00107 B_U_tab[i] = int(0.5 + uv * VIDEOYUV_B_U * scale); 00108 } 00109 } 00110 00111 int clampInt(int val, int lo, int hi) 00112 { 00113 if (val < lo) return lo; 00114 else if (val > hi) return hi; 00115 return val; 00116 } 00117 } 00118 00119 static void yuv420p_decode(int* rgb_dst, 00120 int* bw_dst, 00121 int* rg_dst, 00122 int* by_dst, 00123 const byte* y_src, 00124 const byte* u_src, 00125 const byte* v_src, 00126 const int uv_stride, 00127 const int width, 00128 const int height, 00129 const int lumthresh, 00130 const uint inputbits) 00131 { 00132 if (width <= 0) 00133 LFATAL("width must be positive"); 00134 00135 if (height <= 0) 00136 LFATAL("height must be positive"); 00137 00138 if (width & 1) 00139 LFATAL("width must be even"); 00140 00141 if (height & 1) 00142 LFATAL("height must be even"); 00143 00144 pthread_once(&colorspace_init_once, &colorspace_init); 00145 00146 const int rgb_dst_dif = 3 * width; 00147 const int bw_dst_dif = width; 00148 const int y_dif = width; 00149 00150 int* rgb_dst2 = rgb_dst + 3 * width; 00151 int* bw_dst2 = bw_dst + width; 00152 int* rg_dst2 = rg_dst + width; 00153 int* by_dst2 = by_dst + width; 00154 const byte* y_src2 = y_src + width; 00155 00156 const uint srcbits = BITS_OUT + 8; 00157 00158 if (inputbits > srcbits) 00159 LFATAL("Oops! Can't compute with inputbits (%u) > %u", 00160 inputbits, srcbits); 00161 00162 const int rshiftbits = srcbits - inputbits; 00163 00164 const uint lumbits = (inputbits * 2) / 3; 00165 const uint restorebits = (inputbits - lumbits - 1); 00166 00167 const int shiftedlumthresh = lumthresh << rshiftbits; 00168 00169 for (int y = height / 2; y; y--) { 00170 for (int x = 0; x < width / 2; x++) { 00171 const int u = u_src[x]; 00172 const int v = v_src[x]; 00173 00174 const int r_v = R_V_tab[v]; 00175 const int g_uv = - G_U_tab[u] - G_V_tab[v]; 00176 const int b_u = B_U_tab[u]; 00177 00178 const int scaled_u = SCALED_UV_tab[u] >> 1; 00179 const int scaled_v = SCALED_UV_tab[v] >> 1; 00180 00181 #define DO_LUM(rgptr, byptr) \ 00182 do { \ 00183 if (lum < shiftedlumthresh) \ 00184 { \ 00185 *rgptr++ = 0; \ 00186 *byptr++ = 0; \ 00187 } \ 00188 else \ 00189 { \ 00190 /* compute differences and normalize chroma by luminance: */ \ 00191 *rgptr++ = (scaled_v / (lum >> lumbits)) << restorebits; \ 00192 *byptr++ = (scaled_u / (lum >> lumbits)) << restorebits; \ 00193 } \ 00194 } while (0) 00195 00196 { 00197 const int rgb_y = RGB_Y_tab[*y_src]; 00198 const int r = (rgb_y + r_v); 00199 const int g = (rgb_y + g_uv); 00200 const int b = (rgb_y + b_u); 00201 const int lum = SCALED_Y_tab[*y_src]; 00202 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00203 rgb_dst[0] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1); 00204 rgb_dst[1] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1); 00205 rgb_dst[2] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1); 00206 00207 DO_LUM(rg_dst, by_dst); 00208 00209 y_src++; 00210 } 00211 00212 { 00213 const int rgb_y = RGB_Y_tab[*y_src]; 00214 const int r = (rgb_y + r_v); 00215 const int g = (rgb_y + g_uv); 00216 const int b = (rgb_y + b_u); 00217 const int lum = SCALED_Y_tab[*y_src]; 00218 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00219 rgb_dst[3] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1); 00220 rgb_dst[4] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1); 00221 rgb_dst[5] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1); 00222 00223 DO_LUM(rg_dst, by_dst); 00224 00225 y_src++; 00226 } 00227 00228 { 00229 const int rgb_y = RGB_Y_tab[*y_src2]; 00230 const int r = (rgb_y + r_v); 00231 const int g = (rgb_y + g_uv); 00232 const int b = (rgb_y + b_u); 00233 const int lum = SCALED_Y_tab[*y_src2]; 00234 *bw_dst2++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00235 rgb_dst2[0] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1); 00236 rgb_dst2[1] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1); 00237 rgb_dst2[2] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1); 00238 00239 DO_LUM(rg_dst2, by_dst2); 00240 00241 y_src2++; 00242 } 00243 00244 { 00245 const int rgb_y = RGB_Y_tab[*y_src2]; 00246 const int r = (rgb_y + r_v); 00247 const int g = (rgb_y + g_uv); 00248 const int b = (rgb_y + b_u); 00249 const int lum = SCALED_Y_tab[*y_src2]; 00250 *bw_dst2++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00251 rgb_dst2[3] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1); 00252 rgb_dst2[4] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1); 00253 rgb_dst2[5] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1); 00254 00255 DO_LUM(rg_dst2, by_dst2); 00256 00257 y_src2++; 00258 } 00259 00260 rgb_dst += 6; 00261 rgb_dst2 += 6; 00262 } 00263 00264 rgb_dst += rgb_dst_dif; 00265 rgb_dst2 += rgb_dst_dif; 00266 bw_dst += bw_dst_dif; 00267 bw_dst2 += bw_dst_dif; 00268 rg_dst += bw_dst_dif; 00269 rg_dst2 += bw_dst_dif; 00270 by_dst += bw_dst_dif; 00271 by_dst2 += bw_dst_dif; 00272 00273 y_src += y_dif; 00274 y_src2 += y_dif; 00275 00276 u_src += uv_stride; 00277 v_src += uv_stride; 00278 } 00279 } 00280 00281 static void yuv422p_decode(int* rgb_dst, 00282 int* bw_dst, 00283 int* rg_dst, 00284 int* by_dst, 00285 const byte* y_src, 00286 const byte* u_src, 00287 const byte* v_src, 00288 const int width, 00289 const int height, 00290 const int lumthresh, 00291 const uint inputbits) 00292 { 00293 if (width <= 0) 00294 LFATAL("width must be positive"); 00295 00296 if (height <= 0) 00297 LFATAL("height must be positive"); 00298 00299 if (width & 1) 00300 LFATAL("width must be even"); 00301 00302 if (height & 1) 00303 LFATAL("height must be even"); 00304 00305 pthread_once(&colorspace_init_once, &colorspace_init); 00306 00307 const uint srcbits = BITS_OUT + 8; 00308 00309 if (inputbits > srcbits) 00310 LFATAL("Oops! Can't compute with inputbits (%u) > %u", 00311 inputbits, srcbits); 00312 00313 const int rshiftbits = srcbits - inputbits; 00314 00315 const uint lumbits = (inputbits * 2) / 3; 00316 const uint restorebits = (inputbits - lumbits - 1); 00317 00318 const int shiftedlumthresh = lumthresh << rshiftbits; 00319 00320 00321 for (int j = 0; j < height; ++j) 00322 for (int i = 0; i < width; i += 2) 00323 { 00324 // we have 2 luminance pixels per chroma pair 00325 00326 const int r_v = R_V_tab[*v_src]; 00327 const int g_uv = - G_U_tab[*u_src] - G_V_tab[*v_src]; 00328 const int b_u = B_U_tab[*u_src]; 00329 00330 const int scaled_u = SCALED_UV_tab[*u_src] >> 1; 00331 const int scaled_v = SCALED_UV_tab[*v_src] >> 1; 00332 00333 ++u_src; 00334 ++v_src; 00335 00336 // first luminance pixel: 00337 00338 { 00339 const int lum = SCALED_Y_tab[*y_src]; 00340 const int rgb_y1 = RGB_Y_tab[*y_src++]; 00341 00342 const int r1 = (rgb_y1 + r_v); 00343 const int g1 = (rgb_y1 + g_uv); 00344 const int b1 = (rgb_y1 + b_u); 00345 00346 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00347 *rgb_dst++ = clampInt(r1 >> rshiftbits, 0, (1<<inputbits)-1); 00348 *rgb_dst++ = clampInt(g1 >> rshiftbits, 0, (1<<inputbits)-1); 00349 *rgb_dst++ = clampInt(b1 >> rshiftbits, 0, (1<<inputbits)-1); 00350 00351 DO_LUM(rg_dst, by_dst); 00352 } 00353 00354 // second luminance pixel: 00355 00356 { 00357 const int lum = SCALED_Y_tab[*y_src]; 00358 const int rgb_y2 = RGB_Y_tab[*y_src++]; 00359 00360 const int r2 = (rgb_y2 + r_v); 00361 const int g2 = (rgb_y2 + g_uv); 00362 const int b2 = (rgb_y2 + b_u); 00363 00364 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1); 00365 *rgb_dst++ = clampInt(r2 >> rshiftbits, 0, (1<<inputbits)-1); 00366 *rgb_dst++ = clampInt(g2 >> rshiftbits, 0, (1<<inputbits)-1); 00367 *rgb_dst++ = clampInt(b2 >> rshiftbits, 0, (1<<inputbits)-1); 00368 00369 DO_LUM(rg_dst, by_dst); 00370 } 00371 } 00372 } 00373 00374 // ###################################################################### 00375 IntegerInput::IntegerInput() 00376 : 00377 itsDims(), 00378 itsRgbByte(), 00379 itsInputBits(0), 00380 itsGrayInt(), 00381 itsRgInt(), 00382 itsByInt() 00383 {} 00384 00385 // ###################################################################### 00386 IntegerInput::IntegerInput(const Dims& dims) 00387 : 00388 itsDims(dims), 00389 itsRgbByte(), 00390 itsInputBits(0), 00391 itsGrayInt(dims, NO_INIT), 00392 itsRgInt(dims, NO_INIT), 00393 itsByInt(dims, NO_INIT) 00394 {} 00395 00396 // ###################################################################### 00397 IntegerInput IntegerInput::fromRgb(const Image<PixRGB<byte> >& f, 00398 const uint inputbits) 00399 { 00400 IntegerInput result; 00401 result.itsDims = f.getDims(); 00402 result.itsRgbByte = f; 00403 result.itsInputBits = inputbits; 00404 result.itsGrayInt = intgScaleLuminanceFromByte(&f, inputbits); 00405 00406 // don't compute itsRgInt and itsByInt here; instead, compute them 00407 // lazily if/when the user calls rgInt() or byInt() 00408 00409 return result; 00410 } 00411 00412 // ###################################################################### 00413 IntegerInput IntegerInput::fromVideo(const VideoFrame& vf, 00414 const uint inputbits) 00415 { 00416 const byte* data = vf.getBuffer(); 00417 const size_t buflen = vf.getBufSize(); 00418 const Dims dims = vf.getDims(); 00419 00420 const int w = dims.w(); 00421 const int h = dims.h(); 00422 00423 IntegerInput result(dims); 00424 00425 result.itsInputBits = inputbits; 00426 00427 Image<PixRGB<int> > irgb(dims, NO_INIT); 00428 00429 const float lumthreshf = 25.5f; 00430 const int lumthresh = intgScaleFromFloat(&lumthreshf, inputbits); 00431 00432 switch (vf.getMode()) 00433 { 00434 case VIDFMT_YUV420P: 00435 { 00436 // we have to do (w+1)/2 instead of just w/2, because if e.g. the y 00437 // array has 5 pixels, then we want the u and v arrays to have 3 00438 // pixels, not 2: 00439 const int w2 = (w+1)/2; 00440 const int h2 = (h+1)/2; 00441 00442 checkBufferLength(buflen, dims.sz() + 2*w2*h2); 00443 00444 yuv420p_decode(reinterpret_cast<int*>(irgb.getArrayPtr()), 00445 result.itsGrayInt.getArrayPtr(), 00446 result.itsRgInt.getArrayPtr(), 00447 result.itsByInt.getArrayPtr(), 00448 data, 00449 data + w*h, 00450 data + w*h + w2*h2, 00451 w2 /* uv_stride */, 00452 w /* image width */, 00453 h /* image height */, 00454 lumthresh, 00455 inputbits); 00456 00457 return result; 00458 } 00459 break; 00460 00461 case VIDFMT_YUV422P: 00462 { 00463 checkBufferLength(buflen, dims.sz() + 2*(w/2)*h); 00464 00465 yuv422p_decode(reinterpret_cast<int*>(irgb.getArrayPtr()), 00466 result.itsGrayInt.getArrayPtr(), 00467 result.itsRgInt.getArrayPtr(), 00468 result.itsByInt.getArrayPtr(), 00469 data, 00470 data + w*h, 00471 data + w*h + (w/2) * h, 00472 w /* image width */, 00473 h /* image height */, 00474 lumthresh, 00475 inputbits); 00476 00477 return result; 00478 } 00479 break; 00480 00481 default: 00482 LFATAL("Oops! I don't have any specialized decoder for " 00483 "VideoFormat %s; just try using fromRgb() instead", 00484 convertToString(vf.getMode()).c_str()); 00485 break; 00486 } 00487 00488 return result; 00489 } 00490 00491 // ###################################################################### 00492 IntegerInput IntegerInput::decode(const GenericFrame& f, 00493 const IntegerDecodeType t, 00494 const unsigned int bits) 00495 { 00496 switch (t) 00497 { 00498 case INTG_DECODE_RGB: return IntegerInput::fromRgb(f.asRgb(), bits); 00499 case INTG_DECODE_VIDEO: return IntegerInput::fromVideo(f.asVideo(), bits); 00500 } 00501 00502 LFATAL("Invalid IntegerDecodeType value %d", int(t)); 00503 /* can't happen */ return IntegerInput(); 00504 } 00505 00506 // ###################################################################### 00507 IntegerInput IntegerInput::fromGrayOnly(const Image<int>& bw) 00508 { 00509 IntegerInput result; 00510 result.itsDims = bw.getDims(); 00511 result.itsGrayInt = bw; 00512 return result; 00513 } 00514 00515 // ###################################################################### 00516 const Image<int>& IntegerInput::rgInt() const 00517 { 00518 if (!itsRgInt.initialized() && itsRgbByte.initialized()) 00519 { 00520 ASSERT(itsInputBits > 0); 00521 const int threshfactor = 10; 00522 intgGetRGBY(itsRgbByte, itsRgInt, itsByInt, 00523 threshfactor, itsInputBits); 00524 } 00525 00526 return itsRgInt; 00527 } 00528 00529 // ###################################################################### 00530 const Image<int>& IntegerInput::byInt() const 00531 { 00532 if (!itsByInt.initialized() && itsRgbByte.initialized()) 00533 { 00534 ASSERT(itsInputBits > 0); 00535 const int threshfactor = 10; 00536 intgGetRGBY(itsRgbByte, itsRgInt, itsByInt, 00537 threshfactor, itsInputBits); 00538 } 00539 00540 return itsByInt; 00541 } 00542 00543 // ###################################################################### 00544 /* So things look consistent in everyone's emacs... */ 00545 /* Local Variables: */ 00546 /* mode: c++ */ 00547 /* indent-tabs-mode: nil */ 00548 /* End: */ 00549 00550 #endif // CHANNELS_INTEGERINPUT_C_DEFINED