Skip to content
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

Exploring implementation using Matplotlib as backend #73

Open
kakirastern opened this issue Mar 7, 2019 · 30 comments
Open

Exploring implementation using Matplotlib as backend #73

kakirastern opened this issue Mar 7, 2019 · 30 comments
Labels

Comments

@kakirastern
Copy link
Contributor

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
Copy link
Member

pllim commented Mar 7, 2019

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.

@pllim pllim added the enhancement New feature or request label Mar 7, 2019
@kakirastern
Copy link
Contributor Author

Thanks @pllim for the info!

@mwcraig
Copy link
Member

mwcraig commented Mar 7, 2019

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
Copy link
Member

pllim commented Mar 7, 2019

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
Copy link
Contributor Author

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

@kakirastern
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@mwcraig
Copy link
Member

mwcraig commented Mar 11, 2019

Maybe MattImageWIdget? 😉

@pllim
Copy link
Member

pllim commented Mar 11, 2019

Matplotlib is usually shortened to MPL.

@pllim
Copy link
Member

pllim commented Mar 11, 2019

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
Copy link
Contributor Author

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

@michiboo
Copy link

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

@kakirastern
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@michiboo
Copy link

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

@kakirastern
Copy link
Contributor Author

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

@pllim
Copy link
Member

pllim commented Mar 11, 2019

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
Copy link
Contributor Author

kakirastern commented Mar 11, 2019

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
Copy link
Contributor Author

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
Copy link
Member

pllim commented Mar 12, 2019

@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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Member

pllim commented Mar 13, 2019

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
Copy link
Contributor Author

Yup, sure. Now I see...

@kakirastern
Copy link
Contributor Author

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
Copy link
Contributor Author

kakirastern commented Mar 15, 2019

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

@kakirastern
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants