Un billet de blog inspiré d’un tweet et d’une vidéo YouTube.
‘Luminance-gradient-dependent lightness illusion’
Table des matières
Ces derniers jours, je suis tombé sur ce tweet :
A demo of lightness perception pic.twitter.com/BSVpgcuIw1
— Akiyoshi Kitaoka (@AkiyoshiKitaoka) August 12, 2018
Ici, une démonstration de la façon dont notre perception de la couleur est affectée par la luminance qui entoure cette couleur.
Récemment également, j’ai aussi regardé les enregistrement de useR 2018 sur YouTube, contenant la vidéo The Grammar of Animation, un talk présentant {gganimate}
un package de Thomas Lin Pedersen qui étend la « grammar of graphics » {ggplot2}
à l’animation.
Si vous souhaitez en savoir plus sur {gganimate}
, rendez-vous sur le GitHub repo, et sur YouTube :
Retour à notre perception de la luminance : comme vous pouvez le voir, l’illusion est faite à la main, avec un morceau de papier, en déplaçant le carré manuellement. Recréons cela avec gganimate
.
Le code R
Un data.frame
Donc, nous aurons besoin de trois choses :
- Une variable indiquant tous les états de transition
- Une variable pour y min et max, qui restera la même
- Une variable pour x min et max, qui sera incrémentée de 1 sur chaque état de transition
d <- data.frame(
# x coordinates
x1 = 1:10, x2 = 2:11,
# y
y1 = 4, y2 = 5,
# transition time
t = 1:10
)
Le background
Le fond est un gradient de #000000
à #FFFFFF
(rendez-vous ici pour plus d’infos sur la notation hexadécimale des couleurs). Créons cet objet :
library(grid)
g <- rasterGrob(
# Creating the color gradient
t(colorRampPalette(c("#000000", "#FFFFFF"))(1000)),
# Scaling it to fit the graph
width= unit(1,"npc"), height = unit(1,"npc")
)
Création du ggplot
D’abord, le ggplot
object, composé de 10 carrés, tous remplis avec le même gris : "#7E7E7E"
. J’utilise theme_nothing()
de {ggmap}
comme thème vide.
library(ggplot2)
gg <- ggplot() +
annotation_custom(g , -Inf, Inf, -Inf, Inf) +
geom_rect(data=d,
mapping=
aes(xmin=x1,
xmax=x2,
ymin=y1,
ymax=y2),
color="black", fill = "#7E7E7E") +
ylim(c(1,8)) +
ggmap::theme_nothing()
gg
Animation is illusion
Animons maintenant le graph pour créer l’illusion. Comme je veux que le déplacement soit linéaire, j’utilise la combinaison de transition_time
et ease_aes('linear')
pour rendre la transition fluide.
library(gganimate)
gg_animated <- gg +
transition_time(t) +
ease_aes('linear')
et tadaa !
gg_animated
Sur cette animation, le carré semble changer de couleur. Mais ce n’est pas le cas : le remplissage est toujours « #7E7E7E7E7E ».
Ce qu’il y a derrière
Luminance et perception de la couleur
“Every light is a shade, compared to the higher lights, till you come to the sun; and every shade is a light, compared to the deeper shades, till you come to the night.” —John Ruskin, 1879.
OK, oublions R pour nous concentrer sur ce qui se passe ici, et parlons rapidement de la perception de la luminance (le niveau de lumière qui arrive à l’œil) et de la couleur.
Nous sommes ici face à un phénomène connu sous le nom d’ « illusion de gradient ». L’idée importante derrière cela est que chaque couleur que nous percevons est influencée par son environnement : en d’autres termes, nous percevons la couleur plus claire à gauche de notre image, en contraste avec le noir. Plus le carré se rapproche du blanc, plus le gris apparaît foncé.
Comment ça marche ? Lorsqu’une couleur arrive à l’œil, vous percevez une certaine quantité de luminance. En d’autres termes, vous êtes capable de dire si quelque chose est sombre ou clair ou quelque part au milieu. Notre capacité à le faire est appelée « lightness consistency », mais ces illusions de gradient nous montrent une chose : cette capacité n’est pas parfaite, et l’ « environnement de luminance » dans lequel la couleur apparaît influence la façon dont nous la voyons.
Un phénomène à connaître
Ainsi, comme nous venons de le voir, la perception de la couleur est influencée par son environnement. Lorsqu’il s’agit de créer une dataviz, les échelles de couleurs sont cruciales — encore plus maintenant que nous connaissons ce phénomène. Imaginons que, pour une raison bizarre, nous avons créé ce plot :
ggplot() +
annotation_custom(g , -Inf, Inf, -Inf, Inf) +
geom_col(aes(x = 1, y = 3), fill = "#7E7E7E") +
geom_col(aes(x = 8, y = 4), fill = "#7E7E7E")
Encore une fois, le remplissage est le même, mais une barre semble être plus foncée que l’autre, ce qui peut amener le lecteur à penser que la valeur des deux n’est pas la même. À considérer s’il y a une chance que votre plot soit imprimé en noir et blanc.
Disons que nous dessinons une carte. Les cartes sont composées de régions et peuvent être colorées selon une échelle spécifique. Mais il y a une probabilité que deux régions ayant le même résultat sur cette échelle soient entourées de deux couleurs opposées. Par exemple, deux #7E7E7E
pourraient être entourées par l’un #515151
, l’autre #aeaeae
.
ggplot() +
geom_rect(aes(xmin = 1, xmax = 4, ymin = 1, ymax = 4), fill = "#515151") +
geom_rect(aes(xmin = 2, xmax = 3, ymin = 2, ymax = 3), fill = "#7E7E7E") +
geom_rect(aes(xmin = 4, xmax = 7, ymin = 1, ymax = 4), fill = "#aeaeae") +
geom_rect(aes(xmin = 5, xmax = 6, ymin = 2, ymax = 3), fill = "#7E7E7E")
Et maintenant ?
- Maintenant que vous connaissez ce phénomène, faites-y attention lorsque vous créez des graphes
- Choisissez vos palettes avec soin
- Essayez de mettre votre graphe en noir et blanc avec
colorspace::desaturate
with_palette <- function(palette) {
x <- y <- seq(-8 * pi, 8 * pi, len = 40)
r <- sqrt(outer(x^2, y^2, "+"))
filled.contour(cos(r^2) * exp(-r / (2 * pi)),
axes = FALSE,
color.palette = palette,
asp = 1
)
}
with_palette(
purrr:::compose(
colorspace::desaturate,
viridis::viridis
)
)