Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6087d69
Add ColormapSet for color blending
izkgao Apr 30, 2025
d6961b2
Update Image.make_active to support CARTA 5.0.0+ API changes
izkgao Apr 30, 2025
9fa89de
Add ColorBlending class with layer management and color blending func…
izkgao May 5, 2025
85b3528
Add color blending documentation and update image handling examples
izkgao May 5, 2025
06445bc
Add ColorBlending.from_files method to create blended images directly…
izkgao Sep 10, 2025
266f1c1
Add tests for ColorBlending and Layer classes
izkgao Sep 10, 2025
606c9f3
Improve mock consistency
izkgao Sep 11, 2025
8e560b9
Merge branch 'dev' into color_blending
izkgao Apr 10, 2026
34bf41b
Fix duplicate imports and docstring formatting in colorblending and i…
izkgao Apr 10, 2026
5d4fca4
Add colorblending module to API documentation
izkgao Apr 10, 2026
032949d
Refactor layers property to use Layer.from_list method
izkgao Apr 10, 2026
2e0abce
Fix indentation in Layer class docstring
izkgao Apr 10, 2026
5c52466
Rename reorder_layers to set_layer_sequence and add support for layer…
izkgao Apr 14, 2026
06fff60
Simplify layer_list implementation by using frames.length instead of …
izkgao Apr 14, 2026
b7af8dc
Add explicit validation to prevent deletion of base layer in ColorBle…
izkgao Apr 14, 2026
fbcf72f
Add validation to prevent alpha list length mismatch in set_alpha method
izkgao Apr 14, 2026
6596ab3
Refactor ColorBlending to use store_id instead of image_id and add fr…
izkgao Apr 14, 2026
9a20cfa
Add color_blending_list method to Session class for retrieving open c…
izkgao Apr 14, 2026
d9bcab4
Update quickstart documentation to clarify ColorBlending layer termin…
izkgao Apr 14, 2026
a34e646
Drop support for carta version < 5.0 for make_active
izkgao Apr 14, 2026
4ce4429
Update documentation references to use new wcs_overlay module paths f…
izkgao Apr 14, 2026
25e0baa
Add validation test for invalid colormap name in Layer.set_colormap m…
izkgao Apr 14, 2026
cb22ed5
Fix indentation in Layer.from_layer_ids docstring return section
izkgao Apr 14, 2026
91e952d
Add validation to prevent exceeding maximum initial layer count in Co…
izkgao Apr 14, 2026
3f1c4fe
Rename set_vectoroverlay_visible method to set_vector_overlay_visible…
izkgao Apr 14, 2026
d337759
Rename ColormapSet.Rainbow to RAINBOW for consistent uppercase enum n…
izkgao Apr 14, 2026
345c17d
Add module docstring to colorblending.py describing color blending fu…
izkgao Apr 14, 2026
2bf9d1f
Replace explicit type annotations with placeholder format strings in …
izkgao Apr 14, 2026
e16c9f4
Change periods to colons in ColorBlending section introductory senten…
izkgao Apr 14, 2026
2812ffa
Update test_make_active to use setActiveImageByFileId action instead …
izkgao Apr 14, 2026
509d4e2
Fix ColorBlending initialization to rebuild layers from requested ima…
izkgao Apr 15, 2026
af48cf3
Add validation to reject duplicate layer indices in ColorBlending.set…
izkgao Apr 15, 2026
2f7d76b
Remove automatic inversion reset when applying colormap sets in Color…
izkgao Apr 15, 2026
56e019d
Fix imageview_id property to search imageList by store_id instead of …
izkgao Apr 15, 2026
6e02ba2
Convert layer_indices to list before comparing with current_layer_ind…
izkgao Apr 15, 2026
9ea1a66
Style check
izkgao Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
556 changes: 556 additions & 0 deletions carta/colorblending.py

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions carta/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ class ComplexComponent(StrEnum):
Colormap.__doc__ = """All available colormaps."""


class ColormapSet(StrEnum):
"""Colormap sets for color blending."""
RGB = "RGB"
CMY = "CMY"
RAINBOW = "Rainbow"


class ImageType(IntEnum):
"""Image view item types, corresponding to the frontend ImageType enum."""
FRAME = 0
COLOR_BLENDING = 1
PV_PREVIEW = 2


