#============================================================================== # # Project: SharpImage # Module: CurvesLevelSet.py # Language: IronPython # Author: Dan Mueller # Date: $Date: 2007-07-13 06:30:23 +1000 (Fri, 13 Jul 2007) $ # Revision: $Revision: 8 $ # # Copyright (c) Queensland University of Technology (QUT) 2007. # 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 ImageToImageScript from ImageToImageScript import * # Add reference and import LevelSet library clr.AddReference("ManagedITK.GradientFilters") clr.AddReference("ManagedITK.IntensityFilters") clr.AddReference("ManagedITK.ThresholdFilters") clr.AddReference("ManagedITK.LevelSetFilters") clr.AddReference("ManagedITK.PixelMathFilters") clr.AddReference("ManagedITK.ResizeFilters") from itk import * # Setup for using arrays array = System.Array class CurvesLevelSetScript(ImageToImageScriptObject): # ------------------------------------------------------------------------- Name = "CurvesLevelSet" Help = """An initial contour is propagated outwards (or inwards) until it sticks to the shape boundaries. This is done using a level set speed function based on a user supplied edge potential map. Reference: Lorigo, et al., "Curves: Curve evolution for vessel segmentation". Medical Image Analysis, 5:195-206, 2001.""" Parameters = """(string) Speed = the absolute path or search pattern of the speed/feature image. ("Speed") (string) Initial = the absolute path or search pattern of the initial contour. ("Initial") (double) MinimumRmsChange = If the root mean square (RMS) change drops below this value, the algorithm finishes. (0.02) (double) MaximumIterations = the maximum number of interations to perform. (800)""" Speed = "Speed" Initial = "Initial" IsAborted = False MonitorState = None Form = None Filter = None PropagationScaling = 1.0 CurvatureScaling = 1.0 AdvectionScaling = 1.0 MinimumRmsChange = 0.02 MaximumIterations = 800 # ------------------------------------------------------------------------- def Run(self): """ The entry-point for this script. """ self.Initialise( ) self.StartedWork( ) self.ParentApplication.SetApplicationAsReady( 0 ) self.GetSpeedAndInitialImages( ) self.ShowScalingForm( ) def GetSpeedAndInitialImages(self): """ Get the speed image and initial contour. """ try: # Get the speed image if (self.Speed != None and Path.IsPathRooted(self.Speed)): self.Speed = self.ParentApplication.OpenImage( self.Speed ) elif (self.Speed != None): self.Speed = self.GetOpenImageMatchingPattern( self.Speed ) if (self.Speed == None): raise ArgumentException( "A speed or feature image must be provided." ) # Get the intial image if (self.Initial != None and Path.IsPathRooted(self.Initial)): self.Initial = self.ParentApplication.OpenImage( self.Initial ) elif (self.Initial != None): self.Initial = self.GetOpenImageMatchingPattern( self.Initial ) if (self.Initial == None): raise ArgumentException( "An initial image must be provided." ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def ShowScalingForm(self): """ Show the scalings variable slider form. """ try: self.Form = siFormVariableSlider( self.Input ) self.Form.Text = "Scalings" self.Form.ContinueType = siFormTool.ContinueTypeEnum.Next self.Form.AddVariable( "Propagation Scaling", 1.0, 0.0, 50.0, 1.0 ) self.Form.AddVariable( "Curvature Scaling", 1.0, 0.0, 50.0, 1.0 ) self.Form.AddVariable( "Advection Scaling", 1.0, 0.0, 50.0, 1.0 ) self.Form.ValueStringFormat = "0" self.Form.Initialise( ) self.Form.VariableChanged += self.VariableChangedHandler self.Form.Continue += self.ScalingContinueHandler self.Form.Cancel += self.CancelHandler self.ParentApplication.AddTool( self.Form ) self.Form.Reset( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def ThreadedDoWork(self): """ Evolve the contour using Curves method, on a background thread. """ try: # Write the input, speed, and initial image names self.WriteImageName( "Input=", self.Input ) self.WriteImageName( "Speed=", self.Speed ) self.WriteImageName( "Initial=", self.Initial ) # Setup the filter self.Filter = itkCurvesLevelSetImageFilter.New( self.Input, self.Speed, self.Output.PixelType ) self.AddEventHandlersToProcessObject( self.Filter ) self.Filter.Iteration += self.LevelSetIterationHandler self.Filter.PropagationScaling = itkPixel( self.Output.PixelType, self.PropagationScaling ) self.Filter.CurvatureScaling = itkPixel( self.Output.PixelType, self.CurvatureScaling ) self.Filter.AdvectionScaling = itkPixel( self.Output.PixelType, self.AdvectionScaling ) self.Filter.MaximumRMSError = self.MinimumRmsChange self.Filter.NumberOfIterations = self.MaximumIterations self.WriteLineToConsole( "PropagationScaling=" + self.PropagationScaling.ToString() ) self.WriteLineToConsole( "CurvatureScaling=" + self.CurvatureScaling.ToString() ) self.WriteLineToConsole( "AdvectionScaling=" + self.AdvectionScaling.ToString() ) self.WriteLineToConsole( "MinimumRmsChange=" + self.MinimumRmsChange.ToString() ) self.WriteLineToConsole( "MaximumIterations=" + self.MaximumIterations.ToString() ) self.Filter.SetInput ( self.Initial ) self.Filter.SetFeatureImage( self.Speed ) # Show the monitor form template = "Maximum Iterations: MaximumIterations\r\n" template += "Elapsed Iterations: ElapsedIterations\r\n" template += "Minimum RMS Change: MinimumRmsChange\r\n" template += "Current RMS Change: CurrentRmsChange" self.Form = siFormIteration( ) self.Form.DisplayTemplate = template self.Form.SetDisplayVariable( "MaximumIterations", self.MaximumIterations, "000" ) self.Form.SetDisplayVariable( "ElapsedIterations", 0, "000" ) self.Form.SetDisplayVariable( "MinimumRmsChange", self.MinimumRmsChange, "0.000" ) self.Form.SetDisplayVariable( "CurrentRmsChange", 0.0, "0.000" ) self.Form.RefreshDisplayInfo( ) self.Form.Continue += self.MonitorContinueHandler self.Form.Cancel += self.CancelHandler self.Form.StateChanged += self.FormMonitorStateChanged self.ParentApplication.AddTool( self.Form ) # Bring the renderer to the front self.Renderer.Focus( ) # Do the work self.Filter.Update( ) # Do some house keeping if (not self.IsAborted): self.Filter.GetOutput( self.Output ) self.WriteOutputName( ) self.Output.DisconnectPipeline( ) self.Initial.DisconnectPipeline( ) self.Speed.DisconnectPipeline( ) self.DisposeOfObject( self.Filter ) self.ParentApplication.SetApplicationAsReady ( 0 ) self.ParentApplication.SetApplicationProgress( 0 ) self.FinishedWork( True ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def Finalise(self): """ Finalise the environment after running this script. """ """ NOTE: This function is invoked on the main UI thread. """ if (self.IsAborted): ScriptObject.Finalise( self ) else: ImageToImageScriptObject.Finalise( self ) def FormMonitorStateChanged(self, state): """ Handle the Iteration form StateChanged event. """ self.MonitorState = state if (self.MonitorState == IterationStateEnum.Resumed): self.Thread.Resume( ) # NOTE: this is nasty, but what the hey! elif (self.MonitorState == IterationStateEnum.Stopped): self.AbortProcess( ) elif (self.MonitorState == IterationStateEnum.Paused): self.Thread.Suspend( ) # NOTE: this is nasty, but what the hey! def AbortProcess(self): """ Abort the level set filter. """ if (self.Thread.ThreadState == System.Threading.ThreadState.Suspended): self.Thread.Resume( ) # NOTE: this is nasty, but what the hey! self.IsAborted = True #self.Filter.AbortGenerateData( ) self.Thread.Abort( ) def LevelSetIterationHandler(self, processObject): """ Handle the iteration event on the levelset filter, on the background thread. """ target = CallTarget1( self.LevelSetIterationHandlerOnMainThread ) return self.ParentApplication.InvokeArg1( target, processObject ) def LevelSetIterationHandlerOnMainThread(self, processObject): """ Handle the iteration event on the levelset filter, on the main thread. """ try: # Update monitor form self.Form.SetDisplayVariable( "ElapsedIterations", processObject.ElapsedIterations, "000" ) self.Form.SetDisplayVariable( "CurrentRmsChange", processObject.RMSChange, "0.000" ) self.Form.RefreshDisplayInfo( ) # Update the label after user specified number of iterations modIterations = processObject.ElapsedIterations % self.Form.IterationsBetweenRefresh if (modIterations != 0): return # Apply BinaryThreshold label = itkImage.New( itkPixelType.UC, self.Input.Dimension ) filterThreshold = itkBinaryThresholdImageFilter.New( self.Initial, label ) filterThreshold.RemoveAllObservers( ) filterThreshold.SetInput( processObject.GetOutput() ) filterThreshold.LowerThreshold = itkPixel( self.Initial.PixelType, -1000.0 ) filterThreshold.UpperThreshold = itkPixel( self.Initial.PixelType, 0.0 ) filterThreshold.InsideValue = itkPixel( label.PixelType, 255 ) filterThreshold.OutsideValue = itkPixel( label.PixelType, 0 ) filterThreshold.Update( ) filterThreshold.GetOutput( label ) label.DisconnectPipeline( ) # Setup LUT if (self.Renderer.Inputs.Count == 1): self.Renderer.Inputs.Add( label ) else: self.Renderer.Inputs[1] = label lut = siGdiLookupTable( ) lut.SetTableRange( label, 256 ) lut.SetTableToSingleColor( 255.0, Color.FromArgb(128, Color.Red) ) label.Metadata[ "LookupTable1" ] = lut # Refresh Renderer self.Renderer.Initialise( ) self.Renderer.Repaint( ) Application.DoEvents( ) except Exception, ex: self.HandleException( ex ) self.FinishedWork( False ) self.Finalise( ) def ScalingContinueHandler(self, sender, args): """ Handle the Scaling form Continue event. """ self.CleanUpForm( ) self.DoWork( ) def MonitorContinueHandler(self, sender, args): """ Handle the Monitor form Continue event. """ self.CleanUpForm() self.Renderer.Inputs.RemoveAt( 1 ) self.Renderer.Initialise( ) self.Renderer.Repaint( ) def CancelHandler(self, sender, args): """ Cancel the script. """ self.AbortProcess( ) self.WriteLineToConsole( "Script cancelled by user..." ) self.CleanUpForm( ) self.Renderer.Inputs.RemoveAt( 1 ) self.Renderer.Initialise( ) self.Renderer.Repaint( ) self.FinishedWork( True ) self.Finalise( ) def VariableChangedHandler(self, name, variables): """ Handle the Form.WindowLevelChanged events. """ self.PropagationScaling = variables[ "Propagation Scaling" ] self.CurvatureScaling = variables[ "Curvature Scaling" ] self.AdvectionScaling = variables[ "Advection Scaling" ] def CleanUpForm(self): """ Clean up (close, remove event handlers, dispose) the script form. """ if (self.Form != None): self.Form.Close( ) self.Form.Cancel -= self.CancelHandler self.Form.Continue -= self.ScalingContinueHandler self.Form.Continue -= self.MonitorContinueHandler if (hasattr( self.Form, "VariableChanged" )): self.Form.VariableChanged -= self.VariableChangedHandler self.DisposeOfObject( self.Form )