CVD 0.8
|
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 #include <algorithm> 00028 00029 #include <cvd/vision_exceptions.h> 00030 #include <cvd/image.h> 00031 #include <cvd/internal/pixel_operations.h> 00032 #include <cvd/utility.h> 00033 00034 00035 #if defined(CVD_HAVE_TOON) 00036 #include <TooN/TooN.h> 00037 #include <TooN/helpers.h> 00038 #endif 00039 00040 namespace CVD{ 00047 template<class C> void twoThirdsSample(const SubImage<C>& in, SubImage<C>& out) 00048 { 00049 typedef typename Pixel::traits<C>::wider_type sum_type; 00050 if( (in.size()/3*2) != out.size()) 00051 throw Exceptions::Vision::IncompatibleImageSizes(__FUNCTION__); 00052 00053 for(int yy=0, y=0; y < in.size().y-2; y+=3, yy+=2) 00054 for(int xx=0, x=0; x < in.size().x-2; x+=3, xx+=2) 00055 { 00056 // a b c 00057 // d e f 00058 // g h i 00059 00060 sum_type b = in[y][x+1]*2; 00061 sum_type d = in[y+1][x]*2; 00062 sum_type f = in[y+1][x+2]*2; 00063 sum_type h = in[y+2][x+1]*2; 00064 sum_type e = in[y+1][x+1]; 00065 00066 out[yy][xx] = static_cast<C>((in[ y][ x]*4+b+d+e)/9); 00067 out[yy][xx+1] = static_cast<C>((in[ y][x+2]*4+b+f+e)/9); 00068 out[yy+1][xx] = static_cast<C>((in[y+2][ x]*4+h+d+e)/9); 00069 out[yy+1][xx+1] = static_cast<C>((in[y+2][x+2]*4+h+f+e)/9); 00070 } 00071 } 00072 00076 void twoThirdsSample(const SubImage<byte>& in, SubImage<byte>& out); 00077 00078 #ifndef DOXYGEN_IGNORE_INTERNAL 00079 namespace Internal 00080 { 00081 template<class C> class twoThirdsSampler{}; 00082 template<class C> struct ImagePromise<twoThirdsSampler<C> > 00083 { 00084 ImagePromise(const SubImage<C>& im) 00085 :i(im) 00086 {} 00087 00088 const SubImage<C>& i; 00089 template<class D> void execute(Image<D>& j) 00090 { 00091 j.resize(i.size()/3*2); 00092 twoThirdsSample(i, j); 00093 } 00094 }; 00095 }; 00096 template<class C> Internal::ImagePromise<Internal::twoThirdsSampler<C> > twoThirdsSample(const SubImage<C>& c) 00097 { 00098 return Internal::ImagePromise<Internal::twoThirdsSampler<C> >(c); 00099 } 00100 #else 00101 00102 00103 00104 00105 00106 00107 00108 template<class C> Image<C> twoThirdsSample(const SubImage<C>& from); 00109 00110 #endif 00111 00117 template <class T> 00118 void halfSample(const BasicImage<T>& in, BasicImage<T>& out) 00119 { 00120 typedef typename Pixel::traits<T>::wider_type sum_type; 00121 if( (in.size()/2) != out.size()) 00122 throw Exceptions::Vision::IncompatibleImageSizes("halfSample"); 00123 const T* top = in.data(); 00124 const T* bottom = top + in.size().x; 00125 const T* end = top + in.totalsize(); 00126 int ow = out.size().x; 00127 int skip = in.size().x + (in.size().x % 2); 00128 T* p = out.data(); 00129 while (bottom < end) { 00130 for (int j=0; j<ow; j++) { 00131 *p = static_cast<T>((sum_type(top[0]) + top[1] + bottom[0] + bottom[1])/4); 00132 p++; 00133 top += 2; 00134 bottom += 2; 00135 } 00136 top += skip; 00137 bottom += skip; 00138 } 00139 } 00140 00141 void halfSample(const BasicImage<byte>& in, BasicImage<byte>& out); 00142 00148 template <class T> 00149 inline Image<T> halfSample(const BasicImage<T>& in) 00150 { 00151 Image<T> out(in.size()/2); 00152 halfSample(in, out); 00153 return out; 00154 } 00155 00164 template <class T> 00165 inline Image<T> halfSample( Image<T> in, unsigned int octaves){ 00166 for( ;octaves > 0; --octaves){ 00167 in = halfSample(in); 00168 } 00169 return in; 00170 } 00171 00177 template <class T> 00178 void threshold(BasicImage<T>& im, const T& minimum, const T& hi) 00179 { 00180 typename BasicImage<T>::iterator it = im.begin(); 00181 typename BasicImage<T>::iterator end = im.end(); 00182 while (it != end) { 00183 if (*it < minimum) 00184 *it = T(); 00185 else 00186 *it = hi; 00187 ++it; 00188 } 00189 } 00190 00197 template <class T> 00198 void stats(const BasicImage<T>& im, T& mean, T& stddev) 00199 { 00200 const int c = Pixel::Component<T>::count; 00201 double v; 00202 double sum[c] = {0}; 00203 double sumSq[c] = {0}; 00204 const T* p = im.data(); 00205 const T* end = im.data()+im.totalsize(); 00206 while (p != end) { 00207 for (int k=0; k<c; k++) { 00208 v = Pixel::Component<T>::get(*p, k); 00209 sum[k] += v; 00210 sumSq[k] += v*v; 00211 } 00212 ++p; 00213 } 00214 for (int k=0; k<c; k++) { 00215 double m = sum[k]/im.totalsize(); 00216 Pixel::Component<T>::get(mean,k) = (typename Pixel::Component<T>::type)m; 00217 sumSq[k] /= im.totalsize(); 00218 Pixel::Component<T>::get(stddev,k) = (typename Pixel::Component<T>::type)sqrt(sumSq[k] - m*m); 00219 } 00220 } 00221 00224 template <class T> 00225 struct multiplyBy 00226 { 00227 const T& factor; 00228 multiplyBy(const T& f) : factor(f) {}; 00229 template <class S> inline S operator()(const S& s) const { 00230 return s * factor; 00231 } 00232 }; 00233 00234 template <class S, class T, int Sn=Pixel::Component<S>::count, int Tn=Pixel::Component<T>::count> struct Gradient; 00235 template <class S, class T> struct Gradient<S,T,1,2> { 00236 typedef typename Pixel::Component<S>::type SComp; 00237 typedef typename Pixel::Component<T>::type TComp; 00238 typedef typename Pixel::traits<SComp>::wider_type diff_type; 00239 static void gradient(const BasicImage<S>& I, BasicImage<T>& grad) { 00240 int w = I.size().x; 00241 typename BasicImage<S>::const_iterator s = I.begin() + w + 1; 00242 typename BasicImage<S>::const_iterator end = I.end() - w - 1; 00243 typename BasicImage<T>::iterator t = grad.begin() + w + 1; 00244 while (s != end) { 00245 Pixel::Component<T>::get(*t, 0) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+1)) - *(s-1)); 00246 Pixel::Component<T>::get(*t, 1) = Pixel::scalar_convert<TComp,SComp,diff_type>(diff_type(*(s+w)) - *(s-w)); 00247 s++; 00248 t++; 00249 } 00250 zeroBorders(grad); 00251 } 00252 }; 00253 00260 template <class S, class T> void gradient(const BasicImage<S>& im, BasicImage<T>& out) 00261 { 00262 if( im.size() != out.size()) 00263 throw Exceptions::Vision::IncompatibleImageSizes("gradient"); 00264 Gradient<S,T>::gradient(im,out); 00265 } 00266 00267 00268 #ifndef DOXYGEN_IGNORE_INTERNAL 00269 void gradient(const BasicImage<byte>& im, BasicImage<short[2]>& out); 00270 #endif 00271 00272 00273 template <class T, class S, typename Precision> inline void sample(const SubImage<S>& im, Precision x, Precision y, T& result) 00274 { 00275 typedef typename Pixel::Component<S>::type SComp; 00276 typedef typename Pixel::Component<T>::type TComp; 00277 const int lx = (int)x; 00278 const int ly = (int)y; 00279 x -= lx; 00280 y -= ly; 00281 for(unsigned int i = 0; i < Pixel::Component<T>::count; i++){ 00282 Pixel::Component<T>::get(result,i) = Pixel::scalar_convert<TComp,SComp>( 00283 (1-y)*((1-x)*Pixel::Component<S>::get(im[ly][lx],i) + x*Pixel::Component<S>::get(im[ly][lx+1], i)) + 00284 y * ((1-x)*Pixel::Component<S>::get(im[ly+1][lx],i) + x*Pixel::Component<S>::get(im[ly+1][lx+1],i))); 00285 } 00286 } 00287 00288 template <class T, class S, typename Precision> inline T sample(const SubImage<S>& im, Precision x, Precision y){ 00289 T result; 00290 sample( im, x, y, result); 00291 return result; 00292 } 00293 00294 inline void sample(const SubImage<float>& im, double x, double y, float& result) 00295 { 00296 const int lx = (int)x; 00297 const int ly = (int)y; 00298 const int w = im.row_stride(); 00299 const float* base = im[ly]+lx; 00300 const float a = base[0]; 00301 const float b = base[1]; 00302 const float c = base[w]; 00303 const float d = base[w+1]; 00304 const float e = a-b; 00305 x-=lx; 00306 y-=ly; 00307 result = (float)(x*(y*(e-c+d)-e)+y*(c-a)+a); 00308 } 00309 00310 #if defined (CVD_HAVE_TOON) 00311 00322 template <typename T, typename S, typename P> 00323 int transform(const SubImage<S>& in, SubImage<T>& out, const TooN::Matrix<2, 2, P>& M, const TooN::Vector<2, P>& inOrig, const TooN::Vector<2, P>& outOrig, const T defaultValue = T()) 00324 { 00325 const int w = out.size().x, h = out.size().y, iw = in.size().x, ih = in.size().y; 00326 const TooN::Vector<2, P> across = M.T()[0]; 00327 const TooN::Vector<2, P> down = M.T()[1]; 00328 00329 const TooN::Vector<2, P> p0 = inOrig - M*outOrig; 00330 const TooN::Vector<2, P> p1 = p0 + w*across; 00331 const TooN::Vector<2, P> p2 = p0 + h*down; 00332 const TooN::Vector<2, P> p3 = p0 + w*across + h*down; 00333 00334 // ul --> p0 00335 // ur --> w*across + p0 00336 // ll --> h*down + p0 00337 // lr --> w*across + h*down + p0 00338 P min_x = p0[0], min_y = p0[1]; 00339 P max_x = min_x, max_y = min_y; 00340 00341 // Minimal comparisons needed to determine bounds 00342 if (across[0] < 0) 00343 min_x += w*across[0]; 00344 else 00345 max_x += w*across[0]; 00346 if (down[0] < 0) 00347 min_x += h*down[0]; 00348 else 00349 max_x += h*down[0]; 00350 if (across[1] < 0) 00351 min_y += w*across[1]; 00352 else 00353 max_y += w*across[1]; 00354 if (down[1] < 0) 00355 min_y += h*down[1]; 00356 else 00357 max_y += h*down[1]; 00358 00359 // This gets from the end of one row to the beginning of the next 00360 const TooN::Vector<2, P> carriage_return = down - w*across; 00361 00362 //If the patch being extracted is completely in the image then no 00363 //check is needed with each point. 00364 if (min_x >= 0 && min_y >= 0 && max_x < iw-1 && max_y < ih-1) 00365 { 00366 TooN::Vector<2, P> p = p0; 00367 for (int i=0; i<h; ++i, p+=carriage_return) 00368 for (int j=0; j<w; ++j, p+=across) 00369 sample(in,p[0],p[1],out[i][j]); 00370 return 0; 00371 } 00372 else // Check each source location 00373 { 00374 // Store as doubles to avoid conversion cost for comparison 00375 const P x_bound = iw-1; 00376 const P y_bound = ih-1; 00377 int count = 0; 00378 TooN::Vector<2, P> p = p0; 00379 for (int i=0; i<h; ++i, p+=carriage_return) { 00380 for (int j=0; j<w; ++j, p+=across) { 00381 //Make sure that we are extracting pixels in the image 00382 if (0 <= p[0] && 0 <= p[1] && p[0] < x_bound && p[1] < y_bound) 00383 sample(in,p[0],p[1],out[i][j]); 00384 else { 00385 out[i][j] = defaultValue; 00386 ++count; 00387 } 00388 } 00389 } 00390 return count; 00391 } 00392 } 00393 00394 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" */) 00395 { 00396 TooN::Vector<3> base = Minv.T()[2]; 00397 TooN::Vector<2> offset; 00398 offset[0] = in.size().x/2; 00399 offset[1] = in.size().y/2; 00400 offset -= TooN::project(base); 00401 TooN::Vector<3> across = Minv.T()[0]; 00402 TooN::Vector<3> down = Minv.T()[1]; 00403 double w = in.size().x-1; 00404 double h = in.size().y-1; 00405 int ow = out.size().x; 00406 int oh = out.size().y; 00407 base -= down*(oh/2) + across*(ow/2); 00408 for (int row = 0; row < oh; row++, base+=down) { 00409 TooN::Vector<3> x = base; 00410 for (int col = 0; col < ow; col++, x += across) { 00411 TooN::Vector<2> p = project(x) + offset; 00412 if (p[0] >= 0 && p[0] <= w-1 && p[1] >=0 && p[1] <= h-1) 00413 sample(in,p[0],p[1], out[row][col]); 00414 else 00415 zeroPixel(out[row][col]); 00416 } 00417 } 00418 } 00419 00421 template <typename T, typename CAM1, typename CAM2> 00422 void warp( const SubImage<T> & in, const CAM1 & cam_in, SubImage<T> & out, const CAM2 & cam_out){ 00423 const ImageRef size = out.size(); 00424 for(int y = 0; y < size.y; ++y){ 00425 for(int x = 0; x < size.x; ++x){ 00426 TooN::Vector<2> l = cam_in.project(cam_out.unproject(TooN::makeVector(x,y))); 00427 if(l[0] >= 0 && l[0] <= in.size().x - 1 && l[1] >= 0 && l[1] <= in.size().y -1){ 00428 sample(in, l[0], l[1], out[y][x]); 00429 } else 00430 out[y][x] = T(); 00431 } 00432 } 00433 } 00434 00438 template <typename T, typename CAM1, typename CAM2> 00439 Image<T> warp( const SubImage<T> & in, const CAM1 & cam_in, const ImageRef & size, const CAM2 & cam_out){ 00440 Image<T> result(size); 00441 warp(in, cam_in, result, cam_out); 00442 return result; 00443 } 00444 00448 template <typename T, typename CAM1, typename CAM2> 00449 Image<T> warp( const SubImage<T> & in, const CAM1 & cam_in, const CAM2 & cam_out){ 00450 Image<T> result(in.size()); 00451 warp(in, cam_in, result, cam_out); 00452 return result; 00453 } 00454 00455 #endif 00456 00458 template <class T> void flipVertical( Image<T> & in ) 00459 { 00460 int w = in.size().x; 00461 std::vector<T> buf(w); 00462 T* buffer = &buf[0]; 00463 T * top = in.data(); 00464 T * bottom = top + (in.size().y - 1)*w; 00465 while( top < bottom ) 00466 { 00467 std::copy(top, top+w, buffer); 00468 std::copy(bottom, bottom+w, top); 00469 std::copy(buffer, buffer+w, bottom); 00470 top += w; 00471 bottom -= w; 00472 } 00473 } 00474 00476 template <class T> void flipHorizontal( Image<T> & in ) 00477 { 00478 int w = in.size().x; 00479 int h = in.size().y; 00480 std::auto_ptr<T> buffer_auto(new T[w]); 00481 T* buffer = buffer_auto.get(); 00482 T * left = in.data(); 00483 T * right = left + w; 00484 int row = 0; 00485 while(row < h) 00486 { 00487 std::copy(left, right, buffer); 00488 std::reverse_copy(buffer, buffer+w-1, left); 00489 row++; 00490 left += w; 00491 right += w; 00492 } 00493 } 00494 00495 00496 namespace median { 00497 template <class T> inline T median3(T a, T b, T c) { 00498 if (b<a) 00499 return std::max(b,std::min(a,c)); 00500 else 00501 return std::max(a,std::min(b,c)); 00502 } 00503 00504 template <class T> inline void sort3(T& a, T& b, T& c) { 00505 using std::swap; 00506 if (b<a) swap(a,b); 00507 if (c<b) swap(b,c); 00508 if (b<a) swap(a,b); 00509 } 00510 00511 template <class T> T median_3x3(const T* p, const int w) { 00512 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]; 00513 sort3(a,b,c); 00514 sort3(d,e,f); 00515 sort3(g,h,i); 00516 e = median3(b,e,h); 00517 g = std::max(std::max(a,d),g); 00518 c = std::min(c,std::min(f,i)); 00519 return median3(c,e,g); 00520 } 00521 00522 template <class T> void median_filter_3x3(const T* p, const int w, const int n, T* out) 00523 { 00524 T a = p[-w-1], b = p[-w], d=p[-1], e=p[0], g=p[w-1], h=p[w]; 00525 sort3(a,d,g); 00526 sort3(b,e,h); 00527 for (int j=0; j<n; ++j, ++p, ++out) { 00528 T c = p[-w+1], f = p[1], i = p[w+1]; 00529 sort3(c,f,i); 00530 *out = median3(std::min(std::min(g,h),i), 00531 median3(d,e,f), 00532 std::max(std::max(a,b),c)); 00533 a=b; b=c; d=e; e=f; g=h; h=i; 00534 } 00535 } 00536 } 00537 00538 template <class T> void median_filter_3x3(const SubImage<T>& I, SubImage<T> out) 00539 { 00540 assert(out.size() == I.size()); 00541 const int s = I.row_stride(); 00542 const int n = I.size().x - 2; 00543 for (int i=1; i+1<I.size().y; ++i) 00544 median::median_filter_3x3(I[i]+1, s, n, out[i]+1); 00545 } 00546 00547 void median_filter_3x3(const SubImage<byte>& I, SubImage<byte> out); 00548 00549 //template<class T> 00550 00551 00552 }; // namespace CVD 00553 00554 #endif // CVD_VISION_H_