Scaling = IntEnum('Scaling', ('LINEAR', 'LOG', 'SQRT', 'SQUARE', 'POWER', 'GAMMA'), start=0)
Scaling.__doc__ = """Colormap scaling types."""

Expand Down
5 changes: 2 additions & 3 deletions carta/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from .units import AngularSize, WorldCoordinate
from .validation import validate, Number, Constant, Boolean, Evaluate, Attr, Attrs, OneOf, Size, Coordinate, NoneOr, IterableOf, Point
from .metadata import parse_header

from .raster import Raster
from .contours import Contours
from .vector_overlay import VectorOverlay
Expand Down Expand Up @@ -256,7 +255,7 @@ def polarizations(self):

def make_active(self):
"""Make this the active image."""
self.session.call_action("setActiveFrameById", self.image_id)
self.session.call_action("setActiveImageByFileId", self.image_id)

def make_spatial_reference(self):
"""Make this image the spatial reference."""
Expand Down Expand Up @@ -362,7 +361,7 @@ def valid_wcs(self):
def set_center(self, x, y):
"""Set the center position, in image or world coordinates.

World coordinates are interpreted according to the session's globally set coordinate system and any custom number formats. These can be changed using :obj:`carta.session.set_coordinate_system` and :obj:`set_custom_number_format`.
World coordinates are interpreted according to the session's globally set coordinate system and any custom number formats. These can be changed using :obj:`carta.wcs_overlay.Global.set_coordinate_system` and :obj:`carta.wcs_overlay.Numbers.set_format`.

Coordinates must either both be image coordinates or match the current number formats. Numbers are interpreted as image coordinates, and numeric strings with no units are interpreted as degrees.

Expand Down
14 changes: 14 additions & 0 deletions carta/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import posixpath

from .image import Image
from .colorblending import ColorBlending
from .constants import PanelMode, GridMode, ComplexComponent, Polarization
from .backend import Backend
from .protocol import Protocol
Expand Down Expand Up @@ -520,6 +521,19 @@ def image_list(self):
"""
return Image.from_list(self, self.get_value("frameNames"))

def color_blending_list(self):
"""Return the list of currently open color blending objects.

Returns
-------
list of :obj:`carta.colorblending.ColorBlending` objects
The list of color blending objects open in this session.
"""
path = "imageViewConfigStore.colorBlendingImages"
length = self.get_value(f"{path}.length")
store_ids = [self.get_value(f"{path}[{idx}].id") for idx in range(length)]
return [ColorBlending(self, store_id) for store_id in store_ids]

