Wed, 05 Aug 2009 17:10:56 +0100
add README
philpem@5 | 1 | /* |
philpem@5 | 2 | # |
philpem@5 | 3 | # File : mcf_levelsets3d.cpp |
philpem@5 | 4 | # ( C++ source file ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : Implementation of the Mean Curvature Flow on Surfaces |
philpem@5 | 7 | # using the framework of Level Sets 3D. |
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 | // Apply the Mean curvature flow PDE |
philpem@5 | 58 | //----------------------------------- |
philpem@5 | 59 | template<typename T> CImg<T>& mcf_PDE(CImg<T>& img, const unsigned int nb_iter, |
philpem@5 | 60 | const float dt=0.25f, const float narrow=4.0f) { |
philpem@5 | 61 | CImg<T> veloc(img.dimx(),img.dimy(),img.dimz(),img.dimv()); |
philpem@5 | 62 | CImg_3x3x3(I,float); |
philpem@5 | 63 | for (unsigned int iter=0; iter<nb_iter; iter++) { |
philpem@5 | 64 | cimg_for3x3x3(img,x,y,z,0,I) if (cimg::abs(Iccc)<narrow) { |
philpem@5 | 65 | const float |
philpem@5 | 66 | ix = 0.5f*(Incc-Ipcc), |
philpem@5 | 67 | iy = 0.5f*(Icnc-Icpc), |
philpem@5 | 68 | iz = 0.5f*(Iccn-Iccp), |
philpem@5 | 69 | norm = (float)std::sqrt(1e-5f+ix*ix+iy*iy+iz*iz), |
philpem@5 | 70 | ixx = Incc+Ipcc-2*Iccc, |
philpem@5 | 71 | ixy = 0.25f*(Ippc+Innc-Inpc-Ipnc), |
philpem@5 | 72 | ixz = 0.25f*(Ipcp+Incn-Incp-Ipcn), |
philpem@5 | 73 | iyy = Icnc+Icpc-2*Iccc, |
philpem@5 | 74 | iyz = 0.25f*(Icpp+Icnn-Icnp-Icpn), |
philpem@5 | 75 | izz = Iccn+Iccp-2*Iccc, |
philpem@5 | 76 | a = ix/norm, |
philpem@5 | 77 | b = iy/norm, |
philpem@5 | 78 | c = iz/norm, |
philpem@5 | 79 | inn = a*a*ixx + b*b*iyy + c*c*izz + 2*a*b*ixy + 2*a*c*ixz + 2*b*c*iyz; |
philpem@5 | 80 | veloc(x,y,z) = ixx+iyy+izz-inn; |
philpem@5 | 81 | } else veloc(x,y,z) = 0; |
philpem@5 | 82 | float m, M = veloc.maxmin(m); |
philpem@5 | 83 | const double xdt = dt/cimg::max(cimg::abs(m),cimg::abs(M)); |
philpem@5 | 84 | img+=xdt*veloc; |
philpem@5 | 85 | } |
philpem@5 | 86 | return img; |
philpem@5 | 87 | } |
philpem@5 | 88 | |
philpem@5 | 89 | // Main procedure |
philpem@5 | 90 | //---------------- |
philpem@5 | 91 | int main(int argc,char **argv) { |
philpem@5 | 92 | cimg_usage("Mean curvature flow of a surface, using 3D level sets"); |
philpem@5 | 93 | const char *file_i = cimg_option("-i",(char*)0,"Input image"); |
philpem@5 | 94 | const float dt = cimg_option("-dt",0.05f,"PDE Time step"); |
philpem@5 | 95 | const float narrow = cimg_option("-band",5.0f,"Size of the narrow band"); |
philpem@5 | 96 | const bool both = cimg_option("-both",false,"Show both evolving and initial surface"); |
philpem@5 | 97 | |
philpem@5 | 98 | // Define the signed distance map of the initial surface |
philpem@5 | 99 | CImg<> img; |
philpem@5 | 100 | if (file_i) { |
philpem@5 | 101 | const float sigma = cimg_option("-sigma",1.2f,"Segmentation regularity"); |
philpem@5 | 102 | const float alpha = cimg_option("-alpha",5.0f,"Region growing tolerance"); |
philpem@5 | 103 | img.load(file_i).channel(0); |
philpem@5 | 104 | CImg<int> s; |
philpem@5 | 105 | CImgDisplay disp(img,"Please select a starting point"); |
philpem@5 | 106 | while (!s || s[0]<0) s = img.get_select(0,disp); |
philpem@5 | 107 | CImg<> region; |
philpem@5 | 108 | float tmp[1] = { 0 }; |
philpem@5 | 109 | img.draw_fill(s[0],s[1],s[2],tmp,1,region,alpha); |
philpem@5 | 110 | ((img = region.normalize(-1,1))*=-1).blur(sigma); |
philpem@5 | 111 | |
philpem@5 | 112 | } |
philpem@5 | 113 | else { // Create synthetic implicit function |
philpem@5 | 114 | img.assign(60,60,60); |
philpem@5 | 115 | const float exte[1]={1}, inte[1]={-1}; |
philpem@5 | 116 | img.fill(*exte).draw_rectangle(15,15,15,45,45,45,inte).draw_rectangle(25,25,0,35,35,img.dimz()-1,exte). |
philpem@5 | 117 | draw_rectangle(0,25,25,img.dimx()-1,35,35,exte).draw_rectangle(25,0,25,35,img.dimy()-1,35,exte); |
philpem@5 | 118 | } |
philpem@5 | 119 | img.distance_hamilton(10,0,0.1f); |
philpem@5 | 120 | |
philpem@5 | 121 | // Compute corresponding surface triangularization by the marching cube algorithm (isovalue 0) |
philpem@5 | 122 | CImg<> points0; |
philpem@5 | 123 | CImgList<unsigned int> faces0; |
philpem@5 | 124 | if (both) points0 = img.get_isovalue3d(faces0,0); |
philpem@5 | 125 | const CImgList<unsigned char> colors0(faces0.size,CImg<unsigned char>::vector(100,200,255)); |
philpem@5 | 126 | const CImgList<> opacities0(faces0.size,1,1,1,1,0.2f); |
philpem@5 | 127 | |
philpem@5 | 128 | // Perform MCF evolution |
philpem@5 | 129 | CImgDisplay disp(256,256,"",1), disp3d(512,512,"",0); |
philpem@5 | 130 | float alpha = 0, beta = 0; |
philpem@5 | 131 | for (unsigned int iter=0; !disp.is_closed && !disp3d.is_closed && !disp.is_keyESC && !disp3d.is_keyESC && |
philpem@5 | 132 | !disp.is_keyQ && !disp3d.is_keyQ; iter++) { |
philpem@5 | 133 | disp.set_title("3D implicit Function (iter. %u)",iter); |
philpem@5 | 134 | disp3d.set_title("Mean curvature flow 3D - Isosurface (iter. %u)",iter); |
philpem@5 | 135 | |
philpem@5 | 136 | // Apply PDE on the distance function |
philpem@5 | 137 | mcf_PDE(img,1,dt,narrow); // Do one iteration of mean curvature flow |
philpem@5 | 138 | if (!(iter%10)) img.distance_hamilton(1,narrow,0.5f); // Every 10 steps, do one iteration of distance function re-initialization |
philpem@5 | 139 | |
philpem@5 | 140 | // Compute surface triangularization by the marching cube algorithm (isovalue 0) |
philpem@5 | 141 | CImgList<unsigned int> faces; |
philpem@5 | 142 | CImg<> points = img.get_isovalue3d(faces,0); |
philpem@5 | 143 | CImgList<unsigned char> colors(faces.size,CImg<unsigned char>::vector(200,128,100)); |
philpem@5 | 144 | CImgList<> opacities(faces.size,CImg<>::vector(1.0f)); |
philpem@5 | 145 | const float fact = 3*cimg::max(disp3d.dimx(),disp3d.dimy())/(4.0f*cimg::max(img.dimx(),img.dimy())); |
philpem@5 | 146 | |
philpem@5 | 147 | // Append initial object if necessary. |
philpem@5 | 148 | if (both) { |
philpem@5 | 149 | points.append_object3d(faces,points0,faces0); |
philpem@5 | 150 | colors.insert(colors0); |
philpem@5 | 151 | opacities.insert(opacities0); |
philpem@5 | 152 | } |
philpem@5 | 153 | |
philpem@5 | 154 | // center and rescale the objects |
philpem@5 | 155 | cimg_forX(points,l) { |
philpem@5 | 156 | points(l,0)=(points(l,0)-img.dimx()/2)*fact; |
philpem@5 | 157 | points(l,1)=(points(l,1)-img.dimy()/2)*fact; |
philpem@5 | 158 | points(l,2)=(points(l,2)-img.dimz()/2)*fact; |
philpem@5 | 159 | } |
philpem@5 | 160 | |
philpem@5 | 161 | // Display 3D object on the display window. |
philpem@5 | 162 | CImg<unsigned char> visu(disp3d.dimx(),disp3d.dimy(),1,3,0); |
philpem@5 | 163 | const CImg<> rot = CImg<>::rotation_matrix(1,0,0,(beta+=0.01f))*CImg<>::rotation_matrix(0,1,1,(alpha+=0.05f)); |
philpem@5 | 164 | if (points.size()) { |
philpem@5 | 165 | visu.draw_object3d(visu.dimx()/2.0f,visu.dimy()/2.0f,0.0f, |
philpem@5 | 166 | rot*points,faces,colors,opacities,3, |
philpem@5 | 167 | false,500.0,0.0f,0.0f,-8000.0f).display(disp3d); |
philpem@5 | 168 | } else visu.fill(0).display(disp3d); |
philpem@5 | 169 | img.display(disp.wait(20)); |
philpem@5 | 170 | |
philpem@5 | 171 | if ((disp3d.button || disp3d.key) && points.size()) { |
philpem@5 | 172 | unsigned char white[3]={ 255,255,255 }; |
philpem@5 | 173 | visu.fill(0).draw_text(10,10,"Time stopped, press any key to start again",white). |
philpem@5 | 174 | display_object3d(disp3d,points,faces,colors,opacities,true,4,3,false,500,0.4f,0.3f); |
philpem@5 | 175 | disp3d.key = 0; |
philpem@5 | 176 | } |
philpem@5 | 177 | if (disp.is_resized) disp.resize(false); |
philpem@5 | 178 | if (disp3d.is_resized) disp3d.resize(false); |
philpem@5 | 179 | } |
philpem@5 | 180 | |
philpem@5 | 181 | // Exit |
philpem@5 | 182 | return 0; |
philpem@5 | 183 | } |