astropy / astrowidgets

*PRE-ALPHA*/heavy dev. Jupyter widgets leveraging the Astropy ecosystem
https://astrowidgets.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
31 stars 14 forks source link

Exploring implementation using Matplotlib as backend #73

Open kakirastern opened 5 years ago

kakirastern commented 5 years ago

I am interested in exploring the development of a non-interactive version using Matplotlib as the backend, towards a non-interactive version which would implement as much of the API as possible in a non-interactive format. However, I have never done anything similar. Is there any suggestions as to some workflow or just simple guidelines I should follow?

pllim commented 5 years ago

Thank you for your interest!

I can't say if mine is the suggested workflow, but personally, I would start with example_notebook/ginga_widget.ipynb. For prototyping, it would be easier to implement your version of astrowidgets.core.ImageWidget in your notebook cell. That way, you don't need to restart kernel or whatever to install your dev version constantly.

So, in your prototype, instead of from astrowidgets import ImageWidget, you would be using a local version of the ImageWidget. The goal is to replicate that notebook using your own implementation without changing the public API.

Public methods that are not working yet can raise NotImplementedError for now but they cannot be deleted.

However, optional keyword does not need to exist in your implementation but invoking them also cannot cause error. So a call like ImageWidget(logger=logger) -- while you do not have to necessarily do anything useful with the logger at this point, you have to support that API, so a **kwargs in your constructor would solve that problem right away.

@mwcraig or @eteq , feel free to correct me if I am steering @kakirastern in the wrong direction.

kakirastern commented 5 years ago

Thanks @pllim for the info!

mwcraig commented 5 years ago

This seems like a good way to go. It wouldn't be a bad approach to continue to use ginga to generate the underlying image but then display the image with matplotlib....though I suppose that the classes/functions in astropy.visualization helps quite a bit with converting a raw astronomical image to something displayable by matplotlib.

There is a pretty limited implementation of a simple image viewer using matplotlib at https://github.com/mwcraig/ccd-reduction-and-photometry-guide/blob/master/notebooks/convenience_functions.py#L6

https://github.com/mwcraig/ccd-reduction-and-photometry-guide/blob/master/notebooks/convenience_functions.py#L6 motivation for wanting a non-interactive backend like matplotlib is that the API written for the image widget is really convenient for setting up static plots too!

pllim commented 5 years ago

At one point, outside of notebook, I wrote a script to grab mouse click X & Y locations out of matplotlib imshow. But AFAIK, there is no way to natively have matplotlib then convert X & Y to RA & DEC, so that is something you need to re-implement using astropy.wcs.

https://github.com/pllim/playpen/blob/master/plot_click_example.py (no guarantee that it still works)

kakirastern commented 5 years ago

Thanks for the leads, I will definitely follow up on those

kakirastern commented 5 years ago

I really like the idea of using Ginga to render directly into a Matplotlib figure, even though support is limited to viewing widget itself. The strength would definitely be in the overplot options.

kakirastern commented 5 years ago

BTW, I might as well just call my version of the widget MatImageWidget...

mwcraig commented 5 years ago

Maybe MattImageWIdget? 😉

pllim commented 5 years ago

Matplotlib is usually shortened to MPL.

pllim commented 5 years ago

I don't know what is the benefit of using this widget if it requires Ginga anyway. Ginga also can overplot by overlaying its canvas objects. IMHO I would use this if I want publication quality plots without having to install Ginga. Matt, what is the use case that you have in your mind for this implementation?

kakirastern commented 5 years ago

Ah, I see... then I will go without Ginga then, just Matplotlib.

michiboo commented 5 years ago

I have try to just use Matplotlib but notice the quality of the image was not as good.

kakirastern commented 5 years ago

Hi @michiboo, would it be possible to share a bit of the details about your Matplotlib settings for the displaying of the images? Would like to see if I will get the same qualify with my setup or if I can reproduce the "qualify problem".

kakirastern commented 5 years ago

Matplotlib can produce quite good resolution images from my experience...

michiboo commented 5 years ago

If you compare with one produce by Ginga, you can see the difference.

kakirastern commented 5 years ago

Okay, thanks @michiboo. I will keep that in mind in case the issue becomes a problem.

pllim commented 5 years ago

You can set different Matplotlib backends, as with Ginga. By default, I think Ginga uses Qt, while Matplotlib uses TkAgg. If you want, you can play with different backends and see if they make any, well, difference.

https://matplotlib.org/faq/usage_faq.html#what-is-a-backend

kakirastern commented 5 years ago

