contrib/mul/vil3d/algo/vil3d_make_distance_filter.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 //  \brief Compute filter for an exp. distance transform.
00004 // \author Tim Cootes
00005 
00006 #include "vil3d_make_distance_filter.h"
00007 #include <vcl_cmath.h>
00008 #include <vcl_cassert.h>
00009 
00010 //: Create filter such that d[a] is distance from origin of se[a]
00011 //  Pixel widths are (width_i,width_j,width_k).  Elements are 
00012 //  selected so that only voxels visited before in a raster scan 
00013 //  of the image are included.  Distance transforms require two
00014 //  passes, a forward and reverse pass.
00015 //
00016 //  Consider only voxels within 1 voxel (1 norm) of origin.
00017 //  Indexes (27-1)/2 = 13 voxels.
00018 void vil3d_make_distance_filter_r1(
00019                            double width_i, 
00020                            double width_j, 
00021                            double width_k,
00022                            vil3d_structuring_element& se,
00023                            vcl_vector<double>& d)
00024 {
00025   vcl_vector<int> pi,pj,pk;
00026   d.resize(0);
00027   double wi2=width_i*width_i;
00028   double wj2=width_j*width_j;
00029   double wk2=width_k*width_k;
00030 
00031   for (int k=-1;k<=0;++k)
00032   {
00033     int jhi=0;
00034     if (k<0) jhi=1;
00035     for (int j=-1;j<=jhi;++j)
00036     {
00037       int ihi=0;
00038       if (j<0 || k<0) ihi=1;
00039       for (int i=-1;i<=ihi;++i)
00040         if (!(i==0 && j==0 && k==0))
00041         {
00042           pi.push_back(i);
00043           pj.push_back(j);
00044           pk.push_back(k);
00045           d.push_back(vcl_sqrt(double(i*i*wi2+j*j*wj2+k*k*wk2)));
00046         }
00047     }
00048   }
00049   se.set(pi,pj,pk);
00050 }
00051 
00052 static void vil3d_make_voxel_block(int r, 
00053                               vcl_vector<int>& pi,
00054                               vcl_vector<int>& pj,
00055                               vcl_vector<int>& pk,
00056                               vcl_vector<double>& d)
00057 {
00058   for (int k=-r;k<=0;++k)
00059   {
00060     int jhi=0;
00061     if (k<0) jhi=r;
00062     for (int j=-r;j<=jhi;++j)
00063     {
00064       int ihi=0;
00065       if (j<0 || k<0) ihi=r;
00066       for (int i=-r;i<=ihi;++i)
00067       {
00068         pi.push_back(i);
00069         pj.push_back(j);
00070         pk.push_back(k);
00071         d.push_back(vcl_sqrt(double(i*i+j*j+k*k)));
00072       }
00073     }
00074   }
00075 }
00076 
00077 // Return true if offset(i,j,k) cannot be accurately approximated
00078 // by combinations of two (pi,pj,pk)
00079 static bool vil3d_offset_is_prime(int i,int j,int k,
00080                                const vcl_vector<int>& pi,
00081                                const vcl_vector<int>& pj,
00082                                const vcl_vector<int>& pk,
00083                                const vcl_vector<double>& d)
00084 {
00085   double d_true = vcl_sqrt(double(i*i+j*j+k*k));
00086   for (unsigned a =0;a<pi.size();++a)
00087   {
00088     for (unsigned b=0;b<pi.size();++b)
00089     {
00090       if ((pi[a]+pi[b]==i) &&
00091           (pj[a]+pj[b]==j) &&
00092           (pk[a]+pk[b]==k) )
00093       {
00094         // (i,j,k) is reached by element a + element b
00095         // Check if distance sum is a good approximation
00096         if (vcl_fabs(d[a]+d[b]-d_true)<1e-5) return false;
00097       }
00098     }
00099   }
00100   return true;
00101 }
00102 
00103 //: Create filter such that d[a] is distance from origin of se[a]
00104 //  Pixel widths are (width_i,width_j,width_k).  Elements are 
00105 //  selected so that only voxels visited before in a raster scan 
00106 //  of the image are included.  Distance transforms require two
00107 //  passes, a forward and reverse pass.
00108 //
00109 //  Consider only voxels within r voxels (1 norm) of origin.
00110 void vil3d_make_distance_filter(
00111                            double width_i, 
00112                            double width_j, 
00113                            double width_k,
00114                            int r,
00115                            vil3d_structuring_element& se,
00116                            vcl_vector<double>& d)
00117 {
00118   assert(r>0);
00119   if (r==1)
00120   {
00121     vil3d_make_distance_filter_r1(width_i,width_j,width_k,se,d);
00122     return;
00123   }
00124 
00125   // Compute 'prime' elements, ie offsets which cannot be
00126   // constructed accurately as a sum of two smaller offsets
00127   vil3d_structuring_element se1;
00128   vcl_vector<double> d1;
00129   vil3d_make_distance_filter_r1(1,1,1,se1,d1);
00130   vcl_vector<int> pi=se1.p_i();
00131   vcl_vector<int> pj=se1.p_j();
00132   vcl_vector<int> pk=se1.p_k();
00133 
00134   for (int r1=2;r1<=r;++r1)
00135   {
00136     // Process shell of voxels at inf norm distance r1 from origin
00137 
00138     // Create block of all offsets within (r-1),
00139     // used to check if new offset can be constructed from old ones.
00140     vcl_vector<int> pir,pjr,pkr;
00141     vcl_vector<double> dr;
00142     vil3d_make_voxel_block(r1-1,pir,pjr,pkr,dr);
00143 
00144     for (int k=-r1;k<=0;++k)
00145     {
00146       int jhi=0;
00147       if (k<0) jhi=r1;
00148       for (int j=-r1;j<=jhi;++j)
00149       {
00150         int ihi=0;
00151         if (j<0 || k<0) ihi=r1;
00152         for (int i=-r1;i<=ihi;++i)
00153         if (i==-r1 || i==r1 || j==-r1 || j==r1 || k==-r1)
00154         {
00155           if (vil3d_offset_is_prime(i,j,k,pir,pjr,pkr,dr))
00156           {
00157             pi.push_back(i);
00158             pj.push_back(j);
00159             pk.push_back(k);
00160           }
00161         }
00162       }
00163     }
00164   }
00165 
00166   d.resize(pi.size());
00167   for (unsigned a=0;a<pi.size();++a)
00168   {
00169     double dx=width_i*pi[a];
00170     double dy=width_j*pj[a];
00171     double dz=width_k*pk[a];
00172     d[a]=vcl_sqrt(dx*dx+dy*dy+dz*dz);
00173   }
00174   se.set(pi,pj,pk);
00175 }