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 classes to build RGB images based on Lupton et al. (2004) #5535
Conversation
I don’t know if astropy prefers that feature commits be rebase flattened, but I can do so if you’d prefer. |
Thank you for the contribution! 👍 I didn't review this in detail but just some general comments:
This looks really nice overall but I think someone else is probably more fit to review the actual code. |
Very nice ! I was looking at this algorithm a few days ago, and wanted to test the code you sent to the mailing list a few months ago, so this is perfect 😄 |
It should be fine to have at both places, last time I've checked (when fixed a bug for it) sphinx combined the two. |
@parejkoj - Thank you! There's already comments by yourself and @MSeifert04 with quite a few suggestions. Maybe implement those first, and then we do more rounds of review? I only very quickly browsed the code. There's stuff like In my experience API discussions and review work best when approached from the user perspective. I.e. I'll wait for high-level docs to be here before reviewing this PR. |
TODO list that I volunteer to finish:
Changes are in a PR to this PR: parejkoj#2, tests are running on travis on my fork: https://travis-ci.org/bsipocz/astropy/builds/ Edit: tests are passing now |
Some more commits to address some of @MSeifert04's comments are added to my PR. |
Can http://docs.astropy.org/en/stable/api/astropy.visualization.LinearStretch.html#astropy.visualization.LinearStretch |
Ok, I've pushed some cleanups, and merged a big set of changes from @bsipocz . Responding to a few points:
I've got an example usage at the top of the file, but I'm not sure what more would be needed for "high-level docs". |
@parejkoj - High level docs would mean to add it to somewhere here: http://docs.astropy.org/en/latest/visualization/index.html#astropy-visualization, possibly as a sub page, with a paragraph and at least one example. |
Does anyone have a triple of publicly-shareable >8bit images I could use to make the example? |
yes, that's were the problems start, because ideally we would want to store any such images in the other data repo, and not here. So for the sake of getting this reviewed and into 1.3, I would suggest to use just random np arrays, or three times one of the test images we already have floating around in the docs. Then over the weekend/next week we can still find some real images to put in there. |
Also for the tests, we could use either or both |
I've pushed a short bit of documentation, and linked it from the visualization Definitely, if the astropy folks think it's ok, l'd rather do the detailed content tests in a subsequent PR. |
providing different scalings and a convenience wraper function. To generate a | ||
color PNG file with the default (arcsinh) scaling: | ||
|
||
>>> from astropy.visualization import lupton_rgb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any rst docs example should be a full script within the file, so you need to add import numpy as np
here, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Can I see what my rendered documentation looks like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
python setup.py sphinx_build
, then it's got generated in docs/_build/html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I can't do matplotlib from within the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can, but you need to use the .. plot
directive. Use the example from e.g. docs/visualization/wcsaxes/index.rst
For the plotting, though, you need to give a full standalone script every time. Adding :include-source:
will repeat the code in the docs, too, so you don't need to have it in there twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CircleCI gives the following error for import matplotlib.image
:
UNEXPECTED EXCEPTION: ImportError('No module named matplotlib.image',)
Is the version of matplotlib that circleCI uses out of date?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think optional dependencies are not installed on circleci, so you will need to skip all doctest where matplotlib is imported/used. In general use .. doctest-skip::
before the code block.
However, here you don't need the >>>
before the lines, remove them from inside the plot directive, just as it's in the other files (e.g. docs/visualization/normalization.rst
, as I realize the wcsaxes one is not yet in this branch)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipping the doctests got it to work. Once we get a better set of images (and a place to put them), we can insert them into the docs.
I don't know if travis is waiting because of the OSX problems, or something else, though the tests all pass on my machine. CircleCI passed, so there's now some minimal working documentation. |
More comments are welcome at this point. I don't know if we'll actually be able to get it in before the freeze or not, given that I don't know the exact procedure there. |
Just to note: there's one travis failure due to |
Looks like all of the travis failures were due to timeouts of one sort or another. |
@parejkoj @bsipocz - Thanks for all the work on this! IMO it would be better to not rush this into 1.3, but spend the time to polish it. My main concern is that the end-user API hasn't seen a lot of discussion and most likely isn't final. To help discussion / review, it would be helpful to have a version of the HTML docs online. I wanted to do this, but for me the Sphinx build is currently broken (see here), not sure yet if it's just for this PR or in master also. @bsipocz - have you seen this error? can you reproduce it with this branch? So currently this PR adds the following functions / classes to the public API of
My suggestion would be to rename And also in Astropy we follow PEP8 naming to use For the "Mapping" classes I'm still a little fuzzy how they relate to the existing "Transform" or "Stretch" classes? Concerning docs examples, maybe you could contact the authors of the paper and ask if they can still find one of the FITS files for the examples used in the paper? Concerning license, you put it as BSD-3 here. But it's a copy of older code, right? What was the license there? The easiest would probably to get the author or the original code to leave a comment here that they agree to re-licensing this code as BSD-3 as done here, then it's fine. There's several small things that should be cleaned up. Just one example:
which isn't a good way to do it. |
I restarted the docs build, but there's again timeouts due to SESAME name lookups: @bsipocz - we had the same problem in Concerning https://gist.github.com/cdeil/5cf16ba5146ed5308c122c82bf9d2020, I think that's actually two issues:
Again, @bsipocz or anyone that has time -- can you please have a look, and if there really is an issue with the docs build erroring out on invalid RST input without good error message, please split it out into a separate issue for Astropy or Astropy helpers. |
@cdeil - How did you restart the build? It still says to be finished 'about 11 hours ago'. I think only people with commit rights can restart them (even though travis says the restart was successful, actually nothing happens when I try it). Also there was a problem (probably still there is) with the astropy server at STScI, so many of the time outs comes from there. Changing the examples is something @adrn has to decide about, but I would be surprised if they are causing the issue and the gallery examples are not tested and only run during the docs building. I'll try to look into the details of your gist tracelog later, but it's strange travis doesn't fails. I guess it may be a problem with python2 where we don't test. Have you tried to build the docs with python3? |
Done in latest commit.
I've de-cameled the interface.
I've looked at the Stretch classes, and they are similar, but have less functionality. I suppose we could lift these into Stretch, but these have some things that are quite specific to this algorithm (e.g. assuming there are 3 images). We could certainly rename *Mapping to something else, if you'd prefer.
The code this is converted from lived here, and all the LSST code is GPL3. I'd gotten permission from Paul Price and Robert Lutpon (the two primary contributors) to re-license, plus the LSST project managers support this re-licensing, so there's no worries there. If you'd rather something other than BSD3, we can certainly do that.
I've documented it. I disagree about removing it though: I suspect a significant use case of this code will be batch-producing a number of images, and it'll save the user from just re-doing that same matplotlib line. I think it's quite helpful.
I've asked Mike Evans about whether the SDSS image use policy also applies to images generated from the data. I can easily reproduce e.g. Figure 1 of Lupton et al. from the SDSS data. Finally, I don't understand why the sphinx build is failing. It fails for me locally when I run |
from . import ZScaleInterval | ||
|
||
|
||
__all__ = ['make_lupton_rgb', 'Mapping', 'LinearMapping', 'AsinhMapping', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having think about this a little more, I think we should only leave make_lupton_rgb
here, so all the rest became quasi private, and thus easier to remove/rework later without a having to support the deprecated API. I think this would address some of the review comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds good to me. The classes aren't that hard to use, but they're also quite specific use-cases that most people probably won't want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some more inline comments.
@@ -0,0 +1,29 @@ | |||
********************************** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These ***
lines have to have the exact same length as the heading, or Sphinx will complain.
My guess is that if you fix this and potential other RST formatting issues, than python setup.py build_docs
will work (because it does in master
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks. RST in general is not as picky so long as the header are at least as long as the text.
That said, the docs build is still failing on my local machine, with the same error.
|
||
`Lupton et al. (2004)`_ describe an "optimal" algorithm for producing red-green- | ||
blue composite images from three separate high-dynamic range arrays. This method | ||
is implemented in `~astropy.visualization.lupton_rgb` as a set of classes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not generate a link in the HTML docs currently.
The only symbol Sphinx knows about is ~astropy.visualization.make_lupton_rgb
, so you should link to that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thank you.
.. doctest-skip:: | ||
|
||
>>> import numpy as np | ||
>>> from astropy.visualization import lupton_rgb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The astropy.visualization.lupton_rgb
module isn't part of the official API.
Only the astropy.visualization.make_lupton_rgb
function is, so please change to:
from astropy.visualization import make_lupton_rgb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks.
filename=None): | ||
""" | ||
Make an RGB color image from 3 images using an asinh stretch. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please link to the high-level docs section about this function from the docstring?
Here's an example of what I mean: a class Galactocentric
which has a separate page in docs
and the class docstring and separate page are cross-linked:
http://astropy.readthedocs.io/en/latest/coordinates/galactocentric.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for giving me an example. Done.
""" | ||
image_r = np.asarray(image_r) | ||
if image_g is None: | ||
image_g = np.asarray(image_r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove np.asarray
call on this line. Has already been called before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. I've removed both of those asarrays, as the array sanitizing happens in the Mapping constructor.
if image_b is None: | ||
image_b = image_r | ||
|
||
if saturated_border_width: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A saturated_border_width
option that just raises NotImplementedError
isn't useful.
I would suggest to just add the option in a future PR, if / when it's implemented.
(or just implement it here if you want to add it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code for this currently lives in some LSST C++ that would be a significant effort to convert, particularly because it assumes the inputs are afw.MaskedImages, and thus have the saturated pixels pre-masked.
Should I file a PR for this now?
filename=None): | ||
""" | ||
Make an RGB color image from 3 images using an asinh stretch. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any information about the data type and range of values the input image arrays should have and the output image array has?
Is it float or can it also be integers?
Does it have to be range 0 .. 1 or can input / output be any range?
If it's possible to quickly summarise how to use this function correctly in the docstring, so that users don't have to open up the paper or look at the code, I think that would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I've updated the docs.
|
||
The three images must be aligned and have the same pixel scale and size. | ||
|
||
Example usage: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest to remove the example here.
A better example will be in the high-level docs page.
If you think another example in the "code" file is useful e.g. for interactive help lookup (I don't), then I'd suggest to add it to the function docstring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, that's fine. I put the example there because I didn't know that there would be docs elsewhere.
|
||
Notes | ||
----- | ||
Inputs may be MaskedImages, Images, or numpy arrays and the return is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this note referring to LSST objects "MaskedImages, Images" can just be removed. Here we use Numpy arrays and that's documented above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I thought I'd removed all the references to LSST Images. I found one more MaskedImage reference that I removed.
Docs still don't build, and I don't understand why. I'm trying to reproduce Figure 1 of the paper using the SDSS FITS files, but I need to put the gri images in the same frame. I can't see quite how to do that with astropy.wcs: is there a "remap to sky" method? |
There's https://reproject.readthedocs.io |
Hooray! It all passed - merging! Thanks for all your work on this @parejkoj |
@eteq - thanks for merging this! However, I noticed the following issues with this pull request:
Would it be possible to fix these? Thanks! This is an experimental bot being written by @astrofrog - let me know if the message above is incorrect! |
Oh @astrobot, you are a lifesaver. Changelog added in c3832fd . But FYI, @parejkoj, in future contributions try to remember to do that ;) Speaking of, @astrofrog, it might make sense to drop the "experimental" warning (just replace it with a "contact @astrofrog if there are problems")? |
I would replace experimental with all seeing and powerful. |
(I wish there was a +💯 option as a reaction, as I would give that for #5535 (comment)) |
@eteq Sorry, I totally forgot about the changelog! Thanks for taking care of it. Nobody noticed during review, though... ;-) I see that the "contributing" docs say to put in the changelog note before the PR is opened, so I'll try to do that in the future. |
@parejkoj - Yep that's true, but it's not quite so simple: the catch is that to do that you need to know the PR # - I do a trick where I prepare the commit and then quick check what the last issue # is and do that+1... but that's admittedly awkward. The hope is to develop a better changelog system for this in the near future, though! |
Really great to have this in 1.3, thanks @parejkoj ! |
Thanks, @parejkoj! |
This will need a |
Um, I can't remember how I set up @astrobot. It's sentient now. |
@parejkoj - Thank you! |
I tried this out now, using the This should be changed, no? |
Interesting. I think the solution would be to put |
@parejkoj - I tried to put From a quick look at the MPL function, it seems that there's a bug where the For now, I'm using I don't have time to make a PR against Astropy or MPL this week. @parejkoj - Can you check for the image example in the docs and compare to DS9, and make a PR if there is indeed an issue?
|
One question is whether we should assume that ds9 is "correct" here: there are a few possible conventions. |
Well, my recommendation was to not re-expose I agree there's no right and wrong, just different conventions. So +1 to remove |
Setting |
I agree with @cdeil - the output PNG/JPEG files should be flipped vertically by default. FITS files are stored with the bottom pixels first and other formats have the top pixels first, so this is not just about DS9. |
This code implements the Lupton et al. (2004) algorithm for RGB image stacks, to make optimally colored images from 3 separate bands. Not all functionality is implemented (some LSST C++ code for better interpolation over saturated pixels will need to be ported eventually), but the code has been used by a few different people to make color JPEGs and PNGs from 1-3 FITS images.
Yet to do:
interval.ZScaleInterval
in place ofZScaleMapping
.replaceSaturatedPixels
fromlsst::afw::display
. This should probably be a future feature: it's going to be a bit of work to convert that C++ into relatively fast python.This code was pulled from lsst.afw.display.