cvd/vision.h

00001 /*
00002         This file is part of the CVD Library.
00003 
00004         Copyright (C) 2005 The Authors
00005 
00006         This library is free software; you can redistribute it and/or
00007         modify it under the terms of the GNU Lesser General Public
00008         License as published by the Free Software Foundation; either
00009         version 2.1 of the License, or (at your option) any later version.
00010 
00011         This library is distributed in the hope that it will be useful,
00012         but WITHOUT ANY WARRANTY; without even the implied warranty of
00013         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014         Lesser General Public License for more details.
00015 
00016         You should have received a copy of the GNU Lesser General Public
00017         License along with this library; if not, write to the Free Software
00018         Foundation, Inc.,
00019     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 */
00021 
00022 #ifndef CVD_VISION_H_
00023 #define CVD_VISION_H_
00024 
00025 #include <vector>
00026 #include <memory>
00027 
00028 #include <cvd/exceptions.h>
00029 #include <cvd/image.h>
00030 #include <cvd/internal/pixel_operations.h>
00031 #include <cvd/utility.h>
00032 
00033 
00034 #if defined(CVD_HAVE_TOON)
00035 #include <TooN/TooN.h>
00036 #include <TooN/helpers.h>
00037 #endif
00038 
00039 namespace CVD {
00040 
00041 namespace Exceptions {
00042 
00045     namespace Vision {
00048         struct All: public CVD::Exceptions::All {};
00049 
00052         struct IncompatibleImageSizes : public All {
00053             IncompatibleImageSizes(const std::string & function)
00054             {
00055                 what = "Incompatible image sizes in " + function;
00056             };
00057         };
00058 
00061         struct ImageRefNotInImage : public All {
00062             ImageRefNotInImage(const std::string & function)
00063             {
00064                 what = "Input ImageRefs not in image in " + function;
00065             };
00066         };
00067     };
00068 };
00069 
00075 template <class T>
00076 void halfSample(const BasicImage<T>& in, BasicImage<T>& out)
00077 {
00078     typedef typename Pixel::traits<T>::wider_type sum_type;
00079     if( (in.size()/2) != out.size())
00080         throw Exceptions::Vision::IncompatibleImageSizes("halfSample");
00081     const T* top = in.data();
00082     const T* bottom = top + in.size().x;
00083     const T* end = top + in.totalsize();
00084     int ow = out.size().x;
00085     int skip = in.size().x + (in.size().x % 2);
00086     T* p = out.data();
00087     while (bottom < end) {      
00088       for (int j=0; j<ow; j++) {
00089     *p = static_cast<T>((sum_type(top[0]) + top[1] + bottom[0] + bottom[1])/4);
00090     p++;
00091     top += 2;
00092     bottom += 2;
00093       }
00094       top += skip;
00095       bottom += skip;
00096     }
00097 }
00098 
00099 void halfSample(const BasicImage<byte>& in, BasicImage<byte>& out);
00100 
00106 template <class T>
00107 inline Image<T> halfSample(const BasicImage<T>& in)
00108 {
00109     Image<T> out(in.size()/2);
00110     halfSample(in, out);
00111     return out;
00112 }
00113 
00122 template <class T>
00123 inline Image<T> halfSample( Image<T> in, unsigned int octaves){
00124     for( ;octaves > 0; --octaves){
00125         in = halfSample(in);
00126     }
00127     return in;
00128 }
00129 
00135 template <class T>
00136 void threshold(BasicImage<T>& im, const T& minimum, const T& hi)
00137 {
00138   typename BasicImage<T>::iterator it = im.begin();
00139   typename BasicImage<T>::iterator end = im.end();
00140   while (it != end) {
00141     if (*it < minimum)
00142       *it = T();
00143     else
00144       *it = hi;
00145     ++it;
00146   }
00147 }
00148 
00155 template <class T>
00156 void stats(const BasicImage<T>& im, T& mean, T& stddev)
00157 {
00158     const int c = Pixel::Component<T>::count;
00159     double v;
00160     double sum[c] = {0};
00161     double sumSq[c] = {0};
00162     const T* p = im.data();
00163     const T* end = im.data()+im.totalsize();
00164     while (p != end) {
00165         for (int k=0; k<c; k++) {
00166             v = Pixel::Component<T>::get(*p, k);
00167             sum[k] += v;
00168             sumSq[k] += v*v;
00169         }
00170         ++p;
00171     }
00172     for (int k=0; k<c; k++) {
00173         double m = sum[k]/im.totalsize();
00174         Pixel::Component<T>::get(mean,k) = (typename Pixel::Component<T>::type)m;
00175         sumSq[k] /= im.totalsize();
00176         Pixel::Component<T>::get(stddev,k) = (typename Pixel::Component<T>::type)sqrt(sumSq[k] - m*m);
00177     }
00178 }
00179 
00182 template <class T>
00183 struct multiplyBy
00184 {
00185   const T& factor;
00186   multiplyBy(const T& f) : factor(f) {};
00187   template <class S> inline S operator()(const S& s) const {
00188     return s * factor;
00189   };
00190 };
00191 
00192 template <class S, class T, int Sn=Pixel::Component<S>::count, int Tn=Pixel::Component<T>::count> struct Gradient;
00193 template <class S, class T> struct Gradient<S,T,1,2> {
00194   typedef typename Pixel::Component<S>::type SComp;
00195   typedef typename Pixel::Component<T>::type TComp;
00196   typedef typename Pixel::traits<SComp>::wider_type diff_type;
00197   static void gradient(const BasicImage<S>& I, BasicImage<T>& grad) {
00198     int w = I.size().x;
00199     typename BasicImage<S>::const_iterator s = I.begin() + w + 1;
00200     typename BasicImage<S>::const_iterator end = I.end() - w - 1;
00201     typename BasicImage<T>::iterator t = grad.begin() + w + 1;
00202     while (s != end) {
00203       Pixel::Component<T>::get(*t, 0) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+1)) - *(s-1));
00204       Pixel::Component<T>::get(*t, 1) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+w)) - *(s-w));
00205       s++;
00206       t++;
00207     }
00208     zeroBorders(grad);
00209   }
00210 };
00211 
00218 template <class S, class T> void gradient(const BasicImage<S>& im, BasicImage<T>& out)
00219 {
00220   if( im.size() != out.size())
00221     throw Exceptions::Vision::IncompatibleImageSizes("gradient");
00222   Gradient<S,T>::gradient(im,out);
00223 }
00224 
00225 
00226 #ifndef DOXYGEN_IGNORE_INTERNAL
00227 inline void gradient(const BasicImage<byte>& im, BasicImage<short[2]>& out);
00228 #endif
00229 
00230 
00231 template <class T, class S> inline void sample(const BasicImage<S>& im, double x, double y, T& result)
00232 {
00233   typedef typename Pixel::Component<S>::type SComp;
00234   typedef typename Pixel::Component<T>::type TComp;
00235   int lx = (int)x;
00236   int ly = (int)y;
00237   x -= lx;
00238   y -= ly;
00239   for(unsigned int i = 0; i < Pixel::Component<T>::count; i++){
00240     Pixel::Component<T>::get(result,i) = Pixel::scalar_convert<TComp,SComp>(
00241         (1-y)*((1-x)*Pixel::Component<S>::get(im[ly][lx],i) + x*Pixel::Component<S>::get(im[ly][lx+1], i)) +
00242           y * ((1-x)*Pixel::Component<S>::get(im[ly+1][lx],i) + x*Pixel::Component<S>::get(im[ly+1][lx+1],i)));
00243   }
00244  }
00245 
00246 template <class T, class S> inline T sample(const BasicImage<S>& im, double x, double y){
00247     T result;
00248     sample( im, x, y, result);
00249     return result;
00250 }
00251 
00252 inline void sample(const BasicImage<float>& im, double x, double y, float& result)
00253   {
00254     int lx = (int)x;
00255     int ly = (int)y;
00256     int w = im.size().x;
00257     const float* base = im[ly]+lx;
00258     float a = base[0];
00259     float b = base[1];
00260     float c = base[w];
00261     float d = base[w+1];
00262     float e = a-b;
00263     x-=lx;
00264     y-=ly;
00265     result = (float)(x*(y*(e-c+d)-e)+y*(c-a)+a);
00266   }
00267 
00268 #if defined (CVD_HAVE_TOON)
00269 
00280 template <class T, class S>
00281 int transform(const BasicImage<S>& in, BasicImage<T>& out, const TooN::Matrix<2>& M, const TooN::Vector<2>& inOrig, const TooN::Vector<2>& outOrig, const T defaultValue = T())
00282 {
00283     const int w = out.size().x, h = out.size().y, iw = in.size().x, ih = in.size().y; 
00284     const TooN::Vector<2> across = M.T()[0];
00285     const TooN::Vector<2> down =   M.T()[1];
00286    
00287     const TooN::Vector<2> p0 = inOrig - M*outOrig;
00288     const TooN::Vector<2> p1 = p0 + w*across;
00289     const TooN::Vector<2> p2 = p0 + h*down;
00290     const TooN::Vector<2> p3 = p0 + w*across + h*down;
00291         
00292     // ul --> p0
00293     // ur --> w*across + p0
00294     // ll --> h*down + p0
00295     // lr --> w*across + h*down + p0
00296     double min_x = p0[0], min_y = p0[1];
00297     double max_x = min_x, max_y = min_y;
00298    
00299     // Minimal comparisons needed to determine bounds
00300     if (across[0] < 0)
00301     min_x += w*across[0];
00302     else
00303     max_x += w*across[0];
00304     if (down[0] < 0)
00305     min_x += h*down[0];
00306     else
00307     max_x += h*down[0];
00308     if (across[1] < 0)
00309     min_y += w*across[1];
00310     else
00311     max_y += w*across[1];
00312     if (down[1] < 0)
00313     min_y += h*down[1];
00314     else
00315     max_y += h*down[1];
00316    
00317     // This gets from the end of one row to the beginning of the next
00318     const TooN::Vector<2> carriage_return = down - w*across;
00319 
00320     //If the patch being extracted is completely in the image then no 
00321     //check is needed with each point.
00322     if (min_x >= 0 && min_y >= 0 && max_x < iw-1 && max_y < ih-1) 
00323     {
00324     TooN::Vector<2> p = p0;
00325     for (int i=0; i<h; ++i, p+=carriage_return)
00326         for (int j=0; j<w; ++j, p+=across) 
00327         sample(in,p[0],p[1],out[i][j]);
00328     return 0;
00329     } 
00330     else // Check each source location
00331     {
00332     // Store as doubles to avoid conversion cost for comparison
00333     const double x_bound = iw-1;
00334     const double y_bound = ih-1;
00335     int count = 0;
00336     TooN::Vector<2> p = p0;
00337     for (int i=0; i<h; ++i, p+=carriage_return) {
00338         for (int j=0; j<w; ++j, p+=across) {
00339         //Make sure that we are extracting pixels in the image
00340         if (0 <= p[0] && 0 <= p[1] &&  p[0] < x_bound && p[1] < y_bound)
00341             sample(in,p[0],p[1],out[i][j]);
00342         else {
00343             out[i][j] = defaultValue;
00344             ++count;
00345         }
00346         }
00347     }
00348     return count;
00349     }
00350 }
00351 
00352   template <class T>  void transform(const BasicImage<T>& in, BasicImage<T>& out, const TooN::Matrix<3>& Minv /* <-- takes points in "out" to points in "in" */)
00353   {
00354     TooN::Vector<3> base = Minv.T()[2];
00355     TooN::Vector<2> offset;
00356     offset[0] = in.size().x/2;
00357     offset[1] = in.size().y/2;
00358     offset -= TooN::project(base);
00359     TooN::Vector<3> across = Minv.T()[0];
00360     TooN::Vector<3> down = Minv.T()[1];
00361     double w = in.size().x-1;
00362     double h = in.size().y-1;
00363     int ow = out.size().x;
00364     int oh = out.size().y;
00365     base -= down*(oh/2) + across*(ow/2);
00366     for (int row = 0; row < oh; row++, base+=down) {
00367       TooN::Vector<3> x = base;
00368       for (int col = 0; col < ow; col++, x += across) {
00369     TooN::Vector<2> p = project(x) + offset;
00370     if (p[0] >= 0 && p[0] <= w-1 && p[1] >=0 && p[1] <= h-1)
00371       sample(in,p[0],p[1], out[row][col]);
00372     else
00373       zeroPixel(out[row][col]);
00374       }
00375     }
00376   }
00377 #endif
00378 
00380 template <class T> void flipVertical( Image<T> & in )
00381 {
00382   int w = in.size().x;
00383   std::auto_ptr<T> buffer_auto(new T[w]);
00384   T* buffer = buffer_auto.get();
00385   T * top = in.data();
00386   T * bottom = top + (in.size().y - 1)*w;
00387   while( top < bottom )
00388   {
00389     std::copy(top, top+w, buffer);
00390     std::copy(bottom, bottom+w, top);
00391     std::copy(buffer, buffer+w, bottom);
00392     top += w;
00393     bottom -= w;
00394   }
00395 }
00396 
00397 
00398 namespace median {
00399     template <class T> inline T median3(T a, T b, T c) {
00400     if (b<a)
00401         return std::max(b,std::min(a,c));
00402     else
00403         return std::max(a,std::min(b,c));   
00404     }
00405     
00406     template <class T> inline void sort3(T& a, T& b, T& c) {
00407     using std::swap;
00408     if (b<a) swap(a,b);
00409     if (c<b) swap(b,c);
00410     if (b<a) swap(a,b);
00411     }
00412     
00413     template <class T> T median_3x3(const T* p, const int w) {
00414     T a = p[-w-1], b = p[-w], c = p[-w+1], d=p[-1], e=p[0], f=p[1], g=p[w-1], h=p[w], i=p[w+1];
00415     sort3(a,b,c);
00416     sort3(d,e,f);
00417     sort3(g,h,i);
00418     e = median3(b,e,h);
00419     g = std::max(std::max(a,d),g);
00420     c = std::min(c,std::min(f,i));
00421     return median3(c,e,g);
00422     }
00423     
00424     template <class T> void median_filter_3x3(const T* p, const int w, const int n, T* out)
00425     {
00426     T a = p[-w-1], b = p[-w], d=p[-1], e=p[0], g=p[w-1], h=p[w];
00427     sort3(a,d,g);
00428     sort3(b,e,h);
00429     for (int j=0; j<n; ++j, ++p, ++out) {
00430         T c = p[-w+1], f = p[1], i = p[w+1];
00431         sort3(c,f,i);
00432         *out = median3(std::min(std::min(g,h),i), 
00433                median3(d,e,f), 
00434                std::max(std::max(a,b),c));
00435         a=b; b=c; d=e; e=f; g=h; h=i;
00436     }
00437     }
00438 }
00439 
00440     template <class T> void median_filter_3x3(const SubImage<T>& I, SubImage<T> out)
00441     {
00442     assert(out.size() == I.size());
00443     const int s = I.row_stride();
00444     const int n = I.size().x - 2;
00445     for (int i=1; i+1<I.size().y; ++i)
00446         median::median_filter_3x3(I[i]+1, s, n, out[i]+1);
00447     }
00448 
00449 void median_filter_3x3(const SubImage<byte>& I, SubImage<byte> out);
00450 
00451 //template<class T>
00452 
00453 
00454 }; // namespace CVD
00455 
00456 #endif // CVD_VISION_H_

Generated on Wed Feb 18 10:23:02 2009 for CVD by  doxygen 1.5.3