Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

New data → color pipeline#28658

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
tacaswell merged 18 commits intomatplotlib:mainfromtrygvrad:Colorizer-class
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from1 commit
Commits
Show all changes
18 commits
Select commitHold shift + click to select a range
1e52ba8
Colorizer class
trygvradAug 2, 2024
9e5620d
Creation of colorizer.py
trygvradAug 6, 2024
b3f5260
updated ColorizingArtist.__init__
trygvradAug 7, 2024
edbe127
simplify to_rgba() by extracting the part relating to RGBA data
trygvradAug 9, 2024
fc9973e
updated class hierarchy for Colorizer
trygvradAug 13, 2024
596daec
colorizer keyword on plotting functions with typing
trygvradAug 13, 2024
21a5a64
changes to keyword parameter ordering
trygvradAug 16, 2024
5755d1b
adjustments based on code review
trygvradAug 21, 2024
a6fe9e8
MNT: adjust inheritance so isinstance(..., cm.ScalarMappable) works
tacaswellAug 21, 2024
dbe8cc3
updated docs with colorizer changes
trygvradAug 21, 2024
9ac2739
updates to colorizer pipeline based on feedback from @QuLogic
trygvradAug 27, 2024
7dd31ad
DOC: fix unrelated xref issues
tacaswellAug 29, 2024
1ecfe95
DOC: add multivariate colormaps to the docs
tacaswellAug 29, 2024
c685f36
DOC: fix colorizer related xrefs
tacaswellAug 29, 2024
676a31f
DOC: auto-generate ScalarMappable in conf.py
tacaswellAug 29, 2024
c3cad60
Corrections based on feedback from @QuLogic
trygvradSep 6, 2024
269ace9
MNT: Touch up rebase to use 'register' for docstring interpolations
ksundenOct 11, 2024
336a9ba
fix missing refererence linenos
ksundenOct 23, 2024
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
NextNext commit
Colorizer class
The Colorizer class, ColorizerShim, and ColorableArtist which replaces the old ScalarMappable.
  • Loading branch information
@trygvrad@ksunden
trygvrad authored andksunden committedOct 18, 2024
commit1e52ba8e89c357f9e3442a63b74534ee9e2ecd34
75 changes: 75 additions & 0 deletionslib/matplotlib/artist.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@

import matplotlib as mpl
from . import _api, cbook
from .cm import Colorizer
from .path import Path
from .transforms import (BboxBase, Bbox, IdentityTransform, Transform, TransformedBbox,
TransformedPatchPath, TransformedPath)
Expand DownExpand Up@@ -1392,6 +1393,80 @@ def set_mouseover(self, mouseover):
mouseover = property(get_mouseover, set_mouseover) # backcompat.


class ColorizingArtist(Artist):
def __init__(self, norm=None, cmap=None):
"""
Parameters
----------
norm : `colors.Normalize` (or subclass thereof) or str or `cm.Colorizer` or None
The normalizing object which scales data, typically into the
interval ``[0, 1]``.
If a `str`, a `colors.Normalize` subclass is dynamically generated based
on the scale with the corresponding name.
If `cm.Colorizer`, the norm an colormap on the `cm.Colorizer` will be used
Copy link
Member

@timhoffmtimhoffmAug 6, 2024
edited
Loading

Choose a reason for hiding this comment

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

I would not overload the signature. A typical strategy would be to have the canoncial signature and create a factory classmethod for the other signature:

def __init__(colorizer):    self.colorizer = colorizer@classmethoddef from_norm_and_cmap(cls, norm, cmap):    return cls(Colorizer(norm, cmap)

AFAICT, the situation here is a bit different, because users do not instantiateColorizingArtist directly, and hence, they won't use the factory method. We could do one of the two options:

  • only implementdef __init__(colorizer). If subclasses need to initialize from norm + cmap, they'd simply do
    ColorizingArtist.__init__(Colorizer(norm, cmap))
  • create an alternative initializer
    def _init_from_norm_and_cmap(norm, cmap):    self.colorizer = Colorizer(norm, cmap)

Given that we'll only have a handfull of subclasses and the solution is quite simple, I'm leaning towards the first option.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

The first alternative makes sense to me.

However, we still need the logic where the user can pass aColorizer object as part of the input to the plotting function.

I implemented this with the following function:

def_get_colorizer(cmap,norm):"""    Passes or creates a Colorizer object.    Allows users to pass a Colorizer as the norm keyword    where a artist.ColorizingArtist is used as the artist.    If a Colorizer object is not passed, a Colorizer is created.    """ifisinstance(norm,Colorizer):ifcmap:raiseValueError("Providing a `cm.Colorizer` as the norm while ""at the same time providing a `cmap` is not supported.")returnnormreturnColorizer(cmap,norm)

Such that the initiation of aColorizingArtist in e.g.Collection is:

artist.ColorizingArtist.__init__(self,colorizer._get_colorizer(cmap,norm))

Copy link
Member

Choose a reason for hiding this comment

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

Do we really want to pass aColorizer via thenorm parameter in public API? This feels very hacky. I'm inclined to add acolorizer parameter instead, even if that means we have mutually exclusive parameters. (Note that even with the hack, we'd have to live with partially mutually exclusive parameters, because you cannot passnorm=mycolorizer, cmap='jet').

story645 reacted with thumbs up emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I have no strong opinions, and am inclined to agree with you@timhoffm.
Since this is a change to the call signature of the plotting functions, we should bring it up at a weekly meeting.@story645 could you add it on the agenda for the next meeting?

(My aversion to new keyword arguments is a result of a discussion at a weekly meeting a long time ago about multivariate color mapping, and how we do not wantthat to increase the number of keyword arguments, but this is a different case entirely.)

Copy link
Member

@story645story645Aug 21, 2024
edited
Loading

Choose a reason for hiding this comment

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

Sorry I didn't see this til now 😦 but also anyone can add stuff to the agenda (sorry me and Kyle didn't make that clear)! And also this got resolved as yes add new kwarg, I think.

If *None*, *norm* defaults to a *colors.Normalize* object which
initializes its scaling based on the first data processed.
cmap : str or `~matplotlib.colors.Colormap`
The colormap used to map normalized data values to RGBA colors.
"""

Artist.__init__(self)

self._A = None
if isinstance(norm, Colorizer):
self.colorizer = norm
if cmap:
raise ValueError("Providing a `cm.Colorizer` as the norm while "
"at the same time as a `cmap` is not supported..")
else:
self.colorizer = Colorizer(cmap, norm)

self._id_colorizer = self.colorizer.callbacks.connect('changed', self.changed)
self.callbacks = cbook.CallbackRegistry(signals=["changed"])

def set_array(self, A):
"""
Set the value array from array-like *A*.

Parameters
----------
A : array-like or None
The values that are mapped to colors.

The base class `.VectorMappable` does not make any assumptions on
the dimensionality and shape of the value array *A*.
"""
if A is None:
self._A = None
return

A = cbook.safe_masked_invalid(A, copy=True)
if not np.can_cast(A.dtype, float, "same_kind"):
raise TypeError(f"Image data of dtype {A.dtype} cannot be "
"converted to float")

self._A = A
if not self.norm.scaled():
Copy link
Member

Choose a reason for hiding this comment

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

are we sure that this will always have a.norm?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I'm thinking it will always have a norm, as I think that simplifies the implementation.
However, that norm might beNoNorm.

The implementation here mirrors that ofScalarMappable, which always has aNormalize andColormap object, and I think this is sensible. For images that are already RGB(A) a norm (and cmap) is technically unnecessary, but I believe the overhead of creating them is negligible.

self.colorizer.autoscale_None(A)

def get_array(self):
"""
Return the array of values, that are mapped to colors.

The base class `.VectorMappable` does not make any assumptions on
the dimensionality and shape of the array.
"""
return self._A

def changed(self):
"""
Call this whenever the mappable is changed to notify all the
callbackSM listeners to the 'changed' signal.
"""
self.callbacks.process('changed')
self.stale = True


def _get_tightbbox_for_layout_only(obj, *args, **kwargs):
"""
Matplotlib's `.Axes.get_tightbbox` and `.Axis.get_tightbbox` support a
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp