Proposals:Calculators Architecture: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
 
(17 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Proposal =
= Proposal =
Decide on the best architecture for how to implement ITK CalculatorsShould they inherit from Object and be separate from the pipeline, or should they inherit from ProcessObject and be part of the pipeline?
There are currently two different types of Calculators implemented in ITK, one type that inherits from Object and one type that inherits from ProcessObjectWe should choose one of these types: should Calculators inherit from Object and be separate from the pipeline, or should they inherit from ProcessObject and be part of the pipeline?


= Current situation =  
= Current situation =  
There are currently two different types of Calculators implemented in ITK, one that inherits from Object and is not part of the pipeline, and one that inherits from ProcessObject and is part of the pipeline.


* Originally, all calculators had the following properties:
* Originally, all calculators had the following properties:
* Inherit from '''Object'''
** Inherit from '''Object'''
* Are '''not''' part of the pipeline
** Are '''not''' part of the pipeline
* Must include the following methods: Compute
** Were designed to be fast, separate from the pipeline structure, and easy to write avoiding the code overhead of filters.
* Must include context specific Set/Get methods such as SetImage and GetThreshold  
** Must include the following methods: Compute
** Examples: All calculators except those that inherit from itkHistogramThresholdCalculator, which inherits from ProcessObject.  This includes, for example, OtsuMultipleThresholdsCalculator.
** Must include context specific Set/Get methods such as SetImage and GetThreshold  
** Examples: All calculators except those that inherit from itkHistogramThresholdCalculator (which inherits from ProcessObject).  This includes, for example, OtsuMultipleThresholdsCalculator.


* A new group of calculators having the following properties:
* A new group of calculators having the following properties:
** Inherit from '''ProcessObject'''
** Inherit from '''ProcessObject'''
** Are part of the pipeline
** Are part of the pipeline
** Suffer speed and complexity overhead from belonging to the pipeline
** Must include the following methods: Update, SetInput, GetOutput
** Must include the following methods: Update, SetInput, GetOutput
** Provide a Compute() method that simply calls Update()
** Provide a Compute() method that simply calls Update()
Line 25: Line 25:
* Having two separate implementations, both with the name "Calculator", that inherit from entirely different trees and with different methods is inconsistent and confusing.   
* Having two separate implementations, both with the name "Calculator", that inherit from entirely different trees and with different methods is inconsistent and confusing.   
* Another drawback of having two different inheritance trees is that the calculators can't be hot-swapped since they have different methods.
* Another drawback of having two different inheritance trees is that the calculators can't be hot-swapped since they have different methods.
* If the calculators inherit from ProcessObject, they suffer from the pipeline overhead.


Here is a very troublesome example:
* The OtsuThresholCalculator inherits from HistogramThresholdCalculator, which in turn inherits from ProcessObject.
* The OtsuMultipleThresholdsCalculator inherits from HistogramAlgorithmBase, which in turn inherits directly from Object. 
* The implementations of the two algorithms can be nearly identical except that one can compute multiple thresholds.
* We cannot take advantage of inheritance even though both calculators are nearly identical!
* Since the inheritance trees are so different, OtsuThresholdCalculator cannot be refactored to inherit from OtsuMultipleThresholdsCalculator even though that would be a clean implementation.
* In addition, the two filters have different methods: SetInput versus SetInputHistogram and Compile versus Update. 


 
Solution (unfortunate):
Here is a very troublesome example:
* The only simple option is to instantiate one inside of the other.  OtsuMultipleThresholdsCalculator is instantiated in the GenerateData of itkOtsuThresholdCalculator to do all of the workSee the following patch to implement this: http://review.source.kitware.com/#/c/11935/
Example: Otsu.  The only option is to call one from the other.  Cannot take advantage of inheritanceMethod need to be explicitly called on the one being instantiated.
* All methods need to be explicitly called on the one being instantiated.
* If a new method is added to the one being called, the caller needs to explicitly call that method as well (wouldn't be needed using inheritance)


= Proposed changes =
= Proposed changes =


A decision should be made on which framework should be chosen going forward.  Then the current calculators should be refactored to all have the same inheritance tree and consistent methods.  Here are some suggestions, none of which is great.  
A decision should be made on which framework should be chosen going forward.  Then the current calculators should be refactored to all have the same inheritance tree and consistent methods.  Here are some suggestions, none of which is great.  
* Change the name of the calculators that inherit from itkHistogramThresholdCalculator to have "HistogramFilter" in the name instead of "Calculator"?
* Change the name of the calculators that inherit from itkHistogramThresholdCalculator to have "HistogramFilter" in the name instead of "Calculator"Take guts out of the new calculators and put them in non-pipelined calculators that are called from the new calculators.
* Take guts out of the new calculators and put them in non-pipelined calculators that are called from the new calculators?
* (Dangerous) Change the inheritance tree of itkHistogramThresholdCalculator to derive from Object instead of ProcessObject.  Then clean up the resulting mess of changing GenerateData() calls to Compute().  To retain backward compatibility, add (deprecated) Update() methods to all of the derived classes that simply call Compute().
* Are there other better suggestions?
* Are there other better suggestions?


Line 42: Line 49:
* HistogramThresholdCalculator class with methods: Compute, SetInputHistogram, GetOutputThreshold
* HistogramThresholdCalculator class with methods: Compute, SetInputHistogram, GetOutputThreshold
* ImageCalculator class with methods: Compute, SetInputImage
* ImageCalculator class with methods: Compute, SetInputImage


= Current Status =  
= Current Status =  

Latest revision as of 17:33, 26 July 2013

Proposal

There are currently two different types of Calculators implemented in ITK, one type that inherits from Object and one type that inherits from ProcessObject. We should choose one of these types: should Calculators inherit from Object and be separate from the pipeline, or should they inherit from ProcessObject and be part of the pipeline?

Current situation

  • Originally, all calculators had the following properties:
    • Inherit from Object
    • Are not part of the pipeline
    • Were designed to be fast, separate from the pipeline structure, and easy to write avoiding the code overhead of filters.
    • Must include the following methods: Compute
    • Must include context specific Set/Get methods such as SetImage and GetThreshold
    • Examples: All calculators except those that inherit from itkHistogramThresholdCalculator (which inherits from ProcessObject). This includes, for example, OtsuMultipleThresholdsCalculator.
  • A new group of calculators having the following properties:
    • Inherit from ProcessObject
    • Are part of the pipeline
    • Suffer speed and complexity overhead from belonging to the pipeline
    • Must include the following methods: Update, SetInput, GetOutput
    • Provide a Compute() method that simply calls Update()
    • Examples: All calculators that inherit from itkHistogramThresholdCalculator. This includes, for example, OtsuThresholdCalculator.

Existing limitations

  • How should a developer write a new calculator? Should it inherit from Object or ProcessObject?
  • Having two separate implementations, both with the name "Calculator", that inherit from entirely different trees and with different methods is inconsistent and confusing.
  • Another drawback of having two different inheritance trees is that the calculators can't be hot-swapped since they have different methods.

Here is a very troublesome example:

  • The OtsuThresholCalculator inherits from HistogramThresholdCalculator, which in turn inherits from ProcessObject.
  • The OtsuMultipleThresholdsCalculator inherits from HistogramAlgorithmBase, which in turn inherits directly from Object.
  • The implementations of the two algorithms can be nearly identical except that one can compute multiple thresholds.
  • We cannot take advantage of inheritance even though both calculators are nearly identical!
  • Since the inheritance trees are so different, OtsuThresholdCalculator cannot be refactored to inherit from OtsuMultipleThresholdsCalculator even though that would be a clean implementation.
  • In addition, the two filters have different methods: SetInput versus SetInputHistogram and Compile versus Update.

Solution (unfortunate):

  • The only simple option is to instantiate one inside of the other. OtsuMultipleThresholdsCalculator is instantiated in the GenerateData of itkOtsuThresholdCalculator to do all of the work. See the following patch to implement this: http://review.source.kitware.com/#/c/11935/
  • All methods need to be explicitly called on the one being instantiated.
  • If a new method is added to the one being called, the caller needs to explicitly call that method as well (wouldn't be needed using inheritance)

Proposed changes

A decision should be made on which framework should be chosen going forward. Then the current calculators should be refactored to all have the same inheritance tree and consistent methods. Here are some suggestions, none of which is great.

  • Change the name of the calculators that inherit from itkHistogramThresholdCalculator to have "HistogramFilter" in the name instead of "Calculator". Take guts out of the new calculators and put them in non-pipelined calculators that are called from the new calculators.
  • (Dangerous) Change the inheritance tree of itkHistogramThresholdCalculator to derive from Object instead of ProcessObject. Then clean up the resulting mess of changing GenerateData() calls to Compute(). To retain backward compatibility, add (deprecated) Update() methods to all of the derived classes that simply call Compute().
  • Are there other better suggestions?

If the original framework (non-pipelined calculators) is chosen, methods like Compute() should be moved to a layer between Object and the calculator so that all calculators inherit them automatically to yield a consistent API. Such a layer could consist of classes that have consistent methods for inputs and outputs. For example:

  • HistogramThresholdCalculator class with methods: Compute, SetInputHistogram, GetOutputThreshold
  • ImageCalculator class with methods: Compute, SetInputImage

Current Status

Discussion phase...



ITK: [Welcome | Site Map]