Skip to content

Add ColorBlending module for multi-image blending support#161

Open
izkgao wants to merge 36 commits intodevfrom
zhenkai/color_blending
Open

Add ColorBlending module for multi-image blending support#161
izkgao wants to merge 36 commits intodevfrom
zhenkai/color_blending

Conversation

@izkgao
Copy link
Copy Markdown

@izkgao izkgao commented Apr 15, 2026

Description
Closes #160.
This PR implements the colorblending module that adds support for creating and manipulating color blended images in CARTA.

Checklist

  • Add code examples in documentation
  • Add basic tests

izkgao added 30 commits April 30, 2025 16:38
…or coordinate system and number format methods
…ces for consistency with documentation style
@izkgao izkgao requested review from confluence and kswang1029 April 15, 2026 05:27
@izkgao izkgao added awaiting code review For pull requests that require code review awaiting testing For pull requests that require testing labels Apr 15, 2026
@izkgao izkgao marked this pull request as ready for review April 15, 2026 05:33
Copy link
Copy Markdown
Collaborator

@confluence confluence left a comment

Choose a reason for hiding this comment

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

I would like the factory classmethods not to be exposed to the user -- they should have wrappers (in the same way that the Image factory classmethods are wrapped by methods on the session object). I also have a question about what happens if the user interacts with an image object that corresponds to a color blending (will anything break?).

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

# 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])).

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.

Comment on lines +285 to +291
# 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)
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)

Comment thread carta/colorblending.py
)

@classmethod
def from_imageview_id(cls, session, imageview_id):
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 is the same as the image object ID, right? For consistency, we should call this image_id.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

imageview_id refers to the index in the image list (see the red box),
image
while image_id in carta-python refers to fileId in carta-frontend, which is not exposed to users and does not change after moving from index 0 to index 2 in the image list.

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.

Right, I've remembered how this works now. :) As discussed on slack, I think that we should:

  • rename the current image_id in image objects to file_id
  • use image_id to refer to the image list indices (exposing both in the repr)
  • use blending_id and image_id in the color blending objects
  • unify Image and ColorBlending into the same object hierarchy using a shared base class
  • retrieve both normal files and color blendings in the image list function in the session
  • let functions that refer to an image by ID should accept multiple ID types (as appropriate)
  • not cache the image ID in the objects (because it can change in the frontend), and fetch it dynamically via the stable internal ID instead

Comment thread carta/colorblending.py
image_type = session.get_value(f"{path}.type")
if image_type != ImageType.COLOR_BLENDING:
raise ValueError(
"imageview_id does not refer to a color blending image."
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 message should actually include the value of the ID.

Comment thread carta/colorblending.py
return self.get_value("filename")

@property
def imageview_id(self):
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 be image_id.

@izkgao izkgao added awaiting code changes For pull requests that require code changes and removed awaiting code review For pull requests that require code review awaiting testing For pull requests that require testing labels Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting code changes For pull requests that require code changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support color blending

3 participants