Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

VoxelCoding.cpp

Go to the documentation of this file.
00001 /* ImLib3D
00002  * Copyright (c) 2001, ULP-IPB Strasbourg.
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or (at
00007  * your option) any later version.
00008  * 
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  * 
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00020 #include<ImLib3D/VoxelCoding.hpp>
00021 #include<ImLib3D/Arithmetic.hpp>
00022 #include<ImLib3D/Display.hpp>
00023 #include<ImLib3D/ConvenienceProcessors.hpp>
00024 
00025 void
00026 IP3D::ShortestPathExtraction(const Mask3D &shape,const Vect3Di &start,const Vect3Di &end,vector<Vect3Di> &path,
00027                        Mask3D *pres,StructureElement *psel,
00028                        const LabelImage3D *pssCode0,
00029                        ShortestPathExtractionHelpers::StopCBType stopCB)
00030 {
00031     // optional ssCode image
00032     const LabelImage3D *pssCode=pssCode0;
00033     if(!pssCode)
00034     {
00035         LabelImage3D *tmp=new LabelImage3D();
00036         IP3D::SSCode(shape,end,1,2,3,*tmp,&start);
00037         pssCode=tmp;
00038     }
00039     const LabelImage3D &ssCode=*pssCode;
00040        
00041     path.push_back(start);
00042     vector<Vect3Di> sel;
00043     if(!psel)
00044     {
00046         sel.push_back(Vect3Di(-1,0,0));
00047         sel.push_back(Vect3Di( 1,0,0));
00048         sel.push_back(Vect3Di(0,-1,0));
00049         sel.push_back(Vect3Di(0, 1,0));
00050         sel.push_back(Vect3Di(0,0,-1));
00051         sel.push_back(Vect3Di(0,0, 1));
00052     }
00053     else
00054     {
00055         for(uint i=0;i<psel->size();i++){sel.push_back(psel->GetPoint(i));}
00056     }
00057 
00058     for(uint it=0;;it++)
00059     {
00060         Vect3Di p=path.back();
00061         int smin=-1;
00062         int imin=-1;
00063         for(uint i=0;i<sel.size();i++)
00064         {
00065             Vect3Di npos=p+sel[i];
00066             if(ssCode.IsInside(npos) && ssCode(npos)>=0 && (imin<0 || ssCode(npos)<smin))
00067             {
00068                 smin=ssCode(npos);
00069                 imin=i;
00070             }
00071         }
00072         p=p+sel[imin];
00073         path.push_back(p);
00074         if(stopCB.first && stopCB.first(stopCB.second,p)){p.Show("CShortestPathExtraction:stoping at:","\n");break;}
00075         if(smin==0){break;}
00076     }
00077 
00078     if(!pssCode0){delete pssCode;}
00079 }
00080 void
00081 IP3D::VoxelCode(const Mask3D &shape,const Mask3D &seeds,int m0,int m1,int m2,LabelImage3D& res,const Vect3Di *stopPoint)
00082 {
00083     int metric[3]={m0,m1,m2};
00084     Mask3D s=seeds;
00085     s.Normalize();
00086     s.Invert();
00087     LabelImage3D codes;
00088     codes=s;
00089     int inf=(codes.Width()+codes.Height()+codes.Depth())*10;
00090     codes*=inf;
00091     
00092 
00093     // Build f,e,v neighborhoods
00094     StructureElement fNeighbors(MORPHO_Cross7);
00095     StructureElement planar19(MORPHO_Planar19);
00096     StructureElement cubic27(MORPHO_Cubic27);
00097     vector<Vect3Di> ePoints;
00098     for(uint i=0;i<planar19.size();i++)
00099     {
00100         if(fNeighbors.FindPoint(planar19[i])<0)
00101         {
00102             ePoints.push_back(planar19[i]);
00103         }
00104     }
00105     vector<Vect3Di> vPoints;
00106     for(uint i=0;i<cubic27.size();i++)
00107     {
00108         if(planar19.FindPoint(cubic27[i])<0)
00109         {
00110             vPoints.push_back(cubic27[i]);
00111         }
00112     }
00113     StructureElement eNeighbors(ePoints);
00114     StructureElement vNeighbors(vPoints);
00115 
00116     fNeighbors.RemoveCenter();
00117 //      eNeighbors.RemoveCenter();
00118 //      vNeighbors.RemoveCenter();
00119 
00120     StructureElement *neighbors[3];
00121     neighbors[0]=&fNeighbors;
00122     neighbors[1]=&eNeighbors;
00123     neighbors[2]=&vNeighbors;
00124 
00125     // keeping track of voxels that changed
00126     vector<Vect3Di> changedPts;// pts that changed on last iteration
00127     changedPts.reserve(shape.GetNVoxels());
00128     // initialize with seed points
00129     for(LabelImage3D::iteratorXYZ p=codes.begin();p!=codes.end();++p)
00130     {
00131         if(*p!=inf){changedPts.push_back(p.Pos());}
00132     }
00133     // image containing iteration at which a point last changed
00134     // this is used to avoid adding several times the same point to the changed list 
00135     // on the same iteration
00136     LabelImage3D changedIm(shape.Size());
00137     changedIm.Fill(-1);
00138 
00139     // iterate 
00140     for(int it=0;;it++)
00141     {
00142         bool changed=false;
00143         vector<Vect3Di> newChangedPts;
00144         newChangedPts.reserve(shape.GetNVoxels());
00145         LabelImage3D newCodes=codes;
00146         printf("it:%3d changedPts:%4d\n",it,changedPts.size());
00147         // for each point that changed on the last iter, propagate codes
00148         for(uint pt=0;pt<changedPts.size();pt++)
00149         {
00150             const Vect3Di &pos=changedPts[pt];
00151             // for f,e,v neighborhoods
00152             for(int n=0;n<3;n++)
00153             {
00154                 // replace neighbors  code by new code if necessary
00155                 for(uint i=0;i<neighbors[n]->size();i++)
00156                 {
00157                     Vect3Di npos=pos+neighbors[n]->GetPoint(i);
00158                     int nval=codes(pos)+metric[n];
00159                     if(shape.IsInside(npos) && shape(npos) && nval<newCodes(npos))
00160                     {
00161                         newCodes(npos)=nval;
00162                         if(changedIm(npos)!=it){changedIm(npos)=it;newChangedPts.push_back(npos);}
00163                         changed=true;
00164                     }
00165                 }
00166             }
00167         }
00168         codes=newCodes;
00169         if(0)
00170         {
00171             Mask3D changedDisp;
00172             IP3D::SimpleThresholds2(changedIm,it,it,changedDisp);
00173             ImLib3DDisplay::Show(changedDisp);
00174             ImLib3DDisplay::Show(codes);
00175         }
00176 //          codes.WriteToFile(SPrintf("codes_%02d.im3D",it));
00177         if(stopPoint && changedIm(*stopPoint)!=-1){break;}
00178         if(!changed){break;}
00179         changedPts=newChangedPts;
00180     }
00181     // change inf to -1
00182     for(LabelImage3D::iteratorXYZ p=codes.begin();p!=codes.end();++p)
00183     {
00184         if(*p==inf){*p=-1;}
00185     }
00186     res=codes;
00187 }
00188 
00189 PathGraph::Path::Path(PathGraph::Node *_first,PathGraph::Node *_last,const vector<Vect3Di> &_points):
00190     first(_first),last(_last),points(_points)
00191 {
00192     first->point=points[0];
00193     last ->point=points.back();
00194     first->paths.push_back(this);
00195     last ->paths.push_back(this);
00196 }
00197 void
00198 SkeletonExtraction::MedPathFind::ExtractPath(SkeletonExtraction *se)
00199 {
00200     path.clear();
00201     Vect3Di start=cpath[0]->MedialPoint(cpath[0]->center);
00202     IP3D::ShortestPathExtraction(Mask3D(),start,Vect3Di(),path,NULL,NULL,&se->ssCode,
00203                                            IP3D::ShortestPathExtractionHelpers::StopCBType(&SkeletonExtraction::SpeStopCB,se));
00204     if(se->clustersIma(path.back())->stop>=0)
00205     {
00206         path.back().Show("ExtractPath: found stop at:","\n");
00207         id2=se->clustersIma(path.back())->stop;
00208     }
00209     else{   path.back().Show("ExtractPath: no stop at:","\n");}
00210     if(cpath.size()!=1){ThrowError("MedPathFind::ExtractPath Should only be start cluster in cpath");}
00211     for(uint i=1;i<path.size();i++)
00212     {
00213         cpath.push_back(se->clustersIma(path[i]));
00214     }
00215 }
00216     
00217 void
00218 SkeletonExtraction::ExtractAllMedPaths(vector<MedPathFind> &medPaths)
00219 {
00220     //init
00221     medPaths.clear();
00222     for(uint i=0;i<clusters.size();i++){clusters[i]->stop=-1;}
00223     // sorted LM clusters 
00224     for(uint i=0;i<clusters.size();i++)
00225     {
00226         if(clusters[i]->isLM){medPaths.push_back(MedPathFind(clusters[i]));}
00227     }
00228     sort(medPaths.begin(),medPaths.end());
00229     reverse(medPaths.begin(),medPaths.end());
00230 
00231     for(uint mi=0;mi<medPaths.size();mi++)
00232     {
00233 //          MedPathFind &mpf=medPaths[i];
00234         printf("medpath %d:startcluster :%x val:%d\n",mi,(uint)medPaths[mi].cpath[0],medPaths[mi].cpath[0]->value);
00235         medPaths[mi].id=mi;
00236         medPaths[mi].ExtractPath(this);
00237         medPaths[mi].path[0].Show("first point:","\n");
00238 
00239         for(uint pti=0;pti<medPaths[mi].path.size();pti++)
00240         {
00241             Vect3Di pt=medPaths[mi].path[pti];
00242             CodeCluster *c=clustersIma(pt);
00243             Vect3Di mp=c->MedialPoint(pt);
00244             printf("medpath % 3d : cluster:%x pti:% 3d  ",mi,(uint)c,pti);
00245             pt.Show("pt:","     ");
00246             mp.Show("mp:","\n");
00247 
00248             // create new medpaths if this is a branching cluster
00249             vector<CodeCluster *> s;
00250             c->GetSuccessors(s);
00251             for(uint j=0;j<s.size();j++)
00252             {
00253                 if(pti+1<medPaths[mi].path.size() && 
00254                    s[j]!=clustersIma(medPaths[mi].path[pti+1]))
00255                 {
00256                     printf("branches %d\n",medPaths.size());
00257                     medPaths.push_back(MedPathFind(s[j]));
00258                     medPaths.back().id1=mi;
00259                     medPaths.back().parentCluster=c;
00260                 }
00261             }
00262             medPaths[mi].path[pti]=mp;
00263             c->stop=mi;
00264         }
00265     }
00266 }
00267 void 
00268 SkeletonExtraction::PathGraphFromMedPaths(vector<MedPathFind> &medPaths,PathGraph &pathGraph)
00269 {
00270 
00271     ssCode.WriteToFile("ssCode.im3D");
00272     // display medPaths
00273     {
00274         Image3Df disp(clustersIma.Size());disp.Fill(-1);
00275         for(uint i=0;i<medPaths.size();i++)
00276         {
00277             MedPathFind &mpf=medPaths[i];
00278             for(uint j=0;j<mpf.path.size();j++)
00279             {
00280                 disp(mpf.path[j])=i+j*.5/mpf.path.size();
00281             }
00282         }
00283         disp.WriteToFile("medpaths.im3D");
00284     }
00285     // add first point to branching medpaths
00286     for(uint i=0;i<medPaths.size();i++)
00287     {
00288         MedPathFind &mpf=medPaths[i];
00289         if(mpf.id1>=0)
00290         {
00291             mpf.cpath.insert(mpf.cpath.begin(),mpf.parentCluster);
00292             mpf. path.insert(mpf. path.begin(),mpf.parentCluster->MedialPoint(mpf.path[0]));
00293         }
00294     }
00295 
00296     // create empty branches for each point in each medpath
00297     for(uint i=0;i<medPaths.size();i++)
00298     {
00299         MedPathFind &mpf=medPaths[i];
00300         mpf.branches.assign(mpf.path.size(),vector<int>());
00301     }
00302     // mark branching points
00303     for(uint i=0;i<medPaths.size();i++)
00304     {
00305         MedPathFind &mpf=medPaths[i];
00306         if(mpf.id1>=0)
00307         {
00308             MedPathFind &other=medPaths[mpf.id1];
00309             other.branches[other.FindCluster(mpf.cpath[0])].push_back(i);
00310         }
00311         if(mpf.id2>=0)
00312         {
00313             MedPathFind &other=medPaths[mpf.id2];
00314             other.branches[other.FindCluster(mpf.cpath.back())].push_back(i);
00315         }
00316     }
00317     // display branching
00318     {
00319         for(uint i=0;i<medPaths.size();i++)
00320         {
00321             MedPathFind &mpf=medPaths[i];
00322             printf("medpath %3d : id1:%3d id2:%3d\n",i,mpf.id1,mpf.id2);
00323             for(uint j=0;j<mpf.path.size();j++)
00324             {
00325                 printf("cluster %x, ",(uint)mpf.cpath[j]);
00326                 mpf.path[j].Show("pt:"," ");
00327                 if(mpf.branches[j].size()>0)
00328                 {
00329                     printf("branches (%d):",mpf.branches[j].size());
00330                     for(uint k=0;k<mpf.branches[j].size();k++){printf("%d ",mpf.branches[j][k]);}
00331                 }
00332                 printf("\n");
00333             }
00334         }
00335     }
00336     // create nodes
00337     for(uint i=0;i<medPaths.size();i++)
00338     {
00339         MedPathFind &mpf=medPaths[i];
00340         mpf.branchNodes.assign(mpf.path.size(),NULL);
00341         if(mpf.id1<0){mpf.branchNodes[0]=pathGraph.AddNode();}
00342         for(uint j=1;j<mpf.path.size()-1;j++)
00343         {
00344             if(mpf.branches[j].size()>0)
00345             {
00346                 mpf.branchNodes[j]=pathGraph.AddNode();
00347             }
00348         }
00349         if(mpf.id2<0){mpf.branchNodes[mpf.path.size()-1]=pathGraph.AddNode();}
00350     }
00351     // merge nodes, until all nodes set
00352     bool changed=false;
00353     while(!changed)
00354     {
00355         printf("merging\n");
00356         for(uint i=0;i<medPaths.size();i++)
00357         {
00358             MedPathFind &mpf=medPaths[i];
00359             if(mpf.id1>=0 && !mpf.branchNodes[0])
00360             {
00361                 MedPathFind &other=medPaths[mpf.id1];
00362                 int opos=other.FindCluster(mpf.cpath[0]);
00363                 mpf.branchNodes[0]=other.branchNodes[opos];
00364                 changed=true;
00365             }
00366             int l=mpf.path.size()-1;
00367             if(mpf.id2>=0 && !mpf.branchNodes[l])
00368             {
00369                 MedPathFind &other=medPaths[mpf.id2];
00370                 int opos=other.FindCluster(mpf.cpath[l]);
00371                 mpf.branchNodes[l]=other.branchNodes[opos];
00372                 changed=true;
00373             }
00374         }
00375     }
00376     // create paths
00377     for(uint i=0;i<medPaths.size();i++)
00378     {
00379         MedPathFind &mpf=medPaths[i];
00380         PathGraph::Node *first=mpf.branchNodes[0];
00381         int j0=0;
00382         for(uint j=1;j<mpf.path.size();j++)
00383         {
00384             PathGraph::Node *node=mpf.branchNodes[j];
00385             if(node)
00386             {
00387                 pathGraph.AddPath(first,node,vector<Vect3Di>(mpf.path.begin()+j0,mpf.path.begin()+j+1));
00388                 j0=j;
00389                 first=node;
00390             }
00391         }
00392     }
00393     // reunite node points
00394     for(uint i=0;i<pathGraph.paths.size();i++)
00395     {
00396         pathGraph.paths[i]->points[0]    =pathGraph.paths[i]->first->point;
00397         pathGraph.paths[i]->points.back()=pathGraph.paths[i]->last ->point;
00398     }
00399     // display paths
00400     {
00401         Image3Df disp(clustersIma.Size());disp.Fill(-1);
00402         for(uint i=0;i<pathGraph.paths.size();i++)
00403         {
00404             vector<Vect3Di> &pts=pathGraph.paths[i]->points;
00405             for(uint j=0;j<pts.size();j++)
00406             {
00407                 disp(pts[j])=i+j*.5/pts.size();
00408             }
00409         }
00410         disp.WriteToFile("paths.im3D");
00411     }
00412 
00413 }
00414 
00415 void 
00416 SkeletonExtraction::ExtractPaths(PathGraph &pathGraph)
00417 {
00418     vector<MedPathFind> medPaths;
00419     ExtractAllMedPaths(medPaths);
00420     PathGraphFromMedPaths(medPaths,pathGraph);
00421     // fill in between points too far from each other
00422     for(uint i=0;i<pathGraph.paths.size();i++)
00423     {
00424         vector<Vect3Di> &pts=pathGraph.paths[i]->points;
00425         for(uint j=1;j<pts.size();j++)
00426         {
00427             Vect3Di dd=pts[j-1]-pts[j];
00428             if(abs(dd.x)>1 || abs(dd.y)>1 || abs(dd.z)>1)
00429             {
00430                 vector<Vect3Di> inspts;
00431                 ExtractLinkingMedPath(shape,pts[j-1],pts[j],inspts);
00432                 if(inspts.size()>2)
00433                 {
00434                     pts.insert(pts.begin()+j,inspts.begin()+1,inspts.end()-1);
00435                     j+=inspts.size()-2;
00436                 }
00437             }
00438         }
00439     }
00440 }
00441 
00442 void 
00443 SkeletonExtraction::CreateClusters()
00444 {
00445     clusters.clear();
00446     clustersIma.Resize(ssCode.Size());
00447     clustersIma.Fill(NULL);
00448 
00449     LabelImage3D labels;
00450     int bg=-1;
00451     IP3D::ConnectedComponentLabelling(ssCode,StructureElement(MORPHO_Cubic27),labels,&bg);
00452 //          ImLib3DDisplay::Show(labels);
00453     // for each unique label, create a new cluster  
00454     map<int,CodeCluster *> label2cluster;// used for keeping track of unique labels
00455     for(LabelImage3D::iteratorXYZ p=labels.begin();p!=labels.end();++p)
00456     {
00457         if(ssCode(p.Pos())<0){continue;}
00458         int label=*p;
00459         CodeCluster *cluster;
00460         // if we haven't seen this label yet, create a new cluster
00461         if(label2cluster.find(label)==label2cluster.end())
00462         {
00463             printf("new cluster:label %d value:%d\n",label,ssCode(p.Pos()));
00464             cluster=new CodeCluster(ssCode(p.Pos()));
00465             label2cluster[label]=cluster;
00466             clusters.push_back(cluster);
00467         }
00468         else{cluster=label2cluster[label];}
00469                 
00470         clustersIma(p.Pos())=cluster;
00471         cluster->points.push_back(p.Pos());
00472     }
00473 
00474 }
00475 void 
00476 SkeletonExtraction::InitClusters()
00477 {
00478     for(uint i=0;i<clusters.size();i++)
00479     {
00480         clusters[i]->Init();
00481     }
00482     for(uint i=0;i<clusters.size();i++)
00483     {
00484         clusters[i]->FindNeighbors(clustersIma);
00485     }
00486     for(uint i=0;i<clusters.size();i++)
00487     {
00488         clusters[i]->FindType();
00489     }
00490     for(uint i=0;i<clusters.size();i++)
00491     {
00492         clusters[i]->FindMedialPoints(bsCode);
00493     }
00494 }
00495 SkeletonExtraction::SkeletonExtraction(const Mask3D &_shape,const Vect3Di *psourcePoint):shape(_shape)
00496 {
00497     Vect3Di sourcePoint(-1,0,0);
00498     if(psourcePoint){sourcePoint=*psourcePoint;}
00499     else
00500     {
00501         // automatically find seed point
00502         for(Mask3D::const_iteratorXYZ p=shape.begin();p!=shape.end();++p){if(*p){sourcePoint=p.Pos();break;}}
00503         sourcePoint.Show("SkeletonExtraction: p0:","\n");
00504         if(sourcePoint.x<0){ThrowError("SkeletonExtraction::SkeletonExtraction shape is empty");}
00505         LabelImage3D ss0;
00506         IP3D::SSCode(shape,sourcePoint,1,2,3,ss0);
00507         Vect3Di dud;
00508         int dud0,dud1;
00509         IP3D::FindMinMax(ss0,dud0,dud1,&dud,&sourcePoint);
00510         sourcePoint.Show("SkeletonExtraction: auto sourcePoint:","\n");
00511     }
00512     IP3D::SSCode(shape,sourcePoint,1,2,3,ssCode);
00513     IP3D::BSCode(shape,1,2,3,bsCode);
00514     CreateClusters();
00515     InitClusters();
00516 }
00517 void 
00518 SkeletonExtraction::ExtractAll(const Mask3D &shape,PathGraph &pathGraph)
00519 {
00520     LabelImage3D components;
00521     IP3D::ConnectedComponentLabelling(shape,StructureElement(MORPHO_Cross7),components);
00522     int cmin,cmax;
00523     IP3D::FindMinMax(components,cmin,cmax);
00524 
00525     for(int i=1;i<=cmax;i++)
00526     {
00527         printf("SkeletonExtraction::ExtractAll indep component %d out of %d\n",i,cmax);
00528         Mask3D simpleShape;
00529         IP3D::SimpleThresholds2(components,i,i,simpleShape);
00530         SkeletonExtraction skel(simpleShape);
00531         skel.ExtractPaths(pathGraph);
00532     }
00533 }
00534 RectZone3Di
00535 PathGraph::BoundingBox() const
00536 {
00537     Vect3Di p0=paths[0]->points[0],p1=p0;
00538     for(uint i=0;i<paths.size();i++)
00539     {
00540         for(uint j=0;j<paths[i]->points.size();j++)
00541         {
00542             p0=min(p0,paths[i]->points[j]);
00543             p1=max(p1,paths[i]->points[j]);
00544         }
00545     }
00546     return RectZone3Di(p0,p1);
00547 }
00548 void
00549 SkeletonExtraction::DrawPathGraph(const PathGraph &pathGraph,Mask3D &res)
00550 {
00551     Vect3Di p0(0,0,0);
00552     if(res.Size()==Size3D(0,0,0))
00553     {
00554         RectZone3Di box=pathGraph.BoundingBox();
00555         res.Resize(Size3D(box.Width(),box.Height(),box.Depth()));
00556         p0=box.GetP0();
00557         res.properties.iCenter=new Vect3Di(p0);
00558     }
00559     for(uint i=0;i<pathGraph.paths.size();i++)
00560     {
00561         for(uint j=0;j<pathGraph.paths[i]->points.size();j++)
00562         {
00563             if(!res.IsInside(pathGraph.paths[i]->points[j]-p0))
00564             {
00565                 ThrowError("SkeletonExtraction::DrawPathGraph point outside of image");
00566             }
00567             res(pathGraph.paths[i]->points[j]-p0)=1;
00568         }
00569     }
00570 }
00571 void 
00572 SkeletonExtraction::ExtractLinkingMedPath(const Mask3D &shape,const Vect3Di &start,const Vect3Di &end,vector<Vect3Di> &res)
00573 {
00574     if(start==end){res.push_back(start);res.push_back(end);return;}
00575     if(0)
00576     {// draw a line between both points
00577         int jmx=0,dmx=abs(end(0)-start(0));
00578         for(int j=1;j<3;j++)
00579         {
00580             int dd=abs(end(j)-start(j));
00581             if(dd>dmx){dmx=dd;jmx=j;}
00582         }
00583         int j=jmx;
00584         Vect3Di p0=start,p1=end;
00585         if(p1(j)<p0(j)){p1=start;p0=end;}
00586         for(int i=p0(j);i<=p1(j);i++)
00587         {
00588             Vect3Df p=(Vect3Df)p0+((i-p0(j))/(float)(p1(j)-p0(j)))*((Vect3Df)(p1-p0));
00589             res.push_back(floor(p+Vect3Df(.5,.5,.5)));
00590         }
00591     }
00592 //      SkeletonExtraction skel(shape,&end);
00593 //      vector<Vect3Di> path;
00594     res.clear();
00595     IP3D::ShortestPathExtraction(shape,start,end,res);
00596 //      skel.MedPathFromPath(path,res);
00597 }

Generated on Fri Jun 17 13:36:09 2005 for ImLib3D by  doxygen 1.4.2