9
175
Fork
You've already forked wallust
21

Sorting colors for attention rather than pure brightness #186

Closed
opened 2025年10月19日 18:17:56 +02:00 by usaradark · 10 comments
Contributor
Copy link

Hello once again. As usual, fantastic work on wallust.

Context

If I understood the code correctly, currently all colors are sorted only brightness. Taking that into account, I got around to overhauling my Neovim colorscheme to properly use wallust generated palettes. I mapped colors based on what I wanted more attention of, assigning things like functions calls colors of higher brightness and strings colors of lower brightness.

Problem

However, during testing I noticed that some lower-sorted colors dominated my attention compared to higher-sorted colors. Additionally, when generating palettes from different backgrounds, colors shuffle around (ofc same sorting of brightness), but now my attention swaps due to differing saturation and hues. Now instead of my attention being grabbed by functions, it is now grabbed by strings (i.e. functions are now a cool color, strings are warm).

Example

Take a look at this generated color palette. Of the top-row colors, which colors draw your attention the most?
image

{
 "special": {
 "background": "#080000",
 "foreground": "#D6FED2",
 "cursor": "#E5AC8E"
 },
 "colors": {
 "color0": "#0A0000",
 "color1": "#3965BA",
 "color2": "#BE4235",
 "color3": "#337F7C",
 "color4": "#665E95",
 "color5": "#BFA234",
 "color6": "#4FBC42",
 "color7": "#BDF5B6",
 "color8": "#84AB7F",
 "color9": "#3070E9",
 "color10": "#F44535",
 "color11": "#3BA09D",
 "color12": "#8A7EC6",
 "color13": "#FFD846",
 "color14": "#69FB58",
 "color15": "#BDF5B6"
 }
}

I would say the red, with the right-most colors being close. That red is far on the left, next to the cooler colors (blue, teal). If we instead sorted for attention, it might look like this.

If we instead sorted by attention, it might look something like this.
image

{
 "special": {
 "background": "#080000",
 "foreground": "#D6FED2",
 "cursor": "#E5AC8E"
 },
 "colors": {
 "color0": "#0A0000",
		"color1": "#665E95",
 "color2": "#3965BA",
 "color3": "#337F7C",
		"color4": "#4FBC42",
		"color5": "#BFA234",
		"color6": "#BE4235",
 "color7": "#BDF5B6",
 "color8": "#84AB7F",
		"color9": "#8A7EC6",
 "color10": "#3070E9",
 "color11": "#3BA09D",
		"color12": "#69FB58",
		"color13": "#FFD846",
		"color14": "#F44535",
 "color15": "#BDF5B6"
 }
}

Here's a side by side comparison (pardon my messy code). First is sort by brightness (current), second is sort by attention (proposed).
2025年10月19日T08:48:09,538647645-07:00
image

My attention ordering is as follows: Top-level Definitions > Comments > Functions > Variables > Data > Context > Punctuation which would map to color 7, 6, 5, 4, 3, 2, 1.

Notice that on the first image, there's high attention on the LABEL keyword which is an enum. It should have less attention than a function, hovering somewhere around the attention of a variable and data.

In the second image, more attention is brought to the comments. LABEL here has less attention than functions and stand besides variables and data.

Proposal

All said, brightness is still the main driver of attention. However, color theory suggests that warmer colors can also attention when compared cool colors. Something like: Red > Orange > Yellow > Green > Blue > Violet. It also suggests a similar phenomena can occur with saturation, though I currently don't know if wallust does saturation adjustments to individual colors aside from the saturation flag.

I think if there was an option to sort by attention rather by brightness, I think it would more intuitive for downstream developers to use. Without being able to predict the exact hue that will be used, they run into issues as mentioned in this thread, where warmer, saturated lower-brightness colors dominate desaturated higher-brightness cool colors.

As to exactly how this sorting would work... let's figure out if you think this is a good idea first.

edit - better sorting of attention example

Hello once again. As usual, fantastic work on wallust. ## Context If I understood the code correctly, currently all colors are sorted only brightness. Taking that into account, I got around to overhauling my Neovim colorscheme to properly use wallust generated palettes. I mapped colors based on what I wanted more attention of, assigning things like functions calls colors of higher brightness and strings colors of lower brightness. ## Problem However, during testing I noticed that some lower-sorted colors dominated my attention compared to higher-sorted colors. Additionally, when generating palettes from different backgrounds, colors shuffle around (ofc same sorting of brightness), but now my attention swaps due to differing saturation and hues. Now instead of my attention being grabbed by functions, it is now grabbed by strings (i.e. functions are now a cool color, strings are warm). ## Example Take a look at this generated color palette. Of the top-row colors, which colors draw your attention the most? ![image](/attachments/fc5d2f31-17bd-4921-a174-2b3b19e58fdc) ```json { "special": { "background": "#080000", "foreground": "#D6FED2", "cursor": "#E5AC8E" }, "colors": { "color0": "#0A0000", "color1": "#3965BA", "color2": "#BE4235", "color3": "#337F7C", "color4": "#665E95", "color5": "#BFA234", "color6": "#4FBC42", "color7": "#BDF5B6", "color8": "#84AB7F", "color9": "#3070E9", "color10": "#F44535", "color11": "#3BA09D", "color12": "#8A7EC6", "color13": "#FFD846", "color14": "#69FB58", "color15": "#BDF5B6" } } ``` I would say the red, with the right-most colors being close. That red is far on the left, next to the cooler colors (blue, teal). If we instead sorted for attention, it might look like this. If we instead sorted by attention, it might look something like this. ![image](/attachments/a3f8c6ec-4ac7-4238-805c-252e6189d723) ```json { "special": { "background": "#080000", "foreground": "#D6FED2", "cursor": "#E5AC8E" }, "colors": { "color0": "#0A0000", "color1": "#665E95", "color2": "#3965BA", "color3": "#337F7C", "color4": "#4FBC42", "color5": "#BFA234", "color6": "#BE4235", "color7": "#BDF5B6", "color8": "#84AB7F", "color9": "#8A7EC6", "color10": "#3070E9", "color11": "#3BA09D", "color12": "#69FB58", "color13": "#FFD846", "color14": "#F44535", "color15": "#BDF5B6" } } ``` Here's a side by side comparison (pardon my messy code). First is sort by brightness (current), second is sort by attention (proposed). ![2025年10月19日T08:48:09,538647645-07:00](/attachments/d73935a9-a123-4b7f-9da1-dbb39ab79c2d) ![image](/attachments/f0c169bf-8206-4abb-a3a1-91e2e8beab1c) My attention ordering is as follows: `Top-level Definitions > Comments > Functions > Variables > Data > Context > Punctuation` which would map to color 7, 6, 5, 4, 3, 2, 1. Notice that on the first image, there's high attention on the `LABEL` keyword which is an enum. It should have less attention than a function, hovering somewhere around the attention of a variable and data. In the second image, more attention is brought to the comments. `LABEL` here has less attention than functions and stand besides variables and data. ## Proposal All said, brightness is still the main driver of attention. However, color theory suggests that warmer colors can also attention when compared cool colors. Something like: `Red > Orange > Yellow > Green > Blue > Violet`. It also suggests a similar phenomena can occur with saturation, though I currently don't know if wallust does saturation adjustments to individual colors aside from the saturation flag. I think if there was an option to sort by attention rather by brightness, I think it would more intuitive for downstream developers to use. Without being able to predict the exact hue that will be used, they run into issues as mentioned in this thread, where warmer, saturated lower-brightness colors dominate desaturated higher-brightness cool colors. As to exactly how this sorting would work... let's figure out if you think this is a good idea first. edit - better sorting of attention example

Mainly the LAB colorspace uses brightness. LCH uses brightness (color.l) in combination with chroma (.chroma) which, imo, gives much better results. I questioned these approaches a lot, but couldn't get away with a "perfect" sample.

Lines 55 to 74 in 0a0e7c0
fn sort_col(histo: Vec<Hist>,cs: &ColorOrder)-> Vec<Hist>{
letmuthisto=histo;
usestd::cmp::Ordering;
histo.sort_by(|a,b|matchcs{
// ColorOrder::LightFirst => b.color.l.partial_cmp(&a.color.l).unwrap_or(std::cmp::Ordering::Equal),
// ColorOrder::DarkFirst => a.color.l.partial_cmp(&b.color.l).unwrap_or(std::cmp::Ordering::Equal),
// ColorOrder::LightFirst => a.color.chroma.partial_cmp(&b.color.chroma).unwrap_or(std::cmp::Ordering::Equal),
// ColorOrder::DarkFirst => b.color.chroma.partial_cmp(&a.color.chroma).unwrap_or(std::cmp::Ordering::Equal),
// ColorOrder::LightFirst => b.color.hue.into_inner().partial_cmp(&a.color.hue.into_inner()).unwrap_or(std::cmp::Ordering::Equal),
// ColorOrder::DarkFirst => a.color.hue.into_inner().partial_cmp(&b.color.hue.into_inner()).unwrap_or(std::cmp::Ordering::Equal),
ColorOrder::LightFirst=>(b.color.l,a.color.chroma).partial_cmp(&(a.color.l,b.color.chroma)).unwrap_or(Ordering::Equal),
ColorOrder::DarkFirst=>(a.color.l,b.color.chroma).partial_cmp(&(b.color.l,a.color.chroma)).unwrap_or(Ordering::Equal),
});
histo
}

I currently don't know if wallust does saturation adjustments to individual colors aside from the saturation flag.

Wallust doesn't saturate the colors, it just displays them accurate as possible. Pywal/wal, for instance, saturates the colors a bit, giving it more hue by default.

I think if there was an option to sort by attention rather by brightness,

A new palette, colorspace, or even preset could be made

I think it would more intuitive for downstream developers to use. Without being able to predict the exact hue that will be used, they run into issues as mentioned in this thread, where warmer, saturated lower-brightness colors dominate desaturated higher-brightness cool colors.

Ah, if that's your use case, then something like the material you format could help. Similar to what matugen uses. I think I might implement this, a template could work, but since it's so helpful for ui/ux devs, maybe I should consider using it.

