Disclamer: Hour estimates are very approximate, and may be way too short if something goes wrong for you. They reflect the time required for a TA to finish each section.
Due Date: You have over 2 weeks to do this assignment. Your code must be checked into the svn repository by the start of class on Thursday, Feb. 28. Part 4 may prove especially difficult, and so, as always, try to finish early!
E-mail your TA or instructor with questions or problems.
Acknowledgement: This assignment is based in part on the work of others from previous sessions of this class.
This is your first real programming assignment, and your first use of SVN. Upon completing this assignment, you should feel comfortable not only using ITK, but also programming for arbitrary pixel types and dimensionality. This assignment requires that you have ITK installed and working. This assignment includes:
For further guidance you should consult the ITK software guide and the lecture slides, especially those from lectures 5, 7, and 9.
Usage of SVN and/or CVS was discussed in Lecture 3, starting on slide 13. You will be required to submit your code for all remaining assignments using SVN. You should have received your SVN username and password in class on the 12th. All of the instructions given here will be for the command line version of SVN. You may want to use a GUI instead (see lecture 3), but it is up to you to learn the interface of the GUI of your choice.
Begin by confirming access to your SVN module, by listing the current contents of your module:
svn list --verbose --username {Your_SVN_User_Name} https://cvs.vialab.org/svn/miia08/{Your_SVN_User_Name}
{Provide your SVN password when prompted.}
Your password should be accepted, and you should not have any connection errors.
Change to the directory you previously created to contain ITK, VTK, etc. (e.g. cd c:\MIIA), and then checkout your module:
svn co --username {Your_SVN_User_Name} https://cvs.vialab.org/svn/miia08/{Your_SVN_User_Name}
{SVN will probably remember your password for you, but if not then provide it again when requested.}
You should now have a new directory named C:\MIIA\{Your_SVN_User_Name}. This directory should contain all of your code for the remainder of this class.
Now add a directory to your SVN module to contain this assignment. You must first create the directory locally, then tell SVN to add it to your module, and then commit your changes to the SVN server:
cd c:\MIIA\{Your_SVN_User_Name}
mkdir hw3
svn add hw3
svn commit hw3 -m "Setting up module for hw3"
This is the general template you must follow to add any new file or directory to your SVN repository. When you change a file that already exists, you only need to run the last line above (the commit line) to updated the server's copy of your file. Notice the -m option, followed by a quoted string. SVN requires that every committed change be given a brief description, and this is how to do it. (Warning: If you get strange errors when trying to commit, then make sure you are using the double-quote characters around your commit description.)
Now, to further test and exemplify things, you are going to create a directory for this part of the assignment, add a file containing the one word "Hello", commit the directory + file, change the file by adding the word "World" to the end, and then commit the change:
cd c:\MIIA\{Your_SVN_User_Name}\hw3
mkdir Part1
echo "Hello"> Part1/file.txt
svn add Part1
svn commit Part1 -m "Initializing Part 1"
echo "World">> Part1/file.txt
svn commit Part1 -m "Fixed file.txt"
In the above, if you prefer you could also use a text editor, such as Windows Notepad, instead of the echo commands. Also, as you may have noticed, running an svn command on a directory runs the command on all of the files and directories it contains. This can be nice...or not. Use with caution.
As a last reminder (see above), this part of this assignment should be stored in your svn module in hw3/Part2. For example, create a directory such as c:\MIIA\{Your_SVN_User_Name}\hw3\Part2 and then add it to svn; write and edit your code in that directory, not forgetting to (re-)commit your code to svn each time you "significantly" change your code.
Remember, NEVER build your code in your svn module. Instead, I recommend creating a Bin directory structure that mirrors your SVN module, e.g. the code for part2 would be built in c:\MIIA\Bin\hw3\Part2. Hence, you only submit your source code, not your compiled program.
Now, on to Part2...
Write a command line program (source file named blur.cxx with a CMakeLists.txt file that generates a program named "blur") that does all of the following using ITK (feel free to borrow from the code in InsightToolkit-3.4.0/Examples/Filtering):
blur.png" using an itkImageFileWriter object. Again, the
software guide is invaluable here.Hint: I recommend skipping step 3 at first, just to make sure you are correctly creating and writing the image. After verifying that you are (and committing your code), then go back and implement step 3 as well. Don't forget to commit the final version of your code.
Hine #2: Be aware that some blur filters can only output signed pixel types. If your program crashes for no obvious reason, this is something to check. Even if you use a signed "blur output" pixel type, please make sure your "input" image, created in step 1, uses unsigned char as the pixel type.
Apart from the software guide, you may also wish to consult the online ITK documentation at www.itk.org. Either resource will likely spare you much anguish.
Begin by copying Part2/blur.cxx to Part3/partials.cxx. Copy and update your CMakeLists.txt file as well; the compiled program should be named "derivatives". Add and commit them to SVN. Now, replace your blur filter with a chain of two filters, the first of which is itk::NeighborhoodOperatorImageFilter. Create a first-order itk::DerivativeOperator to use as your kernel. The second filter in your filter chain should be itk::RescaleIntensityImageFilter (consult the software guild or online documentation), which should scale the (-255 to 255 range) output of the derivative-computing image filter to the range of a png file (0 to 255). As a word of warning here, make sure the pixel type you specify for the output of NeighborhoodOperatorImageFilter and the input of RescaleIntensityImageFilter can handle the range of -255 to 255, float is probably a good choice. (I suggest that your "input" image of 2 circles continues to use unsigned char as the pixel type.)
Your code should first create the pipeline, and then second loop through the number of image dimensions. For each dimension:
NeighborhoodOperatorImageFilter's (derivative) operator to that of the current dimension. Note that since the filter's SetOperator() function makes a COPY of the operator, we need to first change the direction of our DerivativeOperator, and then once again call the filter's SetOperator() member function with the new version of our operator. partial_x.png", where x is the number of the current dimension.Hint: The following two ITK files may be of some assistance:
This should be the most difficult part of the assignment. You will need to write a templated, generic filter that creates and applies a custom neighborhood kernel, taking into account the dimensionality and pixel type of the image template parameters specified by the filter's user. You do not know the image dimensionality or pixel type when writing your code (remember, they are templated), and so your code will probably need to examine both TInputImage::ImageDimension and itk::NumericTraits<InputPixelType>, or their appropriate equivalents.
Begin by copying InsightToolkit-3.4.0/Code/BasicFilters/itkNeighborhoodOperatorImageFilter.* to your Part4 directory. Rename these two files to custom_kernel.*. Also, copy Part3/partials.cxx to Part4/test.cxx. Your CMakeLists.txt file's ADD_EXECUTABLE line should look like: ADD_EXECUTABLE(kernel_test test.cxx).
Edit custom_kernel.*, changing the filter name to itk::CustomKernelImageFilter, making the constructor call "this->SetOperator();", and changing the code for SetOperator() to not take any arguments, but instead to automatically generate a radius=2 (width=5) operator as follows (note: the body of SetOperator should be added to the txx file):
Hint 1: It may be easier to fill the operator in the reverse order of the above steps (e.g., set all values = the last formula, and then reassign specific values, etc.).
Hint 2: Most of the real work for Part4 is in creating your new SetOperator(void) function.
Hint 3: In your code for SetOperator(void), the operator you create can actually be just an ITK neighborhood. You don't have to create a full itk::NeighborhoodOperator. The last few lines of SetOperator(void) would then look something like: "m_Operator = my_new_neighborhood_that_contains_my_kernel_values; this->Modified(); return;".
If you have no idea how to proceed (or are just curious), it may be helpful for you to examine the approach of itk::LaplacianOperator, which is located in ITK's Code/Common directory. In particular, notice its use of std::slice in its Fill() function. Other methods are probably easier though.
Now, edit test.cxx so that it neither uses a DerivativeOperator nor loops over the number of image dimensions. It should simply generate the image (which should be of unsigned char pixel type), feed it to our newly created CustomKernelImageFilter (which should output a signed pixel type, such as float), rescale its output, and then write the result to a file named "kernel.png". Although this only tests your code for a 2D image of unsigned chars, be aware that the TA may use a separate piece of code to test your filter for different pixel types in N dimensions! You may wish to create an additional test file for different "input" pixel types and/or 3D, just to make sure things work.