Skip to contents

Absorb a layer into its surrounding color patches

Usage

absorbLayer(
  recolorize_obj,
  layer_idx,
  size_condition = function(s) s <= Inf,
  x_range = c(0, 1),
  y_range = c(0, 1),
  remove_empty_layers = TRUE,
  plotting = TRUE
)

Arguments

recolorize_obj

A recolorize object.

layer_idx

The numeric index of the layer to absorb.

size_condition

A condition for determining which components to absorb, written as a function. The default (function(l) l <= Inf) will affect all components, since they all have fewer than infinity pixels.

x_range, y_range

The rectangular bounding box (as proportions of the image width and length) for selecting patches. Patches with at least partial overlap are counted. Defaults (0-1) include the entire image. See details.

remove_empty_layers

Logical. If the layer is completely absorbed, remove it from the layer indices and renumber the existing patches? (Example: if you completely absorb layer 3, then layer 4 -> 3 and 5 -> 4, and so on).

plotting

Logical. Plot results?

Value

A recolorize object.

Details

This function works by splitting a layer into spatially distinct 'components' using imager::split_connected. A contiguous region of pixels is considered a single component. Only components which satisfy both the size_condition and the location condition (specified via x_range and y_range) are absorbed, so you can be target specific regions with (ideally) a minimum of fuss.

The size_condition is passed as a function which must have a logical vector output (TRUE and FALSE) when applied to a vector of sizes. Usually this will be some combination of greater and less than statements, combined with logical operators like & and |. For example, size_condition = function(x) x > 100 | x < 10 would affect components of greater than 100 pixels and fewer than 10 pixels, but not those with 10-100 pixels.

The x_range and y_range values set the bounding box of a rectangular region as proportions of the image axes, with the origin (0, 0) in the bottom left corner. Any patch which has at least partial overlap with this bounding box will be considered to satisfy the condition. When selecting this region, it can be helpful to plot a grid on the image first to narrow down an approximate region (see examples).

See also

editLayers for editing layers using morphological operations; thresholdRecolor for re-fitting the entire image without minor colors.

Examples


# \donttest{
img <- system.file("extdata/fulgidissima.png", package = "recolorize")

# get an initial fit using recolorize + recluster:
fit1 <- recolorize2(img, bins = 3, cutoff = 65, plotting = FALSE)
#> 
#> Using 3^3 = 27 total bins
# this looks okay, but the brown patch (3) has some speckling
# in the upper right elytron due to reflection, and the orange
# patch (4) has the same issue

# the brown patch is easier to deal with, since size thresholding alone is
# sufficient; we want to leave the stripes intact, so we'll absorb components
# that are 50-250 pixels OR fewer than 20 pixels (to get the tiny speckles),
# leaving the eyes intact
fit2 <- absorbLayer(fit1, layer_idx = 3,
                    size_condition = function(x) x <= 250 &
                      x >= 50 |
                      x < 20)


# what about the orange speckles? this is more difficult, because
# we want to retain the border around the brown stripes, but those patches
# are quite small, so size thresholding won't work

# but we just want to target pixels in that one region, so we can first
# determine a bounding box for it by plotting a grid:
plotImageArray(constructImage(fit2$pixel_assignments,
                    fit2$centers))
axis(1, line = 3); axis(2, line = 1)
abline(v = seq(0, 1, by = 0.1),
       h = seq(0, 1, by = 0.1),
       col = grey(0.2),
       lty = 2)

# x-axis range: 0.5-0.7
# y-axis range: 0.55-0.75
# let's try it:
fit3 <- absorbLayer(fit2, layer_idx = 4,
                    size_condition = function(x) x < 100,
                    x_range = c(0.5, 0.7),
                    y_range = c(0.55, 0.75))

# looks pretty good
# }