Skip to content
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

Countdown to the new Images.jl #542

Closed
32 of 42 tasks
timholy opened this issue Aug 13, 2016 · 88 comments
Closed
32 of 42 tasks

Countdown to the new Images.jl #542

timholy opened this issue Aug 13, 2016 · 88 comments

Comments

@timholy
Copy link
Member

timholy commented Aug 13, 2016

There have been justifiable questions about "what exactly is happening?" so let me post my TODO list here (including completed items, with an explanation of purpose):

  • ImagesCore.jl: handles low-level tasks like color separations, raw <--> ufixed conversions, dimension permutations. The new feature is the use of julia's swanky new array technology to provide views for all of these operations, so that we don't sometimes-copy-and-sometimes-return-a-view. Note this also effectively (partly in conjunction with the next package) solves the "Images is really confusing about image orientation") problem, becuase of the dimension permutation views.
  • ImagesAxes.jl: images with "named" dimensions, most especially temporal dimensions (like movies). Based on AxisArrays.jl, which also allows one to specify a Range (even one with physical units) for each axis, which thus incorporates the pixelspacing. All this information is exposed to the type system (rather than through fields of the properties dict). What that means is that indexing operations become type-predictable: for example, if you slice at a particular spot in time, the new array doesn't have a time axis anymore. In the old Images.jl we had to check for this and set the timedim property to 0 at runtime, and because that's slow we were constantly throwing away the dictionary for performance and therefore losing track of orientation. Now everything should Just Work.
  • ImagesMeta.jl: for those cases where one still wants to attach metadata to images.
  • ImagesFiltering.jl: better imfilter implementations. Everything will be type-stable, efficient algorithms will be chosen automatically based on the kernel size, and a platform will be in place for more efficient operations with separable kernels. Also options for performing the computation on the GPU, etc. This package will also likely be the first place to test another experimental direction: the ability (at the user's choice) to dispatch to algorithms in other libraries like OpenCV. (Currently this package is well underway but still needs work, and has been the occasion for me making changes to julia and a half dozen other packages. It won't be runnable by anyone else until julia-0.5-rc3 at the earliest.)
  • rename the above packages to ImageCore etc.
  • Get necessary dependencies registered and tagged. In addition to the above-mentioned packages, these include:
  • Replace the old MapInfo machinery with something based on functions. This will probably go in ImageCore.
  • Make sure that tests are good, and that as many deprecations are in place as possible.
  • Make sure all documentation is in place, and good:
    • decide about "local" (detailed docs in each pkg) vs "centralized" (everything at juliaimages.io)
    • for each package, make ?pkg give an API summary
    • edit the pkg descriptions (Countdown to the new Images.jl #542 (comment))
    • add usage examples (community contributions necessary here)
  • On a branch (let's call it "images-next"), rebase Images.jl on these packages. Will make heavy use of @rexport. Get the old tests passing:
    • core.jl (src file will be deleted)
    • map.jl
      • src/map.jl needs deprecation strategy
    • overlays.jl
    • algorithms.jl
    • exposure.jl
    • edge.jl
    • writemime.jl (needed for IJulia)
    • corner.jl
    • distances.jl
  • Update IO packages:
    • ImageMagick
    • QuartzImageIO
    • Netpbm
    • NRRD (will return AxisArray objects for axes with physical units or "meaning" to the axes)
  • Post an announcement here to say "check out branch images-next" if you want to play with the new Images" and collect comments from enterprising testers for about a week or so.
  • Merge to master
  • Collect comments for another week or so
  • Tag a new release and post an announcement on julia-users

Things that could be done before or after:

  • Possibly transfer all the intensity-transformation work to a separate package? Basically my thinking is that largish self-contained units might be a separate module. This would include all of @mronian's great work as well as the src/map.jl functionality. (For now I don't really plan on touching that, so that file could probably be copied verbatim.)
  • Resizing, rotation, restriction, etc. This seems like a self-contained chunk that should probably be a standalone package? I am not working on this right now at all, I would welcome contributions (@Evizero you have expressed interested in this area?)
  • Possibly split Overlay into a separate package

Longer-term future: implement lots of missing functions. Other BSD-licensed projects should be used as important inspiration.

@Evizero
Copy link
Member

Evizero commented Aug 13, 2016

Absolutely! Right now I do the operation chaining for my image augmentation package unnecessary inefficient by creating temporary copies after each operation (input -> cropped image -> rotated image -> distorted image .... etc).

After watching your talk I am really sold on the idea of lazy views, which should - if I am not mistaken - allow for just nesting views instead; which would avoid my predicament.

That said, I am not an image processing or computer vision person, so bare with my newbie mistakes I am bound to make, but I am catching on.

To be a little less vague about my true intentions, I essentially want to create a bridge between julias nice Image functionality and machine learning needs, which diverge in some strange places. For example the "z" or "time" dimension does for us store observations with no intrinsic order/relation. Even stranger: in some of our usecases the color dimension of an image is actually farther apart in memory than the spatial!. so a 3x20x20x1000 image would often be represented in 20x20x3x1000 (this is just a consequence of how some of our algorithms work)

Anyway, thank you for taking the time to explain this!

@timholy
Copy link
Member Author

timholy commented Aug 13, 2016

With regards to your 20x20x3x1000 array, using ImagesCore you can already do this:

julia> using ImagesCore, Colors

julia> a = rand(UInt8, 20, 20, 3, 1000);

julia> summary(a)
"20×20×3×1000 Array{UInt8,4}"

julia> p = permuteddimsview(a, (3,1,2,4));

julia> c = colorview(RGB, ufixedview(p));

julia> summary(c)
"20×20×1000 ImagesCore.ColorView{ColorTypes.RGB{FixedPointNumbers.UFixed{UInt8,8}},3,MappedArrays.MappedArray{FixedPointNumbers.UFixed{UInt8,8},4,Base.PermutedDimsArrays.PermutedDimsArray{UInt8,4,(3,1,2,4),(2,3,1,4),Array{UInt8,4}},ImagesCore.##15#17{FixedPointNumbers.UFixed{UInt8,8}},ImagesCore.##16#18}}"

julia> col = c[5, 3, 80]
RGB{U8}(0.953,0.914,0.208)

julia> red(col).i
0xf3

julia> a[5,3,1,80]
0xf3

julia> green(col).i
0xe9

julia> a[5,3,2,80]
0xe9

julia> blue(col).i
0x35

julia> a[5,3,3,80]
0x35

julia> a[5,3,2,80] = 0
0

julia> c[5,3,80]
RGB{U8}(0.953,0.0,0.208)

That's the thing about these views...it takes a while to realize how something that seems so "boring" can be so insanely useful. In this case we put a "permuteddimsview" inside of a "ufixedview" inside of a "colorview" and should get darn good performance despite all those shennanigans.

Now the only thing left is to wrap it in an AxisArray (using ImagesAxes.jl) that indicates that the final dimension is time (if that's what you are using it for).

@mronian
Copy link
Contributor

mronian commented Aug 13, 2016

Maybe we could include corners(recently overhauled) and edges(probably needs changes) in ImageFeatures.jl since they come under feature detection.

👍 for 100% test coverage ! Love to see the bright green coverage badge 😄

Regarding the documentation, I was thinking of having one juliaimages.github.io which will have the docs for everything. I am still a bit confused how to use Documenter.jl to tap into each repository and get the docstring. I think one of the other GSoCers has done it and I'll take help from him. Does this seem like a good idea or should there be a separate documentation for each one?

@mronian
Copy link
Contributor

mronian commented Aug 13, 2016

Should TestImages.jl be moved to JuliaImages? We could also have some other package (ImageSets.jl ?) which will have the famous datasets in a easily importable format.

@Evizero
Copy link
Member

Evizero commented Aug 13, 2016

I think it would make sense to keep the documentations separate and focused, but with a link section somewhere which point to each other.

@mronian
Copy link
Contributor

mronian commented Aug 13, 2016

Maybe we could have stuff like tutorials etc on the main website and if someone wants to dig into the function reference then they would be pointed to the individual websites.

@rsrock
Copy link
Collaborator

rsrock commented Aug 13, 2016

Before I read any of the above, let me say thanks for filling us in! I was wondering "what exactly is happening???"

@rsrock
Copy link
Collaborator

rsrock commented Aug 13, 2016

All of the above sounds great, I can't wait to kick the tires.

Here's one thing for my personal wishlist, a pretty printer for image types. When I see something like:

"20×20×1000 ImagesCore.ColorView{ColorTypes.RGB{FixedPointNumbers.UFixed{UInt8,8}},3,MappedArrays.MappedArray{FixedPointNumbers.UFixed{UInt8,8},4,Base.PermutedDimsArrays.PermutedDimsArray{UInt8,4,(3,1,2,4),(2,3,1,4),Array{UInt8,4}},ImagesCore.##15#17{FixedPointNumbers.UFixed{UInt8,8}},ImagesCore.##16#18}}"

I have to sit there and count curly braces to figure out where I am! A few spaces in there would help as well.

@timholy
Copy link
Member Author

timholy commented Aug 14, 2016

@mronian,

Maybe we could include corners(recently overhauled) and edges(probably needs changes) in ImageFeatures.jl since they come under feature detection.

I was wondering the same thing. I am in the middle of putting the gradient kernels into ImagesFiltering, but the edge-detection stuff doesn't quite fit. One option is to leave them in Images (src/algorithms won't entirely go away), but I tend to agree that ImageFeatures might be a better choice. CC @kmsquire to see if he has a preference.

👍 for 100% test coverage ! Love to see the bright green coverage badge 😄

Yes, that's important. The new repos are generally closer to that goal. ImagesAxes is much better than it looks, because some bug (probably in julia) incorrectly misses test coverage of traits-functions. See mauro3/SimpleTraits.jl#6.

Regarding the documentation, I was thinking of having one juliaimages.github.io which will have the docs for everything.

I've had the same thought. I think it will be good to have the docs for Images.jl take much more of a tutorial style, and they can link out to the docs for the individual packages for details. But it would be good to also have one giant function reference. I think that will be fairly straightforward with Documenter, but I haven't tested yet.

Should TestImages.jl be moved to JuliaImages? We could also have some other package (ImageSets.jl ?) which will have the famous datasets in a easily importable format.

Yes, good idea.

@rsrock,

Here's one thing for my personal wishlist, a pretty printer for image types.

Yes, I agree. Ideally that would be something fixed in base julia, but perhaps we could pioneer something here. Jeff has been violently opposed to abbreviating the output of typeof(x), but seems fine with customizing the printing of x itself. So we could conceivably modify Base.summary. We'd have to do it for all array types, or perhaps all array types of Colorants, which is a little scary, because it would affect other packages too that were loaded at the same time as Images.

@kmsquire
Copy link
Collaborator

I think putting edge detection in ImageFeatures would be fine.

@mronian
Copy link
Contributor

mronian commented Aug 14, 2016

When we move an algorithm say A, how do we do so without affecting either of the packages? If I just directly copy, it gives a redefinition of A error. What is the correct way to do this so that A is ported and none of the packages depending on the older A are affected?

@Evizero
Copy link
Member

Evizero commented Aug 14, 2016

I think it will be good to have the docs for Images.jl take much more of a tutorial style

@timholy What do you have in mind here? I wrote a beginner's tutorial on julia's approach to handling image/pixel data for a project of mine over here, and I'd happily contribute it if you want it (it is somewhat WIP still). But since it is mainly aimed at ML students (possibly new to Julia), and non-image-experts, I am guessing it might be a little too beginner oriented to be useful to you? (also, feel free to criticize me when I made a mistake or over/understated something)

@timholy
Copy link
Member Author

timholy commented Aug 15, 2016

When we move an algorithm say A, how do we do so without affecting either of the packages?

I think we have to do merges to master/tagging in synchrony. In other words, start a new branch in both packages that make the same change.

I wrote a beginner's tutorial

I haven't read it in detail, but this looks like exactly the kind of thing I was envisioning. Most will probably display images in the docs, too.

I haven't looked in detail, but http://scikit-image.org/docs/dev/user_guide.html seems like a promising model to follow.

@Evizero
Copy link
Member

Evizero commented Aug 16, 2016

where would you want such tutorials to be located at?

@timholy
Copy link
Member Author

timholy commented Aug 17, 2016

I think Images.jl should be the home of all the "major" documentation. (Images.jl will move to JuliaImages at the time of the transition.)

@mronian
Copy link
Contributor

mronian commented Aug 17, 2016

We could have the docs in a separate repository -> https://github.com/JuliaImages/juliaimages.github.io so that juliaimages.github.io becomes the website.

@Evizero
Copy link
Member

Evizero commented Aug 17, 2016

One thing to keep in mind is that if the docs should contain lots of images, then they have to be stored somewhere. That can be a hassle depending on where the docs are (since one wants to keep the julia package size small and the git history clean)

@mronian
Copy link
Contributor

mronian commented Aug 17, 2016

We faced this problem in TestImages.jl for the documentation. Turns out you can use the gh-pages branch to store all website related stuff. :)

