pycerr 2.2.0__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pycerr-2.2.0 → pycerr-2.2.2}/CHANGELOG.md +46 -0
- {pycerr-2.2.0/pycerr.egg-info → pycerr-2.2.2}/PKG-INFO +5 -4
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/_version.py +3 -3
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/contour/rasterseg.py +74 -70
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/structure.py +3 -2
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/gray_level_cooccurence.py +2 -2
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/neighbor_gray_tone.py +3 -7
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/README_pycerr_gui.md +5 -2
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/__init__.py +2 -1
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/common.py +14 -2
- {pycerr-2.2.0 → pycerr-2.2.2/pycerr.egg-info}/PKG-INFO +5 -4
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/SOURCES.txt +1 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/requires.txt +5 -3
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/scm_file_list.json +1 -0
- pycerr-2.2.2/pycerr.egg-info/scm_version.json +8 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/pyproject.toml +16 -6
- pycerr-2.2.2/tests/test_poly_fill.py +115 -0
- pycerr-2.2.0/pycerr.egg-info/scm_version.json +0 -8
- {pycerr-2.2.0 → pycerr-2.2.2}/.github/workflows/_version_tmp.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/.github/workflows/publish.yml +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/.github/workflows/python-package.yml +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/.gitignore +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/.readthedocs.yaml +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/CLAUDE.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/LICENSE +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/MANIFEST.in +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/README.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/install_utils.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/post/CT_LungOAR_incrMRRN.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/post/MR_BrainMets_SMITplus.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/post/MR_Rectum_GTV_SMIT.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/prep/CT_LungOAR_incrMRRN.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/prep/MR_BrainMets_SMITplus.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/prep/MR_Rectum_GTV_SMIT.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/ai_models/run_utils.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/contour/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/contour/contour_processing.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/contour/structure_consensus.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/beams.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/dcm_cerr_name_map.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/deform.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/dose.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/header.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/imrt.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/scan.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/scan_info.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dataclasses/uromt.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/convolutional_filter_settings/LoG_filter.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/convolutional_filter_settings/Rot_inv_laws_energy_filter.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/convolutional_filter_settings/gabor_filter.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/convolutional_filter_settings/mean_filter.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/README_LICENSE.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00000.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00001.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00002.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00003.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00004.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00005.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00006.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00007.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00008.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00009.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00010.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00011.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00012.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00013.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00014.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00015.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00016.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00017.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00018.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00019.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00020.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00021.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00022.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00023.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00024.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00025.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00026.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00027.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00028.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00029.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00030.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00031.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00032.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00033.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00034.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00035.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00036.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00037.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00038.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00039.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00040.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00041.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00042.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00043.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00044.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00045.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00046.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00047.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00048.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00049.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00050.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00051.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00052.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00053.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00054.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00055.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00056.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00057.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00058.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_IMG_00059.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_1/DCM_RS_00060.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00000.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00001.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00002.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00003.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00004.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00005.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00006.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00007.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00008.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00009.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00010.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00011.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00012.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00013.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00014.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00015.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00016.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00017.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00018.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00019.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00020.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00021.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00022.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00023.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00024.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00025.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00026.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00027.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00028.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00029.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00030.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00031.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00032.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00033.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00034.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00035.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00036.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00037.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00038.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00039.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_IMG_00040.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_2/DCM_RS_00041.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00000.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00001.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00002.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00003.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00004.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00005.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00006.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00007.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00008.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00009.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00010.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00011.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00012.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00013.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00014.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00015.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00016.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00017.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00018.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00019.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00020.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00021.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00022.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00023.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00024.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00025.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00026.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00027.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00028.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00029.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00030.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00031.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00032.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00033.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00034.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00035.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00036.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00037.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00038.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00039.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00040.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00041.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00042.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00043.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00044.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_IMG_00045.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_3/DCM_RS_00046.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00000.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00001.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00002.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00003.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00004.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00005.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00006.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00007.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00008.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00009.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00010.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00011.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00012.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00013.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00014.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00015.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00016.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00017.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00018.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00019.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00020.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00021.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00022.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00023.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00024.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00025.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00026.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00027.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00028.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00029.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00030.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00031.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00032.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00033.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00034.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00035.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00036.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00037.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00038.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00039.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00040.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00041.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00042.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00043.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00044.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00045.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00046.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00047.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00048.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00049.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00050.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00051.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00052.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00053.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00054.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_IMG_00055.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_phantom_dicom/pat_4/DCM_RS_00056.dcm +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_b1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_b2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_c1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi1/ibsi1_id_c2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1a3.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1a4.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1b1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_1b2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_2a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_2b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_2c.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3a3.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3b1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3b2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3b3.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3c1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3c2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_3c3.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_4a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_4a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_4b1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_4b2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_5a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_5a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_6a1.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_1/ibsi_phase2_1_id_6a2.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_1a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_1b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_2a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_2b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_3a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_3b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_4a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_4b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_5a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_5b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_6a.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/ibsi_settings/ibsi2_phase_2/ibsi_phase2_2_id_6b.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/radiomics_settings/original_settings.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/dose.nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/mask_(testStr1 U testStr3).nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/mask_testStr1.nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/mask_testStr2.nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/mask_testStr3.nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/datasets/sample_ct/dosimetric_model_test_data/scan.nii +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/iod_helper.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/reg_iod.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/rtdose_iod.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/rtplan_iod.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/rtstruct_iod.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dcm_export/scan_iod.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/dvh.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/gamma.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/README_IMRTP.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/QIBData/aahn_18b.dat +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/QIBData/aahn_6b.dat +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/QIBData/qib_tables.npz +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/influence.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/montecarlo.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/qib.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/qib_data.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/raytrace.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/recalc.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/dosecalc/rtplan.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/imrtp.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/imrtp_gui.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/imrtp_problem.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/lattice.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/portpy_bridge.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/imrtp/pyradplan_bridge.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/mri_metrics/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/mri_metrics/dce_mri.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/multiViewHelper.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/plan_container.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/first_order.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/ibsi1.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/neighbor_gray_level_dependence.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/preprocess.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/run_length.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/shape.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/size_zone.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/texture_filters.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/texture_utils.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/radiomics/vector_elasticity.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/ants_reg.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/register.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/settings/plastimatch_ct_ct_intra_pt_affine.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/settings/plastimatch_ct_ct_intra_pt_bsplines.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/settings/plastimatch_ct_ct_intra_pt_w_masks_affine.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/registration/settings/plastimatch_ct_ct_intra_pt_w_masks_bsplines.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/dosimetric_models.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Bronchial stenosis (cox).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Bronchial stenosis (logistic).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Bronchial toxicity (Grade 3+).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Bronchial toxicity (Grade 5).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Esophagitis (Huang).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Esophagitis (Jackson_cox).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Esophagitis (Jackson_logistic).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Esophagitis (Wijsman).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Pneumonitis (Appelt).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Rectal bleeding (grade 2+).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/model_parameters/Xerostomia (grade 2+).json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/roe/roe_gui.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/scripts/bench_uromt_overlay.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/scripts/demo_imrtp_gui.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/scripts/structure_consensus_example.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/scripts/uromt_matlab_compare.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/segmentation/models.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/MATLAB_COMPARISON.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/analyze.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/config.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/data.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/export.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/gpu.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/gpu_kernels.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/kernels.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/numerics.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/settings/dce_concentration_settings.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/settings/uromt_model_settings.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/solver.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/uromt/viz.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/ai_pipeline.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/custom_colormaps.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/image_proc.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/interp.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/mask.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/statistics.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/utils/uid.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/API_pycerr_gui.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/cerr_colormaps.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/CERR_logo.icns +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/CERR_logo.ico +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/CERR_logo.png +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/__main__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/app.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/colorbars.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/dialogs.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/main_window.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/pyCERR_logo_HQ.png +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/slice_view.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/uromt_gui.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_gui/volume3d.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_napari.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/cerr/viewer/pycerr_nbviewer.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/Makefile +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.contour.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.dataclasses.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.datasets.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.dcm_export.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.imrtp.dosecalc.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.imrtp.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.mri_metrics.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.radiomics.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.registration.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.roe.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.utils.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/cerr.viewer.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/conf.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/index.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/make.bat +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/modules.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/requirements.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/setup.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/docs/tests.rst +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/notebooks/urOMT_demo.ipynb +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/notebooks/urOMT_fast_settings.json +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/dependency_links.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/not-zip-safe +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/pycerr.egg-info/top_level.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/requirements.txt +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/setup.cfg +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/setup.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/README.md +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/__init__.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_ants_registration.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dcm_export_reg.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dcm_export_rtstruct.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dcm_reg_import.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dicom_import_robustness.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dicom_load.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_dvh.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gamma.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_disprange_3d.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_dvf_overlay.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_fast_alpha.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_fused_dose.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_isodose_levels.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_plane_labels.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_gui_upsample.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_h5_roundtrip.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_ibsi1_features.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_ibsi2_features.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_ibsi2_filters.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_imrtp.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_lattice.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_module_imports.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_nii_export_import.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_portpy_bridge.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_portpy_bridge_phase2.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_portpy_bridge_phase3.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_portpy_bridge_phase4.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_pyradplan_bridge.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_rasterization.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_roe_models.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_structure_json.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tests/test_uromt.py +0 -0
- {pycerr-2.2.0 → pycerr-2.2.2}/tools/sync-branches.sh +0 -0
|
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.2] - 2026-09-28
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **GLCM sum entropy dropped the p(x+y) = 2 term.** The emptiness check on the
|
|
15
|
+
indices of grey-level sum 2 used `np.any`, and the only cell with that sum
|
|
16
|
+
sits at index 0, so the term p(1,1)·log₂p(1,1) was left out whenever the
|
|
17
|
+
lowest grey level neighboured itself. Sum entropy was underestimated for
|
|
18
|
+
such structures. The same check in the difference-entropy loop is fixed too.
|
|
19
|
+
- **NGTDM contrast skipped grey-level shifts.** The contrast double sum
|
|
20
|
+
skipped shift *k* whenever grey level *k* was empty, which dropped valid
|
|
21
|
+
pairs for any structure with empty grey levels (typically homogeneous
|
|
22
|
+
organs with fixed bin number).
|
|
23
|
+
- **NGTDM level probabilities now follow IBSI.** `p` counted every voxel in
|
|
24
|
+
the structure but was normalised by the number of voxels with a valid
|
|
25
|
+
neighbourhood (N<sub>v,c</sub>), so it could sum to more than 1 in 2D/2.5D.
|
|
26
|
+
It now counts only voxels with at least one neighbour. All NGTDM features
|
|
27
|
+
can change slightly for 2D/2.5D calculations. With both NGTDM fixes, contrast
|
|
28
|
+
matches the IBSI 1 consensus values for configurations A, B and C.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **`polyFill` is vectorized**, 3-23x faster on typical contours, with
|
|
33
|
+
pixel-identical output (checked against the previous implementation in
|
|
34
|
+
`tests/test_poly_fill.py`). Contours extending past the image edge are now
|
|
35
|
+
clipped; previously they raised `IndexError` past the far edge and wrapped
|
|
36
|
+
around at negative indices.
|
|
37
|
+
- **`importStructureMask` pads a uint8 mask** instead of an int64 copy,
|
|
38
|
+
cutting its memory use 8x. Output is unchanged.
|
|
39
|
+
|
|
40
|
+
## [2.2.1] - 2026-09-26
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- **The Qt desktop viewer's dependencies are now an optional extra.** `PyQt5`,
|
|
45
|
+
`pyvista` and `pyvistaqt` moved out of the base install into
|
|
46
|
+
`pip install "pycerr[viewer]"`. PyQt5 publishes no aarch64 Linux wheel, so
|
|
47
|
+
`pip install pycerr` on arm64 Linux (Apple Silicon Docker images, ARM
|
|
48
|
+
servers) tried to build it from source and failed for want of `qmake`.
|
|
49
|
+
Everything that does not draw a Qt window is unaffected: the Python API,
|
|
50
|
+
batch/headless processing in containers, and the Jupyter viewer
|
|
51
|
+
(`cerr.viewer.pycerr_nbviewer`), which uses `ipywidgets` + matplotlib and
|
|
52
|
+
keeps `ipywidgets` as a base dependency. Launching the desktop viewer without
|
|
53
|
+
the extra now raises an ImportError naming the install command instead of
|
|
54
|
+
`No module named 'PySide2'`.
|
|
55
|
+
|
|
10
56
|
## [2.2.0] - 2026-09-17
|
|
11
57
|
|
|
12
58
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycerr
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: A Computational Environment for Radiological Research
|
|
5
5
|
Author-email: "Aditya P. Apte" <aptea@mskcc.org>, Aditi Iyer <iyera@mskcc.org>, Eve LoCastro <locastre@mskcc.org>, "Joseph O. Deasy" <deasyj@mskcc.org>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cerr/pyCERR
|
|
@@ -30,9 +30,10 @@ Requires-Dist: ipywidgets
|
|
|
30
30
|
Requires-Dist: surface-distance-based-measures
|
|
31
31
|
Requires-Dist: itk-strain
|
|
32
32
|
Requires-Dist: numba
|
|
33
|
-
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
33
|
+
Provides-Extra: viewer
|
|
34
|
+
Requires-Dist: PyQt5; extra == "viewer"
|
|
35
|
+
Requires-Dist: pyvista; extra == "viewer"
|
|
36
|
+
Requires-Dist: pyvistaqt; extra == "viewer"
|
|
36
37
|
Provides-Extra: napari
|
|
37
38
|
Requires-Dist: napari[all]; extra == "napari"
|
|
38
39
|
Requires-Dist: napari-animation; extra == "napari"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2,
|
|
21
|
+
__version__ = version = '2.2.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'gf85644a5a'
|
|
@@ -29,77 +29,81 @@ def polyFill(rowV, colV, xSize, ySize):
|
|
|
29
29
|
np.ndarray: Boolean mask of shape ``(xSize, ySize)`` where filled
|
|
30
30
|
pixels are 1 and background pixels are 0.
|
|
31
31
|
"""
|
|
32
|
-
#
|
|
32
|
+
# Vectorized scanline fill: O(edge crossings) rather than a Python loop over
|
|
33
|
+
# scanlines x edges. Scanlines run along the column (y) axis. Semantics:
|
|
34
|
+
# an edge covers scanlines ceil(top)..bottom but toggles only where y != bottom;
|
|
35
|
+
# a flat edge on an integer scanline draws ceil(min row)..floor(max row); an
|
|
36
|
+
# integer bottom vertex draws its own pixel; toggles within 1e-6 of an integer
|
|
37
|
+
# snap to it; sorted toggles pair up as ceil(t0)..floor(t1). An odd crossing
|
|
38
|
+
# count on a scanline raises IndexError. Pixels outside the image are clipped.
|
|
33
39
|
result = np.zeros((xSize, ySize))
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
result[drawlist, y] = 1
|
|
102
|
-
|
|
40
|
+
rowV = np.asarray(rowV, dtype=float)
|
|
41
|
+
colV = np.asarray(colV, dtype=float)
|
|
42
|
+
if len(rowV) == 0:
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
# Edges ordered top to bottom (y-wise); the polygon is closed
|
|
46
|
+
nxt = np.roll(np.arange(len(rowV)), -1)
|
|
47
|
+
p1x, p1y, p2x, p2y = rowV, colV, rowV[nxt], colV[nxt]
|
|
48
|
+
swap = ~(p1y <= p2y)
|
|
49
|
+
ax = np.where(swap, p2x, p1x)
|
|
50
|
+
ay = np.where(swap, p2y, p1y)
|
|
51
|
+
bx = np.where(swap, p1x, p2x)
|
|
52
|
+
by = np.where(swap, p1y, p2y)
|
|
53
|
+
if int(np.floor(np.max(by))) < int(np.ceil(np.min(ay))):
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
# Flat edges lying on an integer scanline
|
|
57
|
+
flat = ay == by
|
|
58
|
+
for e in np.nonzero(flat & (ay == np.round(ay)) & (ay >= 0) & (ay < ySize))[0]:
|
|
59
|
+
lo, hi = (bx[e], ax[e]) if ax[e] > bx[e] else (ax[e], bx[e])
|
|
60
|
+
result[max(int(np.ceil(lo)), 0):int(np.floor(hi)) + 1, int(ay[e])] = 1
|
|
61
|
+
|
|
62
|
+
# Bottom vertices on integer grid points
|
|
63
|
+
ax, ay, bx, by = ax[~flat], ay[~flat], bx[~flat], by[~flat]
|
|
64
|
+
vertex = (by == np.round(by)) & (bx == np.round(bx)) & (bx >= 0) & (bx < xSize) & (by >= 0) & (by < ySize)
|
|
65
|
+
result[bx[vertex].astype(int), by[vertex].astype(int)] = 1
|
|
66
|
+
|
|
67
|
+
# Toggle points of each edge on every scanline it crosses
|
|
68
|
+
y0 = np.ceil(ay).astype(np.int64)
|
|
69
|
+
y1 = np.floor(by).astype(np.int64)
|
|
70
|
+
y1 = np.where(by == np.round(by), y1 - 1, y1) # no toggle on the bottom scanline
|
|
71
|
+
k = np.maximum(y1 - y0 + 1, 0)
|
|
72
|
+
total = int(k.sum())
|
|
73
|
+
if total == 0:
|
|
74
|
+
return result
|
|
75
|
+
e = np.repeat(np.arange(len(k)), k)
|
|
76
|
+
y = y0[e] + (np.arange(total) - np.repeat(np.cumsum(k) - k, k))
|
|
77
|
+
invslope = (bx[e] - ax[e]) / (by[e] - ay[e])
|
|
78
|
+
t = ax[e] + invslope * (y.astype(float) - ay[e])
|
|
79
|
+
|
|
80
|
+
# Snap to center if less than tolerance
|
|
81
|
+
t_round = np.round(t)
|
|
82
|
+
snap = np.abs(t_round - t) < 1e-6
|
|
83
|
+
t[snap] = t_round[snap]
|
|
84
|
+
|
|
85
|
+
# Sort toggles within each scanline and pair them up
|
|
86
|
+
order = np.lexsort((t, y))
|
|
87
|
+
t, y = t[order], y[order]
|
|
88
|
+
if np.any(np.unique(y, return_counts=True)[1] % 2):
|
|
89
|
+
raise IndexError("odd number of polygon crossings on a scanline")
|
|
90
|
+
x1 = np.ceil(t[0::2]).astype(np.int64)
|
|
91
|
+
x2 = np.floor(t[1::2]).astype(np.int64)
|
|
92
|
+
cols = y[0::2]
|
|
93
|
+
# Clip runs to the image
|
|
94
|
+
x1 = np.maximum(x1, 0)
|
|
95
|
+
x2 = np.minimum(x2, xSize - 1)
|
|
96
|
+
keep = (x1 <= x2) & (cols >= 0) & (cols < ySize)
|
|
97
|
+
x1, x2, cols = x1[keep], x2[keep], cols[keep]
|
|
98
|
+
if len(x1) == 0:
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
# Draw all runs at once with a difference array along rows
|
|
102
|
+
r0, r1, c0, c1 = int(x1.min()), int(x2.max()), int(cols.min()), int(cols.max())
|
|
103
|
+
runs = np.zeros((r1 - r0 + 2, c1 - c0 + 1), dtype=np.int32)
|
|
104
|
+
np.add.at(runs, (x1 - r0, cols - c0), 1)
|
|
105
|
+
np.add.at(runs, (x2 + 1 - r0, cols - c0), -1)
|
|
106
|
+
result[r0:r1 + 1, c0:c1 + 1][np.cumsum(runs, axis=0)[:-1] > 0] = 1
|
|
103
107
|
return result
|
|
104
108
|
|
|
105
109
|
# Example usage:
|
|
@@ -956,8 +956,9 @@ def importStructureMask(mask3M, assocScanNum, structName, planC, structNum=None)
|
|
|
956
956
|
"""
|
|
957
957
|
|
|
958
958
|
# Pad mask to account for boundary edges
|
|
959
|
-
|
|
960
|
-
paddedMask3M = np.pad(
|
|
959
|
+
# uint8 rather than int64 keeps the copy small; only voxels == 1 are contoured below
|
|
960
|
+
paddedMask3M = np.pad(np.asarray(mask3M == 1, dtype=np.uint8), ((1, 1), (1, 1), (0, 0)),
|
|
961
|
+
'constant', constant_values=0)
|
|
961
962
|
dt = datetime.now()
|
|
962
963
|
if isinstance(structNum,(int, float, np.integer, np.floating)):
|
|
963
964
|
struct_meta = planC.structure[structNum]
|
|
@@ -178,7 +178,7 @@ def cooccurToScalarFeatures(cooccurM):
|
|
|
178
178
|
px[n, col] = np.sum(cooccurM[indPxC[n], col])
|
|
179
179
|
pXminusY[n, col] = np.sum(cooccurM[indCtrstC[n], col])
|
|
180
180
|
|
|
181
|
-
if
|
|
181
|
+
if len(indCtrstC[n]) > 0:
|
|
182
182
|
pXminusYlogPXminusY[n, col] = pXminusY[n, col] * np.log2(pXminusY[n, col] + np.finfo(float).eps)
|
|
183
183
|
else:
|
|
184
184
|
pXminusYlogPXminusY[n, col] = 0
|
|
@@ -273,7 +273,7 @@ def cooccurToScalarFeatures(cooccurM):
|
|
|
273
273
|
pXplusY[n - 1, off] = np.sum(cooccurM[indPxPlusYc[n - 1], off], axis=0)
|
|
274
274
|
|
|
275
275
|
# Calculate p(x+y) log2(p(x+y))
|
|
276
|
-
if
|
|
276
|
+
if len(indPxPlusYc[n - 1]) > 0:
|
|
277
277
|
pXplusYlogPXplusY[n - 1, off] = pXplusY[n - 1, off] * np.log2(pXplusY[n - 1, off] + np.finfo(float).eps)
|
|
278
278
|
else:
|
|
279
279
|
pXplusYlogPXplusY[n - 1, off] = 0 #np.zeros(numCooccurs)
|
|
@@ -100,12 +100,10 @@ def calcNGTDM(scan_array, patch_size, numGrLevels):
|
|
|
100
100
|
ind_lev_v = (vox_val_v == lev) & vox_mask_v.astype(bool)
|
|
101
101
|
vals_v = q_m[ind_lev_v]
|
|
102
102
|
s[lev - 1, :] += vals_v.sum().astype(s.dtype)
|
|
103
|
+
p[lev - 1] += ind_lev_v.sum()
|
|
103
104
|
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
for iLev in range(1, numGrLevels + 1):
|
|
107
|
-
scanLev3M = scan_array == iLev
|
|
108
|
-
p[iLev - 1] = np.sum(scanLev3M) / Nvc
|
|
105
|
+
# Level probabilities (p) over voxels with a valid neighbourhood (IBSI n_i / N_v,c)
|
|
106
|
+
p = p / Nvc
|
|
109
107
|
|
|
110
108
|
return s, p, Nvc
|
|
111
109
|
|
|
@@ -127,8 +125,6 @@ def ngtdmToScalarFeatures(s, p, Nvc):
|
|
|
127
125
|
term1 = 0
|
|
128
126
|
term2 = 0
|
|
129
127
|
for lev in range(1, numLevels + 1):
|
|
130
|
-
if p[lev-1] == 0:
|
|
131
|
-
continue
|
|
132
128
|
term1 += np.sum(p * np.roll(p, lev) * (indV - np.roll(indV, lev))**2)
|
|
133
129
|
term2 += s[lev - 1, 0]
|
|
134
130
|
featuresS['contrast'] = 1 / (Ng * (Ng - 1)) * term1 * term2 / Nvc
|
|
@@ -8,10 +8,13 @@ A CERR-style (MATLAB CERR) slice viewer built on **pyCERR**, in a single file: `
|
|
|
8
8
|
## Setup (Windows, your paths)
|
|
9
9
|
|
|
10
10
|
```bat
|
|
11
|
-
pip install
|
|
12
|
-
pip install pyvista pyvistaqt :: optional, GPU-accelerated 3D view
|
|
11
|
+
pip install "pycerr[viewer]" :: Qt binding + pyvista/pyvistaqt 3D view
|
|
13
12
|
```
|
|
14
13
|
|
|
14
|
+
The base `pip install pycerr` is deliberately Qt-free, so it installs on
|
|
15
|
+
platforms without a PyQt5 wheel (arm64 Linux) and in headless containers; the
|
|
16
|
+
`viewer` extra adds what this desktop GUI needs.
|
|
17
|
+
|
|
15
18
|
pyCERR itself: either `pip install pyCERR`, **or** use your local checkout — the script
|
|
16
19
|
already points at it via:
|
|
17
20
|
|
|
@@ -57,7 +57,8 @@ Features (mirroring the classic CERR slice viewer):
|
|
|
57
57
|
view (updates as you scroll) and the full field pyramid in the 3D view
|
|
58
58
|
|
|
59
59
|
Requirements:
|
|
60
|
-
pip install
|
|
60
|
+
pip install "pycerr[viewer]" -- Qt binding + pyvista/pyvistaqt 3D view
|
|
61
|
+
(the base pycerr install is Qt-free)
|
|
61
62
|
-- or add your local pyCERR checkout to sys.path (see PYCERR_PATH below).
|
|
62
63
|
|
|
63
64
|
Run:
|
|
@@ -18,8 +18,20 @@ try:
|
|
|
18
18
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
19
19
|
from PyQt5.QtCore import Qt
|
|
20
20
|
except ImportError: # pragma: no cover - PySide fallback
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
try:
|
|
22
|
+
from PySide2 import QtCore, QtGui, QtWidgets # type: ignore
|
|
23
|
+
from PySide2.QtCore import Qt # type: ignore
|
|
24
|
+
except ImportError as exc: # no Qt binding at all
|
|
25
|
+
# The Qt binding is an optional dependency (see pyproject's `viewer`
|
|
26
|
+
# extra), so say how to get it rather than surfacing a bare
|
|
27
|
+
# "No module named 'PySide2'".
|
|
28
|
+
raise ImportError(
|
|
29
|
+
"The pyCERR desktop viewer needs a Qt binding, which is not part of "
|
|
30
|
+
"the base install. Install it with:\n"
|
|
31
|
+
' pip install "pycerr[viewer]"\n'
|
|
32
|
+
"For notebooks use the Qt-free viewer instead: "
|
|
33
|
+
"from cerr.viewer.pycerr_nbviewer import showNB"
|
|
34
|
+
) from exc
|
|
23
35
|
|
|
24
36
|
import logging
|
|
25
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycerr
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: A Computational Environment for Radiological Research
|
|
5
5
|
Author-email: "Aditya P. Apte" <aptea@mskcc.org>, Aditi Iyer <iyera@mskcc.org>, Eve LoCastro <locastre@mskcc.org>, "Joseph O. Deasy" <deasyj@mskcc.org>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cerr/pyCERR
|
|
@@ -30,9 +30,10 @@ Requires-Dist: ipywidgets
|
|
|
30
30
|
Requires-Dist: surface-distance-based-measures
|
|
31
31
|
Requires-Dist: itk-strain
|
|
32
32
|
Requires-Dist: numba
|
|
33
|
-
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
33
|
+
Provides-Extra: viewer
|
|
34
|
+
Requires-Dist: PyQt5; extra == "viewer"
|
|
35
|
+
Requires-Dist: pyvista; extra == "viewer"
|
|
36
|
+
Requires-Dist: pyvistaqt; extra == "viewer"
|
|
36
37
|
Provides-Extra: napari
|
|
37
38
|
Requires-Dist: napari[all]; extra == "napari"
|
|
38
39
|
Requires-Dist: napari-animation; extra == "napari"
|
|
@@ -17,9 +17,6 @@ ipywidgets
|
|
|
17
17
|
surface-distance-based-measures
|
|
18
18
|
itk-strain
|
|
19
19
|
numba
|
|
20
|
-
PyQt5
|
|
21
|
-
pyvista
|
|
22
|
-
pyvistaqt
|
|
23
20
|
|
|
24
21
|
[ants]
|
|
25
22
|
antspyx
|
|
@@ -37,3 +34,8 @@ nvidia-curand
|
|
|
37
34
|
[napari]
|
|
38
35
|
napari[all]
|
|
39
36
|
napari-animation
|
|
37
|
+
|
|
38
|
+
[viewer]
|
|
39
|
+
PyQt5
|
|
40
|
+
pyvista
|
|
41
|
+
pyvistaqt
|
|
@@ -1164,6 +1164,7 @@
|
|
|
1164
1164
|
"tests/test_lattice.py",
|
|
1165
1165
|
"tests/test_module_imports.py",
|
|
1166
1166
|
"tests/test_nii_export_import.py",
|
|
1167
|
+
"tests/test_poly_fill.py",
|
|
1167
1168
|
"tests/test_portpy_bridge.py",
|
|
1168
1169
|
"tests/test_portpy_bridge_phase2.py",
|
|
1169
1170
|
"tests/test_portpy_bridge_phase3.py",
|
|
@@ -48,12 +48,6 @@ dependencies = [
|
|
|
48
48
|
# that dominate its Gauss-Newton CG matvec. ~90x on the derivative operator
|
|
49
49
|
# versus the vectorized numpy form.
|
|
50
50
|
'numba',
|
|
51
|
-
# Desktop Qt viewer (cerr.viewer.pycerr_gui): PyQt5 is the Qt binding;
|
|
52
|
-
# pyvista/pyvistaqt drive the GPU-accelerated 3D view (vtk is pulled in
|
|
53
|
-
# transitively by pyvista).
|
|
54
|
-
'PyQt5',
|
|
55
|
-
'pyvista',
|
|
56
|
-
'pyvistaqt'
|
|
57
51
|
]
|
|
58
52
|
|
|
59
53
|
[project.urls]
|
|
@@ -61,6 +55,22 @@ dependencies = [
|
|
|
61
55
|
"Bug Tracker" = "https://github.com/cerr/pyCERR/issues"
|
|
62
56
|
|
|
63
57
|
[project.optional-dependencies]
|
|
58
|
+
# Desktop Qt viewer (cerr.viewer.pycerr_gui) plus the ROE and IMRTP GUIs: PyQt5
|
|
59
|
+
# is the Qt binding; pyvista/pyvistaqt drive the GPU-accelerated 3D view (vtk
|
|
60
|
+
# comes in transitively via pyvista).
|
|
61
|
+
#
|
|
62
|
+
# Kept out of the base install because PyQt5 publishes no aarch64 Linux wheel:
|
|
63
|
+
# on arm64 Linux (Apple Silicon Docker, ARM servers) `pip install pycerr` tried
|
|
64
|
+
# to build it from source and failed for want of qmake. Everything that does
|
|
65
|
+
# not draw a Qt window -- headless servers and containers, batch processing,
|
|
66
|
+
# and the Jupyter viewer (cerr.viewer.pycerr_nbviewer, which needs only
|
|
67
|
+
# ipywidgets + matplotlib) -- works without it:
|
|
68
|
+
# pip install "pycerr[viewer]"
|
|
69
|
+
viewer = [
|
|
70
|
+
"PyQt5",
|
|
71
|
+
"pyvista",
|
|
72
|
+
"pyvistaqt",
|
|
73
|
+
]
|
|
64
74
|
napari = [
|
|
65
75
|
"napari[all]",
|
|
66
76
|
"napari-animation"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Checks that the vectorized rasterseg.polyFill reproduces the original
|
|
3
|
+
scanline implementation pixel for pixel.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
from cerr.contour.rasterseg import polyFill
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def reference_poly_fill(rowV, colV, xSize, ySize):
|
|
12
|
+
"""Original loop-based polyFill (pyCERR <= 2.2.1), kept as the reference."""
|
|
13
|
+
result = np.zeros((xSize, ySize))
|
|
14
|
+
pointCount = len(rowV)
|
|
15
|
+
edgeList = np.zeros((pointCount, 4))
|
|
16
|
+
for point in range(pointCount):
|
|
17
|
+
p1x, p1y = rowV[point], colV[point]
|
|
18
|
+
p2x, p2y = rowV[(point + 1) % pointCount], colV[(point + 1) % pointCount]
|
|
19
|
+
if p1y <= p2y:
|
|
20
|
+
edgeList[point, :] = [p1x, p1y, p2x, p2y]
|
|
21
|
+
else:
|
|
22
|
+
edgeList[point, :] = [p2x, p2y, p1x, p1y]
|
|
23
|
+
minY = int(np.ceil(np.min(edgeList[:, 1])))
|
|
24
|
+
maxY = int(np.floor(np.max(edgeList[:, 3])))
|
|
25
|
+
for y in range(minY, maxY + 1):
|
|
26
|
+
indV = (edgeList[:, 1] <= y) & (edgeList[:, 3] >= y)
|
|
27
|
+
activeEdges = edgeList[indV, :]
|
|
28
|
+
drawlist = np.empty(0, dtype=int)
|
|
29
|
+
togglelist = np.empty(0, dtype=float)
|
|
30
|
+
for edge in range(activeEdges.shape[0]):
|
|
31
|
+
p1x, p1y, p2x, p2y = activeEdges[edge, :]
|
|
32
|
+
if p1y == p2y:
|
|
33
|
+
if p1x > p2x:
|
|
34
|
+
drawlist = np.append(drawlist, np.arange(int(np.ceil(p2x)), int(np.floor(p1x)) + 1))
|
|
35
|
+
else:
|
|
36
|
+
drawlist = np.append(drawlist, np.arange(int(np.ceil(p1x)), int(np.floor(p2x)) + 1))
|
|
37
|
+
elif p2y == y and p2x == round(p2x):
|
|
38
|
+
drawlist = np.append(drawlist, int(p2x))
|
|
39
|
+
elif p2y != y:
|
|
40
|
+
invslope = float(p2x - p1x) / float(p2y - p1y)
|
|
41
|
+
togglelist = np.append(togglelist, p1x + (invslope * (y - p1y)))
|
|
42
|
+
togglelistRound = np.round(togglelist)
|
|
43
|
+
ind_snap = np.abs(togglelistRound - togglelist) < 1e-6
|
|
44
|
+
togglelist[ind_snap] = togglelistRound[ind_snap]
|
|
45
|
+
togglelist.sort()
|
|
46
|
+
for i in range(0, len(togglelist), 2):
|
|
47
|
+
x1, x2 = int(np.ceil(togglelist[i])), int(np.floor(togglelist[i + 1]))
|
|
48
|
+
result[x1:x2 + 1, y] = 1
|
|
49
|
+
result[drawlist, y] = 1
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def star_polygon(rng, center, rMax, nPts, integer=False):
|
|
54
|
+
ang = np.sort(rng.uniform(0, 2 * np.pi, nPts))
|
|
55
|
+
rad = rng.uniform(0.3 * rMax, rMax, nPts)
|
|
56
|
+
rowV = center[0] + rad * np.cos(ang)
|
|
57
|
+
colV = center[1] + rad * np.sin(ang)
|
|
58
|
+
if integer:
|
|
59
|
+
rowV, colV = np.round(rowV), np.round(colV)
|
|
60
|
+
return rowV, colV
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def assert_same(rowV, colV, xSize, ySize):
|
|
64
|
+
rowV, colV = np.asarray(rowV, dtype=float), np.asarray(colV, dtype=float)
|
|
65
|
+
inside = rowV.min() >= 0 and colV.min() >= 0 and rowV.max() < xSize - 1 and colV.max() < ySize - 1
|
|
66
|
+
if inside:
|
|
67
|
+
expected = reference_poly_fill(rowV, colV, xSize, ySize)
|
|
68
|
+
else:
|
|
69
|
+
# The reference raises past the far edge and wraps negative indices;
|
|
70
|
+
# polyFill clips. Compare against the reference on a padded canvas.
|
|
71
|
+
pad = int(np.ceil(max(-rowV.min(), -colV.min(), rowV.max() - xSize,
|
|
72
|
+
colV.max() - ySize, 0))) + 2
|
|
73
|
+
expected = reference_poly_fill(rowV + pad, colV + pad, xSize + 2 * pad,
|
|
74
|
+
ySize + 2 * pad)[pad:pad + xSize, pad:pad + ySize]
|
|
75
|
+
np.testing.assert_array_equal(polyFill(rowV, colV, xSize, ySize), expected)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@pytest.mark.parametrize("integer", [False, True])
|
|
79
|
+
def test_random_polygons(integer):
|
|
80
|
+
rng = np.random.default_rng(0 if integer else 1)
|
|
81
|
+
for _ in range(300):
|
|
82
|
+
nPts = int(rng.integers(3, 60))
|
|
83
|
+
rowV, colV = star_polygon(rng, (64, 64), rng.uniform(2, 60), nPts, integer)
|
|
84
|
+
assert_same(rowV, colV, 128, 128)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_axis_aligned_and_degenerate():
|
|
88
|
+
# square with flat edges on integer scanlines
|
|
89
|
+
assert_same([10, 10, 20, 20], [10, 30, 30, 10], 64, 64)
|
|
90
|
+
# half-integer square
|
|
91
|
+
assert_same([10.5, 10.5, 20.5, 20.5], [10.5, 30.5, 30.5, 10.5], 64, 64)
|
|
92
|
+
# triangle with integer vertices
|
|
93
|
+
assert_same([10, 20, 30, 20], [5, 10, 5, 1], 50, 50)
|
|
94
|
+
# sub-pixel polygon
|
|
95
|
+
assert_same([5.2, 5.4, 5.3], [5.1, 5.2, 5.4], 16, 16)
|
|
96
|
+
# single point and a line
|
|
97
|
+
assert_same([5.0], [5.0], 16, 16)
|
|
98
|
+
assert_same([2.0, 12.0], [3.0, 3.0], 16, 16)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_polygon_extending_past_image():
|
|
102
|
+
rng = np.random.default_rng(2)
|
|
103
|
+
for _ in range(100):
|
|
104
|
+
rowV, colV = star_polygon(rng, (rng.uniform(40, 60), rng.uniform(40, 60)), 30, 25)
|
|
105
|
+
assert_same(rowV, colV, 64, 64)
|
|
106
|
+
# crossing the top/left border (negative indices)
|
|
107
|
+
rowV, colV = star_polygon(rng, (rng.uniform(0, 10), rng.uniform(15, 50)), 12, 25)
|
|
108
|
+
assert_same(rowV, colV, 64, 64)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_multi_segment_contour():
|
|
112
|
+
# two disjoint polygons concatenated into one vertex list
|
|
113
|
+
rowV = np.r_[10.3, 10.3, 20.7, 20.7, 30.2, 40.8, 35.5]
|
|
114
|
+
colV = np.r_[10.2, 20.6, 20.6, 10.2, 30.1, 30.9, 45.4]
|
|
115
|
+
assert_same(rowV, colV, 64, 64)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|