
Version 0.10
iRfcb 0.10.0
CRAN release: 2026-08-19
New features
- Added support for the per-ROI
cell_countdata produced by the diatom chain counter (Groves et al. 2026, doi:10.1093/plankt/fbaf064) through theifcb-pytorch-classifypipeline and stored in.mat,.h5and.csvclassification files. Abundance can now be reported in cells rather than images, so a chain of eight cells counts as eight.- New
ifcb_summarize_cell_counts()reports cell abundance and chain-length statistics (n_counted,mean,median,max,sd) per sample and class, and abundance per liter when given anhdr_folder.n_countedis the number of ROIs the chain counter measured, including those it found to hold a single cell. -
ifcb_extract_biovolumes()andifcb_summarize_biovolumes()gain ause_cell_countsargument. WhenTRUE,ifcb_summarize_biovolumes()adds acell_countscolumn, andcell_counts_per_literwhen anhdr_folderis supplied. -
single_cell_values(defaultc(-1, 0)) sets whichcell_countvalues are read as one cell: by default the ROIs the counter skipped (-1) and those where it found no cells (0). Any other value is used as it stands. -
cell_countsisNArather than0for a sample whose classification file carries nocell_countdata, since a zero would look the same as a taxon that was genuinely absent.countsstill reports the images, and a warning says how many files were affected. A missing value inside a file that does carrycell_countdata, such as a blank CSV cell, also makes the affected sample’scell_countsNA, and is likewise reported with a warning naming the samples and how many ROIs were missing a value. - The SHARK column template (
ifcb_get_shark_colnames()andifcb_get_shark_example()) gains anIMAGE_COUNTcolumn afterCOUNT. DividingCOUNTbyIMAGE_COUNTgives the average cells per image. That is not the same asmean_chain_length, which averages only over the images the counter actually measured, whereas the ratio includes the images it never looked at.
- New
-
ifcb_extract_biovolumes()andifcb_summarize_biovolumes()gain two arguments controlling how the Menden-Deuer and Lessard (2000) carbon equations are applied. Both keep the previous behavior by default, so carbon values do not change unless you set one.-
diatom_equationchooses the diatom equation."large"(the default) uses the large-diatom equation, asifcb-analysisdoes;"all"uses the all-sizes equation, which gives small cells more carbon;"auto"chooses per ROI by volume, keeping each equation inside the size range it was fitted for. The two disagree at the 3000 micron^3 boundary, predicting about 190 against 135 pgC, so"auto"makes carbon fall as a cell grows past it. That is why it is not the default. New helpersvol2C_diatom()andvol2C_diatom_auto()are exported. -
carbon_conversionchooses the volume the equation is applied to. The equations are fitted per cell, but an IFCB biovolume covers a whole image, which for a chain-forming diatom is the whole chain, so converting the chain volume in one go under-reports carbon."cell"converts per cell and sums over the chain, and requiresuse_cell_counts = TRUE. See?ifcb_extract_biovolumesfor how large the difference gets. - Biovolume itself is unchanged by either argument. It is still measured per image, so a chain of small cells still gives one large biovolume.
-
- Added
ifcb_qc_sample(), which checks raw IFCB samples (the.hdr,.adcand.roitriplet) and returns one row of QC metrics and flags per sample. It looks for missing files, ROI counts that disagree with the header, truncated.roifiles, run times that contradict the ADC, implausible analyzed volumes, and ROI dimensions that failed to parse. Bead runs, empty samples, oversized.roifiles and high recorded humidity or temperature are reported as advisory flags and do not failqc_pass. A check that cannot be run on a sample is reported asNAand does not fail it either, so legacy headers without aroiCountfield are not penalized for a check that could never apply. The function accepts a directory, sample names with adata_folder, or explicit file paths, and needs no Python. -
ifcb_extract_features()gains amultiblobargument. Withmultiblob = TRUEit additionally writesmultiblob/<bin>_multiblob_v4.csvfiles inside the features folder, holding the per-blob features of every blob in a region of interest with more than one blob (the slim table describes only the largest, plussummed*totals), one row per blob withroi_number,blob_numberand 18 morphological columns. This is the sidecar outputifcb-featuresintroduced in v1.2.0, which is also the minimum version required; the function stops with a clear error on older releases, since they never compute per-blob features. As upstream, a bin in which no ROI has more than one blob gets no sidecar file, so the file’s presence means the bin genuinely holds multi-blob ROIs; the skip logic reads thenumBlobscolumn of a bin’s existing feature CSV to tell whether a sidecar is expected, so a re-run withmultiblob = TRUEover a directory extracted without it re-extracts only the bins that actually need one, withoutoverwrite = TRUE.ifcb_read_features()reads these files as it stands, withmultiblob = TRUE; its multiblob filter now matches on the file name rather than the whole path, so a feature folder located somewhere under a directory withmultiblobin its name no longer reads as all-multiblob (or, with the defaultmultiblob = FALSE, as empty).
Minor improvements and fixes
-
ifcb_save_classification()now reports a missingroi_filebefore requiringhdf5r. Its tests no longer depend on the suggestedhdf5rpackage, fixing the CRAN check failures on platforms without it. - Tests using
use_python = TRUEnow skip whenscipyis unavailable, instead of lettingreticulatedownload an ephemeral Python environment mid-check. - The native MAT reader no longer runs out of memory on a truncated compressed section when R is built against zlib-ng (the system zlib on recent Fedora), where
memDecompress()grows its buffer indefinitely instead of erroring. Such sections now fall through to the incremental recovery path as intended. -
ifcb_classify_images(),ifcb_classify_sample()andifcb_classify_models()now retry transient network failures (dropped connections and HTTP 429/5xx responses) up to four times with exponential backoff. A brief server outage, such as the hosted SciLifeLab Serve instance restarting, used to fail every image it touched withCouldn't connect to server, leaving a run ofNArows mid-sample. Setoptions(iRfcb.gradio_max_tries = )to change the number of attempts; a malformed value for either retry option is reported by name rather than surfacing as a cryptic error elsewhere. -
ifcb_extract_biovolumes(),ifcb_summarize_biovolumes()andifcb_summarize_cell_counts()now stop with an error when one sample resolves to more than one classification file, for example a folder holding both a.matand an.h5for the same sample, and name the samples involved. Previously both files were read and joined, which silently doubled that sample’s counts, biovolume and carbon. - The classification-file readers no longer trip over non-class
.csvfiles in a class directory. An IFCB Dashboardclass_scoresexport ({sample}_class.csv) could be picked up and then fail with a confusingUnknown or uninitialised column: 'class'error followed by a WoRMS 400 response. Such files are now skipped with a warning naming the missing columns when a folder is supplied, and raise a clear error when passed explicitly. A valid label file named{sample}_class.csvnow resolves to the same sample as{sample}.csv, so it joins under the right sample instead of appearing as a phantom{sample}_classsample no HDR file could match. Relatedly, anhdr_folderwhose files match none of the classified samples now warns and reportsNAper-liter values instead of aborting with an unexplained join error, and theclassifiercolumn read from.matfiles is a plain character vector rather than a one-column matrix. -
ifcb_is_diatom()gains adetailsargument. WhenTRUEit returns a data frame with the WoRMS class resolved for each taxon instead of a logical vector. Use it to find genus homonyms, that is diatom genera such asNaviculaorActinocyclusthat share a name with an animal and so resolve to a non-diatom class, then add those taxa todiatom_include. A class list in which nothing resolves in WoRMS at all, such as one holding only labels likeunclassifiedordetritus, returnsNAclasses instead of failing. -
ifcb_extract_biovolumes()andifcb_summarize_biovolumes()report the diatom classification more clearly whenverbose = TRUE. The classes treated as diatoms are listed in full, classes missing from WoRMS are listed separately, and the long list of non-diatoms is reduced to a count. Previously that list was printed and truncated, making it hard to see whether a class you expected to be a diatom had been recognized as one. - Creating and editing MATLAB
ifcb-analysismanual classification files no longer requires Python.ifcb_create_class2use(),ifcb_create_manual_file(),ifcb_adjust_classes(),ifcb_correct_annotation(),ifcb_replace_mat_values()and theformat = "mat"output ofifcb_save_classification()write.matfiles with a native R implementation, producing the same output as the previousscipy.io.savematapproach. The wrapper functions that call them are Python-free as a result, soscipyandnumpyare no longer needed for annotation work. - Removed
R.matlabas a dependency. Reading.matfiles without Python now uses the native R reader as well, which decodes MATLAB UTF-16 text correctly, so accented class or path names survive whereR.matlab::readMat()could mangle them. It reads every numeric storage type MATLAB uses to hold an array compactly (int8throughuint32,singleanddouble), preserving each across a read-write round-trip, including values at the signed and unsigned 32-bit limits that R cannot hold in an integer. Multi-row character matrices, such as thefilelistTBsample list thatifcb-analysissummary files hold as one fixed-width row per sample, are read as one string per row, soifcb_read_summary()handles multi-sample summaries the same way with either reader. The reader also refuses input it cannot represent faithfully, naming the variable and the reason, reports a truncated file instead of reading it back quietly padded with zeros, and rejects declared dimensions that disagree with the data carried rather than recycling values to fill them out. The writer applies the same standard: a value that does not fit the storage type a variable was read with is refused rather than silently wrapped, andifcb_correct_annotation()andifcb_replace_mat_values()widen a classlist to double when a new class id outgrows the compact integer type MATLAB stored it in. All of this matters becauseifcb_adjust_classes()andifcb_correct_annotation()write what they read back over the same file. One thing it tolerates rather than refuses: a compressed section that ends without its stream terminator, which some classification files written by MATLAB contain. The data in those is intact, so it is decoded incrementally and read, with a warning naming the file.R.matlabhas moved toSuggests.- Note that this makes reading stricter than in 0.9.0, so a file that used to open may now stop with an error.
R.matlabwould read a.matcontaining a struct, an object, a sparse or complex array, a logical array, more than two dimensions, or a MATLAB string array (as saved byclass2use = ["a" "b"]in a recent MATLAB release), and iRfcb would then quietly write back something that was not what it read. Those files are now named and refused instead. The formats iRfcb itself deals with are unaffected: manual files,class2usefiles and classifier output written by MATLABifcb-analysisor the Python pipelines all read as before. If you do have a third-party file that no longer opens,ifcb_get_mat_names()andifcb_get_mat_variable()read it withuse_python = TRUE, which goes throughSciPyinstead, once a Python environment has been set up withifcb_py_install().
- Note that this makes reading stricter than in 0.9.0, so a file that used to open may now stop with an error.
-
ifcb_extract_features()now works with both raw-data readers used by WHOIifcb-features. Release v1.1.0 swappedpyifcbfor the lighterifcbkit, which brokeiRfcbagainst v1.1.0 and later, and sinceifcb_py_install(features = TRUE)installs the newest release by default this affected new installations. Either reader now works, and a newbackendargument (or theIRFCB_IFCB_BACKENDenvironment variable) pins one. Measurements are unaffected for the D-style bins current instruments produce. The readers differ only on older I-style bins and on ROIs with zero height, so pin a reader if you need results comparable to an earlier I-style run. Installing v1.1.0 or later also drops theh5pydependency, which had restricted which Python versions could be used. -
ifcb_extract_features()now works withifcb-featuresv1.2.0, which changed whatcompute_featuresreturns to carry the new multiblob output. Without this change every ROI failed to unpack, and because a failed ROI is skipped rather than fatal, the extraction would report success while writing feature files holding onlyroi_numberand no blob images. All earlierifcb-featuresreleases still work, and the extracted measurements are unchanged. The per-blob multiblob rows themselves are not used, since they are not part of the slim feature set. - Fixed
ifcb_extract_features()writing text instead of numbers into theEccentricity,MajorAxisLengthandMinorAxisLengthcolumns of<bin>_features_v4.csv. Onnumpy2.3 and later these values arrived as complex numbers and were written as strings such as(0.797+0j), quietly turning three numeric columns into text. They are numeric again and the values are unchanged, with one exception worth knowing about for size spectra and biovolume sums: a degenerate, one-pixel-wide blob is now measured as0rather thanNaN, so it contributes a zero instead of a missing value you could filter out. That matches upstreamifcb-featuresand thesummed*columns. To reproduce an earlier run, pinifcb-featuresv1.0.0 ornumpy < 2.3; see?ifcb_py_install. -
ifcb_extract_features()no longer prints aFutureWarningfor every region of interest from recentscikit-imagereleases, which had been breaking up the progress bar.ifcb_py_install(features = TRUE)also holdsscikit-imagebelow 0.28, the release that removes the deprecated functionsifcb_featurescalls. -
ifcb_extract_features()no longer discards a whole sequential run when one bin cannot be read. A corrupt or truncated.roiused to escape as a Python traceback, taking every bin already processed with it. Such a bin is now reported as a per-bin error like any other. -
ifcb_extract_features()gains afeature_tagargument controlling the feature file name. The default ("features") writes<bin>_features_v4.csvas before;"fea"writes<bin>_fea_v4.csv, the name the IFCB Dashboard serves. - Fixed
ifcb_volume_analyzed_from_adc()failing when given more than one ADC file, or a URL. The existence check was not vectorized, so a vector of files stopped withthe condition has length > 1even though the rest of the function already looped over them, and it rejected URLs outright even though the rest of the function handles remote files. Local paths are now checked one by one, with every missing path reported at once, and URLs pass straight through. - Realigned
ifcb_get_runtime()andifcb_volume_analyzed_from_adc()with their MATLABifcb-analysiscounterparts (IFCBxxx_readhdr.m,IFCB_volume_analyzed_fromADC.m), fixing five places where the R port had drifted from the reference.- Legacy headers, which carry
run timeandinhibit timeon a single line, were read with the inhibit time set to a copy of the run time, so every legacy sample reported a look time of zero and an analyzed volume of zero. The two values are now read from their own positions on the line, as the reference does. - Header keys are matched at the start of the line, as the reference’s
strmatchdoes. A header holding an extra key that merely ends inruntime:used to makeifcb_get_runtime()return two values, whichifcb_qc_sample()then recycled into a duplicated, failing row for that sample. - The startup-offset correction for instruments whose run/inhibit clocks lead the ADC timestamp called
mode(), which in R isbase::mode()and not the statistical mode the MATLAB reference computes, so the correction always failed withnon-numeric argument to binary operatoron exactly the instruments it was written for. - Corrupted inhibit-time rows are repaired the way the reference repairs them, from the last well-behaved row plus one typical increment per bad row, instead of using the corrupted final value as it stands. The timestamp-derived run-time fallback is now applied when the run-time clock disagrees with it, where previously it was computed and then overwritten.
- A volume that cannot be derived, for example from an ADC without run/inhibit time columns, is now
NAwith a warning instead of a silent0, matching theNaNthe reference returns. A zero looked exactly like an instrument that analyzed no water, and madeifcb_qc_sample()fail such samples for an implausible volume.
- Legacy headers, which carry
-
ifcb_correct_annotation()andifcb_replace_mat_values()now check their inputs and fail with a message you can act on. An out-of-range ROI number orcolumn_indexreports the value and the valid range instead ofsubscript out of bounds, a classlist with fewer than two columns is reported as such, and a missing input file or a.matfile without aclasslistvariable is named explicitly.ifcb_adjust_classes()likewise passes the reader’s own refusal message through when it skips a file, instead of relabeling every problem “empty or corrupted”. - Fixed
use_python = TRUEbeing ignored when Python andSciPywere in fact available.ifcb_get_mat_names(),ifcb_get_mat_variable(),ifcb_read_summary(),ifcb_count_mat_annotations(),ifcb_extract_annotated_images()andifcb_adjust_classes()decided whether they could use Python by looking forscipyinreticulate::py_list_packages(), and fell back to the R reader without saying so when it was not listed. Two things went wrong with that: on acondaenvironment the listing reports conda’s own base packages, so an installed and perfectly importablescipywas absent from it; and the check did not initialize Python, so in a fresh session it failed whatever the environment held. Availability is now settled by asking Python to import the module, and whenuse_python = TRUEis requested but Python withscipyis genuinely unavailable, the fallback to the R reader is announced with a warning (once per session) instead of happening silently. This matters most when a.matfile cannot be read by the R reader, sinceuse_python = TRUEis the documented way to read it. - Clarified the deprecation of
ifcb_read_hdr_data(hdr_folder = )andifcb_annotate_batch(adc_folder = ). Both read as though the package had renamedhdr_folderandadc_foldereverywhere, which it has not: those two functions were changed to accept a vector of file paths, so their argument was renamed to match. Functions that genuinely take a single directory, such asifcb_psd(),ifcb_summarize_biovolumes(),ifcb_summarize_cell_counts()andifcb_annotate_samples(), keephdr_folderandadc_folderand are not deprecated. - Corrected the
vol2C_lgdiatom()documentation, which said the relationship applied to diatoms above 2000 micron^3. The Menden-Deuer and Lessard (2000) large-diatom equation is for cells above 3000 micron^3. - Examples that call remote services (
ifcb_download_dashboard_data(),ifcb_download_dashboard_metadata(),ifcb_match_taxa_names()andifcb_is_diatom()) are now wrapped intry()so they fail gracefully when the service is unreachable.