PTdecode/CImg-1.3.0/examples/curve_editor.cpp

Wed, 05 Aug 2009 15:00:54 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 15:00:54 +0100
changeset 12
96e1df9bd27c
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

small changes to hexdump code to stop a gcc warning on platforms where sizeof(int) != sizeof(int*) e.g. x86_64

     1 /*
     2  #
     3  #  File        : curve_editor.cpp
     4  #                ( C++ source file )
     5  #
     6  #  Description : A simple user interface to construct 2D spline curves.
     7  #                This file is a part of the CImg Library project.
     8  #                ( http://cimg.sourceforge.net )
     9  #
    10  #  Copyright   : David Tschumperle
    11  #                ( http://www.greyc.ensicaen.fr/~dtschump/ )
    12  #
    13  #  License     : CeCILL v2.0
    14  #                ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
    15  #
    16  #  This software is governed by the CeCILL  license under French law and
    17  #  abiding by the rules of distribution of free software.  You can  use,
    18  #  modify and/ or redistribute the software under the terms of the CeCILL
    19  #  license as circulated by CEA, CNRS and INRIA at the following URL
    20  #  "http://www.cecill.info".
    21  #
    22  #  As a counterpart to the access to the source code and  rights to copy,
    23  #  modify and redistribute granted by the license, users are provided only
    24  #  with a limited warranty  and the software's author,  the holder of the
    25  #  economic rights,  and the successive licensors  have only  limited
    26  #  liability.
    27  #
    28  #  In this respect, the user's attention is drawn to the risks associated
    29  #  with loading,  using,  modifying and/or developing or reproducing the
    30  #  software by the user in light of its specific status of free software,
    31  #  that may mean  that it is complicated to manipulate,  and  that  also
    32  #  therefore means  that it is reserved for developers  and  experienced
    33  #  professionals having in-depth computer knowledge. Users are therefore
    34  #  encouraged to load and test the software's suitability as regards their
    35  #  requirements in conditions enabling the security of their systems and/or
    36  #  data to be ensured and,  more generally, to use and operate it in the
    37  #  same conditions as regards security.
    38  #
    39  #  The fact that you are presently reading this means that you have had
    40  #  knowledge of the CeCILL license and that you accept its terms.
    41  #
    42 */
    44 #include "CImg.h"
    45 using namespace cimg_library;
    47 // The lines below are necessary when using a non-standard compiler as visualcpp6.
    48 #ifdef cimg_use_visualcpp6
    49 #define std
    50 #endif
    51 #ifdef min
    52 #undef min
    53 #undef max
    54 #endif
    56 //---------------
    57 // Main procedure
    58 //---------------
    59 int main(int argc, char **argv) {
    61   // Read command line parameters
    62   //-----------------------------
    63   cimg_usage("2D Spline Curve Editor");
    64   const char *file_i       = cimg_option("-i",(char*)0,"Input image");
    65   const float contrast     = cimg_option("-contrast",0.6f,"Image contrast");
    66   const char *file_ip      = cimg_option("-ip",(char*)0,"Input control points");
    67   const char *file_oc      = cimg_option("-oc",(char*)0,"Output curve points");
    68   const char *file_op      = cimg_option("-op",(char*)0,"Output control points");
    69   const char *file_od      = cimg_option("-od",(char*)0,"Output distance function");
    70   bool interp              = cimg_option("-poly",true,"Use polynomial interpolation");
    71   bool closed              = cimg_option("-closed",true,"Closed curve");
    72   bool show_tangents       = cimg_option("-tangents",false,"Show tangents");
    73   bool show_points         = cimg_option("-points",true,"Show control points");
    74   bool show_outline        = cimg_option("-outline",true,"Show polygon outline");
    75   bool show_indices        = cimg_option("-indices",true,"Show points indices");
    76   bool show_coordinates    = cimg_option("-coords",false,"Show points coordinates");
    77   const float precision = cimg_option("-prec",0.05f,"Precision of curve discretization");
    79   // Init image data
    80   //-----------------
    81   const unsigned char yellow[] = { 255,255,0 }, white[] = { 255,255,255 }, green[] = { 0,255,0 },
    82                       red[] = { 255,0,50 }, purple[] = { 255,100,255 }, black[] = { 0,0,0 };
    83   CImg<unsigned char> img0, img, help_img;
    84   if (file_i) {
    85     std::fprintf(stderr,"\n - Load input image '%s' : ",cimg::basename(file_i));
    86     img0 = CImg<>(file_i).normalize(0,255.0f*contrast);
    87     std::fprintf(stderr,"Size = %dx%dx%dx%d \n",img0.dimx(),img0.dimy(),img0.dimz(),img0.dimv());
    88     img0.resize(-100,-100,1,3);
    89   }
    90   else {
    91     std::fprintf(stderr,"\n - No input image specified, use default 512x512 image.\n");
    92     img0.assign(512,512,1,3,0).draw_grid(32,32,0,0,false,false,green,0.4f,0xCCCCCCCC,0xCCCCCCCC);
    93   }
    95   help_img.assign(270,160,1,3,0).
    96     draw_text(5,5,
    97               "------------------------------------------\n"
    98               "2D Curve Editor\n"
    99               "------------------------------------------\n"
   100               "Left button : Create or move control point\n"
   101               "Right button : Delete control point\n"
   102               "Spacebar : Switch interpolation\n"
   103               "Key 'C' : Switch open/closed mode\n"
   104               "Key 'T' : Show/hide tangents\n"
   105               "Key 'P' : Show/hide control points\n"
   106               "Key 'O' : Show/hide polygon outline\n"
   107               "Key 'N' : Show/hide points indices\n"
   108               "Key 'X' : Show/hide points coordinates\n"
   109               "Key 'H' : Show/hide this help\n"
   110               "Key 'S' : Save control points\n"
   111               "Key 'R' : Reset curve\n",
   112               green);
   113   CImgDisplay disp(img0,"2D Curve Editor",0);
   114   CImgList<float> points, curve;
   115   bool moving = false;
   116   bool help = !file_i;
   118   if (file_ip) {
   119     std::fprintf(stderr," - Load input control points '%s' : ",cimg::basename(file_ip));
   120     points = CImg<>(file_ip).transpose().get_split('x');
   121     std::fprintf(stderr," %u points\n",points.size);
   122   }
   124   // Enter user loop
   125   //-----------------
   126   while (!disp.is_closed && !disp.is_keyESC && !disp.is_keyQ) {
   128     // Handle mouse manipulation
   129     //---------------------------
   130     const float
   131       x = disp.mouse_x*(float)img0.dimx()/disp.dimx(),
   132       y = disp.mouse_y*(float)img0.dimy()/disp.dimy();
   133     const unsigned int
   134       button = disp.button;
   136     if (points && button && x>=0 && y>=0) {
   138       // Find nearest point and nearest segment
   139       float dmin_pt = 1e10f, dmin_seg = dmin_pt;
   140       unsigned int p_pt = 0, p_seg = 0;
   141       cimglist_for(points,p) {
   142         const unsigned int
   143           pnext = closed?(p+1)%points.size:(p+1<points.size?p+1:p);
   144         const float
   145           xp = points(p,0),
   146           yp = points(p,1),
   147           xm = 0.5f*(xp + points(pnext,0)),
   148           ym = 0.5f*(yp + points(pnext,1));
   149         const float
   150           d_pt  = (xp-x)*(xp-x) + (yp-y)*(yp-y),
   151           d_seg = (xm-x)*(xm-x) + (ym-y)*(ym-y);
   152         if (d_pt<dmin_pt)   { dmin_pt = d_pt; p_pt = p; }
   153         if (d_seg<dmin_seg) { dmin_seg = d_seg; p_seg = p; }
   154       }
   156       // Handle button
   157       if (button&1) {
   158         if (dmin_pt<100 || moving) { points(p_pt,0) = x; points(p_pt,1) = y; }
   159         else points.insert(CImg<>::vector(x,y),p_seg+1);
   160         moving = true;
   161       }
   162       if (button&2 && dmin_pt<100) {
   163         if (points.size>3) points.remove(p_pt);
   164         else points.assign();
   165         disp.button=0;
   166       }
   167     }
   168     if (!button) moving = false;
   170     if (disp.key) {
   171       switch (disp.key) {
   172       case cimg::keySPACE: interp = !interp; break;
   173       case cimg::keyC: closed = !closed; break;
   174       case cimg::keyT: show_tangents = !show_tangents; break;
   175       case cimg::keyP: show_points = !show_points; break;
   176       case cimg::keyO: show_outline = !show_outline; break;
   177       case cimg::keyN: show_indices = !show_indices; break;
   178       case cimg::keyX: show_coordinates = !show_coordinates; break;
   179       case cimg::keyR: points.assign(); break;
   180       case cimg::keyH: help = !help; break;
   181       case cimg::keyS: {
   182         const char *filename = file_op?file_op:"curve_points.dlm";
   183         std::fprintf(stderr," - Save control points in '%s'\n",filename);
   184         points.get_append('x').transpose().save(filename);
   185       } break;
   186       }
   187       disp.key = 0;
   188     }
   190     // Init list of points if empty
   191     //------------------------------
   192     if (!points) {
   193       const float
   194         x0 = img0.dimx()/4.0f,
   195         y0 = img0.dimy()/4.0f,
   196         x1 = img0.dimx()-x0,
   197         y1 = img0.dimy()-y0;
   198       points.insert(CImg<>::vector(x0,y0)).
   199         insert(CImg<>::vector(x1,y0)).
   200         insert(CImg<>::vector(x1,y1)).
   201         insert(CImg<>::vector(x0,y1));
   202     }
   204     // Estimate curve tangents
   205     //-------------------------
   206     CImg<> tangents(points.size,2);
   207     { cimglist_for(points,p) {
   208       const unsigned int
   209         p0 = closed?(p+points.size-1)%points.size:(p?p-1:0),
   210         p1 = closed?(p+1)%points.size:(p+1<points.size?p+1:p);
   211       const float
   212         x  = points(p,0),
   213         y  = points(p,1),
   214         x0 = points(p0,0),
   215         y0 = points(p0,1),
   216         x1 = points(p1,0),
   217         y1 = points(p1,1),
   218         u0 = x-x0,
   219         v0 = y-y0,
   220         n0 = 1e-8f + (float)std::sqrt(u0*u0+v0*v0),
   221         u1 = x1-x,
   222         v1 = y1-y,
   223         n1 = 1e-8f + (float)std::sqrt(u1*u1+v1*v1),
   224         u = u0/n0 + u1/n1,
   225         v = v0/n0 + v1/n1,
   226         n = 1e-8f + (float)std::sqrt(u*u+v*v),
   227         fact = 0.5f*(n0+n1);
   228       tangents(p,0) = fact*u/n;
   229       tangents(p,1) = fact*v/n;
   230     }}
   232     // Estimate 3th-order polynomial interpolation
   233     //---------------------------------------------
   234     curve.assign();
   235     const unsigned int pmax = points.size-(closed?0:1);
   236     for (unsigned int p0=0; p0<pmax; p0++) {
   237       const unsigned int
   238         p1 = closed?(p0+1)%points.size:(p0+1<points.size?p0+1:p0);
   239       const float
   240         x0 = points(p0,0),
   241         y0 = points(p0,1),
   242         x1 = points(p1,0),
   243         y1 = points(p1,1);
   244       float ax=0, bx=0, cx=0, dx=0, ay=0, by=0, cy=0, dy=0;
   245       if (interp) {
   246         const float
   247           u0 = tangents(p0,0),
   248           v0 = tangents(p0,1),
   249           u1 = tangents(p1,0),
   250           v1 = tangents(p1,1);
   251         ax = 2*(x0-x1)+u0+u1;
   252         bx = 3*(x1-x0)-2*u0-u1;
   253         cx = u0;
   254         dx = x0;
   255         ay = 2*(y0-y1)+v0+v1;
   256         by = 3*(y1-y0)-2*v0-v1;
   257         cy = v0;
   258         dy = y0;
   259       } else {
   260         ax = ay = bx = by = 0;
   261         dx = x0;
   262         dy = y0;
   263         cx = (x1-x0);
   264         cy = (y1-y0);
   265       }
   266       const float tmax = 1+precision;
   267       for (float t=0; t<tmax; t+=precision) {
   268         const float
   269           xt = ax*t*t*t + bx*t*t + cx*t + dx,
   270           yt = ay*t*t*t + by*t*t + cy*t + dy;
   271         curve.insert(CImg<>::vector(xt,yt));
   272       }
   273     }
   275     // Draw curve and display image
   276     //-------------------------------
   277     const float
   278       factx = (float)disp.dimx()/img0.dimx(),
   279       facty = (float)disp.dimy()/img0.dimy();
   280     img = img0.get_resize(disp.dimx(),disp.dimy());
   281     if (help) img.draw_image(help_img,0.6f);
   282     if (interp && show_outline) {
   283       CImg<> npoints = points.get_append('x');
   284       npoints.get_shared_line(0)*=factx;
   285       npoints.get_shared_line(1)*=facty;
   286       img.draw_polygon(npoints,red,0.4f);
   287       if (closed) img.draw_polygon(npoints,yellow,0.8f,0x11111111);
   288       else img.draw_line(npoints,yellow,0.8f,0x11111111);
   289     }
   290     CImg<> ncurve = curve.get_append('x');
   291     ncurve.get_shared_line(0)*=factx;
   292     ncurve.get_shared_line(1)*=facty;
   293     if (closed) img.draw_polygon(ncurve,white,1.0f,~0U);
   294     else img.draw_line(ncurve,white);
   296     if (show_points) cimglist_for(points,p) {
   297       const float
   298         x = points(p,0)*factx,
   299         y = points(p,1)*facty;
   300       if (show_tangents) {
   301         const float
   302           u = tangents(p,0),
   303           v = tangents(p,1),
   304           n = 1e-8f + (float)std::sqrt(u*u+v*v),
   305           nu = u/n,
   306           nv = v/n;
   307         img.draw_arrow((int)(x-15*nu),(int)(y-15*nv),(int)(x+15*nu),(int)(y+15*nv),green);
   308       }
   309       if (show_indices) img.draw_text((int)x,(int)(y-16),"%d",purple,black,1,6,p);
   310       if (show_coordinates) img.draw_text((int)(x-24),(int)(y+8),"(%d,%d)",yellow,black,0.5f,6,(int)points(p,0),(int)points(p,1));
   311       img.draw_circle((int)x,(int)y,3,red,0.7f);
   312     }
   314     img.display(disp);
   315     disp.wait();
   317     if (disp.is_resized) disp.resize(false);
   318   }
   320   // Save output result and exit
   321   //-----------------------------
   322   if (file_op) {
   323     std::fprintf(stderr," - Save control points in '%s'\n",cimg::basename(file_op));
   324     points.get_append('x').transpose().save(file_op);
   325   }
   326   if (file_oc) {
   327     std::fprintf(stderr," - Save curve points in '%s'\n",cimg::basename(file_oc));
   328     curve.get_append('x').transpose().save(file_oc);
   329   }
   330   if (file_od) {
   331     std::fprintf(stderr," - Computing distance function, please wait...."); std::fflush(stderr);
   332     CImg<> ncurve = (closed?(+curve).insert(curve[0]):curve).get_append('x');
   333     const float zero = 0.0f, one = 1.0f;
   334     CImg<> distance =
   335       CImg<>(img0.dimx(),img0.dimy(),1,1,-1.0f).draw_line(ncurve,&zero).draw_fill(0,0,&one).
   336       distance_hamilton(200);
   337     std::fprintf(stderr,"\n - Save distance function in '%s'\n",cimg::basename(file_od));
   338     distance.save(file_od);
   339   }
   341   std::fprintf(stderr," - Exit.\n");
   342   std::exit(0);
   343   return 0;
   344 }