#include "objJedi.h" #include #include #include #include //itk includes #include "../../VesselAnalysis/VesselAnalysis/VOL_to_ITK.h" #include "itkDanielssonDistanceMapImageFilter.h" #include "itkBinaryThresholdImageFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkExpandImageFilter.h" #include "itkDiscreteGaussianImageFilter.h" #include "itkCastImageFilter.h" /* This code uses the octree library designed by Simon Perreault (http://nomis80.org/code/octree.html)*/ #include "octree/octree.h" #define OCTREE_SIZE 1024 #define BUFFER_ZONE 0 using namespace std; //definitions for simple structures typedef vector> FilamentType; typedef point3D CoordType; typedef vector NetworkType; struct EdgeType { unsigned int v0; unsigned int v1; float avg_radius; float min_radius; float max_radius; float volume; float length; bool valid; }; struct NodeType { CoordType p; vector edges; bool valid; }; struct ConnectType //this structures stores the info necessary to combine two fibers { unsigned int edge0; unsigned int edge1; unsigned int node0; unsigned int node1; list::iterator fiber0; list::iterator fiber1; }; struct ConnectableType { FilamentType fiber; bool front; bool back; unsigned int id; }; struct AttributeType { float TotalFiberLength; int NumFibers; int NumPoints; float TotalVolume; int NumDisconnectedFibers; int NumBoundaryFibers; int NumBifurcations; }; struct BranchType { float angle; unsigned int branch_id; }; class rtsNetwork { private: int id; //used for swc output ofstream outfile; //also for swc NetworkType network; //list of filaments that make up the network vector NodeList; //vector of endpoints vector EdgeList; //vector of vessels as edges vector3D boundary; vector3D position; float calcFiberLength(unsigned int f); float calcFiberBend(unsigned int f); float calcTotalLength(); bool isBoundaryNode(unsigned int node); vector3D calcTrajectory(FilamentType f, bool end); bool compareTrajectories(CoordType p0, vector3D v0, CoordType p1, vector3D v1, float distance, float cosine_angle); void calcGraph(); vector getConnections(unsigned int node); CoordType toTissue(CoordType grid_coord); FilamentType combineFibers(FilamentType f0, bool endpoint0, FilamentType f1, bool endpoint1); bool isConnectable(ConnectableType c0, ConnectableType c1, bool& c0_end, bool& c1_end, float distance, float cosine_angle); void traverseGraph(int node, int parent_id); void outputFiberToSWC(int edge, int node_from, int parent_id); vector getFiberAngles(unsigned int fiber); void reset(); public: //attribute variables AttributeType Attributes; //control variables point3D voxel_size; //cleaning int cleanDegenerateEdges(float length); int cleanRedundantEdges(); int cleanBarbs(float length); //methods rtsOBJ LoadOBJModel(const char* filename); void AddOBJModel(const char* filename); void SaveSWCModel(const char *filename, float root_x, float root_y); void SaveSWCModel(const char *filename); void Clean(float degenerate_length, float barb_length); void CalculateAttributes(); rtsOBJ Repair(float max_distance, float cos_angle); rtsOBJ CreateModel(); void printFiberStats(unsigned int fiber); void ReMesh(float resample_length); void SaveAngles(const char* filename); void SaveBends(const char* filename); void SaveLengths(const char* filename); void SaveMathematicaGraph(const char* filename); void SaveMathematicaNodeDistances(const char* filename); void SetVoxelSize(float dx, float dy, float dz){voxel_size.x = dx; voxel_size.y = dy; voxel_size.z = dz;} void RemoveTerminalComponents(); void RemoveBoundaryComponents(); void GetRadiusFromVolume(const char* filename, unsigned int threshold, unsigned int range); void GetRadiusFromDistanceMap(const char *filename, int range); point3D GetFiberRadius(unsigned int fiber); void ScaleNetwork(float x, float y, float z); void SetOutputPosition(float x, float y, float z); }; //private void rtsNetwork::reset() { vector::iterator e; for(e=EdgeList.begin(); e!=EdgeList.end(); e++) (*e).valid = true; vector::iterator n; for(n=NodeList.begin(); n!=NodeList.end(); n++) (*n).valid = true; } float rtsNetwork::calcFiberBend(unsigned int f) { /*calculates the bend of a fiber.*/ //find the line connecting the two endpoints point3D p1 = toTissue(NodeList[EdgeList[f].v0].p); point3D p2 = toTissue(NodeList[EdgeList[f].v1].p); //find the largest distance between the filament and the line between endpoints FilamentType::iterator i; float max_distance = 0; for(i = network[f].begin(); i!= network[f].end(); i++) { point3D p0 = toTissue((*i)); //find the distance between the current point and the line made by the endpoints float d = ((p0 - p1).X(p0-p2).Length())/(p2-p1).Length(); if(d > max_distance) max_distance = d; } float fiber_length = calcFiberLength(f); if(max_distance/fiber_length > 0.5) { cout<<"Something's wrong."< length) { newNetwork.push_back(network[e]); removed--; } } //if the fiber is not an end fiber else { newNetwork.push_back(network[e]); removed--; } } network.clear(); network = newNetwork; calcGraph(); cout<<"Barbs removed: "<*/ //form lists of degenerate and non-degenerate edges int removed = 0; list degenerate; list complete; //iterate through each fiber and put it in the appropriate list int numFibers = network.size(); int f; ConnectableType c; for(f=0; f connected_edges; vector shared_vertices; int numEdges, numVerts, e, e0, e1, v; int connectedEdge; //for each degenerate edge list::iterator i; for(i=degenerate.begin(); i!=degenerate.end(); i++) { /*DEGENERATE CASE 1 In this case, we remove edges that are parts of very small cycles (ex short edges that bridge between a bifurcation, forming a small triangle). */ //get all of the edges connected to that edge connected_edges.clear(); //add edges connected to v0 v = EdgeList[(*i).id].v0; numEdges = NodeList[v].edges.size(); for(e=0; e3 && NodeList[EdgeList[(*i).id].v0].edges.size() >3) { delete_fiber = true; removed++; EdgeList[(*i).id].valid = false; } if(!delete_fiber) complete.push_back((*i)); } //re-create the network from NetworkType newNetwork; for(i = complete.begin(); i!=complete.end(); i++) newNetwork.push_back((*i).fiber); network = newNetwork; calcGraph(); return removed; } bool rtsNetwork::isBoundaryNode(unsigned int node) { CoordType p; p = NodeList[node].p; if(NodeList[node].edges.size() > 1) return false; if(p.x > BUFFER_ZONE && p.y > BUFFER_ZONE && p.z > BUFFER_ZONE && p.x < boundary.x - BUFFER_ZONE && p.y < boundary.y - BUFFER_ZONE && p.z < boundary.z - BUFFER_ZONE) return false; else return true; } bool rtsNetwork::isConnectable(ConnectableType c0, ConnectableType c1, bool& c0_end, bool& c1_end, float distance, float cosine_angle) { //tests to see if two ConnectableType fibers can be connected. If so, the function //returns true as well as the optimal connected endpoints as and . //For these parameters, 0 = the first point on the fiber and 1 = the last point. if(c0.id == 1381 && c1.id == 1407) cout<<"here"< t0; vector3D t1; if(c0.front == 0) { t0 = calcTrajectory(c0.fiber, 0); if(c1.front == 0) { t1 = calcTrajectory(c1.fiber, 0); if(compareTrajectories(c0.fiber.front(), t0, c1.fiber.front(), t1, distance, cosine_angle)) { c0_end = 0; c1_end = 0; return true; } } if(c1.back == 0) { t1 = calcTrajectory(c1.fiber, 1); if(compareTrajectories(c0.fiber.front(), t0, c1.fiber.back(), t1, distance, cosine_angle)) { c0_end = 0; c1_end = 1; return true; } } } if(c0.back == 0) { t0 = calcTrajectory(c0.fiber, 1); if(c1.front == 0) { t1 = calcTrajectory(c1.fiber, 0); if(compareTrajectories(c0.fiber.back(), t0, c1.fiber.front(), t1, distance, cosine_angle)) { c0_end = 1; c1_end = 0; return true; } } if(c1.back == 0) { t1 = calcTrajectory(c1.fiber, 1); if(compareTrajectories(c0.fiber.back(), t0, c1.fiber.back(), t1, distance, cosine_angle)) { c0_end = 1; c1_end = 1; return true; } } } return false; } FilamentType rtsNetwork::combineFibers(FilamentType f0, bool endpoint0, FilamentType f1, bool endpoint1) { //create the new filament FilamentType newFilament; //insert the first filament //if the valence-1 vertex is at the beginning if(endpoint0 == 0) { //add the edge backwards for(int v=f0.size() - 1; v>=0; v--) newFilament.push_back(f0[v]); } else { //otherwise we can just copy the filament over newFilament = f0; } //insert the second filament //if the valence-1 vertex is at the beginning if(endpoint1 == 0) { //add the edge in forwards for(int v=0; v=0; v--) newFilament.push_back(f1[v]); } return newFilament; } vector3D rtsNetwork::calcTrajectory(FilamentType f, bool end) { //calculates the trajectory of a fiber at the given endpoint (0 = first, 1 = last) CoordType pEndpoint; CoordType pPrevpoint; if(end == 0) { pEndpoint = f.front(); pPrevpoint = f[1]; //pPrevpoint = f[ceil(f.size()/4.0)]; } else { pEndpoint = f.back(); pPrevpoint = f[f.size() - 2]; //pPrevpoint = f[floor(f.size()*3.0/4.0)]; } //pPrevpoint = f[f.size()/2]; vector3D result = toTissue(pEndpoint) - toTissue(pPrevpoint); result.Normalize(); return result; } bool rtsNetwork::compareTrajectories(CoordType p0, vector3D v0, CoordType p1, vector3D v1, float distance, float cosine_angle) { //determines of two points/trajectory pairs are compatible for connection vector3D difference = p1 - p0; //test distance if(difference.Length() <= distance) { difference.Normalize(); if(v0*difference >= cosine_angle && v1*difference <= -cosine_angle) return true; } return false; } vector rtsNetwork::getConnections(unsigned int node) { return NodeList[node].edges; } point3D rtsNetwork::toTissue(CoordType grid_coord) { //converts a coordinate from the original voxel grid coordinates to tissue-space coordinates return CoordType(grid_coord.x * voxel_size.x, grid_coord.y * voxel_size.y, grid_coord.z * voxel_size.z); //return grid_coord; } float rtsNetwork::calcFiberLength(unsigned int f) { //This function calculates the total length of the given fiber float result = 0.0; //find the length of each fiber int vertNum, v; vertNum = network[f].size(); for(v=1; v outPoint; //if we are traversing the fiber front-to-back if(EdgeList[e].v0 == node_from) { if(parent_id == -1) { p=0; outPoint = network[e][p] + position; outfile<=0; p--) { outPoint = network[e][p] + position; outfile< rootSeed(root_x, root_y, 0); float distance = 9999999; int closest_node; //find the closest node to the root seed for(node = 0; node != NodeList.size(); node++) { float length = (NodeList[node].p - rootSeed).Length(); if(length < distance) { closest_node = node; distance = length; cout< Max) Max = temp; } //place each point ID inside the octree unsigned int id = 0; Octree tree(OCTREE_SIZE); tree.setEmptyValue(UINT_MAX); unsigned int v0, v1; for(f=0; f"< complete; list incomplete; cout<<"Finding Valence-1 Fibers..."< int numFibers = network.size(); int f; unsigned int v0; unsigned int v1; int numVerts, v; for(f=0; f::iterator i; list::iterator j; list::iterator temp; FilamentType newFiber; bool endpoint0, endpoint1; int iIndex, jIndex; iIndex = 0; for(i=incomplete.begin(); i!=incomplete.end(); i++) { jIndex = iIndex; j = i; while(j != incomplete.end() && i != incomplete.end()) { if(i!=j) { if(isConnectable((*i), (*j), endpoint0, endpoint1, max_distance, cos_angle)) { ConnectableType newConnectable; newConnectable.fiber = combineFibers((*i).fiber, endpoint0, (*j).fiber, endpoint1); //determine the new connectivity if(endpoint0 == 0) newConnectable.front = (*i).back; else newConnectable.front = (*i).front; if(endpoint1 == 0) newConnectable.back = (*j).back; else newConnectable.back = (*j).front; //remove the previous connectables temp = i; i++; iIndex++; incomplete.erase(temp); temp = j; //this is just in case the fibers are next to each otehr if(j == i) { i++; iIndex++; j = i; jIndex = iIndex; } else { j = i; jIndex = iIndex; } incomplete.erase(temp); //insert the new connectable in the proper list if(newConnectable.front == 0 || newConnectable.back == 0) incomplete.push_back(newConnectable); else complete.push_back(newConnectable); //numVerts = newConnectable.fiber.size(); //preview.objBegin(OBJ_LINE_STRIP); //for(v=0; v newNetwork; for(i=incomplete.begin(); i!=incomplete.end(); i++) newNetwork.push_back((*i).fiber); for(i=complete.begin(); i!=complete.end(); i++) newNetwork.push_back((*i).fiber); network = newNetwork; calcGraph(); //build the model to return preview = CreateModel(); return preview; } void rtsNetwork::printFiberStats(unsigned int fiber) { /* cout<<"-----------------------------------------------------"< currentTrajectory; vector3D branchTrajectory; unsigned int f; //get the list of angles vector angles = getFiberAngles(fiber); unsigned int node = EdgeList[fiber].v0; int numBranches = NodeList[node].edges.size()-1; cout<<"V0 Valence: "< distanceVector; point3D prevPoint; for(f=0; f= resample_length) { newFiber.push_back(network[f][v]); prevPoint = network[f][v]; } } //deal with the last vertex //newFiber.push_back(network[f][v]); distanceVector = toTissue(network[f][v]) - toTissue(prevPoint); if(distanceVector.Length() >= resample_length || newFiber.size() == 1) { newFiber.push_back(network[f][v]); } else { //change the last point newFiber[newFiber.size() - 1] = network[f][v]; } //cout<<"old: "< angles = getFiberAngles(f); for(int a=0; a179 && angles[a].angle < 181) { //cout<<"ERROR"<90 && angles[a].angle < 90.01) { //cout<<"ERROR"<2) outfile<2) outfile<"<"< distance; outfile<<"d = {"; for(i=0; iSetSpacing(spacing); SaveSlice(volume, 20, "original.bmp"); //volume->ReleaseDataFlagOn(); /* cout<<"Expanding Image..."< ResizeFilterType; ResizeFilterType::Pointer resizeFilter = ResizeFilterType::New(); resizeFilter->SetExpandFactors(resample); resizeFilter->SetInput(volume); resizeFilter->Update(); VOLType::Pointer resizedImage = resizeFilter->GetOutput(); SaveSlice(resizedImage, 20, "resized.bmp"); cout<<"done."< BlurFilterType; BlurFilterType::Pointer blurFilter = BlurFilterType::New(); blurFilter->SetUseImageSpacingOn(); float variance[3] = {0.61, 1.5, 0.61}; blurFilter->SetVariance(variance); blurFilter->SetMaximumKernelWidth(8); blurFilter->SetInput(volume); try { blurFilter->Update(); } catch(itk::ExceptionObject & exp) { std::cout<GetOutput(); typedef itk::CastImageFilter CastingFilterType; CastingFilterType::Pointer castFilter = CastingFilterType::New(); castFilter->SetInput(blurredImage); castFilter->Update(); VOLType::Pointer resizedImage = castFilter->GetOutput(); SaveSlice(resizedImage, 20, "resized.bmp"); //threshold the input image cout<<"Thresholding Volume..."< ThresholdFilterType; ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInsideValue(255); thresholdFilter->SetOutsideValue(0); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(255); thresholdFilter->SetInput(resizedImage); try { thresholdFilter->Update(); } catch(itk::ExceptionObject & exp) { std::cout<GetOutput(); //volume->ReleaseData(); //thresholdFilter->ReleaseDataFlagOn(); SaveSlice(thresholdImage, 20, "threshold.bmp"); cout<<"done."< DistanceFilterType; DistanceFilterType::Pointer distanceFilter = DistanceFilterType::New(); distanceFilter->SetInput(thresholdImage); distanceFilter->UseImageSpacingOn(); try { distanceFilter->Update(); } catch(itk::ExceptionObject & exp) { std::cout<GetOutput(), 20, "distance.bmp"); FloatType::Pointer distanceMap = distanceFilter->GetOutput(); //SaveFloatRAW(distanceMap, "distanceMap.raw"); cout<<"done."< NeighborhoodIteratorType; NeighborhoodIteratorType::RadiusType radius; radius.Fill(range); NeighborhoodIteratorType i(radius, distanceMap, distanceMap->GetBufferedRegion()); vector OffsetVector; OffsetVector.resize((range*2+1)*(range*2+1)*(range*2+1)); int x, y, z, j; j=0; for(x=-range; x<=range; x++) for(y=-range; y<=range; y++) for(z=-range; z<=range; z++) { OffsetVector[j][0]=x; OffsetVector[j][1]=y; OffsetVector[j][2]=z; //cout<<"offset: "< position; float total_radius, near_radius, total_volume, max_radius, min_radius; FloatType::IndexType pixelIndex; float prev_radius; point3D p0, p1; float height; for(f=0; f max_radius) max_radius = near_radius; if(near_radius < min_radius) min_radius = near_radius; total_radius += near_radius; if(v>0) { //compute the volume of the segment p0 = toTissue(network[f][v-1]); p1 = toTissue(network[f][v]); height = (p1 - p0).Length(); total_volume += ((3.14159*height)/3.0)*(prev_radius * prev_radius + near_radius*near_radius + prev_radius*near_radius); } prev_radius = near_radius; } EdgeList[f].avg_radius = total_radius/numVertices; EdgeList[f].min_radius = min_radius; EdgeList[f].max_radius = max_radius; EdgeList[f].volume = total_volume; } cout<<"done."< NeighborhoodIteratorType; NeighborhoodIteratorType::RadiusType radius; radius.Fill(range); NeighborhoodIteratorType i(radius, distanceMap, distanceMap->GetRequestedRegion()); vector OffsetVector; OffsetVector.resize((range*2+1)*(range*2+1)*(range*2+1)); int x, y, z, j; j=0; for(x=-range; x<=range; x++) for(y=-range; y<=range; y++) for(z=-range; z<=range; z++) { OffsetVector[j][0]=x; OffsetVector[j][1]=y; OffsetVector[j][2]=z; //cout<<"offset: "< position; float total_radius, max_radius; FloatType::IndexType pixelIndex; for(f=0; f rtsNetwork::GetFiberRadius(unsigned int fiber) { point3D result; result.x = EdgeList[fiber].min_radius; result.y = EdgeList[fiber].max_radius; result.z = EdgeList[fiber].avg_radius; return result; }