00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00293
00294
00295
00296 double min_x = p0[0], min_y = p0[1];
00297 double max_x = min_x, max_y = min_y;
00298
00299
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
00318 const TooN::Vector<2> carriage_return = down - w*across;
00319
00320
00321
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
00331 {
00332
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
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 )
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
00452
00453
00454 };
00455
00456 #endif // CVD_VISION_H_