You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							363 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							363 lines
						
					
					
						
							12 KiB
						
					
					
				| /*=========================================================================
 | |
| 
 | |
|   Program:   Visualization Toolkit
 | |
|   Module:    $RCSfile: vtkLinkEdgels.cxx,v $
 | |
| 
 | |
|   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 | |
|   All rights reserved.
 | |
|   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
 | |
| 
 | |
|      This software is distributed WITHOUT ANY WARRANTY; without even
 | |
|      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 | |
|      PURPOSE.  See the above copyright notice for more information.
 | |
| 
 | |
| =========================================================================*/
 | |
| #include "vtkLinkEdgels.h"
 | |
| 
 | |
| #include "vtkCellArray.h"
 | |
| #include "vtkDoubleArray.h"
 | |
| #include "vtkImageData.h"
 | |
| #include "vtkInformation.h"
 | |
| #include "vtkInformationVector.h"
 | |
| #include "vtkMath.h"
 | |
| #include "vtkObjectFactory.h"
 | |
| #include "vtkPointData.h"
 | |
| #include "vtkPolyData.h"
 | |
| 
 | |
| vtkCxxRevisionMacro(vtkLinkEdgels, "$Revision: 1.39 $");
 | |
| vtkStandardNewMacro(vtkLinkEdgels);
 | |
| 
 | |
| // Construct instance of vtkLinkEdgels with GradientThreshold set to 
 | |
| // 0.1, PhiThreshold set to 90 degrees and LinkThreshold set to 90 degrees.
 | |
| vtkLinkEdgels::vtkLinkEdgels()
 | |
| {
 | |
|   this->GradientThreshold = 0.1;
 | |
|   this->PhiThreshold = 90;
 | |
|   this->LinkThreshold = 90;
 | |
| }
 | |
| 
 | |
| int vtkLinkEdgels::RequestData(
 | |
|   vtkInformation *vtkNotUsed(request),
 | |
|   vtkInformationVector **inputVector,
 | |
|   vtkInformationVector *outputVector)
 | |
| {
 | |
|   // get the info objects
 | |
|   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
 | |
|   vtkInformation *outInfo = outputVector->GetInformationObject(0);
 | |
| 
 | |
|   // get the input and ouptut
 | |
|   vtkImageData *input = vtkImageData::SafeDownCast(
 | |
|     inInfo->Get(vtkDataObject::DATA_OBJECT()));
 | |
|   vtkPolyData *output = vtkPolyData::SafeDownCast(
 | |
|     outInfo->Get(vtkDataObject::DATA_OBJECT()));
 | |
| 
 | |
|   vtkPointData *pd;
 | |
|   vtkPoints *newPts;
 | |
|   vtkCellArray *newLines;
 | |
|   vtkDoubleArray *inScalars;
 | |
|   vtkDoubleArray *outScalars;
 | |
|   vtkDoubleArray *outVectors;
 | |
|   int *dimensions;
 | |
|   double *CurrMap, *inDataPtr;
 | |
|   vtkDataArray *inVectors;
 | |
|   int ptId;
 | |
|   
 | |
|   vtkDebugMacro(<< "Extracting structured points geometry");
 | |
| 
 | |
|   pd = input->GetPointData();
 | |
|   dimensions = input->GetDimensions();
 | |
|   inScalars = vtkDoubleArray::SafeDownCast(pd->GetScalars());
 | |
|   inVectors = pd->GetVectors();
 | |
|   if ((input->GetNumberOfPoints()) < 2 || inScalars == NULL)
 | |
|     {
 | |
|     vtkErrorMacro(<<"No data to transform (or wrong data type)!");
 | |
|     return 1;
 | |
|     }
 | |
| 
 | |
|   // set up the input
 | |
|   inDataPtr = inScalars->GetPointer(0);
 | |
| 
 | |
|   // Finally do edge following to extract the edge data from the Thin image
 | |
|   newPts = vtkPoints::New();
 | |
|   newLines = vtkCellArray::New();
 | |
|   outScalars = vtkDoubleArray::New();
 | |
|   outVectors = vtkDoubleArray::New();
 | |
|   outVectors->SetNumberOfComponents(3);
 | |
| 
 | |
|   vtkDebugMacro("doing edge linking\n");
 | |
|   //
 | |
|   // Traverse all points, for each point find Gradient in the Image map.
 | |
|   //
 | |
|   for (ptId=0; ptId < dimensions[2]; ptId++)
 | |
|     {
 | |
|     CurrMap = inDataPtr + dimensions[0]*dimensions[1]*ptId;
 | |
|     
 | |
|     this->LinkEdgels(dimensions[0],dimensions[1],CurrMap, inVectors,
 | |
|                      newLines,newPts,outScalars,outVectors,ptId);
 | |
|     }
 | |
|   
 | |
|   output->SetPoints(newPts);
 | |
|   output->SetLines(newLines);
 | |
| 
 | |
|   // Update ourselves
 | |
| //  outScalars->ComputeRange();
 | |
|   output->GetPointData()->SetScalars(outScalars);
 | |
|   output->GetPointData()->SetVectors(outVectors);
 | |
|   
 | |
|   newPts->Delete();
 | |
|   newLines->Delete();
 | |
|   outScalars->Delete();
 | |
|   outVectors->Delete();
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| // This method links the edges for one image. 
 | |
| void vtkLinkEdgels::LinkEdgels(int xdim, int ydim, double *image, 
 | |
|                                vtkDataArray *inVectors,
 | |
|                                vtkCellArray *newLines, 
 | |
|                                vtkPoints *newPts,
 | |
|                                vtkDoubleArray *outScalars, 
 | |
|                                vtkDoubleArray *outVectors,
 | |
|                                int z)
 | |
| {
 | |
|   int **forward;
 | |
|   int **backward;
 | |
|   int x,y,ypos,zpos;
 | |
|   int currX, currY, i;
 | |
|   int newX, newY;
 | |
|   double vec[3], vec1[3], vec2[3];
 | |
|   double linkThresh, phiThresh;
 | |
|   // these direction vectors are rotated 90 degrees
 | |
|   // to convert gradient direction into edgel direction
 | |
|   static double directions[8][2] = {
 | |
|     {0,1},  {-0.707, 0.707},
 | |
|     {-1,0}, {-0.707, -0.707},
 | |
|     {0,-1}, {0.707, -0.707},
 | |
|     {1,0},  {0.707, 0.707}}; 
 | |
|   static int xoffset[8] = {1,1,0,-1,-1,-1,0,1};
 | |
|   static int yoffset[8] = {0,1,1,1,0,-1,-1,-1};
 | |
|   int length, start;
 | |
|   int bestDirection = 0;
 | |
|   double error, bestError;
 | |
| 
 | |
|   forward  = new int *[ydim];
 | |
|   backward = new int *[ydim];
 | |
|   for (i = 0; i < ydim; i++)
 | |
|     {
 | |
|     forward[i]  = new int [xdim];
 | |
|     backward[i] = new int [xdim];
 | |
|     memset(forward[i],0,xdim*sizeof(int));
 | |
|     memset(backward[i],0,xdim*sizeof(int));
 | |
|     }
 | |
| 
 | |
|   zpos = z*xdim*ydim;
 | |
|   linkThresh = cos(this->LinkThreshold*3.1415926/180.0);
 | |
|   phiThresh = cos(this->PhiThreshold*3.1415926/180.0);
 | |
| 
 | |
|   // first find all forward & backwards links
 | |
|   for (y = 0; y < ydim; y++)
 | |
|     {
 | |
|     ypos = y*xdim;
 | |
|     for (x = 0; x < xdim; x++)
 | |
|       {
 | |
|       // find forward and backward neighbor for this pixel
 | |
|       // if its value is less than threshold then ignore it
 | |
|       if (image[x+ypos] < this->GradientThreshold)
 | |
|         {
 | |
|         forward[y][x] = -1;
 | |
|         backward[y][x] = -1;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|         // try all neighbors as forward, first try four connected
 | |
|         inVectors->GetTuple(x+ypos+zpos,vec1); 
 | |
|         vtkMath::Normalize(vec1); 
 | |
|         // first eliminate based on phi1 - alpha
 | |
|         bestError = 0;
 | |
|         for (i = 0; i < 8; i += 2)
 | |
|           {
 | |
|           // make sure it passes the linkThresh test
 | |
|           if ((directions[i][0]*vec1[0]+directions[i][1]*vec1[1]) >= 
 | |
|               linkThresh)
 | |
|             {
 | |
|             // make sure we dont go off the edge and are >= GradientThresh
 | |
|             // and it hasn't already been set
 | |
|             if ((x + xoffset[i] >= 0)&&(x + xoffset[i] < xdim)&&
 | |
|                 (y + yoffset[i] >= 0)&&(y + yoffset[i] < ydim)&&
 | |
|                 (!backward[y+yoffset[i]][x+xoffset[i]])&&
 | |
|                 (image[x + xoffset[i] + (y+yoffset[i])*xdim] >=
 | |
|                  this->GradientThreshold)) 
 | |
|               {
 | |
|               // satisfied the first test, now check second
 | |
|               inVectors->GetTuple(x + xoffset[i] + 
 | |
|                                    (y + yoffset[i])*xdim + zpos,vec2); 
 | |
|               vtkMath::Normalize(vec2); 
 | |
|               if ((vec1[0]*vec2[0] + vec1[1]*vec2[1]) >= phiThresh)
 | |
|                 {
 | |
|                 // pased phi - phi test does the forward neighbor
 | |
|                 // pass the link test
 | |
|                 if ((directions[i][0]*vec2[0]+directions[i][1]*vec2[1]) >= 
 | |
|                     linkThresh)
 | |
|                   {
 | |
|                   // check against the current best solution
 | |
|                   error = (directions[i][0]*vec2[0]+directions[i][1]*vec2[1])
 | |
|                     + (directions[i][0]*vec1[0]+directions[i][1]*vec1[1])
 | |
|                     + (vec1[0]*vec2[0] + vec1[1]*vec2[1]);
 | |
|                   if (error > bestError)
 | |
|                     {
 | |
|                     bestDirection = i;
 | |
|                     bestError = error;
 | |
|                     }
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         if (bestError > 0)
 | |
|           {
 | |
|           forward[y][x] = (bestDirection+1);
 | |
|           backward[y+yoffset[bestDirection]][x+xoffset[bestDirection]] 
 | |
|             = ((bestDirection+4)%8)+1;
 | |
|           }
 | |
|         else
 | |
|           {
 | |
|           // check the eight connected neighbors now
 | |
|           for (i = 1; i < 8; i += 2)
 | |
|             {
 | |
|             // make sure it passes the linkThresh test
 | |
|             if ((directions[i][0]*vec1[0]+directions[i][1]*vec1[1]) >= 
 | |
|                 linkThresh)
 | |
|               {
 | |
|               // make sure we dont go off the edge and are >= GradientThresh
 | |
|               // and it hasn't already been set
 | |
|               if ((x + xoffset[i] >= 0)&&(x + xoffset[i] < xdim)&&
 | |
|                   (y + yoffset[i] >= 0)&&(y + yoffset[i] < ydim)&&
 | |
|                   (!backward[y+yoffset[i]][x+xoffset[i]])&&
 | |
|                   (image[x + xoffset[i] + (y+yoffset[i])*xdim] >=
 | |
|                    this->GradientThreshold)) 
 | |
|                 {
 | |
|                 // satisfied the first test, now check second
 | |
|                 inVectors->GetTuple(x + xoffset[i] + 
 | |
|                                      (y + yoffset[i])*xdim + zpos,vec2); 
 | |
|                 vtkMath::Normalize(vec2); 
 | |
|                 if ((vec1[0]*vec2[0] + vec1[1]*vec2[1]) >= phiThresh)
 | |
|                   {
 | |
|                   // pased phi - phi test does the forward neighbor
 | |
|                   // pass the link test
 | |
|                   if ((directions[i][0]*vec2[0]+directions[i][1]*vec2[1]) >= 
 | |
|                       linkThresh)
 | |
|                     {
 | |
|                     // check against the current best solution
 | |
|                     error = (directions[i][0]*vec2[0]+directions[i][1]*vec2[1])
 | |
|                       + (directions[i][0]*vec1[0]+directions[i][1]*vec1[1])
 | |
|                       + (vec1[0]*vec2[0] + vec1[1]*vec2[1]);
 | |
|                     if (error > bestError)
 | |
|                       {
 | |
|                       bestDirection = i;
 | |
|                       bestError = error;
 | |
|                       }
 | |
|                     }
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           if (bestError > 0)
 | |
|             {
 | |
|             forward[y][x] = (bestDirection+1);
 | |
|             backward[y+yoffset[bestDirection]][x+xoffset[bestDirection]] 
 | |
|               = ((bestDirection+4)%8)+1;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   
 | |
| 
 | |
|   // now construct the chains
 | |
|   vec[2] = z;
 | |
|   for (y = 0; y < ydim; y++)
 | |
|     {
 | |
|     ypos = y*xdim;
 | |
|     for (x = 0; x < xdim; x++)
 | |
|       {
 | |
|       // do we have part of an edgel chain ?
 | |
|       // isolated edgels do not qualify
 | |
|       if (backward[y][x] > 0)
 | |
|         {
 | |
|         // trace back to the beginning
 | |
|         currX = x;
 | |
|         currY = y;
 | |
|         do
 | |
|           {
 | |
|           newX = currX + xoffset[backward[currY][currX] - 1];
 | |
|           currY += yoffset[backward[currY][currX] - 1];
 | |
|           currX = newX;
 | |
|           }
 | |
|         while ((currX != x || currY != y) && backward[currY][currX]);
 | |
| 
 | |
|         // now trace to the end and build the digital curve
 | |
|         length = 0;
 | |
|         start = outScalars->GetNumberOfTuples();
 | |
|         newX = currX;
 | |
|         newY = currY;
 | |
|         do
 | |
|           {
 | |
|           currX = newX;
 | |
|           currY = newY;
 | |
|           outScalars->InsertNextTuple(&(image[currX + currY*xdim]));
 | |
|           inVectors->GetTuple(currX+currY*xdim+zpos,vec2); 
 | |
|           vtkMath::Normalize(vec2); 
 | |
|           outVectors->InsertNextTuple(vec2);
 | |
|           vec[0] = currX;
 | |
|           vec[1] = currY;
 | |
|           newPts->InsertNextPoint(vec);
 | |
|           length++;
 | |
| 
 | |
|           // if there is a next pixel select it
 | |
|           if (forward[currY][currX])
 | |
|             {
 | |
|             newX = currX + xoffset[forward[currY][currX] - 1];
 | |
|             newY = currY + yoffset[forward[currY][currX] - 1];
 | |
|             }
 | |
|           // clear out this edgel now that were done with it
 | |
|           backward[newY][newX] = 0;
 | |
|           forward[currY][currX] = 0;
 | |
|           }
 | |
|         while ((currX != newX || currY != newY));
 | |
|         
 | |
|         // build up the cell
 | |
|         newLines->InsertNextCell(length);
 | |
|         for (i = 0; i < length; i++)
 | |
|           {
 | |
|           newLines->InsertCellPoint(start);
 | |
|           start++;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   // free up the memory
 | |
|   for (i = 0; i < ydim; i++)
 | |
|     {
 | |
|     delete [] forward[i];
 | |
|     delete [] backward[i];
 | |
|     }
 | |
|   delete [] forward;
 | |
|   delete [] backward;
 | |
| }
 | |
| 
 | |
| int vtkLinkEdgels::FillInputPortInformation(int, vtkInformation *info)
 | |
| {
 | |
|   info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| void vtkLinkEdgels::PrintSelf(ostream& os, vtkIndent indent)
 | |
| {
 | |
|   this->Superclass::PrintSelf(os,indent);
 | |
| 
 | |
|   os << indent << "GradientThreshold:" << this->GradientThreshold << "\n";
 | |
|   os << indent << "LinkThreshold:" << this->LinkThreshold << "\n";
 | |
|   os << indent << "PhiThreshold:" << this->PhiThreshold << "\n";
 | |
| }
 | |
| 
 |