Yup, sounds like some good experimentation to try out... I use TkAgg as the default Matplotlib backend. But I can certainly change it to something else and see the difference.

kakirastern commented 5 years ago

I think I will try working on this enhancement a bit since I would like to learn about widgets... But I have decided to use astroquery as my GSoC 2019 project idea, since that one is more flexible and I can adjust my workload based on my progress. Turned out a lot of work is needed to develop astroquery a bit more, but help is lacking. And, I see some other potential candidates have already expressed interest in the astrowidgets idea already, so I think it would be better for me to choose something else. That way maybe our collective chances of getting selected will be greater. That's just a guess though, based on my understanding of the situation.

pllim commented 5 years ago

@kakirastern , you should double check on the official GSoC mailing list for Open Astronomy. I think Open Astronomy only has a limited number of spots. Not all proposed projects will be assigned a participant. Please check over there before making a final decision.

kakirastern commented 5 years ago

Okay @pllim, I will give both ideas a try over the next week or so, and see how things go before making a final decision then.

kakirastern commented 5 years ago

Hi @pllim, I modified a bit of your matplotlib script to make it work in the tentative MatImageWidget as follows:

def plot_click(data):
    """Plot dummy data and handle user clicks."""

    # Show data
    if data is not False:
        plt.imshow(data)

    def _on_click(event):
        """Print and mark clicked coordinates."""

        # Print data coordinates to screen
        print('X, Y:', event.xdata, event.ydata)

        # Mark them on plot too
        plt.plot(event.xdata, event.ydata, 'rx')

    # Register click events
    plt.connect('button_press_event', _on_click)

    # Show plot
    plt.show()

And it is indeed working for on IPython now if I do the following:

import matplotlib  
matplotlib.use('TkAgg') 

from astrowidgets_matplotlib_core import MatImageWidget  
import matplotlib.pyplot as plt
w = MatImageWidget  
w.plot_click(([1, 2, 3, 4], [1, 4, 9, 16]))

Then I would get the image: Figure_1

At least things are functional for now. Will need to tweak the code some more to make it good.

pllim commented 5 years ago

Do you mean w = MatImageWidget() (the instance, not the class)?

This is a good start. At some point, the workflow would be something like this:

w = MatImageWidget()
w.load_array([[1, 2, 3, 4], [1, 4, 9, 16]])
w  # In a notebook, this would display the widget

Then on the widget, you can click and have the click do something (like display X and Y in a status bar).

Please note that I am not asking you to implement all this immediately, but rather give you the big picture, so you know what implementation is suitable and what is not. Keep up the good work!

kakirastern commented 5 years ago

Yup, sure. Now I see...

kakirastern commented 5 years ago

And, borrowing @mwcraig's script for show image, if I do the following:

from astrowidgets_matplotlib_core import MatImageWidget 
from astropy.io import fits

w = MatImageWidget()

filename = 'https://astropy.stsci.edu/data/photometry/spitzer_example_image.fits'  
numhdu = 0
w.load_fits(filename, 0, None)
hdul = fits.open(filename)
data = hdul[0].data
hdr = hdul[0].header
w.show_image(data)

I would get an image like this:

Figure_2

I think I will need to tweak the size of the colorbar relative to the figsize a bit so that it will fit the figure size for each setting better, like the one I have shown above.

kakirastern commented 5 years ago

Definitely should work on making the widget "pan" and "zoom" very soon...

kakirastern commented 5 years ago

As an experiment: If I use "auto" aspect as an argument for imshow, I found that generally I would get the height of the figure the same as that of the colorbar, as follows... Figure_2_auto_aspect Is this desirable behavior?

kakirastern commented 5 years ago

Or alternatively I can use my preferred aspect="equal" in imshow(), which gives something very similar to the original effect, like the following:

Figure_2_aspect_equal

Anyway, for generating the above two plots I am digging the constraints set by the attributes fraction=0.046, pad=0.04 in colorbar(). I think I prefer using an aspect setting in imshow() instead of in colorbar(), if they give the same effects... Any thoughts or preferences for me to use?

kakirastern commented 5 years ago

After using the wcs from the header of the same FITS file as projection, I was able to get the X and Y axis into RA and Dec in "dd:mm:ss" format so that the image now looks like the following instead (with aspect='auto'):

Figure_3_aspect_auto

kakirastern commented 5 years ago

Can get the Matplotlib canvas working in an interactive mode in IPython... not sure if this would help but it looks cool 😮:

Screenshot 2019-03-16 14 20 22

I understand I will need to incorporate Jupyter widgets somehow...