PTdecode/CImg-1.3.0/examples/edge_explorer.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

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : edge_explorer.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : Real time edge detection while moving a ROI
philpem@5 7 # (rectangle of interest) over the original image.
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 : Orges Leka
philpem@5 12 # ( oleka(at)students.uni-mainz.de )
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 // Start main procedure
philpem@5 62 //-----------------------
philpem@5 63 int main(int argc, char** argv) {
philpem@5 64
philpem@5 65 // Usage of the program displayed at the command line
philpem@5 66 cimg_usage("Real time edge detection with CImg. (c) Orges Leka");
philpem@5 67
philpem@5 68 // Read command line arguments
philpem@5 69 // With cimg_option we can get a new name for the image which is to be loaded from the command line.
philpem@5 70 const char* img_name = cimg_option("-i", cimg_imagepath "lena.pgm","Input image.");
philpem@5 71 double
philpem@5 72 alpha = cimg_option("-a",1.0,"Blurring the gradient image."),
philpem@5 73 thresL = cimg_option("-tl",13.5,"Lower thresholding used in Hysteresis."),
philpem@5 74 thresH = cimg_option("-th",13.6,"Higher thresholding used in Hysteresis.");
philpem@5 75 const unsigned int
philpem@5 76 mode = cimg_option("-m",1,"Detection mode: 1 = Hysteresis, 2 = Gradient angle."),
philpem@5 77 factor = cimg_option("-s",80,"Half-size of edge-explorer window.");
philpem@5 78
philpem@5 79 cimg_help("\nAdditional notes : user can press following keys on main display window :\n"
philpem@5 80 " - Left arrow : Decrease alpha.\n"
philpem@5 81 " - Right arrow : Increase alpha.\n");
philpem@5 82
philpem@5 83 // Construct a new image called 'edge' of size (2*factor,2*factor)
philpem@5 84 // and of type 'unsigned char'.
philpem@5 85 CImg<unsigned char> edge(2*factor,2*factor);
philpem@5 86 CImgDisplay disp_edge(512,512,"Edge Explorer");
philpem@5 87
philpem@5 88 // Load the image with the name 'img_name' into the CImg 'img'.
philpem@5 89 // and create a display window 'disp' for the image 'img'.
philpem@5 90 const CImg<unsigned char> img(img_name);
philpem@5 91 CImgDisplay disp(img,"Original Image");
philpem@5 92
philpem@5 93 // Begin main interaction loop.
philpem@5 94 int x = 0, y = 0;
philpem@5 95 bool redraw = false;
philpem@5 96 while (!disp.is_closed && !disp.is_keyQ && !disp.is_keyESC) {
philpem@5 97 disp.wait(100);
philpem@5 98 if (disp.button&1) { alpha+=0.05; redraw = true; }
philpem@5 99 if (disp.button&2) { alpha-=0.05; redraw = true; }
philpem@5 100 if (disp.wheel) { alpha+=0.05*disp.wheel; disp.wheel = 0; redraw = true; }
philpem@5 101 if (alpha<0) alpha = 0;
philpem@5 102 if (disp_edge.is_resized) { disp_edge.resize(); redraw = true; }
philpem@5 103 if (disp_edge.is_closed) disp_edge.show();
philpem@5 104 if (disp.is_resized) disp.resize(disp);
philpem@5 105 if (disp.mouse_x>=0) {
philpem@5 106 x = disp.mouse_x; // Getting the current position of the mouse.
philpem@5 107 y = disp.mouse_y; //
philpem@5 108 redraw = true; // The image should be redrawn.
philpem@5 109 }
philpem@5 110 if (redraw) {
philpem@5 111 disp_edge.set_title("Edge explorer (alpha=%g)",alpha);
philpem@5 112 const int
philpem@5 113 x0 = x-factor, y0 = y-factor, // These are the coordinates for the red rectangle
philpem@5 114 x1 = x+factor, y1 = y+factor; // to be drawn on the original image.
philpem@5 115 const unsigned char
philpem@5 116 red[3] = { 255,0,0 }, //
philpem@5 117 black[3] = { 0,0,0 }; // Defining the colors we need for drawing.
philpem@5 118
philpem@5 119 (+img).draw_rectangle(x0,y0,x1,y1,red,1.0f,0x55555555U).display(disp);
philpem@5 120 //^ We draw the red rectangle on the original window using 'draw_line'. Then we display the result via '.display(disp)' .
philpem@5 121 // Observe, that the color 'red' has to be of type 'const unsigned char',
philpem@5 122 // since the image 'img' is of type 'const CImg<unsigned char>'.
philpem@5 123
philpem@5 124 //'normalize' is used to get a greyscaled image.
philpem@5 125 CImg<> visu_bw = CImg<>(img).get_crop(x0,y0,x1,y1).get_pointwise_norm().normalize(0,255).resize(-100,-100,1,2,2);
philpem@5 126 // get_crop(x0,y0,x1,y1) gets the rectangle we are interested in.
philpem@5 127
philpem@5 128 edge.fill(255); // Background color in the edge-detection window is white.
philpem@5 129
philpem@5 130 // grad[0] is the gradient image of 'visu_bw' in x-direction.
philpem@5 131 // grad[1] is the gradient image of 'visu_bw' in y-direction.
philpem@5 132 CImgList<> grad(visu_bw.blur((float)alpha).normalize(0,255).get_gradient());
philpem@5 133
philpem@5 134 // To avoid unnecessary calculations in the image loops:
philpem@5 135 const double
philpem@5 136 pi = cimg::valuePI,
philpem@5 137 p8 = pi/8.0, p38 = 3.0*p8,
philpem@5 138 p58 = 5.0*p8, p78 = 7.0*p8;
philpem@5 139
philpem@5 140 cimg_forXY(visu_bw,s,t) {
philpem@5 141 // We take s,t instead of x,y, since x,y are already used.
philpem@5 142 // s corresponds to the x-ordinate of the pixel while t corresponds to the y-ordinate.
philpem@5 143 if ( 1 <= s && s <= visu_bw.dimx()-1 && 1 <= t && t <=visu_bw.dimy()-1) { // if - good points
philpem@5 144 double
philpem@5 145 Gs = grad[0](s,t), //
philpem@5 146 Gt = grad[1](s,t), // The actual pixel is (s,t)
philpem@5 147 Gst = cimg::abs(Gs) + cimg::abs(Gt), //
philpem@5 148 // ^-- For efficient computation we observe that |Gs|+ |Gt| ~=~ sqrt( Gs^2 + Gt^2)
philpem@5 149 Gr, Gur, Gu, Gul, Gl, Gdl, Gd, Gdr;
philpem@5 150 // ^-- right, up right, up, up left, left, down left, down, down right.
philpem@5 151 double theta = std::atan2(Gt,Gs)+pi; // theta is from the interval [0,Pi]
philpem@5 152 switch(mode) {
philpem@5 153 case 1: // Hysterese is applied
philpem@5 154 if (Gst>=thresH) { edge.draw_point(s,t,black); }
philpem@5 155 else if (thresL <= Gst && Gst < thresH) {
philpem@5 156 // Neighbourhood of the actual pixel:
philpem@5 157 Gr = cimg::abs(grad[0](s+1,t)) + cimg::abs(grad[1](s+1,t)); // right
philpem@5 158 Gl = cimg::abs(grad[0](s-1,t)) + cimg::abs(grad[1](s-1,t)); // left
philpem@5 159 Gur = cimg::abs(grad[0](s+1,t+1)) + cimg::abs(grad[1](s+1,t+1)); // up right
philpem@5 160 Gdl = cimg::abs(grad[0](s-1,t-1)) + cimg::abs(grad[1](s-1,t-1)); // down left
philpem@5 161 Gu = cimg::abs(grad[0](s,t+1)) + cimg::abs(grad[1](s,t+1)); // up
philpem@5 162 Gd = cimg::abs(grad[0](s,t-1)) + cimg::abs(grad[1](s,t-1)); // down
philpem@5 163 Gul = cimg::abs(grad[0](s-1,t+1)) + cimg::abs(grad[1](s-1,t+1)); // up left
philpem@5 164 Gdr = cimg::abs(grad[0](s+1,t-1)) + cimg::abs(grad[1](s+1,t-1)); // down right
philpem@5 165 if (Gr>=thresH || Gur>=thresH || Gu>=thresH || Gul>=thresH
philpem@5 166 || Gl>=thresH || Gdl >=thresH || Gu >=thresH || Gdr >=thresH) {
philpem@5 167 edge.draw_point(s,t,black);
philpem@5 168 }
philpem@5 169 };
philpem@5 170 break;
philpem@5 171 case 2: // Angle 'theta' of the gradient (Gs,Gt) at the point (s,t).
philpem@5 172 if(theta >= pi)theta-=pi;
philpem@5 173 //rounding theta:
philpem@5 174 if ((p8 < theta && theta <= p38 ) || (p78 < theta && theta <= pi)) {
philpem@5 175 // See (*) below for explanation of the vocabulary used.
philpem@5 176 // Direction-pixel is (s+1,t) with corresponding gradient value Gr.
philpem@5 177 Gr = cimg::abs(grad[0](s+1,t)) + cimg::abs(grad[1](s+1,t)); // right
philpem@5 178 // Contra-direction-pixel is (s-1,t) with corresponding gradient value Gl.
philpem@5 179 Gl = cimg::abs(grad[0](s-1,t)) + cimg::abs(grad[1](s-1,t)); // left
philpem@5 180 if (Gr < Gst && Gl < Gst) {
philpem@5 181 edge.draw_point(s,t,black);
philpem@5 182 }
philpem@5 183 }
philpem@5 184 else if ( p8 < theta && theta <= p38) {
philpem@5 185 // Direction-pixel is (s+1,t+1) with corresponding gradient value Gur.
philpem@5 186 Gur = cimg::abs(grad[0](s+1,t+1)) + cimg::abs(grad[1](s+1,t+1)); // up right
philpem@5 187 // Contra-direction-pixel is (s-1,t-1) with corresponding gradient value Gdl.
philpem@5 188 Gdl = cimg::abs(grad[0](s-1,t-1)) + cimg::abs(grad[1](s-1,t-1)); // down left
philpem@5 189 if (Gur < Gst && Gdl < Gst) {
philpem@5 190 edge.draw_point(s,t,black);
philpem@5 191 }
philpem@5 192 }
philpem@5 193 else if ( p38 < theta && theta <= p58) {
philpem@5 194 // Direction-pixel is (s,t+1) with corresponding gradient value Gu.
philpem@5 195 Gu = cimg::abs(grad[0](s,t+1)) + cimg::abs(grad[1](s,t+1)); // up
philpem@5 196 // Contra-direction-pixel is (s,t-1) with corresponding gradient value Gd.
philpem@5 197 Gd = cimg::abs(grad[0](s,t-1)) + cimg::abs(grad[1](s,t-1)); // down
philpem@5 198 if (Gu < Gst && Gd < Gst) {
philpem@5 199 edge.draw_point(s,t,black);
philpem@5 200 }
philpem@5 201 }
philpem@5 202 else if (p58 < theta && theta <= p78) {
philpem@5 203 // Direction-pixel is (s-1,t+1) with corresponding gradient value Gul.
philpem@5 204 Gul = cimg::abs(grad[0](s-1,t+1)) + cimg::abs(grad[1](s-1,t+1)); // up left
philpem@5 205 // Contra-direction-pixel is (s+1,t-1) with corresponding gradient value Gdr.
philpem@5 206 Gdr = cimg::abs(grad[0](s+1,t-1)) + cimg::abs(grad[1](s+1,t-1)); // down right
philpem@5 207 if (Gul < Gst && Gdr < Gst) {
philpem@5 208 edge.draw_point(s,t,black);
philpem@5 209 }
philpem@5 210 };
philpem@5 211 break;
philpem@5 212 } // switch
philpem@5 213 } // if good-points
philpem@5 214 } // cimg_forXY */
philpem@5 215 edge.display(disp_edge);
philpem@5 216 }// if redraw
philpem@5 217 } // while
philpem@5 218 return 0;
philpem@5 219 }
philpem@5 220
philpem@5 221 // (*) Comments to the vocabulary used:
philpem@5 222 // If (s,t) is the current pixel, and G=(Gs,Gt) is the gradient at (s,t),
philpem@5 223 // then the _direction_pixel_ of (s,t) shall be the one of the eight neighbour pixels
philpem@5 224 // of (s,t) in whose direction the gradient G shows.
philpem@5 225 // The _contra_direction_pixel is the pixel in the opposite direction in which the gradient G shows.
philpem@5 226 // The _corresponding_gradient_value_ of the pixel (x,y) with gradient G = (Gx,Gy)
philpem@5 227 // shall be |Gx|+|Gy| ~=~ sqrt(Gx^2+Gy^2).