How To Avoid Equidistant HSV Colors

As some of you pointed out in the comments of my last post, taking equidistant colors in the HSV color space is no solution for finding a set of colors that are perceived as equidistant. This post describes what’s wrong with HSV and what we can do about this. Note that since this post contains interactive elements built on the latest web technologies, you might need a modern browser to get the most out of it.
click here for ultimate color geekyness

Take Care of your Choropleth Maps

Over the last week I had some fun playing with choropleth maps. Thereby I analyzed the following US poverty map, which was recently published at the Guardian data blog:

To be honest, the first time I saw this map I didn’t thought much about it. Ok, poverty is highest in south central of the United States, especially near Mexican border. But recently I used the same data to demonstrate a choropleth map that I created from-scratch and I was really surprised to see a somewhat different picture:

click to dig into the world of choropleth maps..

Say Goodbye to Red-Green Color Scales

I used to love them, but it’s over now: diverging red-green color scales.

red green scale

I bet the reason for the popularity of red-green color scales is that they are so easy to interpret (at least in my culture). Green means good, red means bad. For instance, the above map shows the income of private households for European regions. I used a diverging color scale to show the difference from the average income (bright yellow) with higher incomes in green and lower incomes in red. In fact, I picked the exact colors from good old flare visualization toolkit.

But there’s some trouble with this scale. Firstly, as alluded to above, the meaning of red and green vary a lot in different cultures. According to this helpful collection of Color Meanings By Culture, green is negatively associated in some Eastern cultures like China or Indonesia while red is associated to love, happiness and a long life.

Continue reading

Ansatz zur automatischen Auswahl von Linienfarben in Diagrammen

Ein Problem bei der (automatischen) Erzeugung von Liniendiagrammen liegt in der Auswahl von unterscheidbaren Farben. In diesem Artikel wird ein Ansatz entwickelt, der dieses Problem lösen soll.

Ausgangspunkt ist zunächst der RGB-Farbraum. Um verschiedene Farben zu erzeugen, können einfach zufällige Farben aus dem RGB-Raum ausgewählt werden. Die folgende Darstellung zeigt eine derart getroffene Auswahl von zehn verschiedenen Linienfarben (rechtsklicken um neue Zufallsfarben zu erzeugen):

Die Nachteile dieses Ansatzes sind offensichtlich und sind vor allem auch Nachteile des Farbmodells. Selbst eine gleichverteilte Auswahl von Farben im RGB-Raum würde mitunter zu nahezu identischen Farben führen. Daher habe ich mich im Folgenden auf den HSL-Farbraum konzentriert. Wie alle wahrnehmungsorientierten Farbräume enthält auch der HSL-Raum die Dimension Farbwert – ein Wert zwischen 0° und 360° der den Farbton bestimmt. Die folgende Darstellung zeigt die Auswahl von 10 Farben mit gleichem Abstand im Farbraum (in diesem Fall 36°):

Wie man sieht bekommt man dadurch 10 Ausschnitte aus dem Regenbogenspektrum. Bei genauerem Hinsehen fallen aber immer noch Nachteile auf: So sind vor allem die beiden Grün- und Blautöne schwer voneinander zu unterscheiden. Um eine mögliche Ursache dafür zu nennen mache ich einen kurzen Ausflug zur Wahrnehmungspsychologie. Auf unserer Netzhaut befinden sich zwei Typen von Rezeptorzellen, die sog. Stäbchen und Zapfen. Während die Stäbchen vor allem zum Sehen in der Dämmerung benutzt werden, dienen Zapfen zum Farbsehen. Nun gibt es unter den Zapfen-Zellen wiederum drei verschiedene Typen, wovon jeder zur Wahrnehmung bestimmter Farbfrequenzen geeignet ist. Um die Sache kurz zu machen, es gibt jeweils einen Zapfentyp für rote, grüne und blaue Farbfrequenzen. Und jetzt kommts: die Verteilung der Zapfenzellen ist in etwa 10:1:1 (rot zu grün zu blau). Daher fällt es Menschen naturgemäß leichter, Rottöne voneinander zu unterscheiden als Blau- oder Grüntöne.

Ich habe versucht, diesen Umstand über eine Transferfunktion für die Farbtöne auszugleichen. Die folgende Abbildung zeigt oben einen Farbwertverlauf ohne und unten mit Korrektur. Wie man sehen kann, habe ich einfach den Bereich für grüne und blaue Farbtöne “zusammengedrückt”, um mehr Spielraum zwischen Rot und Gelb zu bekommen.

oben: normaler HSL-Verlauf, unten: HSL-Verlauf mit Farbtonkorrektur

oben: normaler HSL-Verlauf, unten: HSL-Verlauf mit Farbtonkorrektur

Wählt man die 10 Zufallsfarben nun aus diesem, korrigierten Spektrum, erhält man folgendes Resultat:

Die Farbtonkorrektur hat etwas zur Unterscheidbarkeit der grünen und blauen Linien beigetragen. Für den günstigen Fall, das die Linienhelligkeit keine weitere Bedeutung in der Visualisierung hat, kann man die Unterscheidbarkeit der Linien weiter steigern, indem man abwechselnd helle und dunkle Linien verwendet:

