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
Add page to documentation explaining coordinate conventions in skimage #1170
Comments
|
@stefanv, unfortunately I think it's what makes the most sense when using C-order arrays, since then im[z] gives you a (row, col) image that is a contiguous array. Usually (dep) has a different voxel spacing compared to row-col, and so it should be the leading dimension in C-order arrays (and the trailing dimension in Fortran-order ones). This mirrors the discussion we had about ZYX in SLIC. (which will need to be updated according to this discussion.) |
Hasn't that ship sailed already? scikit-image already uses MxNx3 for color images. Is what you're saying that, for spatial data, we should use a different ordering of axes? |
Imo we should stay with MxNxDepth, independent of rc vs xy |
@stefanv @ahojnnes it's completely arbitrary what we call the axes. (row, col, channel) accurately describes what we do and is intuitive. For 3D grayscale data, whether we call something (row, col, depth) or (depth, row, col), it will not influence the behaviour of anything, only how we think of the volume. There are significant advantages to thinking about it in terms of (depth, row, col). For example, what prints out to the screen will mirror how we are thinking of the image: In [1]: x = np.arange(60).reshape(3, 4, 5)
In [2]: x
Out[2]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39]],
[[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49],
[50, 51, 52, 53, 54],
[55, 56, 57, 58, 59]]]) |
I agree that if the core object for scikit-image is |
@jni I agree that there are advantages to doing it that way, it's just not the way we've been doing it (or calling things) so far. If you're saying that we have no algorithms in skimage at this point in time that depend on the (row, col, depth) format, then I guess we can change it without a problem (and hopefully that is the case since, as you said, naming should be arbitrary unless you do an operation that depends on knowing which way is "down"). |
As far as I know there is no algorithm or implementation in skimage that depends on this. It is purely terminology and we get to choose now how we want to go forward. |
E.g. the whole warping framework depends on (row, col, channel). |
@ahojnnes I am not suggesting changing (row, col, channel) for 2D+c, I'm suggesting that we call 3D grayscale images dimensions (depth, row, col) and 3D+c images (depth, row, col, channel). |
@jni OK, but I would name it (Z, Y, X, C) then... ? The row, col <=> depth sounds / looks inconsistent... at least to my ears / eyes :-) |
This would also fit with the warps, since they use (Y, X, C)... |
ZYX has the problem of the location of the origin, which some would assume to be on the bottom left of each plane. (row, col) makes it clear that we are looking at it in numpy order. I agree that "depth" is a bit off but I think we need something that maintains "row col" but adds a third dimension. |
How about |
@blink1073 much like you, I hate to admit it, but I actually do like that terminology... =) |
This could be my physics roots showing, but I always tightly link each spatial dimension to the increasing NumPy array rank. IMHO, the minor potential gains from c-contiguity order To be clear, o------------> y
/|
/ |
/ |
z |
v
x
IMHO our most important concerns
|
@JDWarner nice writeup, as usual! But in this rare occasion I disagree on a few counts...
Of the above, I would say that (1) is really the most important issue. I'd be ok with getting voted down on issue (2). On issue (3) I'm torn. I agree that maintainability trumps performance, but only to an extent, and it's getting to the point where we are getting regular complaints on the mailing list about our performance. So, I think it'd be good to place a bit more emphasis on the speed of scikit-image functions than we have in the past. |
Oh, PIL is another place where (x, y) doesn't match numpy's usage. (Looking at some things there now.) |
I asked my wife (an Aero engineer and photographer). She agreed with row, col, and suggested "layer", which is what PhotoShop calls it. |
@blink1073 did she have an opinion on the ordering of things? =) |
She prefers |
The only downside is having to type |
And also the speed penalty you mentioned... We could use |
@blink1073 as I have mentioned repeatedly, channels != layers/pages/depth! |
You lost me Juan. I thought we were keeping |
So why are you mentioning "the red layer"? |
Ha! So channel == color/wavelength /alpha? |
^ This right here, is exactly how implicit meanings in an axis label lead to confusion! |
My personal vote would be for I'm not sure forcing Fortran vs. C-contiguous arrays is as bad as you're making out. Any Cython function which uses typed memoryviews should have a Python wrapper with |
@blink1073 yes @JDWarner all of our nD/3D functions transparently allow abstract coordinates, and you can put the (page/plane/depth) at the end if you like, no problem. The question is how we prefer users to think and how we prefer our own code to be arranged. I personally strongly prefer Incidentally, the performance difference is not hypothetical, it's real, check the mailing list. Changing our array order convention is not as trivial as you think: you might also need to change your inner/outer for loops, and you'll break with numpy convention, which is our strongest asset. (i, j, k) is clean, but it is not as useful or self-documenting as (row, col, page) or (page, row, col), when these are used correctly. I'm pretty sure with a bit more effort we can figure out what "correctly" means. =) A bit of context about my thinking about performance. You'll find old statements from me on PRs and on the mailing list that readability is much more important than performance, etc. However, I think we are now at a critical moment in Scientific Python adoption. I think the only thing that can stop Python from becoming the next Perl will be its ability to be as performant as the up-and-comers such as Go and Julia. I think nonchalance about performance has got Python to where it is: famous for being slow. (My own gala library also suffers from this.) If we are going to stop that, we need to be at least a bit careful with what we do. Using the right array ordering and notation for the task is an easy thing to get right. |
The C-order does not help us on RGB images, since we'd always have to access each channel as |
|
Yikes, switching the order internally definitely does not scale well: In [2]: a = np.ones((128, 128, 128, 3))
In [3]: def corder(arr):
...: for z in range(128):
...: b = arr[z, :, :, 0] * 3
...:
In [4]: def forder(arr):
...: arr = np.asfortranarray(arr)
...: for z in range(128):
...: b = arr[:, :, z, 0] * 3
...:
In [5]: %timeit corder(a)
100 loops, best of 3: 5.04 ms per loop
In [6]: %timeit forder(a)
1 loops, best of 3: 116 ms per loop I'm with you @jni, I think |
@jni Oddly enough, I started feeling sympathy for your argument earlier this week when working with image stacks, and where typing Anyway, I'm happy with using the first dimension as the depth of the stack, and as you say it happens to be nice that numpy also prints it that way. It also, ironically, allows a person to distinguish better between color and multi-dimensional data. |
@stefanv See benchmarks above: switching to Fortran for colour is probably not worth it, especially in 3D+c images. The entire algorithm would have to be rewritten to change outer loops to inner loops. If multichannel images need to process each channel separately, the best approach is probably to grap copies of each channel:
Or somesuch. |
Sounds reasonable to me, making a local copy of a 2D array whenever we need to work with it: In [13]: a = np.ones((128, 128, 3))
In [14]: def direct(a):
....: for i in range(10):
....: a[:, :, 0] += 10
....:
In [16]: def copy(a):
....: b = a[:, :, 0].copy()
....: for i in range(10):
....: b += 10
....:
In [17]: %timeit direct(a)
1000 loops, best of 3: 267 µs per loop
In [18]: %timeit copy(a)
10000 loops, best of 3: 102 µs per loop |
Copies are c-contiguous: In [1]: a = np.ones((100, 100))
In [2]: a = a.T
In [3]: a.flags
Out[3]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA : False
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
In [4]: b = a.copy()
In [5]: b.flags
Out[5]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False |
One more tidbit: In [6]: %timeit np.ascontiguousarray(a)
100000 loops, best of 3: 16.1 µs per loop
In [7]: %timeit a.copy()
100000 loops, best of 3: 14.7 µs per loop |
I feel a utility function coming on: channels(image) (or something like it)
|
That's a nice function and very readable, imho! Add a "which" kwarg defaulting to None (all channels), in case we only need to work with one (avoid two copies). |
@jni Are you taking authorship of this one (for writing the page in the user guide as well as the utility function)? |
Oops, this fell by the wayside. I'll look into writing this. |
@emmanuelle @jni was at a workshop yesterday, so he's a bit behind, but I'm sure he'll get to it today :) |
@stefanv wrong again! =P I've scheduled this for next week. Do we want to make 0.11 a Christmas present to the SciPy community? ;) |
@jni That is an excellent idea. Shall I organise an online sprint for the
|
+1 @stefanv |
@stefanv I love the idea of a sprint, but how are we dealing with time zones? =) I'm out on the 13th Australia time but could do the 14th morning, which corresponds with afternoon US time on the 13th, so that could work? But then it's like midnight in Europe. =) Where are you these days anyway? LOL |
Juan everyone knows the only true time is whatever time it is in Boston. |
@jni Tag teams! We do a whole weekend. |
Completely random observation: using CC @tonysyu =D |
What's the latest status here? |
OMG Finally: closed by #1280! =D |
There's been a fair bit of recent confusion regarding coordinate systems in scikit-image. In particular, see #1142 (including this comment) and #1140. I suggest we adopt (at least) the following conventions:
We should strive to keep this consistent throughout the library to avoid confusion (see above issues), and we should add a page in the docs defining this, just as we do with data types and ranges.
The text was updated successfully, but these errors were encountered: