PTdecode/CImg-1.3.0/examples/edge_explorer.cpp

changeset 5
1204ebf9340d
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/PTdecode/CImg-1.3.0/examples/edge_explorer.cpp	Mon Aug 03 14:09:20 2009 +0100
     1.3 @@ -0,0 +1,227 @@
     1.4 +/*
     1.5 + #
     1.6 + #  File        : edge_explorer.cpp
     1.7 + #                ( C++ source file )
     1.8 + #
     1.9 + #  Description : Real time edge detection while moving a ROI
    1.10 + #                (rectangle of interest) over the original image.
    1.11 + #                This file is a part of the CImg Library project.
    1.12 + #                ( http://cimg.sourceforge.net )
    1.13 + #
    1.14 + #  Copyright   : Orges Leka
    1.15 + #                ( oleka(at)students.uni-mainz.de )
    1.16 + #
    1.17 + #  License     : CeCILL v2.0
    1.18 + #                ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
    1.19 + #
    1.20 + #  This software is governed by the CeCILL  license under French law and
    1.21 + #  abiding by the rules of distribution of free software.  You can  use,
    1.22 + #  modify and/ or redistribute the software under the terms of the CeCILL
    1.23 + #  license as circulated by CEA, CNRS and INRIA at the following URL
    1.24 + #  "http://www.cecill.info".
    1.25 + #
    1.26 + #  As a counterpart to the access to the source code and  rights to copy,
    1.27 + #  modify and redistribute granted by the license, users are provided only
    1.28 + #  with a limited warranty  and the software's author,  the holder of the
    1.29 + #  economic rights,  and the successive licensors  have only  limited
    1.30 + #  liability.
    1.31 + #
    1.32 + #  In this respect, the user's attention is drawn to the risks associated
    1.33 + #  with loading,  using,  modifying and/or developing or reproducing the
    1.34 + #  software by the user in light of its specific status of free software,
    1.35 + #  that may mean  that it is complicated to manipulate,  and  that  also
    1.36 + #  therefore means  that it is reserved for developers  and  experienced
    1.37 + #  professionals having in-depth computer knowledge. Users are therefore
    1.38 + #  encouraged to load and test the software's suitability as regards their
    1.39 + #  requirements in conditions enabling the security of their systems and/or
    1.40 + #  data to be ensured and,  more generally, to use and operate it in the
    1.41 + #  same conditions as regards security.
    1.42 + #
    1.43 + #  The fact that you are presently reading this means that you have had
    1.44 + #  knowledge of the CeCILL license and that you accept its terms.
    1.45 + #
    1.46 +*/
    1.47 +
    1.48 +#include "CImg.h"
    1.49 +using namespace cimg_library;
    1.50 +
    1.51 +// The lines below are necessary when using a non-standard compiler as visualcpp6.
    1.52 +#ifdef cimg_use_visualcpp6
    1.53 +#define std
    1.54 +#endif
    1.55 +#ifdef min
    1.56 +#undef min
    1.57 +#undef max
    1.58 +#endif
    1.59 +
    1.60 +#ifndef cimg_imagepath
    1.61 +#define cimg_imagepath "img/"
    1.62 +#endif
    1.63 +
    1.64 +// Start main procedure
    1.65 +//-----------------------
    1.66 +int main(int argc, char** argv) {
    1.67 +
    1.68 +  // Usage of the program displayed at the command line
    1.69 +  cimg_usage("Real time edge detection with CImg. (c) Orges Leka");
    1.70 +
    1.71 +  // Read command line arguments
    1.72 +  // With cimg_option we can get a new name for the image which is to be loaded from the command line.
    1.73 +  const char* img_name =  cimg_option("-i", cimg_imagepath "lena.pgm","Input image.");
    1.74 +  double
    1.75 +    alpha = cimg_option("-a",1.0,"Blurring the gradient image."),
    1.76 +    thresL = cimg_option("-tl",13.5,"Lower thresholding used in Hysteresis."),
    1.77 +    thresH = cimg_option("-th",13.6,"Higher thresholding used in Hysteresis.");
    1.78 +  const unsigned int
    1.79 +    mode = cimg_option("-m",1,"Detection mode: 1 = Hysteresis, 2 = Gradient angle."),
    1.80 +    factor = cimg_option("-s",80,"Half-size of edge-explorer window.");
    1.81 +
    1.82 +  cimg_help("\nAdditional notes : user can press following keys on main display window :\n"
    1.83 +            "     - Left arrow : Decrease alpha.\n"
    1.84 +            "     - Right arrow : Increase alpha.\n");
    1.85 +
    1.86 +  // Construct a new image called 'edge' of size (2*factor,2*factor)
    1.87 +  // and of type 'unsigned char'.
    1.88 +  CImg<unsigned char> edge(2*factor,2*factor);
    1.89 +  CImgDisplay disp_edge(512,512,"Edge Explorer");
    1.90 +
    1.91 +  // Load the image with the name 'img_name' into the CImg 'img'.
    1.92 +  // and create a display window 'disp' for the image 'img'.
    1.93 +  const CImg<unsigned char> img(img_name);
    1.94 +  CImgDisplay disp(img,"Original Image");
    1.95 +
    1.96 +  // Begin main interaction loop.
    1.97 +  int x = 0, y = 0;
    1.98 +  bool redraw = false;
    1.99 +  while (!disp.is_closed && !disp.is_keyQ && !disp.is_keyESC) {
   1.100 +    disp.wait(100);
   1.101 +    if (disp.button&1) { alpha+=0.05; redraw = true; }
   1.102 +    if (disp.button&2) { alpha-=0.05; redraw = true; }
   1.103 +    if (disp.wheel) { alpha+=0.05*disp.wheel; disp.wheel = 0; redraw = true; }
   1.104 +    if (alpha<0) alpha = 0;
   1.105 +    if (disp_edge.is_resized) { disp_edge.resize(); redraw = true; }
   1.106 +    if (disp_edge.is_closed) disp_edge.show();
   1.107 +    if (disp.is_resized) disp.resize(disp);
   1.108 +    if (disp.mouse_x>=0) {
   1.109 +      x = disp.mouse_x; // Getting the current position of the mouse.
   1.110 +      y = disp.mouse_y; //
   1.111 +      redraw = true;    // The image should be redrawn.
   1.112 +    }
   1.113 +    if (redraw) {
   1.114 +      disp_edge.set_title("Edge explorer (alpha=%g)",alpha);
   1.115 +      const int
   1.116 +        x0 = x-factor, y0 = y-factor,  // These are the coordinates for the red rectangle
   1.117 +        x1 = x+factor, y1 = y+factor;  // to be drawn on the original image.
   1.118 +      const unsigned char
   1.119 +        red[3] = { 255,0,0 },          //
   1.120 +        black[3] = { 0,0,0 };          // Defining the colors we need for drawing.
   1.121 +
   1.122 +        (+img).draw_rectangle(x0,y0,x1,y1,red,1.0f,0x55555555U).display(disp);
   1.123 +        //^ We draw the red rectangle on the original window using 'draw_line'. Then we display the result via '.display(disp)' .
   1.124 +        //  Observe, that the color 'red' has to be of type 'const unsigned char',
   1.125 +        //  since the image 'img' is of type 'const CImg<unsigned char>'.
   1.126 +
   1.127 +        //'normalize' is used to get a greyscaled image.
   1.128 +        CImg<> visu_bw = CImg<>(img).get_crop(x0,y0,x1,y1).get_pointwise_norm().normalize(0,255).resize(-100,-100,1,2,2);
   1.129 +        // get_crop(x0,y0,x1,y1) gets the rectangle we are interested in.
   1.130 +
   1.131 +        edge.fill(255); // Background color in the edge-detection window is white.
   1.132 +
   1.133 +        // grad[0] is the gradient image of 'visu_bw' in x-direction.
   1.134 +        // grad[1] is the gradient image of 'visu_bw' in y-direction.
   1.135 +        CImgList<> grad(visu_bw.blur((float)alpha).normalize(0,255).get_gradient());
   1.136 +
   1.137 +        // To avoid unnecessary calculations in the image loops:
   1.138 +        const double
   1.139 +          pi = cimg::valuePI,
   1.140 +          p8 = pi/8.0, p38 = 3.0*p8,
   1.141 +          p58 = 5.0*p8, p78 = 7.0*p8;
   1.142 +
   1.143 +        cimg_forXY(visu_bw,s,t) {
   1.144 +          // We take s,t instead of x,y, since x,y are already used.
   1.145 +          // s corresponds to the x-ordinate of the pixel while t corresponds to the y-ordinate.
   1.146 +          if ( 1 <= s && s <= visu_bw.dimx()-1 && 1 <= t && t <=visu_bw.dimy()-1) { // if - good points
   1.147 +            double
   1.148 +              Gs = grad[0](s,t),                  //
   1.149 +              Gt = grad[1](s,t),                               //  The actual pixel is (s,t)
   1.150 +              Gst = cimg::abs(Gs) + cimg::abs(Gt),    //
   1.151 +              // ^-- For efficient computation we observe that |Gs|+ |Gt| ~=~ sqrt( Gs^2 + Gt^2)
   1.152 +              Gr, Gur, Gu, Gul, Gl, Gdl, Gd, Gdr;
   1.153 +            // ^-- right, up right, up, up left, left, down left, down, down right.
   1.154 +            double theta = std::atan2(Gt,Gs)+pi; // theta is from the interval [0,Pi]
   1.155 +            switch(mode) {
   1.156 +            case 1: // Hysterese is applied
   1.157 +              if (Gst>=thresH) { edge.draw_point(s,t,black); }
   1.158 +              else if (thresL <= Gst && Gst < thresH) {
   1.159 +                // Neighbourhood of the actual pixel:
   1.160 +                Gr = cimg::abs(grad[0](s+1,t)) + cimg::abs(grad[1](s+1,t)); // right
   1.161 +                Gl = cimg::abs(grad[0](s-1,t)) + cimg::abs(grad[1](s-1,t)); // left
   1.162 +                Gur = cimg::abs(grad[0](s+1,t+1)) + cimg::abs(grad[1](s+1,t+1)); // up right
   1.163 +                Gdl = cimg::abs(grad[0](s-1,t-1)) + cimg::abs(grad[1](s-1,t-1)); // down left
   1.164 +                Gu = cimg::abs(grad[0](s,t+1)) + cimg::abs(grad[1](s,t+1)); // up
   1.165 +                Gd = cimg::abs(grad[0](s,t-1)) + cimg::abs(grad[1](s,t-1)); // down
   1.166 +                Gul = cimg::abs(grad[0](s-1,t+1)) + cimg::abs(grad[1](s-1,t+1)); // up left
   1.167 +                Gdr = cimg::abs(grad[0](s+1,t-1)) + cimg::abs(grad[1](s+1,t-1)); // down right
   1.168 +                if (Gr>=thresH || Gur>=thresH || Gu>=thresH || Gul>=thresH
   1.169 +                    || Gl>=thresH || Gdl >=thresH || Gu >=thresH || Gdr >=thresH) {
   1.170 +                  edge.draw_point(s,t,black);
   1.171 +                }
   1.172 +              };
   1.173 +              break;
   1.174 +            case 2: // Angle 'theta' of the gradient (Gs,Gt) at the point (s,t).
   1.175 +              if(theta >= pi)theta-=pi;
   1.176 +              //rounding theta:
   1.177 +              if ((p8 < theta && theta <= p38 ) || (p78 < theta && theta <= pi)) {
   1.178 +                // See (*) below for explanation of the vocabulary used.
   1.179 +                // Direction-pixel is (s+1,t) with corresponding gradient value Gr.
   1.180 +                Gr = cimg::abs(grad[0](s+1,t)) + cimg::abs(grad[1](s+1,t)); // right
   1.181 +                // Contra-direction-pixel is (s-1,t) with corresponding gradient value Gl.
   1.182 +                Gl = cimg::abs(grad[0](s-1,t)) + cimg::abs(grad[1](s-1,t)); // left
   1.183 +                if (Gr < Gst && Gl < Gst) {
   1.184 +                  edge.draw_point(s,t,black);
   1.185 +                }
   1.186 +              }
   1.187 +              else if ( p8 < theta && theta <= p38) {
   1.188 +                // Direction-pixel is (s+1,t+1) with corresponding gradient value Gur.
   1.189 +                Gur = cimg::abs(grad[0](s+1,t+1)) + cimg::abs(grad[1](s+1,t+1)); // up right
   1.190 +                // Contra-direction-pixel is (s-1,t-1) with corresponding gradient value Gdl.
   1.191 +                Gdl = cimg::abs(grad[0](s-1,t-1)) + cimg::abs(grad[1](s-1,t-1)); // down left
   1.192 +                if (Gur < Gst && Gdl < Gst) {
   1.193 +                  edge.draw_point(s,t,black);
   1.194 +                      }
   1.195 +              }
   1.196 +              else if ( p38 < theta && theta <= p58) {
   1.197 +                // Direction-pixel is (s,t+1) with corresponding gradient value Gu.
   1.198 +                Gu = cimg::abs(grad[0](s,t+1)) + cimg::abs(grad[1](s,t+1)); // up
   1.199 +                // Contra-direction-pixel is (s,t-1) with corresponding gradient value Gd.
   1.200 +                Gd = cimg::abs(grad[0](s,t-1)) + cimg::abs(grad[1](s,t-1)); // down
   1.201 +                if (Gu < Gst && Gd < Gst) {
   1.202 +                  edge.draw_point(s,t,black);
   1.203 +                }
   1.204 +              }
   1.205 +              else if (p58 < theta && theta <= p78) {
   1.206 +                // Direction-pixel is (s-1,t+1) with corresponding gradient value Gul.
   1.207 +                Gul = cimg::abs(grad[0](s-1,t+1)) + cimg::abs(grad[1](s-1,t+1)); // up left
   1.208 +                // Contra-direction-pixel is (s+1,t-1) with corresponding gradient value Gdr.
   1.209 +                Gdr = cimg::abs(grad[0](s+1,t-1)) + cimg::abs(grad[1](s+1,t-1)); // down right
   1.210 +                if (Gul < Gst && Gdr < Gst) {
   1.211 +                  edge.draw_point(s,t,black);
   1.212 +                }
   1.213 +              };
   1.214 +              break;
   1.215 +            } // switch
   1.216 +          } // if good-points
   1.217 +        }  // cimg_forXY */
   1.218 +        edge.display(disp_edge);
   1.219 +    }// if redraw
   1.220 +  } // while
   1.221 +  return 0;
   1.222 +}
   1.223 +
   1.224 +// (*) Comments to the vocabulary used:
   1.225 +// If (s,t) is the current pixel, and G=(Gs,Gt) is the gradient at (s,t),
   1.226 +// then the _direction_pixel_ of (s,t) shall be the one of the eight neighbour pixels
   1.227 +// of (s,t) in whose direction the gradient G shows.
   1.228 +// The _contra_direction_pixel is the pixel in the opposite direction in which the gradient G shows.
   1.229 +// The _corresponding_gradient_value_ of the pixel (x,y) with gradient G = (Gx,Gy)
   1.230 +// shall be |Gx|+|Gy| ~=~ sqrt(Gx^2+Gy^2).