procspec() took the minimum and maximum used by opt = "minimum" and
opt = "maximum" once, up front, before fixneg and before either scaling
step ran. Each was then applied with the extremes of the data as it arrived
rather than as it stood, so opt = c("minimum", "maximum") reported scaling to
a maximum of 1 while returning (R - min) / max, which reaches 1 only for a
spectrum whose minimum was already zero. fixneg combined with either scaling
was off the same way. The extremes are now taken where they are used.adjacent() returned Rab as exactly 1 for every image. The background-only
mask was built with the same two lines as the animal-only mask, so the two
transition sets were identical. It now masks the complement: everything inside
the focal outline, or every colour class that is not a bkgID.adjacent() computed B from two sampling grids built by different rules, a
constant pixel step for the scene and a fixed number of points per axis for
the animal. They agree only on square images, and B was inflated by roughly
the aspect ratio on everything else. All three masks now share one grid.adjacent() computed B and Rt against the whole scene's transitions
rather than the object-background ones they are documented as ratios of,
B = O_a_a / O_a_b and Rt = St_a_a / St_a_b. B was bounded in [0, 1] as a
result, which the documented ratio is not. Object-background transitions are
now taken as what the scene has left once the within-object and
within-background transitions are removed.adjacent() returned Inf for B, Rt and Rab together whenever either
the focal object or the background lacked class-change transitions. Each is
now guarded by the transitions it is built from, so a single-class background
keeps B and Rt and reports Rab as NA.voloverlap() now returns the correct result when x, y (and possibly
z) columns in colsp1 and colsp2 are not in the same order.sensmodel() gains a template argument, selecting the visual pigment
template used to generate sensitivity curves. It names both the author and the
chromophore, since a template's shape depends on both, and defaults to
"govardovskii_a1", which is what every previous version of the function used.
"govardovskii_a2" adds the vitamin A2 template from the same paper, and
"ssh_a1" and "ssh_a2" the two from Stavenga, Smits and Hoenders (1993). The
A2 templates cover porphyropsin pigments, found in freshwater fish, amphibians,
and species that switch chromophore seasonally, which pavo previously could not
model at all: an A2 band is close to 20% broader than the A1 band at the same
peak sensitivity, so it is not recovered by adjusting peaksens.
"govardovskii_a2" is the one to reach for, since pairing it with the default
compares chromophores without also changing template family. The template used
is recorded as a "template" attribute on the result. Note that
beta = FALSE is recommended with either SSH template: their beta band has a
fixed peak wavelength rather than one that scales with peaksens, so for
short-wavelength pigments it pulls the maximum of the summed curve several nm
away from the value asked for, and sensmodel() warns where that shift exceeds
5 nm. The alpha band on its own is unaffected, both Govardovskii beta bands
scale with peaksens and do not have this problem, and Stavenga (2010) notes
the fixed beta peak to be a known shortcoming.
bootcoldist() gains a ci.type argument, allowing bias-corrected and
accelerated (BCa) confidence limits as an alternative to the percentile limits
it has always returned. Colour distances are bounded below by zero and typically
right-skewed, so percentile limits tend to sit off-centre; BCa corrects for that
skew and for where the empirical distance falls in the bootstrap distribution.
The acceleration term comes from a jackknife leaving out one row at a time, or
one cluster where cluster is supplied. The default remains "perc".
bootcoldist() gains a cluster argument for hierarchically structured data,
such as several patches per individual, or repeated measurements of one
individual. Bootstrap replicates then resample whole clusters rather than rows,
so non-independent observations stay together and the effective sample size is
the number of clusters. The nesting argument controls whether clusters span
the levels of by ("crossed", one draw shared across groups, preserving their
pairing) or sit within one level ("nested", drawn independently per group),
and defaults to detecting which applies. Empirical means are unaffected, and
results are unchanged when cluster is NULL.
bootcoldist() gains a correct argument, which removes the upward bias in the
distance between group means that comes of those means being estimated rather
than known. Distance is convex in the error of a mean, so two noisy centroids
sit further apart than the true ones, and two samples of one population are
separated by an apparently non-zero distance. On the squared scale the
displacement is the sum over groups of the mean squared pairwise distance among
a group's observations divided by twice their number, so it is worst for small,
variable groups and does not fall away as the true separation approaches zero.
It is therefore largest exactly where discriminability is in question.
correct = TRUE subtracts it from the empirical distance and from every
bootstrap replicate, each using the observations it drew.
Where cluster is supplied the correction works on clusters rather than rows,
matching what is resampled; from rows it can understate the displacement
several-fold. Where the design is crossed, the two group means are correlated
and the correction uses each individual's own paired difference, which carries
that covariance; treating the groups as independent would subtract several times
too much. Partly crossed designs are refused, as is nesting = "nested" where
clusters in fact span groups.
The subtraction is exactly unbiased on the squared scale. Its square root errs
slightly low, so a corrected distance is a little conservative. Exactness also
assumes clusters of roughly equal size; under marked imbalance the
between-cluster part is under-corrected and the within-cluster part
over-corrected, so the net direction depends on which dominates. The correction
is unavailable under quantum noise, in the CIELAB, CIELCh and coc spaces, for
achromatic contrast in a colourspace model, and with ci.type = "bca". The
default is FALSE, so results are unchanged unless it is asked for.
procspec() gains a new "clip" option, controlled by the new clip_range
argument to remove entire regions of the spectra and replace them by linear
interpolation. Code contributed for by @rscherrer, addressing
issue #271.
the rimg2cimg() function has been removed in favour of a custom as.cimg() method.
bootcoldist() with correct = TRUE now returns the signed corrected square as
the "dS.sq" attribute of the result. Flooring at zero is a presentation choice,
since a negative squared distance cannot be reported as a distance, but the
signed value is the quantity the correction is unbiased for and the one to carry
into a further calculation.bootcoldist() now validates achromatic, erroring where it is not a single
TRUE or FALSE.bootcoldist() now says how many bootstrap replicates failed and why, where
it previously discarded the error and reported only that "Bootstrap sampling
encountered errors". A run in which every replicate failed used to produce an
unrelated error about a comparison of length zero, and is now reported like any
other failure.bootcoldist() now fails with an informative error when boot.n is too small
for the requested alpha. Previously the lower quantile index rounded down to
zero, and the interval failed to assemble with an unrelated message about
mismatched dimnames. With the default alpha this affected any boot.n below
20.explorespec() and sensmodel() are now explicit in the function definition.as.rspec() now works out of the box with tibble, rather than requiring users to pass a standard data.frame.colspace() internal functions (dispace(), trispace(), hexagon(), etc.) have been refactored (#263), with two minor user-facing consequences:
tcspace() have been converted to warnings for consistency with the other internal colspace functionscoldist() with noise = "quantum" built the photon-noise term from
log-transformed quantum catches whenever qcatch = "Qi", rather than from the
catches themselves. The term is 2 / (Qa + Qb) and was being computed as
2 / (log Qa + log Qb), which inflates the noise, deflates the resulting
distances, and leaves the quantum model short of the neural one however bright
the illuminant. Results were correct for qcatch = "fi" input throughout. If
you have run coldist() or bootcoldist() with noise = "quantum" and
qcatch = "Qi" on any version from 2.4.0 onward, those distances need
recalculating. Reported by Changku Kang,
issue #281.coldist(). They describe a dim, photon-limited stimulus, and give small
distances rather than negative log catches. Non-positive catches remain an
error, since the noise term is undefined for them.coldist() with noise = "quantum" and achromatic = TRUE returned NA for
the achromatic distances to the reference stimuli that anchor jnd2xyz(). Those
were then read as zeros, putting the achromatic reference on top of the data.
Luminance coordinates from jnd2xyz() change for quantum-noise models; under the
default center = TRUE only the stored reference coordinates move.sensmodel() now generates the same sensitivity curve for a given peaksens
whatever range is requested. The alpha-band expression of Govardovskii et al.
(2000) contains a constant of 300 nm, which was coded as range[1]. Since the
two coincide at the default range = c(300, 700), curves were correct for that
range and for any range sharing its lower bound. Users who called
sensmodel() with a lower bound other than 300 nm should consider regenerating their
sensitivities and rerun any downstream vismodel() or coldist() results.
Everyone else is unaffected and results are unchanged.jndrot() and by extension jnd2xyz() have been adjusted for trichromats to
only allow rotations in the 2D plane. Until now, 3D rotations were allowed and
the result was projected back in 2D but this meant that the output was no
longer representing JNDs distances. Because rotate = TRUE is the default in
jnd2xyz(), we recommend you re-run any jnd2xyz() computation on
trichromats. From our tests, results stay qualitatively similar but specific
values may change.bootcoldist() now summarises colspace() objects by the arithmetic mean of
their coordinates, rather than a geometric mean of coordinates shifted by an
arbitrary constant of 100. Distances in a colour space are measured in the
coordinates themselves, so a group's centroid is their arithmetic mean. Quantum
catches and luminance channels stay geometric, since those distances are linear
in the logged values. Colspace distances change slightly. CIELAB, CIELCh and
segment, whose coordinates can be negative, previously returned NaN and now work.bootcoldist() now summarises qcatch = "fi" models by an arithmetic mean of
the log catches rather than a geometric one, so reported distances for such
models change. fi values are already log(Qi), and their geometric mean is
not a centroid: "Qi" and "fi" models of the same data disagreed by around 6%
on the bundled sicalis data, and the distance depended on the factor the
illuminant was scaled by, though scaling shifts every log catch equally and
cannot change a chromatic distance. Calls that returned NaN because an unscaled
illuminant made log catches negative now work.bootcoldist() now takes qcatch from a vismodel or colspace object's
attribute, as coldist() always has, rather than letting an argument override
it. Results change only where the two disagreed, in which case the old
behaviour was wrong.bootcoldist() now errors where any bootstrapped distance is NA, rather than
silently taking confidence limits from the wrong order statistics. The usual
cause is achromatic = TRUE on a model built without an achromatic channel.bootcoldist(raw = TRUE) now returns bootstrap distances in replicate order.
Each contrast was previously sorted independently before being returned, so
values sharing a row came from different resamplings, and neither different
contrasts nor dS and dL could be compared with one another. The values
themselves are unchanged, only their order.avg.uv and avg.v visual systems used in sensdata() and
vismodel() have been updated to match exactly the ones provided in the
original source (Endler & Mielke 2005). This may result in minute changes
in the values returned by vismodel() if you are using these parameters.viridisLite package has been removed in favour of recent base R functionssimulate_spec(), which allows for the flexible simulation of naturalistic spectra (inc. reflectance, irradiance, radiance, absorbance). See ?simulate_spec for examples and information, and the handbook for further discussion.plot.rspec() now accepts a logical labels argument (and labels.cex), to control whether text labels identifying each spectrum should be added to the outer plot margins. This was previously only available, and was required, for 'stacked' plot types, but is now optional for both 'overlay' (the default) and 'stacked' spectral plots.wlmin and wlmax arguments in summary.rspec() are being deprecated in favour of a single lim argument, for consistency across functions.summary.rspec() has been rewritten for efficiency, and now only calculates the required variables when subset is used. As a result, the function is also slightly slower (0.5 x) when calculating the full set of variables, but much faster (10 x) when calculating a subset.margins argument from various colourspace plots.rgl.triangles with triangles3d() internally to avoid a deprecation issue.x and/or y is equal to 0.aggplot() (as specified by lwd) is now consistent with line width on the plot (#235)procspec(spec, opt = "smooth") now works correctly with uninterpolated spectra. Previously, it was causing a shift of the spectra towards long-wavelengths (#234).coldist() now returns Euclidean distances when given data from the ciexyz model.as.rspec() now preserves numeric column names when converting from a tibble.procimg(), with details and examples available in the function's help documentation and the vignette.raw to bootcoldist(). When TRUE, the full raw list of bootstrapped colour-distances are returned (equal in length to the number of replicates specified by boot.n), rather than the summary statistics which are returned by default.?vismodel() or ?sensdata() for instructions on selecting it.coldist() now uses the package farver when calculating CIE2000 colour distances between points in the CIELab and CIELch spaces.plot.colspace(gamut = TRUE) (as well as triplot(), tcsplot(), tetraplot()) where it would incorrectly display "Max gamut cannot be plotted." and fail to plot the max gamut each time.noise = 'quantum'. Taking the root of negative values following log-transformation, as required when noise = 'quantum', would previously produce an uninformative error, which has been rectified.margins argument has been deprecated for cie, coc, hexagon, segment, projplot, di-, tri-, and tetra-chromatic plots. It was causing problems when later adding points to plots, and margin-handling is best left to the user anyway. The default plots may therefore look slightly different, but the margins can be adjusted to taste using the standard par(mar = c()) pathway.bootcoldist() would not run until supplied with receptor-noise model arguments (n, weber, weber.ref), even when not running the receptor-noise model.bootcoldist() would sometimes fail when given data from colourspace models containing lots of negative coordinate values. This was particularly common for the hexagon model.adjacent().options() and par() are now always set locally, including in vignettes and examples, as to prevent spillover of these changes in the user sessionbootcoldist() and adjacent() now use a random number generator that generates statistically sound values, even when ran in parallel. The output of these functions is thus expected to slightly change, even if you set the seed before.Suggests, which means it will not be installed automatically when you install pavo from CRAN and that you will need to install it yourself if you need it (for vol(type = "alpha"), vol(type = "alpha") and voloverlap(type = "alpha"))vol(), tcsvol() and voloverlap() gain a new argument type = c("convex", "alpha") to decide how you want to compute the colour volume. Please refer to the vignette vignette("pavo-5-alphashapes", package = "pavo") for more information. As a result, the argument order in these 3 function has changed. Check the documentation to update your scripts accordingly. The function summary.colspace() also gains an additional column that returns that colour volume computed with an alpha-shape of parameter alpha* in the case of tcs objects.getimg() now imports image files with uppercase extensions (e.g., JPG or PNG), such as those produced by some camera brands or processing software.data.maxqcatches attribute) now works for segment "visual model" as well. As a side effect, this removes a warning that occurred when users ran vismodel(..., visual = "segment").sensmodel() now accepts the argument sensnames, for specifying the names of the resulting sensitivity curves on-the-fly.vismodel(), by allowing users to specify the illuminant and viewer sensitivity function used when estimating XYZ values (via illum and visual arguments in colspace()).bootcoldist() is now laxer in its argument checks and accept objects that are neither vismodel or colspace objects. This means you can now use this function on quantum catches dataframe that you obtained outside of pavo, such as the MICA toolbox.summary.colspace() now prints a more explicit error when the by argument value is not a multiple of the number of rows in the colspace object (i.e., the number of spectra)teal example dataset columns have been renamed to add an additional zero in front of single digit numbers, so that column names now sort in the correct order by default.as.rspec() is now more lenient for wavelength trimming when interp = FALSE and now works even if the specified lims do not correspond to actual wl values from the input object.summary.rspec().plot() functions for colspace (triplot(), tcsplot(), tetraplot()) gain a new gamut argument to plot the maximum gamut for a given visual system and illuminant. summary.colspace() also now returns the maximum colour volume for a given visual system and illuminant that you can use to compare to the realised volume by a given dataset. More information in PR #180.future package, which offers windows and high performance computing (HPC) environments support. The progress bar is produced by the progressr package and can be customised as well. As a consequence, the cores argument in getspec(), adjacent() and classify() has been deprecated.projplot() twice for the background grid to be displayed.summary.colspace() where NULL was returned instead of summary.data.frame() for non-tcs colourspaces.bootcooldist()pavo.colrverse.comcoldist()that prevented the calculation of achromatic contrast when using custom quantum catch dataplot.rspec() now adds a linear spectrum alongside the x-axis to show the hues corresponding to each wavelength (controlled by the wl.guide boolean argument).cieplot() (and therefore plot.colspace() for CIEXYZ model) now adds the background of the CIEXYZ colour space by default. This can be turned off by switching the ciebg argument to FALSE.voloverlap() uses a different algorithm to determine volume overlaps, which means:
voloverlap() now works for trispace() objects as wellvoloverlap() has been renamed colsp1 and colsp2 instead of tcsres1 and tcsres2montecarlo option has been deprecatedvismodel() now have more explicit messages.pavo functions (excepted vismodel() and spec2rgb()) now work with sub-nm precision, for rspec objects with non-integer wavelengths.summary.colspace() no longer fails for tcs objects with only one row.vismodel() now works for monochromats as well.procspec(opt = "bin") no longer counts bin edges twice (once in each consecutive bin). This will cause changes in the bin stops compared to earlier versions of this function.coc() and categorical() spaces now return Weber luminance contrast by default when passed through coldist(), rather than nothing (as per the original publications).coldist() can now take multiple values for weber, when Weber fractions are known for all receptor classessummary.rspec() no longer errors for a single spectrum when the wavelength range does not contain 450-700nmclassify()coldist() now returns unweighted Euclidean distances for generic di-, tri-, and tetra-chromatic colspace() models. Users wishing to estimate noise-weighted distances (i.e. via the receptor-noise limited model) should continue to use coldist() directly on vismodel() objects, as previously.coldist()vismodel(), colspace(), and coldist() now always return lum and dL columns, for consistencyirrad2flux() and flux2irrad() use slightly more precise constant values, which may lead to slightly different results (less than 0.1% difference between this new version and the previous versions)getimg() can now import images from URL'scores argument in getimg() is deprecated, as image import is vectorisedclassify() when using refID and kcols for multiple imagesresize argument in procimg() now takes a percentage, rather than scaling factorcolspace(space = segment) would return both a B and (redundant) lum columnvismodel()getspec() where badly encoded characters in some spectral files would cause failurecoldist() would attempt to estimate receptor-noise weighted distances rather than cie-distances for cielch model resultsreclass to procimg(), which allows users to interactively correct areas within images that have been misclassifiedpeakshape() uses a completely different algorithm to find the FWHM. It now works as expected for spectra with multiple peaks. See PR #137 for a detailed overview of the changes.bgandilum, transmissiondata, ttvertex, vissyst) is no longer exposed to usersis.vismodel() and is.colspace() are exported to test whether an object is of class vismodel or colspace, respectivelyas.rimg()pfowl and avg.v in vismodel() and sensdata()). This removes an unnecessary but harmless warning when colspace() was used to place quantum catches of such species in the tetrahedral colour space.achro argument in coldist() has been changed for achromatic to better match the arguments from vismodel(). Older scripts that use achro should not be affected and still work as before.imager is no longer a dependency, and is only loaded if using some features of procimg().mapproj is no longer a dependency, and is only loaded if using projplot().labels.stack to plot.rspec, which allows the use of custom spectra labels in stacked plots.as.rspec, and can control the behaviour with the new argument exceed.limits.as.rspec() now accepts both numeric and character vectors to identify the wavelength column using whichwl (eg. whichwl = "wl").\classify() can now be specified using either a numeric vector (to identify by image position in a list) or character vector (to identify by image name).\aggspec() when wavelength column was previously removed by the user.cocplot() would failed whenever type graphical parameter was specified.spec2rgb() has been simplified to rely more on vismodel(). As a result, output values may be slightly different but upon testing, we found that differences between the old and the new version were barely noticeable.getspec() can now read Avantes binary files (.TRM files)summary.rspec() has been fixed to (R700 - R450)/R700. This gives the same result as before but with a flipped sign, and better reflects the original formula in the literature.getspec() has an additional argument ignore.case set to TRUE by default to ignore case in file extension matchinggetspec() would sometimes fail with files including numbers in scientific formattetraplot() to add cone names (u,s,m,l)getspec() can now read OceanOptics .ProcSpec filesgetspecf() (and the argument fast = TRUE in getspec()) have been deprecatedsummary.rspec() returned incorrect values for S7. If you use S7, please re-run your analysessummary.rspec() now properly outputs NA for monotonically decreasing spectrasubset.rspec() was provided with a logical vectorsummary.colspace() was used on a tcs objectby argument in merge.rspec() is no longer ignoredvoloverlap() when plot = TRUEvismodel() when transmission has more than one columnvismodel() that applied von Kries correction to achromatic channelfill=FALSE in voloverlap()jndplot() when suppressing the plotting of arrowscoldist() when noise = "quantum" and achro = TRUE were usedjndplot() when arrow = "none" and achro = TRUEspec2rgb() now takes into account the 390-400 nm wavelength range into account when possibleas.rspec() no longer fails with tibblesprocspec() now works for all values of binscolspace() where it would incorrectly infer a preference for a general trichromatic space, when a cie model is more appropriatebootcoldist() uses bootstrap to calculate the confidence intervals on the mean color distance between samplescoldist() when fewer samples than the number of photoreceptorsgetspec() that would cause it to crash with errorstetraplot() when saving images