FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Image interpolation wrong for pixel values exceeding vmax · Issue #8631 · matplotlib/matplotlib · GitHub

Repository navigation

Image interpolation wrong for pixel values exceeding vmax  #8631

Description

Bug report

Bug summary

With imshow(), image interpolation gives wrong results near pixels with values exceeding vmax. A large rectangular region (given by the interpolation kernel size) is filled with color corresponding to maximum value.

Code for reproduction

t = zeros((50, 50))
t[10, 10] = 1
t[10, 20] = 2
imshow(t, vmin=0, vmax=1, interpolation='lanczos')

see gist

Actual outcome

Expected outcome

Ok with matplotlib 2.0.0

Matplotlib version

  • Operating System: macOS
  • Matplotlib Version: 2.0.1, 2.0.2
  • Python Version: 2.7
  • Jupyter Version (if applicable):
  • Other Libraries:

matplotlib installed via conda-forge

Activity

  1. added this to the milestone on May 16, 2017
  2. dstansby commented on May 16, 2017

    Member

    Can confirm this on master - the problem bisects to 1a7c4ef

  3. QuLogic commented on May 16, 2017

    Member

    Does #8300 help with this then?

  4. tacaswell commented on May 16, 2017

    Member

    No, I do not expect #8300 to help with this.

    The issue is that Agg does some aggressive clipping (not sure exactly when in the interpolation steps) so I am not sure that the 2.0 case is actually interpolating correctly (ex a value that should have been scaled to 2 in the normalized units is clipped to 1 before it is interpolated).

    More generally, poisoning every screen pixel that include out-of-range source pixels seems like the 'right' thing to do.

  5. geggo commented on May 18, 2017

    Author

    Hi,
    thanks for all responses.
    Indeed, changes of commit 1a7c4ef induced the new behaviour, and it seems this was intentionally. I am missing the background why. But from my point of view this very strict handling of over/under values is not a good way: My current use case is displaying real-world grayscale images, to adjust contrast I set vmin/vmax. Some pixel values are outside this range, with the new behaviour they mess up the result, with the old they just saturate - a very natural behaviour.

    With some testing and looking at the sources, I could observe that Agg clips values after interpolation, in this sense interpolation is done correctly, only the information about under/over values is lost. To recover this, I am currently playing with an approach to compare the interpolation result of the raw image and one with values scaled by 0.5. If the interpolation results don't match up, there must have been some clipping. Do you think this makes sense?
    Or would it be better to modify Agg, such that for float data clipping can be suppressed?

    Gregor

  6. tacaswell commented on May 23, 2017

    Member

    Doing the interpolation twice is almost certainly too expensive. Comparing where pixels == 1 or 0 in the output plane and are in the mask where they have been poisoned might be a better approach? I am definitely open to changing this (I was thinking much more about nan / inf when I did this before) and it would break some of the degeneracy in over/under regions overlapping.

    Another reasonable idea may be to just scale down the input to [.1, .9] so we can do the clipping our selves.

    This will still not correctly capture pixels where the interpolation pushes the result value out of range despite everything around it being with-in range (which I think some of the higher-order interpolations can do.

    We have a vendored version of the last non-gpl version of Agg which has slightly divereged from upstream. We are not the only project with a vendored-and-changed Agg and it does cause problems (like seg-fault problems) so I am wary of making aggressive changes to Agg it's self, but using different functions from Agg is on the table.

  7. geggo commented on May 23, 2017

    Author
  8. geggo commented on May 24, 2017

    Author

    First results, I tried the approach to scale the image values to 0.25-0.75 before interpolation, so clipping for the unscaled image can be detected, for a proper assignment of under/over mask. This needs very few changes

    diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py
    index b80f3dd..d2c6cde 100644
    --- a/lib/matplotlib/image.py
    +++ b/lib/matplotlib/image.py
    @@ -375,8 +375,10 @@ class _ImageBase(martist.Artist, cm.ScalarMappable):
                         # this is to work around spurious warnings coming
                         # out of masked arrays.
                         with np.errstate(invalid='ignore'):
    -                        rgba[..., 1] = np.where(A < 0, np.nan, 1)  # under data
    -                        rgba[..., 2] = np.where(A > 1, np.nan, 1)  # over data
    +                        # rgba[..., 1] = np.where(A < 0, np.nan, 1)  # under data
    +                        # rgba[..., 2] = np.where(A > 1, np.nan, 1)  # over data
    +                        rgba[..., 1] = A * 0.5 + 0.25 #scale raw image to 0.25...0.75
    +
                         # Have to invert mask, Agg knows what alpha means
                         # so if you put this in as 0 for 'good' points, they
                         # all get zeroed out
    @@ -457,9 +459,11 @@ class _ImageBase(martist.Artist, cm.ScalarMappable):
                     invalid_mask = ~output.mask * ~np.isnan(output.data)
                     # relabel under data.  If any of the input data for
                     # the pixel has input out of the norm bounds,
    -                output[np.isnan(hid_output[..., 1]) * invalid_mask] = -1
    +                # output[np.isnan(hid_output[..., 1]) * invalid_mask] = -1
                     # relabel over data
    -                output[np.isnan(hid_output[..., 2]) * invalid_mask] = 2
    +                hid_nonclipped = (hid_output[..., 1]-0.5)*2 + 0.5 # interpolation without clipping
    +                output[hid_nonclipped < -0.05] =  -1 # with safety margin
    +                output[hid_nonclipped > 1.05] = 2
     
                 output = self.to_rgba(output, bytes=True, norm=False)

    Demonstration of the outcome for different strength of the outliers (1%, 10%, 100%, 1000% of max) with over/under masking based on values after interpolation (new approach):

    for comparison result with mpl 2.0.0, where over/under mask relies on interpolating the raw over/under mask:

  9. tacaswell commented on May 25, 2017

    Member

    I like it! The oscillations around the strong peak from the higher-order kernels is wild, but I think correct.

    Why rescale the values in plane 1, wouldn't it be simpler to just compare them against [.25, .75]?

    I also do not understand the 5% margin.

    Is the over/under pair in the top-right symetric? It looks like the red one is a bit weaker than the cyan one.

  10. modified the milestones: , on Jun 17, 2017
  11. added
    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
    on Jun 17, 2017
  12. self-assigned this
    on Jul 25, 2017
  13. tacaswell commented on Jul 30, 2017

    Member

    I have a dev branch where we interpolate on the raw data before normalization which seems to do better:

    import numpy as np
    from matplotlib import pyplot as plt
    from matplotlib import cm
    import copy
    W=np.random.randn(200,200)
    cmap = copy.copy(cm.get_cmap('seismic'))
    cmap.set_over('k')
    cmap.set_under('m')
    
    plt.imshow(W, interpolation='bicubic', cmap=cmap, vmin=-1, vmax=1)
    plt.colorbar(extend='both')

    import matplotlib.pyplot as plt
    import numpy as np
    
    cmap = copy.copy(cm.get_cmap())
    cmap.set_over('k')
    cmap.set_under('r')
    
    t = np.zeros((50, 50))
    t[10, 10] = 1
    t[10, 20] = 2
    plt.imshow(t, vmin=-0.01, vmax=1, interpolation='lanczos', cmap=cmap)

    Which seems better in both cases (I set the over/under values to see the wild things that these higher-order interpolations do)

  14. tacaswell commented on Jul 30, 2017

    Member

    Also does better on the pathological stress-test, but still some issues at the edges. Also not clear why the bottom left shows more ringing that the top left...

  15. geggo commented on Aug 2, 2017

    Author

    Hi,
    Thanks for tackling this issue! Missed your comment from May 25, sorry. Here, for completeness, the
    gist for the interpolation test I used to create the test images, nothing special.

    The aim of the 5% safety margin I used in my patch was to somewhat mitigate excessive marking regions as over/under due to ringing, or due to rounding errors. After playing more with this I think it is not needed, does not give a significant improvement.

    Gregor

  16. modified the milestone: on Aug 6, 2017
  17. tomchor commented on Aug 15, 2017

    Is it asking too much to have the fix come out in a 2.0.3 bug-fix release? I think this was the original idea, but then apparently 2.0.3 is not happening anymore.

    This bug seems kind of critical and, while we could downgrade to 2.0.0, I think it's worth it to have it fixed soon, as opposed to waiting longer for 2.1 to be ready.

  18. tacaswell commented on Aug 15, 2017

    Member

    There will be no 2.0.3, but there should be a 2.1rc1 by the end of the month. This is one of the few remaining blocking issues.

  19. tacaswell commented on Aug 15, 2017

    Member

    @tomchor can you have a look at #8966 as well to make sure those fixes are with inline with what you expect?

  20. tomchor commented on Aug 15, 2017

    @tacaswell I already did. I haven't tested it myself to be 100% sure but it appears to be inline with my issue.

  21. added this to the milestone on Aug 31, 2017
  22. tacaswell commented on Aug 31, 2017

    Member

    Closed by #8966

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.status: confirmed bug

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


    Back | FazBrowse Home | New Git URL