Enable warping with more than just doubles.#3253
Enable warping with more than just doubles.#3253hmaarrfk wants to merge 5 commits intoscikit-image:masterfrom
Conversation
afde931 to
689245e
Compare
|
Cool stuff!
No, repeated uint8 -> float -> uint8 -> float... conversions result in artifacts. See one demonstration here. As you know I am not a fan of our type and range conversions, but the conversion to float is there for a good reason and converting float -> uint8 should only happen at the very end of a pipeline — and we have no way of knowing when that is. So the user has to be responsible here, one way or another. We also have to think about how to combine this with #3148... Will have a chat with @stefanv about this, as well as the deprecation path.
Yes, very good! =) |
This comment has been minimized.
This comment has been minimized.
|
while thinking about it. I think it is best for the user to be able to
provide an output dtype if we want to allow flexibility.
That would allow us to chain algorithms together if we need to, while
leaving the user’s units intact.
I also like the idea of providing an output array, but that is a different
story. Maybe there is place for both.
…On Sat, Jul 7, 2018 at 11:19 AM Juan Nunez-Iglesias < ***@***.***> wrote:
@hmaarrfk <https://github.com/hmaarrfk>
Cool stuff!
I think that calling anything like: img_as_* , or any kind of range
checking should be considered a mistake without converting the data back
to the original type, should be considered a mistake.
No, repeated uint8 -> float -> uint8 -> float... conversions result in
artifacts. See one demonstration here
<#2677 (comment)>.
As you know I am not a fan of our type and range conversions, but the
conversion to float is there for a good reason and converting float ->
uint8 should only happen at the very end of a pipeline — and we have no way
of knowing when that is. So the user has to be responsible here, one way or
another.
We also have to think about how to combine this with #3148
<#3148>...
Will have a chat with @stefanv <https://github.com/stefanv> about this,
as well as the deprecation path.
for now it passes, so that is a good thing right???
Yes, very good! =)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3253 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAFfmNHH6IPGtqhmq42RSiRch6JoFM72ks5uEPujgaJpZM4VGMoJ>
.
|
|
Hello @hmaarrfk! Thanks for updating the PR.
Comment last updated on October 25, 2018 at 02:48 Hours UTC |
Codecov Report
@@ Coverage Diff @@
## master #3253 +/- ##
==========================================
- Coverage 87.77% 86.82% -0.95%
==========================================
Files 323 341 +18
Lines 27138 27439 +301
==========================================
+ Hits 23820 23825 +5
- Misses 3318 3614 +296
Continue to review full report at Codecov.
|
1cd0abe to
8262d73
Compare
|
@jni: I just improved the the PR taking into consideration your feedback. Honestly, its up to you and the BDFL team with regards to how you want to deal with types, I just wanted to add flexibility to the algorithms which I think this PR achieves without imposing my point of view on the codebase.
|
06df6da to
7c51f55
Compare
|
plot twist, this actuallly speeds up float64->float64 somehow, but slows down uint8->uint8 significantly. fun times! |
7c51f55 to
a0e2a5d
Compare
skimage/feature/_texture.pyx
Outdated
|
|
||
|
|
||
| def _local_binary_pattern(double[:, ::1] image, | ||
| def _local_binary_pattern(cnp.float64_t[:, ::1] image, |
There was a problem hiding this comment.
i don't know. gardening. Raymond Hettinger taught me not to do it. I'll revert.
| texture[i] = bilinear_interpolation(&image[0, 0], rows, cols, | ||
| r + rp[i], c + cp[i], | ||
| 'C', 0) | ||
| bilinear_interpolation[cnp.float64_t, double, double]( |
There was a problem hiding this comment.
Whoa, what's going on here? Do you have a reference for this syntax?
There was a problem hiding this comment.
https://cython.readthedocs.io/en/latest/src/userguide/fusedtypes.html#indexing
fused type indexing. I didn't want to make changes to this function.
| output_shape : tuple (rows, cols), optional | ||
| Shape of the output image generated (default None). | ||
| dtype : | ||
| The numric type of the output image. |
There was a problem hiding this comment.
This docstring is pretty woefully inaccurate...
|
|
||
| ctypedef fused np_numeric: | ||
| np_real_numeric | ||
| np_complexes |
There was a problem hiding this comment.
I love this file, and have wanted it for a long time. Thank you!!!
There was a problem hiding this comment.
This should really be part of cython....
There was a problem hiding this comment.
Also, if you want this, you should take it out of this PR since I'm not sure where the performance drop is coming from here. Cython casting?
There was a problem hiding this comment.
I agree about Cython building this in. Perhaps that should be your next PR? ;) In the meantime I'm happy to wait for you to hunt down the errant cast... And I'm also happy to merge, since ~90% of our warping is probably float-based.
There was a problem hiding this comment.
i probably need a bit of time to polish it up. it will get there.
There was a problem hiding this comment.
Perhaps that should be your next PR? ;
I'd also suggest writing a book on Practical Cython 😅. Seems like @hmaarrfk is a rare expert in this topic.
skimage/_shared/interpolation.pxd
Outdated
| cdef inline void nearest_neighbour_interpolation( | ||
| np_real_numeric* image, Py_ssize_t rows, Py_ssize_t cols, | ||
| np_floats r, np_floats c, char mode, np_real_numeric cval, | ||
| np_real_numeric_out* out) nogil: |
There was a problem hiding this comment.
Can you indent this 8 spaces to differentiate from a code block?
| cdef inline double quadratic_interpolation(double x, double[3] f) nogil: | ||
| top = (1 - dc) * top_left + dc * top_right | ||
| bottom = (1 - dc) * bottom_left + dc * bottom_right | ||
| out[0] = <np_real_numeric_out> ((1 - dr) * top + dr * bottom) |
There was a problem hiding this comment.
Looking at this code now, it might not be as hard as I thought to generalise to nD... =) (Not related to this PR, I just meant to say that your code has made me wonder about this long-standing goal.
skimage/feature/_texture.pyx
Outdated
|
|
||
| # To compute the variance features | ||
| cdef double sum_, var_, texture_i | ||
| cdef cnp.float64_t zero = 0 |
There was a problem hiding this comment.
Why do i have this? I'm going to try and take it off.
| if output_shape is None: | ||
| out = np.empty_like(image, dtype=dtype) | ||
| else: | ||
| out = np.empty(shape=output_shape[:2], dtype=dtype) |
There was a problem hiding this comment.
I'm not sure what happens if you pass a 3D image and specify output shape.
|
@jni, I'm glad you like this, but the performance is about 30% worse on unint8, and 20% better on float64. Now that I got asv running, I think I need to be a little more systematic about the development of this. edit: specified |
stefanv
left a comment
There was a problem hiding this comment.
I suspect the slowdown comes from situations such as when your image is of a different dtype than your cval. Then, a cast has to happen every time that the cval is assigned to the output image. I doubt there is a way to tell Cython: the image can be of any numeric type, but then we also want the cval to be the same type, otherwise cast it to the same type before continuing.
| cdef inline Py_ssize_t fmin(Py_ssize_t one, Py_ssize_t two) nogil: | ||
| return one if one < two else two | ||
|
|
||
| ctypedef fused np_real_numeric_out: |
There was a problem hiding this comment.
I'm forcing cross type compilation too. so uint8->uint16, but more importantly uint8->float64
There was a problem hiding this comment.
I added a note about this.
|
@stefanv, the fact that
However, I think you are correct in thinking that it is related to an extra cast somewhere. |
e79e15a to
4b8a863
Compare
|
Final closing remarks on my experience addign fused types in this: It seems that might actually have sped up interpolation order=3 seems to be compute bound and not memory bound. It did help by about 20% in the CPU bound case of a 4096x4096 matrix. This might hit the limitations of what cython can do. Not too sure. This might be a good demo for Pythran or Numba to attack. |
|
I don't know why no speedup is seen with the float32 operations. Seems like you should be able to get a 2x speed up from going to float32 because SSE(2) can make 2x as many vectorized operations. Currently, this is an Maybe for vectorization, we can make this a Anyway, just some thoughts. |
2126022 to
f88a814
Compare
ee4ad77 to
0ca1d93
Compare
skimage/transform/_warps_cy.pyx
Outdated
| Parameters | ||
| ---------- | ||
| x, y : double | ||
| x, y : np_float |
There was a problem hiding this comment.
Considering our other discussion, do you think np_float is unambiguous here?
There was a problem hiding this comment.
haha totally, i'll revert the notation!
|
@stefanv I'm really not sure how to interpret them :/ |
9fa5823 to
fc4e60a
Compare
|
I think @jni is more ahead of me on this one. I just want to see the results of a command like |
|
@hmaarrfk those benchmarks look like small sizes might be dominated by the dispatch time, whereas big sizes benefit from type-specific computing? What asv command did you use to run that? |
|
I wish I remembered..... |
|
Here is the docs for it https://asv.readthedocs.io/en/stable/commands.html#asv-continuous |
Fixes #1287
With complex types too!!! That was more difficult than it sounds....too difficult to do without help from the compiler.This breaks things because people might have expected an implicit cast to float. Needs somebody to make a decision on how to deprecate this.I think that calling anything like:
img_as_*, or any kind of range checking should be considered a mistakewithout converting the data back to the original type, should be considered a mistake.Personally, I think that the parameter should be ripped out. Instead, people should have to think about what type they want their data to be represented as.
Edit: I just think that types and units are different. The
img_as*functions assume that the units you are I think that theimg_as_*functions within algorithms is a mistake as it assumes a very specific unit system namely that uint8 is in ADC counts, and that floats are normalized ADC counts. That might be true for most cameras, but at the end of the day, it is just one unit system, and isn't universaly true, especially for other data that might have ND correlations.I probably need some help writing tests for this, but for now it passes, so that is a good thing right??? maybe there is an implicit cast as float somewhere.Edit: clarification on what I think should be considered a mistake.
Checklist
[It's fine to submit PRs which are a work in progress! But before they are merged, all PRs should provide:]
./benchmarks, if your changes aren't covered by anexisting benchmark
For reviewers
(Don't remove the checklist below.)
later.
__init__.py.doc/release/release_dev.rst.@meeseeksdev backport to v0.14.x