Mainly the LAB colorspace uses brightness. LCH uses brightness (`color.l`) in combination with chroma (`.chroma`) which, imo, gives much better results. I questioned these approaches a lot, but couldn't get away with a "perfect" sample. https://codeberg.org/explosion-mental/wallust/src/commit/0a0e7c0ca83a4528f1e6e9f35ecce7a263847438/src/colorspaces/lch.rs#L55-L74 > I currently don't know if wallust does saturation adjustments to individual colors aside from the saturation flag. Wallust doesn't saturate the colors, it just displays them accurate as possible. Pywal/wal, for instance, saturates the colors a bit, giving it more hue by default. > I think if there was an option to sort by attention rather by brightness, A new palette, colorspace, or even preset could be made > I think it would more intuitive for downstream developers to use. Without being able to predict the exact hue that will be used, they run into issues as mentioned in this thread, where warmer, saturated lower-brightness colors dominate desaturated higher-brightness cool colors. Ah, if that's your use case, then something like the material you format could help. Similar to what [matugen uses](https://github.com/InioX/matugen/wiki/Configuration#keywords). I think I might implement this, a template could work, but since it's so helpful for ui/ux devs, maybe I should consider using it.
Author
Contributor
Copy link

A new palette, colorspace, or even preset could be made

I think a new palette could work? I was theorizing some ways of how attention could be measured. I don't really know what I'm doing, but I was thinking converting the components of LCH into salience (attention) values and then taking a weighted sum. Perception isn't the same as salience. If two colors have the exact same luminance and choma, but one is blue and one is red, the red one will grab more attention than a blue one.

Here's a desmos graph I plotted to convert values to salience values.
https://www.desmos.com/calculator/bkdimfewnc

It loosely converts individual values of perception into salience.

I think the most important component here is that attention values for hue follows a cosine function, peaking at warm colors and dipping at cooler colors. This can be shifted to instead peak against the background color if it has saturation above some amount. We could interpolate between warm vs. cool and bg.hue.complementary vs bg.hue, where when bg.chroma is low, warm vs. cool dominates, but when bg.chroma is perceptible, we start switching tobg.hue.complementary vs bg.hue.

A weighted sum of the salience components are done and can then be used for sorting colors based on attention.

For light themes, we would shift the optimal luminance down to from 70 (0.7) to 40 (0.4) and make adjustments to weights, favoring luminance over hue.

Ah, if that's your use case, then something like the material you format could help.

I actually want to stick to wal ecosystem and the amount of colors it choses. I like my fastfetch color palettes. By downstream developers I meant people who live on terminals and leverage colors there, not GUI apps.

I don't know Rust, but this is something I would really to see implemented. Making that quick demo to demonstrate attention sorting made my code a LOT more readable and less noisy. I also saw improvements to general terminal use, but that could be placebo.

This could also a way used to "replace" the standard ANSI color palette if people prefer that over attention scaling from 0->7. Red is usually used for errors, so in its place we put the highest attention color there. Blue and cyan are usually mapped to info, so the lowest attention color would take its place, etc.

I'd be down to try my hand at making something work if you're interested. I would just need some guidance on what code to touch. Maybe I can finally say I'm learning Rust.

> A new palette, colorspace, or even preset could be made I think a new palette could work? I was theorizing some ways of how attention could be measured. I don't really know what I'm doing, but I was thinking converting the components of LCH into [salience](https://en.wikipedia.org/wiki/Salience_(neuroscience)#In_interaction_design) (attention) values and then taking a weighted sum. Perception isn't the same as salience. If two colors have the exact same luminance and choma, but one is blue and one is red, the red one will grab more attention than a blue one. Here's a desmos graph I plotted to convert values to salience values. https://www.desmos.com/calculator/bkdimfewnc It loosely converts individual values of perception into salience. I think the most important component here is that attention values for hue follows a cosine function, peaking at warm colors and dipping at cooler colors. This can be shifted to instead peak against the background color if it has saturation above some amount. We could interpolate between warm vs. cool and bg.hue.complementary vs bg.hue, where when bg.chroma is low, warm vs. cool dominates, but when bg.chroma is perceptible, we start switching tobg.hue.complementary vs bg.hue. A weighted sum of the salience components are done and can then be used for sorting colors based on attention. For light themes, we would shift the optimal luminance down to from 70 (0.7) to 40 (0.4) and make adjustments to weights, favoring luminance over hue. > Ah, if that's your use case, then something like the material you format could help. I actually want to stick to wal ecosystem and the amount of colors it choses. I like my fastfetch color palettes. By downstream developers I meant people who live on terminals and leverage colors there, not GUI apps. I don't know Rust, but this is something I would really to see implemented. Making that quick demo to demonstrate attention sorting made my code a LOT more readable and less noisy. I also saw improvements to general terminal use, but that could be placebo. This could also a way used to "replace" the standard ANSI color palette if people prefer that over attention scaling from 0->7. Red is usually used for errors, so in its place we put the highest attention color there. Blue and cyan are usually mapped to info, so the lowest attention color would take its place, etc. I'd be down to try my hand at making something work if you're interested. I would just need some guidance on what code to touch. Maybe I can finally say I'm learning Rust.

Damn, this is really insteresting. I found a crate about it, but I would like to see your implementation. I could make you a template if you want for you to start drafting. Take as an example the LAB implementation, you would need to create your own Salient struct tho, to alias it to Spec. I would start by making it a colorspace, and then a palette for it. Then you could make a palette that works perfectly for that colorspace. This is the approach I took with the ANSI colorspace and palette. Also, I'm currently reviewing the preset feature, but could also be added later.

TLDR; check colorspaces, use the lab as an example.

Damn, this is really insteresting. I found [a crate](https://docs.rs/mss_saliency/latest/mss_saliency) about it, but I would like to see your implementation. I could make you a template if you want for you to start drafting. Take as an example the LAB implementation, you would need to create your own Salient `struct` tho, to alias it to Spec. I would start by making it a `colorspace`, and then a `palette` for it. Then you could make a palette that works perfectly for that colorspace. This is the approach I took with the `ANSI` colorspace and palette. Also, I'm currently reviewing the preset feature, but could also be added later. TLDR; check `colorspaces`, use the [lab](https://codeberg.org/explosion-mental/wallust/src/branch/master/src/colorspaces/lab.rs) as an example.
Author
Contributor
Copy link

https://codeberg.org/usaradark/wallust

Here is the fork. I pushed the salience conversion but I'm still wrapping my head around how it should be integrated into the current infrastructure. Only dark and dark16 has been implemented.

From what I understood, colorspaces build the histogram based on raw color differences, truncates, then sorts the colors.

The only thing I touched here was the sorting, which is now by salience rather than by luminance, lowest salience first. I was considering salience differences for histogram bucketing, using some salience_diff instead of col_diff, but for now I just wanted to prove that salience works and makes later colors "pop" out more. I might experiment on it later.

On palettes, this is where I will need some guidance.

Using this background with this config.

backend = "fastresize"
check_contrast = true

After truncating colors after colorspaces, we are left with 6-16 colors sorted with increasing salience. Of these colors, dark grabs the first 5 brightest colors in decreasing brightness. highsaliencedark does this, but with salience instead, picking the 5 most salient colors in decreasing salience.
2025年10月21日T10:40:24,865584417-07:00
With this, I feel that all colors pop out way too much. Contrast feels a bit too harsh against background.

I tried the opposite, lowsaliencedark, grabbing the last 5 colors (still asc order).
2025年10月21日T10:40:17,143483186-07:00
Looks a bit better.

I tried a more balanced approach, picking colors evenly distributed across all usable colors. This is the default saliencedark.
2025年10月21日T10:40:30,947256401-07:00

The key thing to look here is the contrast from the background. With high salience, colors should pop in reference to the background. The highsaliencedark approach has everything pop too much. lowsaliencedark is good for those sensitive to high contrast, and the balanced saliencedark

There's a few more tweaks that I know needs to be done and further discussion/contemplation.

  1. Currently assumes no chroma on background. In this case, calculation have warm colors as the optimal. It needs to be changed to consider cases where the background has chroma, where the optimal is its complementary.
  2. No idea what to do with bg, fg, and col[7] (white). Currently does what dark does. If I consider ANSI and typical terminal design... col[7] is supposed to be a neutral color. Perhaps I can do some salience averaging and have it such that fg and col[7] is of neutral salience. bg is probably fine as is.
  3. Salience palette feels like it could be a palette variation rather than its own palette. In its most simplest form, it would sort colors based on salience. So if we did colorspace lch palette darksalience, it would sort and truncate for the brightest colors then order by salience. Compared to colorspace lchsalience palette saliencedark, it sorts and truncates for the salient colors, then (redundantly) order by salience. In either case, col[7] and fg would be adjusted as stated in (2).
  4. Salience... Salient... Attention... I don't know what to call it. I didn't learn of the term salience until a few days ago. You can decide whether to call it salience or attention. Salience is defined as the property by which something stands out.
  5. Salience could also be used as a way to check for contrast. Though probably better to follow SC 1.4.3 when against BG. Might be useful elsewhere (like histogram sorting mentioned earlier).
  6. If we keep lowsaliencedark and highsaliencedark, not too sure if you want them as individual files or if it's okay as one file. All it does is modify the cols and then passes it to the palette generator.
https://codeberg.org/usaradark/wallust Here is the fork. I pushed the salience conversion but I'm still wrapping my head around how it should be integrated into the current infrastructure. Only dark and dark16 has been implemented. From what I understood, colorspaces build the histogram based on raw color differences, truncates, then sorts the colors. The only thing I touched here was the sorting, which is now by salience rather than by luminance, **lowest salience first**. I was considering salience differences for histogram bucketing, using some `salience_diff` instead of `col_diff`, but for now I just wanted to prove that salience works and makes later colors "pop" out more. I might experiment on it later. On palettes, this is where I will need some guidance. Using this [background](/attachments/c1322147-e2fb-4c06-850b-dbde0fb42638) with this config. ```toml backend = "fastresize" check_contrast = true ``` After truncating colors after colorspaces, we are left with 6-16 colors sorted with increasing salience. Of these colors, `dark` grabs the first 5 brightest colors in decreasing brightness. `highsaliencedark` does this, but with salience instead, picking the 5 most salient colors in decreasing salience. ![2025年10月21日T10:40:24,865584417-07:00](/attachments/74213a05-da88-409c-9232-0783b58409ab) With this, I feel that all colors pop out way too much. Contrast feels a bit too harsh against background. I tried the opposite, `lowsaliencedark`, grabbing the last 5 colors (still asc order). ![2025年10月21日T10:40:17,143483186-07:00](/attachments/593385d3-dd1b-44db-866b-efdcb965dff4) Looks a bit better. I tried a more balanced approach, picking colors evenly distributed across all usable colors. This is the default `saliencedark`. ![2025年10月21日T10:40:30,947256401-07:00](/attachments/702b49e4-e2d9-4dd2-82f3-f888ffc1fe86) The key thing to look here is the contrast from the background. With high salience, colors should pop in reference to the background. The `highsaliencedark` approach has everything pop too much. `lowsaliencedark` is good for those sensitive to high contrast, and the balanced `saliencedark` There's a few more tweaks that I know needs to be done and further discussion/contemplation. 1. Currently assumes no chroma on background. In this case, calculation have warm colors as the optimal. It needs to be changed to consider cases where the background has chroma, where the optimal is its complementary. 2. No idea what to do with `bg`, `fg`, and `col[7]` (white). Currently does what `dark` does. If I consider ANSI and typical terminal design... `col[7]` is supposed to be a neutral color. Perhaps I can do some salience averaging and have it such that `fg` and `col[7]` is of neutral salience. `bg` is probably fine as is. 3. Salience palette feels like it could be a palette variation rather than its own palette. In its most simplest form, it would sort colors based on salience. So if we did colorspace `lch` palette `darksalience`, it would sort and truncate for the brightest colors then order by salience. Compared to colorspace `lchsalience` palette `saliencedark`, it sorts and truncates for the salient colors, then (redundantly) order by salience. In either case, `col[7]` and `fg` would be adjusted as stated in (2). 4. Salience... Salient... Attention... I don't know what to call it. I didn't learn of the term salience until a few days ago. You can decide whether to call it salience or attention. [Salience](https://en.wikipedia.org/wiki/Salience_(neuroscience)) is defined as the property by which something stands out. 5. Salience could also be used as a way to check for contrast. Though probably better to follow SC 1.4.3 when against BG. Might be useful elsewhere (like histogram sorting mentioned earlier). 6. If we keep `lowsaliencedark` and `highsaliencedark`, not too sure if you want them as individual files or if it's okay as one file. All it does is modify the `cols` and then passes it to the palette generator.
Author
Contributor
Copy link

I experimented with a v2 branch. It uses an updated functions to map deltas to salience, with each component normalized to 0-1, then the weighted average is taken.

I also, without much thinking, implemented salience as a trait. I did it more so just to see if I could do it, but it might not be necessary. Regardless it works. Generated palette look pleasing so far. Here are some demos.

image
image
image


Update v4 with adjustments and tweaks to salience calculations. This will probably be the final iteration (for now) for modeling salience between two colors.
image

Next things to do is to have hue consider the true actual final background color (somehow). I also want to experiment on with the sorting of the histogram, to also consider salience rather than just count of color.

I experimented with a [v2](https://codeberg.org/usaradark/wallust/src/branch/salience-v2) branch. It uses an updated functions to map deltas to salience, with each component normalized to 0-1, then the weighted average is taken. I also, without much thinking, implemented salience as a trait. I did it more so just to see if I could do it, but it might not be necessary. Regardless it works. Generated palette look pleasing so far. Here are some demos. ![image](/attachments/ac67b206-444e-45d3-ae39-1f9f072db3ed) ![image](/attachments/4e4eabe0-15b5-4ed8-a8cf-3f073667a1d1) ![image](/attachments/6cadbd0c-aeba-4201-b9bd-4e05a237a8cc) --- Update [v4](https://codeberg.org/usaradark/wallust/src/branch/salience-v4) with adjustments and tweaks to salience calculations. This will probably be the final iteration (for now) for modeling salience between two colors. ![image](/attachments/77117042-990e-47f5-8b65-a573b17c8084) Next things to do is to have hue consider the true actual final background color (somehow). I also want to experiment on with the sorting of the histogram, to also consider salience rather than just count of color.
Author
Contributor
Copy link

I finally got around to reading that paper the salience crate is based on. I think it can definitely be used to contribute to how colors are picked from an image.

Roughly speaking, instead of sorting by count, we could sort by a score. If the color exists, score +1. If it also exists in the salience map, +X to the score. A color needs to be ~30% more than a salience color of the same amount for it to rank higher... or something like that. The paper does not take into account biological aspects, opting to do a center-surround contrast thingy, so I'll have to consider that in score calculations.

But what's also interesting about the salience map is that it does seem to separate the background form the subject. I might be able to leverage that to pick a better background color. By doing this, it would be possible to calculate the true salience against the final background before truncating colors, as currently it assumes a constant dark/light color for backgrounds.

That said, the sorting potion of colors against salience is nearing finished. Dark is more or less done, but light theme is messy and is rather sensitive to warm colors. Still finetuning and experimenting to find optimal salience curves. It will be "usable," but not feature complete as the colorspace for picking salient colors has yet to fully be implemented, as mentioned in this earlier comment.

If you want to give it a go, pull the v4 branch, compile and run with colorspace lchsalience and palette saliencedark16 (or no 16). It assumes that your apps map the least important colors to 1 and the most important colors to 6. Color 1 should pop out less compared later colors. I haven't done an ANSI palette variant, but it should be trivial. For dark, colors would probably remap as follows:

black	red	green	yellow	blue	magenta	cyan	white
0		6	4		5		1		3		2		7

Update

Several tweaks and optimizations to color processing before they get clipped/truncated and passed over to the palette generator. No salience map yet, but the infrastructure is there.

Instead of sorting by count, we sort by score which accounts for salience and a logarithmic scale of count. This logarithmic scaling allows colors of lower count to dominate colors of higher counts if their salience is high enough (or the inverse, lower counts can dominate if their salience is high enough). Salience calculation here is naive because the background is not decided. Naive calculations use lumanince and chroma, but not hue. This is to prevent clipping of color hues that may be significant against the true hue of the finalized background.

From the sorted scored colors, we designate the lowest scored color to be the background, and then we sort by salience against the background.

A similar process also done on the palette generator to allow other backends to work, but because we are working with histogram and not colors, we do a slightly different process. This re-sort might mess up the lchsalience sorting, will need to validate if it causes issues laterrrrrr.

Currently the palette generator for salience is not optimal, but it works good enough, will polish later. Got some messy imports and what not, will figure that one out later as well...

It just keeps getting better and better.
image

I finally got around to reading that paper the salience crate is based on. I think it can definitely be used to contribute to how colors are picked from an image. Roughly speaking, instead of sorting by count, we could sort by a score. If the color exists, score +1. If it also exists in the salience map, +X to the score. A color needs to be ~30% more than a salience color of the same amount for it to rank higher... or something like that. The paper does not take into account biological aspects, opting to do a center-surround contrast thingy, so I'll have to consider that in score calculations. But what's also interesting about the salience map is that it does seem to separate the background form the subject. I might be able to leverage that to pick a better background color. By doing this, it would be possible to calculate the true salience against the final background before truncating colors, as currently it assumes a constant dark/light color for backgrounds. That said, the sorting potion of colors against salience is nearing finished. Dark is more or less done, but light theme is messy and is rather sensitive to warm colors. Still finetuning and experimenting to find optimal salience curves. It will be "usable," but not feature complete as the colorspace for picking salient colors has yet to fully be implemented, as mentioned in this earlier comment. If you want to give it a go, pull the [v4 branch](https://codeberg.org/usaradark/wallust/src/branch/salience-v4), compile and run with colorspace `lchsalience` and palette `saliencedark16` (or no 16). It assumes that your apps map the least important colors to 1 and the most important colors to 6. Color 1 should pop out less compared later colors. I haven't done an ANSI palette variant, but it should be trivial. For dark, colors would probably remap as follows: ``` black red green yellow blue magenta cyan white 0 6 4 5 1 3 2 7 ``` --- ### Update Several tweaks and optimizations to color processing before they get clipped/truncated and passed over to the palette generator. No salience map yet, but the infrastructure is there. Instead of sorting by count, we sort by score which accounts for salience and a logarithmic scale of count. This logarithmic scaling allows colors of lower count to dominate colors of higher counts if their salience is high enough (or the inverse, lower counts can dominate if their salience is high enough). Salience calculation here is naive because the background is not decided. Naive calculations use lumanince and chroma, but not hue. This is to prevent clipping of color hues that may be significant against the true hue of the finalized background. From the sorted scored colors, we designate the lowest scored color to be the background, and then we sort by salience against the background. A similar process also done on the palette generator to allow other backends to work, but because we are working with histogram and not colors, we do a slightly different process. This re-sort might mess up the `lchsalience` sorting, will need to validate if it causes issues laterrrrrr. Currently the palette generator for salience is not optimal, but it works good enough, will polish later. Got some messy imports and what not, will figure that one out later as well... It just keeps getting better and better. ![image](/attachments/b91013f6-b300-4c06-ab4f-f4466430f066)
807 KiB

I really like the results of lchsalience + saliencedark.

Instead of sorting by count, we sort by score which accounts for salience and a logarithmic scale of count.

I thought this was the initial idea, that's why I suggested tweaking the colorspace module. I haven't wrapped my head around this yet, but I thought it was going to be similar to the other color spaces (as in rgb -> lch, then sort by count). But I think the salience actually requires either LCH/LAB for perceptual uniformity. If that's the case then renaming it lchsalience to just salience should be good enough, since there would probably be no changes if we used lab or lch, and if so, I think it should be a internal change.

I haven't done an ANSI palette variant,

How would that work? ANSI has predefined colors, meaning it will ignore the salience order. Salience won't matter if there is a hardcoded sorting.

Currently the palette generator for salience is not optimal, but it works good enough, will polish later. Got some messy imports and what not, will figure that one out later as well...

It's pretty good for someone learning Rust, I hope you like the language (so you can keep an eye here after :p). And yes, afterwards you can polish with cargo clippy.

If the color exists, score +1. If it also exists in the salience map, +X to the score. A color needs to be ~30% more than a salience color of the same amount for it to rank higher...

Btw this is very interesting. I think it could even be applied to the construction of the histogram for the general use. This, however, still brings back my questions about how many choices should the user get... Salience is probably the top usecase for what this program is for, having a colorpalette, getting the colors that are "interesting" for the eye, but does that guaranties that the palette would be the desired one from the user? That's mostly why I kept choice, with good defaults, at the cost of the "best results".

Here are the results for me:

lchsalience + saliencedark

lchsalience-saliencedark

lchsalience + dark (lchsalience seems to work out fine with other palettes)

lchsalience-dark

lch+dark (usual setup)

lch-dark

I really like the results of `lchsalience` + `saliencedark`. > Instead of sorting by count, we sort by score which accounts for salience and a logarithmic scale of count. I thought this was the initial idea, that's why I suggested tweaking the `colorspace` module. I haven't wrapped my head around this yet, but I thought it was going to be similar to the other color spaces (as in `rgb` -> `lch`, then sort by count). But I think the salience actually requires either LCH/LAB for perceptual uniformity. If that's the case then renaming it `lchsalience` to just `salience` should be good enough, since there would probably be no changes if we used lab or lch, and if so, I think it should be a internal change. > I haven't done an ANSI palette variant, How would that work? ANSI has predefined colors, meaning it will ignore the salience order. Salience won't matter if there is a hardcoded sorting. > Currently the palette generator for salience is not optimal, but it works good enough, will polish later. Got some messy imports and what not, will figure that one out later as well... It's pretty good for someone learning Rust, I hope you like the language (so you can keep an eye here after :p). And yes, afterwards you can polish with `cargo clippy`. > If the color exists, score +1. If it also exists in the salience map, +X to the score. A color needs to be ~30% more than a salience color of the same amount for it to rank higher... Btw this is very interesting. I think it could even be applied to the construction of the histogram for the general use. This, however, still brings back my questions about how many choices should the user get... Salience is probably the top usecase for what this program is for, having a colorpalette, getting the colors that are "interesting" for the eye, but does that guaranties that the palette would be the desired one from the user? That's mostly why I kept choice, with good defaults, at the cost of the "best results". Here are the results for me: # `lchsalience` + `saliencedark` ![lchsalience-saliencedark](/attachments/f111756f-2085-41bb-8b86-f296e47957f3) # `lchsalience` + `dark` (lchsalience seems to work out fine with other palettes) ![lchsalience-dark](/attachments/20216933-f3ca-40e1-9cdf-fa99c5a86f50) # `lch`+`dark` (usual setup) ![lch-dark](/attachments/500a6528-4858-4f0d-8ecd-c1250e8e05ee)
Author
Contributor
Copy link

Well this is funny. Lately I've been trying to finetune the model of salience with colors against a background. It's been pretty difficult. I actually have a v5 more or less ready, but I wasn't expecting this much trouble.

Blues and yellows and other extreme colors are difficult to model and sort properly, leading to a pair being swapped when they shouldn't. It's passable, but I want it to be better. I did some more research LAB/LCH has its limitations when it comes to perceptual salience. A more recent colorspace is Cam16, which palette supports. Cam16 enforces a viewing conditions for context, which is precisely what I'm trying to do. If my initial research is correct, this should result in a much simpler and accurate implementation of the salience model.

For now I can push what I have for you (or anyone) to try out. I really like the results, but it struggles sorting colors of extreme luminance or chroma, especially on the lower end of things, as mentioned earlier.

As much as I would like to consider merging this upstream, the code is very messy, neither have I ran it through clippy yet. In the coming days I'll experiment with Cam16 to see where that goes, but what I have right now with LCH is good enough for general use.

How would that work? ANSI has predefined colors, meaning it will ignore the salience order. Salience won't matter if there is a hardcoded sorting.

My intuition was to remap ansi colors to salience, then use that mapping to map the palette to ansi. So from initial calculations, of the top-row ansi colors, yellow is actually the most salient color, so therefore, the most salient color generated would map to yellow. Blue is the least salient color, so the least salient color generated would map to blue. Etc. Very experimental idea, but it allows compatibility against those who prefer the ansi layout rather than the modern low->high mapping.

Btw this is very interesting. I think it could even be applied to the construction of the histogram for the general use.

v4 (and v5) actually has the histoscore implementation which accounts for salience when sorting colors before truncating and passing it to the palette generator. In short, after deduping the colors, we scale the count of colors logarithmically. Logarithmic scaling prevents extreme counts from blowing up the sorting. This then gets multiplied by the salience of the color. If 80% of the colors is black, it's salience value is near 0. 80% of total pixels * 0 = 0, so it get sorted much later, allowing the other 20% of the colors to be sorted earlier (earlier is more salient). It works quite well, though I haven't tested with other backends and varying image dimensions.

This, however, still brings back my questions about how many choices should the user get...

I think being able to choose the contrast of colors (low, high, even, distributed) is... maybe reasonable, at least with salience scoring. High salience colors against a dark background can lead to some insane contrast. I think you worked around this by adjusting the background brightness in accordance to the lower brightness color from the palette. It worked to tighten the contrast, but I feel like there's a better way to go about this. Especially under the salience model, there's nuances like preserving background color such that hue still keeps contrast...

I'm pretty brain dead tonight. I pushed what I got. I'm quite interested in experimenting with Cam16. We'll see in the coming days...

With the v5 branch. v5 simplifies the salience calculations and takes into account some more variables, such as chroma having no salience when luminance approaches 0 or 100, or hue having no salience if luminance approaches 0 or 100 or chroma approaches 0.
image
image

Brain is fried. 'till next time.

Well this is funny. Lately I've been trying to finetune the model of salience with colors against a background. It's been pretty difficult. I actually have a [v5](https://codeberg.org/usaradark/wallust/src/branch/salience-v5) more or less ready, but I wasn't expecting this much trouble. Blues and yellows and other extreme colors are difficult to model and sort properly, leading to a pair being swapped when they shouldn't. It's _passable_, but I want it to be better. I did some more research LAB/LCH has its limitations when it comes to perceptual salience. A more recent colorspace is Cam16, which palette supports. Cam16 enforces a viewing conditions for context, which is precisely what I'm trying to do. If my initial research is correct, this should result in a much simpler and accurate implementation of the salience model. For now I can push what I have for you (or anyone) to try out. I really like the results, but it struggles sorting colors of extreme luminance or chroma, especially on the lower end of things, as mentioned earlier. As much as I would like to consider merging this upstream, the code is very messy, neither have I ran it through clippy yet. In the coming days I'll experiment with Cam16 to see where that goes, but what I have right now with LCH is _good enough_ for general use. > How would that work? ANSI has predefined colors, meaning it will ignore the salience order. Salience won't matter if there is a hardcoded sorting. My intuition was to remap ansi colors to salience, then use that mapping to map the palette to ansi. So from initial calculations, of the top-row ansi colors, yellow is actually the most salient color, so therefore, the most salient color generated would map to yellow. Blue is the least salient color, so the least salient color generated would map to blue. Etc. Very experimental idea, but it allows compatibility against those who prefer the ansi layout rather than the modern low->high mapping. > Btw this is very interesting. I think it could even be applied to the construction of the histogram for the general use. v4 (and v5) actually has the `histoscore` implementation which accounts for salience when sorting colors before truncating and passing it to the palette generator. [In short, after deduping the colors, we scale the count of colors logarithmically](https://codeberg.org/usaradark/wallust/src/commit/bb10ab943444fef1166faf145bb497693e620f7c/src/colorspaces/lchsalience.rs#L459). Logarithmic scaling prevents extreme counts from blowing up the sorting. This then gets multiplied by the salience of the color. If 80% of the colors is black, it's salience value is near 0. 80% of total pixels * 0 = 0, so it get sorted much later, allowing the other 20% of the colors to be sorted earlier (earlier is more salient). It works quite well, though I haven't tested with other backends and varying image dimensions. > This, however, still brings back my questions about how many choices should the user get... I think being able to choose the contrast of colors (low, high, even, distributed) is... maybe reasonable, at least with salience scoring. High salience colors against a dark background can lead to some insane contrast. I think you worked around this by adjusting the background brightness in accordance to the lower brightness color from the palette. It worked to tighten the contrast, but I feel like there's a better way to go about this. Especially under the salience model, there's nuances like preserving background color such that hue still keeps contrast... I'm pretty brain dead tonight. I pushed what I got. I'm quite interested in experimenting with Cam16. We'll see in the coming days... With the v5 branch. v5 simplifies the salience calculations and takes into account some more variables, such as chroma having no salience when luminance approaches 0 or 100, or hue having no salience if luminance approaches 0 or 100 or chroma approaches 0. ![image](/attachments/97db3609-d5b8-4650-a508-57de1f7b83de) ![image](/attachments/d1dd383d-c28e-4563-a683-c9702c1bee04) Brain is fried. 'till next time.
3.1 MiB
4.1 MiB

For now I can push what I have for you (or anyone) to try out. I really like the results, but it struggles sorting colors of extreme luminance or chroma, especially on the lower end of things, as mentioned earlier.

Brain is fried. 'till next time.

You can clean up your current implementation, I can help out if you allow changes from the maintainer in a PR. Cleaning code is the most fun part! After merging this, you can work on the CAM16 if you have the time, updating yet another branch . This helps when you can't think anymore, since it's just refactoring and applying best practices.

v4 (and v5) actually has the histoscore implementation which accounts for salience when sorting colors before truncating and passing it to the palette generator. In short, after deduping the colors, we scale the count of colors logarithmically. Logarithmic scaling prevents extreme counts from blowing up the sorting. This then gets multiplied by the salience of the color. If 80% of the colors is black, it's salience value is near 0. 80% of total pixels * 0 = 0, so it get sorted much later, allowing the other 20% of the colors to be sorted earlier (earlier is more salient). It works quite well, though I haven't tested with other backends and varying image dimensions.

I think there could even be another option: sorting by count or by "score" (cnt log). At the same time tho, I think this only respects to a implementation of colorspace, given the circumstances about LAB/LCH and finally CAM16.

Edit: Another "choice" for the user, but maybe that would be overkill.

My intuition was to remap ansi colors to salience, then use that mapping to map the palette to ansi. So from initial calculations, of the top-row ansi colors, yellow is actually the most salient color, so therefore, the most salient color generated would map to yellow. Blue is the least salient color, so the least salient color generated would map to blue. Etc. Very experimental idea, but it allows compatibility against those who prefer the ansi layout rather than the modern low->high mapping.

Ah, I understand. LchAnsi artificially creates the colors missing assuring a "perfect" ansi palette, if combined with ansidark. This measures hues, and generates them if missing. So your salience ansi impl would be interesting in which the mappings would corresponnd to the chart you made earlier.

> For now I can push what I have for you (or anyone) to try out. I really like the results, but it struggles sorting colors of extreme luminance or chroma, especially on the lower end of things, as mentioned earlier. > Brain is fried. 'till next time. You can clean up your current implementation, I can help out if you allow changes from the maintainer in a PR. Cleaning code is the most fun part! After merging this, you can work on the CAM16 if you have the time, updating _yet another branch_ . This helps when you can't think anymore, since it's just refactoring and applying best practices. > v4 (and v5) actually has the histoscore implementation which accounts for salience when sorting colors before truncating and passing it to the palette generator. In short, after deduping the colors, we scale the count of colors logarithmically. Logarithmic scaling prevents extreme counts from blowing up the sorting. This then gets multiplied by the salience of the color. If 80% of the colors is black, it's salience value is near 0. 80% of total pixels * 0 = 0, so it get sorted much later, allowing the other 20% of the colors to be sorted earlier (earlier is more salient). It works quite well, though I haven't tested with other backends and varying image dimensions. I think there could even be another option: sorting by count or by "score" (cnt log). At the same time tho, I think this only respects to a implementation of colorspace, given the circumstances about LAB/LCH and finally CAM16. Edit: Another "choice" for the user, but maybe that would be overkill. > My intuition was to remap ansi colors to salience, then use that mapping to map the palette to ansi. So from initial calculations, of the top-row ansi colors, yellow is actually the most salient color, so therefore, the most salient color generated would map to yellow. Blue is the least salient color, so the least salient color generated would map to blue. Etc. Very experimental idea, but it allows compatibility against those who prefer the ansi layout rather than the modern low->high mapping. Ah, I understand. `LchAnsi` artificially creates the colors missing assuring a "perfect" ansi palette, if combined with `ansidark`. This measures hues, and generates them if missing. So your salience ansi impl would be interesting in which the mappings would corresponnd to the chart you made earlier.

PR at #188

PR at #188
Sign in to join this conversation.
No Branch/Tag specified
master
flake-lock-update
v3
pages
ui
4.0.0-alpha
3.5.2
3.5.1
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.0
3.0.0-beta
3.0.0-alpha
2.10.0
2.9.0
2.8.0
2.7.1
2.6.1
2.6.0
2.5.1
2.5.0
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.0
2.0.0
1.0.1
1.0.0
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
explosion-mental/wallust#186
Reference in a new issue
explosion-mental/wallust
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?