def active_frame(self):
"""Return the currently active image.

Expand Down
8 changes: 8 additions & 0 deletions docs/source/carta.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ carta.browser module
:undoc-members:
:show-inheritance:

carta.colorblending module
--------------------------

.. automodule:: carta.colorblending
:members:
:undoc-members:
:show-inheritance:

carta.constants module
----------------------

Expand Down
119 changes: 115 additions & 4 deletions docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ Helper methods on the session object open images in the frontend and return imag
.. code-block:: python

# Open or append images
img1 = session.open_image("data/hdf5/first_file.hdf5")
img2 = session.open_image("data/fits/second_file.fits", append=True)
img0 = session.open_image("data/hdf5/first_file.hdf5")
img1 = session.open_image("data/fits/second_file.fits", append=True)
img2 = session.open_image("data/fits/third_file.fits", append=True)

Changing image properties
-------------------------
Expand All @@ -192,7 +193,7 @@ Properties specific to individual images can be accessed through image objects:
# pan and zoom
y, x = img.shape[-2:]
img.set_center(x/2, y/2)
img.set_zoom(4)
img.set_zoom_level(4)

# change colormap
img.raster.set_colormap(Colormap.VIRIDIS)
Expand Down Expand Up @@ -225,7 +226,117 @@ Properties which affect the whole session can be set through the session object:
session.wcs.global_.set_color(PaletteColor.RED)
session.wcs.ticks.set_color(PaletteColor.VIOLET)
session.wcs.title.show()


Making color blended image
--------------------------

Create a color blending object from a list of files:

.. code-block:: python

from carta.colorblending import ColorBlending
from carta.constants import Colormap, ColormapSet

# Make a color blending object
# Warning: setting `append=False` will close any existing images
# Note: The base layer (index = 0) cannot be deleted or moved.
files = [
"data/hdf5/first_file.hdf5",
"data/fits/second_file.fits",
"data/fits/third_file.fits",
]
cb = ColorBlending.from_files(session, files, append=False)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class method should not be exposed to the user directly. The user should access it through a wrapper on the session object, e.g. something like session.open_as_color_blending([path1, path2, path3]).

This is analogous to the open_images function on the session.


Create a color blending object from a list of images:

.. code-block:: python

from carta.colorblending import ColorBlending
from carta.constants import Colormap, ColormapSet

# Make a color blending object
# Warning: This will break the current spatial matching and
# use the first image as the spatial reference
# Note: The base layer (index = 0) cannot be deleted or moved.
cb = ColorBlending.from_images(session, [img0, img1, img2])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, this should not be exposed. I'm not sure if it would be best to add the wrapper method to the session object (e.g. maybe session.color_blending_from([img0, img1, img2])) or to the image object (to be called on the image that's the spatial reference, e.g. img0.color_blending_with([img1, img2])).


To work with color blending images that are already open in a session, use
the session helper:

.. code-block:: python

# Get all open color blending objects in this session
color_blendings = session.color_blending_list()
cb = color_blendings[0]

# Or get a color blending object by its image view index
cb = ColorBlending.from_imageview_id(session, 3)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also not be exposed. Is each colour blending also an image that appears in the image list? If yes, then the wrapper for this should be on the image object, and be decorated as an attribute, since the only parameter is the image's ID. Then the user could access it with img.color_blending.

Broader question: if each color blending is also an image and can be accessed through an image object in the wrapper, does that break anything (because it's not a normal image)? E.g. what will happen if the user accesses a blending as an image object and tries to get the number of channels?

If this does break image functionality, I think that the correct thing to do is to specialise Image into subclasses (ImageBase, Image, and ColorBlending), but I don't want to overthink this if it's not actually an issue.


.. note::
The ``ColorBlending`` constructor takes the internal color blending store ID,
not the image view index. Use ``ColorBlending.from_files``,
``ColorBlending.from_images``, ``ColorBlending.from_imageview_id`` or
``session.color_blending_list`` in scripts.

Manipulate properties of the color blending object and the underlying images:

.. code-block:: python

# Get layer objects
layers = cb.layer_list()

# Set colormap for individual layers
layers[0].set_colormap(Colormap.REDS)
layers[1].set_colormap(Colormap.GREENS)
layers[2].set_colormap(Colormap.BLUES)
Comment on lines +285 to +291
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest unpacking in the assignment, like this:

# Get layer objects
red, green, blue = cb.layer_list()

# Set colormap for individual layers
red.set_colormap(Colormap.REDS)
green.set_colormap(Colormap.GREENS)
blue.set_colormap(Colormap.BLUES)


# Or apply an existing colormap set
cb.set_colormap_set(ColormapSet.RGB)

# Print the current alpha values of all layers
print(cb.alpha)

# Set alpha for individual layers
layers[0].set_alpha(0.7)
layers[1].set_alpha(0.8)
layers[2].set_alpha(0.9)

# Or set alpha for all layers at once
cb.set_alpha([0.7, 0.8, 0.9])

# Set which layers to keep, and in what order
# The first layer index must be the base layer (index = 0)
# Since the base layer cannot be moved,
# the layers will be reordered as [img0, img2, img1]
cb.set_layer_sequence([0, 2, 1])

# Remove the last layer (index = 2)
cb.delete_layer(2)

# Add a new layer
# The layer to be added cannot be one of the current layers
cb.add_layer(img1)

# Set center
cb.set_center(100, 100)

# Set zoom level
cb.set_zoom_level(2)

# Set the color blending object as the active frame
cb.make_active()

# Set contour visibility
# This will hide the contours (if any)
cb.set_contour_visible(False)

# Close the color blending object
cb.close()

.. note::
If you need to change the layer order involving the base layer (index = 0),
close the current color blending object and create a new one.

Saving or displaying an image
-----------------------------

Expand Down
Loading
Loading