|
| 1 | +#' Load image from file or URL |
| 2 | +#' |
| 3 | +#' @description simple wrapper around \code{\link[imager]{load.image}} included |
| 4 | +#' here for convenience and consitency reasons only. |
| 5 | +#' |
| 6 | +#' @param file path to file of URL |
| 7 | +#' |
| 8 | +#' @return an object of class \code{\link[imager]{cimg}} |
| 9 | +#' |
| 10 | +#' @examples |
| 11 | +#' mcr_load_image(system.file("extdata", "testim.png", package = "mincountr")) |
| 12 | +#' |
| 13 | +#' @export |
| 14 | +#' |
| 15 | +mcr_load_image <- function(file){ |
| 16 | + imager::load.image(file) |
| 17 | +} |
| 18 | + |
| 19 | +#' Plot an images brightness density distribution |
| 20 | +#' |
| 21 | +#' @description Inspecting the density distribution of an electron |
| 22 | +#' microscope image of minerals effectively allows to identify different phases |
| 23 | +#' as their distinct brightness is a key giveaway of their different chemical |
| 24 | +#' composition (and conductivity). |
| 25 | +#' |
| 26 | +#' @param x an image loaded with \code{\link{mcr_load_image}} |
| 27 | +#' |
| 28 | +#' @examples |
| 29 | +#' myimage <- mcr_load_image(system.file("extdata", "testim.png", package = "mincountr")) |
| 30 | +#' mcr_inspect_phases(myimage) |
| 31 | +#' |
| 32 | +#' @export |
| 33 | + |
| 34 | +mcr_inspect_phases <- function(x){ |
| 35 | + imgr <- imager::grayscale(x) #Making sure image is in greyscale |
| 36 | + imtb <- as.data.frame(imgr) |
| 37 | + imtidy <- tibble::as.tibble(imtb) |
| 38 | + ggplot2::ggplot(data = imtidy, ggplot2::aes(value)) + |
| 39 | + ggplot2::geom_density(kernel="gaussian") + |
| 40 | + ggplot2::scale_x_continuous(breaks=c(seq(0,1,0.1))) |
| 41 | +} |
| 42 | + |
| 43 | +#' Plot group assignment of a phase in an image |
| 44 | +#' |
| 45 | +#' @description Plots a false color image using groups of brightness that can |
| 46 | +#' either be determined from a graphical inspection of the result of |
| 47 | +#' \code{\link{mcr_inspect_phases}} or automaticalle determined by |
| 48 | +#' \code{\link{mcr_herd_minerals}} |
| 49 | +#' |
| 50 | +#' @param x an image loaded with \code{\link{mcr_load_image}} |
| 51 | +#' @param lhs a vector with the left-hand-side position of peaks observed in \code{\link{mcr_inspect_phases}} |
| 52 | +#' @param rhs a vector with the right-hand-side position of peaks observed in \code{\link{mcr_inspect_phases}} |
| 53 | +#' |
| 54 | +#' @examples |
| 55 | +#' myimage <- mcr_load_image(system.file("extdata", "testim.png", package = "mincountr")) |
| 56 | +#' |
| 57 | +#' # (Semi-)Automatic approach: |
| 58 | +#' mypeaks <- mcr_autoconstrain(myimage) |
| 59 | +#' mcr_inspect_assignement(myimage, mypeaks$x1, mypeaks$x2) |
| 60 | +#' |
| 61 | +#' # Manual assignements of brightnessgrouplimits: |
| 62 | +#' mcr_inspect_assignement( |
| 63 | +#' myimage, |
| 64 | +#' lhs = c(0, 0.3, 0.5, 0.92), |
| 65 | +#' rhs = c(0.05, 0.45, 0.65, 1) |
| 66 | +#' ) |
| 67 | +#' |
| 68 | +#' @export |
| 69 | + |
| 70 | +mcr_inspect_assignement <- function(x, lhs, rhs) { |
| 71 | + |
| 72 | + #Some input testing |
| 73 | + if(!all(is.numeric(c(lhs, rhs)))){ |
| 74 | + stop("both 'lhs' and 'rhs' must be vectors of type 'numeric'") |
| 75 | + } |
| 76 | + |
| 77 | + if(length(lhs) != length(rhs)){ |
| 78 | + stop("numeric vectors 'lhs' and 'rhs' must be of the same length") |
| 79 | + } |
| 80 | + |
| 81 | + if(any(c(lhs, rhs) < 0)){ |
| 82 | + stop("elements in numeric vectors 'lhs' ans 'rhs' must be >= 0 & <= 1") |
| 83 | + } |
| 84 | + |
| 85 | + if(any(c(lhs, rhs) > 1)){ |
| 86 | + stop("elements in numeric vectors 'lhs' ans 'rhs' must be >= 0 & <= 1") |
| 87 | + } |
| 88 | + |
| 89 | + imgr <- imager::grayscale(x) #Making sure image is in greyscale |
| 90 | + imtb <- as.data.frame(imgr) |
| 91 | + imtidy <- tibble::as.tibble(imtb) |
| 92 | + |
| 93 | + minbins <- as.vector(rbind(lhs, rhs)) |
| 94 | + |
| 95 | + # assining image pixels to phases, respectively their phases |
| 96 | + minlev<-.bincode(imtidy$value,minbins,TRUE,TRUE) |
| 97 | + lvldimg<-cbind(imtidy,minlev) |
| 98 | + |
| 99 | + ggplot2::ggplot( |
| 100 | + data = lvldimg, |
| 101 | + ggplot2::aes( |
| 102 | + x = x, y = y, fill = as.factor(minlev) |
| 103 | + ) |
| 104 | + ) + |
| 105 | + ggplot2::geom_raster() + |
| 106 | + ggplot2::labs(fill = "Phase_ID") + |
| 107 | + paletteer::scale_fill_paletteer_d(package = "ggsci", palette = "default_igv") |
| 108 | +} |
| 109 | + |
| 110 | +#' Automatic peak detection |
| 111 | +#' |
| 112 | +#' @description This function automatically determines the position of peaks |
| 113 | +#' and their half-height-width in brightness density distribution of an image. |
| 114 | +#' This is key input information necessary to assign brightness-niveaus to |
| 115 | +#' certain (mineral) phases an can be used in \code{\link{mcr_herd_minerals}} |
| 116 | +#' |
| 117 | +#' @param x an image loaded with \code{\link{mcr_load_image}} |
| 118 | +#' |
| 119 | +#' @return A \code{\link[tibble]{tibble}} containing the positions of peak maxima and half-height-width both to the left and to the right of the peak |
| 120 | +#' |
| 121 | +#' @examples |
| 122 | +#' myimage <- mcr_load_image(system.file("extdata", "testim.png", package = "mincountr")) |
| 123 | +#' mcr_autoconstrain(myimage) |
| 124 | +#' |
| 125 | +#' @export |
| 126 | +#' |
| 127 | + |
| 128 | +mcr_autoconstrain <- function(x){ |
| 129 | + imgr <- imager::grayscale(x) #Making sure image is in greyscale |
| 130 | + imtb <- as.data.frame(imgr) |
| 131 | + imtidy <- tibble::as.tibble(imtb) |
| 132 | + |
| 133 | + img_density <- stats::density(imtidy$value) |
| 134 | + |
| 135 | + # identifying turning points in the density line |
| 136 | + extreme_points <- tibble::tibble( |
| 137 | + x = img_density$x, y = img_density$y, |
| 138 | + extr = c(0, diff(sign(diff(img_density$y))), 0) |
| 139 | + ) |
| 140 | + |
| 141 | + peaks <- extreme_points %>% |
| 142 | + dplyr::select(x, extr) %>% |
| 143 | + dplyr::filter(extr == -2) %>% |
| 144 | + .$x |
| 145 | + |
| 146 | + peaks[peaks<0] <- 0 |
| 147 | + peaks[peaks>1] <- 1 |
| 148 | + |
| 149 | + valleys<- extreme_points %>% |
| 150 | + dplyr::select(x, extr) %>% |
| 151 | + dplyr::filter(extr == 2) %>% |
| 152 | + .$x |
| 153 | + |
| 154 | + valleys[valleys<0] <- 0 |
| 155 | + valleys[valleys>1] <- 1 |
| 156 | + |
| 157 | + # processing each peak individually |
| 158 | + catcher <- vector("list", length = length(peaks)) |
| 159 | + for(i in seq_along(catcher)){ |
| 160 | + |
| 161 | + # cut out the single peak |
| 162 | + if(peaks[i] == 0){ |
| 163 | + cutoff_left <- 0 |
| 164 | + cutoff_right <- valleys[valleys > peaks[i]] %>% min() |
| 165 | + if(is.infinite(cutoff_right)){cutoff_right <- 1} |
| 166 | + } else if (peaks[i] == 1){ |
| 167 | + cutoff_right <- 1 |
| 168 | + cutoff_left <- valleys[valleys < peaks[i]] %>% max() |
| 169 | + if(is.infinite(cutoff_left)){cutoff_left <- 0} |
| 170 | + } else { |
| 171 | + cutoff_left <- valleys[valleys < peaks[i]] %>% max() |
| 172 | + if(is.infinite(cutoff_left)){cutoff_left <- 0} |
| 173 | + cutoff_right <- valleys[valleys > peaks[i]] %>% min() |
| 174 | + if(is.infinite(cutoff_right)){cutoff_right <- 1} |
| 175 | + } |
| 176 | + df <- extreme_points %>% |
| 177 | + dplyr::filter(x >= cutoff_left) %>% |
| 178 | + dplyr::filter(x <= cutoff_right) %>% |
| 179 | + dplyr::mutate(Peak_id = i) |
| 180 | + |
| 181 | + # calculate half-height-width |
| 182 | + |
| 183 | + # left "border" of the actual peak |
| 184 | + if(cutoff_left == 0){ |
| 185 | + x1 <- 0 |
| 186 | + } else { |
| 187 | + x1 <- df$x[df$x < peaks[i]][which.min(abs(df$y[df$x < peaks[i]]-max(df$y)/2))] |
| 188 | + } |
| 189 | + |
| 190 | + # right "border" of the actual peak |
| 191 | + if(cutoff_right == 1){ |
| 192 | + x2 <- 1 |
| 193 | + } else { |
| 194 | + x2 <- df$x[df$x > peaks[i]][which.min(abs(df$y[df$x > peaks[i]]-max(df$y)/2))] |
| 195 | + } |
| 196 | + |
| 197 | + catcher[[i]] <- tibble::tibble( |
| 198 | + x1 = x1, |
| 199 | + peakpos = peaks[i], |
| 200 | + x2 = x2, |
| 201 | + ID = i |
| 202 | + ) |
| 203 | + |
| 204 | + } |
| 205 | + do.call("rbind", catcher) |
| 206 | +} |
| 207 | + |
| 208 | + |
| 209 | +#' Calculate phase area share |
| 210 | +#' |
| 211 | +#' @description calculate the percentage share of mineral phases of the area of |
| 212 | +#' an image. |
| 213 | +#' |
| 214 | +#' @param x an image loaded with \code{\link{mcr_load_image}} |
| 215 | +#' @param lhs a vector with the left-hand-side position of peaks observed in \code{\link{mcr_inspect_phases}} |
| 216 | +#' @param rhs a vector with the right-hand-side position of peaks observed in \code{\link{mcr_inspect_phases}} |
| 217 | +#' |
| 218 | +#' @return A \code{\link[tibble]{tibble}} containing the number of pixels and their relative share of total image area per phase |
| 219 | +#' |
| 220 | +#' @examples |
| 221 | +#' myimage <- mcr_load_image(system.file("extdata", "testim.png", package = "mincountr")) |
| 222 | +#' mypeaks <- mcr_autoconstrain(myimage) |
| 223 | +#' mcr_herd_minerals(myimage, mypeaks$x1, mypeaks$x2) |
| 224 | +#' |
| 225 | +#' @export |
| 226 | +#' |
| 227 | + |
| 228 | +mcr_herd_minerals <- function(x, lhs, rhs){ |
| 229 | + |
| 230 | + #Some input testing |
| 231 | + if(!all(is.numeric(c(lhs, rhs)))){ |
| 232 | + stop("both 'lhs' and 'rhs' must be vectors of type 'numeric'") |
| 233 | + } |
| 234 | + |
| 235 | + if(length(lhs) != length(rhs)){ |
| 236 | + stop("numeric vectors 'lhs' and 'rhs' must be of the same length") |
| 237 | + } |
| 238 | + |
| 239 | + if(any(c(lhs, rhs) < 0)){ |
| 240 | + stop("elements in numeric vectors 'lhs' ans 'rhs' must be >= 0 & <= 1") |
| 241 | + } |
| 242 | + |
| 243 | + if(any(c(lhs, rhs) > 1)){ |
| 244 | + stop("elements in numeric vectors 'lhs' ans 'rhs' must be >= 0 & <= 1") |
| 245 | + } |
| 246 | + |
| 247 | + imgr <- imager::grayscale(x) #Making sure image is in greyscale |
| 248 | + imtb <- as.data.frame(imgr) |
| 249 | + imtidy <- tibble::as.tibble(imtb) |
| 250 | + |
| 251 | + |
| 252 | + minbins <- as.vector(rbind(lhs, rhs)) |
| 253 | + |
| 254 | + # assining image pixels to phases, respectively their phases |
| 255 | + minlev<-.bincode(imtidy$value,minbins,TRUE,TRUE) |
| 256 | + lvldimg<-cbind(imtidy,minlev) |
| 257 | + |
| 258 | + sumup <- lvldimg %>% |
| 259 | + dplyr::group_by(minlev) %>% |
| 260 | + dplyr::summarize(count=dplyr::n()) %>% |
| 261 | + dplyr::ungroup() %>% |
| 262 | + dplyr::rename(Phase_ID = minlev, pixels = count) %>% |
| 263 | + dplyr::mutate( |
| 264 | + `proportion_percentage` = (pixels/sum(pixels))*100 |
| 265 | + ) |
| 266 | + |
| 267 | + return(sumup) |
| 268 | +} |
| 269 | + |
0 commit comments