--- title: "Munsell Color Conversion" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Munsell Color Conversion} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, echo=FALSE, results='hide', warning=FALSE} knitr::opts_chunk$set( message = FALSE, warning = FALSE, background = '#F7F7F7', fig.align = 'center', dev = 'png', dpi = 96, optipng = knitr::hook_optipng, comment = "#>" ) # keep examples from using more than 2 cores data.table::setDTthreads(Sys.getenv("OMP_THREAD_LIMIT", unset = 2)) options(width = 100, stringsAsFactors = FALSE, timeout = 600) suppressMessages(library(aqp, quietly = TRUE)) ``` The `aqp` package provides several functions for working with colors specified in the [Munsell color system](https://en.wikipedia.org/wiki/Munsell_color_system). ## Color Conversion Functions Conversion of Munsell notation to "something" that can be displayed on-screen (sRGB color representation) is probably the most common color transformation applied to soil morphology data. The `munsell2rgb()` function is convenient for this operation when Munsell notation has been split into vectors of hue, value, and chroma. These could be columns in a `data.frame` or separate vectors. The `parseMunsell()` function is convenient when a full Munsell notation is given as a character vector. Note that `parseMunsell()` is a wrapper to `munsell2rgb()`. For example, converting `10YR 4/6` with either function can return: * hex-notation of a color: `#805921FF`, or * [sRGB](https://en.wikipedia.org/wiki/SRGB) color coordinates: `0.5002233 0.3489249 0.1287694`, or * [CIELAB](https://en.wikipedia.org/wiki/CIELAB_color_space) color coordinates: `40.95021 10.31088 37.49513`. Selection of the closest `n` Munsell color "chips", given sRGB or CIELAB colorspace coordinates is performed with the `col2Munsell()` function. Input to this function can be colorspace coordinates, named colors (e.g. `red`), or hex-notation of a color. For example, the selection of the closest Munsell chip for CIELAB coordinates `(51.4337, 9.917916, 38.6889)` results in `10YR 5/6` with a reported sigma (error) of `1.5e-6`. This error is estimated as the [CIE2000 distance](https://en.wikipedia.org/wiki/Color_difference#CIEDE2000) between the source CIELAB coordinates and the CIELAB coordinates of the closest Munsell chip. A representative Munsell color can be estimated from reflectance spectra in the range of 380nm to 730nm with the `spec2Munsell()` function. ```{r echo = FALSE, fig.width=8, fig.height=4} .m <- '10YR 6/6' data("munsell.spectra.wide") w <- munsell.spectra.wide[, 1] s <- munsell.spectra.wide[, .m] par(mar = c(4.5, 4.5, 2, 1), cex.axis = 0.75, lend = 2) plot(w, s, xlab = 'Wavelength (nm)', ylab = 'Reflectance', type = 'b', main = .m, cex = 0.8, pch = 16, axes = FALSE) rect(xleft = 400, ybottom = 0.35, xright = 450, ytop = 0.4, col = parseMunsell(.m), border = 1, lwd = 1) axis(side = 1, at = seq(380, 730, by = 20)) axis(side = 2, las = 1) ``` ### Special Cases Neutral colors are commonly specified two ways in the Munsell system: `N 3/` or `N 3/0`, either format will work with `munsell2rgb()` and `parseMunsell()`. Non-standard Munsell notation (e.g. `3.6YR 4.4 / 5.6`), possibly collected with a sensor vs. color book, can be approximated with `getClosestMunsellChip()`. A more accurate conversion can be performed with the [`munsellinterpol` package.](https://cran.r-project.org/package=munsellinterpol). ### Examples ```{r} # Munsell -> hex color parseMunsell('5PB 4/6') # Munsell -> sRGB parseMunsell('5PB 4/6', return_triplets = TRUE) # Munsell -> CIELAB parseMunsell('5PB 4/6', returnLAB = TRUE) # hex color -> Munsell col2Munsell('#476189FF') # neutral color parseMunsell('N 5/') # non-standard notation getClosestMunsellChip('3.3YR 4.4/6.1', convertColors = FALSE) ``` ## Estimating Dry ↔ Moist Colors All else equal, soil color will predictably shift in perceived lightness (change in Munsell value) as moisture content changes. Field-described soil colors are typically collected at approximately air dry ("dry") and field capacity ("moist") states. This function estimates "dry" soil colors from "moist" soil colors and vice versa. Two methods are available for estimation, both developed from a national collection of field-described soil colors (approx. 800k horizons). Estimates are only valid for mineral soil material, having Munsell values and chroma < 10. Estimation has a median error rate of approximately (CIE dE2000) 5. Available Methods: * "procrustes": soil colors are converted using scale, rotation, and translation parameters in CIELAB color space * "ols": soil colors are converted using 3 multiple linear regression models (CIELAB coordinates) The two methods will give similar results, with `ols` usually closer to observed moist or dry colors. Estimating moist → dry color change is not guaranteed symmetric with estimating dry → moist color. In the example below, the estimated dry colors are the same for both `method = 'procrustes'` and `method = 'ols'`. ```{r fig.width=10, fig.height=4} # example moist soil colors from the Musick soil series m <- c("7.5YR 2.5/1", "10YR 3/2", "7.5YR 4/3", "2.5YR 3/6", "2.5YR 3/6", "2.5YR 4/6", "5YR 4/6", "7.5YR 5/4") mm <- parseMunsell(m, convertColors = FALSE) d.p <- estimateSoilColor( hue = mm$hue, value = mm$value, chroma = mm$chroma, method = 'procrustes', sourceMoistureState = 'moist' ) d.ols <- estimateSoilColor( hue = mm$hue, value = mm$value, chroma = mm$chroma, method = 'ols', sourceMoistureState = 'moist' ) d.p <- sprintf("%s %s/%s", d.p$hue, d.p$value, d.p$chroma) d.ols <- sprintf("%s %s/%s", d.ols$hue, d.ols$value, d.ols$chroma) colorContrastPlot(m, d.p, labels = c('Moist', 'Estimated\nDry'), d.cex = 0.9) # it is the same # colorContrastPlot(m, d.ols, labels = c('Moist', 'Estimated\nDry'), d.cex = 0.9) ``` ### Organic Soil Colors Representative O horizon colors for the most common types of organic soil material, at dry and moist states, have been added to aqp (>=2.3.1) as `Ohz.colors`. Colors were derived from an analysis of soil morphologic data (5245 horizons) within the USDA-NRCS National Soil Information System. Representative colors are the L1 median colors (see `colorVariation()`) within groups generalized to "Oi", "Oe", "Oa", and all "other". These estimates may be useful place-holder values for soil color in collections where O horizon color was not recorded. ```{r fig.width=6, fig.height=3} data("Ohz.colors") Ohz.colors$col <- parseMunsell(Ohz.colors$L1.munsell) op <- par(mfrow = c(2, 1), mar = c(0.5, 0.5, 1.5, 0)) with( Ohz.colors[Ohz.colors$state == 'dry', ], soilPalette(colors = col, lab = sprintf("%s\n%s", genhz, L1.munsell), lab.cex = 1) ) title(main = 'Dry Colors') with( Ohz.colors[Ohz.colors$state == 'moist', ], soilPalette(colors = col, lab = sprintf("%s\n%s", genhz, L1.munsell), lab.cex = 1) ) title(main = 'Moist Colors') # restore original base graphics state par(op) ```