@timholy
Copy link
Member Author

timholy commented Aug 17, 2016

All good suggestions. 👍 to putting it in the org.

@ViralBShah
Copy link
Contributor

ViralBShah commented Aug 21, 2016

Is JuliaImages going to be the new github org for all this? I do see various repos initialized there, so seems like it. This is all looking quite good!

@ViralBShah
Copy link
Contributor

We should certainly do a blog post on julialang.org once this is done.

@tlnagy
Copy link
Contributor

tlnagy commented Sep 7, 2016

Will the new Images be able to be installed and used with a simple using Images? Because right now it looks like it is broken into a huge number of smaller packages, which could be a nightmare to interface with.

@timholy
Copy link
Member Author

timholy commented Sep 7, 2016

Yes, it will. First, they will all be REQUIREd packages, so they'll be installed automatically. Second, their functions will all be @reexported, so unless you want just a subset of the functionality of Images (and therefore load specific sub-packages) you won't even have to care how the functionality is partitioned at the source-code level. Images will probably still have some "real" code in it, but in many ways it is evolving into a meta-package that just bundles a lot of related functionality together.

This split-up is partly designed to make them more accessible to people who want a subset of the functionality of Images...for example JuliaLang/julia#18384. There is no way to satisfy the desire to "reuse code across different types of applications" unless we split things into relatively focused units of functionality. Reexporting is the key that allows you to have this without sacrificing on other equally-worthy goals, like "provide a comprehensive Images package."

@tlnagy
Copy link
Contributor

tlnagy commented Sep 7, 2016 via email

@timholy
Copy link
Member Author

timholy commented Sep 7, 2016

You can run any tests you want...but it sounds like you have something specific in mind with "framework", what do you have in mind?

@tlnagy
Copy link
Contributor

tlnagy commented Sep 7, 2016

So currently most Julia packages have a test/ folder with unit tests
that gets run every time a new commit is pushed to master or a PR is
updated. I guess each subpackage of Images will load all of Images every
time and test all functionality to make sure that there are no
regressions in some other Images subpackage. Now that I said it, it
sounds reasonable.

On Wed, Sep 7, 2016, at 15:06, Tim Holy wrote:

You can run any tests you want...but it sounds like you have something
specific in mind with "framework", what do you have in mind?
— You are receiving this because you commented. Reply to this email
directly, view it on GitHub[1], or mute the thread[2].

Links:

  1. Countdown to the new Images.jl #542 (comment)
  2. https://github.com/notifications/unsubscribe-auth/ABlaL5F4fW-XkufoV_Kkc82LQqcRFnpIks5qnzV7gaJpZM4Jjs9f

@mronian
Copy link
Contributor

mronian commented Sep 7, 2016

We could define the runtests.jl in Images.jl in such a way that it provides a few options like running specific tests or the tests of each package.

@tknopp
Copy link
Contributor

tknopp commented Dec 29, 2016

Is there some documentation how to combine ImageMeta objects with AxisArrays? I would like to start with an object that is a close to the current Image object and start from there.

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Sure, they're completely orthogonal: just use an AxisArray for the array argument of ImageMeta. There's already some special-purpose code in place to look for this case, e.g., https://github.com/JuliaImages/ImageMetadata.jl/blob/b8e5c162b71babe457b56669e4cb8530964407f3/src/ImageMetadata.jl#L71-L73.

@tknopp
Copy link
Contributor

tknopp commented Jan 5, 2017

So, a typical usage of Images.jl was

https://github.com/simonster/NIfTI.jl/blob/2f217aac10d0e48e77eeb437e82a11c203dc2255/examples/mriview.jl#L9

What is your advice to handle this in the future. I will try playing around with this myself, but maybe it would be good to have this particular case discussed in the documentation somewhere.

And whats about the other image loader, are the already ported to the new Image infrastructure?

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

In case you haven't seen it, https://juliaimages.github.io/ImageAxes.jl/latest/ might help.

Simple (ignoring pixel spacing): ImageMeta(AxisArray(colorview(Gray, ni.raw), :x, :y, :z, :time))

There isn't quite as simple an approach for handling the pixel spacing (this is exactly the kind of feedback I'm looking for), but it is more flexible:

