diff --git a/.github/workflows/apple.yml b/.github/workflows/apple.yml index 17fff68..4d609e5 100644 --- a/.github/workflows/apple.yml +++ b/.github/workflows/apple.yml @@ -8,8 +8,8 @@ run-name: ${{ github.actor }}::pytest pcdl library on mac os x; the latest pytho on: push: branches: ["master", "v3", "v4"] - pull_request: - branches: ["master", "v3", "v4"] + #pull_request: + # branches: ["master", "v3", "v4"] jobs: build-macosx: @@ -17,8 +17,8 @@ jobs: strategy: fail-fast: false matrix: - #python-version: ["3.12"] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.13"] + #python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] env: PYTHONPATH: /Users/runner/work/physicelldataloader/physicelldataloader @@ -33,7 +33,7 @@ jobs: run: | brew install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest aicsimageio anndata matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk python -m pip install /Users/runner/work/physicelldataloader/physicelldataloader -v #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: lint with flake8 @@ -45,4 +45,3 @@ jobs: - name: test with pytest run: | pytest - diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 933d075..8f0aa06 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -8,8 +8,8 @@ run-name: ${{ github.actor }}::pytest pcdl library on linux os; all python3 vers on: push: branches: ["master", "v3", "v4"] - pull_request: - branches: ["master", "v3", "v4"] + #pull_request: + # branches: ["master", "v3", "v4"] jobs: build-linux: @@ -17,8 +17,8 @@ jobs: strategy: fail-fast: false matrix: - #python-version: ["3.12"] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.13"] + #python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] env: PYTHONPATH: /home/runner/work/physicelldataloader/physicelldataloader @@ -33,7 +33,7 @@ jobs: run: | sudo apt install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest aicsimageio anndata matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk python -m pip install /home/runner/work/physicelldataloader/physicelldataloader -v #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: lint with flake8 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e2fbe83..f464fcc 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -8,8 +8,8 @@ run-name: ${{ github.actor }}::pytest pcdl library on windows os; the latest pyt on: push: branches: ["master", "v3", "v4"] - pull_request: - branches: ["master", "v3", "v4"] + #pull_request: + # branches: ["master", "v3", "v4"] jobs: build-windows: @@ -17,8 +17,8 @@ jobs: strategy: fail-fast: false matrix: - #python-version: ["3.12"] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.13"] + #python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] env: PYTHONPATH: D:\a\physicelldataloader\physicelldataloader @@ -33,7 +33,7 @@ jobs: run: | choco install ffmpeg imagemagick python -m pip install --upgrade pip - python -m pip install flake8 pytest aicsimageio anndata matplotlib numpy pandas requests scipy vtk + python -m pip install flake8 pytest anndata bioio matplotlib numpy pandas requests scipy vtk python -m pip install D:\a\physicelldataloader\physicelldataloader -v #echo 'set PYTHONPATH=D:\a\physicelldataloader\physicelldataloader' >> $GITHUB_ENV #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi diff --git a/README.md b/README.md index 1dacc92..e884ad2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ![physicellcdataloader logo & title](man/img/physicelldataloader_title_v3.0.0.png) - ## Abstract: physicelldataloader (pcdl) provides a platform independent, python3 based, [pip](https://en.wikipedia.org/wiki/Pip_(package_manager)) installable interface @@ -20,7 +19,7 @@ The pcdl python3 library maintains three branches: ## Header: + Language: python [>= 3.9](https://devguide.python.org/versions/) -+ Library dependencies: aicsimageio, anndata, matplotlib, numpy, pandas, (requests), scipy, vtk ++ Library dependencies: anndata, bioio, matplotlib, numpy, pandas, (requests), scipy, vtk + Date of origin original PhysiCell-Tools python-loader: 2019-09-02 + Date of origin pcdl fork: 2022-08-30 + Doi: https://doi.org/10.5281/ZENODO.8176399 @@ -123,10 +122,14 @@ Developers, please make pull requests to the https://github.com/elmbeech/physice + evt generate lineage tree graph output files. + evt add neuroglancer ome.tiff support. -+ switch from aicsimageio to bioio library, when the library is ripe (napari has switched to bioio). - ++ evt add DataDiVR support. ## Release Notes: ++ version 3.3.4 (2025-03-07): elmbeech/physicelldataloader + + replace the **aicsimageio** library dependency with its successor **bioio**. special thanks to Joel Eliason! + + **make_ome_tiff** can handle automatically generated file names with > 255 characters. special thank to Genevieve Stein-O'Brien and DanielBergman! + + **get_mesh_spacing** handels now an edge case correctly that would have resulted in a division by zero. special thanks to Randy Heiland! + + version 3.3.3 (2025-01-10): elmbeech/physicelldataloader + bug fix **plot_contour** plot orientation. special thanks to Marco Ruscone! + add test data for new improved **unittest physicell model**. special thanks to Nick Oldfather! diff --git a/man/TUTORIAL_blender.md b/man/TUTORIAL_blender.md index fe53097..d5e05ca 100644 --- a/man/TUTORIAL_blender.md +++ b/man/TUTORIAL_blender.md @@ -66,25 +66,21 @@ To learn more about Blender and BVTK Node plugin, please study the official docu ## ✨ Handle ome tiff files -The blender bioxel nodes plugin allows us load ome tiff files into blender. +The blender bioxel nodes plugin allows us load single time step ome tiff files into blender. -### Generate vtk files from the command line +### Generate ome tiff files from the command line ```bash -pcdl_make_conc_vtk output -``` -```bash -pcdl_make_cell_vtk output +pcdl_make_ome_tiff output --collapse false ``` -### Generate vtk files from within python +### Generate ome tiff files from within python ```python import pcdl mcdsts = pcdl.TimeSeries('output/') -mcdsts.make_conc_vtk() -mcdsts.make_cell_vtk() +mcdsts.make_ome_tiff(collapse=False) ``` ### The blender bioxel nodes plugin diff --git a/man/TUTORIAL_python3_napari.md b/man/TUTORIAL_python3_napari.md index 8dc3706..e6e6908 100644 --- a/man/TUTORIAL_python3_napari.md +++ b/man/TUTORIAL_python3_napari.md @@ -8,13 +8,8 @@ https://github.com/AllenCellModeling/napari-aicsimageio ## Install napari -And install the [aicsimageio](https://github.com/AllenCellModeling/aicsimageio) library, -which installs the "ome-types" napari plugin, -that napari can read ome.tiff images inclusive ome metadata. - ```bash pip3 install napari[all] -pip3 install aicsimageio ``` diff --git a/man/TUTORIAL_python3_ometiff.md b/man/TUTORIAL_python3_ometiff.md index abb3dd6..1274219 100644 --- a/man/TUTORIAL_python3_ometiff.md +++ b/man/TUTORIAL_python3_ometiff.md @@ -14,7 +14,7 @@ and [TUTORIAL_fiji_imagej.md](https://github.com/elmbeech/physicelldataloader/bl Additionally ome.tiff, tiff, png, and jpeg files can as well be loaded back in to python as [numpy](https://numpy.org/) array, for example with the [sci-kit image](https://scikit-image.org/) library (image data only). -Besides that, ome.tiff files can be loaded with Allen Institute for Cell Science's [aicsimageio](https://github.com/AllenCellModeling/aicsimageio) library and possibly with its successor library [bioio](https://github.com/bioio-devs/bioio) (image and metadata). +Besides that, ome.tiff files can be loaded with the [bioio](https://github.com/bioio-devs/bioio) library (image and metadata). ### Save pcdl data constructs from the command line into tiff and ome.tiff files @@ -64,12 +64,12 @@ a_ome.shape # (25, 2, 200, 300) ``` -### Load ome.tiff files as AICSImage object into python +### Load ome.tiff files as BioImage object into python ```python -from aicsimageio import AICSImage +from bioio import BioImage -img = AICSImage('output/timeseries_ID.ome.tiff') +img = BioImage('output/timeseries_ID.ome.tiff') img.shape # (25, 2, 1, 200, 300) ``` ```python diff --git a/pcdl/VERSION.py b/pcdl/VERSION.py index bbeb48b..6c61f23 100644 --- a/pcdl/VERSION.py +++ b/pcdl/VERSION.py @@ -1 +1 @@ -__version__ = '3.3.3' +__version__ = '3.3.4' diff --git a/pcdl/ometiff2neuro.py b/pcdl/ometiff2neuro.py new file mode 100644 index 0000000..5cb67aa --- /dev/null +++ b/pcdl/ometiff2neuro.py @@ -0,0 +1,266 @@ +######### +# title: ometiff2neuro.py +# +# language: python3 +# date: 2022-02-16 +# license: MIT +# author: jason lu, viviana kwong, elmar bucher +# +# installation: +# conda create -n neuro python=3 # generate an own python environment for to run this code. +# conda activate neuro # activate the generated python environment. +# pip install neuroglancer # the basics +# pip install ipython # for coding +# pip install matplotlib # for expression value color maps +# pip install scikit-image # for loading images as numpy array and signal thresh +# #pip install bioio # for extracting ometiff metadata +# +# test dataset: +# conda activate neuro +# pip install synapseclient # for downloading the test dataset from synapse +# synapse get -r syn26848775 # download test dataset into the current work directory folder. +# +# run: +# python3 -i ometiff2neuro.py +# +# description: +# script to render multi channel multi slice (ome)tiff files into the +# neuroglancer software. +# +# the script here makes use of the neuroglancer python library and is based +# on the neuroglancer/python/examples/example.py code. +# with this script, it is possible to load three-dimensional single +# time step ome-tiff files straight into the neuroglancer software. +# for each channel, mesh generation, rendering, and expression intensity +# coloring is done on the fly. +# channels can be viewed together or alone by toggling them on or off in +# the neuroglancer user interface. +# +# references: +# + https://www.openmicroscopy.org/ome-files/ +# + https://github.com/google/neuroglancer +# + https://github.com/google/neuroglancer/tree/master/python +######### + + +# library +import argparse +from matplotlib import cm +import neuroglancer +import neuroglancer.cli +import numpy as np +from skimage import exposure, filters, io, util +import sys + + +# functions +def ometiff2neuro( + o_state, + s_pathfile_tiff, + ls_channel_label = None, # ['DNA1','PD1','TLR3','SOX10', 'DNA2','CD163','CD3D','PDL1','DNA3','CD4','ICOS','HLADPB1','DNA4','CD8A','CD68','GZMB','DNA5','CD40L','LAG3','HLAA','DNA6','SQSTM','VIN','TIM3', 'DNA7','LAMP1/CD107A','PDL1_2','PD1_2', 'nuc_segement'], # dataset dependent information. + o_intensity_cm = cm.gray, # None + b_intensity_norm = True, # False + o_thresh = filters.threshold_li, # None + di_coor = {'c':1, 'z':0, 'y':2, 'x':3}, # dataset dependent information. + di_nm = {'z':200, 'y':108, 'x':108}, # microscope dependent information. + e_render = None, # {0, 'CD4', 'CD8A', 'GZMB', 28}, + ): + ''' + input: + o_state: neuroglancer viewer state object. + + s_pathfile_tiff: file name and path to input tiff file. + + ls_channel_label: list of channel labels. + default is None. + if None, hexadecimal channel labels will be generated. + this is dataset dependent information. + in future, if ometiff metadata is provided, this information will be extracted from the ometiff metadata. + + o_intensity_cm: matlab color map object, used to display expression intensity values. + default is cm.gray. + if None, no intensity layers will be generated. + + b_intensity_norm: boolean. + default is True. + if True, expression intensity values will be cut by the 2.5 and 97.5 percentiles, to remove outliers, + and intensity will be stretched over the whole range. + + o_thresh: skimage.filter thresh method object to threshold non-threshed data. + default is filters.threshold_li. + this is dataset dependent information. + set None if the tiff contains no raw but already threshed or segmentation mask data! + + di_coor: dictionary of integers, to link c,z,y, and x channel to the corresponding tiff (numpy array) columns. + this is dataset dependent information. + in future, if ometiff metadata is provided, this information will be extracted from the ometiff metadata. + + di_nm: dictionary of integers, to specify z slice distance, and y x pixel size in nanometer. + this is microscope dependent information. + in future, if ometiff metadata is provided, this information will be extracted from the ometiff metadata. + + e_render: set of channel marker labels and/or integers to specify which markers should be rendered into neurogalncer. + default is None. + if None, all channels will be rendered into neuroglancer (if enough RAM is available). + + output: + local url where the rendered data can be viewed. + + description: + function to render multi channel multi slice (ome)tiff files into the neuroglancer software. + ''' + print(f'\nprocessing: {s_pathfile_tiff}') + + # load tiff image as numpy array + a_img = io.imread(s_pathfile_tiff) + print(f'image shape ({[m[0] for m in sorted(di_coor.items(), key=lambda n: n[1])]}): {a_img.shape}') + + # channel count + i_channel = a_img.shape[di_coor['c']] + + # handle channel labels + if ls_channel_label is None: + ls_channel_label = [hex(n) for n in range(i_channel)] + elif len(ls_channel_label) != i_channel: + sys.exit(f'Error @ ometiff2neuro : ls_channel_label shape (len(ls_channel_label)) does not match channel shape {i_channel}.') + print(f'ls_channel_label: {ls_channel_label}') + + # handle set with labels from channels that will be rendered + if e_render is None: + e_render = set(range(i_channel)) + + # generate neuroglancer layers # + i_c = di_coor['c'] + for i_n in range(i_channel): + s_label = ls_channel_label[i_n] + print(f'check channel {i_n}/{i_channel}: {s_label}') + if (i_n in e_render) or (s_label in e_render): + print(f'rendering channel {i_n}/{i_channel}: {s_label}') + + # extract data and xyz coordinate columns + i_x = di_coor['x'] + i_y = di_coor['y'] + i_z = di_coor['z'] + if i_c == 0: + a_channel = a_img[i_n,:,:,:] + elif i_c == 1: + a_channel = a_img[:,i_n,:,:] + if di_coor['x'] > i_c: + i_x -= 1 + if di_coor['y'] > i_c: + i_y -= 1 + if di_coor['z'] > i_c: + i_z -= 1 + elif i_c == 2: + a_channel = a_img[:,:,i_n,:] + if di_coor['x'] > i_c: + i_x -= 1 + if di_coor['y'] > i_c: + i_y -= 1 + if di_coor['z'] > i_c: + i_z -= 1 + elif i_c == 3: + a_channel = a_img[:,:,:,i_n] + else: + sys.exit(f'Error @ ometiff2neuro : the source code is broken. please, fix the script.') + + + # 3D rendering # + # thresh data + a_thresh = a_channel.copy() + if not (o_thresh is None): + r_thresh = o_thresh(a_thresh) + a_thresh[a_thresh < r_thresh] = 0 + ab_thresh = a_thresh > 0 + + # shape + a_shape = np.zeros(ab_thresh.shape, dtype=np.uint32) + a_shape[ab_thresh] = i_n + 1 + + # generate neuroglancer object + ls_name = [None, None, None] + ls_name[i_x] = 'x' + ls_name[i_y] = 'y' + ls_name[i_z] = 'z' + li_scale = [None, None, None] + li_scale[i_x] = di_nm['x'] + li_scale[i_y] = di_nm['y'] + li_scale[i_z] = di_nm['z'] + state.layers.append( + name = s_label, + layer = neuroglancer.LocalVolume( + data = a_shape, + dimensions = neuroglancer.CoordinateSpace( + # rgb, x, y, z + names = ls_name, + scales = li_scale, + units = ['nm', 'nm', 'nm'], + ), + ), + ) + + + # expression intensity rendering # + if not (o_intensity_cm is None): + + # normalize expression values by clip by two sigma and scale over the whole uint range + if (b_intensity_norm): + i_min_clip = int(np.percentile(a_channel, 2.5)) + i_max_clip = int(np.percentile(a_channel, 97.5)) + a_clipped = np.clip(a_channel, a_min=i_min_clip, a_max=i_max_clip) + a_channel = exposure.rescale_intensity(a_clipped, in_range='image') # 16 or 8[bit] normalized + + # translate intensity by color map + a_8bit = util.img_as_ubyte(a_channel) + a_intensity = o_intensity_cm(a_8bit, alpha=None, bytes=True)[:,:,:,0:3] + + # generate neuroglancer object + ls_name = [None, None, None,'c^'] + ls_name[i_x] = 'x' + ls_name[i_y] = 'y' + ls_name[i_z] = 'z' + li_scale = [None, None, None, 3] + li_scale[i_x] = di_nm['x'] + li_scale[i_y] = di_nm['y'] + li_scale[i_z] = di_nm['z'] + state.layers.append( + name = s_label + '_intensity', + layer = neuroglancer.LocalVolume( + data = a_intensity, + dimensions = neuroglancer.CoordinateSpace( + # rgb, x, y, z + names = ls_name, + scales = li_scale, + units = ['nm', 'nm', 'nm', ''], + ), + ), + shader= +""" +void main() { + emitRGB( + vec3(toNormalized(getDataValue(0)), + toNormalized(getDataValue(1)), + toNormalized(getDataValue(2))) + ); +} +""", + ) + + +# run the code from the command line +if __name__ == '__main__': + o_parser = argparse.ArgumentParser(description='Script to render ome.tiff files into the neuroglancer software.') + # request path to ometiff and file name as command line argument + o_parser.add_argument('ometiff', type=str, nargs=1, help='ome.tiff path/filename') + # start neuroglancer + neuroglancer.cli.add_server_arguments(o_parser) + neuroglancer.cli.handle_server_arguments(o_parser.parse_args()) + viewer = neuroglancer.Viewer() + with viewer.txn() as state: + # render ometiff + ometiff2neuro( + o_state = state, + s_pathfile_tiff = o_parser.parse_args().ometiff[0], + ) + # print neuroglancer viewer url + print(viewer) diff --git a/pcdl/pyCLI.py b/pcdl/pyCLI.py index 41ad527..5a16608 100644 --- a/pcdl/pyCLI.py +++ b/pcdl/pyCLI.py @@ -1274,7 +1274,7 @@ def get_anndata(): ) # going home s_opathfile = s_pathfile.replace('.xml', f'_cell_{args.scale}.h5ad') - ann_mcds.write(s_opathfile) + ann_mcds.write_h5ad(s_opathfile) return s_opathfile else: @@ -1300,12 +1300,12 @@ def get_anndata(): # going home if b_collapse : s_opathfile = f'{s_path}/timeseries_cell_{args.scale}.h5ad' - ann_mcdsts.write(s_opathfile) + ann_mcdsts.write_h5ad(s_opathfile) return s_opathfile else: ls_opathfile = [f"{s_path}/{s_xmlfile.replace('.xml', '_cell_{}.h5ad'.format(args.scale))}" for s_xmlfile in mcdsts.get_xmlfile_list()] for i, ann_mcds in enumerate(ann_mcdsts): - ann_mcds.write(ls_opathfile[i]) + ann_mcds.write_h5ad(ls_opathfile[i]) return ls_opathfile diff --git a/pcdl/pyMCDS.py b/pcdl/pyMCDS.py index 1c1c33e..5a1fc2d 100644 --- a/pcdl/pyMCDS.py +++ b/pcdl/pyMCDS.py @@ -15,8 +15,8 @@ # load library -import aicsimageio # bioio, bioio_base -from aicsimageio.writers import OmeTiffWriter +import bioio_base +from bioio.writers import OmeTiffWriter import matplotlib.pyplot as plt from matplotlib import cm from matplotlib import colors @@ -569,12 +569,22 @@ def get_mesh_spacing(self): tr_m_range, tr_n_range, tr_p_range = self.get_mesh_mnp_range() ar_m_axis, ar_n_axis, ar_p_axis = self.get_mesh_mnp_axis() - dm = (tr_m_range[1] - tr_m_range[0]) / (ar_m_axis.shape[0] - 1) - dn = (tr_n_range[1] - tr_n_range[0]) / (ar_n_axis.shape[0] - 1) + # m axis + if (len(set(tr_m_range)) == 1): + dm = np.float64(1.0) + else: + dm = (tr_m_range[1] - tr_m_range[0]) / (ar_m_axis.shape[0] - 1) + # n axis + if (len(set(tr_n_range)) == 1): + dn = np.float64(1.0) + else: + dn = (tr_n_range[1] - tr_n_range[0]) / (ar_n_axis.shape[0] - 1) + # p axis if (len(set(tr_p_range)) == 1): dp = np.float64(1.0) else: dp = (tr_p_range[1] - tr_p_range[0]) / (ar_p_axis.shape[0] - 1) + return [dm, dn, dp] @@ -1412,7 +1422,7 @@ def make_conc_vtk(self, visualize=True): iren.Start() # free memory - del vfa_value + #del vfa_value # save vtk file s_vtkpathfile = self.path + '/' + s_vtkfile @@ -2062,7 +2072,8 @@ def make_cell_vtk(self, attribute=['cell_type'], visualize=True): voa_data.InsertNextValue(df_cell.loc[i, s_attribute]) vug_data.GetPointData().AddArray(voa_data) - del voa_data + # free memory + #del voa_data # generate sphere source vss_data = vtk.vtkSphereSource() @@ -2327,7 +2338,12 @@ def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=Tr if len(ls_celltype) > 0: s_channel += f'_{cell_attribute}' s_tifffile = self.xmlfile.replace('.xml', f'{s_channel}.ome.tiff') + if (len(s_tifffile) > 255): + print(f"Warning: filename {len(s_tifffile)} > 255 character.") + s_tifffile = self.xmlfile.replace('.xml', f'_channels.ome.tiff') + print(f"file name adjusted to {s_tifffile}.") s_tiffpathfile = self.path + '/' + s_tifffile + # save to file OmeTiffWriter.save( a_czyx_img, @@ -2336,7 +2352,7 @@ def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=Tr #ome_xml=x_img, channel_names = ls_channel, image_names = [s_tifffile.replace('.ome.tiff','')], - physical_pixel_sizes = aicsimageio.types.PhysicalPixelSizes(self.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] + physical_pixel_sizes = bioio_base.types.PhysicalPixelSizes(self.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] #channel_colors=, #fs_kwargs={}, ) diff --git a/pcdl/pyMCDSts.py b/pcdl/pyMCDSts.py index 0ff04ba..2378a38 100644 --- a/pcdl/pyMCDSts.py +++ b/pcdl/pyMCDSts.py @@ -20,8 +20,8 @@ # load libraries -import aicsimageio # bioio, bioio_base -from aicsimageio.writers import OmeTiffWriter +import bioio_base +from bioio.writers import OmeTiffWriter import glob import matplotlib.pyplot as plt import numpy as np @@ -1082,6 +1082,7 @@ def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=Tr a_tczyx_img = np.array(l_tczyx_img) if self.verbose: print('a_tczyx_img shape:', a_tczyx_img.shape) + # generate filename s_channel = '' for s_substrate in ls_substrate: @@ -1094,7 +1095,13 @@ def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=Tr s_channel += f'_{s_celltype}' if len(ls_celltype) > 0: s_channel += f'_{cell_attribute}' - s_tiffpathfile = self.path + f'/timeseries{s_channel}.ome.tiff' + s_tifffile = f'timeseries{s_channel}.ome.tiff' + if (len(s_tifffile) > 255): + print(f"Warning: filename {len(s_tifffile)} > 255 character.") + s_tifffile = 'timeseries_channels.ome.tiff' + print(f"file name adjusted to {s_tifffile}.") + s_tiffpathfile = self.path + '/' + s_tifffile + # save to file OmeTiffWriter.save( a_tczyx_img, @@ -1103,7 +1110,7 @@ def make_ome_tiff(self, cell_attribute='ID', conc_cutoff={}, focus=None, file=Tr #ome_xml=x_img, channel_names = ls_substrate + ls_celltype, image_names = [f'timeseries_{cell_attribute}'], - physical_pixel_sizes = aicsimageio.types.PhysicalPixelSizes(mcds.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] + physical_pixel_sizes = bioio_base.types.PhysicalPixelSizes(mcds.get_voxel_spacing()[2], 1.0, 1.0), # z,y,x [um] #channel_colors=, #fs_kwargs={}, ) diff --git a/pyproject.toml b/pyproject.toml index a9048d2..1d21b09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,8 +70,8 @@ classifiers = [ # bue 2024-12-06: enforcing some versions dependencies = [ - "aicsimageio", "anndata>=0.10.8", + "bioio>=1.2.1", "matplotlib", "numpy<2.0.0", "pandas>=2.2.2", diff --git a/test/test_cli_2d.py b/test/test_cli_2d.py index dded686..71b6aed 100644 --- a/test/test_cli_2d.py +++ b/test/test_cli_2d.py @@ -813,7 +813,7 @@ def test_pcdl_get_anndata_timeseries(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -829,7 +829,7 @@ def test_pcdl_get_anndata_timeseries_collapsed(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--collapse', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - ls_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace("['","").replace("']\n","").split("', '") + ls_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.\n','').replace("['","").replace("']\n","").split("', '") # bue 20250307: >= python3.11 assert len(ls_opathfile) == 25 and \ ls_opathfile[0].endswith('output_2d/output00000000_cell_maxabs.h5ad') and \ ls_opathfile[-1].endswith('output_2d/output00000024_cell_maxabs.h5ad') and \ @@ -841,7 +841,7 @@ def test_pcdl_get_anndata_timeseries_customtype(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--custom_data_type', 'sample:bool'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'sample'})) and \ @@ -858,7 +858,7 @@ def test_pcdl_get_anndata_timeseries_microenv(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--microenv', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (not set(ann.var_names).issuperset({'oxygen'})) and \ @@ -875,7 +875,7 @@ def test_pcdl_get_anndata_timeseries_graph(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--graph', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -891,7 +891,7 @@ def test_pcdl_get_anndata_timeseries_physiboss(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--physiboss', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -907,7 +907,7 @@ def test_pcdl_get_anndata_timeseries_settingxmlfalse(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--settingxml', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'default_fusion_rates'})) and \ @@ -924,7 +924,7 @@ def test_pcdl_get_anndata_timeseries_settingxmlnone(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--settingxml', 'none'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'default_fusion_rates'})) and \ @@ -941,7 +941,7 @@ def test_pcdl_get_anndata_timeseries_value(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '2'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -957,7 +957,7 @@ def test_pcdl_get_anndata_timeseries_drop(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--drop', 'cell_type', 'oxygen'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (not set(ann.var_names).issuperset({'cell_type'})) and \ @@ -975,7 +975,7 @@ def test_pcdl_get_anndata_timeseries_keep(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--keep', 'cell_type', 'oxygen'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'oxygen'})) and \ @@ -993,7 +993,7 @@ def test_pcdl_get_anndata_timeseries_scale(self): s_result = subprocess.run(['pcdl_get_anndata', s_path_2d, '--scale', 'std'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/timeseries_cell_std.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -1010,7 +1010,7 @@ def test_pcdl_get_anndata_timestep(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -1026,7 +1026,7 @@ def test_pcdl_get_anndata_timestep_microenv(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--microenv', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (not set(ann.var_names).issuperset({'oxygen'})) and \ @@ -1043,7 +1043,7 @@ def test_pcdl_get_anndata_timestep_graph(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--graph', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -1059,7 +1059,7 @@ def test_pcdl_get_anndata_timestep_physiboss(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--physiboss', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -1075,7 +1075,7 @@ def test_pcdl_get_anndata_timestep_settingxmlfalse(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--settingxml', 'false'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'default_fusion_rates'})) and \ @@ -1092,7 +1092,7 @@ def test_pcdl_get_anndata_timestep_settingxmlnone(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--settingxml', 'none'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'default_fusion_rates'})) and \ @@ -1109,7 +1109,7 @@ def test_pcdl_get_anndata_timestep_value(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '2'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (ann.shape[0] > 9) and \ @@ -1125,7 +1125,7 @@ def test_pcdl_get_anndata_timestep_drop(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--drop', 'cell_type', 'oxygen'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (not set(ann.var_names).issuperset({'cell_type'})) and \ @@ -1143,7 +1143,7 @@ def test_pcdl_get_anndata_timestep_keep(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--keep', 'cell_type', 'oxygen'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_maxabs.h5ad')) and \ (set(ann.var_names).issuperset({'oxygen'})) and \ @@ -1161,7 +1161,7 @@ def test_pcdl_get_anndata_timestep_scale(self): s_result = subprocess.run(['pcdl_get_anndata', s_pathfile_2d, '--scale', 'std'], check=False, capture_output=True) #print(f'\ns_result.stdout: {s_result.stdout}\n') #print(f'\ns_result.stderr: {s_result.stderr}\n') - s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','') + s_opathfile = s_result.stderr.decode('UTF8').replace('\r','').replace('\n','').replace(':0:','sys:1:').replace('sys:1: DeprecationWarning: Call to deprecated function (or staticmethod) _destroy.','') # bue 20250307: >= python3.11 ann = ad.read_h5ad(s_opathfile) assert (s_opathfile.endswith('output_2d/output00000024_cell_std.h5ad')) and \ (ann.shape[0] > 9) and \