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

ENH color brightening#9985

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

Closed

Conversation

jklymak
Copy link
Member

@jklymakjklymak commentedDec 12, 2017
edited
Loading

PR Summary

Redo of#8895. Simple utility to brighten a color. Changed name from shade to brighten because positive was brightening. "Lightening" is also possible, since we are changing the L channel in HSL, but is clunky linguistically.

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)

@jklymakjklymak added this to thev2.2 milestoneDec 12, 2017
@jklymakjklymakforce-pushed theenh-color-shading branch 2 times, most recently from8f48367 to3342ff1CompareDecember 12, 2017 18:48
@jklymakjklymak mentioned this pull requestDec 12, 2017
7 tasks

h, l, s = colorsys.rgb_to_hls(*rgb)

l *= 1. + float(frac)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not clear to me that this is a more useful definition thanl += frac.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I'd argue that most people have no idea what the L value of a color is, but they know they want it 50% lighter.

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW and based solely on my limited own experience, I would be inclined to agree with@jklymak about the relative vs. absolute increment scheme.

Copy link
Contributor

@eric-wiesereric-wieserDec 12, 2017
edited
Loading

Choose a reason for hiding this comment

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

The question here is not whether users understand L, but whether they expectbrighten_color(brighten_color(x, 0,2), 0.2) == brighten_color(x, 0.4) (as opposed to0.44, as you implement it).

Also thefloat is pointless here, as you convert it above

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Given that the formula is explicitly in the docs, I hope they realize they are incrementing by 20% each time.

@jklymakjklymakforce-pushed theenh-color-shading branch 2 times, most recently from0893966 to96642c5CompareDecember 12, 2017 21:53
@@ -0,0 +1,9 @@
Easy color brightening
Copy link
Member

Choose a reason for hiding this comment

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

Color Brightening

jklymak reacted with thumbs up emoji
Often we would like the ability add subtle color variation to our plots,
especially when similar element are plotted in close proximity to one another.
`matplotlib.colors.brighten_color` can be used to take an existing color and
slightly brighten or darken it, by giving a positive or negative fraction.
Copy link
Member

Choose a reason for hiding this comment

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

I think you don't need slightly since the level of brightness or darkness is relative to the fraction. Please explain what the fraction is?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Its in the docstring which is linked, and should be obvious from the example...

if not np.isfinite(frac):
raise ValueError('argument frac must be a finite float')

rgb = colorConverter.to_rgb(color)
Copy link
Member

Choose a reason for hiding this comment

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

don't think these all need to be on separate lines

assert_equal(sc, expected)


def test_color_brightening():
Copy link
Member

Choose a reason for hiding this comment

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

What about parameterization? and do these floats need to be this long?

TestBrighten(object):test_colors= ('red','red','red','red','red', [0,.5,.9],'slategrey')test_brighten= (0,0.50,1.00,-0.50,-1.00,0.20,-0.20)known_brighten_result= ((1.0,0.0,0.0), (1.0,0.5,0.5),               (1.0,1.0,1.0), (0.5,0.0,0.0), (0.0,0.0,0.0),               (0.080000000000000071,0.59111111111111092,1.0),               (0.35097730430754981,0.40156862745098038,0.45215995059441105))@pytest.mark.parametrize('color, shade, expected',zip(test_colors,test_brighten,known_brighten_result))deftest_color_brightening(color,shade,expected):sc=mcolors.brighten_color(color,shade)assert_equal(sc,expected)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I didn't write the test, so this is a disinterested comment, but: I'm still not sure what the advantage of parameterization is? I don't find the above very easy to parse...

Copy link
Member

Choose a reason for hiding this comment

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

You don't have to explicitly loop through the cases and in this case there's no needed for the secondary helper function.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

hmmm, well, there was no need for the helper fcn anyway. And

@pytest.mark.parametrize('color, shade, expected',                    zip(test_colors, test_brighten, known_brighten_result))

is not any shorter than writing

forcolor,brighten,expectedinzip(test_colors,test_brighten,known_brighten_result):

The latter has the benefit of being standard python, whereas the former requires knowing pytest decorators. I'm not adverse to change, but only if there is a compelling reason!

Copy link
Member

Choose a reason for hiding this comment

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

The two big wins from parameterization come from when you can stack more than 1 and get the outer-product of the input and because it lets each test fail independently.

jklymak reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

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

I always forget about that, but the test failing independenty is the real reason to use parameterization becuase that's what makes it a true unit test. Plus in pytest you can add a list of lables for each paramterized set and so you can identify what the inputs are testing for.

for color, brighten, expected in zip(test_colors,
test_brighten,
known_brighten_result):
_brighten_test_helper(color, brighten, expected)
Copy link
Contributor

@eric-wiesereric-wieserDec 13, 2017
edited
Loading

