Source code for dclab.cli.task_tdms2rtdc

"""Convert .tdms to .rtdc files"""
import argparse
import pathlib
import warnings

import hdf5plugin

from ..rtdc_dataset import fmt_tdms, new_dataset, RTDCWriter
from .._version import version

from . import common

[docs]def tdms2rtdc(path_tdms=None, path_rtdc=None, compute_features=False, skip_initial_empty_image=True, skip_final_empty_image=True, verbose=False): """Convert .tdms datasets to the hdf5-based .rtdc file format Parameters ---------- path_tdms: str or pathlib.Path Path to input .tdms file path_rtdc: str or pathlib.Path Path to output .rtdc file compute_features: bool If `True`, compute all ancillary features and store them in the output file skip_initial_empty_image: bool In old versions of Shape-In, the first image was sometimes not stored in the resulting .avi file. In dclab, such images are represented as zero-valued images. If `True` (default), this first image is not included in the resulting .rtdc file. skip_final_empty_image: bool In other versions of Shape-In, the final image is sometimes also not stored in the .avi file. If `True` (default), this final image is not included in the resulting .rtdc file. verbose: bool If `True`, print messages to stdout """ cmp_kw = hdf5plugin.Zstd(clevel=5) if path_tdms is None or path_rtdc is None: parser = tdms2rtdc_parser() args = parser.parse_args() path_tdms = pathlib.Path(args.tdms_path).resolve() path_rtdc = pathlib.Path(args.rtdc_path) compute_features = args.compute_features skip_initial_empty_image = not args.include_empty_boundary_images skip_final_empty_image = not args.include_empty_boundary_images verbose = True # Determine whether input path is a tdms file or a directory if path_tdms.is_dir(): # we have a directory to search files_tdms = fmt_tdms.get_tdms_files(path_tdms) if path_rtdc.is_file(): raise ValueError( f"Output path is a file, expected folder: '{path_rtdc}'!") files_rtdc = [] for path_in in files_tdms: path_in = pathlib.Path(path_in) rp = path_in.relative_to(path_tdms) # determine output file name (same relative path) rpr = path_rtdc / rp.with_suffix(".rtdc") files_rtdc.append(rpr) else: # we have a single file or a non-existent path files_tdms = [path_tdms] files_rtdc = [path_rtdc] files_tdms, files_rtdc, files_temp = common.setup_task_paths( paths_in=files_tdms, paths_out=files_rtdc, allowed_input_suffixes=[".tdms"] ) for ii in range(len(files_tdms)): path_in = files_tdms[ii] path_out = files_rtdc[ii] path_temp = files_temp[ii] if verbose: common.print_info( f"Converting {ii+1:d}/{len(files_tdms):d}: {path_in}") # create directory path_out.parent.mkdir(parents=True, exist_ok=True) # load and export dataset with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") # ignore ResourceWarning: unclosed file <_io.BufferedReader...> warnings.simplefilter("ignore", ResourceWarning) # noqa: F821 # ignore SlowVideoWarning warnings.simplefilter("ignore", fmt_tdms.event_image.SlowVideoWarning) if skip_initial_empty_image: # If the initial frame is skipped when empty, # suppress any related warning messages. warnings.simplefilter( "ignore", fmt_tdms.event_image.InitialFrameMissingWarning) with new_dataset(path_in) as ds: # determine features to export if compute_features: features = ds.features else: # consider special case for "image", "trace", and "contour" # (This will export both "mask" and "contour". # The "mask" is computed from "contour" and it is needed # by dclab for other ancillary features. We still keep # "contour" because it is original data. features = ds.features_innate common.skip_empty_image_events( ds=ds, initial=skip_initial_empty_image, final=skip_final_empty_image) # export as hdf5 ds.export.hdf5(path=path_temp, features=features, filtered=True, override=True, compression_kwargs=cmp_kw) # write logs custom_dict = {} # computed features cfeats = list(set(features) - set(ds.features_innate)) if "mask" in features: # Mask is always computed from contour data cfeats.append("mask") custom_dict["ancillary features"] = sorted(cfeats) # command log logs = {"dclab-tdms2rtdc": common.get_command_log( paths=[path_in], custom_dict=custom_dict)} # warnings log if w: logs["dclab-tdms2rtdc-warnings"] = \ common.assemble_warnings(w) logs.update(ds.logs) with RTDCWriter(path_temp, compression_kwargs=cmp_kw) as hw: for name in logs: hw.store_log(name, logs[name]) # Finally, rename temp to out path_temp.rename(path_out)
def tdms2rtdc_parser(): descr = "Convert RT-DC .tdms files to the hdf5-based .rtdc file format. " \ + "Note: Do not delete original .tdms files after conversion. " \ + "The conversion might be incomplete." parser = argparse.ArgumentParser(description=descr) parser.add_argument('--compute-ancillary-features', dest='compute_features', action='store_true', help='Compute features, such as volume or emodulus, ' + 'that are otherwise computed on-the-fly. ' + 'Use this if you want to minimize analysis ' + 'time in e.g. Shape-Out. CAUTION: ancillary ' + 'feature recipes might be subject to change ' + '(e.g. if an error is found in the recipe). ' + 'Disabling this option maximizes ' + 'compatibility with future versions and ' + 'allows to isolate the original data.') parser.set_defaults(compute_features=False) parser.add_argument('--include-empty-boundary-images', dest='include_empty_boundary_images', action='store_true', help='In old versions of Shape-In, the first or last ' + 'images were sometimes not stored in the ' + 'resulting .avi file. In dclab, such images ' + 'are represented as zero-valued images. Set ' + 'this option, if you wish to include these ' + 'events with empty image data.') parser.set_defaults(include_empty_boundary_images=False) parser.add_argument('tdms_path', metavar="TDMS_PATH", type=str, help='Input path (tdms file or folder containing ' + 'tdms files)') parser.add_argument('rtdc_path', metavar="RTDC_PATH", type=str, help='Output path (file or folder), existing data ' + 'will be overridden') parser.add_argument('--version', action='version', version=f'dclab-tdms2rtdc {version}') return parser