/* # # File : dtmri_view3d.cpp # ( C++ source file ) # # Description : A viewer of Diffusion-Tensor MRI volumes (medical imaging). # This file is a part of the CImg Library project. # ( http://cimg.eu ) # # Copyright : David Tschumperlé # ( http://tschumperle.users.greyc.fr/ ) # # License : CeCILL v2.0 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) # # This software is governed by the CeCILL license under French law and # abiding by the rules of distribution of free software. You can use, # modify and/ or redistribute the software under the terms of the CeCILL # license as circulated by CEA, CNRS and INRIA at the following URL # "http://www.cecill.info". # # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL license and that you accept its terms. # */ #include "CImg.h" using namespace cimg_library; #undef min #undef max // Compute fractional anisotropy (FA) of a tensor //------------------------------------------- template float get_FA(const T& val1, const T& val2, const T& val3) { const float l1 = val1>0?val1:0, l2 = val2>0?val2:0, l3 = val3>0?val3:0, lm = (l1 + l2 + l3)/3, tr2 = 2*(l1*l1 + l2*l2 + l3*l3), ll1 = l1 - lm, ll2 = l2 - lm, ll3 = l3 - lm; if (tr2>0) return (float)std::sqrt(3*(ll1*ll1 + ll2*ll2 + ll3*ll3)/tr2); return 0; } // Insert an ellipsoid in a CImg 3D scene //---------------------------------------- template void insert_ellipsoid(const CImg& tensor, const float X, const float Y, const float Z, const float tfact, const float vx, const float vy, const float vz, CImgList& points, CImgList& faces, CImgList& colors, const unsigned int res1=20, const unsigned int res2=20) { // Compute eigen elements float l1 = tensor[0], l2 = tensor[1], l3 = tensor[2], fa = get_FA(l1,l2,l3); CImg<> vec = CImg<>::matrix(tensor[3],tensor[6],tensor[9], tensor[4],tensor[7],tensor[10], tensor[5],tensor[8],tensor[11]); const int r = (int)std::min(30 + 1.5f*cimg::abs(255*fa*tensor[3]),255.0f), g = (int)std::min(30 + 1.5f*cimg::abs(255*fa*tensor[4]),255.0f), b = (int)std::min(30 + 1.5f*cimg::abs(255*fa*tensor[5]),255.0f); // Define mesh points const unsigned int N0 = points.size(); for (unsigned int v = 1; v::vector(X,Y,Z) + vec*CImg::vector(x,y,z)).mul(CImg::vector(vx,vy,vz))); } const unsigned int N1 = points.size(); points.insert((CImg::vector(X,Y,Z) - vec*CImg::vector(0,0,l3*tfact))); points.insert((CImg::vector(X,Y,Z) + vec*CImg::vector(0,0,l3*tfact))); points[points.size() - 2](0)*=vx; points[points.size() - 2](1)*=vy; points[points.size() - 2](2)*=vz; points[points.size() - 1](0)*=vx; points[points.size() - 1](1)*=vy; points[points.size() - 1](2)*=vz; // Define mesh triangles for (unsigned int vv = 0; vv::vector(N0 + res1*vv + nu,N0 + res1*nv + uu,N0 + res1*vv + uu)); faces.insert(CImg::vector(N0 + res1*vv + nu,N0 + res1*nv + nu,N0 + res1*nv + uu)); colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); } for (unsigned int uu = 0; uu::vector(N0 + nu,N0 + uu,N1)); faces.insert(CImg::vector(N0 + res1*(res2 - 2) + nu, N1 + 1,N0 + res1*(res2 - 2) + uu)); colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); } } // Insert a fiber in a CImg 3D scene //----------------------------------- template void insert_fiber(const CImg& fiber, const CImg& eigen, const CImg& palette, const int xm, const int ym, const int zm, const float vx, const float vy, const float vz, CImgList& points, CImgList& primitives, CImgList& colors) { const int N0 = points.size(); float x0 = fiber(0,0), y0 = fiber(0,1), z0 = fiber(0,2), fa0 = eigen.linear_atXYZ(x0,y0,z0,12); points.insert(CImg<>::vector(vx*(x0 -xm),vy*(y0 - ym),vz*(z0 - zm))); for (int l = 1; l::vector(vx*(x1 - xm),vy*(y1 - ym),vz*(z1 - zm))); primitives.insert(CImg::vector(N0 + l - 1,N0 + l)); const unsigned char icol = (unsigned char)(fa0*255), r = palette(icol,0), g = palette(icol,1), b = palette(icol,2); colors.insert(CImg::vector(r,g,b)); x0 = x1; y0 = y1; z0 = z1; fa0 = fa1; } } // Compute fiber tracking using 4th-order Runge Kutta integration //----------------------------------------------------------------- template CImg<> get_fibertrack(CImg& eigen, const int X0, const int Y0, const int Z0, const float lmax=100, const float dl=0.1f, const float FAmin=0.7f, const float cmin=0.5f) { #define align_eigen(i,j,k) \ { T &u = eigen(i,j,k,3), &v = eigen(i,j,k,4), &w = eigen(i,j,k,5); \ if (u*cu + v*cv + w*cw<0) { u=-u; v=-v; w=-w; }} CImgList<> resf; // Forward tracking float normU = 0, normpU = 0, l = 0, X = (float)X0, Y = (float)Y0, Z = (float)Z0; T pu = eigen(X0,Y0,Z0,3), pv = eigen(X0,Y0,Z0,4), pw = eigen(X0,Y0,Z0,5); normpU = (float)std::sqrt(pu*pu + pv*pv + pw*pw); bool stopflag = false; while (!stopflag) { if (X<0 || X>eigen.width() - 1 || Y<0 || Y>eigen.height() - 1 || Z<0 || Z>eigen.depth() - 1 || eigen((int)X,(int)Y,(int)Z,12)lmax) stopflag = true; else { resf.insert(CImg<>::vector(X,Y,Z)); const int cx = (int)X, px = (cx - 1<0)?0:cx - 1, nx = (cx + 1>=eigen.width())?eigen.width() - 1:cx + 1, cy = (int)Y, py = (cy - 1<0)?0:cy - 1, ny = (cy + 1>=eigen.height())?eigen.height() - 1:cy + 1, cz = (int)Z, pz = (cz - 1<0)?0:cz - 1, nz = (cz + 1>=eigen.depth())?eigen.depth() - 1:cz + 1; const T cu = eigen(cx,cy,cz,3), cv = eigen(cx,cy,cz,4), cw = eigen(cx,cy,cz,5); align_eigen(px,py,pz); align_eigen(cx,py,pz); align_eigen(nx,py,pz); align_eigen(px,cy,pz); align_eigen(cx,cy,pz); align_eigen(nx,cy,pz); align_eigen(px,ny,pz); align_eigen(cx,ny,pz); align_eigen(nx,ny,pz); align_eigen(px,py,cz); align_eigen(cx,py,cz); align_eigen(nx,py,cz); align_eigen(px,cy,cz); align_eigen(nx,cy,cz); align_eigen(px,ny,cz); align_eigen(cx,ny,cz); align_eigen(nx,ny,cz); align_eigen(px,py,nz); align_eigen(cx,py,nz); align_eigen(nx,py,nz); align_eigen(px,cy,nz); align_eigen(cx,cy,nz); align_eigen(nx,cy,nz); align_eigen(px,ny,nz); align_eigen(cx,ny,nz); align_eigen(nx,ny,nz); const T u0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,3), v0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,4), w0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,5), u1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,3), v1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,4), w1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,5), u2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,3), v2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,4), w2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,5), u3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,3), v3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,4), w3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,5); T u = u0/3 + 2*u1/3 + 2*u2/3 + u3/3, v = v0/3 + 2*v1/3 + 2*v2/3 + v3/3, w = w0/3 + 2*w1/3 + 2*w2/3 + w3/3; if (u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } normU = (float)std::sqrt(u*u + v*v + w*w); const float scal = (u*pu + v*pv + w*pw)/(normU*normpU); if (scaleigen.width() - 1 || Y<0 || Y>eigen.height() - 1 || Z<0 || Z>eigen.depth() - 1 || eigen((int)X,(int)Y,(int)Z,12)lmax) stopflag = true; else { const int cx = (int)X, px = (cx - 1<0)?0:cx - 1, nx = (cx + 1>=eigen.width())?eigen.width() - 1:cx + 1, cy = (int)Y, py = (cy - 1<0)?0:cy - 1, ny = (cy + 1>=eigen.height())?eigen.height() - 1:cy + 1, cz = (int)Z, pz = (cz - 1<0)?0:cz - 1, nz = (cz + 1>=eigen.depth())?eigen.depth() - 1:cz + 1; const T cu = eigen(cx,cy,cz,3), cv = eigen(cx,cy,cz,4), cw = eigen(cx,cy,cz,5); align_eigen(px,py,pz); align_eigen(cx,py,pz); align_eigen(nx,py,pz); align_eigen(px,cy,pz); align_eigen(cx,cy,pz); align_eigen(nx,cy,pz); align_eigen(px,ny,pz); align_eigen(cx,ny,pz); align_eigen(nx,ny,pz); align_eigen(px,py,cz); align_eigen(cx,py,cz); align_eigen(nx,py,cz); align_eigen(px,cy,cz); align_eigen(nx,cy,cz); align_eigen(px,ny,cz); align_eigen(cx,ny,cz); align_eigen(nx,ny,cz); align_eigen(px,py,nz); align_eigen(cx,py,nz); align_eigen(nx,py,nz); align_eigen(px,cy,nz); align_eigen(cx,cy,nz); align_eigen(nx,cy,nz); align_eigen(px,ny,nz); align_eigen(cx,ny,nz); align_eigen(nx,ny,nz); const T u0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,3), v0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,4), w0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,5), u1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,3), v1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,4), w1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,5), u2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,3), v2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,4), w2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,5), u3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,3), v3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,4), w3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,5); T u = u0/3 + 2*u1/3 + 2*u2/3 + u3/3, v = v0/3 + 2*v1/3 + 2*v2/3 + v3/3, w = w0/3 + 2*w1/3 + 2*w2/3 + w3/3; if (u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } normU = (float)std::sqrt(u*u + v*v + w*w); const float scal = (u*pu + v*pv + w*pw)/(normU*normpU); if (scal::vector(X,Y,Z),0); } } return resf>'x'; } // Main procedure //---------------- int main(int argc,char **argv) { // Read and init data //-------------------- cimg_usage("A viewer of Diffusion-Tensor MRI volumes."); const char *file_i = cimg_option("-i",(char*)0,"Input : Filename of tensor field (volume wxhxdx6)"); const char* vsize = cimg_option("-vsize","1x1x1","Input : Voxel aspect"); const bool normalize = cimg_option("-normalize",true,"Input : Enable tensor normalization"); const char *file_f = cimg_option("-f",(char*)0,"Input : Input fibers\n"); const float dl = cimg_option("-dl",0.5f,"Fiber computation : Integration step"); const float famin = cimg_option("-famin",0.3f,"Fiber computation : Fractional Anisotropy threshold"); const float cmin = cimg_option("-cmin",0.2f,"Fiber computation : Curvature threshold"); const float lmin = cimg_option("-lmin",10.0f,"Fiber computation : Minimum length\n"); const float lmax = cimg_option("-lmax",1000.0f,"Fiber computation : Maximum length\n"); const float tfact = cimg_option("-tfact",1.2f,"Display : Tensor size factor"); const char *bgcolor = cimg_option("-bg","0,0,0","Display : Background color"); unsigned int bgr = 0, bgg = 0, bgb = 0; std::sscanf(bgcolor,"%u%*c%u%*c%u",&bgr,&bgg,&bgb); CImg<> tensors; if (file_i) { std::fprintf(stderr,"\n- Loading tensors '%s'",cimg::basename(file_i)); tensors.load(file_i); } else { // Create a synthetic tensor field here std::fprintf(stderr,"\n- No input files : Creating a synthetic tensor field"); tensors.assign(32,32,32,6); cimg_forXYZ(tensors,x,y,z) { const float u = x - tensors.width()/2.0f, v = y - tensors.height()/2.0f, w = z - tensors.depth()/2.0f, norm = (float)std::sqrt(1e-5f + u*u + v*v + w*w), nu = u/norm, nv = v/norm, nw = w/norm; const CImg<> dir1 = CImg<>::vector(nu,nv,nw), dir2 = CImg<>::vector(-nv,nu,nw), dir3 = CImg<>::vector(nw*(nv - nu),-nw*(nu + nv),nu*nu + nv*nv); tensors.set_tensor_at(2.0*dir1*dir1.get_transpose() + 1.0*dir2*dir2.get_transpose() + 0.7*dir3*dir3.get_transpose(), x,y,z); } } float voxw = 1, voxh = 1, voxd = 1; std::sscanf(vsize,"%f%*c%f%*c%f",&voxw,&voxh,&voxd); std::fprintf(stderr," : %ux%ux%u image, voxsize=%gx%gx%g.", tensors.width(),tensors.height(),tensors.depth(), voxw,voxh,voxd); CImgList<> fibers; if (file_f) { std::fprintf(stderr,"\n- Loading fibers '%s'.",cimg::basename(file_f)); fibers.load(file_f); } const CImg fiber_palette = CImg<>(2,1,1,3).fill(200,255,0,255,0,200).RGBtoHSV().resize(256,1,1,3,3).HSVtoRGB(); // Compute eigen elements //------------------------ std::fprintf(stderr,"\n- Compute eigen elements."); CImg coloredFA(tensors.width(),tensors.height(),tensors.depth(),3); CImg<> eigen(tensors.width(),tensors.height(),tensors.depth(),13); CImg<> val,vec; float eigmax = 0; cimg_forXYZ(tensors,x,y,z) { tensors.get_tensor_at(x,y,z).symmetric_eigen(val,vec); eigen(x,y,z,0) = val[0]; eigen(x,y,z,1) = val[1]; eigen(x,y,z,2) = val[2]; if (val[0]<0) val[0] = 0; if (val[1]<0) val[1] = 0; if (val[2]<0) val[2] = 0; if (val[0]>eigmax) eigmax = val[0]; eigen(x,y,z,3) = vec(0,0); eigen(x,y,z,4) = vec(0,1); eigen(x,y,z,5) = vec(0,2); eigen(x,y,z,6) = vec(1,0); eigen(x,y,z,7) = vec(1,1); eigen(x,y,z,8) = vec(1,2); eigen(x,y,z,9) = vec(2,0); eigen(x,y,z,10) = vec(2,1); eigen(x,y,z,11) = vec(2,2); const float fa = get_FA(val[0],val[1],val[2]); eigen(x,y,z,12) = fa; const int r = (int)std::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,0))), g = (int)std::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,1))), b = (int)std::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,2))); coloredFA(x,y,z,0) = (unsigned char)r; coloredFA(x,y,z,1) = (unsigned char)g; coloredFA(x,y,z,2) = (unsigned char)b; } tensors.assign(); std::fprintf(stderr,"\n- Maximum diffusivity = %g, Maximum FA = %g",eigmax,eigen.get_shared_channel(12).max()); if (normalize) { std::fprintf(stderr,"\n- Normalize tensors."); eigen.get_shared_channels(0,2)/=eigmax; } // Init display and begin user interaction //----------------------------------------- std::fprintf(stderr,"\n- Open user window."); CImgDisplay disp(256,256,"DTMRI Viewer",0); CImgDisplay disp3d(800,600,"3D Local View",0,false,true); unsigned int XYZ[3]; XYZ[0] = eigen.width()/2; XYZ[1] = eigen.height()/2; XYZ[2] = eigen.depth()/2; while (!disp.is_closed() && !disp.is_keyQ() && !disp.is_keyESC()) { const CImg s = coloredFA.get_select(disp,2,XYZ); if (!disp.is_closed()) switch (disp.key()) { // Open 3D visualization window //----------------------------- case cimg::keyA : case 0 : { const unsigned char white[] = { 255 }; disp3d.display(CImg(disp3d.width(),disp3d.height(),1,1,0). draw_text(10,10,"Please wait...",white)).show(); int xm, ym, zm, xM, yM, zM; if (!disp.key()) { xm = s[0]; ym = s[1]; zm = s[2]; xM = s[3]; yM = s[4]; zM = s[5]; } else { xm = ym = zm = 0; xM = eigen.width() - 1; yM = eigen.height() - 1; zM = eigen.height() - 1; } const CImg<> img = eigen.get_crop(xm,ym,zm,xM,yM,zM); CImgList<> points; CImgList primitives; CImgList colors; // Add ellipsoids to the 3D scene int X = img.width()/2, Y = img.height()/2, Z = img.depth()/2; cimg_forXY(img,x,y) insert_ellipsoid(img.get_vector_at(x,y,Z),(float)x,(float)y,(float)Z, tfact,voxw,voxh,voxd,points,primitives,colors,10,6); cimg_forXZ(img,x,z) insert_ellipsoid(img.get_vector_at(x,Y,z),(float)x,(float)Y,(float)z, tfact,voxw,voxh,voxd,points,primitives,colors,10,6); cimg_forYZ(img,y,z) insert_ellipsoid(img.get_vector_at(X,y,z),(float)X,(float)y,(float)z, tfact,voxw,voxh,voxd,points,primitives,colors,10,6); // Add computed fibers to the 3D scene const CImg<> veigen = eigen.get_crop(xm,ym,zm,xM,yM,zM); cimglist_for(fibers,l) { const CImg<>& fiber = fibers[l]; if (fiber.width()) insert_fiber(fiber,eigen,fiber_palette, xm,ym,zm,voxw,voxh,voxd, points,primitives,colors); } // Display 3D object CImg visu = CImg(3,disp3d.width(),disp3d.height(),1,0). fill((unsigned char)bgr,(unsigned char)bgg,(unsigned char)bgb). permute_axes("yzcx"); bool stopflag = false; while (!disp3d.is_closed() && !stopflag) { const CImg<> pts = points>'x'; visu.display_object3d(disp3d,pts,primitives,colors,true,4,-1,false,800,0.05f,1.0f); disp3d.close(); switch (disp3d.key()) { case cimg::keyM : { // Create movie std::fprintf(stderr,"\n- Movie mode.\n"); const unsigned int N = 256; CImg<> cpts(pts); const CImg<> x = pts.get_shared_row(0), y = pts.get_shared_row(1), z = pts.get_shared_row(2); float _xm, _xM = x.max_min(_xm), _ym, _yM = y.max_min(_ym), _zm, _zM = z.max_min(_zm), ratio = 2.0f*std::min(visu.width(),visu.height())/(3.0f*cimg::max(_xM - _xm,_yM - _ym,_zM - _zm)), dx = 0.5f*(_xM + _xm), dy = 0.5f*(_yM + _ym), dz = 0.5f*(_zM + _zm); cimg_forX(pts,l) { cpts(l,0) = (pts(l,0) - dx)*ratio; cpts(l,1) = (pts(l,1) - dy)*ratio; cpts(l,2) = (pts(l,2) - dz)*ratio; } for (unsigned int i=0; i rpts = CImg<>::rotation_matrix(0,1,0,alpha)*CImg<>::rotation_matrix(1,0,0,75)*cpts; visu.fill(0).draw_object3d(visu.width()/2.0f,visu.height()/2.0f,-500.0f,rpts,primitives,colors, 4,false,800.0f,visu.width()/2.0f,visu.height()/2.0f,-800.0f,0.05f,1.0f). display(disp3d); visu.save("frame.png",i); } visu.fill(0); } break; default: stopflag = true; } } if (disp3d.is_fullscreen()) disp3d.toggle_fullscreen().resize(800,600).close(); } break; // Compute region statistics //--------------------------- case cimg::keyR : { std::fprintf(stderr,"\n- Statistics computation. Select region."); std::fflush(stderr); const CImg sel = coloredFA.get_select(disp,2,XYZ); int xm, ym, zm, xM, yM, zM; if (!disp.key()) { xm = sel[0]; ym = sel[1]; zm = sel[2]; xM = sel[3]; yM = sel[4]; zM = sel[5]; } else { xm = ym = zm = 0; xM = eigen.width() - 1; yM = eigen.height() - 1; zM = eigen.height() - 1; } const CImg<> img = eigen.get_crop(xm,ym,zm,xM,yM,zM); std::fprintf(stderr,"\n- Mean diffusivity = %g, Mean FA = %g\n", eigen.get_shared_channel(0).mean(), eigen.get_shared_channel(12).mean()); } break; // Track fiber bundle (single region) //---------------------------------- case cimg::keyF : { std::fprintf(stderr,"\n- Tracking mode (single region). Select starting region.\n"); std::fflush(stderr); const CImg sel = coloredFA.get_select(disp,2,XYZ); const unsigned int N = fibers.size(); for (int z = sel[2]; z<=sel[5]; ++z) for (int y = sel[1]; y<=sel[4]; ++y) for (int x = sel[0]; x<=sel[3]; ++x) { const CImg<> fiber = get_fibertrack(eigen,x,y,z,lmax,dl,famin,cmin); if (fiber.width()>lmin) { std::fprintf(stderr,"\rFiber %u : Starting from (%d,%d,%d)\t\t",fibers.size(),x,y,z); fibers.insert(fiber); } } std::fprintf(stderr,"\n- %u fiber(s) added (total %u).",fibers.size() - N,fibers.size()); } break; // Track fiber bundle (double regions) //------------------------------------ case cimg::keyG : { std::fprintf(stderr,"\n- Tracking mode (double region). Select starting region."); std::fflush(stderr); const CImg sel = coloredFA.get_select(disp,2,XYZ); std::fprintf(stderr," Select ending region."); std::fflush(stderr); const CImg nsel = coloredFA.get_select(disp,2,XYZ); const unsigned int N = fibers.size(); // Track from start to end for (int z = sel[2]; z<=sel[5]; ++z) for (int y = sel[1]; y<=sel[4]; ++y) for (int x = sel[0]; x<=sel[3]; ++x) { const CImg<> fiber = get_fibertrack(eigen,x,y,z,lmax,dl,famin,cmin); if (fiber.width()>lmin) { bool valid_fiber = false; cimg_forX(fiber,k) { const int fx = (int)fiber(k,0), fy = (int)fiber(k,1), fz = (int)fiber(k,2); if (fx>=nsel[0] && fx<=nsel[3] && fy>=nsel[1] && fy<=nsel[4] && fz>=nsel[2] && fz<=nsel[5]) valid_fiber = true; } if (valid_fiber) fibers.insert(fiber); } } // Track from end to start for (int z = nsel[2]; z<=nsel[5]; ++z) for (int y = nsel[1]; y<=nsel[4]; ++y) for (int x = nsel[0]; x<=nsel[3]; ++x) { const CImg<> fiber = get_fibertrack(eigen,x,y,z,lmax,dl,famin,cmin); if (fiber.width()>lmin) { bool valid_fiber = false; cimg_forX(fiber,k) { const int fx = (int)fiber(k,0), fy = (int)fiber(k,1), fz = (int)fiber(k,2); if (fx>=sel[0] && fx<=sel[3] && fy>=sel[1] && fy<=sel[4] && fz>=sel[2] && fz<=sel[5]) valid_fiber = true; } if (valid_fiber) { std::fprintf(stderr,"\rFiber %u : Starting from (%d,%d,%d)\t\t",fibers.size(),x,y,z); fibers.insert(fiber); } } } std::fprintf(stderr," %u fiber(s) added (total %u).",fibers.size() - N,fibers.size()); } break; // Clear fiber bundle //------------------- case cimg::keyC : { std::fprintf(stderr,"\n- Fibers removed."); fibers.assign(); } break; // Save fibers //------------- case cimg::keyS : { fibers.save("fibers.cimg"); std::fprintf(stderr,"\n- Fibers saved."); } break; } } std::fprintf(stderr,"\n- Exit.\n\n\n"); return 0; }