Title: | A Shiny Application for Automatic Measurements of Tree-Ring Widths on Digital Images |
---|---|
Description: | Use morphological image processing and edge detection algorithms to automatically measure tree ring widths on digital images. Users can also manually mark tree rings on species with complex anatomical structures. The arcs of inner-rings and angles of successive inclined ring boundaries are used to correct ring-width series. The package provides a Shiny-based application, allowing R beginners to easily analyze tree ring images and export ring-width series in standard file formats. |
Authors: | Jingning Shi [aut, cre], Wei Xiang [aut] |
Maintainer: | Jingning Shi <[email protected]> |
License: | GPL-3 |
Version: | 1.4.5 |
Built: | 2024-08-16 05:52:21 UTC |
Source: | https://github.com/ropensci/MtreeRing |
Use morphological image processing and edge detection algorithms to automatically measure tree ring widths on digital images. Users can also manually mark tree rings on species with complex anatomical structures. The arcs of inner-rings and angles of successive inclined ring boundaries are used to correct ring-width series. The package provides a Shiny-based application, allowing R beginners to easily analyze tree ring images and export ring-width series in standard file formats.
Package: | MtreeRing |
Type: | Package |
License: | GPL-3 |
Maintainer: | Jingning Shi <[email protected]> |
LazyData: | TRUE |
Jingning Shi, Wei Xiang
This function can calibrate the ring-width series using arcs of inner rings.
pith_measure(ring.data, inner.arc = TRUE, last.yr = NULL, color = "black", border.type = 16, label.cex = 1.5)
pith_measure(ring.data, inner.arc = TRUE, last.yr = NULL, color = "black", border.type = 16, label.cex = 1.5)
ring.data |
A magick image object produced by |
inner.arc |
A logical value indicating whether to calibrate the ring-width series using the arcs of inner rings. See details below. |
last.yr |
|
color |
Color for labels. |
border.type |
Symbol for ring borders. See |
label.cex |
The magnification to be used for years or border numbers. |
This function allows the user to create a path, and manually mark ring borders by clicking on the graphical window.
An example demonstrated with pictures can be found in the package vignette.
Type vignette('pith-MtreeRing')
to see this example.
If inner.arc = TRUE
, the ring-width series is calibrated using arcs
of inner rings (Duncan, 1989).
Step1. You can click the left mouse button to add a horizontal path. The path should traverse an appropriate arc (read the reference below for more details).
Step2. You can add three points to the selected arc by left-clicking. The first point should be placed on the left endpoint of the arc, and the second point is placed on the right endpoint.
After adding these two points, a vertical dashed line will be plotted automatically according to the (x,y) positions of endpoints you just added. The third points should be placed on the intersection of the vertical dashed line and the selected arc.
Step3. you are prompted to mark tree rings along the path by left-clicking on the image. Every click draws a point. Note that the left endpoint of the arc will be considered as the last ring border without the need to mark it.
After marking tree rings, the identification process does not automatically stop by itself. On the Windows platform, the identification process can be terminated by clicking the second button and selecting Stop from the menu. On the MacOS system, you can press the Escape key to terminate this process.
The ring-width series are corrected using formulas proposed by Duncan (1989).
If inner.arc = FALSE
, the user can create a path which matches
the direction of wood growth.
Step1. You can add two points by left-clicking on the image. Every click draws a point. A path passing through these two points will be plotted. The path should follow the rays from bark to pith.
Step2. You can mark tree rings along the path by left-clicking on the image. The termination of identification process is similar.
A data frame of the calibrated ring-width series. The measurements units are millimeters (mm)
Jingning Shi
Duncan R. (1989) An evaluation of errors in tree age estimates based on increment cores in Kahikatea (Dacrycarpus dacrydiodes). New Zealand Natural Sciences 16(4), 1-37.
img.path <- system.file("missing_pith.png", package = "MtreeRing") ## Read the image: t1 <- ring_read(img = img.path, dpi = 1200, plot = FALSE) ## Use the arcs of inner rings to calibrate ring-width series: t2 <- pith_measure(t1, inner.arc = TRUE, last.yr = 2016) ## Try another method to measure ring widths: t3 <- pith_measure(t1, inner.arc = FALSE, last.yr = 2016)
img.path <- system.file("missing_pith.png", package = "MtreeRing") ## Read the image: t1 <- ring_read(img = img.path, dpi = 1200, plot = FALSE) ## Use the arcs of inner rings to calibrate ring-width series: t2 <- pith_measure(t1, inner.arc = TRUE, last.yr = 2016) ## Try another method to measure ring widths: t3 <- pith_measure(t1, inner.arc = FALSE, last.yr = 2016)
Run a Shiny-based application within the system's default web browser.
ring_app_launch(launch.browser = TRUE)
ring_app_launch(launch.browser = TRUE)
launch.browser |
A logical value.
If |
launch.browser = FALSE
is not recommended, as the file renaming
does not work on the RStudio built-in browser when saving the data.
A workflow for the Shiny app can be found here: https://ropensci.github.io/MtreeRing/articles/app-MtreeRing.html. Most steps are demonstrated with a gif to make the workflow more understandable.
To stop the app, go to the R console and press the Escape key. You can also click the stop sign icon in the upper right corner of the RStudio console.
Jingning Shi, Wei Xiang
This function can calculate ring widths according to coordinates of detected ring borders.
ring_calculate(ring.data, seriesID)
ring_calculate(ring.data, seriesID)
ring.data |
A matrix or array produced by |
seriesID |
A character string specifying the column name of the ring-width series. |
A data frame. The series ID is the column name and years are row names. The measurements units are millimeters (mm).
Jingning Shi
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(ring.data = t1, seg = 3, method = 'watershed') ## Calculate ring widths from the attribute list of t2: rw.df <- ring_calculate(ring.data = t2, seriesID = "940220")
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(ring.data = t1, seg = 3, method = 'watershed') ## Calculate ring widths from the attribute list of t2: rw.df <- ring_calculate(ring.data = t2, seriesID = "940220")
This function is used to automatically detect tree ring borders along the user-defined path.
ring_detect(ring.data, seg = 1, auto.path = TRUE, manual = FALSE, method = "canny", incline = FALSE, sample.yr = NULL, watershed.threshold = "auto", watershed.adjust = 0.8, struc.ele1 = NULL, struc.ele2 = NULL, marker.correction = FALSE, default.canny = TRUE, canny.t1, canny.t2, canny.smoothing = 2, canny.adjust = 1.4, path.dis = 1, origin = 0, border.color = "black", border.type = 16, label.color = "black", label.cex = 1.2)
ring_detect(ring.data, seg = 1, auto.path = TRUE, manual = FALSE, method = "canny", incline = FALSE, sample.yr = NULL, watershed.threshold = "auto", watershed.adjust = 0.8, struc.ele1 = NULL, struc.ele2 = NULL, marker.correction = FALSE, default.canny = TRUE, canny.t1, canny.t2, canny.smoothing = 2, canny.adjust = 1.4, path.dis = 1, origin = 0, border.color = "black", border.type = 16, label.color = "black", label.cex = 1.2)
ring.data |
A magick image object produced by |
seg |
An integer specifying the number of image segments. |
auto.path |
A logical value. If |
manual |
A logical value indicating whether to skip the automatic
detection. If |
method |
A character string specifying how ring borders are detected.
It requires one of the following characters: |
incline |
A logical value indicating whether to correct ring widths.
If |
sample.yr |
|
watershed.threshold |
The threshold used for producing the marker image, either a numeric from 0 to 1, or the character "auto" (using the Otsu algorithm), or a character of the form "XX%" (e.g., "58%"). |
watershed.adjust |
A numeric used to adjust the Otsu threshold.
The default is 1 which means that the threshold will not be adjusted.
The sizes of early-wood regions in the marker image will reduce along
with the decrease of |
struc.ele1 |
|
struc.ele2 |
|
marker.correction |
A logical value indicating whether to relabel early-wood regions by comparing the values of their left-side neighbours. |
default.canny |
A logical value. If |
canny.t1 |
A numeric giving the threshold for weak edges. |
canny.t2 |
A numeric giving the threshold for strong edges. |
canny.smoothing |
An integer specifying the degree of smoothing. |
canny.adjust |
A numeric used as a sensitivity control factor for the Canny edge detector. The default is 1 which means that the sensitivity will not be adjusted. The number of detected borders will reduce along with the increase of this value. |
path.dis |
A numeric specifying the perpendicular distance between
two paths when the argument |
origin |
A numeric specifying the origin in smoothed gray to find
ring borders. See |
border.color |
Color for ring borders. |
border.type |
Symbol for ring borders. See |
label.color |
Color for years and border numbers. |
label.cex |
The magnification to be used for years and border numbers. |
If auto.path = FALSE
, the user can create a rectangular sub-image
and a horizontal path by interactively clicking on the tree ring image.
The automatic detection will be performed within this rectangular
sub-image.
To create a sub-image and a path, follow these steps.
Step 1. Select the left and right edges of the rectangle
The user can point the mouse at any desired locations and click the left mouse button to add each edge.
Step 2. Select the top and bottom edges of the rectangle
The user can point the mouse at any desired locations and click the left mouse button to add each edge. The width of the rectangle is defined as the distance between the top and bottom edges, and should not be unnecessarily large to reduce time consumption and memory usage. Creating a long and narrow rectangle if possible.
Step 3. Create a path
After creating the rectangular sub-image, the user can add a horizontal
path by left-clicking on the sub-image (generally at the center of the
sub-image, try to choose a clean defect-free area). Ring borders and
other markers are plotted along this path. If incline = TRUE
,
two paths are added simultaneously.
After creating the sub-image and the path, this function will open several
graphics windows and plot detected ring borders on image segments. The
number of image segments is controlled by the argument seg
.
Argument method
determines how ring borders are identified.
If method = "watershed"
, this function uses the watershed algorithm
to obtain ring borders (Soille and Misson, 2001).
If method = "canny"
, this function uses the Canny algorithm
to detect borders.
If method = "lineardetect"
, a linear detection algorithm from the
package measuRing
is used to identify ring borders (Lara
et al., 2015). Note that incline = TRUE
is not supported in this
mode, and path will be automatically created at the center of the image.
If the argument method = "watershed"
or "canny"
, the original
image is processed by morphological openings and closings using rectangular
structuring elements of increasing size before detecting borders. The first
small structuring element is used to remove smaller dark spots in early
wood regions, and the second large structuring element is used to remove
light strips in late wood regions. More details about morphological
processing can be found at Soille and Misson (2001).
A matrix (grayscale image) or array (color image) representing the tree-ring image.
This function uses locator
to record mouse
positions so it only works on "X11", "windows" and "quartz" devices.
Jingning Shi
Soille, P., Misson, L. (2001) Tree ring area measurements using morphological image analysis. Canadian Journal of Forest Research 31, 1074-1083. doi: 10.1139/cjfr-31-6-1074
Lara, W., Bravo, F., Sierra, C.A. (2015) measuRing: An R package to measure tree-ring widths from scanned images. Dendrochronologia 34, 43-50. doi: 10.1016/j.dendro.2015.04.002
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200, plot = FALSE) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(t1, seg = 3, method = 'watershed', border.color = 'green')
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200, plot = FALSE) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(t1, seg = 3, method = 'watershed', border.color = 'green')
This function can remove existing ring borders or add new borders.
ring_modify(ring.data, del = NULL, del.u = NULL, del.l = NULL, add = FALSE)
ring_modify(ring.data, del = NULL, del.u = NULL, del.l = NULL, add = FALSE)
ring.data |
A matrix or array produced by |
del |
A numeric vector giving the border numbers to be removed. |
del.u |
A numeric vector giving the border numbers to be removed on the upper path. |
del.l |
A numeric vector giving the border numbers to be removed on the lower path. |
add |
A logical value indicating whether to add new ring borders. |
This function is used to remove existing ring borders, or to add new borders by interactively clicking on the image segments.
If the user creates one path (incline = FALSE
), the argument
del
is used to remove ring borders. If the user creates two paths
(incline = TRUE
), arguments del.u
and del.l
are used
to remove ring borders.
If add = TRUE
, graphics windows opened by ring_detect
will be activated sequentially. When a graphics window is activated,
the user can add new borders by left-clicking the mouse along the path.
Every click draws a point representing the ring border.
Type vignette('detection-MtreeRing')
to see
an example of adding ring borders.
The identification process does not automatically stop by itself.
On the Windows system, the identification process can be terminated by pressing the right mouse button and selecting Stop from the menu.
On the MacOS system, for a X11 device the identification process is terminated by pressing any mouse button other than the first, and for a quartz device this process is terminated by pressing the ESC key.
Once the user terminates the identification process, the current
graphics window will be closed automatically, and the graphics window of
the following segment is activated. When all graphics windows are closed,
ring_modify
will re-open graphics windows and plot new borders.
This function can perform both deletion and addition in one call. The removal of ring borders takes precedence over addition.
A matrix (grayscale image) or array (color image) representing the tree ring image.
Jingning Shi
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(ring.data = t1, seg = 3, method = 'watershed') ## Do not modify t2, but create a new array object t3. ## Remove some borders without adding new borders: t3 <- ring_modify(ring.data = t2, del = c(1, 3, 5, 19:21), add = FALSE)
img.path <- system.file("001.png", package = "MtreeRing") ## Read a tree ring image: t1 <- ring_read(img = img.path, dpi = 1200) ## Split a long core sample into 3 pieces to ## get better display performance and use the ## watershed algorithm to detect ring borders: t2 <- ring_detect(ring.data = t1, seg = 3, method = 'watershed') ## Do not modify t2, but create a new array object t3. ## Remove some borders without adding new borders: t3 <- ring_modify(ring.data = t2, del = c(1, 3, 5, 19:21), add = FALSE)
This function can read an image file from the hard disk and plot it in a newly-opened graphics device.
ring_read(img, dpi = NULL, RGB = c(0.299, 0.587, 0.114), plot = FALSE, rotate = 0, magick = TRUE)
ring_read(img, dpi = NULL, RGB = c(0.299, 0.587, 0.114), plot = FALSE, rotate = 0, magick = TRUE)
img |
A character string indicating the path of the image file. Supported formats include png, tiff, jpg and bmp. |
dpi |
An integer specifying the dpi of the image file. A minimum of 300 dpi is required when running automatic detection. |
RGB |
A numeric vector of length 3 giving the weight of RGB channels. |
plot |
A logical value indicating whether to plot the tree ring image
when reading it. If |
rotate |
An integer specifying how many degrees to rotate (clockwise).
It requires one of the following values:
|
magick |
A logical value. If |
Proper image preparation has a great influence on the measurement of ring widths. A tree-ring image should not contain irrelevant or redundant features, such as wooden mounts where cores are glued. The larger the file size of an image, the slower the image processing operation will be.
Pith side of a wood sample should be placed on the right side
of a graphics window. Use rotate
to change its position.
It is highly recommended to use the default value magick = TRUE
,
because magick
can significantly reduce the memory usage
when reading a large file.
If image data is stored in a non-standard format, image reading may fail.
In that case you can set magick = FALSE
to
avoid the use of magick
.
A magick image object containing the image data.
Jingning Shi
img.path <- system.file("001.png", package = "MtreeRing") ## Read and plot the image: t1 <- ring_read(img = img.path, dpi = 1200, plot = TRUE)
img.path <- system.file("001.png", package = "MtreeRing") ## Read and plot the image: t1 <- ring_read(img = img.path, dpi = 1200, plot = TRUE)