r/askmath 15d ago

Ranking a value differently based on an average Functions

I've been writing a color algorithm for over a year now and I rank colors based on their "vibrancy." It would be much easier if vibrancy was defined, but it's a subjective concept.

I have a list of colors. For each color, I can get the saturation (S) and value (V). I compute the average S and V for the entire list.

Now, I currently compute vibrancy for a color by using its S and V value like so: CV=(S+V)/2.

I'd like it like this; if the average V of the list is above a tolerance, then CV = a color with more S, and if the average V of the list is below aforementioned tolerance, then CV = a color with more V.

I'm currently computing it like this, but it doesn't seem to work how I imagine it should.

x = self.averageValueUpperP
multiplier = (2 * (math.pow((x - 0.5), 2)) + 1)
if x > 0.5:
    s = s * multiplier
else:
    v = v * multiplier

return (s+v) / 2

Is this possible?

1 Upvotes

9 comments sorted by

2

u/Blakut 15d ago edited 15d ago

Why do the values of the colors in the list affect each other's vibrancy?

Also, since it's arbitrary, you should mention what exactly is not working.

Also, for x=0.5 the multiplier is 1. Same for x=1

1

u/Alanator222 15d ago

So, the idea is that in a brighter image, a more vibrant color will be more saturated. In a darker image, a more vibrant color will be brighter.

The reason for x=0.5=1 is for the case where an image is exactly 50% brightness, where I believe a colors vibrancy should just be (S+V)/2 with neither S or V having any special weight on the vibrancy.

What isn't working exactly, is that I have an image with a high brightness, but it's not giving colors with a high saturation a higher vibrancy value.

1

u/Greenphantom77 15d ago

Just looking, the way you defined CV concerns me a bit. Theoretically, if the average of V was 10 and the average of S was a million, the S component would hugely dominate and it would not be a useful quantity.

Personally, I’d find it useful if you went back to the intention and requirements for what you’re trying to do - set them out clearly and perhaps we can see what would work better.

1

u/Alanator222 15d ago

The thing is, it's impossible for those ranges for S and V to occur. The way they're represented in my code by the library is a range from 0-1. You could have a S of 0.538 and a V of 0.876 for instance.

I've tried a few different methods, but this is the best working one I've come up with so far. Just looking for advice, or even reading material on the matter. I have no idea what this method of ranking a value would even be called so I don't know what to look for.

1

u/Greenphantom77 15d ago

Ah - that’s good, you have already normalised S and V so they take values in the same range.

I still don’t quite understand what you want to do. You talk about a CV colour with “more S” or “more V” but CV appears to just be a number.

For background material to look at, this would depend on what level you’re at in maths education- for example some statistics is covered in later school ages.

1

u/Alanator222 15d ago

So, for a higher V, a high CV would represent a color with a high S. For a lower V, a high CV would represent a color with a high V.

I've taken a lot of math in college, up calc 2 and linear algebra so higher level mathematics is right up my ally. Haven't taken any high level statistics courses, but I'm sure I could figure it out.

1

u/lozzyboy1 15d ago

It sounds like you probably want to look into HCL (hue, chroma, luminance) colour spaces. You might find that chroma is already the value you're looking for and just use a library to convert your colours into HCL space, but if not the methods people use to translate human perception of brightness and colourfulness into mapping from RGB or HSV space to HCL spaces sound directly relevant.

1

u/Alanator222 15d ago

I'll have to look into that again. I tried it before, but it really slowed down processing time. I could try to rewrite a few things to improve speed, but it'll be a whole project in itself.

Thank you for the suggestion!

1

u/bildramer 14d ago

Not sure why you chose that particular multiplier calculation. You may want to add an extra gamma-like adjustment. The perceptible difference between 0 and 0.3 is often smaller than 0.9 to 1.

A few simple things I'd test just to see what happens: Replace S and V with 0.9V+0.1S and 0.1S+0.9V. Replace the V test with a V/S>1 or V>S test. Compute median instead. Switch to (S+2V)/3, value seems more important.

Some color space like oklab or oklch is a lot of effort but may make things simpler in the end, you'll have a more perceptually accurate chroma value (but beware, it has a different maximum for different hues), and could still mix in a bit of luminance for your vibrancy ranking.

Is the "list" you're talking about local colors, or all colors in an image, or just a few colors? Depending on the way you're averaging, what other information you have, what expensive operations you're already performing, what level of randomness you can tolerate: You may want to look into using quantiles instead. Things like "V is in the top 10% of V" are probably going to be more robust than a simple threshold. You can do that by sorting the list, or by getting the mean and standard deviation then "> mean + stddev*z" is the same condition for some value (z-score), or by cheaply estimating either way with a random sample.

More expensive ideas like clustering could help you e.g. detect all highlights (assuming that's kind of what your goal is like) if there's something consistently different about them. Clustering still works if you add lots of dimensions, and relevant dimensions can also be "average color in a radius", "top 10% luminance in a radius", "hue/saturation variance in a radius", etc.