Choose a reason for hiding this comment

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

I think we're missing the obvious case that this should just be written:

brighten=mcolors.brighten_colorassert_equal(brighten('red',0.0), (1.0,0.0,0.0))assert_equal(brighten('red',0.5), (1.0,0.5,0.5))assert_equal(brighten('red',1.0), (1.0,1.0,1.0))...

These aren't test parameters - they're the tests themselves, and the loop and helper functions just obscure the pairing between the results and the expected values

@@ -1992,3 +1994,49 @@ def from_levels_and_colors(levels, colors, extend='neither'):

norm = BoundaryNorm(levels, ncolors=n_data_colors)
return cmap, norm


def brighten_color(color, frac):
Copy link
Member

Choose a reason for hiding this comment

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

to 🚲 🏠 the name a bit, should this beadjust_color oradjust_tint or something like that? I almost left a comment after quickly skimming the code as "do we need to add a darken color too?" (and then I read the docstring and left this comment instead).

Copy link
Contributor

Choose a reason for hiding this comment

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

Does a function in the modulecolors really need to havecolor as part of its name?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

adjust_lightness? Opens up to adjust saturation.


This function first converts the given color to RGB using
`~.colorConverter` and then to
HSL using `colorsys.rgb_to_hsl`. The lightness is modified according
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems foolish to use the non-vectorizedcolorsys functions when we already have a vectorizedrgb_to_hsv higher up in the file.

I know that HSV and HSL are different, but either we should write a vectorizedrgb_to_hsl for consistency, or just use the HSV one that's already here

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This is outside what I'm willing to do for this PR.

If we could just use HSV, that'd be great, but my understanding is that to get white in HSV you need to change both S and V, whereas in HSL you can just change L. HSL seems the better colourspace for what we are doing here.

Just for context, this is an old PR that was sitting around for 2+ years, so I'm willing to put it through, but I'm not interested enough in getting the functionality that I'm willing to write a whole rgb_to_hsl conversion routine. If someone wants to put in a PR that does that, either before this one is accepted, or after, then that'd be swell. But I think the PR has merit even if it is not vectorized.


def _brighten_test_helper(color, shade, expected):
sc = mcolors.brighten_color(color, shade)
assert_equal(sc, expected)
Copy link
Member

Choose a reason for hiding this comment

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

Are these both tuples? Justassert sc == expected.

HSL using `colorsys.rgb_to_hsl`. The lightness is modified according
to the given fraction, clipped to be between 0 and 1, and then converted
back to RGB using `colorsys.hsl_to_rgb`:
L = np.clip(L0 * (1.0 + frac), 0., 1.), where L0 is the original
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not convinced that this operation really makes a lot of sense, e.g. is going from L=0.1 to L=0.2 (frac=1) really "equivalent" from going to L=0.5 to L=1? If anything I think this should something like a probit or logit scale.

Copy link
Contributor

@eric-wiesereric-wieserDec 13, 2017
edited
Loading

Choose a reason for hiding this comment

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

Seethis comment - I agree with you

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I don’t feel strongly about this. Do you want black to stay black or go grey? I could go either way. I still prefer fractional change because I think that’s what most people would mean when they say to lighten an image. I’d you want the histogram to move right but black would still stay black.

We could also just forget any algorithm and just ask the user for the L value they want perhaps w an accompanying way to get L so they know where they are starting from.

Copy link
Contributor

@anntzeranntzerDec 13, 2017
edited
Loading

Choose a reason for hiding this comment

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

I would indeed prefer a design where we expose a color class with methods such ascolor.hsl_replace(l=...),color.hsv_replace(s=...) (and allowing multiple kwargs of course, just leaving the ones not specified untouched).

@jklymak
Copy link
MemberAuthor

OK, based on the feedback above I'd say that folks see merit in being able to manipulate colors, but want something more fulsome than this PR. i.e. a color management class. That seems a bit of a big project, particularly as it would make sense to let such a color class be a valid input to most places a color is now accepted. Though maybe mostly a documentation project because I think~.colorConverter typically handles the conversion.

I'm closing and opening an issue linking back to this...

@tacaswelltacaswell modified the milestones:v2.2,unassignedDec 13, 2017
@jklymakjklymak deleted the enh-color-shading branchMarch 5, 2019 16:09
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tacaswelltacaswelltacaswell left review comments

@QuLogicQuLogicQuLogic left review comments

@eric-wiesereric-wiesereric-wieser left review comments

@story645story645story645 left review comments

@anntzeranntzeranntzer left review comments

@afvincentafvincentafvincent left review comments

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

8 participants
@jklymak@tacaswell@QuLogic@eric-wieser@story645@anntzer@afvincent@dvreed77

[8]ページ先頭

©2009-2025 Movatter.jp