--- title: "Reading and Writing Images" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Reading and Writing Images} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Reading TIFF files Check out the following video: ![](`r system.file("img", "Rlogo-banana.gif", package = "ijtiff")`) As you can see, it's a colour video of a banana dancing in front of the R logo. Hence, it has colour channel (red, green and blue) and frame (a video is comprised of several _frames_) information inside. I have this video saved in a TIFF file. ```{r dancing-banana-path} path_dancing_banana <- system.file("img", "Rlogo-banana.tif", package = "ijtiff") print(path_dancing_banana) ``` To read it in, you just need `read_tif()` and the path to the image. ```{r read-dancing-banana} pacman::p_load(ijtiff, magrittr) img_dancing_banana <- read_tif(path_dancing_banana) ``` Let's take a peek inside of `img_dancing_banana`. ```{r peek} print(img_dancing_banana) ``` You can see it's a `r length(dim(img_dancing_banana))`-dimensional array. The last two dimensions are `r dplyr::nth(dim(img_dancing_banana), -2)` and `r dplyr::nth(dim(img_dancing_banana), -1)`; this is because these are the channel and frame slots respectively: the image has `r dplyr::nth(dim(img_dancing_banana), -2)` channels (red, green and blue) and `r dplyr::nth(dim(img_dancing_banana), -1)` frames. The first two dimensions tell us that the images in the video are `r dim(img_dancing_banana)[1]` pixels tall and `r dim(img_dancing_banana)[2]` pixels wide. The image object is of class `ijtiff_img`. This guarantees that it is a 4-dimensional array with this structure. The attributes of the `ijtiff_img` give information on the various TIFF tags that were part of the TIFF image. You can read more about various TIFF tags at https://www.awaresystems.be/imaging/tiff/tifftags.html. To read just the tags and not the image, use the `read_tags()` function. Let's visualize the constituent parts of that 8-frame, colour TIFF. ```{r red-blue-green-banana, echo=FALSE, message=FALSE, out.width='100%', dpi=300, fig.height=0.9} d <- dim(img_dancing_banana) reds <- purrr::map(seq_len(d[4]), ~ img_dancing_banana[, , 1, .]) %>% purrr::reduce(cbind) greens <- purrr::map(seq_len(d[4]), ~ img_dancing_banana[, , 2, .]) %>% purrr::reduce(cbind) blues <- purrr::map(seq_len(d[4]), ~ img_dancing_banana[, , 3, .]) %>% purrr::reduce(cbind) to_display <- array(0, dim = c(3 * nrow(reds), ncol(reds), 3, 1)) to_display[seq_len(nrow(reds)), , 1, ] <- reds to_display[seq_len(nrow(reds)) + nrow(reds), , 2, ] <- greens to_display[seq_len(nrow(reds)) + 2 * nrow(reds), , 3, ] <- blues display(to_display) ``` There you go: 8 frames in 3 colours. ### Reading only certain frames It's possible to read only certain frames. This can be a massive time and memory saver when working with large images. Suppose we only want frames 3, 5 and 7 from the image above. ```{r threefiveseven} img_dancing_banana357 <- read_tif(path_dancing_banana, frames = c(3, 5, 7)) ``` Let's visualize again. ```{r red-bblue-green-banana357, echo=FALSE, message=FALSE, out.width='100%', dpi=300, fig.height=0.9} d <- dim(img_dancing_banana357) reds <- purrr::map(seq_len(d[4]), ~ img_dancing_banana357[, , 1, .]) %>% purrr::reduce(cbind) greens <- purrr::map(seq_len(d[4]), ~ img_dancing_banana357[, , 2, .]) %>% purrr::reduce(cbind) blues <- purrr::map(seq_len(d[4]), ~ img_dancing_banana357[, , 3, .]) %>% purrr::reduce(cbind) to_display <- array(0, dim = c(3 * nrow(reds), ncol(reds), 3, 1)) to_display[seq_len(nrow(reds)), , 1, ] <- reds to_display[seq_len(nrow(reds)) + nrow(reds), , 2, ] <- greens to_display[seq_len(nrow(reds)) + 2 * nrow(reds), , 3, ] <- blues display(to_display) ``` Just in case you're wondering, it's not currently possible to read only certain channels. ### More examples If you read an image with only one frame, the frame slot (4) will still be there: ```{r one-frame, dpi=300, fig.height=0.5, fig.width=0.5} path_rlogo <- system.file("img", "Rlogo.tif", package = "ijtiff") img_rlogo <- read_tif(path_rlogo) dim(img_rlogo) # 4 channels, 1 frame class(img_rlogo) display(img_rlogo) ``` You can also have an image with only 1 channel: ```{r one-channel, dpi=300, fig.height=0.5, fig.width=0.5} path_rlogo_grey <- system.file("img", "Rlogo-grey.tif", package = "ijtiff") img_rlogo_grey <- read_tif(path_rlogo_grey) dim(img_rlogo_grey) # 1 channel, 1 frame display(img_rlogo_grey) ``` ## Writing TIFF files To write an image, you need an object in the style of an `ijtiff_img` object (see `help("ijtiff_img", package = "ijtiff")`). The basic idea is to have your image in a 4-dimensional array with the structure `img[y, x, channel, frame]`. Then, to write this image to the location `path`, you just type `write_tif(img, path)`. ```{r write-tif} path <- tempfile(pattern = "dancing-banana", fileext = ".tif") print(path) write_tif(img_dancing_banana, path) ``` ## Reading text images Note: if you don't know what text images are, see `vignette("text-images", package = "ijtiff")`. You may have a text image that you want to read (but realistically, you might never). ```{r read-txt-img} path_txt_img <- system.file("img", "Rlogo-grey.txt", package = "ijtiff") txt_img <- read_txt_img(path_txt_img) ``` ## Writing text images Writing a text image works as you'd expect. ```{r, write-txt-img} write_txt_img(txt_img, path = tempfile(pattern = "txtimg", fileext = ".txt")) ```