diff --git a/TPTBox/core/bids_constants.py b/TPTBox/core/bids_constants.py index 0041b1c..6519282 100755 --- a/TPTBox/core/bids_constants.py +++ b/TPTBox/core/bids_constants.py @@ -163,6 +163,8 @@ "localizer", "difference", "labels", + "report", + "pet", ] # https://bids-specification.readthedocs.io/en/stable/appendices/entity-table.html formats_relaxed = [*formats, "t2", "t1", "t2c", "t1c", "mr", "snapshot", "t1dixon", "dwi", "ctb"] @@ -221,7 +223,7 @@ "OPT": "Ophthalmic Tomography", "OPV": "Ophthalmic Visual Field", "OSS": "Optical Surface Scan", - "OT": "Other ", + "OT": "Other", "PLAN": "Plan", "PR": "Presentation State", "PT": "Positron emission tomography (PET)", diff --git a/TPTBox/core/bids_files.py b/TPTBox/core/bids_files.py index e71efb3..d4daa34 100755 --- a/TPTBox/core/bids_files.py +++ b/TPTBox/core/bids_files.py @@ -687,7 +687,7 @@ def get_changed_bids( auto_add_run_id=False, additional_folder: str | None = None, dataset_path: str | None = None, - make_parent=True, + make_parent=False, non_strict_mode=False, ): ds = dataset_path if dataset_path is not None else self.get_path_decomposed()[0] diff --git a/TPTBox/core/dicom/dicom_extract.py b/TPTBox/core/dicom/dicom_extract.py index 0d63226..b2c633b 100644 --- a/TPTBox/core/dicom/dicom_extract.py +++ b/TPTBox/core/dicom/dicom_extract.py @@ -190,7 +190,68 @@ def dicom_to_nifti_multiframe(ds, nii_path): return nii_path -def _convert_to_nifti(dicom_out_path, nii_path): +def _export_pdf_from_dicom(dcm_path, out_pdf): + assert len(dcm_path) == 1, dcm_path + ds = dcm_path[0] + + # verify modality / SOP class + if ds.Modality.upper() != "PDF": + raise ValueError("Not a PDF DICOM") + + if "EncapsulatedDocument" not in ds: + raise ValueError("No embedded PDF found") + + pdf_bytes = ds.EncapsulatedDocument + + out_pdf = Path(out_pdf) + out_pdf.write_bytes(pdf_bytes) + + +def _collect_text(ds, txt_lines: list[str] | None = None): + if txt_lines is None: + txt_lines = [] + + def _help_collect_text(content_sequence, level: int = 0): + for item in content_sequence: + prefix = " " * level + + concept = "" + + if hasattr(item, "ConceptNameCodeSequence"): + try: + concept = item.ConceptNameCodeSequence[0].CodeMeaning + except Exception: + pass + + value = None + + for attr in ["TextValue", "CodeMeaning", "NumericValue"]: + if hasattr(item, attr): + value = getattr(item, attr) + break + + if concept or value is not None: + txt_lines.append(f"{prefix}{concept}: {value}") + + if hasattr(item, "ContentSequence"): + _help_collect_text( + item.ContentSequence, + level + 1, + ) + + if hasattr(ds, "ContentSequence"): + _help_collect_text(ds.ContentSequence) + return txt_lines + + +def _extract_txt_from_dicom(dcm_path, out_txt): + lines = [] + for p in dcm_path: + lines = _collect_text(p, lines) + Path(out_txt).write_text("\n".join(lines)) + + +def _extract_nii_from_dicom(dicom_out_path, nii_path): """ Convert DICOM files to NIfTI format and handle common conversion errors. @@ -206,6 +267,7 @@ def _convert_to_nifti(dicom_out_path, nii_path): FunctionTimedOut: Raised if the DICOM-to-NIfTI conversion times out. ValueError: Raised for generic validation failures. """ + try: if isinstance(dicom_out_path, list): try: @@ -217,6 +279,7 @@ def _convert_to_nifti(dicom_out_path, nii_path): return True except Exception as e: logger.on_debug("Multi-Frame DICOM did not work:", e) + ## The PDF dicom lands here convert_dicom.dicom_array_to_nifti(dicom_out_path, nii_path, True) else: # func_timeout(10, dicom2nifti.dicom_series_to_nifti, (dicom_out_path, nii_path, True)) @@ -246,6 +309,9 @@ def _convert_to_nifti(dicom_out_path, nii_path): logger.print_error() return False + except Exception: + print(nii_path) + return True @@ -264,7 +330,7 @@ def _get_paths( ): if keys is None: keys = {} - (mri_format, keys) = extract_keys_from_json( + (mri_format, keys, ending) = extract_keys_from_json( simp_json, dcm_data_l, use_session, @@ -277,7 +343,7 @@ def _get_paths( json_file_name, json_bids_name = _generate_bids_path( dataset_nifti_dir, keys, mri_format, simp_json, make_subject_chunks=make_subject_chunks, parent=parent ) - nii_path = str(json_file_name).replace(".json", "") + ".nii.gz" + nii_path = str(json_file_name).replace(".json", "") + ending return json_file_name, json_bids_name, nii_path @@ -364,11 +430,17 @@ def _from_dicom_to_nii( if exist and Path(nii_path).exists(): logger.print("already exists:", json_file_name, ltype=Log_Type.STRANGE, verbose=verbose) return nii_path - suc = _convert_to_nifti(dcm_data_l, nii_path) + add_grid = False + if nii_path.endswith(".pdf"): + _export_pdf_from_dicom(dcm_data_l, nii_path) + elif nii_path.endswith(".txt"): + _extract_txt_from_dicom(dcm_data_l, nii_path) + else: + add_grid = _extract_nii_from_dicom(dcm_data_l, nii_path) - if suc: + if add_grid: _add_grid_info_to_json(nii_path, json_file_name) - return nii_path if suc else None + return nii_path if add_grid else None def _add_grid_info_to_json(nii_path: Path | str, simp_json: Path | str, force_update=False, add=True): @@ -390,7 +462,7 @@ def _add_grid_info_to_json(nii_path: Path | str, simp_json: Path | str, force_up return json_dict -def _find_all_files(dcm_dirs: Path | list[Path]): +def _find_all_files(dcm_dirs: Path | list[Path],verbose=False): """ Recursively find all DICOM directories or files in the given paths. @@ -400,6 +472,9 @@ def _find_all_files(dcm_dirs: Path | list[Path]): Yields: Path: Paths to directories or individual DICOM files found during the search. """ + if verbose: + logger.on_neutral("Start file searching") + i = 0 yield dcm_dirs dcm_dirs = dcm_dirs if isinstance(dcm_dirs, list) else [dcm_dirs] for dcm_dir in dcm_dirs: @@ -408,9 +483,15 @@ def _find_all_files(dcm_dirs: Path | list[Path]): file = "" for file in files: if Path(file).is_file(): # str(file).endswith(".dcm") or str(file).endswith(".ima") + if verbose: + logger.on_neutral("File ",i,end="\r") + i += 1 yield Path(root, file).absolute().parent break else: + if verbose: + logger.on_neutral("File ",i,end="\r") + i += 1 yield Path(root, file) # if "." not in str(file): # yield Path(root, file).absolute().parent @@ -606,7 +687,7 @@ def extract_dicom_folder( convert_dicom.settings.disable_validate_slice_increment() outs = {} - for p in _find_all_files(dicom_folder): + for p in _find_all_files(dicom_folder,verbose=verbose): dicom_path = p if str(dicom_path).endswith(".pkl"): @@ -672,6 +753,7 @@ def process_series(key, files, parts): p, Path("/media/data/robert/datasets", "dataset-Durchleuchtung222"), False, False, validate_slice_increment=False ) + sys.exit() # s = "/home/robert/Downloads/bein/dataset-oberschenkel/rawdata/sub-1-3-46-670589-11-2889201787-2305829596-303261238-2367429497/mr/sub-1-3-46-670589-11-2889201787-2305829596-303261238-2367429497_sequ-406_mr.nii.gz" # nii2 = NII.load(s, False) diff --git a/TPTBox/core/dicom/dicom_header_to_keys.py b/TPTBox/core/dicom/dicom_header_to_keys.py index c9aabff..e7f51cb 100644 --- a/TPTBox/core/dicom/dicom_header_to_keys.py +++ b/TPTBox/core/dicom/dicom_header_to_keys.py @@ -165,7 +165,15 @@ def extract_keys_from_json( # noqa: C901 def _get(key, default=None): if key not in simp_json: return keys.get(key, default) - return str(simp_json[key]).replace("_", "-").replace(" ", "-").replace(".", "-") + value = str(simp_json[key]).replace("_", "-").replace(" ", "-").replace(".", "-") + # remove invalid filename characters + value = re.sub(r'[<>:"/\\|?*\x00-\x1F]', "", value) + # collapse repeated dashes + value = re.sub(r"-+", "-", value) + # strip leading/trailing dots and dashes + value = value.strip(".-") + + return value """Extract keys from JSON based on study and series descriptions.""" #### NAKO FIXED #### @@ -174,25 +182,28 @@ def _get(key, default=None): series_description = _get("SeriesDescription", "unnamed") """Determine the MRI format based on the series description.""" if "T2_TSE" in series_description: - return "T2w", {"acq": "sag", "chunk": series_description.split("_")[-1], "sequ": simp_json["SeriesNumber"], **keys} + return "T2w", {"acq": "sag", "chunk": series_description.split("_")[-1], "sequ": simp_json["SeriesNumber"], **keys}, ".nii.gz" elif "3D_GRE_TRA" in series_description: - return "vibe", { - "acq": "ax", - "part": dixon_mapping[series_description.split("_")[-1].lower()], - "chunk": _get("ProtocolName", "unnamed").split("_")[-1], - **keys, - } + return ( + "vibe", + { + "acq": "ax", + "part": dixon_mapping[series_description.split("_")[-1].lower()], + "chunk": _get("ProtocolName", "unnamed").split("_")[-1], + **keys, + }, + ".nii.gz", + ) elif "ME_vibe" in series_description: - return "mevibe", { - "acq": "ax", - "part": dixon_mapping[series_description.split("_")[-1].lower()], - "sequ": simp_json["SeriesNumber"], - **keys, - } + return ( + "mevibe", + {"acq": "ax", "part": dixon_mapping[series_description.split("_")[-1].lower()], "sequ": simp_json["SeriesNumber"], **keys}, + ".nii.gz", + ) elif "PD" in series_description: - return "pd", {"acq": "iso", **keys} + return "pd", {"acq": "iso", **keys}, ".nii.gz" elif "T2_HASTE" in series_description: - return "T2haste", {"acq": "ax", **keys} + return "T2haste", {"acq": "ax", **keys}, ".nii.gz" else: raise NotImplementedError(series_description) # GENERAL @@ -268,6 +279,8 @@ def _get(key, default=None): found = False if modality == "ct": mri_format = "ct" + elif modality.lower() == "pt": + mri_format = "pet" elif modality == "xa": # Angiography biplane = False if "BIPLANE A" in image_type or "SINGLE A" in image_type: @@ -319,9 +332,14 @@ def _get(key, default=None): " km " in series_description.lower() or series_description.startswith("km") or series_description.endswith("km") ) and keys.get("ce") is None: keys["ce"] = "ContrastAgent" + elif modality.lower() == "pdf": + return "report", keys, ".pdf" + elif modality.lower() == "sr": + keys["desc"] = _get("SeriesDescription", None) + return "report", keys, ".txt" else: - raise NotImplementedError(f"modality='{modality.upper()}', ({modalities.get(modality.upper())})") + raise NotImplementedError(f"modality='{modality}', ({modalities.get(modality.upper(), 'Non Standard Modality key')})") # ".*sub.*t1.*": "subtraktion", # "subtraktion.*t1.*": "subtraktion", - return mri_format, keys + return mri_format, keys, ".nii.gz" diff --git a/TPTBox/core/nii_wrapper.py b/TPTBox/core/nii_wrapper.py index 28119bb..f23717d 100755 --- a/TPTBox/core/nii_wrapper.py +++ b/TPTBox/core/nii_wrapper.py @@ -804,9 +804,7 @@ def apply_pad( mode: MODES = "constant", inplace=False, verbose: logging = True - ): - #TODO add other modes - #TODO add testcases and options for modes + ): if padd is None or padd == 0: return self if inplace else self.copy() @@ -829,13 +827,36 @@ def apply_pad( affine = self.affine @ transform + arr = self.get_array() + + # ---- 1. CROPPING (negative padding) ---- + slices = [] + + for i, (before, after) in enumerate(padd[:self.dims]): + start = max(0, -before) + end = arr.shape[i] - max(0, -after) + slices.append(slice(start, end)) + + # keep non-spatial dims unchanged + slices += [slice(None)] * (arr.ndim - self.dims) + + arr = arr[tuple(slices)] + + # ---- 2. PADDING (positive only) ---- + padd_positive = tuple( + (max(0, b), max(0, a)) for b, a in padd + ) + args = {} if mode == "constant": args["constant_values"] = self.get_c_val() + if mode == "nearest": + mode = "edge" + log.print(f"Padd {padd}; {mode=}, {args}", verbose=verbose) - arr = np.pad(self.get_array(), padd, mode=mode, **args) + arr = np.pad(arr, padd_positive, mode=mode, **args) nii = (arr, affine, self.header) @@ -940,35 +961,38 @@ def resample_from_to(self, to_vox_map:Image_Reference|Has_Grid|tuple[SHAPE,AFFIN mapping = to_vox_map.to_gird() else: mapping = to_vox_map if isinstance(to_vox_map, tuple) else to_nii_optional(to_vox_map, seg=self.seg, default=to_vox_map) - if isinstance(mapping,Has_Grid) and mapping.assert_affine(self,raise_error=False,origin_tolerance=0.000001,error_tolerance=0.000001,shape_tolerance=0): - log.print(f"resample_from_to skipped; already in space: {self}",verbose=verbose) - return self if inplace else self.copy() - - #m1 = mapping.make_empty_POI().reorient(self.orientation) - #if m1.assert_affine(self,raise_error=False,origin_tolerance=0.000001,error_tolerance=0.000001,shape_tolerance=0): - # log.print(f"resample_from_to only need reorientation; {self.orientation}",verbose=verbose) - # return self.reorient(mapping.orientation,inplace=inplace) - #if self.orientation == mapping.orientation and self.zoom == mapping.zoom: - # shift = (np.array(self.origin) - np.array(m1.origin)) / np.array(m1.zoom) - # if np.allclose(shift, np.round(shift), atol=1e-6): - # self = self.reorient(mapping.orientation,inplace=inplace) # noqa: PLW0642 - # shift = (np.array(self.origin) - np.array(mapping.origin)) / np.array(mapping.zoom) - # shift = np.round(shift).astype(int) - # src_shape = np.array(mapping.shape) - # dst_shape = np.array(self.shape) - # # padding before = how much dst starts before src - # pad_before = np.maximum(-shift, 0) - # - # # where src ends inside dst - # src_end_in_dst = shift + src_shape - # # padding after = remaining dst size after src - # pad_after = np.maximum(dst_shape - src_end_in_dst, 0) - # pad = tuple((int(b), int(a)) for b, a in zip(pad_before, pad_after)) - # ret = self.apply_pad(pad, mode=mode) - # - # log.print(f"resample_from_to only needs padding/cropping {pad}, ",verbose=verbose,) - # ret.assert_affine(mapping,raise_error=False,origin_tolerance=0.000001,error_tolerance=0.000001,shape_tolerance=0) - # return ret + if isinstance(mapping,Has_Grid): + if mapping.assert_affine(self,raise_error=False,origin_tolerance=0.000001,error_tolerance=0.000001,shape_tolerance=0): + log.print(f"resample_from_to skipped; already in space: {self}",verbose=verbose) + return self if inplace else self.copy() + + m1 = mapping if mapping.orientation == self.orientation else mapping.make_empty_POI().reorient(self.orientation) + if m1.assert_affine(self,raise_error=False,origin_tolerance=0.00001,error_tolerance=0.00001,shape_tolerance=0): + log.print(f"resample_from_to only need reorientation; {self.orientation}",verbose=verbose) + ret = self.reorient(mapping.orientation,inplace=inplace) + ret.affine = mapping.affine #remove floating point error + return ret + if self.orientation == mapping.orientation and np.allclose(self.zoom , mapping.zoom, atol=1e-6): + shift = (np.array(self.origin) - np.array(m1.origin)) / np.array(m1.zoom) + if np.allclose(shift, np.round(shift), atol=1e-6): + s = self.reorient(mapping.orientation,inplace=inplace) # noqa: PLW0642 + shift = (np.array(self.origin) - np.array(mapping.origin)) / np.array(mapping.zoom) + shift = np.round(shift).astype(int) + dst_shape = np.array(mapping.shape) + src_shape = np.array(s.shape) + # padding before = how much dst starts before src + pad_before = shift + # padding after = remaining dst size after src + pad_after = dst_shape-shift-src_shape + pad = tuple((int(b), int(a)) for b, a in zip(pad_before, pad_after)) + ret = s.apply_pad(pad, mode=mode,inplace=inplace,verbose=verbose) + + #TODO SET raise_error=False before committing + valid = ret.assert_affine(mapping,raise_error=True,origin_tolerance=0.0001,error_tolerance=0.0001,shape_tolerance=0) + if valid: + log.print(f"resample_from_to only needs padding/cropping {pad}",verbose=verbose) + ret.affine = mapping.affine #remove floating point error + return ret assert mapping is not None @@ -1777,7 +1801,7 @@ def infect(self: NII, reference_mask: NII, inplace=False,verbose=True,axis:int|s """ self.assert_affine(reference_mask) if _do_crop: - crop = reference_mask.compute_crop(0,5) + crop = reference_mask.compute_crop(0,5,raise_error=False) s = self.apply_crop(crop) reference_mask = reference_mask.apply_crop(crop) else: diff --git a/TPTBox/core/np_utils.py b/TPTBox/core/np_utils.py index 6a955f5..4c9924e 100755 --- a/TPTBox/core/np_utils.py +++ b/TPTBox/core/np_utils.py @@ -944,7 +944,7 @@ def np_get_connected_components_center_of_mass( connectivity=connectivity, label_ref=label, ) - coms = list(np_center_of_mass(subreg_cc[label]).values()) + coms = list(np_center_of_mass(subreg_cc[label]).values()) if label in subreg_cc else [] if sort_by_axis is not None: coms.sort(key=lambda a: a[sort_by_axis]) @@ -1050,7 +1050,7 @@ def np_fill_holes( else: assert 0 <= slice_wise_dim <= arr.ndim - 1, f"slice_wise_dim needs to be in range [0, {arr.ndim - 1}]" filled = np.swapaxes(arr_lc.copy(), 0, slice_wise_dim) - filled = np.stack([_fill(x) for x in filled]) + filled = np.stack([_fill(x).astype(arr.dtype) for x in filled]) filled = np.swapaxes(filled, 0, slice_wise_dim) filled[filled != 0] = l if use_crop: diff --git a/TPTBox/mesh3D/snapshot3D.py b/TPTBox/mesh3D/snapshot3D.py index 12f985e..f90f46e 100644 --- a/TPTBox/mesh3D/snapshot3D.py +++ b/TPTBox/mesh3D/snapshot3D.py @@ -243,7 +243,7 @@ def _set_input( return vtk_object -def _contour_from_roi_smooth(data, affine=None, color: np.ndarray | list = _red, opacity=1, smoothing=0): +def _contour_from_roi_smooth(data: np.ndarray, affine=None, color: np.ndarray | list = _red, opacity=1, smoothing=0): """Generates surface actor from a binary ROI. Code from dipy, but added awesome smoothing! @@ -274,10 +274,8 @@ def _contour_from_roi_smooth(data, affine=None, color: np.ndarray | list = _red, else: nb_components = 1 - data = (data > 0) * 1 - vol = np.interp(data, xp=[data.min(), data.max()], fp=[0, 255]) - vol = vol.astype("uint8") - + vol = data.astype("uint8") * 255 + assert data.max() <= 1, np.unique(data) im = vtk.vtkImageData() if major_version <= 5: im.SetScalarTypeToUnsignedChar() # type: ignore @@ -291,12 +289,10 @@ def _contour_from_roi_smooth(data, affine=None, color: np.ndarray | list = _red, im.SetNumberOfScalarComponents(nb_components) # type: ignore else: im.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, nb_components) - - # copy data vol = np.swapaxes(vol, 0, 2) - vol = np.ascontiguousarray(vol) + # vol = np.ascontiguousarray(vol) # already is - vol = vol.ravel() if nb_components == 1 else np.reshape(vol, [np.prod(vol.shape[:3]), vol.shape[3]]) + vol = vol.reshape(-1) if nb_components == 1 else np.reshape(vol, [np.prod(vol.shape[:3]), vol.shape[3]]) uchar_array = numpy_support.numpy_to_vtk(vol, deep=0) im.GetPointData().SetScalars(uchar_array) diff --git a/TPTBox/segmentation/VibeSeg/inference_nnunet.py b/TPTBox/segmentation/VibeSeg/inference_nnunet.py index bfa8736..34979a2 100644 --- a/TPTBox/segmentation/VibeSeg/inference_nnunet.py +++ b/TPTBox/segmentation/VibeSeg/inference_nnunet.py @@ -227,7 +227,7 @@ def to_int(a: str, k: None | int = None): try: return enum_[a].value except Exception: - print("no ", enum_) + pass if k is not None and k not in unknown_strings.values(): return k unknown_strings[a] = unknown_strings["max"] @@ -247,7 +247,7 @@ def to_int(a: str, k: None | int = None): logger.print(f"{mapping=}") seg_nii.map_labels_(mapping) if out_file is not None and (not Path(out_file).exists() or override): - seg_nii.save(out_file) + seg_nii.set_dtype("smallest_uint").save(out_file) del nnunet torch.cuda.empty_cache() diff --git a/TPTBox/segmentation/__init__.py b/TPTBox/segmentation/__init__.py index 0601e1f..0070a83 100644 --- a/TPTBox/segmentation/__init__.py +++ b/TPTBox/segmentation/__init__.py @@ -1,5 +1,4 @@ from __future__ import annotations -from TPTBox.segmentation.spineps import _run_spineps_all, run_spineps +from TPTBox.segmentation.spineps import _run_spineps_all, get_outpaths_spineps, run_spineps from TPTBox.segmentation.VibeSeg.vibeseg import extract_vertebra_bodies_from_VibeSeg, run_inference_on_file, run_nnunet, run_vibeseg -from TPTBox.segmentation.VibeSeg.vibeseg import run_vibeseg as run_totalvibeseg # TODO deprecate diff --git a/TPTBox/stitching/stitching.py b/TPTBox/stitching/stitching.py index 11a75a5..02b8e2e 100755 --- a/TPTBox/stitching/stitching.py +++ b/TPTBox/stitching/stitching.py @@ -324,6 +324,7 @@ def main( # noqa: C901 is_segmentation: bool = False, dtype: type | str = float, save=True, + ramp_path=None, ): np.set_printoptions(precision=2, floatmode="fixed") if is_segmentation: @@ -529,7 +530,8 @@ def main( # noqa: C901 occupancy_arr = occupancy_arr[ex_slice] assert output is not None nii_occ = set_array(nii_out, occupancy_arr) - output = output.replace(".nii.gz", "_ramps.nii.gz") + nii_occ.set_data_dtype(np.int8) + output = output.replace(".nii.gz", "_ramps.nii.gz").replace("_msk_", "_") if ramp_path is None else ramp_path if save: nib.save(nii_occ, output) # type: ignore print("Saved ", output) if verbose else None diff --git a/TPTBox/stitching/stitching_tools.py b/TPTBox/stitching/stitching_tools.py index c1d9e66..bbcb4be 100755 --- a/TPTBox/stitching/stitching_tools.py +++ b/TPTBox/stitching/stitching_tools.py @@ -22,6 +22,7 @@ def stitching( dtype: type = float, match_histogram=False, store_ramp=False, + ramp_path=None, ): out = str(out.file["nii.gz"]) if isinstance(out, BIDS_FILE) else str(out) files = [to_nii(bf).nii for bf in bids_files] @@ -37,6 +38,7 @@ def stitching( kick_out_fully_integrated_images=kick_out_fully_integrated_images, is_segmentation=is_seg, dtype=dtype, + ramp_path=ramp_path, ) diff --git a/pyproject.toml b/pyproject.toml index 6ee026e..61fd111 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,26 +13,35 @@ packages = [{ include = "TPTBox" }] [tool.poetry.dependencies] python = "^3.9 || ^3.10 || ^3.11 || ^3.12 || ^3.13 || ^3.14" -pathlib = "*" nibabel = "^5.2.0" -numpy = "^1.26.3" typing-extensions = "^4.9.0" -scipy = "^1.12.0" -dataclasses = "*" SimpleITK = "^2.3.1" matplotlib = "^3.8.2" dill = "^0.3.7" -scikit-image = "^0.22.0" fill-voids = "^2.0.6" connected-components-3d = "^3.12.3" tqdm = "*" joblib = "*" scikit-learn = "*" -antspyx = "0.4.2" pynrrd = "*" -#hf-deepali = "*" requests = "*" +# --- OLD STACK (Python < 3.11) +numpy = [ + { version = ">=1.26.3,<2.0", python = "<3.11" }, + { version = ">=2.0,<3.0", python = ">=3.11" } +] + +scipy = [ + { version = ">=1.11,<1.13", python = "<3.11" }, + { version = ">=1.13", python = ">=3.11" } +] + +scikit-image = [ + { version = ">=0.22,<0.23", python = "<3.11" }, + { version = ">=0.24", python = ">=3.11" } +] + [tool.poetry.group.dev.dependencies] pytest = ">=8.1.1" vtk = "*" diff --git a/unit_tests/test_nrrd.py b/unit_tests/test_nrrd.py index 6c2c4a1..2d121a5 100644 --- a/unit_tests/test_nrrd.py +++ b/unit_tests/test_nrrd.py @@ -19,7 +19,7 @@ class TestAnts(unittest.TestCase): - @unittest.skipIf(not has_ants, "requires spineps to be installed") + @unittest.skipIf(not has_ants, "requires ants to be installed") def test_segmentation_CT(self): """Test round-trip for Segmentation.seg.nrrd.""" ct, subreg, vert = get_nii_paths_ct() diff --git a/unit_tests/test_stiching.py b/unit_tests/test_stiching.py index 44494ea..75f5aaf 100755 --- a/unit_tests/test_stiching.py +++ b/unit_tests/test_stiching.py @@ -78,7 +78,7 @@ def test_stitching( store_ramp=False, verbose=False, min_value=0, - bias_field=True, + bias_field=False, crop_to_bias_field=False, crop_empty=False, histogram=None,