DivideReal always returns double image

When using Python 3.0+, the operator / utilizes truediv instead of div. For itk.Image, truediv uses DivideReal and div uses Divide. This is relevant because DivideReal returns an image with double datatype and Divide uses the datatype of the input image.

Since I am using 32-bit floats for my images, this requires me to cast to a 32-bit float each time. It would be more convenient if it mimicked the datatype of the input image OR you could specify the desired datatype.

Here is a test case to show what I mean:

image = sitk.Image(50, 50, 50, sitk.sitkFloat32)
image2 = sitk.DivideReal(image, 4.0)
print(image) # <---- Type is float
print(image2) # <---- Type is double

image = sitk.Image(50, 50, 50, sitk.sitkFloat32)
image2 = sitk.Divide(image, 4.0)
print(image) # <---- Type is float
print(image2) # <---- Type is float

I’ve been using ITK for a few weeks and SimpleITK for about a week. I’ve have SimpleITK built from source.

  • Is this a feature/bug worthy of fixing?
  • Can someone point me in the right direction as to where I should start for fixing the issue?
  • Any other tips and comments are welcome!

Hi,

That is a correct description of the behavior. If you are using Python 2 (or python 2 with __future__ import division ), then the result will be a double pixel type which defined itk::NumericTraits<typename InputImageType::PixelType>::RealType[1]. This type is based on ITK convention of using the NumericTraits for the “real type”[2]. Also keep in mind that the “float” type in Python is implemented using the double C` type. So I believe this follows ITK and Python conventions.

As you noted the work around is to use the sitk.Divide function, as opposed to the overloaded operator to get the same type as the input.

What may be a missing feature in SimpleITK is implicit type coercion, so that image types could be implicitly promoted to the type supported by a filter.

[1] https://github.com/SimpleITK/SimpleITK/blob/0c2a9e1b2d99460c5c5f6a23a58aa184e5ac9ac4/Code/BasicFilters/json/DivideRealImageFilter.json#L7
[2] https://itk.org/Doxygen/html/classitk_1_1NumericTraits.html

Okay, that makes sense.

Can you give an example of what you mean by ‘implicit type coercion’? I think I know what you mean but I want to be sure we are on the same page. I was actually thinking the same thing when I found out that you can’t do basic operations with one image being a float and another being a double.

What I’m saying:
image1 <— float type
image2 <---- double type

result = image1 / image2 <----- This throws an error which is odd! The answer should be to implicitly convert image1 -> double or image2 -> float and proceed.

You can find general information on what type coercion on Wikipedia.

image1 <— float type
image2 <---- double type

result = image1 / image2 <----- This throws an error which is odd! The answer should be to implicitly convert image1 -> double or image2 -> float and proceed.

Yes that is what I am talking about. Currently, most of the binary functor filters expect the same type for their inputs. So, as with the case about, when the type don’t match an exception is generated, which state: sitk::ERROR: Image2 for MultiplyImageFilter doesn't match type or dimension!. Adding implicit coercion might cast image1 to double so that the filter could be executed. The complication is that there are many different policies which could be implemented, and sometime the results may be unexpected or contain errors. Take C++ for example, there are implicit conversion with signed and unsigned integers, but this some times cause compilation warnings, and overflow issues.

Yeah, there is definitely a trade off between ease of use and shooting yourself in the foot when it implicitly casts to a type you don’t want it to.

I doubt that I am the first one to bring up this issue. It seems that ITK’s approach is strongly typed where you have to cast it yourself. Is that worthwhile for SimpleITK though? The point of it is to be simple and easy to use.

Personally, I would prefer implicit casting in much the same way that C++ implements it. You may shoot yourself in the foot, but after awhile you learn how to check that issue by checking the type on each image. I am pretty content with how C++ handles their implicit casting but that may be because I’ve used it for so long.

What are your thoughts? If you don’t think it is worth adding implicit casting, then I can accept that and move on. But, if you think it’s worth adding to SimpleITK, I’ll try to find some time and investigate adding this.

With SimpleITK the image pixel type, is not strongly typed. That is unlike traditional ITK where you explicitly typedef the image with a pixel type, and typedef the input and output types of a filter. This make it clear exactly what are being used. The concern with implicit type coercion in SimpleITK is that it may be unexpected if types get automatically promoted, adding second layer of implicity. But, that second layer may not add any more uncertainty than the output pixel type for filters being dependent on the input image types.

The C++ implicit conversion rules are complicated, and have very subtle details that make them difficult to understand and implement. Additionally, may cases can yield warnings from a compiler for example what happens when adding a int and an unsigned int; the operation is done as an integer, with the potential problems of converting the signed integer to an unsigned int. These C++ conversion rules would not be good to adapt for SimpleITK’s image types.

A better policy may be to only implicitly convert an image’s pixel type to a lossless type. So adding an image of int32 and uint32 would promote both images to int64. This would be nearly 3X increase in the memory requirement, than if the operation computed in 32-bit. The memory allocated would include the original image, plus the implicit copy of the 64-bit integer image. Having the increase in memory requirements happen implicitly may be unexpected and detrimental to some usages.

This is a complicated topic which has some significant trade offs. It my need to be implemented in such a way as to allow the user to enable, disable it, or perhaps select what coercion policy to use.

The thought of making it a customizable option works as well. It crossed my mind but then I thought about most programming languages (and libraries from what I know) don’t allow this but rather pick a method and document it well.

This is fine too though, I think it will be well suited for ITK.

I’m quite new to Simple ITK (and ITK) in general but I have quite a bit of programming experience. I plan to contribute as much as I can to Simple ITK to hopefully help it out. It’s a great project but there are some issues that I would like to fix for the next new person.

Here’s my basic plan:

  1. Work on documentation! This is the biggest pitfall to me. I want to start with explaining how an average person can contribute to documentation and code. Explain how to build it in detail, etc.
  2. More work on documentation! Then I have a list of functions that I felt like I don’t know what they do based on reading the description. I’ll investigate them more (maybe bug people on this forum about them) and document them well.
  3. Then I can investigate adding some features to Simple ITK that I found a bit lacking. A great example is a way to convert from Simple ITK to ITK in Python.

If you have any projects/ideas that you want me to work on, I’m all ears.

Welcome to the SimpleITK and ITK Community, and thank you for your interest in contributing.

SimpleITK is currently in transition from using a Wiki for much of the documentation to using ReadTheDocs. On the Wiki there were a couple visual guides to building SimpleITK. Perhaps this is closer to the step by step instruction you were looking for to compile SimpleITK? It would be a welcomed contribution to update and redo these or something similar in Sphinx for ReadTheDocs.

A Readme on the “Documentation” directory is needed to explain how to build the Sphinx and Doxygen documentation etc… But for the filter documental we reuse much of the ITK Doxygen documentation, and there is a bit of a separate process of that.

In general we are just using the standard Github pull request work flow for contributions to SimpleITK. We recently moved to just using Github for things. Currently, ITK is starting to move that way too. We will probably leverage this new workflow for SimpleITK as well.

Do you have a list of “functions” that need improvement. As a first step could you open an issue on Github to track this task?

@addisonElliott
With regard to documentation (not sure you are aware) we also maintain a Jupyter notebook repository with extensive examples illustrating features of SimpleITK and realistic image analysis workflows in Python and R .

I was aware of that repository and it was pretty helpful.

I know that as I learn and start to use functions, I want to write a detailed explanation on what it does, how it works, etc. I haven’t decided if I should put these examples in the Sphinx documentation or the notebook repository you linked to. What are your thoughts?

Also, I found that the repository was a great starting point but quickly found out it was limited. As an example, I’m fighting with trying to get geodesic active contours working on a MRI image. There’s not a whole lot of documentation on FastMarchFilter, GeodesicActiveContourLevelSetImageFilter(or whatever it is), etc.

Got it.

We have several levels of documentation:

  1. Class/function documentation via Doxygen - primarily “borrowed” from the original ITK classes.
  2. Examples + language agnostic text which are in the main SimpleITK repo. These make their way into Read The Docs. The code here should be relatively short and illustrates the usage of a single class or very specific task.
  3. Notebooks repository - long code illustrating how to perform a specific task using as many classes as need be.

Based on the filters you listed and minimal effort, you can go for option 1 or 2.

If you are willing to invest a bit more effort you could have a notebook where the task you address is segmenting X from MRI, illustrating the whole workflow with various options. There is always more than one way to do things and there are advantages and disadvantages associated with each. For example, when segmenting MRI you may need to do bias field correction for method A but method B is able to deal with the bias field implicitly.

Hope this clarifies things a bit.

That makes sense to me but I have a few other questions. (We’re getting a bit off topic, but I think what we’re talking about is important information)

Option 1 makes sense to me. It comes from ITK basically and essentially the developer should go here if he knows how to use the function but needs to know the name, syntax, parameters, etc.

Option 2 and 3 gets a bit murky for me. I don’t quite understand why the content in the notebook could not be placed in the ReadTheDocs.

As an example of what I would like to add to the ReadTheDocs, take a look at this page that OpenCV has in theirs:
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html

It’s more of a guide and teaching you what these functions are doing and why we are choosing parameters the way we are. It is also visual and goes into theory of how the algorithms work.

I would love to see more guides like that in SimpleITK docs but there is an issue that SimpleITK supports like 6-7 languages with small syntactic differences, while the OpenCV docs is just for the Python OpenCV. Maybe we just do it for one language and then users can map it to different languages if desired. Or maybe we have a drop-down box beside each snippet of code and you select the language. It’s a lot of work to write code that’ll work for all 6-7 platforms however.

Also the ITK software guide complements the sitk documentation. There is s large section on level sets that may be of help: https://itk.org/ITKSoftwareGuide/html/Book2/ITKSoftwareGuide-Book2ch4.html#x37-1890004.3

If you look at the current read the docs example, we have experimented with a pull down to select the language. It should work well with that restructured text format.

I agree that reusing notebooks as guide is a good idea.