IntegerInput.C

Go to the documentation of this file.
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
Generated on Sun May 8 08:40:21 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3