-
Notifications
You must be signed in to change notification settings - Fork 77
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
request to be able to show a second image of values as one explores primary image #842
Comments
I can only advise from the plugin side. You might be able to pull it off by writing something like I can display DQ straight on active image using DQInspect, so I don't see why you cannot similarly display values from another extension on yours. |
@jhennawi, can you clarify just a little? Do you have to construct an auxiliary image or is that simply for the convenience of making the wavelength values conveniently accessible? Is the original data a cube? |
Hi Eric,
Thanks for the quick reply. We have two images say, a science image and
wavelength image. At the moment these are images. In the future for IFUs
these may be cubes, and then we would want something like ds9's cube
functionality, but were not there yet.
In reality we have like 4 different science diagnostic images (raw data,
sky-subtracted, sky-subtracted noise normalized, etc.) and then we have a
common wavelength image for them all. What we want to be able to do is use
ginga in WCS registered mode and toggle around all these images (that
currently works). We then want to be able to see the wavelength value at
the bottom of the screen based on the pixel we are hovering over, as
derived from the wavelength image.
As I mentioned, we hacked this in with the WCS, but then that breaks WCS
registration. I'll add finally that the WCS registration is just based on
aligning the pixels, i.e. we are not actually using a full WCS, since these
are spectra.
Joe
…On Tue, May 26, 2020 at 5:04 PM ejeschke ***@***.***> wrote:
@jhennawi <https://github.com/jhennawi>, can you clarify just a little?
Do you have to construct an auxiliary image or is that simply for the
convenience of making the wavelength values conveniently accessible? Is the
original data a cube?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#842 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACC6HI7APGYXUHPDA2B65E3RTRKKDANCNFSM4NKEOG2A>
.
--
------------------------------------------------
Joseph F. Hennawi
Associate Professor
Department of Physics
Broida Hall, UC Santa Barbara
Santa Barbara, CA 93106-9530
Phone: 805-893-3503
Mobile: 805-450-8697
E-mail: joe@ <[email protected]>physics.ucsb.edu
http://web.physics.ucsb.edu/~joe/
enigma.physics.ucsb.edu
------------------------------------------------
|
So the wavelength value for a given pixel at (X, Y) (data coords) is just the value of the wavelength array at (X, Y)? |
that is right.
…On Tue, May 26, 2020 at 5:31 PM ejeschke ***@***.***> wrote:
I'll add finally that the WCS registration is just based on
aligning the pixels, i.e. we are not actually using a full WCS, since these
are spectra.
So the wavelength value for a given pixel at (X, Y) (data coords) is just
the value of the wavelength array at (X, Y)?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#842 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACC6HI4WAYRHD4FQUWLVGFLRTRNPDANCNFSM4NKEOG2A>
.
--
------------------------------------------------
Joseph F. Hennawi
Associate Professor
Department of Physics
Broida Hall, UC Santa Barbara
Santa Barbara, CA 93106-9530
Phone: 805-893-3503
Mobile: 805-450-8697
E-mail: joe@ <[email protected]>physics.ucsb.edu
http://web.physics.ucsb.edu/~joe/
enigma.physics.ucsb.edu
------------------------------------------------
|
@jhennawi, do you want both the |
Hi Eric,
It would be convenient to have a Wavelength or Wave entry on both the left
(Info) and the bottom (Cursor) panels. We will not be mixing camera and
spectra in the same viewer for the present, so having these ready
consistently is better I think. If we have multiple images next to each
other in channels now they would all be spectra. I can imagine a future
application with IFU pseudo images and Camera images next to each other in
different channels where it would be nice to know the wavelength of the IFU
pseudo image in the image cube, but we are nowhere near there yet.
Joe
…On Wed, May 27, 2020 at 3:27 PM ejeschke ***@***.***> wrote:
@jhennawi <https://github.com/jhennawi>, do you want both the Info (to
the left) panel and the Cursor panel (on the bottom) to read the same
way? Will you be mixing viewing of spectra and camera images in the same
viewer?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#842 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACC6HIYBH2B4TOMESVVGHSDRTWHVBANCNFSM4NKEOG2A>
.
--
------------------------------------------------
Joseph F. Hennawi
Associate Professor
Department of Physics
Broida Hall, UC Santa Barbara
Santa Barbara, CA 93106-9530
Phone: 805-893-3503
Mobile: 805-450-8697
E-mail: joe@ <[email protected]>physics.ucsb.edu
http://web.physics.ucsb.edu/~joe/
enigma.physics.ucsb.edu
------------------------------------------------
|
@jhennawi, this sounds pretty simple, I think. I've long wanted to refactor the way that the cursor readout is handled, because I think this won't be the last time we'll want to customize it. I am now thinking about the best way to go about that. From the PR I'll link back to this issue. |
All good Eric, many thanks!
…On Mon, Jun 1, 2020 at 4:10 PM ejeschke ***@***.***> wrote:
@jhennawi <https://github.com/jhennawi>, this sounds pretty simple, I
think. I've long wanted to refactor the way that the cursor readout is
handled, because I think this won't be the last time we'll want to
customize it. I am now thinking about the best way to go about that. From
the PR I'll link back to this issue.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#842 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACC6HI5HD6ADPRGM3I3JGG3RUQYN7ANCNFSM4NKEOG2A>
.
--
------------------------------------------------
Joseph F. Hennawi
Associate Professor
Department of Physics
Broida Hall, UC Santa Barbara
Santa Barbara, CA 93106-9530
Phone: 805-893-3503
Mobile: 805-450-8697
E-mail: joe@ <[email protected]>physics.ucsb.edu
http://web.physics.ucsb.edu/~joe/
enigma.physics.ucsb.edu
------------------------------------------------
|
Hi Joe. So after thinking about this request for a little bit I think it can be pretty easily handled via the existing logic if you are willing to launch Ginga with a custom plugin. Here is a custom plugin called "Pypelt": import numpy as np
from ginga import GingaPlugin
from ginga.AstroImage import AstroImage
class PypeltImage(AstroImage):
"""
Custom image type for Pypelt
"""
def __init__(self, wav_np=None, **kwargs):
AstroImage.__init__(self, **kwargs)
self.wav_np = wav_np
def info_xy(self, data_x, data_y, settings):
info = super(PypeltImage, self).info_xy(data_x, data_y, settings)
try:
# We report the value across the pixel, even though the coords
# change halfway across the pixel
_d_x, _d_y = (int(np.floor(data_x + 0.5)),
int(np.floor(data_y + 0.5)))
_ht, _wd = self.wav_np.shape
if 0 <= _d_y < _ht and 0 <= _d_x < _wd:
# spectral wavelength is stored in auxillary array
wavelength = self.wav_np[_d_y, _d_x]
# choose your best formatting here...
wav_s = "{:<14.6g}".format(wavelength)
else:
wav_s = ''
info.update(dict(ra_lbl="\u03bb", ra_txt=wav_s,
dec_lbl='', dec_txt=''))
except Exception as e:
self.logger.error("Error getting wavelength value: {}".format(e),
exc_info=True)
return info
class Pypelt(GingaPlugin.GlobalPlugin):
def __init__(self, fv):
super(Pypelt, self).__init__(fv)
def load_buffer(self, imname, chname, img_buf, dims, dtype,
header, wav_buf, wav_dtype, metadata):
"""Display a FITS image buffer.
Parameters
----------
imname : string
a name to use for the image in Ginga
chname : string
channel in which to load the image
img_buf : bytes
the image data, as a buffer
dims : tuple
image dimensions in pixels (usually (height, width))
dtype : string
numpy data type of encoding (e.g. 'float32')
header : dict
fits file header as a dictionary
wav_buf : bytes
the wavelength data, as a buffer
wav_dtype : string
numpy data type of wav_buf array encoding (e.g. 'float32')
metadata : dict
other metadata about image to attach to image
Returns
-------
0
Notes
-----
* Get array dims: data.shape
* Get array dtype: str(data.dtype)
* Make a string from a numpy array: buf = grc.Blob(data.tobytes())
"""
self.logger.info("received image data len=%d" % (len(img_buf)))
# Unpack the data
try:
# dtype string works for most instances
if dtype == '':
dtype = np.float
byteswap = metadata.get('byteswap', False)
# unpack the auxillary wavelength file
data = np.fromstring(wav_buf, dtype=wav_dtype)
if byteswap:
data.byteswap(True)
wav_np = data.reshape(dims)
# Create image container
image = PypeltImage(logger=self.logger, wav_np=wav_np)
image.load_buffer(img_buf, dims, dtype, byteswap=byteswap,
metadata=metadata)
image.update_keywords(header)
image.set(name=imname, path=None)
except Exception as e:
# Some kind of error unpacking the data
errmsg = "Error creating image data for '%s': %s" % (
imname, str(e))
self.logger.error(errmsg)
raise GingaPlugin.PluginError(errmsg)
# Display the image
channel = self.fv.gui_call(self.fv.get_channel_on_demand, chname)
# Note: this little hack needed to let window resize in time for
# file to auto-size properly
self.fv.gui_do(self.fv.change_channel, channel.name)
self.fv.gui_do(self.fv.add_image, imname, image,
chname=channel.name)
return 0
def __str__(self):
return 'pypelt' If you save this as a file in $ ginga --loglevel=20 --stderr --modules=Pypelt,RC you can then load your file like so from Python: sh = viewer.shell()
# image name "foo", channel "Image", data is ndarray of float, aux is wavelength data of same
# dimensions (also float), d is a dictionary of FITS header keys and values
args = ["foo", "Image", grc.Blob(data.tobytes()), data.shape, 'float', d, grc.Blob(aux.tobytes()), 'float', {}]
sh.call_global_plugin_method('Pypelt', 'load_buffer', args, {}) |
The good thing about this solution is that you will have established a "beachhead" in Ginga with your own plugin. You can then begin to add more methods or even a GUI, a plugin settings configuration, etc. |
Great! We will give this a try and get back to you.
Thanks,
Joe
…On Mon, Jun 22, 2020 at 4:23 PM ejeschke ***@***.***> wrote:
Hi Joe. So after thinking about this request for a little bit I think it
can be pretty easily handled via the existing logic if you are willing to
launch Ginga with a custom plugin.
Here is a custom plugin called "Pypelt":
import numpy as np
from ginga import GingaPluginfrom ginga.AstroImage import AstroImage
class PypeltImage(AstroImage):
""" Custom image type for Pypelt """
def __init__(self, wav_np=None, **kwargs):
AstroImage.__init__(self, **kwargs)
self.wav_np = wav_np
def info_xy(self, data_x, data_y, settings):
info = super(PypeltImage, self).info_xy(data_x, data_y, settings)
try:
# We report the value across the pixel, even though the coords
# change halfway across the pixel
_d_x, _d_y = (int(np.floor(data_x + 0.5)),
int(np.floor(data_y + 0.5)))
_ht, _wd = self.wav_np.shape
if 0 <= _d_y < _ht and 0 <= _d_x < _wd:
# spectral wavelength is stored in auxillary array
wavelength = self.wav_np[_d_y, _d_x]
# choose your best formatting here...
wav_s = "{:<14.6g}".format(wavelength)
else:
wav_s = ''
info.update(dict(ra_lbl="\u03bb", ra_txt=wav_s,
dec_lbl='', dec_txt=''))
except Exception as e:
self.logger.error("Error getting wavelength value: {}".format(e),
exc_info=True)
return info
class Pypelt(GingaPlugin.GlobalPlugin):
def __init__(self, fv):
super(Pypelt, self).__init__(fv)
def load_buffer(self, imname, chname, img_buf, dims, dtype,
header, wav_buf, wav_dtype, metadata):
"""Display a FITS image buffer. Parameters ---------- imname : string a name to use for the image in Ginga chname : string channel in which to load the image img_buf : bytes the image data, as a buffer dims : tuple image dimensions in pixels (usually (height, width)) dtype : string numpy data type of encoding (e.g. 'float32') header : dict fits file header as a dictionary wav_buf : bytes the wavelength data, as a buffer wav_dtype : string numpy data type of wav_buf array encoding (e.g. 'float32') metadata : dict other metadata about image to attach to image Returns ------- 0 Notes ----- * Get array dims: data.shape * Get array dtype: str(data.dtype) * Make a string from a numpy array: buf = grc.Blob(data.tobytes()) """
self.logger.info("received image data len=%d" % (len(img_buf)))
# Unpack the data
try:
# dtype string works for most instances
if dtype == '':
dtype = np.float
byteswap = metadata.get('byteswap', False)
# unpack the auxillary wavelength file
data = np.fromstring(wav_buf, dtype=wav_dtype)
if byteswap:
data.byteswap(True)
wav_np = data.reshape(dims)
# Create image container
image = PypeltImage(logger=self.logger, wav_np=wav_np)
image.load_buffer(img_buf, dims, dtype, byteswap=byteswap,
metadata=metadata)
image.update_keywords(header)
image.set(name=imname, path=None)
except Exception as e:
# Some kind of error unpacking the data
errmsg = "Error creating image data for '%s': %s" % (
imname, str(e))
self.logger.error(errmsg)
raise GingaPlugin.PluginError(errmsg)
# Display the image
channel = self.fv.gui_call(self.fv.get_channel_on_demand, chname)
# Note: this little hack needed to let window resize in time for
# file to auto-size properly
self.fv.gui_do(self.fv.change_channel, channel.name)
self.fv.gui_do(self.fv.add_image, imname, image,
chname=channel.name)
return 0
def __str__(self):
return 'pypelt'
If you save this as a file in $HOME/.ginga/plugins/Pypelt.py, and invoke
ginga by:
$ ginga --loglevel=20 --stderr --modules=Pypelt,RC
you can then load your file like so from Python:
sh = viewer.shell()# image name "foo", channel "Image", data is ndarray of float, aux is wavelength data of same# dimensions (also float), d is a dictionary of FITS header keys and valuesargs = ["foo", "Image", grc.Blob(data.tobytes()), data.shape, 'float', d, grc.Blob(aux.tobytes()), 'float', {}]sh.call_global_plugin_method('Pypelt', 'load_buffer', args, {})
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#842 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACC6HIZ3RHQAOYEN7OTK65TRX7RXPANCNFSM4NKEOG2A>
.
--
------------------------------------------------
Joseph F. Hennawi
Associate Professor
Department of Physics
Broida Hall, UC Santa Barbara
Santa Barbara, CA 93106-9530
Phone: 805-893-3503
Mobile: 805-450-8697
E-mail: joe@ <[email protected]>physics.ucsb.edu
http://web.physics.ucsb.edu/~joe/
enigma.physics.ucsb.edu
------------------------------------------------
|
Dear @ejeschke, I finally had a chance to implement this. It works beautifully -- many thanks for your help. The one question I have is whether it is possible for the plugin to live somewhere else besides $HOME/.ginga/plugins/Pypelt.py, and if we can somehow tell ginga to look elsewhere on launch. The issue is that PypeIt is pip installable etc. and we don't think it is appropriate (may also not be possible) to automatically install a file in someone's home directory. We would prefer the plugin file to reside in the PypeIt code directory, and tell ginga to look in a different place via launch. I understand this may not be possible, but I thought I would just ask. Thanks! |
@jhennawi, the plugin module simply needs to be in the import path. Are you launching the reference viewer from your application, or is it expected to be launched by the user independently? If you are launching it, simply set the If you'd prefer the user to be able to launch the reference viewer and find your custom plugin, I'd recommend using the custom plugin template. The instructions specify how to make a standalone install package, but you could adapt them to doing the install as part of the PyPelt install without having a separate package, etc. |
Hi @ejeschke, We usually have the user launch ginga themselves, but if a script that needs ginga is run and ginga is not yet launched, then we launch if for them. I'll take a look at the custom plugin template, and get back to you. Many thanks for your help with this. |
Dear Ginga Developers,
We have been developing the PypeIt spectroscopic data reduction package (see https://arxiv.org/abs/2005.06505) and we use ginga as our data visualization viewer. We are anticipating widespread adoption by spectroscopists. Four viewing spectra, it would be very valuable if we could display the wavelengths as a second set of values as we move the cursor around the image, analogous to the alpha and delta WCS coordinates. Currently in order to get this to work we have to use an unpleasant hack, i.e in RC mode:
ch.load_np(chname, img, 'fits', header, wcs_image='wavelengths.fits')
Which then allows us to display wavelengths instead of the WCS. Besides being clunky, i.e. we have to write out the wavlengths.fits file, this also had the unwanted side-effect of then breaking the WCSMatch feature, when we want to display multiple registered images (i.e. raw data and sky-subtracted spectra) next to each other.
We would greatly appreciate an option like to simply pass in an image
ch.load_np(chname, img, 'fits', header, waveimg = waveimg)
That would allow us to display the wavelength image directly with "Wavelength" being currently displayed as alpha and delta are.
Would it be possible to add such functionality? We realize this could probably be accommodated with a plugin, but we have thus far not been able to venture that deeply into the ginga source to figure out how to do so.
Many Thanks,
Joe Hennawi on behalf of the PypeIt team
The text was updated successfully, but these errors were encountered: