#============================================================================== # # Project: SharpImage # Module: ThesisHelperPath.py # Language: IronPython # Author: Dan Mueller # Date: $Date$ # Revision: $Revision$ # # Copyright (c) Queensland University of Technology (QUT) 2008. # All rights reserved. # # This software is distributed WITHOUT ANY WARRANTY; without even # the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the above copyright notices for more information. # #============================================================================== # Import the base script class import Script from Script import * clr.AddReference("ManagedITK.LevelSetFilters") clr.AddReference("ManagedITK.ThresholdFilters") clr.AddReference("ManagedITK.Optimizers") clr.AddReference("ManagedITK.Paths") clr.AddReference("ManagedITK.PixelMathFilters") from System import String from itk import * class ThesisHelperPathScript(ScriptObject): # ------------------------------------------------------------------------- Name = "ThesisHelperPath" Help = """Display a form for driving my thesis path presentation.""" Parameters = """None""" FormDriver = None FormPoints = None Renderer = None ImageInput = None ImageCost = None ImageArrival = None ImagePath = None # ------------------------------------------------------------------------- def Run(self): """ The entry-point for this script. """ try: self.Initialise() self.DoWork() except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise() def Initialise(self): """ Initialise the environment for running this script. """ """ NOTE: This function is invoked on the main UI thread. """ try: # Initialise the base class ScriptObject.Initialise( self ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def DoWork(self): """ Perform the main functions of the script. """ """ NOTE: This function is invoked on the main UI thread. """ try: self.StartedWork() # Create driver tool form self.FormDriver = siFormFlowLayout( ) self.FormDriver.Text = "Helper" self.FormDriver.Continue += self.OnContinueClicked self.FormDriver.Cancel += self.OnCancelClicked # Populate driver form self.FormDriver.AddOpenFile( CallTarget1(self.OnOpenFile) ) self.FormDriver.AddButton( "View Input", CallTarget2(self.OnClickedInput) ) self.FormDriver.AddButton( "View Cost", CallTarget2(self.OnClickedCost) ) self.FormDriver.AddButton( "Set Path Info", CallTarget2(self.OnClickedPoints) ) self.FormDriver.AddButton( "Compute Arrival", CallTarget2(self.OnClickedArrival) ) self.FormDriver.AddButton( "Compute Path", CallTarget2(self.OnClickedPathFromCost) ) self.FormDriver.AddButton( "Rasterise Path", CallTarget2(self.OnClickedRasterise) ) self.FormDriver.AddButton( "Clear", CallTarget2(self.OnClickedClear) ) self.ParentApplication.AddTool( self.FormDriver ) # Let user do work self.ParentApplication.SetApplicationAsReady( 0 ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnContinueClicked(self, sender, args): """ Close the form. """ self.UnregisterEventHandlers() self.FormDriver.Close() self.FinishedWork( True ) self.Finalise( ) def OnCancelClicked(self, sender, args): """ Cancel the script. """ self.UnregisterEventHandlers() self.FormDriver.Close() self.FinishedWork( True ) self.Finalise( ) def UnregisterEventHandlers(self): """ Unregister all the event handlers. """ if (self.FormDriver != None): self.FormDriver.Continue -= self.OnContinueClicked self.FormDriver.Cancel -= self.OnCancelClicked def OnOpenFile( self, filepath ): """Opens the given text file and reads upto four images (Value, Gradient, Extra1, and/or Extra2) from the fully qualified file paths. The types are as follows: Value=SS3, Gradient=UC3, Extra1=UC3, Extra2=F3""" try: # Set application as working self.ParentApplication.SetApplicationAsWorking( ) self.ParentApplication.SetApplicationStatusLabel( "Loading images..." ) # Dispose of existing images if ( self.ImageInput != None ): self.ImageInput.Dispose( ) self.ImageInput = None if ( self.ImageCost != None ): self.ImageCost.Dispose( ) self.ImageCost = None # Close and dispose of existing renderer if ( self.Renderer != None ): self.Renderer.Close( ) self.Renderer.Dispose( ) self.Renderer = None # Read Volume (*.vol) file lines = File.ReadAllLines( filepath ) # Extract images and transfer functions (tf) pathInput = None pathCost = None for i in range(lines.Length): if (lines[i].StartsWith( "Input=" ) ): pathInput = lines[i].Replace( "Input=", "" ) elif (lines[i].StartsWith( "Cost=" ) ): pathCost = lines[i].Replace( "Cost=", "" ) # Read images if ( pathInput != None and pathInput.Length > 0 ): self.WriteLineToConsole( "Reading " + Path.GetFileName( pathInput ) ) info = itkImage.ReadInformation( pathInput ); self.ImageInput = itkImage.New( info.PixelType, info.Dimension ) self.ImageInput.Read( pathInput ) else: self.ImageInput = None if ( pathCost != None and pathCost.Length > 0 ): self.WriteLineToConsole( "Reading " + Path.GetFileName( pathCost ) ) info = itkImage.ReadInformation( pathCost ); self.ImageCost = itkImage.New( info.PixelType, info.Dimension ) self.ImageCost.Read( pathCost ) else: self.ImageCost = None # Set application as ready self.ParentApplication.SetApplicationAsReady( 0 ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def InitSlices( self, image ): """Show the given image in the current slice renderer. If the current renderer is not a slice renderer, or the current rendderer is None, the current renderer is closed and a new renderer is displayed.""" try: if ( image == None ): return typeRenderer = Type.GetType("SharpImage.Rendering.siGdiSliceRenderer") if ( self.Renderer != None and String.Compare( self.Renderer.TypeName, "GDI Viewer" ) != 0 ): # Close existing renderer self.Renderer.Close( ) self.Renderer.Dispose( ) self.Renderer = None if ( self.Renderer == None ): # Create new slice renderer self.Renderer = self.ParentApplication.ShowImageInNewRenderer( image ) else: # Set input of current renderer self.Renderer.Inputs[0] = image self.Renderer.InitialiseInputs( ) self.Renderer.Repaint( ) self.Renderer.Focus( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def SetPathInfo( self, filterPath ): """Returns an PathInfo object from the current self.FormPoints object.""" try: for selection in self.FormPoints.Paths: info = itkPathInfo( self.ImageCost.Dimension ) info.StartPoint = selection.Start info.EndPoint = selection.End for way in selection.WayPoints: info.AddWayPoint( way ) filterPath.AddPathInfo( info ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def ComputeArrival( self ): """Computes the arrival function if it doesn't already exist.""" try: # Check valid state if ( self.ImageArrival != None ): return if ( self.ImageCost == None ): return if ( self.FormPoints == None ): return if ( self.FormPoints.Paths == None ): return if ( self.FormPoints.Paths.Count == 0 ): return # Create arrival image self.ImageArrival = itkImage.New( self.ImageCost ) # Compute Fast Marching filterFastMarching = itkFastMarchingImageFilter.New( self.ImageCost, self.ImageCost ) #self.AddEventHandlersToProcessObject( filterFastMarching ) filterFastMarching.StoppingValue = 1000.0 filterFastMarching.SetInput( self.ImageCost ) trialPoints = List[itkLevelSetNode]() for i in range( self.FormPoints.Paths.Count ): pointStart = self.FormPoints.Paths[i].Start indexStartRef = clr.Reference[itkIndex]( itkIndex( self.ImageCost.Dimension ) ) self.ImageCost.TransformPhysicalPointToIndex( pointStart, indexStartRef ) trialPoints.Add( itkLevelSetNode(0.0, indexStartRef.Value) ) #self.WriteLineToConsole( "Added trial point=" + indexStartRef.Value.ToString() ) filterFastMarching.TrialPoints = trialPoints.ToArray() # Threshold filterThreshold = itkThresholdImageFilter.New( self.ImageCost ) #self.AddEventHandlersToProcessObject( filterThreshold ) filterThreshold.SetInput( filterFastMarching.GetOutput() ) filterThreshold.OutsideValue = itkPixel( self.ImageCost.PixelType, 1000.0 ) pixelLower = itkPixel( self.ImageCost.PixelType, 0.0 ) pixelUpper = itkPixel( self.ImageCost.PixelType, 1000.0 ) filterThreshold.ThresholdOutside( pixelLower, pixelUpper ) filterThreshold.GetOutput( self.ImageArrival ) filterThreshold.UpdateLargestPossibleRegion() except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def ShowRenderer( self ): """Show the current renderer.""" try: if ( self.Renderer == None ): return self.ParentApplication.ShowRenderer( self.Renderer ) self.Renderer.Form.WindowState = FormWindowState.Maximized except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedInput(self, sender, args): """Allow the user to select points.""" try: self.InitSlices( self.ImageInput ) self.ShowRenderer( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedCost(self, sender, args): """Allow the user to select points.""" try: self.InitSlices( self.ImageCost ) self.ShowRenderer( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedPoints(self, sender, args): """Allow the user to select points.""" try: self.FormPoints = siFormSelectPaths( self.Renderer ) self.FormPoints.Continue += self.OnClickedPointsContinue self.FormPoints.Cancel += self.OnClickedPointsCancel self.ParentApplication.AddTool( self.FormPoints ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedPointsContinue(self, sender, args): """Handler method for points continue event.""" self.ImageArrival = None def OnClickedPointsCancel(self, sender, args): """Handler method for points cancel event.""" self.FormPoints = None def OnClickedArrival(self, sender, args): """Allow the user to compute and view the arrival function.""" try: self.ComputeArrival( ) self.InitSlices( self.ImageArrival ) self.ShowRenderer( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedPathFromCost(self, sender, args): """Allow the user to compute and display the extracted path.""" try: # Check valid state if ( self.ImageCost == None ): return if ( self.FormPoints == None ): return if ( self.FormPoints.Paths == None ): return if ( self.FormPoints.Paths.Count == 0 ): return self.ParentApplication.SetApplicationAsWorking( ) self.ParentApplication.SetApplicationStatusLabel( "Computing path from cost..." ) # Setup parameters iterations = 2000 maximumStepLength = self.ImageCost.Spacing[0] minimumStepLength = 0.5 * self.ImageCost.Spacing[0] relaxationFactor = 0.75 terminationValue = 3.0 # Setup cost function cost = itkSingleImageCostFunction.New( self.ImageCost ); # Setup the optimizer optimizer = itkRegularStepGradientDescentOptimizer.New() optimizer.NumberOfIterations = iterations optimizer.MaximumStepLength = maximumStepLength optimizer.MinimumStepLength = minimumStepLength optimizer.RelaxationFactor = relaxationFactor # Setup the path extraction filter filterPath = itkSpeedFunctionToPathFilter.New( self.ImageCost ) filterPath.SetInput( self.ImageCost ) filterPath.SetCostFunction( cost ) filterPath.SetOptimizer( optimizer ) filterPath.TerminationValue = terminationValue self.SetPathInfo( filterPath ) # Get the paths filterPath.Update( ) self.Paths = List[itkPolyLineParametricPath]( filterPath.NumberOfOutputs ) for i in range( filterPath.NumberOfOutputs ): path = itkPolyLineParametricPath.New(self.ImageCost.Dimension) filterPath.GetOutput( i, path ) path.DisconnectPipeline( ) path.Metadata["Color"] = Color.Red path.Metadata["Width"] = 2.0 self.Renderer.Inputs.Add( path ) # Clean up and finish cost.Dispose( ) optimizer.Dispose( ) filterPath.Dispose( ) self.Renderer.Repaint( ) # Set application as ready self.ParentApplication.SetApplicationAsReady( 0 ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedRasterise(self, sender, args): """Allow the user to compute and view the rasterised path.""" try: # Check valid state if ( self.Renderer == None ): return if ( self.Renderer.Inputs == 1 ): return self.ParentApplication.SetApplicationAsWorking( ) self.ParentApplication.SetApplicationStatusLabel( "Rasterising path..." ) # Create image self.ImagePath = itkImage.New( itkPixelType.UC, self.ImageCost.Dimension ) # Get the paths to rasterise pathsToRasterise = List[itkDataObject]() for dataobj in self.Renderer.Inputs: if ( Type.GetType("itk.itkParametricPath, ManagedITK.Common").IsInstanceOfType(dataobj) ): pathsToRasterise.Add( dataobj ) # Rasterise the paths if ( pathsToRasterise.Count == 0 ): return if ( pathsToRasterise.Count == 1 ): self.WriteTask( "Rasterising path" ) pathType = itkPolyLineParametricPath.New( self.ImageCost.Dimension ) filterPath = itkPathToImageFilter.New( pathType, self.ImagePath ) filterPath.Size = self.ImageCost.Size filterPath.Spacing = self.ImageCost.Spacing filterPath.Origin = self.ImageCost.Origin filterPath.BackgroundValue = itkPixel_UC.NewMin( ) filterPath.PathValue = itkPixel_UC.NewMax( ) filterPath.SetInput( pathsToRasterise[0] ) filterPath.Update( ) filterPath.GetOutput( self.ImagePath ) elif ( pathsToRasterise.Count > 1 ): i = 0; filterMax = itkNaryMaximumImageFilter.New( self.ImagePath, self.ImagePath ) temp = itkImage.New( itkPixelType.UC, self.ImageCost.Dimension ) for dataobj in pathsToRasterise: self.WriteTask( "Rasterising path " + i.ToString() ) pathType = itkPolyLineParametricPath.New( self.ImageCost.Dimension ) filterPath = itkPathToImageFilter.New( pathType, self.ImageCost ) filterPath.Size = self.ImageCost.Size filterPath.Spacing = self.ImageCost.Spacing filterPath.Origin = self.ImageCost.Origin filterPath.BackgroundValue = itkPixel_UC.NewMin( ) filterPath.PathValue = itkPixel_UC.NewMax( ) filterPath.SetInput( dataobj ) filterPath.Update() filterMax.SetInput( i, filterPath.GetOutput() ) i += 1 filterMax.Update() filterMax.GetOutput( self.ImagePath ) # Display as label lut = siGdiLookupTable( ) lut.SetTableRange( self.ImagePath, 256 ) lut.SetTableToSingleColor( 255.0, Color.FromArgb(100, 0, 0, 255) ) self.Renderer.AddInputAsLabel( self.ImagePath, lut ) self.Renderer.Repaint() self.Renderer.Focus() # Set application as ready self.ParentApplication.SetApplicationAsReady( 0 ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def OnClickedClear(self, sender, args): """Allow the user to clear the path.""" try: # Check valid state if ( self.Renderer == None ): return if ( self.Renderer.Inputs == 1 ): return self.Renderer.ClearPaths( ) self.Renderer.ClearLabels( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( )