libidx
/home/rex/ebltrunk/core/libidx/include/image.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Yann LeCun and Pierre Sermanet *
00003  *   yann@cs.nyu.edu, pierre.sermanet@gmail.com *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source
00014  *       Initiative (http://www.opensource.org) must display the
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef IMAGE_HPP_
00034 #define IMAGE_HPP_
00035 
00036 #include <math.h>
00037 #include <stdlib.h>
00038 
00039 using namespace std;
00040 
00041 #define BLK_AVRG(nlin, ncol) {                                          \
00042     int k,l; int norm = ncol * nlin;                                    \
00043     int acc0=0, acc1=0, acc2=0;                                         \
00044     for (k=0; k<nlin; k++) {                                            \
00045       register T *pinptr = pin+k*in_mod0;                               \
00046       for (l=0; l<ncol; l++) {                                          \
00047         acc0 += (int) pinptr[0];                                        \
00048         acc1 += (int) pinptr[1];                                        \
00049         acc2 += (int) pinptr[2];                                        \
00050         pinptr += in_mod1; }}                                           \
00051     pout[0] = acc0 / norm;pout[1] = acc1 / norm;pout[2] = acc2 / norm;}
00052 
00053 namespace ebl {
00054 
00055   template <typename T>
00056   idx<T> image_crop(idx<T> &in, int x, int y, int w, int h) {
00057     idx<T> bla = in.narrow(0, h, y).narrow(1, w, x);
00058     idx<T> bla3(bla.dim(0), bla.dim(1), bla.order() < 3 ? -1 : bla.dim(2));
00059     idx_copy(bla, bla3);
00060     return bla3;
00061   }
00062 
00064   // resize
00065 
00066   template <typename T>
00067   idx<T> image_resize(idx<T> &image, double h, double w, int mode,
00068                       rect<int> *iregion_, rect<int> *oregion_) {
00069     if (image.order() < 2) eblerror("image must have at least an order of 2.");
00070     // iregion is optional, set it to entire image if not given
00071     rect<int> iregion = rect<int>(0, 0, image.dim(0), image.dim(1));
00072     if (iregion_)
00073       iregion = *iregion_;
00074     double ratioh = h / iregion.height;
00075     double ratiow = w / iregion.width;
00076     double ratiomin = min(ratiow, ratioh);
00077     // if data is not contiguous, copy it to a contiguous buffer
00078     idx<T> contim(image);
00079     if (!image.contiguousp()) {
00080       idxdim d(image.spec);
00081       idx<T> tmp(d);
00082       idx_copy(image, tmp);
00083       contim = tmp;
00084     }
00085     int imw = (int) contim.dim(1);
00086     int imh = (int) contim.dim(0);
00087     //    int rw = 0, rh = 0;
00088     double ow = 0, oh = 0;
00089     if ((imw == 0) || (imh == 0))
00090       eblerror("cannot have dimensions of size 0"
00091                << " while trying to resize image " << image
00092                << " to " << h << "x" << w << " with mode " << mode);
00093     // determine actual size of output image
00094     if ((0 == w) || (0 == h)) {
00095       if (0 == w) {
00096         if (0 == h)
00097           eblerror("desired width and height cannot be both zero"
00098                    << " while trying to resize image " << image
00099                    << " to " << h << "x" << w << " with mode " << mode)
00100           else  w = max(1, (int) (imw * (w / imh)));
00101       } else    h = max(1, (int) (imh * (h / imw)));
00102     }
00103     if ((mode == 0) || (mode == 3)) { // preserve aspect ratio
00104       ratiow = ratiomin;
00105       ratioh = ratiomin;
00106     }
00107     else if (mode == 1) { // possibly modify aspect ratio
00108       // ratiow = w; //ratiow;
00109       // ratioh = h; //ratioh;
00110     }
00111     else if (mode == 2) { // use w and h as scaling ratios
00112       ratiow = w;
00113       ratioh = h;
00114     }
00115     else
00116       eblerror("illegal mode or desired dimensions"
00117                << " while trying to resize image " << image
00118                << " to " << h << "x" << w << " with mode " << mode);
00119     // output sizes of entire image
00120     if (iregion_ || (mode == 2) || (mode == 0) || (mode == 3)) {
00121       ow = rint(max(1.0, imw * ratiow));
00122       oh = rint(max(1.0, imh * ratioh));
00123     } else {
00124       ow = w;//max(1.0, imw * ratiow);
00125       oh = h;//max(1.0, imh * ratioh);
00126     }
00127     // // compute closest integer subsampling ratio
00128     // rw = std::max(1, (int) (1 / ratiow));
00129     // rh = std::max(1, (int) (1 / ratioh));
00130     // compute output region
00131     rect<int> oregion((uint)(iregion.h0 * ratioh),
00132                       (uint)(iregion.w0 * ratiow),
00133                       (uint)(iregion.height * ratioh),
00134                       (uint)(iregion.width * ratiow));
00135     if (oregion_)
00136       *oregion_ = oregion;
00137     // TODO: why is this useful? for ubyte images?
00138     // subsample by integer ratio if necessary
00139     //     if ((rh > 1) || (rw > 1)) {
00140     //       contim = image_subsample(contim, rh, rw);
00141     //       imw = contim.dim(1);
00142     //       imh = contim.dim(0);
00143     //     }
00144     // resample from subsampled image with bilinear interpolation
00145     idx<T> rez((intg) oh, (intg) ow, (contim.order() == 3) ? contim.dim(2) : 1);
00146     idx<T> bg(4);
00147     idx_clear(bg);
00148     // the 0.5 thingies are necessary because warp-bilin interprets
00149     // integer coordinates as being at the center of each pixel.
00150     float x1 = (float) -0.5, y1 = (float) -0.5;
00151     float x3 = (float) imw - (float) 0.5, y3 = (float) imh - (float) 0.5;
00152     float p1 = (float) -0.5, q1 = (float) -0.5;
00153     float p3 = (float) ow - (float) 0.5, q3 = (float) oh - (float) 0.5;
00154     image_warp_quad(contim, rez, bg, 1, x1, y1, x3, y1, x3, y3, x1, y3, 
00155                     p1, q1, p3, q3);
00156     if (contim.order() == 2)
00157       return rez.select(2, 0);
00158     // copy preserved ratio output in the middle of the wanted out size
00159     if ((mode == 3) && ((rez.dim(0) != h) || (rez.dim(1) != w))) {
00160       idx<T> out((intg) h, (intg) w, rez.dim(2));
00161       idx_clear(out);
00162       idx<T> tmp = out.narrow(0, rez.dim(0), (intg) ((h - rez.dim(0)) / 2));
00163       tmp = tmp.narrow(1, rez.dim(1), (intg) ((w - rez.dim(1)) / 2));
00164       idx_copy(rez, tmp);
00165       rez = out;
00166     }
00167     return rez;
00168   }
00169 
00170   template <typename T>
00171   idx<T> image_gaussian_resize(idx<T> &im, double oheight, double owidth,
00172                                uint mode, rect<int> *iregion_,
00173                                rect<int> *oregion) {
00174     // only accept 2D or 3D images
00175     if ((im.order() != 2) && (im.order() != 3)) {
00176       cerr << "illegal order: " << im << endl;
00177       eblerror("unexpected image format");
00178     }
00179     // iregion is optional, set it to entire image if not given
00180     rect<uint> iregion(0, 0, im.dim(0), im.dim(1));
00181     if (iregion_)
00182       iregion = *iregion_;
00183     // if region's height and width already have the correct size, return
00184     if ((iregion.height == oheight) && (iregion.width <= owidth)) {
00185       if (oregion)
00186         *oregion = iregion;
00187       return im;
00188     }
00189     // gaussian resize
00190     gaussian_pyramid<T> gp;
00191     rect<uint> outr;
00192     idx<T> rim;
00194     if ((iregion.width > owidth) || (iregion.height > oheight)) { // reduce
00195       rect<uint> exact_inr;
00196       uint reductions;
00197   
00198       switch (mode) {
00199       case 0: // keep aspect ratio
00200         // compute biggest down/up-sizing factor. do not use a double variable
00201         // to compute ratio, compute it directly. somehow the results are
00202         // more precise without the variable.
00203         if (oheight / (double) iregion.height <
00204             owidth  / (double) iregion.width)
00205           outr = rect<uint>
00206             ((uint)(iregion.h0 * oheight / (double)iregion.height),
00207              (uint)(iregion.w0 * oheight / (double)iregion.height),
00208              (uint)(iregion.height * oheight / (double)iregion.height),
00209              (uint)(iregion.width * oheight / (double)iregion.height));
00210         else
00211           outr = rect<uint>
00212             ((uint) (iregion.h0     * owidth / (double)iregion.width),
00213              (uint) (iregion.w0     * owidth / (double)iregion.width),
00214              (uint) (iregion.height * owidth / (double)iregion.width),
00215              (uint) (iregion.width  * owidth / (double)iregion.width));
00216         reductions = gp.count_reductions_exact(iregion, outr, exact_inr);
00217         break ;
00218       default:
00219         eblerror("unsupported mode " << mode);
00220       }
00221       // bilinear resize at closest resolution to current resolution
00222       double exact_imh = (exact_inr.height / (double) iregion.height)
00223         * (double) im.dim(0);
00224       double exact_imw = (exact_inr.width / (double) iregion.width)
00225         * (double) im.dim(1);
00226       rim = image_resize(im, exact_imh, exact_imw, 1);
00227       // now gaussian resize to exact target size
00228       rim = rim.shift_dim(2, 0);
00229       rim = gp.reduce(rim, reductions);
00230       rim = rim.shift_dim(0, 2);
00232     } else { // expand
00233       uint expansions;
00234       uint imax, omax, dist;
00235       rect<uint> outr2;
00236       // select biggest side
00237       if (iregion.height > iregion.width) {
00238         imax = iregion.height;
00239         omax = (uint) oheight;
00240       } else {
00241         imax = iregion.width;
00242         omax = (uint) owidth;
00243       }
00244       // count how many expansions necessary
00245       switch (mode) {
00246       case 0: // keep aspect ratio
00247         expansions = gp.count_expansions(imax, omax, dist);
00248         break ;
00249       default:
00250         eblerror("unsupported mode");
00251       }
00252       // now gaussian resize to exact target size
00253       rim = im.shift_dim(2, 0);
00254       rim = gp.expand(rim, expansions);
00255       outr2 = gp.expand_rect(iregion, expansions);
00256       // bilinear resize to target resolution
00257       rim = rim.shift_dim(0, 2);
00258       // TODO: casting to int correct?
00259       rect<int> oor2, oor;
00260       oor2 = outr2; oor = outr;
00261       rim = image_resize(rim, oheight, owidth, mode, &oor2, &oor);
00262       outr2 = oor2; outr = oor;
00263     }
00265     if (oregion)
00266       *oregion = outr;
00267     return rim;
00268   }
00269 
00270   template <typename T>
00271   idx<T> image_mean_resize(idx<T> &im, double oheight, double owidth,
00272                            uint mode, rect<int> *iregion_, rect<int> *oregion) {
00273     // only accept 2D or 3D images
00274     if ((im.order() != 2) && (im.order() != 3)) {
00275       cerr << "illegal order: " << im << endl;
00276       eblerror("unexpected image format");
00277     }
00278     if (oheight == 0 || owidth == 0) {
00279       cerr << "oheight: " << oheight << " owidth " << owidth << endl;
00280       eblerror("illegal resize image to zero");
00281     }
00282     // iregion is optional, set it to entire image if not given
00283     rect<int> iregion(0, 0, im.dim(0), im.dim(1));
00284     if (iregion_)
00285       iregion = *iregion_;
00286     // if region's height and width already have the correct size, return
00287     if ((iregion.height == oheight) && (iregion.width <= owidth)) {
00288       if (oregion)
00289         *oregion = iregion;
00290       return im;
00291     }
00292     // mean resize
00293     rect<int> outr, inr;
00294     idx<T> rim, out;
00296     if ((iregion.width > owidth) || (iregion.height > oheight)) { // reduce
00297       switch (mode) {
00298       case 0: // keep aspect ratio
00299         // compute biggest down/up-sizing factor. do not use a double variable
00300         // to compute ratio, compute it directly. somehow the results are
00301         // more precise without the variable.
00302         if (oheight / (double) iregion.height <
00303             owidth  / (double) iregion.width) {
00304           outr =
00305             rect<int>((uint)(iregion.h0    * oheight / (double)iregion.height),
00306                       (uint)(iregion.w0    * oheight / (double)iregion.height),
00307                       (uint)(iregion.height* oheight / (double)iregion.height),
00308                       (uint)(iregion.width * oheight / (double)iregion.height));
00309           inr =
00310             rect<int>(0, 0,
00311                       (uint) (im.dim(0) * oheight / (double)iregion.height),
00312                       (uint) (im.dim(1) * oheight / (double)iregion.height));
00313         } else { // use width factor (smaller factor)
00314           outr =
00315             rect<int>((uint) (iregion.h0     * owidth / (double)iregion.width),
00316                       (uint) (iregion.w0     * owidth / (double)iregion.width),
00317                       (uint) (iregion.height * owidth / (double)iregion.width),
00318                       (uint) (iregion.width  * owidth / (double)iregion.width));
00319           inr =
00320             rect<int>(0, 0,
00321                       (uint) (im.dim(0) * owidth / (double)iregion.width),
00322                       (uint) (im.dim(1) * owidth / (double)iregion.width));
00323         }
00324         break ;
00325       default:
00326         eblerror("unsupported mode");
00327       }
00328       // find closest multiple of target area that is smaller than input area
00329       // (smaller to avoid upsampling)
00330       uint fact = (std::min)((uint)floor(iregion.height / (float) outr.height),
00331                              (uint)floor(iregion.width / (float) outr.width));
00332       if (fact == 0) // no multiple smaller than input, go straight for bilinear
00333         return image_resize(im, oheight, owidth, mode, iregion_, oregion);      
00334       // bilinear resize at closest resolution to current resolution
00335       rim = image_resize(im, inr.height * fact, inr.width * fact, 1);
00336       //  add extra padding around original image if it's not a multiple of fact
00337       // rect rrim(0, 0, rim.dim(0), rim.dim(1));
00338       // rect cropped;
00339       // uint hmod = rim.dim(0) % fact; 
00340       // if (hmod) hmod = fact - hmod;
00341       // uint wmod = rim.dim(1) % fact;
00342       // if (wmod) wmod = fact - wmod;
00343       // idx<T> rimf = image_region_to_rect(rim, rrim, rim.dim(0) + hmod,
00344       //                                         rim.dim(1) + wmod, cropped);
00345       // allocate output
00346       out = idx<T>(rim.dim(0) / fact, rim.dim(1) / fact, rim.dim(2));
00347       idx<T> tmp, tmpout;
00348       uint i = 0, j = 0;
00349       // now mean resize to exact target size
00350       idx_bloop1(ou, out, T) { // loop on output rows
00351         idx_bloop1(o, ou, T) { // loop on output cols
00352           tmp = rim.narrow(0, fact, i * fact);
00353           tmp = tmp.narrow(1, fact, j * fact);
00354           idx_eloop2(layer, tmp, T, olayer, o, T) { // loop on each layer
00355             olayer.set(idx_mean(layer));
00356           }
00357           j++;
00358         }
00359         i++;
00360         j = 0;
00361       }
00363     } else { // expansion: return bilinear resizing
00364       return image_resize(im, oheight, owidth, mode, iregion_, oregion);
00365     }
00367     if (oregion)
00368       *oregion = outr;
00369     return out;
00370   }
00371 
00372   template <typename T> 
00373   idx<T> image_region_to_square(idx<T> &im, const rect<uint> &r) {
00374     // TODO: check expecting 2D or 3D
00375     // TODO: check that rectangle is within image
00376     uint sz = std::max(r.height, r.width);
00377     idxdim d(im);
00378     uint dh = 0;
00379     uint dw = 1;
00380     d.setdim(dh, sz);
00381     d.setdim(dw, sz);
00382     idx<T> res(d);
00383     uint tmp_h = MIN(sz, r.height);
00384     uint tmp_w = MIN(sz, r.width);
00385     idx<T> tmpres = res.narrow(dh, tmp_h, res.dim(dh) / 2 - tmp_h / 2);
00386     tmpres = tmpres.narrow(dw, tmp_w, res.dim(dw) / 2 - tmp_w / 2);
00387     idx<T> tmpim = im.narrow(dh, tmp_h, r.h0);
00388     tmpim = tmpim.narrow(dw, tmp_w, r.w0);
00389     idx_clear(res);
00390     idx_copy(tmpim, tmpres);
00391     return res;
00392   }
00393   
00394   template <typename T> 
00395   idx<ubyte> image_to_ubyte(idx<T> &im, double zoomh, double zoomw,
00396                             T minv, T maxv) {
00397     // check the order and dimensions
00398     if ((im.order() < 2) || (im.order() > 3) || 
00399         ((im.order() == 3) && (im.dim(2) != 1) && (im.dim(2) != 3))) {
00400       eblerror("expected a 2D idx or a 3D idx with 1 or 3 channels only "
00401                << "but got: " << im);
00402     }
00403     // create a copy
00404     idxdim d(im);
00405     idx<T> im1(d);
00406     idx_copy(im, im1);
00407     // check zoom factor
00408     if ((zoomw <= 0.0) || (zoomh <= 0.0))
00409       eblerror("cannot zoom by a factor <= 0.0");
00410     // check minv maxv
00411     if (minv > maxv) {
00412       T tmp = minv;
00413       minv = maxv;
00414       maxv = tmp;
00415     }
00416     // if minv and maxv are defaults, take actual min and max of the image
00417     if (minv == maxv) {
00418       minv = idx_min(im1);
00419       maxv = idx_max(im1);
00420     }
00421     if (minv == maxv) { // if minv still == maxv, use 0 and 1
00422       minv = 0;
00423       maxv = 1;
00424     }
00425     // create target image
00426     int newh = (int) (im1.dim(0) * zoomh);
00427     int neww = (int) (im1.dim(1) * zoomw);
00428     idx<T> im2 = ((newh == im1.dim(0)) && (neww == im1.dim(1))) ?
00429       im1 : image_resize(im1, newh, neww);
00430     d.setdim(0, newh);
00431     d.setdim(1, neww);
00432     idx<ubyte> image(d);
00433     // map values between minv and maxv to 0 .. 255
00434     idx_subc_bounded(im2, minv, im2);
00435     idx_dotc_bounded(im2, (T) (255.0 / (double) (maxv - minv)), im2);
00436     idx_copy_clip(im2, image);
00437     return image;
00438   }
00439 
00440   template <typename T>
00441   idx<T> image_subsample_grayscale(idx<T> &in, int nlin, int ncol) {
00442     intg h = in.dim(0);
00443     intg w = in.dim(1);
00444     intg nh = h / nlin;
00445     intg nw = w / ncol;
00446     idx<T> out(nh, nw);
00447     if ((nlin == 1) && (ncol == 1)) {
00448       idx_copy(in, out);
00449       return out;
00450     }
00451     idx<T> inp = in.narrow(0, nlin * nh, 0);
00452     inp = inp.narrow(1, ncol * nw, 0);
00453     T *_idx2loopc1, *pin;
00454     T *_idx2loopc2, *pout;
00455     int i, _imax = out.dim(0);
00456     int j, _jmax = out.dim(1);
00457     int _imat1_m0 = inp.mod(0);
00458     int _imat1_m1 = inp.mod(1);
00459     int _imat2_m0 = out.mod(0);
00460     int _imat2_m1 = out.mod(1);
00461     int pin_incr = ncol * _imat1_m1;
00462     int norm = ncol * nlin;
00463     register int acc0;
00464     register int k,l;
00465     register T *pinptr;
00466     register int pinptr_incr = _imat1_m0 - ncol* _imat1_m1;
00467     _idx2loopc1 = inp.idx_ptr();
00468     _idx2loopc2 = out.idx_ptr();
00469     for (i = 0; i < _imax; i++) {
00470       pin = _idx2loopc1;
00471       pout = _idx2loopc2;
00472       for (j = 0; j < _jmax; j++) {
00473         acc0 =0;
00474         pinptr = pin;
00475         for (k=0; k<nlin; k++) {
00476           for (l=0; l<ncol; l++) {
00477             acc0 += (int) pinptr[0];
00478             pinptr += _imat1_m1;
00479           }
00480           pinptr += pinptr_incr;
00481         }
00482         pout[0] = acc0/norm;
00483         pin += pin_incr;
00484         pout += _imat2_m1;
00485       }
00486       _idx2loopc1 += _imat1_m0 * nlin;
00487       _idx2loopc2 += _imat2_m0;
00488     }
00489     return out;
00490   }
00491 
00492   template <typename T> idx<T> image_subsample_rgb(idx<T> &in, int nlin, int ncol) {
00493     intg h = in.dim(0);
00494     intg w = in.dim(1);
00495     intg nh = h / nlin;
00496     intg nw = w / ncol;
00497     idx<T> out(nh, nw, in.dim(2));
00498     if ((nlin == 1) && (ncol == 1)) {
00499       idx_copy(in, out);
00500       return out;
00501     }
00502     idx<T> inp = in.narrow(0, nlin * nh, 0).narrow(1, ncol * nw, 0);
00503     T *in_line, *pin;
00504     T *out_line, *pout;
00505     int i, _imax = out.dim(0);
00506     int j, _jmax = out.dim(1);
00507     int in_mod0 = inp.mod(0);
00508     int in_mod1 = inp.mod(1);
00509     int out_mod0 = out.mod(0);
00510     int out_mod1 = out.mod(1);
00511     int pin_incr = ncol * in_mod1;
00512     in_line = inp.idx_ptr();
00513     out_line = out.idx_ptr();
00514     for (i = 0; i < _imax; i++) {
00515       pin = in_line;
00516       pout = out_line;
00517       for (j = 0; j < _jmax; j++) {
00518         BLK_AVRG(nlin, ncol);
00519         pin += pin_incr;
00520         pout += out_mod1;
00521       }
00522       in_line += in_mod0 * nlin;
00523       out_line += out_mod0;
00524     }
00525     return out;
00526   }
00527 
00528   template <typename T> idx<T> image_subsample(idx<T> &in, int nlin, int ncol) {
00529     switch (in.order()) {
00530     case 2:
00531       return image_subsample_grayscale(in, nlin, ncol);
00532     case 3:
00533       return image_subsample_rgb(in, nlin, ncol);
00534     default:
00535       eblerror("image must have at least an order of 2.");
00536       return in;
00537     }
00538   }
00539 
00540   template <typename T>
00541   void image_warp_quad(idx<T> &in, idx<T> &out,
00542                        idx<T> &background, int mode,
00543                        float x1, float y1, float x2, float y2,
00544                        float x3, float y3, float x4, float y4,
00545                        float p1, float q1, float p3, float q3) {
00546     intg outi = out.dim(0);
00547     intg outj = out.dim(1);
00548     idx<int> dispi(outi, outj);
00549     idx<int> dispj(outi, outj);
00550     compute_bilin_transform<float>(dispi, dispj, x1, y1, x2, y2, x3, y3,
00551                                    x4, y4, p1, q1, p3, q3);
00552     if (0 == mode)
00553       image_warp_fast(in, out, background.idx_ptr(), dispi, dispj);
00554     else
00555       image_warp(in, out, background, dispi, dispj);
00556   }
00557 
00558 
00559   template <typename T>
00560   void image_warp(idx<T> &in, idx<T> &out, idx<T> &background,
00561                   idx<int> &pi, idx<int> &pj) {
00562     T* pin = in.idx_ptr();
00563     int indimi = in.dim(0);
00564     int indimj = in.dim(1);
00565     int inmodi = in.mod(0);
00566     int inmodj = in.mod(1);
00567     int ppi, ppj;
00568     { idx_bloop3(lout, out, T, lpi, pi, int, lpj, pj, int) {
00569         { idx_bloop3(llout, lout, T, llpi, lpi, int, llpj, lpj, int) {
00570             ppi = llpi.get();
00571             ppj = llpj.get();
00572             image_interpolate_bilin(background.idx_ptr(), pin, indimi, indimj,
00573                                     inmodi, inmodj, ppi, ppj, llout.idx_ptr(),
00574                                     (int) out.dim(2));
00575           }}
00576       }}
00577   }
00578 
00579   template <typename T>
00580   void image_warp_fast(idx<T> &in, idx<T> &out, T *background,
00581                        idx<int> &pi, idx<int> &pj) {
00582     T* pin = in.idx_ptr();
00583     int indimi = in.dim(0);
00584     int indimj = in.dim(1);
00585     int inmodi = in.mod(0);
00586     int inmodj = in.mod(1);
00587     int ppi, ppj;
00588     register int li, lj;
00589     register T *inn, *outt;
00590     int outsize = out.dim(2);
00591     { idx_bloop3(lout, out, T, lpi, pi, int, lpj, pj, int) {
00592         { idx_bloop3(llout, lout, T, llpi, lpi, int, llpj, lpj, int) {
00593             outt = llout.idx_ptr();
00594             ppi = llpi.get();
00595             ppj = llpj.get();
00596             li = (ppi+0x7f) >> 16;
00597             lj = (ppj+0x7f) >> 16;
00598             if ((li>=0) && (li<indimi) && (lj>=0) && (lj<indimj)) {
00599               inn = (T*)(pin) + inmodi * li + inmodj * lj;
00600               outt[0] = inn[0];
00601               if (outsize >= 3) {
00602                 outt[1] = inn[1];
00603                 outt[2] = inn[2];
00604               }
00605               if (outsize >= 4)
00606                 outt[3] = inn[3];
00607             } else {
00608               outt[0] = *(background);
00609               if (outsize >= 3) {
00610                 outt[1] = *(background+1);
00611                 outt[2] = *(background+2);
00612               }
00613               if (outsize >= 4)
00614                 outt[3] = *(background+3);
00615             }
00616           }}
00617       }}
00618   }
00619 
00620   template <typename T>
00621   void image_warp_flow(idx<T> &src, idx<T> &dst, idx<float> &flow,
00622                        bool bilinear, bool use_bg, T background) {
00623     // dims
00624     int width = src.dim(1);
00625     int height = src.dim(0);
00626     int channels = src.dim(2);
00627     intg *is = src.mod_ptr();
00628     intg *os = dst.mod_ptr();
00629     intg *fs = flow.mod_ptr();
00630 
00631     // get raw pointers
00632     T *dst_data = dst.idx_ptr();
00633     T *src_data = src.idx_ptr();
00634     float *flow_data = flow.idx_ptr();
00635 
00636     // resample
00637     intg k,x,y;
00638     for (y = 0; y < height; y++) {
00639       for (x = 0; x < width; x++) {
00640         // subpixel position:
00641         float flow_y = flow_data[ 0*fs[0] + y*fs[1] + x*fs[2] ];
00642         float flow_x = flow_data[ 1*fs[0] + y*fs[1] + x*fs[2] ];
00643         float iy = y + flow_y;
00644         float ix = x + flow_x;
00645 
00646         // when going beyond image's boundaries, use background value
00647         if (use_bg && (ix < 0 || ix >= width || iy < 0 || iy >= height)) {
00648           for (k = 0; k < channels; k++)
00649             dst_data[ y*os[0] + x*os[1] + k*os[2] ] = background;
00650           continue ;
00651         }
00652         
00653         // borders
00654         ix = std::max(ix,(float)0);
00655         ix = std::min(ix,(float)(width-1));
00656         iy = std::max(iy,(float)0);
00657         iy = std::min(iy,(float)(height-1));
00658 
00659         // bilinear?
00660         if (bilinear) {
00661           // 4 nearest neighbors:
00662           int ix_nw = (int) floor(ix);
00663           int iy_nw = (int) floor(iy);
00664           int ix_ne = ix_nw + 1;
00665           int iy_ne = iy_nw;
00666           int ix_sw = ix_nw;
00667           int iy_sw = iy_nw + 1;
00668           int ix_se = ix_nw + 1;
00669           int iy_se = iy_nw + 1;
00670           
00671           // get surfaces to each neighbor:
00672           float nw = ((float)(ix_se-ix))*(iy_se-iy);
00673           float ne = ((float)(ix-ix_sw))*(iy_sw-iy);
00674           float sw = ((float)(ix_ne-ix))*(iy-iy_ne);
00675           float se = ((float)(ix-ix_nw))*(iy-iy_nw);
00676           
00677           // weighted sum of neighbors:
00678           for (k=0; k<channels; k++) {
00679             dst_data[ k*os[2] + y*os[0] + x*os[1] ] = 
00680               src_data[ k*is[2] + iy_nw*is[0] + ix_nw*is[1] ] * nw
00681               + src_data[ k*is[2] + iy_ne*is[0]
00682                           + std::min(ix_ne,width-1)*is[1]] * ne
00683               + src_data[ k*is[2] + std::min(iy_sw,height-1)*is[0]
00684                           + ix_sw*is[1] ] * sw
00685               + src_data[ k*is[2] + std::min(iy_se,height-1)*is[0]
00686                           + std::min(ix_se,width-1)*is[1] ] * se;
00687           }
00688         } else {
00689           // 1 nearest neighbor:
00690           int ix_n = (int) floor(ix+0.5);
00691           int iy_n = (int) floor(iy+0.5);
00692           
00693           // weighted sum of neighbors:
00694           for (k = 0; k < channels; k++)
00695             dst_data[ y*os[0] + x*os[1] + k*os[2] ] =
00696               src_data[ iy_n*is[0] + ix_n*is[1] + k*is[2] ];
00697         }
00698       }
00699     }
00700   }
00701 
00702   template <typename T>
00703   void image_interpolate_bilin(T* background, T *pin, int indimi, int indimj,
00704                                int inmodi, int inmodj, int ppi, int ppj,
00705                                T* out, int outsize) {
00706     int li0, lj0;
00707     register int li1, lj1;
00708     int deltai, ndeltai;
00709     int deltaj, ndeltaj;
00710     register T *pin00;
00711     register T *v00, *v01, *v10, *v11;
00712     li0 = ppi >> 16;
00713     li1 = li0+1;
00714     deltai = ppi & 0x0000ffff;
00715     ndeltai = 0x00010000 - deltai;
00716     lj0 = ppj  >> 16;
00717     lj1 = lj0+1;
00718     deltaj = ppj & 0x0000ffff;
00719     ndeltaj = 0x00010000 - deltaj;
00720     pin00 = (T*)(pin) + inmodi * li0 + inmodj * lj0;
00721     if ((li1>0)&&(li1<indimi)) {
00722       if ((lj1>0)&&(lj1<indimj)) {
00723         v00 = (pin00);
00724         v01 = (pin00+inmodj);
00725         v11 = (pin00+inmodi+inmodj);
00726         v10 = (pin00+inmodi);
00727       } else if (lj1==0) {
00728         v00 = background;
00729         v01 = (pin00+inmodj);
00730         v11 = (pin00+inmodi+inmodj);
00731         v10 = background;
00732       } else if (lj1==indimj) {
00733         v00 = (pin00);
00734         v01 = background;
00735         v11 = background;
00736         v10 = (pin00+inmodi);
00737       } else {
00738         v00 = background;
00739         v01 = background;
00740         v11 = background;
00741         v10 = background;
00742       }
00743     } else if (li1==0) {
00744       if ((lj1>0)&&(lj1<indimj)) {
00745         v00 = background;
00746         v01 = background;
00747         v11 = (pin00+inmodi+inmodj);
00748         v10 = (pin00+inmodi);
00749       } else if (lj1==0) {
00750         v00 = background;
00751         v01 = background;
00752         v11 = (pin00+inmodi+inmodj);
00753         v10 = background;
00754       } else if (lj1==indimj) {
00755         v00 = background;
00756         v01 = background;
00757         v11 = background;
00758         v10 = (pin00+inmodi);
00759       } else {
00760         v00 = background;
00761         v01 = background;
00762         v11 = background;
00763         v10 = background;
00764       }
00765     } else if (li1==indimi) {
00766       if ((lj1>0)&&(lj1<indimj)) {
00767         v00 = (pin00);
00768         v01 = (pin00+inmodj);
00769         v11 = background;
00770         v10 = background;
00771       } else if (lj1==0) {
00772         v00 = background;
00773         v01 = (pin00+inmodj);
00774         v11 = background;
00775         v10 = background;
00776       } else if (lj1==indimj) {
00777         v00 = (pin00);
00778         v01 = background;
00779         v11 = background;
00780         v10 = background;
00781       } else {
00782         v00 = background;
00783         v01 = background;
00784         v11 = background;
00785         v10 = background;
00786       }
00787     } else {
00788       v00 = background;
00789       v01 = background;
00790       v11 = background;
00791       v10 = background;
00792     }
00793     // TODO: this does not work for ubyte (cf ubyte implementation in image.cpp)
00794     // make it generic to avoid code redondancy.
00795     double dd = 1.0 / 65536.0;
00796     T d = (T) dd;
00797     if (sizeof (T) <= 2)
00798       d = 0;
00799     if (outsize >= 1)
00800       *out = (ndeltaj * (( *v10*deltai + *v00*ndeltai )*d) +
00801               deltaj  * (( *v11*deltai + *v01*ndeltai )*d))*d;
00802     if (outsize >= 2)
00803       *(out + 1) = (ndeltaj * ((v10[1]*deltai + v00[1]*ndeltai) * d) +
00804                     deltaj  * ((v11[1]*deltai + v01[1]*ndeltai) * d)) * d;
00805     if (outsize >= 3)
00806       *(out + 2) = (ndeltaj * ((v10[2]*deltai + v00[2]*ndeltai) * d) +
00807                     deltaj  * ((v11[2]*deltai + v01[2]*ndeltai) * d)) * d;
00808     if (outsize >= 4)
00809       *(out + 3) = (ndeltaj * ((v10[3]*deltai + v00[3]*ndeltai) * d) +
00810                     deltaj  * ((v11[3]*deltai + v01[3]*ndeltai) * d)) * d;
00811     if (outsize >= 5) {
00812       eblerror("not implemented for more than 4 channels ("
00813                << outsize << " channels in " << indimi << "x" << indimj
00814                << " image)");
00815     }
00816   }
00817 
00818   template <typename T> void compute_bilin_transform(idx<int> &dispi,
00819                                                  idx<int> &dispj,
00820                                                  float x1, float y1, float x2,
00821                                                  float y2, float x3, float y3,
00822                                                  float x4, float y4, float p1,
00823                                                  float q1, float p3, float q3) {
00824     // compute transformation matrix from coordinates
00825     // in target (rectangular) space to coordinates
00826     // in original (irregular quadrilateral) image
00827     // transformation matrix is in 16.16 fixed point format.
00828     float k = 65536 / ((p3 - p1) * (q3 - q1));
00829     float x41 = x4 - x1;
00830     float x21 = x2 - x1;
00831     float x43 = x4 - x3;
00832     float x23 = x2 - x3;
00833     int mx0 = (int) (k * ((q3 * x21) + (q1 * x43)));
00834     int mx1 = (int) (k * ((p3 * x41) + (p1 * x23)));
00835     int mx2 = (int) ((-k) * (x41 + x23));
00836     int mx3 = (int) (k * (((p3 * q3 * x1) + (p1 * q1 * x3)) -
00837                           ((p1 * q3 * x2) + (p3 * q1 * x4))));
00838     float y41 = y4 - y1;
00839     float y21 = y2 - y1;
00840     float y43 = y4 - y3;
00841     float y23 = y2 - y3;
00842     int my0 = (int) (k * ((q3 * y21) + (q1 * y43)));
00843     int my1 = (int) (k * ((p3 * y41) + (p1 * y23)));
00844     int my2 = (int) ((-k) * (y41 + y23));
00845     int my3 = (int) (k * (((p3 * q3 * y1) + (p1 * q1 * y3)) -
00846                           ((p1 * q3 * y2) + (p3 * q1 * y4))));
00847     int q = 0, p = 0;
00848     { idx_bloop2(ispi, dispi, int, ispj, dispj, int) {
00849         p = 0;
00850         { idx_bloop2(di, ispi, int, dj, ispj, int) {
00851             di.set(my0 * p + my1 * q + my2 * p * q + my3);
00852             dj.set(mx0 * p + mx1 * q + mx2 * p * q + mx3);
00853             p++;
00854           }}
00855         q++;
00856       }}
00857   }
00858 
00859 
00860 //   template <typename T> void compute_bilin_transform2(idx<int> &dispi,
00861 //                                                idx<int> &dispj,
00862 //                                                float x1, float y1,
00863 //                                                float x2, float y2,
00864 //                                                float x3, float y3,
00865 //                                                float x4, float y4,
00866 //                                                float p1, float q1,
00867 //                                                float p2, float q2,
00868 //                                                float p3, float q3,
00869 //                                                float p4, float q4) {
00870 //     // compute transformation matrix from coordinates
00871 //     // in target (rectangular) space to coordinates
00872 //     // in original (irregular quadrilateral) image
00873 //     // transformation matrix is in 16.16 fixed point format.
00874 //     float k = 65536 / ((p3 - p1) * (q3 - q1));
00875 //     float x41 = x4 - x1;
00876 //     float x21 = x2 - x1;
00877 //     float x43 = x4 - x3;
00878 //     float x23 = x2 - x3;
00879 //     int mx0 = (int) (k * ((q3 * x21) + (q1 * x43)));
00880 //     int mx1 = (int) (k * ((p3 * x41) + (p1 * x23)));
00881 //     int mx2 = (int) ((-k) * (x41 + x23));
00882 //     int mx3 = (int) (k * (((p3 * q3 * x1) + (p1 * q1 * x3)) -
00883 //                        ((p1 * q3 * x2) + (p3 * q1 * x4))));
00884 //     float y41 = y4 - y1;
00885 //     float y21 = y2 - y1;
00886 //     float y43 = y4 - y3;
00887 //     float y23 = y2 - y3;
00888 //     int my0 = (int) (k * ((q3 * y21) + (q1 * y43)));
00889 //     int my1 = (int) (k * ((p3 * y41) + (p1 * y23)));
00890 //     int my2 = (int) ((-k) * (y41 + y23));
00891 //     int my3 = (int) (k * (((p3 * q3 * y1) + (p1 * q1 * y3)) -
00892 //                        ((p1 * q3 * y2) + (p3 * q1 * y4))));
00893 //     int q = 0, p = 0;
00894 //     { idx_bloop2(ispi, dispi, int, ispj, dispj, int) {
00895 //      p = 0;
00896 //      { idx_bloop2(di, ispi, int, dj, ispj, int) {
00897 //          di.set(my0 * p + my1 * q + my2 * p * q + my3);
00898 //          dj.set(mx0 * p + mx1 * q + mx2 * p * q + mx3);
00899 //          p++;
00900 //        }}
00901 //      q++;
00902 //       }}
00903 //   }
00904 
00905   template <typename T>
00906   idx<T> image_rotscale(idx<T> &src, double sx, double sy, double dx,
00907                         double dy, double angle, double coeff, T bg_val) {
00908     if (!src.contiguousp())
00909       eblerror("image must be contiguous");
00910     double q = 1000;
00911     double coeff_inv = 1/coeff;
00912     double sa = q*sin(angle * 0.017453292);
00913     double ca = q*cos(angle * 0.017453292);
00914     double ca_plus_sa = coeff_inv * (sa + ca);
00915     double ca_minus_sa = coeff_inv * (ca - sa);
00916     float x1 = sy - ca_plus_sa;
00917     float y1 = sx - ca_minus_sa;
00918     float x2 = sy + ca_minus_sa;
00919     float y2 = sx - ca_plus_sa;
00920     float x3 = sy + ca_plus_sa;
00921     float y3 = sx + ca_minus_sa;
00922     float x4 = sy - ca_minus_sa;
00923     float y4 = sx + ca_plus_sa;
00924     float p1 = dy - q;
00925     float q1 = dx - q;
00926     float p3 = dy + q;
00927     float q3 = dx + q;
00928     idx<T> out(src.get_idxdim());
00929     idx<T> bg(src.dim(0));
00930     idx_fill(bg, bg_val);
00931     image_warp_quad(src, out, bg, 1, x1, y1, x2, y2, x3, y3, x4, y4,
00932                     p1, q1, p3, q3);
00933     return out;
00934   }
00935 
00936   template <typename T>
00937   idx<T> image_rotate(idx<T> &src, double angle, float ch, float cw, T bg_val) {
00938     if (!src.contiguousp())
00939       eblerror("image must be contiguous");
00940     if (ch == -1) ch = src.dim(0) / (float) 2;
00941     if (cw == -1) cw = src.dim(1) / (float) 2;
00942     idx<T> rotated(src.get_idxdim());
00943     double q = 1000;
00944     double sa = q*sin(angle * 0.017453292);
00945     double ca = q*cos(angle * 0.017453292);
00946     double ca_plus_sa = sa + ca;
00947     double ca_minus_sa = ca - sa;
00948     float x1 = cw - ca_plus_sa;
00949     float y1 = ch - ca_minus_sa;
00950     float x2 = cw + ca_minus_sa;
00951     float y2 = ch - ca_plus_sa;
00952     float x3 = cw + ca_plus_sa;
00953     float y3 = ch + ca_minus_sa;
00954     float x4 = cw - ca_minus_sa;
00955     float y4 = ch + ca_plus_sa;
00956     float p1 = cw - q;
00957     float q1 = ch - q;
00958     float p3 = cw + q;
00959     float q3 = ch + q;
00960     idx<T> bg(src.dim(0));
00961     idx_fill(bg, bg_val);
00962     image_warp_quad(src, rotated, bg, 1, x1, y1, x2, y2, x3, y3, x4, y4,
00963                     p1, q1, p3, q3);
00964     return rotated;
00965   }
00966 
00968   // Drawing
00969 
00970   template <typename T> void image_draw_box(idx<T> &img, T val,
00971                                         uint x, uint y, uint dx, uint dy) {
00972     idx_checkorder1(img, 2);
00973     for (unsigned int i = x; i < x + dx; ++i) {
00974       img.set(val, i, y);
00975       img.set(val, i, y + dy);
00976     }
00977     for (unsigned int j = y; j < y + dy; ++j) {
00978       img.set(val, x, j);
00979       img.set(val, x + dx, j);
00980     }
00981   }
00982 
00984   // Filters
00985 
00986   template <typename T>
00987   void image_mexican_filter(idx<T> &in, idx<T> &out, double s, int n,
00988                             idx<T> *filter_, idx<T> *tmp_) {
00989     idx<T> filter = filter_ ? *filter_ : create_mexican_hat<T>(s, n);
00990     idxdim d(in);
00991     idx<T> tmp = tmp_ ? *tmp_ : idx<T>(d);
00992     idx_checkorder3(in, 2, filter, 2, out, 2);
00993     image_apply_filter(in, out, filter, &tmp);
00994   }
00995 
00996   // TODO: handle empty sides
00997   // TODO: check for tmp size incompatibilities
00998   template <typename T>
00999   void image_global_normalization(idx<T> &in) {
01000     idx_std_normalize(in, in);
01001     // problem with code below: it separates by channels which are assumed to
01002     // be in last dimension. this can lead to nasty bugs.
01003     // switch (in.order()) {
01004     // case 2:
01005     //   idx_std_normalize(in, in); // zero-mean and divide by standard deviation
01006     //   break ;
01007     // case 3:
01008     //   // normalize layer by layer
01009     //   { idx_eloop1(i, in, T) {
01010     //  idx_std_normalize(i, i); // zero-mean and divide by standard deviation
01011     //  }}
01012     //   break ;
01013     // default:
01014     //   eblerror("image_global_normalization: dimension not implemented");
01015     // }
01016   }
01017 
01018   // TODO: handle empty sides
01019   // TODO: check for tmp size incompatibilities
01020   // TODO: cleanup
01021   template <typename T>
01022   void image_local_normalization(idx<T> &in, idx<T> &out, int n) {
01023     if (in.order() != 2 || out.order() != 2)
01024       eblerror("this function only accepts 2D inputs, but got " << in
01025                << " and " << out);
01026     // 1. create normalized gaussian kernel (kernel / sum(kernel))
01027     idx<T> kernel = create_gaussian_kernel<T>(n);
01028     idx<T> tmp(in.dim(0) + n - 1, in.dim(1) + n - 1);
01029     idx<T> tmp2 = tmp.narrow(0, in.dim(0), (intg) floor((float) n / 2));
01030     tmp2 = tmp2.narrow(1, in.dim(1), (intg) floor((float) n / 2));
01031     idx<T> tmp3(n, n);
01032     idxdim d(in);
01033     idx<T> tmp4(d);
01034     idx<T> tmp5(d);
01035 
01036     // sum_j (w_j * in_j)
01037     idx<T> out1 = image_filter(in, kernel);
01038     idx<T> in2 = in.narrow(0, out1.dim(0), (intg)floor((float)kernel.dim(0)/2));
01039     in2 = in2.narrow(1, out1.dim(1), (intg) floor((float)kernel.dim(1)/2));
01040     idxdim outd(out1);
01041     idx<T> out2(outd);
01042     // in - mean
01043     idx_sub(in2, out1, out1);
01044     // (in - mean)^2
01045     idx_mul(out1, out1, out2);
01046     // sum_j (w_j * (in - mean)^2)
01047     idx<T> out3 = image_filter(out2, kernel);
01048     // sqrt(sum_j (w_j (in - mean)^2))
01049     idx_sqrt(out3, out3);    
01050     // std(std < 1) = 1
01051     idx_threshold(out3, (T)1.0);
01052     // 1/std
01053     idx_inv(out3, out3);
01054     // out = (in - mean) / std
01055     idx<T> out4 = out1.narrow(0, out3.dim(0), 
01056                               (intg) floor((float)kernel.dim(0)/2));
01057     out4 = out4.narrow(1, out3.dim(1), (intg) floor((float)kernel.dim(1)/2));
01058     idx_mul(out4, out3, out3);
01059     // finally copy result centered on an image the same size as in
01060     idx<T> out5 = out.narrow(0, out3.dim(0), (out.dim(0) - out3.dim(0)) / 2);
01061     out5 = out5.narrow(1, out3.dim(1), (out.dim(1) - out3.dim(1)) / 2);
01062     idx_clear(out);
01063     idx_copy(out3, out5);
01064     
01065 //     // in - mean
01066 //     idx_sub(in, tmp5, tmp5);
01067 //     // (in - mean)^2
01068 //     idx_mul(tmp5, tmp5, tmp4);
01069 //     // sum_j (w_j * (in - mean)^2)
01070 //     image_apply_filter(tmp4, out, kernel, &tmp);
01071 //     // sqrt(sum_j (w_j (in - mean)^2))
01072 //     idx_sqrt(out, out);    
01073 //     // std(std < 1) = 1
01074 //     idx_threshold(out, (T)1.0, out);
01075 //     // 1/std
01076 //     idx_inv(out, out);
01077 //     // out = (in - mean) / std
01078 //     idx_mul(tmp5, out, out);
01079   }
01080 
01081   // TODO: get rid of this function
01082   template <typename T>
01083   void image_apply_filter(idx<T> &in, idx<T> &out, idx<T> &filter,
01084                           idx<T> *tmp_) {
01085     idxdim d(in);
01086     if ((out.dim(0) != d.dim(0)) || (out.dim(1) != d.dim(1)))
01087       out.resize(d);
01088     idx_clear(out);
01089     idx<T> tmp = out.narrow(0, in.dim(0) - filter.dim(0) + 1,
01090                             (intg) floor((float)filter.dim(0)/2));
01091     tmp = tmp.narrow(1, in.dim(1) - filter.dim(1) + 1, 
01092                      (intg) floor((float)filter.dim(1)/2));
01093     idx_2dconvol(in, filter, tmp);   
01094     
01095 //     // compute sizes of the temporary buffer
01096 //     d.setdim(0, in.dim(0) + filter.dim(0) - 1);
01097 //     d.setdim(1, in.dim(1) + filter.dim(1) - 1);
01098 //     idx<T> tmp;
01099 //     if (tmp_) {
01100 //       tmp = *tmp_;
01101 //       if ((tmp.dim(0) != d.dim(0)) || (tmp.dim(1) != d.dim(1))) {
01102 //      tmp.resize(d);
01103 //      *tmp_ = tmp;
01104 //       }
01105 //     } else
01106 //       tmp = idx<T>(d);
01107 //     // copy input into temporary buffer
01108 //     idx_clear(tmp);
01109 //     idx<T> tmp2 = tmp.narrow(0, in.dim(0), floor(filter.dim(0)/2));
01110 //     tmp2 = tmp2.narrow(1, in.dim(1), floor(filter.dim(1)/2));
01111 //     idx_copy(in, tmp2);
01112 //     idx_2dconvol(tmp, filter, out);
01113   }
01114   
01115   template <typename T>
01116   idx<T> image_filter(idx<T> &in, idx<T> &filter) {
01117     // check that image is bigger than filter
01118     if ((in.dim(0) < filter.dim(0)) ||
01119         (in.dim(1) < filter.dim(1))) {
01120       cerr << "error: image " << in << " is too small to be convolved with ";
01121       cerr << filter << " filter." << endl;
01122       eblerror("too small image for convolution");
01123     }
01124     idxdim d(in);
01125     d.setdim(0, in.dim(0) - filter.dim(0) + 1);
01126     d.setdim(1, in.dim(1) - filter.dim(1) + 1);
01127     idx<T> out(d);
01128     idx_clear(out);
01129     idx_2dconvol(in, filter, out);
01130     return out;
01131   }
01132     
01133   // deformations //////////////////////////////////////////////////////////////
01134 
01135   template <typename T>
01136   void image_deformation_ranperspective(idx<T> &in, idx<T> &out,
01137                                         int hrange, int wrange, T background) {
01138     idx<float> bg(1);
01139     bg.set(background, 0);
01140     float x1diff = (float) drand( wrange/2, -wrange/2);
01141     float y1diff = (float) drand( hrange/2, -hrange/2);
01142     float x2diff = (float) drand(-wrange/2,  wrange/2);
01143     float y2diff = (float) drand( hrange/2, -hrange/2);
01144     float x3diff = (float) drand(-wrange/2,  wrange/2);
01145     float y3diff = (float) drand(-hrange/2,  hrange/2);
01146     float x4diff = (float) drand( wrange/2, -wrange/2);
01147     float y4diff = (float) drand(-hrange/2,  hrange/2);
01148     image_warp_quad(in, out, bg, 1,
01149                     -.5 + x1diff, -.5 + y1diff, // x1 y1
01150                     in.dim(1) -.5 + x2diff, -.5 + y2diff, // x2 y2
01151                     in.dim(1) -.5 + x3diff, in.dim(0) -.5 + y3diff, // x3 y3
01152                     -.5 + x4diff, in.dim(0) - .5 + y4diff, // x4 y4
01153                     -.5, -.5, in.dim(1) - .5, in.dim(0) - .5);
01154   }
01155 
01156   template <typename T>
01157   idx<float> image_deformation_flow(idx<T> &in, float th, float tw, 
01158                                     float sh, float sw, float deg,
01159                                     float shh, float shw,
01160                                     uint elsize, float elcoeff,
01161                                     T background) {
01162     idxdim d(in);
01163     d.remove_dim(2);
01164     idx<float> grid = create_grid(d);
01165     idx<float> flow(grid.get_idxdim());
01166     idx<T> bg(in.dim(2));
01167     idx_fill(bg, background);
01168     idx_clear(flow);
01169 
01170 
01171     if (th != 0 || tw != 0) translation_flow(grid, flow, th, tw);
01172     if (deg != 0) rotation_flow(grid, flow, deg);
01173     if (th != 1 || tw != 1) scale_flow(grid, flow, sh, sw);
01174     if (shh != 0 || shw != 0) shear_flow(grid, flow, shh, shw);
01175     if (elsize > 0) elastic_flow(flow, elsize, elcoeff);
01176 
01177     // affine_flow(grid, flow, 0, 0, sh, sw, 0, 0, deg);
01178     return flow;
01179   }
01180   
01181   template <typename T>
01182   void image_deformation(idx<T> &in, idx<T> &out, float th, float tw,
01183                          float sh, float sw, float deg,
01184                          float shh, float shw, uint elsize, float elcoeff,
01185                          T background) {
01186     idx<float> flow =
01187       image_deformation_flow(in, th, tw, sh, sw, deg, shh, shw, elsize, elcoeff,
01188                              background);
01189     // apply flow
01190     image_warp_flow(in, out, flow);
01191   }
01192   
01193 } // end namespace ebl
01194 
01195 #endif /* IMAGE_HPP_ */