Retinex.C

Go to the documentation of this file.
00001 /*!@file Image/Retinex.C Retinex color-correction algorithm */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/Retinex.C $
00035 // $Id: Retinex.C 9996 2008-07-29 01:08:40Z icore $
00036 //
00037 
00038 #ifndef IMAGE_RETINEX_C_DEFINED
00039 #define IMAGE_RETINEX_C_DEFINED
00040 
00041 #include "Image/Retinex.H"
00042 
00043 #include "Image/Image.H"
00044 #include "Image/ImageSet.H"
00045 #include "Image/MathOps.H"
00046 #include "Image/PyramidOps.H"
00047 #include "Image/Range.H"
00048 #include "Image/ShapeOps.H"
00049 #include "Util/Assert.H"
00050 #include "Util/log.H"
00051 #include "Util/safecopy.H"
00052 
00053 #include <vector>
00054 
00055 // ######################################################################
00056 template <class T> static
00057 Image<T> retinexCompareNeighbors(const Image<T>& src,
00058                                  const Image<T>& radiance,
00059                                  const Point2D<int> shift,
00060                                  const T maxi,
00061                                  const Rectangle& bbox)
00062 {
00063   ASSERT(src.getDims() == radiance.getDims());
00064 
00065   Image<T> result(src.getDims(), NO_INIT);
00066 
00067   const int w = result.getWidth();
00068   const int h = result.getHeight();
00069 
00070   const int offset = shift.i + shift.j * w;
00071 
00072   const int top = bbox.top();
00073   const int bottom = bbox.bottomO();
00074   const int left = bbox.left();
00075   const int right = bbox.rightO();
00076 
00077   // the number of pixels that we need to skip after each row because
00078   // they are outside the range of our output rect:
00079   const int wskip = w - (right - left);
00080 
00081   typename Image<T>::const_iterator sptr = src.begin() + top * w + bbox.left();
00082   typename Image<T>::const_iterator ritr = radiance.begin() + top * w + bbox.left();
00083   typename Image<T>::iterator dptr = result.beginw() + top * w + bbox.left();
00084 
00085   for (int y = top; y < bottom; ++y)
00086     {
00087       for (int x = left; x < right; ++x, ++sptr, ++ritr, ++dptr)
00088         {
00089           T val;
00090 
00091           if (y+shift.j < 0 || y+shift.j >= h
00092               || x+shift.i < 0 || x+shift.i >= w)
00093             {
00094               val = sptr[0];
00095             }
00096           else
00097             {
00098               val = sptr[offset] + ritr[0] - ritr[offset];
00099             }
00100 
00101           if (val > maxi) val = maxi;
00102 
00103           *dptr = T(0.5F * (val + sptr[0]));
00104         }
00105 
00106       sptr += wskip;
00107       ritr += wskip;
00108       dptr += wskip;
00109     }
00110 
00111   return result;
00112 }
00113 
00114 // ######################################################################
00115 size_t retinexDepth(const Dims& dims)
00116 {
00117   size_t power = 0;
00118 
00119   while ((dims.w() >> power) > 1 &&  (dims.h() >> power) > 1)
00120     ++power;
00121 
00122   return power + 1;
00123 }
00124 
00125 // ######################################################################
00126 template <class T>
00127 Image<T> intXYWithPad(const Image<T>& src, const bool padx, const bool pady)
00128 {
00129   ASSERT(src.initialized());   // works only if there is an image
00130 
00131   const int w = src.getWidth();
00132   const int h = src.getHeight();
00133 
00134   const int w2 = w * 2 + int(padx);
00135   const int h2 = h * 2 + int(pady);
00136 
00137   Image<T> result(w2, h2, NO_INIT);
00138 
00139   typename Image<T>::const_iterator sptr = src.begin();
00140   typename Image<T>::iterator dptr = result.beginw();
00141   for (int j = 0; j < h; ++j)
00142     {
00143       for (int i = 0; i < w; ++i)
00144         {
00145           *dptr++ = *sptr;     // copy one point
00146           *dptr++ = *sptr;     // again
00147           if (padx && i == w-1)
00148             *dptr++ = *sptr;   // and one more time if padx and we're at the end of the line
00149           ++sptr;
00150         }
00151       // duplicate line we just wrote:
00152       safecopy(dptr, dptr - w2, w2);
00153       dptr += w2;
00154       if (pady && j == h-1)
00155         {
00156           // duplicate the line one more time if pady and we're at the last line
00157           safecopy(dptr, dptr - w2, w2);
00158           dptr += w2;
00159         }
00160     }
00161 
00162   return result;
00163 }
00164 
00165 // ######################################################################
00166 template <class T>
00167 ImageSet<T> buildPyrRetinexLog(const Image<T>& L,
00168                                const size_t depth, const int niter,
00169                                const Rectangle& outrect)
00170 {
00171   ASSERT(depth >= 1);
00172   ASSERT(niter >= 1);
00173 
00174   const ImageSet<T> radiancePyr = buildPyrLocalAvg2x2(L, depth);
00175 
00176   if (radiancePyr[depth - 1].getSize() > 25)
00177     LFATAL("invalid toplevel image size %dx%d",
00178            radiancePyr[depth - 1].getWidth(),
00179            radiancePyr[depth - 1].getHeight());
00180 
00181   const Range<T> rng = rangeOf(radiancePyr[0]);
00182 
00183   Image<T> OP(radiancePyr[depth - 1].getDims(), NO_INIT);
00184   OP.clear(rng.max());
00185 
00186   ImageSet<T> result(depth);
00187 
00188   std::vector<Rectangle> bboxes(depth);
00189   bboxes[0] = L.getBounds().getOverlap
00190     (Rectangle::tlbrO(outrect.top() - niter - 1,
00191                       outrect.left() - niter - 1,
00192                       outrect.bottomO() + niter + 1,
00193                       outrect.rightO() + niter + 1));
00194   for (size_t i = 1; i < bboxes.size(); ++i)
00195     {
00196       bboxes[i] = radiancePyr[i].getBounds().getOverlap
00197         (Rectangle::tlbrO(bboxes[i-1].top() / 2 - niter - 1,
00198                           bboxes[i-1].left() / 2 - niter - 1,
00199                           bboxes[i-1].bottomO() / 2 + niter,
00200                           bboxes[i-1].rightO() / 2 + niter));
00201     }
00202 
00203   for (size_t i = 0; i < bboxes.size(); ++i)
00204     {
00205       LINFO("pyr[%"ZU"] dims=%dx%d, rect=%dx%d @ (%d,%d)",
00206             i, radiancePyr[i].getWidth(), radiancePyr[i].getHeight(),
00207             bboxes[i].width(), bboxes[i].height(),
00208             bboxes[i].left(), bboxes[i].top());
00209     }
00210 
00211   for (int lev = depth - 1; lev >= 0; --lev)
00212     {
00213       const Image<T> radiance = radiancePyr[lev];
00214 
00215       ASSERT(OP.getDims() == radiance.getDims());
00216 
00217       for (int iter = 0; iter < niter; ++iter)
00218         {
00219           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>( 0, -1), rng.max(), bboxes[lev]);
00220           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>( 1, -1), rng.max(), bboxes[lev]);
00221           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>( 1,  0), rng.max(), bboxes[lev]);
00222           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>( 1,  1), rng.max(), bboxes[lev]);
00223           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>( 0,  1), rng.max(), bboxes[lev]);
00224           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>(-1,  1), rng.max(), bboxes[lev]);
00225           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>(-1,  0), rng.max(), bboxes[lev]);
00226           OP = retinexCompareNeighbors(OP, radiance, Point2D<int>(-1, -1), rng.max(), bboxes[lev]);
00227         }
00228 
00229       result[lev] = OP;
00230 
00231       if (lev > 0)
00232         {
00233           const int padx = radiancePyr[lev-1].getWidth() - OP.getWidth() * 2;
00234           ASSERT(padx == 0 || padx == 1);
00235           const int pady = radiancePyr[lev-1].getHeight() - OP.getHeight() * 2;
00236           ASSERT(pady == 0 || pady == 1);
00237 
00238           LINFO("padx = %d, pady = %d", padx, pady);
00239 
00240           if (!padx && !pady)
00241             OP = intXY(OP, true);
00242           else
00243             OP = intXYWithPad(OP, bool(padx), bool(pady));
00244         }
00245       else
00246         // we are going to be exiting the loop, so we don't need OP
00247         // anymore:
00248         OP = Image<T>();
00249     }
00250 
00251   return result;
00252 }
00253 
00254 template ImageSet<float> buildPyrRetinexLog(const Image<float>&, size_t, int,
00255                                             const Rectangle&);
00256 
00257 // ######################################################################
00258 /* So things look consistent in everyone's emacs... */
00259 /* Local Variables: */
00260 /* mode: c++ */
00261 /* indent-tabs-mode: nil */
00262 /* End: */
00263 
00264 #endif // IMAGE_RETINEX_C_DEFINED
Generated on Sun May 8 08:40:57 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3