-
-
Notifications
You must be signed in to change notification settings - Fork 726
ENH: Add static ImageFileWriter<>::WriteImage(image, fileName) member function #2102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,7 +82,7 @@ class ITKIOImageBase_EXPORT ImageFileWriterException : public ExceptionObject | |
| * \sphinxexample{IO/ImageBase/WriteAnImage,Write An image} | ||
| * \endsphinx | ||
| */ | ||
| template <typename TInputImage> | ||
| template <typename TInputImage = void> | ||
| class ITK_TEMPLATE_EXPORT ImageFileWriter : public ProcessObject | ||
| { | ||
| public: | ||
|
|
@@ -235,6 +235,32 @@ class ITK_TEMPLATE_EXPORT ImageFileWriter : public ProcessObject | |
| int m_CompressionLevel{ -1 }; | ||
| bool m_UseInputMetaDataDictionary{ true }; | ||
| }; | ||
|
|
||
| template <> | ||
| class ImageFileWriter<void> | ||
| { | ||
| public: | ||
| ITK_DISALLOW_COPY_AND_MOVE(ImageFileWriter); | ||
|
|
||
| ImageFileWriter() = delete; | ||
| ~ImageFileWriter() = delete; | ||
|
|
||
| /** Writes an image to the specified file. Example: | ||
| \code | ||
| itk::ImageFileWriter<>::WriteImage(*imagePointer, outputFileName); | ||
| \endcode | ||
| */ | ||
| template <typename TInputImage> | ||
| static void | ||
| WriteImage(const TInputImage & image, const std::string & fileName) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ?? Should we expose other conditional behaviors as default options? UseCompression = false, CompressionLevel = -1, UseINputMetaDataDictionary=true)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question. The aim of this PR is to make the most common use case a simple "one-liner".
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My solution does not change the most common use case, but does allow for more flexibility easily for those who need it. The UseCompression is the issue I am most interested in maintaining access to.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Internal to ITK defaults are used almost 100% of the time. In my applications, and many of the applications that I work on, defaults are used near 0% of the time because I explicitly want my applications to use compression for cost storage reasons. I love you code simplification solution, and would implement it's used in my applications as long as I don't lose the ability to compress data.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, extra options could still be added as function parameters with default argument values. (Possibly afterwards.) Another redesign could be to add those options as data members to the new Which would default-construct a writer (containing default values for its options), and then use it directly, calling a non-static For the moment I still prefer the current PR (just a static member function), possible with some optional default arguments. What do you think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the proposed option is implemented In C++ the unnamed parameters are not clear what the argument position means. Perhaps adding a
There should be implicit conversions from smart pointers to raw pointers, therefor just a method with a raw pointer should work. This is a new style for ITK, we have not had function like calls before for process object. An alternative for the void template parameters would be to place the template function(s) into a namespace.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe, but if there is no exact match template arguments cannot be deduced.
I like it! So we could have: #include "ImageFileWriter.h"
...
itk::WriteImage(image, fileName);
itk::WriteImage(filter->GetOutput(), fileName2, true); // compressedI like default
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the idea of having a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would have to include |
||
| { | ||
| const auto writer = itk::ImageFileWriter<TInputImage>::New(); | ||
| writer->SetFileName(fileName); | ||
| writer->SetInput(&image); | ||
| writer->Update(); | ||
| } | ||
| }; | ||
|
|
||
| } // end namespace itk | ||
|
|
||
| #ifndef ITK_MANUAL_INSTANTIATION | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like having to write
*varName, I prefer justvarName. See declaration suggestion on how to accomplish this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My proposal was to use an image reference, rather than a pointer, because a pointer in C++ is used to indicate "might be null". So I wonder, why would someone want to call
WriteImagewith a (possible) null pointer as image?A smart pointer is specifically used to indicate that the ownership on an object is shared between entities. It does make sense to share with an ("old style")
ImageFileWriter<ImageType>object, but I wonder if it makes sense to share with a static member function call.That's why I'm reluctant to declare the image parameter as a pointer. But I do see your point that it is cumbersome to always add a
*.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@N-Dekker If a user has pointer which might be null, and wants to print it if it is not null, he will have to write an
if. And if we require a reference, he will have to write an if before converting it to a reference. As all ITK images are held through pointers or smart pointers, accepting those is more natural than expecting a reference. I don't see much practical difference between the two, except that we will always have to add*during invocation with the reference approach.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dzenanz Just a few links to the C++ Core Guidelines to clarify my reluctance to declare the image as a pointer (smart or raw pointer):
"Take smart pointers as parameters only to explicitly express lifetime semantics"
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#Rr-smartptrparam
"For general use, take T* or T& arguments rather than smart pointers"
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#f7-for-general-use-take-t-or-t-arguments-rather-than-smart-pointers
"For “in” parameters, pass cheaply-copied types by value and others by reference to const"
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#f7-for-general-use-take-t-or-t-arguments-rather-than-smart-pointers
"Prefer T* over T& when “no argument” is a valid option"
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#f60-prefer-t-over-t-when-no-argument-is-a-valid-option
Now I know that there are a lot of old ITK (member) functions that have pointers as argument types. But for a newly added function maybe we could move forward to use a reference as argument type instead...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would be explicitly taking
itk::SmartPointer<Image<...>>in order to aid template parameter deduction. And primary specialization would be takingImage *. And if you want to be super-correct, you can check for null to avoid crashing.As we can only hold a (smart) pointer to an ITK image in a variable, this is what we should be passing around. Entire ITK API takes either
Image *orImage::Pointer(akaitk::SmartPointer<Image<...>>). Trying to switch one function to taking anImage &seems unnecessary and would make its use harder (specifically having to dereference image at each place of invocation).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And as you might guess, refactoring ITK to change convention from passing
Image*to passingImage&is a huge task which breaks backwards compatibility with little tangible gain.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function would likely be used for debugging, so it would be good add a null check and a check that the image has been allocated.
There are other issues with the wrapping to use raw pointers instead of references.