Zur Erzeugung der Abbildungen habe ich die ActionScript-Klasse PerceptualColor verwendet. Die Farbwertkorrektur wird über die folgende Klasse HueCorrection gelöst. Ein- und Ausgabe für die Funktion correctHue ist ein Farbwinkel zwischen 0 und 360°.

public class HueCorrection
{
	private static var _tf:Array = [
		[10,5], [30,45], [50,70],
		[70,94], [110,100], [125,115],
		[145,148],[161,174], [182,179],
		[188,185],
		[210,225], [250,255]
	];
 
	public static function correctHue(hue:Number):Number {
		var h:uint = hue / 360 * 255;
		var new_hue:Number;
		var lx:Number = 0;
		var ly:Number = 0;
 
		for (var i:uint=0; i<_tf.length; i++) {
			if (h == _tf[i][1]) {
				new_hue = _tf[i][0];
				break;
			} else if (h < _tf[i][1]) {
				new_hue = lx + ((_tf[i][0] - lx) / (_tf[i][1] - ly)) * (h - ly);
				break;
			}
			lx = _tf[i][0];
			ly = _tf[i][1];
		}
		return new_hue / 255 * 360;
	}
}

Visualization of Perceptual Color Spaces

Die folgenden Visualisierungen sollen die Farbräume HSV, HSI, HSL und HSB veranschaulichen. Dazu wurden jeweils 48 Farbkreise mit unterschiedlicher Sättigung und Farbwert/Helligkeit/Intensität/Leuchtdichte auf einer Fläche angeordnet. Die Sättigung fällt von oben nach unten hin, Farbwert/Helligkeit/Intensität fallen hingegen von links nach rechts. Bei den Darstellungen handelt es sich um Flashfilme, zur Berechnung wurde die Klasse PerceptualColor verwendet. Die Visualisierungen können auch im Vollbild betrachtet werden.

HSV-Farbraum

HSI-Farbraum

HSL-Farbraum

HSB-Farbraum

Color Space Transformations in ActionScript

Here is a small actionscript3 class for converting colors between different color spaces like RGB, HSV, HSL and HSB. You can input and output the colors either as integer or string value or as serperated color components (e.g. hue, saturation and lightness). The class has no other dependencies.

API

/*
 * Modes:
 * PerceptualColor.HSV, PerceptualColor.HSL, PerceptualColor.HSI
 */ 
 
var color:PerceptualColor;
 
// Konstruktoren
color = new PerceptualColor(mode:String = 'hsl');
color = PerceptualColor.fromInt(i:uint, mode = 'hsl');
color = PerceptualColor.fromHex(h:String, mode = 'hsl'); // format: "#RRGGBB"
color = PerceptualColor.fromRGB(r:uint, g:uint, b:uint, mode = 'hsl');
color = PerceptualColor.fromHSV(h:Number, s:Number, v:Number, mode = 'hsv');
color = PerceptualColor.fromHSL(h:Number, s:Number, l:Number, mode = 'hsl');
color = PerceptualColor.fromHSI(h:Number, s:Number, i:Number, mode = 'hsi');
 
// RGB-Setter (Getter äquivalent)
color.intValue = 0x00BBFF;
color.hexValue = "#00BBFF";
color.red = 0;
color.green = 125;
color.blue = 40;
color.setRGB(0, 125, 40);
 
// HSX-Setter (Getter äquivalent)
color.hue = 0;
color.saturation = 0.6;
// nur im HSV-Modus
color.value = 1;
color.setHSV(0, 0.6, 1);
// nur im HSL-Modus
color.lightness = 1;
color.setHSL(0, 0.6, 1);
 // funktioniert nur im HSV-Modus
color.intensity = 1;
color.setHSI(0, 0.6, 1);
 
// Modus ändern
color.mode = PerceptualColor.HSL;

Usage Examples

/*
 * Beispiel 1:
 * Einlesen einer Integer-Farbe, Sättigung auf 50% setzen, Hex-Farbe ausgeben
 */
var color:PerceptualColor = PerceptualColor.fromInt(0xFF0000);
color.saturation = 0.5;
trace(color.hexValue); // Ausgabe: "#BF3F3F"
 
/*
 * Beispiel 2:
 * Einlesen einer RGB-Farbe, Helligkeit auf 70% begrenzen, RGB-Were ausgeben
 */
color = PerceptualColor.fromRGB(220, 250, 180);
if (color.lightness > 0.7) color.lightness = 0.7;
trace(color.red, color.green, color.blue); // Ausgabe: "188 245 111"
 
/*
 * Beispiel 3:
 * Einlesen einer HSV-Farbe, Umwandlung in den HSL-Raum, Helligkeit auf 50% setzen,
 * zurück in den HSV-Raum umwandeln, Ausgabe
 */
color = PerceptualColor.fromHSV(0, .7, .9);
trace(color.hue, color.saturation, color.value); // Ausgabe: "0 0.7 0.9"
color.mode = PerceptualColor.HSL;
trace(color.hue, color.saturation, color.lightness); // Ausgabe: "0 0.755 0.582"
color.lightness = 0.5;
color.mode = PerceptualColor.HSV;
trace(color.hue, color.saturation, color.value); // Ausgabe: "0 0.861 0.874"

Download

Download: PerceptualColor.as (10,4 kB)