├── .gitignore ├── LICENSE ├── figs ├── Desert.jpg ├── BillMurray.jpg ├── CeleryLunch.jpg ├── LifeAquatic.jpg ├── celeryBill2.png ├── desertCrop.png ├── desertDiscrete.png ├── LifeAquaticCrop.jpg ├── celerifiedBill2.png ├── desertContinuous.png ├── lifeAquaticBars.png ├── lifeAquaticScale.png ├── mandrill_median7.png ├── mandrill_median4k4.png └── lifeAquaticScaleTweak.png ├── .Rbuildignore ├── NEWS.md ├── rPalette.Rproj ├── DESCRIPTION ├── NAMESPACE ├── man ├── vbox.Rd ├── display_image.Rd ├── display_palette.Rd ├── quantize_image.Rd ├── median_cut.Rd ├── switch_colors.Rd ├── image_pal.Rd ├── image_palette.Rd └── scale_image.Rd ├── cran-comments.md ├── R ├── volumeBox.R ├── medianCut.R ├── quantizeImage.R ├── utils.R ├── paletteFunction.R └── scale_image.R ├── README.md └── README.Rmd /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR:2015 2 | COPYRIGHT HOLDER: Joel Carlson 3 | -------------------------------------------------------------------------------- /figs/Desert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/Desert.jpg -------------------------------------------------------------------------------- /figs/BillMurray.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/BillMurray.jpg -------------------------------------------------------------------------------- /figs/CeleryLunch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/CeleryLunch.jpg -------------------------------------------------------------------------------- /figs/LifeAquatic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/LifeAquatic.jpg -------------------------------------------------------------------------------- /figs/celeryBill2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/celeryBill2.png -------------------------------------------------------------------------------- /figs/desertCrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/desertCrop.png -------------------------------------------------------------------------------- /figs/desertDiscrete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/desertDiscrete.png -------------------------------------------------------------------------------- /figs/LifeAquaticCrop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/LifeAquaticCrop.jpg -------------------------------------------------------------------------------- /figs/celerifiedBill2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/celerifiedBill2.png -------------------------------------------------------------------------------- /figs/desertContinuous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/desertContinuous.png -------------------------------------------------------------------------------- /figs/lifeAquaticBars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/lifeAquaticBars.png -------------------------------------------------------------------------------- /figs/lifeAquaticScale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/lifeAquaticScale.png -------------------------------------------------------------------------------- /figs/mandrill_median7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/mandrill_median7.png -------------------------------------------------------------------------------- /figs/mandrill_median4k4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/mandrill_median4k4.png -------------------------------------------------------------------------------- /figs/lifeAquaticScaleTweak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joelcarlson/RImagePalette/HEAD/figs/lifeAquaticScaleTweak.png -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^README-.*\.png$ 5 | ^NEWS\.md$ 6 | ^figs$ 7 | ^cran-comments\.md$ 8 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # RImagePalette 0.1.1 2 | 3 | - Very important bug fix where when creating discrete scales for use with `ggplot2` the plot colors and the legend colors did not match. 4 | This impacts: 5 | 6 | + `scale_color_image` 7 | + `scale_fill_image` 8 | 9 | 10 | - Added `quantize_image` function 11 | 12 | + Allows user to reduce the number of distinct colors in given image 13 | 14 | 15 | # RImagePalette 0.1.0 16 | 17 | * First release! 18 | -------------------------------------------------------------------------------- /rPalette.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RImagePalette 2 | Type: Package 3 | Title: Extract the Colors from Images 4 | Version: 0.1.1 5 | Date: 2016-01-05 6 | Author: Joel Carlson 7 | Maintainer: Joel Carlson 8 | Description: A pure R implementation of the median cut algorithm. 9 | Extracts the dominant colors from an image, and turns them into 10 | a scale for use in plots or for fun! 11 | License: MIT + file LICENSE 12 | LazyData: TRUE 13 | Depends: 14 | R (>= 2.1.0) 15 | Imports: 16 | ggplot2 17 | Suggests: 18 | testthat, 19 | scales, 20 | jpeg, 21 | png 22 | 23 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2 (4.1.1): do not edit by hand 2 | 3 | export(display_image) 4 | export(display_palette) 5 | export(image_pal) 6 | export(image_palette) 7 | export(quantize_image) 8 | export(scale_color_image) 9 | export(scale_colour_image) 10 | export(scale_fill_image) 11 | export(switch_colors) 12 | importFrom(ggplot2,discrete_scale) 13 | importFrom(ggplot2,scale_color_gradientn) 14 | importFrom(ggplot2,scale_fill_gradientn) 15 | importFrom(grDevices,col2rgb) 16 | importFrom(grDevices,colorRampPalette) 17 | importFrom(grDevices,rgb) 18 | importFrom(graphics,barplot) 19 | importFrom(graphics,image) 20 | importFrom(stats,median) 21 | importFrom(stats,runif) 22 | -------------------------------------------------------------------------------- /man/vbox.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/volumeBox.R 3 | \name{vbox} 4 | \alias{vbox} 5 | \title{Volume box} 6 | \usage{ 7 | vbox(im) 8 | } 9 | \arguments{ 10 | \item{im}{List An image in list form, with three components: red, green, blue} 11 | } 12 | \value{ 13 | A list containing the minimum, maximum, median, extent, and volume 14 | of each component of the image 15 | } 16 | \description{ 17 | Extract minimum, maximum, median, extent, and volume information 18 | from red, green, and blue color channels. 19 | } 20 | \details{ 21 | For passing to \code{median_cut()}. 22 | } 23 | \seealso{ 24 | \code{\link{median_cut}} \code{\link{image_palette}} 25 | } 26 | 27 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Release summary 2 | 3 | Apologies for the fast resubmission. 4 | 5 | Very important bugfix in a key pair of functions. 6 | 7 | ## Test environments 8 | 9 | * local Ubuntu 14.04 LTS install, R 3.2.2 10 | * local Windows 7 install, R 3.1.3 11 | * win-builder 12 | 13 | ## R CMD check results 14 | 15 | There were no ERRORs or WARNINGs. 16 | 17 | 1 Note 18 | 19 | checking CRAN incoming feasibility ... NOTE 20 | 21 | Maintainer: 'Joel Carlson ' 22 | 23 | License components with restrictions and base license permitting such: 24 | MIT + file LICENSE 25 | File 'LICENSE': 26 | YEAR:2015 27 | COPYRIGHT HOLDER: Joel Carlson 28 | 29 | ## Downstream dependencies 30 | 31 | None to report 32 | -------------------------------------------------------------------------------- /man/display_image.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{display_image} 4 | \alias{display_image} 5 | \title{Display color image} 6 | \usage{ 7 | display_image(image) 8 | } 9 | \arguments{ 10 | \item{image}{Matrix The image from which the palette will be extracted from. Should 11 | be a 3 (or more) dimensional matrix. The output of functions such as \code{readJPG()} 12 | are suitable as \code{image}.} 13 | } 14 | \value{ 15 | A raster image in the plot window. 16 | } 17 | \description{ 18 | Convenience wrapper to create a raster image of the image 19 | you wish to extract the palette from. 20 | } 21 | \examples{ 22 | img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 23 | display_image(img) 24 | } 25 | 26 | -------------------------------------------------------------------------------- /man/display_palette.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{display_palette} 4 | \alias{display_palette} 5 | \title{Display color palette} 6 | \usage{ 7 | display_palette(palette) 8 | } 9 | \arguments{ 10 | \item{palette}{Vector The output of \code{image_palette}.} 11 | } 12 | \value{ 13 | A plot of the colors extracted from the image 14 | } 15 | \description{ 16 | Displays the created palette as a barchart with axis labels 17 | representing hex values of the colors. A more attractive method 18 | for doing so would be to use \code{show_cols()} from 19 | \code{library(scales)}. 20 | } 21 | \examples{ 22 | img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 23 | display_palette(image_palette(img, n=5)) 24 | } 25 | \seealso{ 26 | \code{scales::show_cols()} 27 | } 28 | 29 | -------------------------------------------------------------------------------- /man/quantize_image.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/quantizeImage.R 3 | \name{quantize_image} 4 | \alias{quantize_image} 5 | \title{Quantize image} 6 | \usage{ 7 | quantize_image(image, n, ...) 8 | } 9 | \arguments{ 10 | \item{image}{Matrix The image from which the palette will be extracted from. Should 11 | be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 12 | or \code{readPNG()} are suitable as \code{image}.} 13 | 14 | \item{n}{Integer The number of discrete colors to be extracted from the image.} 15 | 16 | \item{...}{Pass any of the arguments for \code{image_palette}} 17 | } 18 | \description{ 19 | Quantize image into discrete colors using the median cut algorithm 20 | } 21 | \details{ 22 | Note: This function is extremely slow for large images. 23 | Takes up to 20 seconds for 500x500 image on a desktop with 2.7GHz processor and 4Gb ram. 24 | } 25 | \examples{ 26 | img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 27 | quant_img <- quantize_image(img, n=3) 28 | display_image(img) 29 | display_image(quant_img) 30 | } 31 | \seealso{ 32 | \code{\link{image_palette}} 33 | } 34 | 35 | -------------------------------------------------------------------------------- /R/volumeBox.R: -------------------------------------------------------------------------------- 1 | #' Volume box 2 | #' 3 | #' Extract minimum, maximum, median, extent, and volume information 4 | #' from red, green, and blue color channels. 5 | #' 6 | #' For passing to \code{median_cut()}. 7 | #' 8 | #' 9 | #' @param im List An image in list form, with three components: red, green, blue 10 | #' @return A list containing the minimum, maximum, median, extent, and volume 11 | #' of each component of the image 12 | #' 13 | #' @seealso \code{\link{median_cut}} \code{\link{image_palette}} 14 | #'@importFrom stats median 15 | vbox <- function(im){ 16 | #Red 17 | r1 <- min(im$red) 18 | r2 <- max(im$red) 19 | rmed <- median(im$red) 20 | rext <- r2- r1 21 | #Green 22 | g1 <- min(im$green) 23 | g2 <- max(im$green) 24 | gmed <- median(im$green) 25 | gext <- g2 - g1 26 | #Blue 27 | b1 <- min(im$blue) 28 | b2 <- max(im$blue) 29 | bmed <- median(im$blue) 30 | bext <- b2 - b1 31 | 32 | volume <- rext * gext * bext 33 | return(list("min"=list("red" = r1, "green" = g1, "blue" = b1), 34 | "max"=list("red" = r2, "green" = g2, "blue" = b2), 35 | "med"=list("red" = rmed, "green" = gmed, "blue" = bmed), 36 | "ext"=list("red" = rext, "green" = gext, "blue" = bext), 37 | "volume"=volume)) 38 | } 39 | -------------------------------------------------------------------------------- /man/median_cut.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/medianCut.R 3 | \name{median_cut} 4 | \alias{median_cut} 5 | \title{The median cut algorithm} 6 | \usage{ 7 | median_cut(image, vbox, iter = 1) 8 | } 9 | \arguments{ 10 | \item{image}{List An image in list form, with three components: red, green, blue} 11 | 12 | \item{vbox}{List The output of \code{vbox()} for the given image. A list of image parameters ("min", "max", "med", "ext" and "volume")} 13 | 14 | \item{iter}{Integer The number attached to the names of the two new images.} 15 | } 16 | \value{ 17 | Two new images in a list, each separated into rgb components 18 | } 19 | \description{ 20 | Cut an rgb cube into two color cubes, each with as imilar number of 21 | elements. 22 | } 23 | \details{ 24 | Represents the rgb colorspace as a cube, with side lengths 25 | based on the red, green, and blue extents (difference between 26 | maximum and minimum within-color values). 27 | 28 | The algorithm takes the side with the largest extent (extent information 29 | is passed in via the \code{vbox()} parameter), 30 | and splits the cube along the median value. 31 | 32 | Both halves of the cube are then returned. 33 | } 34 | \seealso{ 35 | \code{\link{vbox}} \code{\link{image_palette}} 36 | } 37 | 38 | -------------------------------------------------------------------------------- /man/switch_colors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{switch_colors} 4 | \alias{switch_colors} 5 | \title{Swap Colors in an Image} 6 | \usage{ 7 | switch_colors(target_image, source_image, source_colors = 3, 8 | smoothness = 100, ...) 9 | } 10 | \arguments{ 11 | \item{target_image}{Matrix The image you wish to transfer colors into. 12 | The output from \code{readJPEG} is of suitable format.} 13 | 14 | \item{source_image}{Matrix The image you wish to transfer colors from.} 15 | 16 | \item{source_colors}{Integer The number of colors you wish to extract from the 17 | source image.} 18 | 19 | \item{smoothness}{Integer The source colors are interpolated such that the image 20 | doesn't appear blocky. The value of smoothness determines how many values are interpoloated 21 | between the source_colors. That is, smoothness determines the length of the palette used. 22 | Higher values return smoother images.} 23 | 24 | \item{...}{Pass any of the arguments for \code{image_palette}} 25 | } 26 | \value{ 27 | The image, but with swapped colors! 28 | } 29 | \description{ 30 | Swap the palette of an image! 31 | } 32 | \examples{ 33 | #Trivial example of using only 5 dominant colors 34 | # from an image to recolor itself 35 | img1 <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 36 | img2 <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 37 | switch_colors(img1, img2, source_colors=5, smoothness=20) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /man/image_pal.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/scale_image.R 3 | \name{image_pal} 4 | \alias{image_pal} 5 | \title{Image palette} 6 | \usage{ 7 | image_pal(image, choice = mean, volume = FALSE) 8 | } 9 | \arguments{ 10 | \item{image}{Matrix The image from which the palette will be extracted from. Should 11 | be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 12 | or \code{readPNG()} are suitable as \code{image}.} 13 | 14 | \item{choice}{Function Defines how the color will be chosen from the final color cubes. 15 | The default choice is to take the \code{mean} value of the image cube, but other choices 16 | may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 17 | whatever summary statistic suits your fancy.} 18 | 19 | \item{volume}{Logical volume controls the method for choosing which color cube to split 20 | at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 21 | choose the cube based on which cube contains the largest extent (that is, the largest range 22 | of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 23 | Occasionally, setting to \code{TRUE} returns a better palette.} 24 | } 25 | \description{ 26 | Image palette function 27 | } 28 | \examples{ 29 | img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 30 | display_image(img) 31 | scales::show_col(image_pal(img)(10)) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /man/image_palette.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/paletteFunction.R 3 | \name{image_palette} 4 | \alias{image_palette} 5 | \title{Create image palette} 6 | \usage{ 7 | image_palette(image, n, choice = mean, volume = FALSE) 8 | } 9 | \arguments{ 10 | \item{image}{Matrix The image from which the palette will be extracted from. Should 11 | be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 12 | or \code{readPNG()} are suitable as \code{image}.} 13 | 14 | \item{n}{Integer The number of discrete colors to be extracted from the image.} 15 | 16 | \item{choice}{Function Defines how the color will be chosen from the final color cubes. 17 | The default choice is to take the \code{mean} value of the image cube, but other choices 18 | may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 19 | whatever summary statistic suits your fancy.} 20 | 21 | \item{volume}{Logical volume controls the method for choosing which color cube to split 22 | at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 23 | choose the cube based on which cube contains the largest extent (that is, the largest range 24 | of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 25 | Occasionally, setting to \code{TRUE} returns a better palette.} 26 | } 27 | \description{ 28 | Image palette function 29 | } 30 | \details{ 31 | Uses the median cut algorithm to create \code{n} discrete colors based on colors 32 | present in an image. See \code{\link{median_cut}} for more details. 33 | } 34 | \examples{ 35 | img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 36 | display_image(img) 37 | scales::show_col(image_palette(img, n=5)) 38 | } 39 | \seealso{ 40 | \code{\link{median_cut}} 41 | } 42 | 43 | -------------------------------------------------------------------------------- /R/medianCut.R: -------------------------------------------------------------------------------- 1 | #' The median cut algorithm 2 | #' 3 | #' Cut an rgb cube into two color cubes, each with as imilar number of 4 | #' elements. 5 | #' 6 | #' Represents the rgb colorspace as a cube, with side lengths 7 | #' based on the red, green, and blue extents (difference between 8 | #' maximum and minimum within-color values). 9 | #' 10 | #' The algorithm takes the side with the largest extent (extent information 11 | #' is passed in via the \code{vbox()} parameter), 12 | #' and splits the cube along the median value. 13 | #' 14 | #' Both halves of the cube are then returned. 15 | #' 16 | #' @param image List An image in list form, with three components: red, green, blue 17 | #' @param vbox List The output of \code{vbox()} for the given image. A list of image parameters ("min", "max", "med", "ext" and "volume") 18 | #' @param iter Integer The number attached to the names of the two new images. 19 | #' @return Two new images in a list, each separated into rgb components 20 | #' @seealso \code{\link{vbox}} \code{\link{image_palette}} 21 | median_cut <- function(image, vbox, iter=1){ 22 | 23 | #find color which splits vbox on largest extent 24 | #Sample to randomly shuffle, 25 | #meaningful when there is a tie in maximum extents 26 | cut_color <- which.max(sample(vbox[["ext"]])) 27 | 28 | #Cut the extent along the median value 29 | #from the rgb values we make a new set that is lower than the median of the largest exent group, and one that is higher 30 | aboveList <- lapply(image, function(color_channel){ 31 | color_channel[which(image[[names(cut_color)]] >= vbox$med[[names(cut_color)]])] 32 | }) 33 | belowList <- lapply(image, function(color_channel){ 34 | color_channel[which(image[[names(cut_color)]] < vbox$med[[names(cut_color)]])] 35 | }) 36 | 37 | above <- paste0("A", iter) 38 | below <- paste0("B", iter) 39 | cut_image <- list() 40 | cut_image[[above]] <- aboveList 41 | cut_image[[below]] <- belowList 42 | return(cut_image) 43 | } 44 | -------------------------------------------------------------------------------- /R/quantizeImage.R: -------------------------------------------------------------------------------- 1 | #' Quantize image 2 | #' 3 | #' Quantize image into discrete colors using the median cut algorithm 4 | #' 5 | #' Note: This function is extremely slow for large images. 6 | #' Takes up to 20 seconds for 500x500 image on a desktop with 2.7GHz processor and 4Gb ram. 7 | #' @param image Matrix The image from which the palette will be extracted from. Should 8 | #' be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 9 | #' or \code{readPNG()} are suitable as \code{image}. 10 | #' @param n Integer The number of discrete colors to be extracted from the image. 11 | #' @param ... Pass any of the arguments for \code{image_palette} 12 | #' @seealso \code{\link{image_palette}} 13 | #' @export 14 | #' @examples 15 | #' img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 16 | #' quant_img <- quantize_image(img, n=3) 17 | #' display_image(img) 18 | #' display_image(quant_img) 19 | #' @importFrom grDevices col2rgb 20 | quantize_image <- function(image, n, ...){ 21 | dims <- dim(image) 22 | #convert to hex values 23 | hex_image <- rgb(image[,,1], image[,,2], image[,,3]) 24 | #create image palette using median cut algorithm 25 | pal <- image_palette(image, n, ...) 26 | 27 | #Function to extract euclidean distance between 28 | #palette colors, and each pixel of the image 29 | distances <- function(palette, pixel){ 30 | apply(palette, 2, function(x) sqrt(sum((x - pixel)^2))) 31 | 32 | } 33 | 34 | #convert hex palette to rgb 35 | rgb_palette <- col2rgb(pal) 36 | 37 | 38 | #extract the appropriate palette color based on minimum euclidean distance 39 | hex_values <- lapply(hex_image, function(x) pal[which.min(distances(rgb_palette, col2rgb(x)))]) 40 | #back to rgb to compile into image 41 | rgb_values <- col2rgb(hex_values) 42 | 43 | #Extract components 44 | red_channel <- matrix(rgb_values[seq(1,dims[1]*dims[2]*3,3)], nrow=dims[1], ncol=dims[2], byrow=FALSE) 45 | green_channel <- matrix(rgb_values[seq(2,dims[1]*dims[2]*3,3)], nrow=dims[1], ncol=dims[2], byrow=FALSE) 46 | blue_channel <- matrix(rgb_values[seq(3,dims[1]*dims[2]*3,3)], nrow=dims[1], ncol=dims[2], byrow=FALSE) 47 | #Compile into image 48 | rgb_image = array(dim=dims) 49 | rgb_image[,,1] = red_channel 50 | rgb_image[,,2] = green_channel 51 | rgb_image[,,3] = blue_channel 52 | 53 | return(rgb_image/255) 54 | } 55 | -------------------------------------------------------------------------------- /man/scale_image.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2 (4.1.1): do not edit by hand 2 | % Please edit documentation in R/scale_image.R 3 | \name{scale_color_image} 4 | \alias{scale_color_image} 5 | \alias{scale_colour_image} 6 | \alias{scale_fill_image} 7 | \title{Image color scales} 8 | \usage{ 9 | scale_color_image(..., image, n = 3, choice = mean, volume = FALSE, 10 | discrete = TRUE) 11 | 12 | scale_colour_image(..., image, n = 3, choice = mean, volume = FALSE, 13 | discrete = TRUE) 14 | 15 | scale_fill_image(..., image, n = 3, choice = mean, volume = FALSE, 16 | discrete = TRUE) 17 | } 18 | \arguments{ 19 | \item{...}{parameters to \code{discrete_scale} or \code{scale_fill_gradientn}} 20 | 21 | \item{image}{Matrix The image from which the palette will be extracted from. Should 22 | be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 23 | or \code{readPNG()} are suitable as \code{image}.} 24 | 25 | \item{n}{For continuous color scales, you may optionally pass in an integer, n. 26 | This allows some control over the scale, if n is too large the scale has too many 27 | colors and ceases to be meaningul. n = 3 to n = 5 is recommended.} 28 | 29 | \item{choice}{Function Defines how the color will be chosen from the final color cubes. 30 | The default choice is to take the \code{mean} value of the image cube, but other choices 31 | may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 32 | whatever summary statistic suits your fancy.} 33 | 34 | \item{volume}{Logical volume controls the method for choosing which color cube to split 35 | at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 36 | choose the cube which contains the largest extent (that is, the largest range 37 | of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 38 | Occasionally, setting to \code{TRUE} returns a better palette.} 39 | 40 | \item{discrete}{generate a discrete palette? (default: \code{FALSE} - generate continuous palette)} 41 | } 42 | \description{ 43 | Uses the image color scale. 44 | } 45 | \details{ 46 | For \code{discrete == TRUE} (the default) the function will return a \code{discrete_scale} with the plot-computed 47 | number of colors. All other arguments are as to 48 | \link[ggplot2]{scale_fill_gradientn} or \link[ggplot2]{scale_color_gradientn}. 49 | 50 | See \link{image_palette} for more information on the color scale. 51 | } 52 | \examples{ 53 | library(ggplot2) 54 | 55 | # ripped from the pages of ggplot2 56 | your_image <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 57 | display_image(your_image) 58 | 59 | #Discrete scale example 60 | p <- ggplot(mtcars, aes(wt, mpg)) 61 | p + geom_point(size=4, aes(colour = factor(cyl))) + 62 | scale_color_image(image = your_image) + 63 | theme_bw() 64 | 65 | #Continuous scale example 66 | dsub <- subset(diamonds, x > 5 & x < 6 & y > 5 & y < 6) 67 | dsub$diff <- with(dsub, sqrt(abs(x-y))* sign(x-y)) 68 | d <- ggplot(dsub, aes(x, y, colour=diff)) + geom_point() 69 | d + scale_color_image(image = your_image, discrete=FALSE) + theme_bw() 70 | } 71 | \seealso{ 72 | \code{\link{median_cut}} \code{\link{image_palette}} \code{\link{vbox}} 73 | \code{\link{display_image}} 74 | } 75 | 76 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Display color image 2 | #' 3 | #' Convenience wrapper to create a raster image of the image 4 | #' you wish to extract the palette from. 5 | #' 6 | #' @param image Matrix The image from which the palette will be extracted from. Should 7 | #' be a 3 (or more) dimensional matrix. The output of functions such as \code{readJPG()} 8 | #' are suitable as \code{image}. 9 | #' @return A raster image in the plot window. 10 | #' @export 11 | #' @examples 12 | #' img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 13 | #' display_image(img) 14 | display_image <- function(image){ 15 | graphics::plot(1:2, type="n", axes=F, ylab='n', xlab='n', ann=FALSE) 16 | graphics::rasterImage(image, 1, 1, 2, 2) 17 | } 18 | 19 | #' Display color palette 20 | #' 21 | #' Displays the created palette as a barchart with axis labels 22 | #' representing hex values of the colors. A more attractive method 23 | #' for doing so would be to use \code{show_cols()} from 24 | #' \code{library(scales)}. 25 | #' 26 | #' @param palette Vector The output of \code{image_palette}. 27 | #' @return A plot of the colors extracted from the image 28 | #' @seealso \code{scales::show_cols()} 29 | #' @export 30 | #' @examples 31 | #' img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 32 | #' display_palette(image_palette(img, n=5)) 33 | #'@importFrom graphics barplot 34 | display_palette <- function(palette){ 35 | barplot(rep(1, length(palette)), col=palette, names=palette, las=2, axes=F, ann=FALSE) 36 | } 37 | 38 | #' Swap Colors in an Image 39 | #' 40 | #' Swap the palette of an image! 41 | #' 42 | #' @param target_image Matrix The image you wish to transfer colors into. 43 | #' The output from \code{readJPEG} is of suitable format. 44 | #' @param source_image Matrix The image you wish to transfer colors from. 45 | #' @param source_colors Integer The number of colors you wish to extract from the 46 | #' source image. 47 | #' @param smoothness Integer The source colors are interpolated such that the image 48 | #' doesn't appear blocky. The value of smoothness determines how many values are interpoloated 49 | #' between the source_colors. That is, smoothness determines the length of the palette used. 50 | #' Higher values return smoother images. 51 | #' @param ... Pass any of the arguments for \code{image_palette} 52 | #' @return The image, but with swapped colors! 53 | #' @export 54 | #' @examples 55 | #' #Trivial example of using only 5 dominant colors 56 | #' # from an image to recolor itself 57 | #' img1 <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 58 | #' img2 <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 59 | #' switch_colors(img1, img2, source_colors=5, smoothness=20) 60 | #'@importFrom graphics image 61 | #'@importFrom grDevices colorRampPalette 62 | switch_colors <- function(target_image, source_image, source_colors=3, smoothness=100, ...){ 63 | #Create palette from image 64 | palette <- image_palette(source_image, n=source_colors, ...) 65 | palette <- sort(palette) 66 | palette <- colorRampPalette(colors=palette) 67 | 68 | #Flop the image about so that it displays in the correct orientation 69 | target_image <- t(target_image[dim(target_image[,,1])[1]:1,,1]) 70 | image(target_image, col=palette(smoothness), useRaster=TRUE, axes=F, ann=FALSE, asp=dim(target_image)[2]/dim(target_image)[1]) 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /R/paletteFunction.R: -------------------------------------------------------------------------------- 1 | #' Create image palette 2 | #' 3 | #' Image palette function 4 | #' 5 | #' Uses the median cut algorithm to create \code{n} discrete colors based on colors 6 | #' present in an image. See \code{\link{median_cut}} for more details. 7 | #' 8 | #' @param image Matrix The image from which the palette will be extracted from. Should 9 | #' be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 10 | #' or \code{readPNG()} are suitable as \code{image}. 11 | #' @param n Integer The number of discrete colors to be extracted from the image. 12 | #' @param choice Function Defines how the color will be chosen from the final color cubes. 13 | #' The default choice is to take the \code{mean} value of the image cube, but other choices 14 | #' may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 15 | #' whatever summary statistic suits your fancy. 16 | #' @param volume Logical volume controls the method for choosing which color cube to split 17 | #' at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 18 | #' choose the cube based on which cube contains the largest extent (that is, the largest range 19 | #' of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 20 | #' Occasionally, setting to \code{TRUE} returns a better palette. 21 | #' @seealso \code{\link{median_cut}} 22 | #' @export 23 | #' @examples 24 | #' img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 25 | #' display_image(img) 26 | #' scales::show_col(image_palette(img, n=5)) 27 | #' @importFrom grDevices rgb 28 | image_palette <- function(image, n, choice=mean, volume=FALSE){ 29 | image_list <- list("red"=image[,,1], "green"=image[,,2], "blue"=image[,,3]) 30 | cut_image_list <- list() 31 | if(n == 1) return(rgb(mean(image_list$red), mean(image_list$green), mean(image_list$blue))) 32 | 33 | iter <- 1 34 | while(iter < n*2 & length(cut_image_list) < n){ 35 | #Get volume box for first iteration 36 | #to decide which extent to cut 37 | if(iter == 1){ 38 | vboxes <- vbox(image_list) 39 | #Cut the image using the median cut algorithm 40 | cut_image_list <- median_cut(image_list, vboxes, iter = iter) 41 | 42 | } else { 43 | 44 | #---Prepare for next loop--- 45 | #Get vboxes, careful to not recalculate vboxes we already have - too expensive 46 | if(exists("image_to_split")){ 47 | vboxes <- c(vboxes[which(names(vboxes) %in% names(cut_image_list))], 48 | lapply(cut_image_list[which(!names(cut_image_list) %in% names(vboxes))], vbox)) 49 | 50 | } else { 51 | vboxes <- lapply(cut_image_list, vbox) 52 | } 53 | 54 | 55 | #Allow user to choose what to cut on - volume of box, or extent 56 | if(volume){ 57 | #Choose box to cut based on largest volume 58 | cut_criteria <- lapply(vboxes, function(x) x$volume) 59 | } else { 60 | #Choose box to cut based on largest extent 61 | cut_criteria <- lapply(vboxes, function(x) max(x$ext$red, x$ext$green, x$ext$blue)) 62 | } 63 | 64 | 65 | 66 | #Choose which box to split based on highest volume 67 | image_to_split <- which.max(sample(cut_criteria)) 68 | 69 | #Cut the box with the biggest volume using medcut 70 | image_medcut <- median_cut(cut_image_list[[names(image_to_split)]], vboxes[[names(image_to_split)]], iter=iter) 71 | 72 | #Remove any empty images - only comes into play when we are way down at the end 73 | image_medcut <- image_medcut[c(unlist(lapply(image_medcut, 74 | function(x) !any(length(x$red) == 0 | length(x$green) == 0 | length(x$blue) == 0) 75 | ) 76 | ))] 77 | 78 | #Combine r2 medcut with r1, remove the box we cut 79 | cut_image_list <- c(image_medcut, cut_image_list[which(!names(cut_image_list) %in% names(image_to_split))]) 80 | 81 | } 82 | 83 | 84 | iter <- iter + 1 85 | 86 | 87 | } 88 | 89 | return(unname(unlist(lapply(cut_image_list, function(x) rgb(choice(x$red), choice(x$green), choice(x$blue)) )))) 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /R/scale_image.R: -------------------------------------------------------------------------------- 1 | #' Image palette 2 | #' 3 | #' Image palette function 4 | #' 5 | #' @param image Matrix The image from which the palette will be extracted from. Should 6 | #' be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 7 | #' or \code{readPNG()} are suitable as \code{image}. 8 | #' 9 | #' @param choice Function Defines how the color will be chosen from the final color cubes. 10 | #' The default choice is to take the \code{mean} value of the image cube, but other choices 11 | #' may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 12 | #' whatever summary statistic suits your fancy. 13 | #' 14 | #' @param volume Logical volume controls the method for choosing which color cube to split 15 | #' at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 16 | #' choose the cube based on which cube contains the largest extent (that is, the largest range 17 | #' of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 18 | #' Occasionally, setting to \code{TRUE} returns a better palette. 19 | #' @export 20 | #' @examples 21 | #' img <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 22 | #' display_image(img) 23 | #' scales::show_col(image_pal(img)(10)) 24 | image_pal <- function(image, choice=mean, volume=FALSE) { 25 | #Capture seed as discrete_scale calls image_pal twice - 26 | #once for the plot and once for the legend. Without manipulating 27 | #the seed, the colors and legend dont match 28 | current_seed <- .Random.seed 29 | function(n) { 30 | pal <- image_palette(image, n, choice, volume) 31 | .Random.seed <<- current_seed 32 | return(pal) 33 | } 34 | } 35 | 36 | #' @rdname scale_image 37 | #' @importFrom ggplot2 scale_fill_gradientn scale_color_gradientn discrete_scale 38 | #' @importFrom stats runif 39 | #' @export 40 | scale_color_image <- function(..., image, n=3, choice=mean, volume=FALSE, discrete=TRUE) { 41 | if (discrete) { 42 | invisible(runif(1)) #To increment seed 43 | discrete_scale("colour", "image", image_pal(image, choice, volume), ...) 44 | } else { 45 | scale_color_gradientn(colours = sort(image_palette(image, n, choice, volume)), ...) 46 | } 47 | } 48 | 49 | #' @rdname scale_image 50 | #' @export 51 | scale_colour_image <- scale_color_image 52 | 53 | 54 | #' Image color scales 55 | #' 56 | #' Uses the image color scale. 57 | #' 58 | #' For \code{discrete == TRUE} (the default) the function will return a \code{discrete_scale} with the plot-computed 59 | #' number of colors. All other arguments are as to 60 | #' \link[ggplot2]{scale_fill_gradientn} or \link[ggplot2]{scale_color_gradientn}. 61 | #' 62 | #' See \link{image_palette} for more information on the color scale. 63 | #' 64 | #' @param ... parameters to \code{discrete_scale} or \code{scale_fill_gradientn} 65 | #' @param image Matrix The image from which the palette will be extracted from. Should 66 | #' be a 3 (or more) dimensional matrix. The output of a function such as \code{readJPG()} 67 | #' or \code{readPNG()} are suitable as \code{image}. 68 | #' @param n For continuous color scales, you may optionally pass in an integer, n. 69 | #' This allows some control over the scale, if n is too large the scale has too many 70 | #' colors and ceases to be meaningul. n = 3 to n = 5 is recommended. 71 | #' @param choice Function Defines how the color will be chosen from the final color cubes. 72 | #' The default choice is to take the \code{mean} value of the image cube, but other choices 73 | #' may return a subjectively superior scale. Try \code{median}, or \code{min}, or \code{max}, or 74 | #' whatever summary statistic suits your fancy. 75 | #' @param volume Logical volume controls the method for choosing which color cube to split 76 | #' at each iteration of the algorithm. The default choice (when \code{volume = FALSE}) is to 77 | #' choose the cube which contains the largest extent (that is, the largest range 78 | #' of some color). When \code{volume = TRUE}, the cube with the largest volume is chosen to split. 79 | #' Occasionally, setting to \code{TRUE} returns a better palette. 80 | #' @param discrete generate a discrete palette? (default: \code{FALSE} - generate continuous palette) 81 | #' @rdname scale_image 82 | #' @seealso \code{\link{median_cut}} \code{\link{image_palette}} \code{\link{vbox}} 83 | #' \code{\link{display_image}} 84 | #' @importFrom ggplot2 scale_fill_gradientn scale_color_gradientn discrete_scale 85 | #' @export 86 | #' @examples 87 | #' library(ggplot2) 88 | #' 89 | #' # ripped from the pages of ggplot2 90 | #' your_image <- jpeg::readJPEG(system.file("img", "Rlogo.jpg", package="jpeg")) 91 | #' display_image(your_image) 92 | #' 93 | #' #Discrete scale example 94 | #' p <- ggplot(mtcars, aes(wt, mpg)) 95 | #' p + geom_point(size=4, aes(colour = factor(cyl))) + 96 | #' scale_color_image(image = your_image) + 97 | #' theme_bw() 98 | #' 99 | #' #Continuous scale example 100 | #' dsub <- subset(diamonds, x > 5 & x < 6 & y > 5 & y < 6) 101 | #' dsub$diff <- with(dsub, sqrt(abs(x-y))* sign(x-y)) 102 | #' d <- ggplot(dsub, aes(x, y, colour=diff)) + geom_point() 103 | #' d + scale_color_image(image = your_image, discrete=FALSE) + theme_bw() 104 | #' 105 | #' 106 | scale_fill_image <- function (..., image, n=3, choice=mean, volume=FALSE, discrete=TRUE) { 107 | if (discrete) { 108 | invisible(runif(1)) #To increment seed 109 | discrete_scale("fill", "image", image_pal(image, choice, volume), ...) 110 | } else { 111 | scale_fill_gradientn(colours = sort(image_palette(image, n, choice, volume)), ...) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RImagePalette 2 | ============= 3 | 4 | [![cran version](http://www.r-pkg.org/badges/version/RImagePalette)](http://cran.rstudio.com/web/packages/RImagePalette)[![rstudio mirror downloads](http://cranlogs.r-pkg.org/badges/RImagePalette)](https://github.com/metacran/cranlogs.app) 5 | 6 | 7 | ### Extract colors from an image, then use them in plots or for fun! 8 | 9 | #### Play with an interactive version [here](https://jnkcarlson.shinyapps.io/RImagePaletteShiny)! 10 | 11 | The `RImagePalette` package is a pure R implementation of the median cut algorithm for extracting the dominant colors from an image (in-depth explanation [here](http://joelcarlson.github.io/2016/01/15/median-cut/)). This package lets you use the colors from an image you like to create pretty plots, or to swap colors from one image to another. 12 | 13 | Install from [CRAN](https://cran.r-project.org/web/packages/RImagePalette/index.html) using: 14 | 15 | ``` r 16 | install.packages("RImagePalette") 17 | ``` 18 | 19 | Or from github, using: 20 | 21 | ``` r 22 | devtools::install_github("joelcarlson/RImagePalette") 23 | ``` 24 | 25 | Viewing Palettes 26 | ================ 27 | 28 | It's simple to create palettes from an image using the `image_palette()` function: 29 | 30 | ``` r 31 | library(RImagePalette) 32 | 33 | #Load an image 34 | lifeAquatic <- jpeg::readJPEG("figs/LifeAquatic.jpg") 35 | display_image(lifeAquatic) 36 | ``` 37 | 38 | 39 | 40 | ``` r 41 | #Create a palette of 9 colors 42 | lifeAquaticPalette <- image_palette(lifeAquatic, n=9) 43 | scales::show_col(lifeAquaticPalette) 44 | ``` 45 | 46 | 47 | 48 | Not happy with the results? We can tweak some settings until the scale is to our liking: 49 | 50 | ``` r 51 | lifeAquaticPalette <- image_palette(lifeAquatic, n=9, choice=median, volume=TRUE) 52 | scales::show_col(lifeAquaticPalette) 53 | ``` 54 | 55 | 56 | 57 | If it contains colors we like, we can pick and choose, and use them as a scale: 58 | 59 | ``` r 60 | library(ggplot2) 61 | #Create plot 62 | p <- ggplot(data = iris, aes(x=Species, y=Sepal.Width, fill=Species)) + geom_bar(stat="identity") 63 | #Apply scale 64 | p + theme_bw() + scale_fill_manual(values=lifeAquaticPalette[c(2,3,6)]) 65 | ``` 66 | 67 | 68 | 69 | Images as Scales 70 | ================ 71 | 72 | `RImagePalette` can create both discrete and continuous scales from images for use with `ggplot2` using the new `scale_color_image` (or for plots requiring fills, the `scale_fill_image()`) function: 73 | 74 | ``` r 75 | #Load an image 76 | desert <- jpeg::readJPEG("figs/Desert.jpg") 77 | display_image(desert) 78 | ``` 79 | 80 | 81 | 82 | ### Discrete Scale 83 | 84 | ``` r 85 | #Create plot 86 | p <- ggplot(data = iris, aes(x=Sepal.Length, y=Sepal.Width, col=Species)) + geom_point(size=3) 87 | #Add discrete scale from image 88 | p + theme_bw() + scale_color_image(image=desert) 89 | ``` 90 | 91 | 92 | 93 | ### Continuous Scale 94 | 95 | ``` r 96 | #Create plot 97 | p <- ggplot(data = iris, aes(x=Sepal.Length, y=Sepal.Width, col=Sepal.Length)) + geom_point(size=3) 98 | #Use discrete=FALSE for a continuous scale 99 | p + theme_bw() + scale_color_image(image=desert, discrete=FALSE) 100 | ``` 101 | 102 | 103 | 104 | Quantizing Images 105 | ================= 106 | 107 | *Note: This feature is experimental at the moment, and as such is non-optimized, and slow. You must install from github to access the `quantize_image` function* 108 | 109 | We can also quantize images into a discrete number of colors using the `quantize_image` function: 110 | 111 | ``` r 112 | #Load the famous mandrill 113 | mandrill <- png::readPNG("figs/mandrill.png") 114 | 115 | #Quantize using 7 colors 116 | quant_mandrill <- quantize_image(mandrill, n=7) 117 | ``` 118 | 119 | When displayed closely reproduces the original image: 120 | 121 | 122 | 123 | Another method for doing so is to use the kmeans approach, as discussed in [this blog post](http://blog.ryanwalker.us/2016/01/color-quantization-in-r.html) by [Ryan Walker](http://www.ms.uky.edu/~rwalker/). Here is the comparison between kmeans (on the left) and median cut (on the right) using 4 colors: 124 | 125 | 126 | 127 | Just for fun 128 | ============ 129 | 130 | We can swap colors across images using the `switch_colors()` function: 131 | 132 | ``` r 133 | celery <- jpeg::readJPEG("figs/CeleryLunch.jpg") 134 | billMurray <- jpeg::readJPEG("figs/BillMurray.jpg") 135 | ``` 136 | 137 | 138 | 139 | ``` r 140 | switch_colors(billMurray, celery, source_colors = 10) 141 | ``` 142 | 143 | 144 | 145 | ### Note 146 | 147 | There is an element of randomness in the median cut algorithm, so set your seeds carefully, and try running the algorithm a few times if you aren't happy with the results. Other ways to alter the palette: try using `choice = median`, `volume = TRUE` or change the value of `n`. 148 | 149 | ### Special Thanks 150 | 151 | There are a number of projects that inspired or helped this project along, and they deserve some recognition: 152 | 153 | [color-thief.js](http://lokeshdhakar.com/projects/color-thief/) by [Lokesh Dhakar](http://lokeshdhakar.com). 154 | 155 | [Wes Anderson Palettes](https://github.com/karthik/wesanderson) by [Karthik Ram](http://inundata.org). 156 | 157 | [this blog post](http://blenditbayes.blogspot.com/2014/05/towards-yet-another-r-colour-palette.html) from [Jo Fai Chow](http://www.jofaichow.co.uk/). 158 | 159 | and [this blog post](http://blog.ryanwalker.us/2016/01/color-quantization-in-r.html) by [Ryan Walker](http://www.ms.uky.edu/~rwalker/) 160 | 161 | Thank you all for your great work! 162 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: 3 | md_document: 4 | variant: markdown_github 5 | --- 6 | 7 | ```{r, echo = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "figs/README/" 12 | ) 13 | par(mar=c(0,0,0,0), mai=c(0,0,0,0)) 14 | ``` 15 | 16 | #RImagePalette 17 | 18 | [![cran version](http://www.r-pkg.org/badges/version/RImagePalette)](http://cran.rstudio.com/web/packages/RImagePalette) 19 | 20 | ###Extract colors from an image, then use them in plots or for fun! 21 | 22 | The `RImagePalette` package is a pure R implementation of the median cut algorithm for extracting the dominant colors from an image. This package lets you use the colors from an image you like to create pretty plots, or to swap colors from one image to another. 23 | 24 | Install from [CRAN](https://cran.r-project.org/web/packages/RImagePalette/index.html) using: 25 | 26 | ```{r,eval=FALSE} 27 | install.packages("RImagePalette") 28 | ``` 29 | 30 | Or from github, using: 31 | 32 | ```{r,eval=FALSE} 33 | devtools::install_github("joelcarlson/RImagePalette") 34 | ``` 35 | 36 | 37 | ```{r,echo=FALSE, message=FALSE} 38 | devtools::load_all(".") 39 | ``` 40 | 41 | #Viewing Palettes 42 | 43 | It's simple to create palettes from an image using the `image_palette()` function: 44 | 45 | ```{r, echo=FALSE} 46 | set.seed(17) 47 | lifeAquatic <- jpeg::readJPEG("figs/LifeAquatic.jpg") 48 | ``` 49 | ```{r lifeaquaticscale, eval=FALSE} 50 | library(RImagePalette) 51 | 52 | #Load an image 53 | lifeAquatic <- jpeg::readJPEG("figs/LifeAquatic.jpg") 54 | display_image(lifeAquatic) 55 | ``` 56 | 57 | ```{r, eval=FALSE} 58 | #Create a palette of 9 colors 59 | lifeAquaticPalette <- image_palette(lifeAquatic, n=9) 60 | scales::show_col(lifeAquaticPalette) 61 | ``` 62 | 63 | 64 | 65 | Not happy with the results? We can tweak some settings until the scale is to our liking: 66 | 67 | ```{r, echo=FALSE} 68 | set.seed(17) 69 | ``` 70 | ```{r lifeaquaticscale2, eval=FALSE} 71 | lifeAquaticPalette <- image_palette(lifeAquatic, n=9, choice=median, volume=TRUE) 72 | scales::show_col(lifeAquaticPalette) 73 | ``` 74 | 75 | 76 | 77 | If it contains colors we like, we can pick and choose, and use them as a scale: 78 | 79 | ```{r lifeaquaticbar, eval=FALSE} 80 | library(ggplot2) 81 | #Create plot 82 | p <- ggplot(data = iris, aes(x=Species, y=Sepal.Width, fill=Species)) + geom_bar(stat="identity") 83 | #Apply scale 84 | p + theme_bw() + scale_fill_manual(values=lifeAquaticPalette[c(2,3,6)]) 85 | ``` 86 | 87 | 88 | 89 | #Images as Scales 90 | 91 | `RImagePalette` can create both discrete and continuous scales from images for use with `ggplot2` using the new `scale_color_image` (or for plots requiring fills, the `scale_fill_image()`) function: 92 | 93 | ```{r desert, eval=FALSE} 94 | #Load an image 95 | desert <- jpeg::readJPEG("figs/Desert.jpg") 96 | display_image(desert) 97 | ``` 98 | 99 | 100 | 101 | ```{r,echo=FALSE} 102 | set.seed(103) 103 | ``` 104 | 105 | ###Discrete Scale 106 | 107 | ```{r desertplot, message=FALSE, eval=FALSE} 108 | #Create plot 109 | p <- ggplot(data = iris, aes(x=Sepal.Length, y=Sepal.Width, col=Species)) + geom_point(size=3) 110 | #Add discrete scale from image 111 | p + theme_bw() + scale_color_image(image=desert) 112 | ``` 113 | 114 | 115 | 116 | ###Continuous Scale 117 | 118 | ```{r,echo=FALSE} 119 | set.seed(104) 120 | ``` 121 | 122 | ```{r desertplotcontinuous, eval=FALSE} 123 | #Create plot 124 | p <- ggplot(data = iris, aes(x=Sepal.Length, y=Sepal.Width, col=Sepal.Length)) + geom_point(size=3) 125 | #Use discrete=FALSE for a continuous scale 126 | p + theme_bw() + scale_color_image(image=desert, discrete=FALSE) 127 | ``` 128 | 129 | 130 | 131 | #Quantizing Images 132 | 133 | *Note: This feature is experimental at the moment, and as such is non-optimized, and slow. You must install from github to access the `quantize_image` function* 134 | 135 | We can also quantize images into a discrete number of colors using the `quantize_image` function: 136 | 137 | ```{r,eval=FALSE} 138 | #Load the famous mandrill 139 | mandrill <- png::readPNG("figs/mandrill.png") 140 | 141 | #Quantize using 7 colors 142 | quant_mandrill <- quantize_image(mandrill, n=7) 143 | ``` 144 | 145 | When displayed closely reproduces the original image: 146 | 147 | 148 | 149 | Another method for doing so is to use the kmeans approach, 150 | as discussed in [this blog post](http://blog.ryanwalker.us/2016/01/color-quantization-in-r.html) by 151 | [Ryan Walker](http://www.ms.uky.edu/~rwalker/). Here is the comparison between kmeans 152 | (on the left) and median cut (on the right) using 4 colors: 153 | 154 | 155 | 156 | #Just for fun 157 | 158 | We can swap colors across images using the `switch_colors()` function: 159 | 160 | ```{r, echo=FALSE} 161 | set.seed(100) 162 | ``` 163 | 164 | ```{r} 165 | celery <- jpeg::readJPEG("figs/CeleryLunch.jpg") 166 | billMurray <- jpeg::readJPEG("figs/BillMurray.jpg") 167 | ``` 168 | ```{r celandbill, echo=FALSE, eval=FALSE} 169 | par(mfrow=c(1,2), mar=c(0,0,0,0)) 170 | display_image(celery) 171 | display_image(billMurray) 172 | par(mfrow=c(1,1), mar=c(0,0,0,0)) 173 | ``` 174 | 175 | 176 | 177 | ```{r celerybill, eval=FALSE} 178 | switch_colors(billMurray, celery, source_colors = 10) 179 | ``` 180 | 181 | 182 | 183 | ###Note 184 | 185 | There is an element of randomness in the median cut algorithm, so set your seeds carefully, and try running the algorithm a few times if you aren't happy with the results. Other ways to alter the palette: try using `choice = median`, `volume = TRUE` or change the value of `n`. 186 | 187 | ###Special Thanks 188 | 189 | There are a number of projects that inspired or helped this project along, and they deserve some recognition: 190 | 191 | [color-thief.js](http://lokeshdhakar.com/projects/color-thief/) by [Lokesh Dhakar](http://lokeshdhakar.com). 192 | 193 | [Wes Anderson Palettes](https://github.com/karthik/wesanderson) by [Karthik Ram](http://inundata.org). 194 | 195 | [this blog post](http://blenditbayes.blogspot.com/2014/05/towards-yet-another-r-colour-palette.html) from [Jo Fai Chow](http://www.jofaichow.co.uk/). 196 | 197 | and [this blog post](http://blog.ryanwalker.us/2016/01/color-quantization-in-r.html) by [Ryan Walker](http://www.ms.uky.edu/~rwalker/) 198 | 199 | Thank you all for your great work! 200 | 201 | --------------------------------------------------------------------------------