PTdecode/CImg-1.3.0/examples/image_registration.cpp

Mon, 03 Aug 2009 23:41:04 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Mon, 03 Aug 2009 23:41:04 +0100
changeset 11
69416826d18c
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

added dep/*.d and obj/*.o to hgignore

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : image_registration.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : Compute a motion field between two images,
philpem@5 7 # with a multiscale and variational algorithm.
philpem@5 8 # This file is a part of the CImg Library project.
philpem@5 9 # ( http://cimg.sourceforge.net )
philpem@5 10 #
philpem@5 11 # Copyright : David Tschumperle
philpem@5 12 # ( http://www.greyc.ensicaen.fr/~dtschump/ )
philpem@5 13 #
philpem@5 14 # License : CeCILL v2.0
philpem@5 15 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
philpem@5 16 #
philpem@5 17 # This software is governed by the CeCILL license under French law and
philpem@5 18 # abiding by the rules of distribution of free software. You can use,
philpem@5 19 # modify and/ or redistribute the software under the terms of the CeCILL
philpem@5 20 # license as circulated by CEA, CNRS and INRIA at the following URL
philpem@5 21 # "http://www.cecill.info".
philpem@5 22 #
philpem@5 23 # As a counterpart to the access to the source code and rights to copy,
philpem@5 24 # modify and redistribute granted by the license, users are provided only
philpem@5 25 # with a limited warranty and the software's author, the holder of the
philpem@5 26 # economic rights, and the successive licensors have only limited
philpem@5 27 # liability.
philpem@5 28 #
philpem@5 29 # In this respect, the user's attention is drawn to the risks associated
philpem@5 30 # with loading, using, modifying and/or developing or reproducing the
philpem@5 31 # software by the user in light of its specific status of free software,
philpem@5 32 # that may mean that it is complicated to manipulate, and that also
philpem@5 33 # therefore means that it is reserved for developers and experienced
philpem@5 34 # professionals having in-depth computer knowledge. Users are therefore
philpem@5 35 # encouraged to load and test the software's suitability as regards their
philpem@5 36 # requirements in conditions enabling the security of their systems and/or
philpem@5 37 # data to be ensured and, more generally, to use and operate it in the
philpem@5 38 # same conditions as regards security.
philpem@5 39 #
philpem@5 40 # The fact that you are presently reading this means that you have had
philpem@5 41 # knowledge of the CeCILL license and that you accept its terms.
philpem@5 42 #
philpem@5 43 */
philpem@5 44
philpem@5 45 #include "CImg.h"
philpem@5 46 using namespace cimg_library;
philpem@5 47
philpem@5 48 // The lines below are necessary when using a non-standard compiler as visualcpp6.
philpem@5 49 #ifdef cimg_use_visualcpp6
philpem@5 50 #define std
philpem@5 51 #endif
philpem@5 52 #ifdef min
philpem@5 53 #undef min
philpem@5 54 #undef max
philpem@5 55 #endif
philpem@5 56
philpem@5 57 #ifndef cimg_imagepath
philpem@5 58 #define cimg_imagepath "img/"
philpem@5 59 #endif
philpem@5 60
philpem@5 61 // animate_warp() : Create warping animation from two images and a motion field
philpem@5 62 //----------------
philpem@5 63 void animate_warp(const CImg<unsigned char>& src, const CImg<unsigned char>& dest, const CImg<>& u,
philpem@5 64 const bool morph, const bool imode, const char *filename,int nb, CImgDisplay& disp) {
philpem@5 65 CImg<unsigned char> visu = CImgList<unsigned char>(src,dest,src).get_append('x'), warp(src);
philpem@5 66 float t=0;
philpem@5 67 for (unsigned int iter=0; !disp || (!disp.is_closed && !disp.is_keyQ); iter++) {
philpem@5 68 if (morph) cimg_forXYV(warp,x,y,k) {
philpem@5 69 const float dx = u(x,y,0), dy = u(x,y,1),
philpem@5 70 I1 = (float)src.linear_atXY(x-t*dx, y-t*dy, k),
philpem@5 71 I2 = (float)dest.linear_atXY(x+(1-t)*dx,y+(1-t)*dy,k);
philpem@5 72 warp(x,y,k) = (unsigned char)((1-t)*I1 + t*I2);
philpem@5 73 } else cimg_forXYV(warp,x,y,k) {
philpem@5 74 const float dx = u(x,y,0), dy = u(x,y,1), I1 = (float)src.linear_atXY(x-t*dx, y-t*dy, 0,k);
philpem@5 75 warp(x,y,k) = (unsigned char)I1;
philpem@5 76 }
philpem@5 77 if (disp) visu.draw_image(2*src.dimx(),warp).display(disp.resize().wait(30));
philpem@5 78 if (filename && *filename && (imode || (int)iter<nb)) {
philpem@5 79 std::fprintf(stderr,"\r > frame %d ",iter);
philpem@5 80 warp.save(filename,iter);
philpem@5 81 }
philpem@5 82 t+=1.0f/nb;
philpem@5 83 if (t<0) { t=0; nb=-nb; }
philpem@5 84 if (t>1) { t=1; nb=-nb; if (filename && *filename) std::exit(0); }
philpem@5 85 }
philpem@5 86 }
philpem@5 87
philpem@5 88 // get_warp() : Return the image src warped by the motion field u.
philpem@5 89 //------------
philpem@5 90 template<typename T> CImg<T> getwarp(const CImg<T>& src, const CImg<>& u) {
philpem@5 91 CImg<T> warp(src);
philpem@5 92 cimg_forXY(warp,x,y) warp(x,y) = (T)src.linear_atXY(x - u(x,y,0), y - u(x,y,1));
philpem@5 93 return warp;
philpem@5 94 }
philpem@5 95
philpem@5 96 // optmonoflow() : Register images for one scale ( semi-implicite PDE scheme ) between I2->I1
philpem@5 97 //---------------
philpem@5 98 CImg<> optmonoflow(const CImg<>& I1, const CImg<>& I2, const CImg<>& u0,
philpem@5 99 const float smooth, const float precision, CImgDisplay& disp) {
philpem@5 100
philpem@5 101 CImg<> u = u0.get_resize(I1.dimx(),I1.dimy(),1,2,3),dI(u);
philpem@5 102 CImg_3x3(I,float);
philpem@5 103 float dt=2,E=1e20f;
philpem@5 104
philpem@5 105 // compute first derivatives of I2
philpem@5 106 cimg_for3x3(I2,x,y,0,0,I) {
philpem@5 107 dI(x,y,0) = 0.5f*(Inc-Ipc);
philpem@5 108 dI(x,y,1) = 0.5f*(Icn-Icp);
philpem@5 109 }
philpem@5 110
philpem@5 111 // Main PDE iteration
philpem@5 112 for (unsigned int iter=0; iter<100000; iter++) {
philpem@5 113 std::fprintf(stderr,"\r- Iteration %d - E = %g",iter,E); std::fflush(stderr);
philpem@5 114 const float Eold = E;
philpem@5 115 E = 0;
philpem@5 116 cimg_for3XY(u,x,y) {
philpem@5 117 const float
philpem@5 118 X = x + u(x,y,0),
philpem@5 119 Y = y + u(x,y,1),
philpem@5 120 deltaI = (float)(I2.linear_atXY(X,Y) - I1(x,y));
philpem@5 121 float tmpf = 0;
philpem@5 122 cimg_forV(u,k) {
philpem@5 123 const float
philpem@5 124 ux = 0.5f*(u(_n1x,y,k)-u(_p1x,y,k)),
philpem@5 125 uy = 0.5f*(u(x,_n1y,k)-u(x,_p1y,k));
philpem@5 126 u(x,y,k) = (float)( u(x,y,k) +
philpem@5 127 dt*(
philpem@5 128 -deltaI*dI.linear_atXY(X,Y,k) +
philpem@5 129 smooth* ( u(_n1x,y,k) + u(_p1x,y,k) + u(x,_n1y,k) + u(x,_p1y,k) )
philpem@5 130 )
philpem@5 131 )/(1+4*smooth*dt);
philpem@5 132 tmpf += ux*ux + uy*uy;
philpem@5 133 }
philpem@5 134 E += deltaI*deltaI + smooth * tmpf;
philpem@5 135 }
philpem@5 136 if (cimg::abs(Eold-E)<precision) break;
philpem@5 137 if (Eold<E) dt*=0.5;
philpem@5 138 if (disp) disp.resize();
philpem@5 139 if (disp && disp.is_closed) std::exit(0);
philpem@5 140 if (disp && !(iter%300)) {
philpem@5 141 const unsigned char white = 255;
philpem@5 142 CImg<unsigned char> tmp = getwarp(I1,u).normalize(0,200);
philpem@5 143 tmp.resize(disp.dimx(),disp.dimy()).draw_quiver(u,&white,0.7f,15,-14,0).display(disp);
philpem@5 144 }
philpem@5 145 }
philpem@5 146 return u;
philpem@5 147 }
philpem@5 148
philpem@5 149 // optflow() : multiscale version of the image registration algorithm
philpem@5 150 //-----------
philpem@5 151 CImg<> optflow(const CImg<>& xsrc, const CImg<>& xdest,
philpem@5 152 const float smooth, const float precision, const unsigned int pnb_scale, CImgDisplay& disp) {
philpem@5 153 const CImg<>
philpem@5 154 src = xsrc.get_pointwise_norm(1).resize(xdest.dimx(),xdest.dimy(),1,1,3).normalize(0,1),
philpem@5 155 dest = xdest.get_pointwise_norm(1).resize(xdest.dimx(),xdest.dimy(),1,1,3).normalize(0,1);
philpem@5 156 CImg<> u = CImg<>(src.dimx(),src.dimy(),1,2).fill(0);
philpem@5 157
philpem@5 158 const unsigned int nb_scale = pnb_scale>0?pnb_scale:(unsigned int)(2*std::log((double)(cimg::max(src.dimx(),src.dimy()))));
philpem@5 159 for (int scale=nb_scale-1; scale>=0; scale--) {
philpem@5 160 const CImg<> I1 = src.get_resize((int)(src.dimx()/std::pow(1.5,scale)), (int)(src.dimy()/std::pow(1.5,scale)) ,1,1,3);
philpem@5 161 const CImg<> I2 = dest.get_resize((int)(src.dimx()/std::pow(1.5,scale)), (int)(src.dimy()/std::pow(1.5,scale)) ,1,1,3);
philpem@5 162 std::fprintf(stderr," * Scale %d\n",scale);
philpem@5 163 u*=1.5;
philpem@5 164 u = optmonoflow(I1,I2,u,smooth,(float)(precision/std::pow(2.25,1+scale)),disp);
philpem@5 165 std::fprintf(stderr,"\n");
philpem@5 166 }
philpem@5 167 return u;
philpem@5 168 }
philpem@5 169
philpem@5 170 /*------------------------
philpem@5 171
philpem@5 172 Main function
philpem@5 173
philpem@5 174 ------------------------*/
philpem@5 175
philpem@5 176 int main(int argc,char **argv) {
philpem@5 177
philpem@5 178 // Read command line parameters
philpem@5 179 cimg_usage("Compute an optical flow between two 2D images, and create a warped animation");
philpem@5 180 const char
philpem@5 181 *name_i1 = cimg_option("-i",cimg_imagepath "sh0r.pgm","Input Image 1 (Destination)"),
philpem@5 182 *name_i2 = cimg_option("-i2",cimg_imagepath "sh1r.pgm","Input Image 2 (Source)"),
philpem@5 183 *name_o = cimg_option("-o",(const char*)NULL,"Output 2D flow (inrimage)"),
philpem@5 184 *name_seq = cimg_option("-o2",(const char*)NULL,"Output Warping Sequence");
philpem@5 185 const float
philpem@5 186 smooth = cimg_option("-s",0.1f,"Flow Smoothness"),
philpem@5 187 precision = cimg_option("-p",0.9f,"Convergence precision");
philpem@5 188 const unsigned int
philpem@5 189 nb = cimg_option("-n",40,"Number of warped frames"),
philpem@5 190 nbscale = cimg_option("-scale",0,"Number of scales (0=auto)");
philpem@5 191 const bool
philpem@5 192 normalize = cimg_option("-equalize",true,"Histogram normalization of the images"),
philpem@5 193 morph = cimg_option("-m",true,"Morphing mode"),
philpem@5 194 imode = cimg_option("-c",true,"Complete interpolation (or last frame is missing)"),
philpem@5 195 dispflag = !cimg_option("-novisu",false,"Visualization");
philpem@5 196
philpem@5 197 // Init images and display
philpem@5 198 std::fprintf(stderr," - Init images.\n");
philpem@5 199 const CImg<>
philpem@5 200 src(name_i1),
philpem@5 201 dest(CImg<>(name_i2).resize(src,3)),
philpem@5 202 src_blur = normalize?src.get_blur(0.5f).equalize(256):src.get_blur(0.5f),
philpem@5 203 dest_blur = normalize?dest.get_blur(0.5f).equalize(256):dest.get_blur(0.5f);
philpem@5 204
philpem@5 205 CImgDisplay disp;
philpem@5 206 if (dispflag) {
philpem@5 207 unsigned int w = src.dimx(), h = src.dimy();
philpem@5 208 const unsigned int dmin = cimg::min(w,h), minsiz = 512;
philpem@5 209 if (dmin<minsiz) { w=w*minsiz/dmin; h=h*minsiz/dmin; }
philpem@5 210 const unsigned int dmax = cimg::max(w,h), maxsiz = 1024;
philpem@5 211 if (dmax>maxsiz) { w=w*maxsiz/dmax; h=h*maxsiz/dmax; }
philpem@5 212 disp.assign(w,h,"Estimated Motion",0);
philpem@5 213 }
philpem@5 214
philpem@5 215 // Run Motion estimation algorithm
philpem@5 216 std::fprintf(stderr," - Compute optical flow.\n");
philpem@5 217 const CImg<> u = optflow(src_blur,dest_blur,smooth,precision,nbscale,disp);
philpem@5 218 if (name_o) u.save(name_o);
philpem@5 219 u.print("Computed flow");
philpem@5 220
philpem@5 221 // Do morphing animation
philpem@5 222 std::fprintf(stderr," - Create warped animation.\n");
philpem@5 223 CImgDisplay disp2;
philpem@5 224 if (dispflag) {
philpem@5 225 unsigned int w = src.dimx(), h = src.dimy();
philpem@5 226 const unsigned int dmin = cimg::min(w,h), minsiz = 100;
philpem@5 227 if (dmin<minsiz) { w=w*minsiz/dmin; h=h*minsiz/dmin; }
philpem@5 228 const unsigned int dmax = cimg::max(w,h), maxsiz = 1024/3;
philpem@5 229 if (dmax>maxsiz) { w=w*maxsiz/dmax; h=h*maxsiz/dmax; }
philpem@5 230 disp2.assign(3*w,h,"Source/Destination images and Motion animation",0);
philpem@5 231 }
philpem@5 232
philpem@5 233 animate_warp(src.get_normalize(0,255),dest.get_normalize(0,255),u,morph,imode,name_seq,nb,disp2);
philpem@5 234
philpem@5 235 std::exit(0);
philpem@5 236 return 0;
philpem@5 237 }