Il existe plusieurs façons pour définir un espace mathématique pour les couleurs. Ici, nous allons en présenter quelques-uns parmi les plus répandus.
L’idée de ce document est de comprendre les outils mathématiques qui permettent de répondre à ce type de questions :
Quelle est la différence/lien entre les systèmes RGB et le CMY ?
Quelles couleurs vais-je obtenir en choisissant les triplets (0, 0, 0), (1/3, 1/3, 1/3), (1, 1, 1), (1, 1/2, 0), etc en RGB vs CMY ?
Qu’est-ce que le cercle chromatique ?
Qu’est ce que le triangle de Maxwell ?
Qu’est ce que la chromaticité, la luminosité, la saturation ?
Dans ce document, on a utilisé les packages R suivants:
install.packages(
c("cartography", # réalisations de cartes
"compositions", # manipulations de données de compositions
"GGally", # graphique de corrélation
"sp", # données spatiales (ancienne norme)
"sf", # données spatiales (nouvelle norme)
"spdep", # matrices de voisinages
"tidyvserse", # utilisation de graphes à la ggplot2
"wesanderson" # palette de couleurs
)
Tobias Mayer (1723-1762) a été un des premiers à essayer de quantifier le nombre possible de couleurs. Dans son système, il utilise les 3 couleurs primaires dans la synthèse soustractive (jaune, bleu, rouge) et va faire des parts (en base 12) de chaque couleur et mélanger ces parts de telle sorte que la somme vaut 12. Dans son système, il appelle R (röte) pour rouge, G (gelb) pour jaune et B (blau) pour bleu.
Exemple : r4g3b5 indique \(4/12\) de rouge, \(3/12\) de jaune et \(5/12\) de bleu. r11g1 indique \(11/12\) de rouge, \(1/12\) de jaune (et donc \(0/12\) de bleu). Il y a ainsi \(13+12+\ldots+1=91\) couleurs possibles.
Il a ajouté une dimension supplémentaire, pour ajouter soit du blanc, soit du noir. Par r3g2b4w3 pour ajouter une composante de blanc ou r6g1b3k2 pour ajouter une composante de noir, ce qui crée 819 couleurs.
Myaer a été suivi par Johann Heinrich Lambert (1728-1777), mais qui s’est limité à un découpage de chaque couleur en 7 et n’utilise pas la dimension de la couleur noir arguant que des nuances de gris apparaîssent déjà dans le triangle.
Les longueurs d’onde de la lumière ont différentes couleurs et chacune de ces ondes est une source monochromatique. Par exemple, la lumière rouge vaut \(\lambda=480nm\) et la lumière pourpre vaut \(\lambda=420nm\). Toutefois, ces sources monochromatiques ne contiennent pas l’ensemble des couleurs perçues. Par exemple, la couleur magenta ne correspond à aucune longueur d’onde.
Newton a été le premier à comprendre que la lumière blanche était une combinaison de plusieurs longueurs d’onde et dans ces expériences, il était capable de séparer certaines couleurs de la lumière à l’aide de lentilles et de mirroirs.
Grassmann était intrigué par l’idée que deux couleurs mélangées puissent produire une couleur différente, telle que rouge + bleu = magenta. Dans ce dernier cas, il est à noter que la couleur créée ne correspond à aucune longueur d’onde. Il a ainsi montré que dans une équation, il était possible d’ajouter une couleur des deux côtés et qu’il y avait toujours un résultat, comme rouge+bleu+jaune=magenta+jaune
Dans le graphique suivant, on montre quelles sont les couleurs de la lumière en fonction de la longueur d’onde :
inline <- function(wave_length_nm, my_gamma = 1, intensity_max = 1) {
red <- 0
green <- 0
blue <- 0
if ((380.0 <= wave_length_nm) && (wave_length_nm <= 439.0)) {
red <- -(wave_length_nm - 440.0) / (440.0 - 380.0)
green <- 0.0
blue <- 1.0
} else if ((440.0 <= wave_length_nm) && (wave_length_nm <= 489.0)) {
red <- 0.0
green <- (wave_length_nm - 440.0) / (490.0 - 440.0)
blue <- 1.0
} else if ((490.0 <= wave_length_nm) && (wave_length_nm <= 509.0)) {
red <- 0.0
green <- 1.0
blue <- -(wave_length_nm - 510.0) / (510.0 - 490.0)
} else if ((510.0 <= wave_length_nm) && (wave_length_nm <= 579.0)) {
red <- (wave_length_nm - 510.0) / (580.0 - 510.0)
green <- 1.0
blue <- 0.0
} else if ((580.0 <= wave_length_nm) && (wave_length_nm <= 644.0)) {
red <- 1.0
green <- -(wave_length_nm - 645.0) / (645.0 - 580.0)
blue <- 0.0
} else if ((645.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) {
red <- 1.0
green <- 0.0
blue <- 0.0
}
if ((380.0 <= wave_length_nm) && (wave_length_nm <= 419.0)) {
factor = 0.3 + 0.7 * (wave_length_nm - 380.0) / (420.0 - 380.0)
} else if ((420.0 <= wave_length_nm) && (wave_length_nm <= 700.0)) {
factor = 1.0
} else if ((701.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) {
factor = 0.3 + 0.7 * (780.0 - wave_length_nm) / (780.0 - 700.0)
} else {
factor = 0.0
}
red <- ifelse(red == 0.0, red, intensity_max * (red * factor) ^ my_gamma)
green <- ifelse(green == 0.0, green, intensity_max * (green * factor) ^ my_gamma)
blue <- ifelse(blue == 0.0, blue, intensity_max * (blue * factor) ^ my_gamma)
#undef round
return(rgb(red, green, blue))
}
par(oma = c(2, 0, 0, 0), mar = c(2, 0, 0, 0))
plot(0, 0, type = "n", yaxt = "n",
ylim = c(0, 1), xlim = c(375, 725))
for(i in 380:725) {
segments(i, 0, i, 0.95, col = inline(i), lwd = 2)
}
Un certain nombre d’expériences ont consisté à reproduire chaque couleur du spectre lumineux comme étant une combinaison de trois longueurs d’onde.
Ce système proposé par la “Comission Internationale de l’éclairage” en 1931 permet de concilier la façon dont l’être humain perçoit les couleurs et la façon dont elle sont définies en longueur d’ondes \(\lambda\). Ce système a donc été réalisé à partir d’expériences empiriques réalisées sur des observateurs.
Au début du 20e siècle, John Guild et William David Wright ont conduit des expériences en France dans lesquelles ils présentaient des combinaisons de trois couleurs du spectre (le rouge, le vert et le bleu) avec des proportions différentes dans le but de les faire correspondre avec des couleurs de la lumière blanche (on pourra voir la page https://michaelbach.de/ot/col-match/index.html pour un exemple interractif d’ajustements de couleurs).
Par exemple, dans la figure suivante, on cherche à reproduire la couleur jaune du spectre et l’utilisateur doit ajuster l’intensité des couleurs primaires pour tenter de reproduire au mieux la couleur jaune:
require(sf)
# we define the points as spatial object
point_1 <- data.frame(lat = 0, long = 0 ) %>%
st_as_sf(coords = c("long", "lat"))
point_2 <- data.frame(lat = 0, long = 1 ) %>%
st_as_sf(coords = c("long", "lat"))
point_3 <- data.frame(lat = 0, long = 2) %>%
st_as_sf(coords = c("long", "lat"))
# we create buffers to obtain circles for the primary colors
blue <- st_buffer(point_1, dist = 0.5)
red <- st_buffer(point_2, dist = 0.5)
green <- st_buffer(point_3, dist = 0.5)
# we create the intersections between primary objects
point_4 <- data.frame(lat = -1, long = 1) %>%
st_as_sf(coords = c("long", "lat"))
point_5 <- data.frame(lat = -2, long = 1) %>%
st_as_sf(coords = c("long", "lat"))
mix <- st_buffer(point_4, dist = 0.5, endCapStyle = "SQUARE", joinStyle = "BEVEL")
TC <- st_buffer(point_5, dist = 0.5, endCapStyle = "SQUARE", joinStyle = "BEVEL")
# we plot the object with the colors
par(mfrow = c(1, 2))
plot(st_geometry(blue), col = rgb(0, 0, 200, max = 255), xlim = c(0.5, 1.5), ylim = c(-2.5, 0.5),
bg = rgb(0, 0, 0, max = 255), border = rgb(0, 0, 200, max = 255))
plot(st_geometry(red), col = rgb(200, 0, 0, max = 255),
add = T, border = rgb(200, 0, 0, max = 255))
plot(st_geometry(green), col = rgb(0, 50, 0, max = 255), add = T,
border = rgb(0, 50, 0, max = 255))
plot(st_geometry(mix), col = rgb(200, 50, 200, max = 255), add = T,
border = rgb(200, 50, 200, max = 255))
plot(st_geometry(TC), col = rgb(238, 130, 238, max = 255), add = T,
border = rgb(238, 130, 238, max = 255))
text(st_coordinates(st_centroid(st_geometry(mix))), "Mélange des\n 3 couleurs\n primaires")
text(st_coordinates(st_centroid(st_geometry(TC))), "lambda=445")
plot(st_geometry(blue), col = rgb(0, 0, 238, max = 255), xlim = c(0.5, 1.5), ylim = c(-2.5, 0.5),
bg = rgb(0, 0, 0, max = 255), border = rgb(0, 0, 238, max = 255))
plot(st_geometry(red), col = rgb(238, 0, 0, max = 255),
add = T, border = rgb(238, 0, 0, max = 255))
plot(st_geometry(green), col = rgb(0, 130, 0, max = 255), add = T,
border = rgb(0, 130, 0, max = 255))
plot(st_geometry(mix), col = rgb(238, 130, 238, max = 255), add = T,
border = rgb(238, 130, 238, max = 255))
plot(st_geometry(TC), col = rgb(238, 130, 238, max = 255), add = T,
border = rgb(238, 130, 238, max = 255))
text(st_coordinates(st_centroid(st_geometry(mix))), "Mélange des\n 3 couleurs\n primaires")
text(st_coordinates(st_centroid(st_geometry(TC))), "lambda=445")
Finalement, ils ont représenté dans la figure suivante, pour chaque longueur d’onde (en abscisse), la combinaison nécessaire des 3 couleurs. L’échelle des ordonnées est définie de telle façon que \(\int\bar{r}(\lambda)d\lambda=\int\bar{g}(\lambda)d\lambda=\int\bar{b}(\lambda)d\lambda\) (ce choix se justifie par le fait que la perception visuelle d’un stimulus neutre (couleur blanche) correspond à une réponse équilibrée des trois types de cônes bleu, vert et rouge).
Comment expliquer les valeurs négatives de rouge dans les combinaisons de couleurs pour reproduire certaines longueurs d’ondes ? Pour cela, il faut rappeler une des lois de Grassman.
Loi de Grassman : si deux couleurs sont métamères (elles sont indifférentiables à l’oeil nu mais correspondent à deux longeurs d’onde différentes) et qu’on on ajoute une troisème couleur à chacune d’entre elles, alors le résultat sera deux couleurs métamères.
Par ailleurs, notre perception de la couleur est essentiellement linéaire. Par exemple, pour reproduire la longuer d’onde correspondant au violet, on a :
\[I_{violet}≡0.97I_{bleu}+0.20I_{rouge}+0.02I_{vert}\]
Lors de leurs expérimentations, John Guild et William David Wright se sont rendus compte qu’on ne pouvait pas reproduire certaines couleurs du spectre comme le jaune. En revanche, on ajoutant au jaune du violet, il est possible de trouver une combinaison de couleurs rouge/vert/bleu tels que :
\[I_{jaune}+I_{violet}≡1.3I_{bleu}+0.1I_{rouge}+0.3I_{vert}\] ce qui permet d’écrire le jaune comme étant une conbinaison des trois couleurs rouger/vert/bleu:
\[I_{jaune}≡0.33I_{bleu}-0.1I_{rouge}+0.28I_{vert}\] ce qui explique les valeurs négatives dans la figure précédente.
Pour résumer, Guild, Wright et Grassman ont observé que notre perception des couleurs était de forme linéaire.
Pour éviter les valeurs négatives, il est possible de changer de repères. On va utiliser les fonctions de correspondance des couleurs de l’observateur standard CIE XYZ.
Il est possible de passer du CIE RGB au CIE XYZ en faisant la transformation suivante (voir https://en.wikipedia.org/wiki/CIE_1931_color_space):
\[ \begin{align} \frac{1}{0.17} \begin{pmatrix} 0.49 & 0.31 & 0.2\\ 0.17 & 0.81 & 0.01 \\ 0.00 & 0.01 & 0.99 \end{pmatrix} \times \begin{pmatrix} \bar{r}(\lambda) \\ \bar{g}(\lambda) \\ \bar{b}(\lambda) \end{pmatrix} = \begin{pmatrix} \bar{x}(\lambda) \\ \bar{y}(\lambda) \\ \bar{z}(\lambda) \end{pmatrix} \end{align} \]
Cette transformation plus ou moins abitraire permet que les valeurs de chaque fonction soit positive. Les fonctions \(\bar{x}(\lambda),\bar{y}(\lambda),\bar{z}(\lambda)\) sont la description numérique de la réponse chromatique de l’observateur.
On obtient une valeur approchée de \(\bar x(\lambda), \bar y(\lambda), \bar z(\lambda)\) en utilisant les formules suivantes (voir Wikipedia):
\(\bar{x}(\lambda)=g(\lambda,1.056, 59998, 379, 310) + g(\lambda,0.362, 4420, 160, 267)+g(\lambda, -0.065,5011,204,262)\)
\(\bar{y}(\lambda)=g(\lambda,0.821,5688,469,405) + g(\lambda,0.286,5309,163,311)\)
\(\bar{z}(\lambda)=g(\lambda,1.217,4370,118,360) + g(\lambda,0.681,4590,260,138)\)
avec :
\(g(x,\alpha,\mu,\sigma_1,\sigma_2)=\alpha\exp(\frac{(x-\mu)^2}{-2\sigma^2})\)
gaussian_color <- function(x, a, mu, s1, s2) {
t <- (x - mu) / ifelse(x < mu, s1, s2)
return(a * exp(-(t * t) / 2))
}
lambda <- 380:725
par(las = 1, mar = c(2, 3, 0, 0), oma = c(0, 0, 0, 0))
plot(lambda, gaussian_color(lambda * 10, 1.056, 5998, 379, 310) +
gaussian_color(lambda * 10, 0.362, 4420, 160, 267) +
gaussian_color(lambda * 10, -0.065, 5011, 204, 262), type = "l",
ylab = "", col = "red", ylim = c(0,2))
lines(lambda, gaussian_color(lambda * 10, 0.821, 5688, 469, 405) +
gaussian_color(lambda * 10, 0.286, 5309, 163, 311), col = "green")
lines(lambda, gaussian_color(lambda * 10, 1.217, 4370, 118, 360) +
gaussian_color(lambda * 10, 0.681, 4590, 260, 138), col = "blue")
legend("topright", legend = as.expression(list(bquote(bar(x)(lambda)), bquote(bar(y)(lambda)),
bquote(bar(z)(lambda)))), lty = 1, col = c("red", "green", "blue"))
On peut les considérer comme les courbes de sensibilité spectrale de trois détecteurs linéaires de lumière donnant les valeurs tristimulus CIE X, Y et Z. Collectivement, ces trois fonctions sont connues sous le nom d’observateur standard CIE.
A cause de la linéarité, il existe une définition bien connue de l’intensité de la lumière qui correspond à la somme des intensités de toutes les longueurs d’ondes. Par exemple, pour représenter une simple longueur d’onde, on a :
\[I(\lambda)=Intensity=\bar{x}(\lambda)+\bar{y}(\lambda)+\bar{z}(\lambda)\]
On représente ici les couleurs associées aux longueurs d’onde dans un plan à 3 dimensions:
gaussian_color <- function(x, a, mu, s1, s2) {
t <- (x - mu) / ifelse(x < mu, s1, s2)
return(a * exp(-(t * t) / 2))
}
compute_xyz <- function(lambda) {
c(gaussian_color(lambda, 1.056, 5998, 379, 310) +
gaussian_color(lambda, 0.362, 4420, 160, 267) +
gaussian_color(lambda, -0.065, 5011, 204, 262),
gaussian_color(lambda, 0.821, 5688, 469, 405) +
gaussian_color(lambda, 0.286, 5309, 163, 311),
gaussian_color(lambda, 1.217, 4370, 118, 360) +
gaussian_color(lambda, 0.681, 4590, 260, 138))
}
my_lambda <- 380:700 * 10
my_xyz <- t(sapply(my_lambda, compute_xyz))
library("plot3D")
scatter3D(my_xyz[, 1], my_xyz[, 3], my_xyz[, 2],
colvar = NULL, col = sapply(380:700, inline),
pch = 19, cex = 0.5)
# true value taken in https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxtZXNpbWFnZXNzdG9ja3xneDo3MGMzZTI0OWRkMzAzNTI3
xy <- rbind(
c(380, 0.17411, 0.00496),
c(385, 0.17401, 0.00498),
c(390, 0.17380, 0.00492),
c(395, 0.17356, 0.00492),
c(400, 0.17334, 0.00480),
c(405, 0.17302, 0.00478),
c(410, 0.17258, 0.00480),
c(415, 0.17209, 0.00483),
c(420, 0.17141, 0.00510),
c(425, 0.17030, 0.00579),
c(430, 0.16888, 0.00690),
c(435, 0.16690, 0.00856),
c(440, 0.16441, 0.01086),
c(445, 0.16110, 0.01379),
c(450, 0.15664, 0.01770),
c(455, 0.15099, 0.02274),
c(460, 0.14396, 0.02970),
c(465, 0.13550, 0.03988),
c(470, 0.12412, 0.05780),
c(475, 0.10959, 0.08684),
c(480, 0.09129, 0.13270),
c(485, 0.06871, 0.20072),
c(490, 0.04539, 0.29498),
c(495, 0.02346, 0.41270),
c(500, 0.00817, 0.53842),
c(505, 0.00386, 0.65482),
c(510, 0.01387, 0.75019),
c(515, 0.03885, 0.81202),
c(520, 0.07430, 0.83380),
c(525, 0.11416, 0.82621),
c(530, 0.15472, 0.80586),
c(535, 0.19288, 0.78163),
c(540, 0.22962, 0.75433),
c(545, 0.26578, 0.72432),
c(550, 0.30160, 0.69231),
c(555, 0.33736, 0.65885),
c(560, 0.37310, 0.62445),
c(565, 0.40874, 0.58961),
c(570, 0.44406, 0.55471),
c(575, 0.47877, 0.52020),
c(580, 0.51249, 0.48659),
c(585, 0.54479, 0.45443),
c(590, 0.57515, 0.42423),
c(595, 0.60293, 0.39650),
c(600, 0.62704, 0.37249),
c(605, 0.64823, 0.35139),
c(610, 0.66576, 0.33401),
c(615, 0.68008, 0.31975),
c(620, 0.69150, 0.30834),
c(625, 0.70061, 0.29930),
c(630, 0.70792, 0.29203),
c(635, 0.71403, 0.28593),
c(640, 0.71903, 0.28093),
c(645, 0.72303, 0.27695),
c(650, 0.72599, 0.27401),
c(655, 0.72827, 0.27173),
c(660, 0.72997, 0.27003),
c(665, 0.73109, 0.26891),
c(670, 0.73199, 0.26801),
c(675, 0.73272, 0.26728),
c(680, 0.73342, 0.26658),
c(685, 0.73405, 0.26595),
c(690, 0.73439, 0.26561),
c(695, 0.73459, 0.26541),
c(700, 0.73469, 0.26531),
c(705, 0.73469, 0.26531),
c(710, 0.73469, 0.26531),
c(715, 0.73469, 0.26531),
c(720, 0.73469, 0.26531),
c(725, 0.73469, 0.26531),
c(730, 0.73469, 0.26531),
c(735, 0.73469, 0.26531),
c(740, 0.73469, 0.26531),
c(745, 0.73469, 0.26531),
c(750, 0.73469, 0.26531),
c(755, 0.73469, 0.26531),
c(760, 0.73469, 0.26531),
c(765, 0.73469, 0.26531),
c(770, 0.73469, 0.26531),
c(775, 0.73469, 0.26531),
c(780, 0.73469, 0.26531))
xy <- data.frame(lambda = xy[, 1], x = xy[, 2], y = xy[, 3])
xy_compo <- cbind(xy[, 2], xy[, 3], 1 - xy[, 2] - xy[, 3])
Le modèle CIE capitalise sur ce fait en définissant \(Y\) comme luminance. \(Z\) est quasi-égal à la stimulation bleue, ou à la réponse du cône \(S\), et \(X\) est un mélange (une combinaison linéaire) de courbes de réponse au cône choisies comme non négatives. Les valeurs tristimulus \(XYZ\) sont donc analogues aux réponses du cône LMS de l’oeil humain, mais différentes. Définir \(Y\) comme luminance a le résultat utile que pour toute valeur \(Y\) donnée, le plan \(XZ\) contiendra toutes les chromaticités possibles à cette luminance.
Il est donc possible de se ramener à un plan à deux dimensions pour représenter l’ensemble des chomaticités.
On utilise la relation linéaire entre deux couleurs pour représenter toutes les combinaisons possibles en faisant varier \(\alpha\) entre ces couleurs :
\[x_{mixed}=\alpha x_{red}+(1-\alpha) x_{blue}\] \[y_{mixed}=\alpha y_{red}+(1-\alpha) xy_{blue}\]
Ainsi, en choisissant uniquement 3 couleurs monochromatiques du spectre lumineux, il est possible de représenter toutes les couleurs incluses dans le triangle défini par les 3 longueurs d’ondes choisies et appelé le GAMUT. On notera que pour une couleur donnée, il existe plusieurs perceptions possibles selon notamment la lumière et que toutes ces variations ne sont pas représentées.
library(colorscience)
library(pavo)
# my_xyz_compo <- t(apply(my_xyz, 1, function(x) x / sum(x)))
par(las = 1, mar = c(3, 3, 1, 1), oma = c(0, 0, 0, 0), mfrow = c(2, 2))
# représentation des longueurs d'onde
plot(xy[, 2], xy[, 3], col = sapply(as.data.frame(t(xy_compo)), function(x) rgb(xyz2srgb(x)$sRGB, maxColorValue = 255)),
pch = 16, asp = 1, cex = 1)
# représentation des longueurs d'onde
plot(xy[, 2], xy[, 3], col = sapply(as.data.frame(t(xy_compo)), function(x) rgb(xyz2srgb(x)$sRGB, maxColorValue = 255)),
pch = 16, asp = 1, cex = 1)
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3])
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
segments(xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
# représentation des couleurs entre les trois couleurs primaires
plot(xy[, 2], xy[, 3], col = sapply(as.data.frame(t(xy_compo)), function(x) rgb(xyz2srgb(x)$sRGB, maxColorValue = 255)),
pch = 16, asp = 1, cex = 1)
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3])
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
segments(xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
for(k in 1:100) {
points(xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2],
xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3],
col = rgb(xyz2srgb(cbind((k/100), 0, 1 - k/100))$sRGB, maxColorValue = 255),
cex = 0.5)
points(xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 2],
xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 3],
col = rgb(xyz2srgb(cbind(0, (k/100), 1 - k/100))$sRGB, maxColorValue = 255),
cex = 0.5)
points(xy[xy$lambda == 545, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2],
xy[xy$lambda == 545, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3],
col = rgb(xyz2srgb(cbind((k/100), 1 - k/100, 0))$sRGB, maxColorValue = 255),
cex = 0.5)
}
# représentation des couleurs entre les trois couleurs primaires
coldat <- as.data.frame(matrix(runif(n = 3, min = 0.15, max = 0.5), nrow = 1, ncol = 3))
attr(coldat, "clrsp") <- "CIEXYZ"
colnames(coldat) <- c("x", "y", "z")
cieplot(coldat, cex = 0)
points(xy[, 2], xy[, 3], col = sapply(xy[, 1], inline),
pch = 16, cex = 1)
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3])
segments(xy[xy$lambda == 435, 2], xy[xy$lambda == 435, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
segments(xy[xy$lambda == 545, 2], xy[xy$lambda == 545, 3],
xy[xy$lambda == 700, 2], xy[xy$lambda == 700, 3])
# for(k in 1:100) {
# points(xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2],
# xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3],
# col = rgb(xyz2srgb(cbind((k/100), 0, 1 - k/100))$sRGB, maxColorValue = 255),
# cex = 0.5)
# points(xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 2],
# xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 3],
# col = rgb(xyz2srgb(cbind(0, (k/100), 1 - k/100))$sRGB, maxColorValue = 255),
# cex = 0.5)
# points(xy[xy$lambda == 545, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2],
# xy[xy$lambda == 545, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3],
# col = rgb(xyz2srgb(cbind((k/100), 1 - k/100, 0))$sRGB, maxColorValue = 255),
# cex = 0.5)
#
# pt1_x <- xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2]
# pt1_y <- xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3]
#
# pt2_x <- xy[xy$lambda == 435, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 2]
# pt2_y <- xy[xy$lambda == 435, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 545, 3]
#
# pt3_x <- xy[xy$lambda == 545, 2] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 2]
# pt3_y <- xy[xy$lambda == 545, 3] * (1 - k/100) + k/100 * xy[xy$lambda == 700, 3]
#
# seq_x_1 <- seq(pt1_x, 1/3, length.out = 100)
# seq_y_1 <- seq(pt1_y, 1/3, length.out = 100)
# rgb_col_1 <- cbind(seq((k/100), 1, length.out = 100),
# seq(0, 1, length.out = 100),
# seq(1 - k/100, 1, length.out = 100))
#
# seq_x_2 <- seq(pt2_x, 1/3, length.out = 100)
# seq_y_2 <- seq(pt2_y, 1/3, length.out = 100)
# rgb_col_2 <- cbind(seq(0, 1, length.out = 100),
# seq((k/100), 1, length.out = 100),
# seq(1 - k/100, 1, length.out = 100))
#
# seq_x_3 <- seq(pt3_x, 1/3, length.out = 100)
# seq_y_3 <- seq(pt3_y, 1/3, length.out = 100)
# rgb_col_3 <- cbind(seq((k/100), 1, length.out = 100),
# seq(1 - k/100, 1, length.out = 100),
# seq(0, 1, length.out = 100))
# for(i in 1:100) {
# points(seq_x_1[i], seq_y_1[i], col = rgb(xyz2srgb(t(cbind(rgb_col_1[i, ])))$sRGB, maxColorValue = 255))
# points(seq_x_2[i], seq_y_2[i], col = rgb(xyz2srgb(t(cbind(rgb_col_2[i, ])))$sRGB, maxColorValue = 255))
# points(seq_x_3[i], seq_y_3[i], col = rgb(xyz2srgb(t(cbind(rgb_col_3[i, ])))$sRGB, maxColorValue = 255))
# }
# }
Pour passer des coordonnées XYZ au système RGB tel qu’utilisé dans les écrans, il suffit d’utiliser la conversion suivante (voir https://www.oceanopticsbook.info/view/photometry-and-visibility/from-xyz-to-rgb pour plus d’informations). On va utiliser le package colorscience et la fonction xyz2srgb() qui permet de faire la conversion :
$$ \[\begin{align} \begin{pmatrix} 3.240 & - 1.537 & -0.498\\ -0.969 & 1.876 & 0.041 \\ 0.055 & -0.204 & 1.057 \end{pmatrix} \times \begin{pmatrix} X \\ Y \\ Z \end{pmatrix} = \begin{pmatrix} sR' \\ sG'\\ sB' \end{pmatrix} \end{align}\] $$
En synthèse additive, dans le système RVB (pour Rouge, vert, bleu) ou RGB en anglais, la couleur obtenue résulte du mélange additif des couleurs de départ. “Additif”, dans ce contexte, signifie que l’énergie lumineuse de la couleur résultante est la somme des énergies lumineuses des couleurs de départ. Ainsi, le jaune est une couleur plus énergétique et donc plus lumineuse que le sont le vert et le rouge individuellement.
Exemple d’application : télévision, écran ordinateur
Pour chacune des couleurs primaires, la valeur est soit :
Rappel : en système héxadécimal, un nombre peut s’écrire en base 16 :
\[a_0\times16^0+a_1\times 16^1+a_2\times 16^2+\ldots\]
où \(a_1,a_2,\ldots\) sont des nombres entiers compris entre 0 et 15. Pour représenter un nombre compris entre 0 et 255, on a juste besoin de connaître \(a_0\) et \(a_1\). Par exemple, le nombre 225 s’écrit \(1\times 16^0+14\times 16^1\). Pour faire en sorte que chacun de ces 16 nombres soit codé par un seul chiffre, on fait l’association suivante : 0=“0”, 1=“1”,\(\ldots\) 9=“9”, 10=“A”, 11=“B”, 12=“C”, 13=“D”, 14=“E”, 15=“F”. Ainsi, en remplaçant le 14 par la lettre “E”, le nombre 225 s’écrit “E1” en base hexadécimal (on place d’abord \(a_1\), puis \(a_0\)).
Lorsqu’on utilise une valeur \(p\) en pourcentage, celle-ci sera transformée d’abord en entier compris entre 0 et 255 en prenant l’arrondi de \(p\times255\) pour avoir un entier, puis en système hexadécimal qui sera facilement interprétable par la machine.
Application :
Remarque : les trois primaires en quantité égale codent du gris, au maximum donnent du blanc. Le système produit ainsi 256 à la puissance 3 codes de couleur, soit 16 777 216, trente fois le nombre de couleurs différenciables par l’humain dans de bonnes conditions.
La figure suivante présente dans la synthèse additive les couleurs primaires (rouge, vert, bleu), les couleurs secondaires (magenta, cyan, jaune) ainsi que le noir et le blanc.
library(sf)
library(tidyverse)
# we define the points as spatial object
point_1 <- data.frame(lat = 0, long = 0 ) %>%
st_as_sf(coords = c("long", "lat"))
point_2 <- data.frame(lat = 0, long = 1 ) %>%
st_as_sf(coords = c("long", "lat"))
point_3 <- data.frame(lat = sqrt(3) / 2, long = 1 / 2) %>%
st_as_sf(coords = c("long", "lat"))
# we create buffers to obtain circles for the primary colors
blue <- st_buffer(point_1, dist = 1)
red <- st_buffer(point_2, dist = 1)
green <- st_buffer(point_3, dist = 1)
# we create the intersections between primary objects
magenta <- st_difference(st_intersection(blue, red), green)
cyan <- st_difference(st_intersection(blue, green),red)
yellow <- st_difference(st_intersection(green, red), blue)
white <- st_intersection(st_intersection(green, red), blue)
# we plot the object with the colors
plot(st_geometry(blue), col = rgb(0, 0, 255, max = 255), xlim = c(-1, 2), ylim = c(-1, 2),
bg = rgb(0, 0, 0, max = 255), border = rgb(0, 0, 255, max = 255))
plot(st_geometry(red), col = rgb(255, 0, 0, max = 255),
add = T, border = rgb(255, 0, 0, max = 255))
plot(st_geometry(green), col = rgb(0, 255, 0, max = 255), add = T,
border = rgb(0, 255, 0, max = 255))
plot(st_geometry(magenta), col = rgb(255, 0, 255, max = 255), add = T,
border = rgb(255, 0, 255, max = 255))
plot(st_geometry(cyan), col = rgb(0, 255, 255, max = 255), add = T,
border = rgb(0, 255, 255, max = 255))
plot(st_geometry(yellow), col = rgb(255, 255, 0, max = 255), add = T,
border = rgb(255, 255, 0, max = 255))
plot(st_geometry(white), col = rgb(255, 255, 255, max = 255), add = T,
border = rgb(255, 255, 255, max = 255))
On considère le repère \((0, \vec{R}, \vec{G}, \vec{B})\). On définit une couleur dans ce repère par un triplet d’entiers positifs \((r,g,b)\) où \((r,g,b)\in[0,255]\times[0,255]\times[0,255]\).
Le centre de ce repère est défini par le triplet \((0, 0, 0)\), i.e. la couleur noir.
Remarque : dans la figure ci-dessus, la partie verte peut sembler plus brillante que la couleur bleue ou rouge. Cette notion de brillance aura son importance pour définir d’autres sytèmes de couleurs (par exemple le CIE XYZ ou CIELAB)
Les couleurs primaires définissent 3 sommets du cube :
Les couleurs secondaires définissent 3 autres sommets du cube :
\(\vec{R}\) (255, 0, 0) + \(\vec{G}\) (0, 255, 0)
= \(\vec{G}\) (0, 255, 0) + \(\vec{R}\) (255, 0, 0)
= \(\vec{Y}\) (255, 255, 0)
\(\vec{R}\) (255, 0, 0) + \(\vec{B}\) (0, 0, 255)
= \(\vec{B}\) (0, 0, 255) + \(\vec{R}\) (255, 0, 0)
= \(\vec{M}\) (255, 0, 255)
\(\vec{B}\) (0, 0, 255) + \(\vec{G}\) (0, 255, 0)
= \(\vec{G}\) (0, 255, 0) + \(\vec{B}\) (0, 0, 255)
= \(\vec{C}\) (0, 255, 255)
En additionnant les trois couleurs primaires, on obtient le blanc, le dernier sommet du cube :
\(\vec{R}\) (255, 0, 0) + \(\vec{G}\) (0, 255, 0) + \(\vec{B}\) (0, 0, 255) = \(\vec{W}\) (255, 255, 255)
qui peut être également vu comme une somme d’une couleur primaire et une couleur secondaire :
(\(\vec{R}\) (255, 0, 0) + \(\vec{G}\) (0, 255, 0) ) + \(\vec{B}\) (0, 0, 255)
= \(\vec{Y}\) (255, 255, 0) + \(\vec{B}\) (0, 0, 255)
= \(\vec{W}\) (255, 255, 255)
ou bien :
\(\vec{R}\) (255, 0, 0) + (\(\vec{G}\) (0, 255, 0) + \(\vec{B}\) (0, 0, 255) )
= \(\vec{R}\) (255, 0, 0) + \(\vec{C}\) (0, 255, 255)
= \(\vec{W}\) (255, 255, 255)
ou bien :
(\(\vec{R}\) (255, 0, 0) + \(\vec{B}\) (0, 0, 255) ) + \(\vec{G}\) (0, 255, 0)
= \(\vec{M}\) (255, 0, 255) + \(\vec{G}\) (0, 255, 0)
= \(\vec{W}\) (255, 255, 255)
Remarque : ceci nous permet d’écrire les trois couleurs primaires d’un autre système (CMY) comme étant la soustraction du blanc aux troix couleurs primaires du système (RGB)
\(\vec{C}\) (0, 255, 255) = \(\vec{W}\) (255, 255, 255) - \(\vec{R}\) (255, 0, 0)
\(\vec{M}\) (255, 0, 255) = \(\vec{W}\) (255, 255, 255) - \(\vec{G}\) (0, 255, 0)
\(\vec{Y}\) (255, 255, 0) = \(\vec{W}\) (255, 255, 255) - \(\vec{B}\) (0, 0, 255)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2)
points(c(0.4, 1, 1.2, 0.75, 0.2, 0), c(0, 0.2, 0.8, 1.2, 1, 0.4),
col = c("orange", rgb(0.5, 1, 0), rgb(0, 1, 0.5), rgb(0, 0.5, 1),
rgb(0.5, 0, 1), rgb(1, 0, 0.5)), pch = 16,
cex = 2)
Pour obtenir les couleurs tertiaires, on peut utilise la définition suivante extraite de Wikipedia : “A tertiary color or intermediate color is a color made by mixing full saturation of one primary color with half saturation of another primary color and none of a third primary color, in a given color space such as RGB, CMYK (more modern) or RYB (traditional)”.
\(\vec{R}\) (255, 0, 0) + \(\frac{1}{2}\vec{V}\) (0, 127, 0)
= \(\vec{O}\) (255, 127, 0)
\(\vec{G}\) (0, 255, 0) + \(\frac{1}{2}\vec{R}\) (127, 0, 0)
= \(\vec{Ch}\) (127, 255, 0)
\(\vec{G}\) (0, 255, 0) + \(\frac{1}{2}\vec{B}\) (0, 0, 127)
= \(\vec{Sp}\) (0, 255, 127)
\(\vec{B}\) (0, 0, 255) + \(\frac{1}{2}\vec{G}\) (0, 127, 0)
= \(\vec{Az}\) (0, 127, 255)
\(\vec{B}\) (0, 0, 255) + \(\frac{1}{2}\vec{R}\) (127, 0, 0)
= \(\vec{Vi}\) (127, 0, 255)
\(\vec{R}\) (255, 0, 0) + \(\frac{1}{2}\vec{B}\) (0, 0, 127)
= \(\vec{Ro}\) (255, 0, 127)
On pourrait continuer sur ce principe pour obtenir des couleurs :
quaternaires en appliquant la formule : une couleur primaire + 1/4 ou 3/4 d’une autre couleur primaire (et 0 de la dernière couleur primaire)
quinquénaires en appliquant la formule : une couleur primaire + 1/8 ou 3/8 ou 5/8 ou 7/8 d’une autre couleur primaire (et 0 de la dernière couleur primaire)
et ainsi de suite. Si on applique toutes les couleurs combinaisons possibles en mélange une couleur primaire avec \(\alpha\) d’une autre couleur primaire avec \(\alpha=\frac{k}{255}\) où \(k=1,\ldots,254\), on obtiendrait la figure suivante :
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)), pch = 16,
cex = 2)
for(k in 1:254) {
points(0 + k /255 * 0.8, 0, col = rgb(1, k / 255, 0), pch = 16,
cex = 1)
points(0.4 + k /255 * 0.8, 1.2, col = rgb(0, k / 255, 1), pch = 16,
cex = 1)
points(0, 0 + k /255 * 0.8, col = rgb(1, 0, k / 255), pch = 16,
cex = 1)
points(0 + k / 255 * 0.4, 0.8 + k /255 * 0.4, col = rgb(1 - k / 255, 0, 1), pch = 16,
cex = 1)
points(0.8 + k / 255 * 0.4, k /255 * 0.4, col = rgb(1 - k / 255, 1, 0), pch = 16,
cex = 1)
points(1.2, 0.4 + k /255 * 0.8, col = rgb(0, 1, k / 255), pch = 16,
cex = 1)
}
Il arrive courramment que les couleurs dessinées ci-dessus soient représentées dans un cercle. Par exemple, en plaçant, le point Rouge sur le point (0, 1) du cercle trigonométrique, on obient un disque chromatique où chaque couleur a une forme pure en ce qu’elle s’oppose au blanc, au noir et au gris.
Chacune de ces couleurs (ou teintes) peut être identifiée par son angle \(\theta\in[0, 2\pi]\) dans le cercle.
Le lien inverse entre l’angle \(\theta\) et le triplet RGB est le suivant
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(cos(pi/2 + 5 * pi / 6), cos(5*pi/6 + 5 * pi / 6), cos(7*pi/6 + 5 * pi / 6), cos(3*pi/2 + 5 * pi / 6), cos(11*pi/6 + 5 * pi / 6), cos(pi/6 + 5 * pi / 6)),
c(sin(pi/2 + 5 * pi / 6), sin(5*pi/6 + 5 * pi / 6), sin(7*pi/6 + 5 * pi / 6), sin(3*pi/2 + 5 * pi / 6), sin(11*pi/6 + 5 * pi / 6), sin(pi/6 + 5 * pi / 6)),
col = c("blue", "magenta", "red", "yellow", "green", "cyan"), pch = 16,
cex = 4, xaxt = "n", yaxt = "n", ylim = c(-1, 1), xlim = c(-1, 1), asp = 1)
points(cos(pi/6*c(2, 4, 6, 8, 10, 12) + 5 * pi / 6), sin(pi/6*c(2, 4, 6, 8, 10, 12) + 5 * pi / 6),
col = c(rgb(0, 0.5, 1), rgb(0.5, 0, 1), rgb(1, 0, 0.5), rgb(1, 0.5, 0),
rgb(0.5, 1, 0), rgb(0, 1, 0.5)), pch = 16,
cex = 2)
for(k in 1:255) {
points(cos(pi/2 + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + k/255 * pi/3 + 5 * pi / 6),
col = rgb(k / 255, 0, 1),
pch = 16,
cex = 1)
points(cos(pi/2 + pi/3 + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + pi/3 + k/255 * pi/3 + 5 * pi / 6),
col = rgb(1, 0, 1 - k / 255),
pch = 16,
cex = 1)
points(cos(pi/2 + 2*pi/3 + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + 2*pi/3 + k/255 * pi/3 + 5 * pi / 6),
col = rgb(1, k / 255, 0),
pch = 16,
cex = 1)
points(cos(pi/2 + pi + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + pi + k/255 * pi/3 + 5 * pi / 6),
col = rgb(1 - k / 255, 1, 0),
pch = 16,
cex = 1)
points(cos(pi/2 + 4*pi/3 + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + 4*pi/3 + k/255 * pi/3 + 5 * pi / 6),
col = rgb(0, 1, k/255),
pch = 16,
cex = 1)
points(cos(pi/2 + 5*pi/3 + k/255 * pi/3 + 5 * pi / 6), sin(pi/2 + 5*pi/3 + k/255 * pi/3 + 5 * pi / 6),
col = rgb(0, 1 - k/255, 1),
pch = 16,
cex = 1)
}
for(k in 1:6) {
segments(cos(k*pi/6), sin(k*pi/6), cos(pi + k*pi/6), sin(pi + k*pi/6), lty = 2)
}
Quelques définitions à partir du cercle chromatique :
les couleurs complémentaires sont deux couleurs opposées l’une à l’autre dans le cercle. D’un point de vue mathématiques, si on additionne deux couleurs complémentaires, on obtient le blanc. Par exemple : \(\vec{B}\) (0, 0, 255) + \(\vec{Y}\) (255, 255, 0) = \(\vec{W}\) (255, 255, 255). On a aussi : \(\vec{O}\) (255, 127, 0) + \(\vec{Az}\) (0, 127, 255) = \(\vec{W}\) (255, 255, 255) et ainsi de suite pour chaque couple de couleurs complémentaires.
les couleurs chaudes (comme le jaune, orange et rouge) évoque la chaleur car elles nous font penser au soleil ou au feu.
les couleurs froides (comme le bleu, violet) évoque le froid car elles nous font penser à la mer.
NB : les couleurs que nous avons définies à l’aide du triplet RGB ne correspondent pas nécessairement à la même pigmentation utilisée en peinture; par exemple, le vert en peinture aura l’air beaucoup plus foncé que le Vert (O, 255, 0).
Cette section est majoritairement extraite de Wikipedia : https://fr.wikipedia.org/wiki/Lois_de_Grassmann
Toutefois, nous allons essayer d’illustrer ces lois dans le cube RGB.
Toute sensation colorée \(C\) peut être reproduite par un mélange additif de trois couleurs primaires convenablement choisies \((r,g,b)\).
\(\vec{C} ≡ r\vec{R} + g\vec{G} + b\vec{B}\)
Le symbole \(≡\) est l’égalité de la représentation fondée sur le principe du métamérisme, selon lequel deux couleurs perçues de façon identique n’ont pas nécessairement la même composition spectrale.
Dans l’exemple ci-dessous, on a représenté la couleur \(\vec{C}\) définie par le triplet \((204,77,51)\). Chaque sous-vecteur est représenté par la couleur correspondant à l’intensité de rouge, vert ou bleu.
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
arrows(0.4, 0.4, 0.4 * 0.3, 0.4 * 0.3, col = rgb(0.8, 0, 0))
arrows(0.4 * 0.3, 0.4 * 0.3, 0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3, col = rgb(0, 0.4, 0))
arrows(0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3, 0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3 + 0.8 * 0.2, col = rgb(0, 0, 0.2))
points(0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3 + 0.8 * 0.2, pch = 16, cex = 2, col = rgb(0.8, 0.3, 0.2) )
Il en découle si \(\vec{C_1} ≡ \vec{C_2}\) et \(\vec{C_2} ≡ \vec{C_3}\) alors :
Soient deux couleurs \(\vec{C_1}\) \((r_1,g_1,b_1)\) et \(\vec{C_2}\) \((r_2,g_2,b_2)\), alors la couleur \(C\) obtenu par la synthèse additive est définie par :
\(\vec{C}≡\vec{C_1}+\vec{C_2}\), \((r_1+r_2,g_1+g_2,b_1+b_2)\).
Dans l’exemple ci-dessous, on additionne la couleur \(\vec{C_1}\) définie par le triplet \((204,77,51)\) et la couleur \(\vec{C_2}\) définie par le triplet \((51, 127, 127)\) et on obtient ainsi la couleur \(\vec{C}\) définie par le triplet \((255,204, 178)\)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow= c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.95, 0.95, 0.95)),
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
arrows(0.4, 0.4, 0.4 * 0.3, 0.4 * 0.3, col = rgb(0.8, 0, 0))
arrows(0.4 * 0.3, 0.4 * 0.3, 0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3, col = rgb(0, 0.4, 0))
arrows(0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3, 0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3 + 0.8 * 0.2, col = rgb(0, 0, 0.2))
points(0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3 + 0.8 * 0.2, pch = 16, cex = 2, col = rgb(0.8, 0.3, 0.2) )
text(0.4 * 0.3 + 0.8 * 0.3, 0.4 * 0.3 + 0.8 * 0.2, "C1", pos = 3)
arrows(0.4, 0.4, 0.4 + 0.8 * 0.5, 0.4, col = rgb(0, 0.5, 0))
arrows(0.4 + 0.8 * 0.5, 0.4, 0.4 + 0.8 * 0.5-0.2 * 0.4, 0.4 - 0.2*0.4, col = rgb(0.2, 0, 0))
arrows(0.4 + 0.8 * 0.5-0.2 * 0.4, 0.4 - 0.2*0.4,
0.4 + 0.8 * 0.5-0.2 * 0.4 , 0.4 - 0.2*0.4+ 0.8*0.5, col = rgb(0, 0, 0.5))
points(0.4 + 0.8 * 0.5-0.2 * 0.4 , 0.4 - 0.2*0.4+ 0.8*0.5,
pch = 16, cex = 2, col = rgb(0.2, 0.5, 0.5) )
text(0.4 + 0.8 * 0.5-0.2 * 0.4 , 0.4 - 0.2*0.4+ 0.8*0.5, "C2", pos = 3)
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.95, 0.95, 0.95)),
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
arrows(0.4, 0.4, 0, 0, col = rgb(1, 0, 0))
arrows(0, 0, 0.8 *0.7, 0, col = rgb(0, 0.8, 0))
arrows(0.8*0.8, 0, 0.8*0.8, 0.7*0.8, col = rgb(0, 0, 0.7))
points(0.8*0.8, 0.7*0.8, pch = 16, cex = 2, col = rgb(1, 0.8, 0.7) )
text(0.8*0.8, 0.7*0.8, "C=C1+C2", pos = 3)
Il en découle que si \(\vec{C_1}≡\vec{C_2}\) et \(\vec{C_3}≡\vec{C_4}\), alors :
Si une lumière colorée baisse ou augmente en intensité, il faut, pour l’égaliser modifier les trois primaires dans les mêmes proportions
\(\vec{C'}≡k\vec{C}\), \((kr,kg,kb)\)
Dans l’exemple ci-dessous, on a représenté la couleur \(\vec{C}\) définie par le triplet \((51, 76, 51)\) et on a représenté pour différentes valeurs de \(k\) la couleur résultante. Pour cela on a trouvé les coordonnées du point qui est localisé sur la surface cube, en appliquant la formule \(\frac{255}{\max(r,g,b)}(r,g,b)\), à savoir \((170, 255, 170)\) et on a relié tous les points passant entre le centre du cube et ce point.
Remarque : si on divise chaque valeur \((r,g,b)\) de la droite par la somme \((r+g+b)\), on trouvera la même composition de rouge, vert et bleu, à savoir \((2/7,3/7,2/7)\) dont la somme vaut bien entendu 1. D’un point de vue chromacité, tous les points de la droite ont la même couleur. Il faut imaginer qu’en pleine lumière du jour, on pourrait visualiser le point de saturation maximale localisée sur la surface du cube, mais que si la lumière diminuait (nuage ou coucher du soleil), on aurait une perception de cette couleur qui change et se rapprocherait du point noir (obscurité totale).
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow",
rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
arrows(0.4, 0.4, 0.4 * 0.8, 0.4 * 0.8, col = rgb(0.2, 0, 0))
arrows(0.4 * 0.8, 0.4 * 0.8, 0.8 * 0.3 + 0.4 * 0.8, 0.4 * 0.8, col = rgb(0, 0.4, 0))
arrows(0.8 * 0.3 + 0.4 * 0.8, 0.4 * 0.8,
0.8 * 0.3 + 0.4 * 0.8, 0.4 * 0.8 + 0.8 * 0.2, col = rgb(0, 0, 0.2))
points(0.8 * 0.3 + 0.4 * 0.8, 0.4 * 0.8 + 0.8 * 0.2, pch = 16, cex = 2, col = rgb(0.2, 0.3, 0.2))
points(seq(0.4, 0.4 * 1/3 + 0.8, length.out = 100),
seq(0.4, 0.4 * 1/3 + 0.8 * 2/3, length.out = 100), pch = 16, cex = 0.8,
col = rgb(seq(0, 2/3, length.out = 100), seq(0, 1, length.out = 100),
seq(0, 2/3, length.out = 100)))
points(0.4 * 1/3 + 0.8, 0.4 * 1/3 + 0.8 * 2/3, pch = 16, cex = 2, col = rgb(2/3, 1, 2/3))
A partir des lois présentées ci-dessus, on peut se demander que se passerait-il si en additionant deux couleurs, on sortait du cube. La réponse a été trouvée dans https://www.rit.edu/cos/colorscience/rc_faq_all.php#925. L’expérience consiste à supperposer deux spots lumineux sur un écran blanc, l’un présentant la couleur cyan \((0, 255, 255)\), l’autre la couleur magenta \((255, 0, 255)\). En utilisant le deuxième loi de Grassman, on peut prédire que la couleur résultante sera le triplet \((255,255,510)\). Or, il n’est pas possible de représenter une valeur supérieur à 255. On peut donc utiliser la troisième loi de Grassman et trouver la valeur \(k\) tel que chaque valeur de \((r,g,b)\) soit compris dans l’intervalle 0 et 255. Ici, il suffit de prendre \(k=\frac{\max(r,g,b)}{255}=1/2\) et la couleur sera ainsi représentée par le triplet \((127, 127, 255)\)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow",
rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.65), xlim = c(0, 1.25), asp = 1)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
arrows(0.4, 0.4, 0, 0.8, col = "magenta")
arrows(0, 0.8, 0.8, 1.6, col = "cyan")
arrows(0.4, 0.4, 0.8, 1.6, lty = 2)
points(0.6, 1, pch = 16, cex = 2, col = rgb(0.5, 0.5, 1))
Remarque : la perception d’une couleur n’est pas transitive. En effet, en terme de couleur on a \((Rouge+Rouge)+Vert=Rouge+Vert=Jaune\)
alors que :
\(Rouge+(Rouge+Vert)=Rouge+Jaune=Orange\)
En fait, il faut penser une lumière comme une énergie lumineuse et lorsqu’on additionne plusieurs sources lumineuses, il faut les additionner en gardant le principe de transitivité
(\(\vec{R}\) (255, 0, 0) + \(\vec{R}\) (255, 0, 0) ) + \(\vec{V}\) (0, 255, 0)
= \(2\vec{R}\) (510, 0, 0) + \(\vec{V}\) (0, 255, 0)
= \(\vec{Orange}\) (510, 255, 0)
où la lumière (510, 255, 0) est perçue comme la couleur (255, 127, 0)
\(\vec{R}\) (255, 0, 0) + (\(\vec{R}\) (255, 0, 0) + \(\vec{V}\) (0, 255, 0))
= \(\vec{R}\) (255, 0, 0) + \(\vec{Yellow}\) (255, 255, 0)
= \(\vec{Orange}\) (510, 255, 0)
La droite passant par les points Noir \((0,0,0)\) et Blanc \((1,1,1)\) est appelée axe des gris, axe des couleurs neutres ou encore axe achromatique. Les points de cette droite représentent des nuances de gris allant du noir au blanc.
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
for(k in 1:254) {
points(0.4 + k /255 * 0.4, 0.4 + k /255 * 0.4, col = rgb(k / 255, k/ 255, k / 255),
pch = 16,
cex = 1)
}
Deux couleurs peuvent posséder le même caractère chromatique (un point du cercle chromatique), mais avoir des composantes trichomatiques différentes à cause de leur luminance.
La notion de luminance est directement liée à la troisième loi de Grassman : pour modifier la luminosité d’une couleur, il faut faire varier le niveau des trois primaires dans la même proportion (voir http://voc500.be/textes/coulumsat.asp).
Par exemple, on part de la couleur définie par le triplet \((130,196,108)\) pour laquelle on représente tous les différents degrés de luminosité possible en allant du plus foncé (le noir absolu) au plus brillant.
Une façon d’y arriver mathématiquement et de considérer tous les triplets \(k\times(130,196,108)\) où \(k\) est défini tel que \(k\times 196\leq255\)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow",
rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
#arrows(0.4, 0.4, 0.4 * 0.49, 0.4 * 0.49, col = rgb(0.51, 0, 0))
#arrows(0.4 * 0.49, 0.4 * 0.49, 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49, col = rgb(0, 0.77, 0))
#arrows(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49,
# 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42, col = rgb(0, 0, 0.42))
points(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42,
pch = 16, cex = 2, col = rgb(0.51, 0.77, 0.42))
points(seq(0.4, 0.8 * 0.66 + 0.4, length.out = 100),
seq(0.4, 0.4 * 0.33 + 0.8 * 0.55, length.out = 100), pch = 16, cex = 0.8,
col = rgb(seq(0, 0.66, length.out = 100), seq(0, 1, length.out = 100),
seq(0, 0.55, length.out = 100)))
points(0.8 * 0.66 + 0.4, 0.4 * 0.33 + 0.8 * 0.55, pch = 16, cex = 2, col = rgb(0.66, 1, 0.55))
Remarque : en prenant la luminosité maximale, on va nécessairement projet sur une des faces du cubes : RMWY (rouge-magenta-white-yellow) qui vérifie \(R=1\), YWCG (yellow-white-cyan-green) qui vérifie \(G=1\) ou MWCB (magenta-white-cyan-blue) qui vérivie \(B=1\). On a représentée ci-dessous ces trois faces de cube qui correspondent donc aux couleurs les plus éclairées.
png("figures/clair.png")
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(j in 0:255) {
for(k in 0:255) {
points(0.8 + k / 255 * 0.4, 0.8 *(255 - j)/255 + k /255 * 0.4, col = rgb(1 - k / 255, 1, 1 - j / 255), pch = 16,
cex = 1)
points(k /255 * 0.8, 0.8 - (j)/255*0.8, col = rgb(1, k / 255, 1 - j / 255), pch = 16,
cex = 1)
points(0.8 - j/255 * 0.8 + k / 255 * 0.4, 0.8 + k /255 * 0.4, col = rgb(1 - k / 255, 1-j/255, 1), pch = 16,
cex = 1)
}
}
#arrows(0.4, 0.4, 0, 0, lty = 2)
#arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
#arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
text(0, 0, "R(1,0,0)")
text(1.2, 0.4, "G(0,1,0)")
text(0.4, 1.2, "B(0,0,1)")
text(0.8, 0.8, "W(1,1,1)")
text(0.8, 0, "Y(1,1,0)")
text(1.2, 1.2, "C(0,1,1)")
text(0, 0.8, "M(1,0,1)")
dev.off()
Pour la saturation, il s’agit de prendre la perpendulaire à la droite des gris qui passent par le point \((130,196,108)\) et de la projeter sur la face du cube de la composante la plus faible (ici le bleu). Autrement dit, soit on se rapproche de la droite des gris pour atteindre un point de chromacité neutre (ici le point \((196,196,196)\)), soit on s’en éloigne pour atteindre le point de saturation maximale (ici le point sur le plan \(B=0\))
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow",
rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
points(seq(0.4, 0.8, length.out = 100), seq(0.4, 0.8, length.out = 100),
col = rgb(seq(0, 1, length.out = 100),
seq(0, 1, length.out = 100),
seq(0, 1, length.out = 100)))
#arrows(0.4, 0.4, 0.4 * 0.49, 0.4 * 0.49, col = rgb(0.51, 0, 0))
#arrows(0.4 * 0.49, 0.4 * 0.49, 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49, col = rgb(0, 0.77, 0))
#arrows(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49,
# 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42, col = rgb(0, 0, 0.42))
points(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42,
pch = 16, cex = 2, col = rgb(0.51, 0.77, 0.42))
#points(seq(0.4, 0.8 * 0.66 + 0.4, length.out = 100),
# seq(0.4, 0.4 * 0.33 + 0.8 * 0.55, length.out = 100), pch = 16, cex = 0.8,
# col = rgb(seq(0, 0.66, length.out = 100), seq(0, 1, length.out = 100),
# seq(0, 0.55, length.out = 100)))
points(seq(0.4 + 0.4 * 0.76, 0.4 * 0.81 + 0.8*0.76, length.out = 100),
seq(0.4 + 0.4 * 0.76, 0.4 * 0.81, length.out = 100), pch = 16,
col = rgb(seq(196, 49, length.out = 100),
seq(196, 196, length.out = 100),
seq(196, 0, length.out = 100), maxColorValue = 255))
points(0.4 * 0.81 + 0.8*0.76, 0.4 * 0.81, pch = 16, cex = 2,
col = rgb(49, 196, 0, maxColorValue = 255))
Remarque : en considérant la saturation maximale, on tombe nécessairement sur une des faces du cube RMBB (red-magenta-blue-black) défini par le plan \(G=0\), BBCG (clack-blue-cyan-green) défini par le plan \(R=0\) ou RBGY (red-black-green-yellow) défini par le plan \(B=0\). On a représenté les trois faces du cube de saturation maximale
png("figures/sombre.png")
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(j in 0:255) {
for(k in 0:255) {
points(0 + k / 255 * 0.4, 0.8 *(255 - j)/255 + k /255 * 0.4, col = rgb(1 - k / 255, 0, 1 - j / 255), pch = 16,
cex = 1)
points(0.4 + k /255 * 0.8, 1.2 - (j)/255*0.8, col = rgb(0, k / 255, 1 - j / 255), pch = 16,
cex = 1)
points(0.8 - j/255 * 0.8 + k / 255 * 0.4, k /255 * 0.4, col = rgb(1 - k / 255, 1-j/255, 0), pch = 16,
cex = 1)
}
}
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
#segments(0.8, 0, 0.8, 0.8)
#segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
#segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
dev.off()
Si on fait l’intersection entre les couleurs de luminosité maximale et les couleurs de saturation maximale, on tombe sur les couleurs du cercle chromatique.
Il est donc possible de voir n’importe quelle couleur du cube RGB comme le choix d’une couleur pure (choix sur le cercle chormatique) sur laquelle on peut faire varier la luminosité et la saturation.
Par exemple, sur l’exemple précédent, on retrouve la couleur pure associée au triplet \((130,196,108)\) en ajoutant de la luminosité maximale (à savoir le point \((169,255,140)\)) et la luminosité en même temps en partant du triplet \((130,196,108)\), on obtient la figure suivante :
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow",
rgb(0.95, 0.95, 0.95)), pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", rgb(0.9, 0.9, 0.9)),
pch = 16,
cex = 2)
#arrows(0.4, 0.4, 0.4 * 0.49, 0.4 * 0.49, col = rgb(0.51, 0, 0))
#arrows(0.4 * 0.49, 0.4 * 0.49, 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49, col = rgb(0, 0.77, 0))
#arrows(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49,
# 0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42, col = rgb(0, 0, 0.42))
#points(0.8 * 0.77 + 0.4 * 0.49, 0.4 * 0.49 + 0.8 * 0.42,
# pch = 16, cex = 2, col = rgb(0.51, 0.77, 0.42))
col_grey_x <- seq(0.4, 0.8, length.out = 100)
col_grey_y <- seq(0.4, 0.8, length.out = 100)
points(col_grey_x, col_grey_y,
col = rgb(seq(0, 1, length.out = 100),
seq(0, 1, length.out = 100),
seq(0, 1, length.out = 100)))
light_1 <- seq(0.4, 0.8 * 0.66 + 0.4, length.out = 100)
light_2 <- seq(0.4, 0.4 * 0.33 + 0.8 * 0.55, length.out = 100)
col_light <- cbind(seq(0, 0.66, length.out = 100), seq(0, 1, length.out = 100),
seq(0, 0.55, length.out = 100))
#dark_line_x <- seq(0, )
#dark_line_y <- seq(0, )
points(0.75 * 0.4 + 0.8, 0.75 * 0.4, col = rgb(0.25, 1, 0), pch = 16, cex = 2)
z_0_x <- seq(0.4, 0.75 * 0.4 + 0.8, length.out = 100)
z_0_y <- seq(0.4, 0.75 * 0.4, length.out = 100)
col_z <- cbind(seq(0, 0.25, length.out = 100),
seq(0, 1, length.out = 100),
0)
for(k in 1:100) {
points(light_1[k],
light_2[k], pch = 16, cex = 0.8,
col = rgb(t(col_light[k, ])))
#points(seq(0.4 + 0.4 * k/100, dark_line_x[k], 0.4 * 0.81 + 0.8*0.76, length.out = 100),
# seq(0.4 + 0.4 * k/100, dark_line_y[k], 0.4 * 0.81, length.out = 100), pch = 16,
# col = rgb(seq(196, 49, length.out = 100),
# seq(196, 196, length.out = 100),
# seq(196, 0, length.out = 100), maxColorValue = 255))
points(seq(col_grey_x[k], z_0_x[k], length.out = 100), seq(col_grey_y[k], z_0_y[k], length.out = 100),
col = rgb(seq((k-1)/100, col_z[k, 1], length.out = 100),
seq((k-1)/100, col_z[k, 2], length.out = 100),
seq((k-1)/100, col_z[k, 3], length.out = 100)))
}
Autres exemples
On a représenté ci-dessous pour le magenta et pour le vert-jaune, toutes les perceptions possibles en faisant varier la luminosité et la saturation.
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(0, 0.8, col = rgb(1, 0, 1), pch = 16, cex = 1.5)
points(0.6, 0.6, col = rgb(0.5, 0.5, 0.5), pch = 16, cex = 1.5)
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
for(i in 1:254) {
points(0 + i /255 * 0.4, 0.8 - i /255 * 0.4, col = rgb(1 - i / 255, 0, 1 - i / 255),
pch = 16,
cex = 1)
points(0 + i /255 * 0.8, 0.8, col = rgb(1, i / 255, 1),
pch = 16,
cex = 1)
# rose claire
x_seq <- seq((0 + i /255 * 0.4), 0.6, length.out = 127)
y_seq <- seq((0.8 - i /255 * 0.4), 0.6, length.out = 127)
col_seq_r <- seq(1 - i / 255, 1/2, length.out = 127)
col_seq_g <- seq(0, 1/2, length.out = 127)
col_seq_b <- seq(1 - i / 255, 1/2, length.out = 127)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
# rose foncé
x_seq <- seq(0 + i /255 * 0.8, 0.6, length.out = 127)
y_seq <- seq(0.8, 0.6, length.out = 127)
col_seq_r <- seq(1, 1/2, length.out = 127)
col_seq_g <- seq(i / 255, 1/2, length.out = 127)
col_seq_b <- seq(1, 1/2, length.out = 127)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(1, 0.2, col = rgb(1/2, 1, 0), pch = 16, cex = 1.5)
points(0.6, 0.6, col = rgb(0.5, 0.5, 0.5), pch = 16, cex = 1.5)
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
for(k in 1:254) {
points(1 - k /255 * 0.6, 0.2 + k /255 * 0.2, col = rgb(0.5 - k / 255 * 0.5, 1 - k / 255, 0),
pch = 16,
cex = 1)
points(1 - k /255 * 0.2, 0.2 + k /255 * 0.6, col = rgb(0.5 + k / 255 * 0.5, 1, k / 255),
pch = 16,
cex = 1)
# vert claire
x_seq <- seq(1 - k /255 * 0.6, 0.6, length.out = 127)
y_seq <- seq(0.2 + k /255 * 0.2, 0.6, length.out = 127)
col_seq_r <- seq(0.5 - k / 255 * 0.5, 1/2, length.out = 127)
col_seq_g <- seq(1 - k / 255, 1/2, length.out = 127)
col_seq_b <- seq(0, 1/2, length.out = 127)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
# rose foncé
x_seq <- seq(1 - k /255 * 0.2, 0.6, length.out = 127)
y_seq <- seq(0.2 + k /255 * 0.6, 0.6, length.out = 127)
col_seq_r <- seq(0.5 + k / 255 * 0.5, 1/2, length.out = 127)
col_seq_g <- seq(1, 1/2, length.out = 127)
col_seq_b <- seq(k / 255, 1/2, length.out = 127)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Dans l’espace RGB, on considère le triangle défini par les sommets RGB du cube. Dans ce triangle, chaque combinaison de couleur a la particularité de respecter l’équation : \(R+G+B=1\) (ici, on a changé l’échelle en considérant que chaque composante va de 0 à 1 au lieu de 0 à 255)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
for(i in 1:254) {
points(0 + i /255 * 1.2, 0 + i /255 * 0.4, col = rgb(1 - i / 255, i / 255, 0),
pch = 16,
cex = 1)
# Triangle
x_seq <- seq(0 + i /255 * 1.2, 0.4, length.out = 255)
y_seq <- seq(0 + i /255 * 0.4, 1.2, length.out = 255)
col_seq_r <- seq(1 - i / 255, 0, length.out = 255)
col_seq_g <- seq(i / 255, 0, length.out = 255)
col_seq_b <- seq(0, 1, length.out = 255)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
# for(k in 85:254) {
# points(0.4 + k /255 * 0.4, 0.4 + k /255 * 0.4, col = rgb(k / 255, k/ 255, k / 255),
# cex = 0.2)
# }
# points(0.4 + 85 /255 * 0.4, 0.4 + 85 /255 * 0.4, col = rgb(85 / 255, 85/ 255, 85 / 255),
# cex = 0.8, pch = 16)
# points(0.4 + 185 /255 * 0.4, 0.4 + 185 /255 * 0.4, col = rgb(185 / 255, 185/ 255, 185 / 255),
# cex = 0.8, pch = 16)
Ce triangle s’apparente au triangle du Maxwell. En réalité, le triangle de Maxwell a été défini de plusieurs façons et ce cas ne réprésente pas nécessairement le plus utilisé. En effet, un point du triangle de Maxwell est supposé donner le pourcentage de chaque couleur primaire. Par exemple le centre du triangle définit le point avec autant de rouge, que de vert et que de bleu \((1/3,1/3,1/3)\). Or, il existe une multitude de couleurs qui vérifient cela, comme par exemple le point \((1, 1, 1)\), i.e. la couleur blanche, le point (2/3, 2/3, 2/3), etc.
Comme vu précédémment, un point qui a la même proportion de rouge, vert et bleu a la même chromaticité. Ainsi, le point blanc (1, 1, 1) a la même chromaticité que le point gris (1/3, 1/3, 1/3).
Dans la figure ci-dessous, on a représenté pour certaines chromaticités les différentes perceptions possibles en fonction de l’intensité (représenté sur l’axe des abscisses) qui vaut \(R+G+B\). On remarque que le point blanc est le point théorique avec le plus d’intensité (1+1+1=3) et correspond à la chromaticité ayant le plus de variantes possibles. Le “cyan” a une intensité égale à 2, et le bleu a une intensité égale à 1.
my_maxwell <- function(r, g, b, imax = FALSE) {
if (!imax){
intensity <- seq(0, 3, length.out = 1000)
candidates <- c(r, g, b)
for (k in intensity) {
r_full <- r * k
g_full <- g * k
b_full <- b * k
if (r_full <= 1 & g_full <= 1 & b_full <= 1) {
candidates <- rbind(candidates,
c(r_full, g_full, b_full))
} else {
break
}
}
return(candidates[-1, ])
} else {
# vectorial
my_max <- apply(cbind(r, g, b), 1, max)
return(cbind(r / my_max, g / my_max, b / my_max))
}
}
intensity <- seq(0, 3, length.out = 1000)
par(oma = c(2, 0, 0, 0), mar = c(2, 0, 0, 0))
plot(0, 3, type = "n", yaxt = "n",
ylim = c(0, 6), xlim = c(0, 3))
res <- rgb(my_maxwell(0, 0, 1))
segments(intensity[1:length(res)], 0, intensity[1:length(res)], 0.95, col = res, lwd = 2)
res <- rgb(my_maxwell(0, 0.25, 0.75))
segments(intensity[1:length(res)], 1, intensity[1:length(res)], 1.95, col = res, lwd = 2)
res <- rgb(my_maxwell(0, 0.5, 0.5))
segments(intensity[1:length(res)], 2, intensity[1:length(res)], 2.95, col = res, lwd = 2)
res <- rgb(my_maxwell(0.2, 0.4, 0.4))
segments(intensity[1:length(res)], 3, intensity[1:length(res)], 3.95, col = res, lwd = 2)
res <- rgb(my_maxwell(0.3, 0.35, 0.35))
segments(intensity[1:length(res)], 4, intensity[1:length(res)], 4.95, col = res, lwd = 2)
res <- rgb(my_maxwell(1/3, 1/3, 1/3))
segments(intensity[1:length(res)], 5, intensity[1:length(res)], 5.95, col = res, lwd = 2)
text(2.5, 0.5, "r=0%,g=0%,b=100%")
text(2.5, 1.5, "r=0%,g=25%,b=75%")
text(2.5, 2.5, "r=0%,g=50%,b=50%")
text(2.5, 3.5, "r=20%,g=40%,b=40%")
text(2.5, 4.5, "r=30%,g=35%,b=35%")
text(2.5, 5.5, "r=33,33%,g=33,33%,b=33,33%")
On a représenté une autre version du triangle de Maxwell où chaque point correspond à une chomaticité avec une intensité maximale.
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
op <- par(oma = c(0, 0, 0, 0), mar = c(0, 0.7, 0.5, 0.7), mfrow = c(1, 2))
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in 1:254) {
my_Y <- cbind(1 - i / 255, i / 255, 0)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , col = rgb(1 - i / 255, i / 255, 0),
pch = 16,
cex = 1)
# Triangle
x_seq <- seq(0 + i /255 * 1.2, 0.4, length.out = 255)
y_seq <- seq(0 + i /255 * 0.4, 1.2, length.out = 255)
col_seq_r <- seq(1 - i / 255, 0, length.out = 255)
col_seq_g <- seq(i / 255, 0, length.out = 255)
col_seq_b <- seq(0, 1, length.out = 255)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
####
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in 1:254) {
my_Y <- cbind(1 - i / 255, i / 255, 0)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , col = rgb(1 - i / 255, i / 255, 0),
pch = 16,
cex = 1)
# Triangle
x_seq <- seq(0 + i /255 * 1.2, 0.4, length.out = 255)
y_seq <- seq(0 + i /255 * 0.4, 1.2, length.out = 255)
col_seq_r <- seq(1 - i / 255, 0, length.out = 255)
col_seq_g <- seq(i / 255, 0, length.out = 255)
col_seq_b <- seq(0, 1, length.out = 255)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(cbind(my_maxwell(col_seq_r, col_seq_g, col_seq_b, imax = T))))
}
Remarque 1 : dans la version de gauche, tous les points ont une inténsité de 1 alors que dans le deuxième on a représenté les chromaticités d’intensité maximale. Chaque point n’a donc pas la même intensité (le point blanc a une inténsité de 3, le cyan ou le magenta de 2, le rouge, bleu ou vert a une intensité égale à 1)
Remarque 2 : dans la version de droite, on a l’impression que les couleurs primaires (rouge, vert et bleu dominent) au détriment des couleurs secondaires. De plus, si on regarde les couleurs tertiaires, elles ne sont pas localisées entre une couleur primaire et une secondaire. Prenons par exemple la couleur Orange qui est défini en mélangeant le rouge avec une moitié de vert et qui est défini par le triplet \((255, 127, 0)\). Dans le cube, cette couleur est située au milieu du rouge et du jaune. Dans le triangle de Maxwell, le Orange est déterminé par la composition \((2/3, 1/3, 0)\) et par conséquent, il est situé sur le segment Rouge-Vert tel que \(Orange=\alpha \times rouge+(1-\alpha)\times vert\) avec \(\alpha=2/3\). Les couleurs tertiaires sont donc plus proche des couleurs secondaires ce qui explique qu’on a l’impression que les couleurs primaires dominent.
On a vu précédemment que le triangle de Maxwell représentait les couleurs telles que \(R+G+B=1\).
D’un point de vue données de composition, on peut choisir la valeur de l’intensité lumineuse \(k\in]0,3]\) et représenter dans le simplex toutes les couleurs qui vérifient \(R+G+B=k\). Ainsi, toutes les couleurs représentées auront la même inténsité lumineuse.
En réalité, on va voir que lorsque \(k>1\), on ne sera pas capable de balayer toutes les valeurs possibles du simplex; pour associer une couleur à chaque donnée de composition, il faudra donc accepter d’avoir des couleurs avec des intensités lumineuses différentes
Cas où \(k\in]0,1]\)
Lorsque \(k=0\), il n’y a qu’un point qui vaut (0,0,0) qu’on ne peut donc pas représenter. Ici, on va représenter dans un triangle l’ensemble des couleurs qui vérifient l’équation : \(R+G+B=\frac{1}{2}\). Dans un premier temps, on normalise par \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\). Ensuite, on représente chaque point \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\) dans le triangle et on l’associe à la couleur \((R,G,B)\).
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in seq(0,1, length.out = 100)) {
points(seq(0.2 + i * 0.6, 0.4, length.out = 100),
seq(0.2 + i * 0.2, 0.8, length.out = 100),
col = rgb(seq(0.5 - i * 0.5, 0, length.out = 100),
seq(0 + i*0.5, 0, length.out = 100),
seq(0, 0.5, length.out = 100)), pch = 16)
}
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
###########################################################
##### Diagramme ternaire
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in seq(0,1, length.out = 100)) {
# Triangle
col_seq_r <- seq(0.5 - i * 0.5, 0, length.out = 100)
col_seq_g <- seq(0 + i*0.5, 0, length.out = 100)
col_seq_b <- seq(0, 0.5, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 0.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Cas où \(k\in]1,2]\)
On va représenter dans un triangle l’ensemble des couleurs qui vérifient l’équation : \(R+G+B=1.5\). Dans un premier temps, on normalise par \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\). Ensuite, on représente chaque point \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\) dans le triangle et on l’associe à la couleur \((R,G,B)\).
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in seq(0,1, length.out = 100)) {
points(seq(0 + i * 0.4, 0.2 + i * 0.6, length.out = 100),
seq(0.4 - i * 0.4, 1 + i*0.2, length.out = 100),
col = rgb(seq(1, 0.5 - i * 0.5, length.out = 100),
seq(0+i*0.5, 0 + i *0.5, length.out = 100),
seq(0.5-i*0.5, 1, length.out = 100)), pch = 16)
points(seq(0.4 + i * 0.6, 0.8 + i * 0.4, length.out = 100),
seq(i * 0.2, 1.2 - i*0.4, length.out = 100),
col = rgb(seq(1 - i * 0.5, 0, length.out = 100),
seq(0.5+i*0.5, 0.5 + i *0.5, length.out = 100),
seq(0, 1-i*0.5, length.out = 100)), pch = 16)
}
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in seq(0,1, length.out = 100)) {
# Triangle
col_seq_r <- seq(1, 0.5 - i * 0.5, length.out = 100)
col_seq_g <- seq(0+i*0.5, 0 + i *0.5, length.out = 100)
col_seq_b <- seq(0.5-i*0.5, 1, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 1.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1 - i * 0.5, 0, length.out = 100)
col_seq_g <- seq(0.5+i*0.5, 0.5 + i *0.5, length.out = 100)
col_seq_b <- seq(0, 1-i*0.5, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 1.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Dans le diagrame ternaire, on voit qu’il nous manque un bout proche des sommets du triangle. En effet, dans le plan \(R+G+B=1.5\), lorsqu’une composante \(R\), \(G\) ou \(B\) vaut 1, cela implique la somme des deux autres composantes vaut 0.5 et que sa composition vaut \(\frac{1}{1+0.5}=2/3\). Autrement dit, \(R\), \(G\) ou \(B\) ne peuvent dépasser la valeur \(2/3\) dans le simplex. On propose donc de remplacer les triangles manquants par les triangles dans le cube ROrRo (Rouge-Orange-Rose), VChSp (Vert-Chartreuse-Spring) et BViAz (Bleu-Violet-Azure) afin d’assurer une continuité en terme de perception de couleurs.
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in seq(0,1, length.out = 100)) {
points(seq(0 + i * 0.4, 0.2 + i * 0.6, length.out = 100),
seq(0.4 - i * 0.4, 1 + i*0.2, length.out = 100),
col = rgb(seq(1, 0.5 - i * 0.5, length.out = 100),
seq(0+i*0.5, 0 + i *0.5, length.out = 100),
seq(0.5-i*0.5, 1, length.out = 100)), pch = 16)
points(seq(0.4 + i * 0.6, 0.8 + i * 0.4, length.out = 100),
seq(i * 0.2, 1.2 - i*0.4, length.out = 100),
col = rgb(seq(1 - i * 0.5, 0, length.out = 100),
seq(0.5+i*0.5, 0.5 + i *0.5, length.out = 100),
seq(0, 1-i*0.5, length.out = 100)), pch = 16)
# trois petits triangles
points(seq(i * 0.4, 0, length.out = 100),
seq(0.4 - i * 0.4, 0, length.out = 100),
col = rgb(seq(1, 1, length.out = 100),
seq(0 + i * 0.5, 0, length.out = 100),
seq(0.5 - i * 0.5, 0, length.out = 100)), pch = 16)
points(seq(1 + i * 0.2, 1.2, length.out = 100),
seq(0.2 + i * 0.6, 0.4, length.out = 100),
col = rgb(seq(0.5 - i * 0.5, 0, length.out = 100),
seq(1, 1, length.out = 100),
seq(0 + i * 0.5, 0, length.out = 100)), pch = 16)
points(seq(0.2 + i * 0.6, 0.4, length.out = 100),
seq(1 + i * 0.2, 1.2, length.out = 100),
col = rgb(seq(0.5 - i * 0.5, 0, length.out = 100),
seq(0+i*0.5, 0, length.out = 100),
seq(1, 1, length.out = 100)), pch = 16)
}
polygon(c(0, 0.4, 0, 0), c(0, 0, 0.4, 0), lty = 2)
text(c(0, 0.4, 0), c(0, 0, 0.4), c("Red", "Or", "Ro"))
polygon(c(1, 1.2, 1.2, 1), c(0.2, 0.4, 0.8, 0.2), lty = 2)
text(c(1, 1.2, 1.2), c(0.2, 0.4, 0.8), c("Ch", "Vert", "Sp"))
polygon(c(0.2, 0.4, 0.8, 0.2), c(1, 1.2, 1.2, 1), lty = 2)
text(c(0.2, 0.4, 0.8), c(1, 1.2, 1.2), c("Vi", "Bleu", "Az"))
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in seq(0,1, length.out = 100)) {
# Triangle
col_seq_r <- seq(1, 0.5 - i * 0.5, length.out = 100)
col_seq_g <- seq(0+i*0.5, 0 + i *0.5, length.out = 100)
col_seq_b <- seq(0.5-i*0.5, 1, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 1.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1 - i * 0.5, 0, length.out = 100)
col_seq_g <- seq(0.5+i*0.5, 0.5 + i *0.5, length.out = 100)
col_seq_b <- seq(0, 1-i*0.5, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 1.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1, 1, length.out = 100)
col_seq_g <- seq(0, 0.5 - i *0.5, length.out = 100)
col_seq_b <- seq(0.5-i*0.5, 0, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(0.5 - i*0.5, 0, length.out = 100)
col_seq_g <- seq(1, 1, length.out = 100)
col_seq_b <- seq(0, 0.5-i*0.5, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(0.5 - i*0.5, 0, length.out = 100)
col_seq_g <- seq(0, 0.5-i*0.5, length.out = 100)
col_seq_b <- seq(1, 1, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Remarque : dans la représentation ci-dessus, on voit bien que les triangles qui ont été ajoutés n’ont pas la même intensité lumineuse que le centre du triangle.
On peut effectuer le même procédé mais pour des valeurs différentes de \(k\in]1,2]\). Par exemple si on prend dans un premier temps le plan défini par \(R+G+B=2\) et qu’on ajoute ensuite les triangles manquants RMJ (rouge-magenta-jaune), JVC (jaune-vert-cyan) et BMC (bleu-magenta-cyan), on obtiendrait la représentation suivante :
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"), type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in seq(0,1, length.out = 100)) {
points(seq(0 + i * 1.2, 0.8, length.out = 100),
seq(0.8 + i * 0.4, 0, length.out = 100),
col = rgb(seq(1 - i, 1, length.out = 100),
seq(0 + i, 1, length.out = 100),
seq(1, 0, length.out = 100)), pch = 16)
# trois petits triangles
points(seq(i * 0.8, 0, length.out = 100),
seq(0.8 - i * 0.8, 0, length.out = 100),
col = rgb(seq(1, 1, length.out = 100),
seq(0 + i , 0, length.out = 100),
seq(1 - i, 0, length.out = 100)), pch = 16)
points(seq(0.8 + i * 0.4, 1.2, length.out = 100),
seq(0 + i * 1.2, 0.4, length.out = 100),
col = rgb(seq(1 - i, 0, length.out = 100),
seq(1, 1, length.out = 100),
seq(0 + i, 0, length.out = 100)), pch = 16)
points(seq(0 + i * 1.2, 0.4, length.out = 100),
seq(0.8 + i * 0.4, 1.2, length.out = 100),
col = rgb(seq(1 - i * 1, 0, length.out = 100),
seq(0+i, 0, length.out = 100),
seq(1, 1, length.out = 100)), pch = 16)
}
polygon(c(0, 0.8, 0, 0), c(0, 0, 0.8, 0), lty = 2)
text(c(0, 0.8, 0), c(0, 0, 0.8), c("Red", "Ye", "Magenta"))
polygon(c(0.8, 1.2, 1.2, 0.8), c(0, 0.4, 1.2, 0), lty = 2)
text(c(0.8, 1.2, 1.2), c(0, 0.4, 1.2), c("", "Vert", "Cyan"))
polygon(c(0, 0.4, 1.2, 0), c(0.8, 1.2, 1.2, 0.8), lty = 2)
text(c(0, 0.4, 1.2), c(0.8, 1.2, 1.2), c("", "Bleu", ""))
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
# create a grid of shares for Y
x_seq <- seq(0.01, 0.99, 0.01)
my_Y <- data.frame(y1 = rep(x_seq, 101),
y2 = rep(x_seq, each = 101))
my_Y$y3 <- 1 - (my_Y$y1 + my_Y$y2)
my_Y <- my_Y[my_Y$y3 > 0, ]
Y_simplex_x <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
aline_1 <- my_Y[which(my_Y[, 1] == my_Y[, 2]), ]
aline_1_x <- Y_simplex_x[which(my_Y[, 1] == my_Y[, 2])]
aline_1_y <- Y_simplex_y[which(my_Y[, 1] == my_Y[, 2])]
bline_2 <- my_Y[which(my_Y[, 1] == my_Y[, 3]), ]
bline_2_x <- c(Y_simplex_x[which(my_Y[, 1] == my_Y[, 3])], 1)
bline_2_y <- c(Y_simplex_y[which(my_Y[, 1] == my_Y[, 3])], 0)
cline_3 <- my_Y[which(my_Y[, 2] == my_Y[, 3]), ]
cline_3_x <- c(0, Y_simplex_x[which(my_Y[, 2] == my_Y[, 3])])
cline_3_y <- c(0,Y_simplex_y[which(my_Y[, 2] == my_Y[, 3])])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in seq(0,1, length.out = 100)) {
# Triangle
col_seq_r <- seq(1 - i, 1, length.out = 100)
col_seq_g <- seq(0 + i, 1, length.out = 100)
col_seq_b <- seq(1, 0, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 2
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1, 1, length.out = 100)
col_seq_g <- seq(0, 1 - i, length.out = 100)
col_seq_b <- seq(1 - i, 0, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1 - i, 0, length.out = 100)
col_seq_g <- seq(1, 1, length.out = 100)
col_seq_b <- seq(0, 1 - i, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
col_seq_r <- seq(1 - i, 0, length.out = 100)
col_seq_g <- seq(0, 1 - i, length.out = 100)
col_seq_b <- seq(1, 1, length.out = 100)
apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Remarque : on a de nouveau le sentiment qu’il y a une discontinuité dans l’intensité lumineuse.
Cas où \(k\in]2,3]\)
On va représenter dans un triangle l’ensemble des couleurs qui vérifient l’équation : \(R+G+B=2.5\). Dans un premier temps, on normalise par \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\). Ensuite, on représente chaque point \((\frac{R}{R+G+B},\frac{G}{R+G+B},\frac{B}{R+G+B})\) dans le triangle et on l’associe à la couleur \((R,G,B)\).
Remarque : Lorsque \(k=3\), il n’y a qu’un seul point qui est le point blanc \((1,1,1)\) et qui sera donc représenté dans le simplex par la valeur \((1/3,1/3,1/3)\).
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"),
type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in seq(0,1, length.out = 100)) {
points(seq(0.4 + i * 0.6, 0.8, length.out = 100),
seq(0.8 + i * 0.2, 0.4, length.out = 100),
col = rgb(seq(1 - i*0.5, 1, length.out = 100),
seq(0.5 + i*0.5, 1, length.out = 100),
seq(1, 0.5, length.out = 100)), pch = 16)
# trois petits triangles
# points(seq(i * 0.8, 0, length.out = 100),
# seq(0.8 - i * 0.8, 0, length.out = 100),
# col = rgb(seq(1, 1, length.out = 100),
# seq(0 + i , 0, length.out = 100),
# seq(1 - i, 0, length.out = 100)), pch = 16)
#
# points(seq(0.8 + i * 0.4, 1.2, length.out = 100),
# seq(0 + i * 1.2, 0.4, length.out = 100),
# col = rgb(seq(1 - i, 0, length.out = 100),
# seq(1, 1, length.out = 100),
# seq(0 + i, 0, length.out = 100)), pch = 16)
#
# points(seq(0 + i * 1.2, 0.4, length.out = 100),
# seq(0.8 + i * 0.4, 1.2, length.out = 100),
# col = rgb(seq(1 - i * 1, 0, length.out = 100),
# seq(0+i, 0, length.out = 100),
# seq(1, 1, length.out = 100)), pch = 16)
}
# polygon(c(0, 0.8, 0, 0), c(0, 0, 0.8, 0), lty = 2)
# text(c(0, 0.8, 0), c(0, 0, 0.8), c("Red", "Ye", "Magenta"))
#
# polygon(c(0.8, 1.2, 1.2, 0.8), c(0, 0.4, 1.2, 0), lty = 2)
# text(c(0.8, 1.2, 1.2), c(0, 0.4, 1.2), c("", "Vert", "Cyan"))
#
# polygon(c(0, 0.4, 1.2, 0), c(0.8, 1.2, 1.2, 0.8), lty = 2)
# text(c(0, 0.4, 1.2), c(0.8, 1.2, 1.2), c("", "Bleu", ""))
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
library("wesanderson")
library("compositions")
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
# create a grid of shares for Y
x_seq <- seq(0.01, 0.99, 0.01)
my_Y <- data.frame(y1 = rep(x_seq, 101),
y2 = rep(x_seq, each = 101))
my_Y$y3 <- 1 - (my_Y$y1 + my_Y$y2)
my_Y <- my_Y[my_Y$y3 > 0, ]
Y_simplex_x <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
aline_1 <- my_Y[which(my_Y[, 1] == my_Y[, 2]), ]
aline_1_x <- Y_simplex_x[which(my_Y[, 1] == my_Y[, 2])]
aline_1_y <- Y_simplex_y[which(my_Y[, 1] == my_Y[, 2])]
bline_2 <- my_Y[which(my_Y[, 1] == my_Y[, 3]), ]
bline_2_x <- c(Y_simplex_x[which(my_Y[, 1] == my_Y[, 3])], 1)
bline_2_y <- c(Y_simplex_y[which(my_Y[, 1] == my_Y[, 3])], 0)
cline_3 <- my_Y[which(my_Y[, 2] == my_Y[, 3]), ]
cline_3_x <- c(0, Y_simplex_x[which(my_Y[, 2] == my_Y[, 3])])
cline_3_y <- c(0,Y_simplex_y[which(my_Y[, 2] == my_Y[, 3])])
#pdf(file = "figures/our_triangle.pdf", width = 8, height = 8)
plot(rbind(R, G, B, W, Y, M, C),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
for(i in seq(0,1, length.out = 100)) {
# Triangle
col_seq_r <- seq(1 - i*0.5, 1, length.out = 100)
col_seq_g <- seq(0.5 + i*0.5, 1, length.out = 100)
col_seq_b <- seq(1, 0.5, length.out = 100)
my_Y <- cbind(col_seq_r, col_seq_g, col_seq_b) / 2.5
Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
cex = 1, col = rgb(col_seq_r, col_seq_g, col_seq_b))
# col_seq_r <- seq(1, 1, length.out = 100)
# col_seq_g <- seq(0, 1 - i, length.out = 100)
# col_seq_b <- seq(1 - i, 0, length.out = 100)
# apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
# my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
#
# Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
# Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
#
#
# points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
# cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
#
#
# col_seq_r <- seq(1 - i, 0, length.out = 100)
# col_seq_g <- seq(1, 1, length.out = 100)
# col_seq_b <- seq(0, 1 - i, length.out = 100)
# apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
# my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
#
# Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
# Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
#
#
# points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
# cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
#
#
# col_seq_r <- seq(1 - i, 0, length.out = 100)
# col_seq_g <- seq(0, 1 - i, length.out = 100)
# col_seq_b <- seq(1, 1, length.out = 100)
# apply_sum <- apply(cbind(col_seq_r, col_seq_g, col_seq_b), 1, sum)
# my_Y <- cbind(col_seq_r / apply_sum, col_seq_g / apply_sum, col_seq_b/apply_sum)
#
# Y_simplex_x_new <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
# Y_simplex_y_new <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
#
# points(Y_simplex_x_new , Y_simplex_y_new , pch = 16,
# cex = 0.8, col = rgb(col_seq_r, col_seq_g, col_seq_b))
}
Pour compléter le vide dans le diagramme ternaire, il suffit dans les faces du cube de luminosité maximale les bouts manquants :
R <- c(0, 0)
G <- c(1, 0)
B <- c(0.5, sqrt(3) / 2)
# create a grid of shares for Y
W <- c(1/3* R[1] + 1/3 * G[1] + 1/3 * B[1],
1/3* R[2] + 1/3 * G[2] + 1/3 * B[2])
Y <- c(0.5, 0)
M <- c(0.5* R[1] + 0 * G[1] + 0.5 * B[1],
0.5* R[2] + 0 * G[2] + 0.5 * B[2])
C <- c(0* R[1] + 0.5 * G[1] + 0.5 * B[1],
0* R[2] + 0.5 * G[2] + 0.5 * B[2])
library(sf)
x_seq <- seq(0.01, 0.99, 0.01)
my_Y <- data.frame(y1 = rep(x_seq, 101),
y2 = rep(x_seq, each = 101))
my_Y$y3 <- 1 - (my_Y$y1 + my_Y$y2)
my_Y <- my_Y[my_Y$y3 > 0, ]
Y_simplex_x <- my_Y[, 1] * R[1] + my_Y[, 2] * G[1] + my_Y[, 3] * B[1]
Y_simplex_y <- my_Y[, 1] * R[2] + my_Y[, 2] * G[2] + my_Y[, 3] * B[2]
sf_pts <- data.frame(long = Y_simplex_x, lat = Y_simplex_y) %>%
st_as_sf(coords = c("long", "lat"))
# triangle RWY
pl1 <- st_polygon(list(rbind(R, W, Y, R)))
# triangle RWM
pl2 <- st_polygon(list(rbind(R, W, M, R)))
# triangle MWB
pl3 <- st_polygon(list(rbind(M, W, B, M)))
# triangle BWC
pl4 <- st_polygon(list(rbind(B, W, C, B)))
# triangle CWG
pl5 <- st_polygon(list(rbind(C, W, G, C)))
# triangle CWG
pl6 <- st_polygon(list(rbind(G, W, Y, G)))
jpeg(file = "figures/triangle_k25.jpeg", width = 920, height = 480)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c("black", "red", "green", "blue", "magenta", "cyan", "yellow", "grey"),
type = "n",
pch = 16, frame = F,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(j in 0:255) {
for(k in 0:255) {
points(0.8 + k / 255 * 0.4, 0.8 *(255 - j)/255 + k /255 * 0.4, col = rgb(1 - k / 255, 1, 1 - j / 255), pch = 16,
cex = 1)
points(k /255 * 0.8, 0.8 - (j)/255*0.8, col = rgb(1, k / 255, 1 - j / 255), pch = 16,
cex = 1)
points(0.8 - j/255 * 0.8 + k / 255 * 0.4, 0.8 + k /255 * 0.4, col = rgb(1 - k / 255, 1-j/255, 1), pch = 16,
cex = 1)
}
}
for(i in seq(0,1, length.out = 100)) {
points(seq(0.4 + i * 0.6, 0.8, length.out = 100),
seq(0.8 + i * 0.2, 0.4, length.out = 100),
col = rgb(seq(1 - i*0.5, 1, length.out = 100),
seq(0.5 + i*0.5, 1, length.out = 100),
seq(1, 0.5, length.out = 100)), pch = 16)
# trois petits triangles
# points(seq(i * 0.8, 0, length.out = 100),
# seq(0.8 - i * 0.8, 0, length.out = 100),
# col = rgb(seq(1, 1, length.out = 100),
# seq(0 + i , 0, length.out = 100),
# seq(1 - i, 0, length.out = 100)), pch = 16)
#
# points(seq(0.8 + i * 0.4, 1.2, length.out = 100),
# seq(0 + i * 1.2, 0.4, length.out = 100),
# col = rgb(seq(1 - i, 0, length.out = 100),
# seq(1, 1, length.out = 100),
# seq(0 + i, 0, length.out = 100)), pch = 16)
#
# points(seq(0 + i * 1.2, 0.4, length.out = 100),
# seq(0.8 + i * 0.4, 1.2, length.out = 100),
# col = rgb(seq(1 - i * 1, 0, length.out = 100),
# seq(0+i, 0, length.out = 100),
# seq(1, 1, length.out = 100)), pch = 16)
}
lines(c(0.4, 0.8), c(0.8, 0.4), lty = 2)
lines(c(0.8, 1), c(0.4, 1), lty = 2)
lines(c(0.4, 1), c(0.8, 1), lty = 2)
# polygon(c(0, 0.8, 0, 0), c(0, 0, 0.8, 0), lty = 2)
# text(c(0, 0.8, 0), c(0, 0, 0.8), c("Red", "Ye", "Magenta"))
#
# polygon(c(0.8, 1.2, 1.2, 0.8), c(0, 0.4, 1.2, 0), lty = 2)
# text(c(0.8, 1.2, 1.2), c(0, 0.4, 1.2), c("", "Vert", "Cyan"))
#
# polygon(c(0, 0.4, 1.2, 0), c(0.8, 1.2, 1.2, 0.8), lty = 2)
# text(c(0, 0.4, 1.2), c(0.8, 1.2, 1.2), c("", "Bleu", ""))
#points(1, 0.2, col = rgb(0.5, 1, 0), pch = 16, cex = 3)
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
library("wesanderson")
library("compositions")
# create a grid of shares for Y
x_seq <- seq(0.01, 0.99, 0.01)
Y <- data.frame(y1 = rep(x_seq, 101),
y2 = rep(x_seq, each = 101))
Y$y3 <- 1 - (Y$y1 + Y$y2)
Y <- Y[Y$y3 > 0, ]
# Y <- Y[which(apply(Y, 1, sum) == 1), ]
Y_simplex_x <- Y[, 1] * R[1] + Y[, 2] * G[1] + Y[, 3] * B[1]
Y_simplex_y <- Y[, 1] * R[2] + Y[, 2] * G[2] + Y[, 3] * B[2]
plot(rbind(R, G, B),
xaxt = "n",
yaxt = "n",
frame = F,
pch = 16,
type = "n",
xlab = "",
ylab = "",
asp = 1,
ylim = c(-0.1, sqrt(3)/2)
)
lines(c(0, 1), c(0, 0), col = "black")
lines(c(0, 0.5), c(0, sqrt(3)/2), col = "black")
lines(c(1, 0.5), c(0, sqrt(3)/2), col = "black")
points(Y_simplex_x, Y_simplex_y, pch = 16,
col = my_rgb_coda(Y, max_intensity = 2.5),
cex = 1)
dev.off()
Ce système est utilisée en peinture, pour les impressions couleurs ou au cinéma pour projeter sur les écrans blancs.
Le peintre qui utilise et mélange des pigments fait de la synthèse soustractive. Poser une touche de couleur (encre, teinture, peinture gouache), c’est poser un corps qui absorbe une partie de la lumière blanche. Ce que l’on voit, c’est ce qui n’est pas absorbé. Si on pose une touche de gouache, jaune par exemple, la surface blanche du papier va soustraire toutes les couleurs de la lumière blanche, sauf la lumière jaune qu’elle renvoie et qui seule sera perçue par notre oeil.
Le jaune, le rouge magenta et le bleu cyan sont les couleurs primaires de la synthèse soustractive. Mélangées les unes aux autres, elles donnent :
library(sf)
library(tidyverse)
# we define the points as spatial object
point_1 <- data.frame(lat = 0, long = 0 ) %>%
st_as_sf(coords = c("long", "lat"))
point_2 <- data.frame(lat = 0, long = 1 ) %>%
st_as_sf(coords = c("long", "lat"))
point_3 <- data.frame(lat = sqrt(3) / 2, long = 1 / 2) %>%
st_as_sf(coords = c("long", "lat"))
# we create buffers to obtain circles for the primary colors
magenta <- st_buffer(point_1, dist = 1)
yellow <- st_buffer(point_2, dist = 1)
cyan <- st_buffer(point_3, dist = 1)
# we create the intersections between primary objects
red <- st_difference(st_intersection(magenta, yellow), cyan)
blue <- st_difference(st_intersection(magenta, cyan), yellow)
green <- st_difference(st_intersection(yellow, cyan), magenta)
black <- st_intersection(st_intersection(magenta, yellow), cyan)
# we plot the object with the colors
plot(st_geometry(magenta), col = rgb(255, 0, 255, max = 255), xlim = c(-1, 2), ylim = c(-1, 2),
bg = rgb(255, 255, 255, max = 255), border = rgb(255, 0, 255, max = 255))
plot(st_geometry(yellow), col = rgb(255, 255, 0, max = 255),
add = T, border = rgb(255, 255, 0, max = 255))
plot(st_geometry(cyan), col = rgb(0, 255, 255, max = 255), add = T,
border = rgb(0, 255, 255, max = 255))
plot(st_geometry(red), col = rgb(255, 0, 0, max = 255), add = T,
border = rgb(255, 0, 0, max = 255))
plot(st_geometry(green), col = rgb(0, 255, 0, max = 255), add = T,
border = rgb(0, 255, 0, max = 255))
plot(st_geometry(blue), col = rgb(0, 0, 255, max = 255), add = T,
border = rgb(0, 0, 255, max = 255))
plot(st_geometry(black), col = rgb(0, 0, 0, max = 255), add = T,
border = rgb(0, 0, 0, max = 255))
Pour obtenir la figure ci-dessus :
On considère le repère \((0, \vec{C}, \vec{M}, \vec{Y})\). On définit une couleur dans ce repère par un triplet d’entiers positifs \((c,m,y)\) où \((c,m,y)\in[0,1]\times[0,1]\times[0,1]\).
Le centre de ce repère est défini par le triplet (0, 0, 0), i.e. la couleur blanche
Les couleurs primaires définissent les 3 sommets du cube. Les couleurs secondaires définissent 3 autres sommets du cube :
\(\vec{C}\) (1, 0, 0) + \(\vec{M}\) (0, 1, 0)
= \(\vec{M}\) (0, 1, 0) + \(\vec{C}\) (1, 0, 0)
= \(\vec{B}\) (1, 1, 0)
\(\vec{M}\) (0, 1, 0) + \(\vec{Y}\) (0, 0, 1)
= \(\vec{Y}\) (0, 0, 1) + \(\vec{M}\) (0, 1, 0)
= \(\vec{R}\) (0, 1, 1)
\(\vec{Y}\) (0, 0, 1) + \(\vec{C}\) (1, 0, 0)
= \(\vec{C}\) (1, 0, 0) + \(\vec{Y}\) (0, 0, 1)
= \(\vec{G}\) (1, 0, 1)
En additionnant les trois couleurs primaires, on obtient le noir, le dernier sommet du cube :
\(\vec{C}\) (1, 0, 0) + \(\vec{M}\) (0, 1, 0) + \(\vec{Y}\) (0, 0, 1) = \(\vec{Black}\) (1, 1, 1)
qui peut être également vu comme une somme d’une couleur primaire et une couleur secondaire :
(\(\vec{C}\) (1, 0, 0) + \(\vec{M}\) (0, 1, 0) ) + \(\vec{Y}\) (0, 0, 1)
= \(\vec{B}\) (1, 1, 0) + \(\vec{Y}\) (0, 0, 1)
\(\vec{Black}\) (1, 1, 1)
ou bien :
\(\vec{C}\) (1, 0, 0) + (\(\vec{M}\) (0, 1, 0) + \(\vec{Y}\) (0, 0, 1) )
= \(\vec{C}\) (1, 0, 0) + \(\vec{R}\) (0, 1, 1)
\(\vec{Black}\) (1, 1, 1)
ou bien :
(\(\vec{C}\) (1, 0, 0) + \(\vec{Y}\) (0, 0, 1) ) + \(\vec{M}\) (0, 1, 0)
= \(\vec{G}\) (1, 0, 1) + \(\vec{M}\) (0, 1, 0)
=\(\vec{Black}\) (1, 1, 1)
On peut également ajouter les couleurs tertiaires en utilisant la même définition que précédement mais dans l’espace CMY.
\(\vec{Y}\) (0, 0, 1) + \(\frac{1}{2}\vec{M}\) (0, 0.5, 0)
= \(\vec{O}\) (0.5, 1, 0)
\(\vec{Y}\) (0, 0, 1) + \(\frac{1}{2}\vec{C}\) (0.5, 0, 0)
= \(\vec{Ch}\) (0.5, 0, 1)
\(\vec{C}\) (1, 0, 0) + \(\frac{1}{2}\vec{Y}\) (0, 0, 0.5)
= \(\vec{Sp}\) (1, 0, 0.5)
\(\vec{C}\) (1, 0, 0) + \(\frac{1}{2}\vec{M}\) (0, 0.5, 0)
= \(\vec{Az}\) (1, 0.5, 0)
\(\vec{M}\) (0, 1, 0) + \(\frac{1}{2}\vec{C}\) (0.5, 0, 0)
= \(\vec{Vi}\) (0.5, 1, 0)
\(\vec{M}\) (0, 1, 0) + \(\frac{1}{2}\vec{Y}\) (0, 0, 0.5)
= \(\vec{Ro}\) (0, 1, 0.5)
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c(rgb(0.9, 0.9, 0.9), "magenta", "cyan", "yellow", "blue", "red", "green", "black"),
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c(rgb(0.9, 0.9, 0.9), "magenta", "cyan", "yellow", "red", "green", "blue", "black"),
pch = 16,
cex = 2)
points(c(0.4, 1, 1.2, 0.75, 0.2, 0), c(0, 0.2, 0.8, 1.2, 1, 0.4),
col = c(rgb(0.5, 0, 1), rgb(0, 0.5, 1), rgb(0, 1, 0.5),
rgb(0.5, 1, 0), rgb(1, 0.5, 0), rgb(1, 0, 0.5)), pch = 16,
cex = 2)
Soit une couleur qui s’exprime \((r,g,b)\) dans le système RGB et \((c,m,y)\) dans le système CMY. On cherche à écrire \[ \left \{ \begin{array}{rcl} \alpha + \beta r+\gamma g + \delta b&=&c \\ \alpha' + \beta' r+\gamma' g + \delta' b&=&m \\ \alpha'' + \beta'' r+\gamma'' g + \delta'' b&=&y \\ \end{array} \right. \]
Le noir s’écrit (0, 0, 0) dans RGB et (1, 1, 1) dans CMY. On en déduit donc que \(\alpha=\alpha'=\alpha''=1\)
La couleur rouge s’écrit (1, 0, 0) dans RGB et (0, 1, 1) dans CMY. La couleur verte s’écrit (0, 1, 0) dans RGB et (1, 0, 1) dans CMY. La couleur bleu s’écrit (0, 0, 1) dans RGB et (1, 1, 0) dans CMY.
On obtient facilement \(\beta=\gamma'=\delta''=-1\) et les autres coefficients égaux à 0. Finalement :
\[ \left \{ \begin{array}{rcl} c&=&1 - r \\ m &=&1 - g \\ y&=&1 - b \\ \end{array} \right. \]
Et pour passer du CMY au RGB :
\[ \left \{ \begin{array}{rcl} r&=&1 - c \\ g &=&1 - m \\ b&=&1 - y \\ \end{array} \right. \]
On représente le triangle de Maxwell dans le référentiel CMY
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c(rgb(0.9, 0.9, 0.9), "magenta", "cyan", "yellow", "blue", "red", "green", "black"),
pch = 16,
cex = 2, xaxt = "n", yaxt = "n", ylim = c(0, 1.25), xlim = c(0, 1.25))
for(i in 1:254) {
points(0 + i /255 * 1.2, 0 + i /255 * 0.4, col = rgb(1 - i / 255, i / 255, 1),
pch = 16,
cex = 1)
# Triangle
x_seq <- seq(0 + i /255 * 1.2, 0.4, length.out = 255)
y_seq <- seq(0 + i /255 * 0.4, 1.2, length.out = 255)
col_seq_r <- seq(1 - i / 255, 0, length.out = 255)
col_seq_g <- seq(i / 255, 0, length.out = 255)
col_seq_b <- seq(0, 1, length.out = 255)
points(x_seq, y_seq, pch = 16,
cex = 1, col = rgb(1 - col_seq_g, 1 - col_seq_r, 1 - col_seq_b))
}
arrows(0.4, 0.4, 0, 0, lty = 2)
arrows(0.4, 0.4, 1.2, 0.4, lty = 2)
arrows(0.4, 0.4, 0.4, 1.2, lty = 2)
segments(0, 0, 0, 0.8)
segments(0, 0, 0.8, 0)
segments(0.8, 0, 0.8, 0.8)
segments(0, 0.8, 0.8, 0.8)
segments(0, 0.8, 0.4, 1.2)
segments(0.4, 1.2, 1.2, 1.2)
segments(1.2, 1.2, 0.8, 0.8)
segments(0.8, 0, 1.2, 0.4)
segments(1.2, 0.4, 1.2, 1.2)
points(c(0.4, 0, 1.2, 0.4, 0, 1.2, 0.8, 0.8), c(0.4, 0, 0.4, 1.2, 0.8, 1.2, 0, 0.8),
col = c(rgb(0.9, 0.9, 0.9), "magenta", "cyan", "yellow", "red", "green", "blue", "black"),
pch = 16,
cex = 2)
points(c(0.4, 1, 1.2, 0.75, 0.2, 0), c(0, 0.2, 0.8, 1.2, 1, 0.4),
col = c(rgb(0.5, 0, 1), rgb(0, 0.5, 1), rgb(0, 1, 0.5),
rgb(0.5, 1, 0), rgb(1, 0.5, 0), rgb(1, 0, 0.5)), pch = 16,
cex = 2)
On va utiliser le même procédé que tout à l’heure, à savoir, représenter le plan tel que \(M+C+Y=k\) dans le but de construire une palette de couleur dans le diagramme ternaire dans la base CMY.
my_rgb_coda_cmy <- function(my_data, max_intensity = 1.5) {
# check that data are CODA
# stopifnot(all(apply(my_data, 1, sum) == sum(my_data[1, ])))
my_don_col <- character(nrow(my_data))
# give color to each composition
for(i in 1:nrow(my_data)) {
if(max(my_data[i, ]) <= 1 / max_intensity) {
my_don_col[i] <- rgb(1 - my_data[i, 1] * max_intensity,
1 - my_data[i, 2] * max_intensity,
1 - my_data[i, 3] * max_intensity)
} else {
my_rate <- 1 / max(my_data[i, ])
my_rgb_local <- my_rate * my_data[i, ]
my_don_col[i] <- rgb(1 - my_rgb_local[1],
1 - my_rgb_local[2],
1 - my_rgb_local[3])
}
}
return(my_don_col)
}
TSL (teinte, saturation, lumière) ou HSL (pour Hue, saturation, light en anglais) ou HLS (pour Hue, light, saturation) est un espace de représentation des couleurs.
Les études de psychologie de la perception entreprises dès le XIXe siècle concluent que la teinte, la sautaration et la luminosité décrivent les couleurs
La teinte distingue les sensations colorées (rouge, vert, jaune, bleu, etc.). Il s’agit d’un angle compris entre 0 et 360. A chaque valeur, on a attribué une couleur correspondant au mélange d’une couleur primaire avec un pourcentage d’une deuxième couleur primaire (la troisième couleur primaire étant égal à 0). On rappelle ici comment est effectué le mélange :
my_hue <- function(theta) {
# rules :
# theta in [0,60] -> "full_red_part_green"
# theta in [60,120] -> "full_green_part_red"
# theta in [120,180] -> "full_green_part_blue"
# theta in [180,240] -> "full_blue_part_green"
# theta in [240,300] -> "full_blue_part_red"
# theta in [300,360] -> "full_red_part_blue"
interval <- c(0, 60, 120, 180, 240, 300, 360)
my_rgb <- switch(as.character(findInterval(theta, interval)),
"1" = rgb(1, theta/60, 0),
"2" = rgb(1 - (theta - 60) / 60, 1, 0),
"3" = rgb(0, 1, (theta - 120) / 60),
"4" = rgb(0, 1 - (theta - 180) / 60, 1),
"5" = rgb((theta - 240) / 60, 0, 1),
"6" = rgb(1, 0, 1 - (theta - 300) / 60))
return(my_rgb)
}
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0))
plot(0, 0, type = "n", xaxt = "n", yaxt = "n",
ylim = c(-2, 2), xlim = c(-2, 2))
for(i in seq(0, 360, length.out = 1000)) {
my_rad <- pi * i / 180
interval <- c(0, 60, 120, 180, 240, 300, 360)
segments(0, 0, 1/3 * cos(my_rad), 1/3 * sin(my_rad),
col = switch(as.character(findInterval(i, interval)),
"1" = rgb(1, 0, 0),
"2" = rgb(0, 1, 0),
"3" = rgb(0, 1, 0),
"4" = rgb(0, 0, 1),
"5" = rgb(0, 0, 1),
"6" = rgb(1, 0, 0), lwd = 2))
segments(1/2 * cos(my_rad), 1/2 * sin(my_rad), 5/6 * cos(my_rad), 5/6 * sin(my_rad),
col = switch(as.character(findInterval(i, interval)),
"1" = rgb(0, i/60, 0),
"2" = rgb(1 - (i - 60) / 60, 0, 0),
"3" = rgb(0, 0, (i - 120) / 60),
"4" = rgb(0, 1 - (i - 180) / 60, 0),
"5" = rgb((i - 240) / 60, 0, 0),
"6" = rgb(0, 0, 1 - (i - 300) / 60)), lwd = 2)
segments(cos(my_rad), sin(my_rad), 2 * cos(my_rad), 2 * sin(my_rad),
col = my_hue(i), lwd = 2)
}
text(1/3+1/12, 0, "+")
text(1-1/12, 0, "=")
On considère une teinte définie par un angle \(\theta\in[0,360]\). On peut aussi écrire \(M=(r,g,b)\) cette couleur dans l’espace RGB. L’objectif est de déplacer un point \(M'\) sur le segment \([GM]\) (où \(G\) est le centre du cube de coordonnées \(G=(1/2,1/2,1/2)\)) avec un facteur \(\alpha\in[0,1]\).
Le nouveau point aura les coordonnées \(M'=(0.5+(r-0.5)\alpha, 0.5+(0.5-g)\alpha, 0.5+(0.5-b)\alpha)\) dans l’espace RGB. Par exemple, pour les trois couleurs primaires et secondaires, on représente ici les différents degrés de saturation pour \(\alpha\in[0,1]\).
my_satu <- function(theta, alpha) {
interval <- c(0, 60, 120, 180, 240, 300, 360)
vec_hue <- switch(as.character(findInterval(theta, interval)),
"1" = c(1, theta/60, 0),
"2" = c(1 - (theta - 60) / 60, 1, 0),
"3" = c(0, 1, (theta - 120) / 60),
"4" = c(0, 1 - (theta - 180) / 60, 1),
"5" = c((theta - 240) / 60, 0, 1),
"6" = c(1, 0, 1 - (theta - 300) / 60))
return(rgb(0.5 + (vec_hue[1] - 0.5) * alpha,
0.5 + (vec_hue[2] - 0.5) * alpha,
0.5 + (vec_hue[3] - 0.5) * alpha))
}
par(oma = c(2, 0, 0, 0), mar = c(2, 0, 0, 0))
plot(0, 0, type = "n", yaxt = "n",
ylim = c(0, 6), xlim = c(0, 1))
for(i in seq(0, 1, length.out = 1000)) {
segments(i, 0, i, 0.95, col = my_satu(0, i), lwd = 2)
segments(i, 1, i, 1.95, col = my_satu(60, i), lwd = 2)
segments(i, 2, i, 2.95, col = my_satu(120, i), lwd = 2)
segments(i, 3, i, 3.95, col = my_satu(180, i), lwd = 2)
segments(i, 4, i, 4.95, col = my_satu(240, i), lwd = 2)
segments(i, 5, i, 5.95, col = my_satu(300, i), lwd = 2)
}
La clarté caractérise l’intensité lumineuse relative perçue ; elle est souvent représentée sur une échelle de \(\beta=0\%\) (noir) à \(\beta=100\%\) (blanc). La valeur \(\beta=50\%\) correspond à la couleur de base.
Quand on ajoute de la luminosité, on se rapproche du point blanc. On part d’une teinte qu’on représente par le point \(M (r,g,b)\) dans l’espace RGB. L’objectif est de déplacer un point \(M'\) sur le segment \(MW\) (où \(W\) est le point Blanc de coordonnées \((1,1,1)\)) avec un facteur \(\beta\in[50\%,100\%]\). Le nouveau point \(M'\) aura les coordonnées \((1+(r-1)\times2\times(1-\beta), 1+(1-g)\times2\times(1-\beta), 1+(1-b)\times2\times(1-\beta))\) dans l’espace RGB.
Quand on enlève de la luminosité, on se rapproche du point noir. On part d’une teinte qu’on représente par le point \(M (r,g,b)\) dans l’espace RGB. L’objectif est déplacer un point \(M'\) sur le segment \(NM\) (où \(N\) est le point noir de coordonnées \((0,0,0)\)) avec un facteur \(\beta\in[0\%,50\%]\). Le nouveau point aura les coordonnées \((r\times2\times\beta, g\times2\times\beta, b\times2\times\beta)\) dans l’espace RGB.
my_light <- function(theta, beta) {
interval <- c(0, 60, 120, 180, 240, 300, 360)
vec_hue <- switch(as.character(findInterval(theta, interval)),
"1" = c(1, theta/60, 0),
"2" = c(1 - (theta - 60) / 60, 1, 0),
"3" = c(0, 1, (theta - 120) / 60),
"4" = c(0, 1 - (theta - 180) / 60, 1),
"5" = c((theta - 240) / 60, 0, 1),
"6" = c(1, 0, 1 - (theta - 300) / 60))
interval_2 <- c(0, 0.5, 1)
return(switch(as.character(findInterval(beta, interval_2)),
"1" = rgb(vec_hue[1] * 2 * beta,
vec_hue[2] * 2 * beta,
vec_hue[3] * 2 * beta),
"2" = rgb(1 + (vec_hue[1] - 1) * 2 * (1 - beta),
1 + (vec_hue[2] - 1) * 2 * (1-beta),
1 + (vec_hue[3] - 1) * 2 * (1-beta))
)
)
}
par(oma = c(2, 0, 0, 0), mar = c(2, 0, 0, 0))
plot(0, 0, type = "n", yaxt = "n",
ylim = c(0, 6), xlim = c(0, 1))
for(i in seq(0, 1, length.out = 1000)) {
segments(i, 0, i, 0.95, col = my_light(0, i), lwd = 2)
segments(i, 1, i, 1.95, col = my_light(60, i), lwd = 2)
segments(i, 2, i, 2.95, col = my_light(120, i), lwd = 2)
segments(i, 3, i, 3.95, col = my_light(180, i), lwd = 2)
segments(i, 4, i, 4.95, col = my_light(240, i), lwd = 2)
segments(i, 5, i, 5.95, col = my_light(300, i), lwd = 2)
}
Remarque : si on applique la même quantité de saturation (\(50\%\)) et de luminosité (\(70\%\)) à l’ensemble des couleurs du cercle chromatique, on obtient le spectre suivant
my_hsl <- function(h, s, l) {
# hue
interval <- c(0, 60, 120, 180, 240, 300, 361)
vec_hue <- switch(as.character(findInterval(h, interval)),
"1" = c(1, h/60, 0),
"2" = c(1 - (h - 60) / 60, 1, 0),
"3" = c(0, 1, (h - 120) / 60),
"4" = c(0, 1 - (h - 180) / 60, 1),
"5" = c((h - 240) / 60, 0, 1),
"6" = c(1, 0, 1 - (h - 300) / 60))
# lighteness
interval_2 <- c(0, 0.5, 1)
vec_hue <- switch(as.character(findInterval(l, interval_2)),
"1" = c(vec_hue[1] * 2 * l,
vec_hue[2] * 2 * l,
vec_hue[3] * 2 * l),
"2" = c(1 + (vec_hue[1] - 1) * 2 * (1 - l),
1 + (vec_hue[2] - 1) * 2 * (1-l),
1 + (vec_hue[3] - 1) * 2 * (1-l))
)
# saturation
vec_hue <- c(0.5 + (vec_hue[1] - 0.5) * s,
0.5 + (vec_hue[2] - 0.5) * s,
0.5 + (vec_hue[3] - 0.5) * s)
return(rgb(vec_hue[1], vec_hue[2], vec_hue[3]))
}
par(oma = c(0, 0, 0, 0), mar = c(0, 0, 0, 0), mfrow = c(1, 2))
plot(0, 0, type = "n", xaxt = "n", yaxt = "n",
ylim = c(-2, 2), xlim = c(-2, 2))
for(i in seq(0, 360, length.out = 1000)) {
my_rad <- pi * i / 180
segments(cos(my_rad), sin(my_rad), 2 * cos(my_rad), 2 * sin(my_rad),
col = my_hue(i), lwd = 2)
}
plot(0, 0, type = "n", xaxt = "n", yaxt = "n",
ylim = c(-2, 2), xlim = c(-2, 2))
for(i in seq(0, 360, length.out = 1000)) {
my_rad <- pi * i / 180
segments(cos(my_rad), sin(my_rad), 2 * cos(my_rad), 2 * sin(my_rad),
col = my_hsl(i, 0.5, 0.7), lwd = 2)
}
Remarque : (Zeileis et al., 2009) HSV colors have a fundamental drawback: its three dimensions map to the three dimensions of human color perception very poorly. The three dimensions are confounded: The brightness of colors is not uniform over hues and saturations —therefore, HSV colors are often not considered to be perceptually based.
Référence :