ImageMeta(AxisArray(colorview(Gray, ni.raw), Axis{:x}(rngx), Axis{:y}(rngy), Axis{:z}(rngz), Axis{:time}(rngt)))

where rng[xyzt] is a range that describes the positions of the different coordinates, e.g., 0:2:284 if the pixels are 2mm and go from 0mm to 284mm. (If you want to attach real units, currently Julia's ranges are kinda problematic, but see Unitful.jl, Ranges.jl and JuliaLang/julia#18777.)

Do you need an easier way to specify pixel spacing? The default AxisArray behavior is very flexible (it lets you specify the origin) but maybe somewhat verbose.

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Note you don't really need the ImageMeta here, everything is encoded by the AxisArray...one of the main points of the redesign was to encode everything having to do with orientation in the type system so that A[:, 5, :] can preserve the names of the correct axes without hammering performance.

@tknopp
Copy link
Contributor

tknopp commented Jan 5, 2017

Hm, this is of course flexible but also pretty complicated.
The pixelspacing is one of the most important things since in our (tomographic) imaging method we have anisotropic voxels. Furthermore I use two custom fields "offset" and "rotation" within the image object that allow to define the precise coordinate system in space. With this I can take two Image objects measured with two different tomographs and overlay them doing the appropriate registration/regridding/image fusion. The rotation field is necessary since MRI scans can be arbitrarily angulated.

@tknopp
Copy link
Contributor

tknopp commented Jan 5, 2017

So if I see it correctly I will have to but the offset into the ranges, but the rotation will remain an ImageMeta field.

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Well, offset is exactly what you can encode in the AxisArray ranges. Might want a simpler approach, how about

A = AxisImage(data, (:x, :y, :z, :time), (dx, dy, dz, dt), [(x0, y0, z0, t0)])

?

As for rotation goes...that would either have to be in the metadata, or possibly you could use a TransformedArray from AffineTransforms.jl. That package will be deprecated by CoordinateTransforms.jl, however, but I can't remember the current status.

@tknopp
Copy link
Contributor

tknopp commented Jan 5, 2017

Yes, such a syntax would be definitely a nice convenience constructor, so thumbs up.

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Oh, and to answer an earlier question, yes, all the IO packages have been updated too if you check things out as described in #542 (comment). That gist has been updated several times, so you may want a fresh copy.

@tknopp
Copy link
Contributor

tknopp commented Jan 5, 2017

Ok thanks.

Its quite interesting to see how this restructuring will change the workflow. Before, all parameters such as pixelspacing where just metadata and I extracted the underlaying array before giving it to a hot loop. Maybe this won't be necessary anymore if the type stability is preserved.

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Yes, the idea is you just pass an AbstractArray to whatever you do. That way you never have to make choices between performance and "self-documentation." It should be just as fast as an Array.

Could you open an issue on ImageAxes with #542 (comment), so I don't forget?

@timholy
Copy link
Member Author

timholy commented Jan 5, 2017

Added to AxisArrays (need to be running master).

@timholy
Copy link
Member Author

timholy commented Jan 30, 2017

Closed by #577

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests