Skip to content

Commit

Permalink
Merge pull request #1647 from pllim/all-the-single-pixels
Browse files Browse the repository at this point in the history
Imviz: Alt-click to create single-pixel region
  • Loading branch information
pllim authored Sep 29, 2022
2 parents e73766a + 084e75f commit 9f3bd13
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 56 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Imviz

- Changing link options now updates immediately without needing to press "Link" button. [#1598]

- New tool to create a single-pixel spatial region on the image. [#1647]

Mosviz
^^^^^^

Expand Down
14 changes: 9 additions & 5 deletions docs/cubeviz/displaycubes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,15 @@ Spectrum At Spaxel
==================

This tool allows the user to create a one spaxel subset in an image viewer. This subset will then be
visualized in the spectrum viewer by showing the spectrum at that spaxel. Users can hold down the
alt key (Alt key on Windows, Option key on Mac) while clicking on a spaxel to create a new subset at
that point. Users can then compare spectra at different spaxels using the spectrum viewer. Users can
also utilize the different subset modes that are explained in the
:ref:`Spatial Regions <imviz_defining_spatial_regions>` section.
visualized in the spectrum viewer by showing the spectrum at that spaxel.
Activate this tool and then left-click to create the new region.
Click again to move the region to a new location under the cursor. Holding down the
alt key (Alt key on Windows, Option key on Mac) while clicking on a spaxel creates a new subset at
that point instead of moving the previously created region.
You can then compare spectra at different spaxels using the spectrum viewer.
You can also use the subset modes that are explained in the
:ref:`Spatial Regions <imviz_defining_spatial_regions>`
section above in the same way you would with the other subset selection tools.

.. _cubeviz-display-settings:

Expand Down
31 changes: 31 additions & 0 deletions docs/imviz/displayimages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,37 @@ You can :ref:`import <imviz-import-regions-api>` and :ref:`export <imviz_export_
There are options available in the :guilabel:`Layer` tab under the |icon-settings-sliders| icon
to make subsets visible or invisible, to change their color, and to change their opacity.

.. _imviz_defining_spatial_regions_single_pixel:

Single-Pixel Selection
----------------------

This tool allows the user to create a single-pixel spatial region
in an image viewer. Activate this tool and then left-click to create
the new region. Click again to move the region to a new location under
the cursor. Holding down the alt key (Alt key on Windows, Option key
on Mac) while clicking pixels creates a new region at each point instead
of moving the previously created region. You can also use the subset
modes that are explained in the
:ref:`Spatial Regions <imviz_defining_spatial_regions>`
section above in the same way you would with the other subset selection
tools.

When you have multiple images loaded and linked by WCS
(see :ref:`imviz-link-control`), the region defined is with respect to
the reference image, which might not be the image you are viewing.

.. warning::

Region created might not accurately represent area you think you are
clicking under the mouse if you click on an image that is zoomed out
too much. It is recommended that you zoom in sufficiently to see the
individual pixels to use this feature.

.. note::

Creating too many single-pixel regions may affect performance.

Blinking
========

Expand Down
46 changes: 2 additions & 44 deletions jdaviz/configs/cubeviz/plugins/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

from glue.config import viewer_tool
from glue.viewers.common.tool import CheckableTool
from glue.core.roi import RectangularROI
from glue.core.edit_subset_mode import NewMode

from jdaviz.configs.imviz.plugins.tools import _MatchedZoomMixin
from jdaviz.core.events import SliceToolStateMessage
from jdaviz.core.tools import PanZoom
from jdaviz.core.tools import PanZoom, SinglePixelRegion

__all__ = []

Expand Down Expand Up @@ -59,49 +57,9 @@ def on_mouse_event(self, data):


@viewer_tool
class SpectrumPerSpaxel(CheckableTool):
class SpectrumPerSpaxel(SinglePixelRegion):

icon = os.path.join(ICON_DIR, 'pixelspectra.svg')
tool_id = 'jdaviz:spectrumperspaxel'
action_text = 'See spectrum at a single spaxel'
tool_tip = 'Click on the viewer and see the spectrum at that spaxel in the spectrum viewer'

def __init__(self, viewer, **kwargs):
super().__init__(viewer, **kwargs)

def activate(self):
self.viewer.add_event_callback(self.on_mouse_event,
events=['click'])

def deactivate(self):
self.viewer.remove_event_callback(self.on_mouse_event)

def on_mouse_event(self, data):
event = data['event']

if event == 'click' and data['altKey'] is True:
self.alt_subset_at_spaxel(data)
elif event == 'click':
self.subset_at_spaxel(data)

def alt_subset_at_spaxel(self, data):
# Add a new subset if the alt key is held down
previous_subset_mode = self.viewer.session.edit_subset_mode.mode

self.viewer.session.edit_subset_mode.mode = NewMode
self.subset_at_spaxel(data)
self.viewer.session.edit_subset_mode.mode = previous_subset_mode

def subset_at_spaxel(self, data):
x = data['domain']['x']
y = data['domain']['y']
xmin, xmax, ymin, ymax = self._calc_coords(x, y)
self.viewer.apply_roi(RectangularROI(xmin, xmax, ymin, ymax))

def _calc_coords(self, x, y):
xmin = x - 0.5
xmax = x + 0.5
ymin = y - 0.5
ymax = y + 0.5

return xmin, xmax, ymin, ymax
5 changes: 3 additions & 2 deletions jdaviz/configs/imviz/plugins/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ class ImvizImageView(BqplotImageView, AstrowidgetsImageViewerMixin, JdavizViewer
tools = ['jdaviz:homezoom', 'jdaviz:boxzoommatch', 'jdaviz:boxzoom',
'jdaviz:panzoommatch', 'jdaviz:imagepanzoom',
'jdaviz:contrastbias', 'jdaviz:blinkonce',
'bqplot:rectangle', 'bqplot:circle', 'bqplot:ellipse']
'bqplot:rectangle', 'bqplot:circle', 'bqplot:ellipse', 'jdaviz:singlepixelregion']

# categories: zoom resets, zoom, pan, subset, select tools, shortcuts
tools_nested = [
['jdaviz:homezoom', 'jdaviz:prevzoom'],
['jdaviz:boxzoommatch', 'jdaviz:boxzoom'],
['jdaviz:panzoommatch', 'jdaviz:imagepanzoom'],
['bqplot:circle', 'bqplot:rectangle', 'bqplot:ellipse'],
['bqplot:circle', 'bqplot:rectangle', 'bqplot:ellipse',
'jdaviz:singlepixelregion'],
['jdaviz:blinkonce', 'jdaviz:contrastbias'],
['jdaviz:sidebar_plot', 'jdaviz:sidebar_export', 'jdaviz:sidebar_compass']
]
Expand Down
42 changes: 39 additions & 3 deletions jdaviz/configs/imviz/tests/test_tools.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
from regions import RectanglePixelRegion

from jdaviz.configs.imviz.tests.utils import BaseImviz_WCS_WCS

Expand Down Expand Up @@ -60,18 +61,53 @@ def test_panzoom_tools(self):
t_linkedpan.deactivate()


# We use a new test class to avoid a dirty state from previous test.
class TestSinglePixelRegion(BaseImviz_WCS_WCS):
def test_singlepixelregion(self):
self.imviz.link_data(link_type='wcs')

t = self.imviz.default_viewer.toolbar_nested.tools['jdaviz:singlepixelregion']
t.activate()

# Create a region while viewing reference data.
t.on_mouse_event({'event': 'click', 'altKey': False, 'domain': {'x': 1, 'y': 2}})
regions = self.imviz.get_interactive_regions()
assert len(regions) == 1
reg = regions['Subset 1']
assert (isinstance(reg, RectanglePixelRegion) and reg.center.x == 1 and reg.center.y == 2
and reg.width == 1 and reg.height == 1)

# Clicking again will move the region, not creating a new one.
t.on_mouse_event({'event': 'click', 'altKey': False, 'domain': {'x': 2, 'y': 3}})
regions = self.imviz.get_interactive_regions()
assert len(regions) == 1
reg = regions['Subset 1']
assert (isinstance(reg, RectanglePixelRegion) and reg.center.x == 2 and reg.center.y == 3
and reg.width == 1 and reg.height == 1)

# Create a new region while viewing dithered data.
# Region will still be w.r.t. reference data, that is, x and y from domain stay the same.
self.imviz.default_viewer.blink_once()
t.on_mouse_event({'event': 'click', 'altKey': True, 'domain': {'x': 3, 'y': 4}})
regions = self.imviz.get_interactive_regions()
assert len(regions) == 2
reg = regions['Subset 2']
assert (isinstance(reg, RectanglePixelRegion) and reg.center.x == 3 and reg.center.y == 4
and reg.width == 1 and reg.height == 1)

t.deactivate()


def test_blink(imviz_helper):
viewer = imviz_helper.default_viewer

for i in range(3):
imviz_helper.load_data(np.zeros((2, 2)) + i, data_label=f'image_{i}')

# Last loaded is shown first. So, blinking will take you back to the first one.
# Blink forward. The event will also initialize viewer.label_mouseover .
viewer.on_mouse_or_key_event({'event': 'keydown', 'key': 'b', 'domain': {'x': 0, 'y': 0}})
assert viewer.label_mouseover.value == '+0.00000e+00 '

# Blink forward again and update coordinates info panel.
# Blink forward and update coordinates info panel.
viewer.blink_once()
viewer.on_mouse_or_key_event({'event': 'mousemove', 'domain': {'x': 0, 'y': 0}})
assert viewer.label_mouseover.value == '+1.00000e+00 '
Expand Down
45 changes: 43 additions & 2 deletions jdaviz/core/tools.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np
import os
from echo import delay_callback

import numpy as np
from echo import delay_callback
from glue.config import viewer_tool
from glue.core import HubListener
from glue.viewers.common.tool import Tool
Expand Down Expand Up @@ -249,3 +249,44 @@ class SidebarShortcutCompass(_BaseSidebarShortcut):
tool_id = 'jdaviz:sidebar_compass'
action_text = 'Compass'
tool_tip = 'Open compass plugin in sidebar'


@viewer_tool
class SinglePixelRegion(CheckableTool):

icon = os.path.join(ICON_DIR, 'select_single_pixel.svg')
tool_id = 'jdaviz:singlepixelregion'
action_text = 'Create single-pixel spatial region'
tool_tip = 'Define a single-pixel spatial region of interest'

def activate(self):
self.viewer.add_event_callback(self.on_mouse_event, events=['click'])

def deactivate(self):
self.viewer.remove_event_callback(self.on_mouse_event)

def on_mouse_event(self, data):
# Extract data coordinates - these are pixels in the reference image.
# NOTE: We always use the reference image pixel coordinates because
# Subset is defined w.r.t. reference image.
x = data['domain']['x']
y = data['domain']['y']

if data['altKey'] is True:
reg = self.get_subset(x, y, as_roi=False)
self.viewer.jdaviz_helper.load_regions(reg)
else:
roi = self.get_subset(x, y, as_roi=True)
self.viewer.apply_roi(roi)

def get_subset(self, x, y, as_roi=False):
from regions import RectanglePixelRegion, PixCoord
x, y = np.rint([x, y]) # Center on nearest pixel
reg = RectanglePixelRegion(center=PixCoord(x=x, y=y), width=1, height=1)

if as_roi:
from jdaviz.core.region_translators import regions2roi
roi = regions2roi(reg)
return roi

return reg
1 change: 1 addition & 0 deletions jdaviz/data/icons/select_single_pixel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9f3bd13

Please sign in to comment.