diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 00000000..4ea72a85 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,8 @@ +[bumpversion] +current_version = 0.1.8 +tag = True +commit = True + +[bumpversion:file:pyproject.toml] + +[bumpversion:file:cyto_dl/__init__.py] diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml new file mode 100644 index 00000000..e33d3d84 --- /dev/null +++ b/.github/workflows/bump.yml @@ -0,0 +1,53 @@ +--- +name: bump +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + bump: + # make sure this job runs only when the PR is not a version bump PR and the pr is merged to main (not just closed) + if: github.event.pull_request.title != 'admin/version-bump' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + contents: write # Add this permission to allow pushing the version bump + pull-requests: write + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Setup PDM + uses: pdm-project/setup-pdm@v2 + with: + python-version: "3.10" + + - name: Install bumpversion + run: pip install bumpversion + + - name: Configure Git + run: | + git config user.name github-actions + git config user.email github-actions@github.com + + - name: Delete remote branch # delete the branch if it exists + run: | + git push origin --delete admin/version-bump || true + + - name: Apply bumpversion minor + run: | + git checkout -b admin/version-bump + bumpversion minor + git push origin admin/version-bump + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + branch: admin/version-bump + title: admin/version-bump + body: "Automated version bump" + base: main diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 567cf437..f29a1dc1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,11 +1,13 @@ --- name: publish on: - push: + pull_request: + types: [closed] branches: [main] - jobs: publish: + # only publish when the PR is a version bump PR and the pr is merged to main + if: github.event.pull_request.title == 'admin/version-bump' && github.event.pull_request.merged == true runs-on: ubuntu-latest environment: release permissions: diff --git a/configs/data/im2im/segmentation_plugin.yaml b/configs/data/im2im/segmentation_plugin.yaml index 1ffa15e1..56536f13 100644 --- a/configs/data/im2im/segmentation_plugin.yaml +++ b/configs/data/im2im/segmentation_plugin.yaml @@ -33,14 +33,16 @@ transforms: reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [TRAIN] Exposing C as a configurable param for multi-channel segs + C: ${target_col1_channel} - _target_: monai.transforms.LoadImaged keys: ${target_col2} allow_missing_keys: True reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [TRAIN] Exposing C as a configurable param for multi-channel segs + C: ${target_col2_channel} - _target_: monai.transforms.ThresholdIntensityd allow_missing_keys: True keys: @@ -108,14 +110,16 @@ transforms: reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [TEST] Exposing C as a configurable param for multi-channel segs + C: ${target_col1_channel} - _target_: monai.transforms.LoadImaged keys: ${target_col2} allow_missing_keys: True reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [TEST] Exposing C as a configurable param for multi-channel segs + C: ${target_col2_channel} # load merging mask - assumed not to exist by default - _target_: cyto_dl.image.io.PolygonLoaderd keys: @@ -184,14 +188,16 @@ transforms: reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [VAL] Exposing C as a configurable param for multi-channel segs + C: ${target_col1_channel} - _target_: monai.transforms.LoadImaged keys: ${target_col2} allow_missing_keys: True reader: - _target_: cyto_dl.image.io.MonaiBioReader dimension_order_out: ${eval:'"CZYX" if ${spatial_dims}==3 else "CYX"'} - C: 0 + # [VAL] Exposing C as a configurable param for multi-channel segs + C: ${target_col2_channel} - _target_: monai.transforms.ThresholdIntensityd allow_missing_keys: True diff --git a/configs/experiment/im2im/segmentation_plugin.yaml b/configs/experiment/im2im/segmentation_plugin.yaml index cf0c35e6..1e8cb38c 100644 --- a/configs/experiment/im2im/segmentation_plugin.yaml +++ b/configs/experiment/im2im/segmentation_plugin.yaml @@ -29,6 +29,8 @@ test: False source_col: raw target_col1: seg1 target_col2: seg2 +target_col1_channel: 0 +target_col2_channel: 0 merge_mask_col: merge_mask exclude_mask_col: exclude_mask base_image_col: base_image diff --git a/cyto_dl/__init__.py b/cyto_dl/__init__.py index 9993d06b..4ae4758a 100644 --- a/cyto_dl/__init__.py +++ b/cyto_dl/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.1.7" +__version__ = "0.1.8" # silence bio packages warnings diff --git a/cyto_dl/train.py b/cyto_dl/train.py index 6403b15c..e00eaaa2 100644 --- a/cyto_dl/train.py +++ b/cyto_dl/train.py @@ -8,6 +8,7 @@ import hydra import lightning import pyrootutils +import torch from lightning import Callback, LightningDataModule, LightningModule, Trainer from lightning.pytorch.loggers.logger import Logger from omegaconf import DictConfig, OmegaConf @@ -94,6 +95,16 @@ def train(cfg: DictConfig) -> Tuple[dict, dict]: if cfg.get("train"): log.info("Starting training!") + + if cfg.get("weights_only"): + assert cfg.get( + "ckpt_path" + ), "ckpt_path must be provided to with argument weights_only=True" + # load model from state dict to get around trainer.max_epochs limit, useful for resuming model training from existing weights + state_dict = torch.load(cfg["ckpt_path"])["state_dict"] + model.load_state_dict(state_dict) + cfg["ckpt_path"] = None + if isinstance(data, LightningDataModule): trainer.fit(model=model, datamodule=data, ckpt_path=cfg.get("ckpt_path")) else: diff --git a/pyproject.toml b/pyproject.toml index 3679dd06..69783749 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "pdm.pep517.api" [project] name = "cyto-dl" -version = "0.1.7" +version = "0.1.8" description = """\ Collection of representation learning models, techniques, callbacks, utils, \ used to create latent variable models of cell shape, morphology and \ diff --git a/tests/test_polygon_loader.py b/tests/test_polygon_loader.py index ed998869..2349b79a 100644 --- a/tests/test_polygon_loader.py +++ b/tests/test_polygon_loader.py @@ -7,8 +7,9 @@ def test_load_polygon(tmp_path): # create random image and find contours - image = random_shapes((500, 500), max_shapes=1, num_channels=1)[0].squeeze() - image[image == 255] = 0 + image = random_shapes((500, 500), max_shapes=1, num_channels=1, random_seed=3)[0].squeeze() + # create polygon from background + image = image == 255 contours = find_contours(label(image)) # Convert contours to polygons and save @@ -24,8 +25,8 @@ def test_load_polygon(tmp_path): reconstructed = transform(data)["poly"][0] # Check that the reconstructed mask is the close to as the original - iou = np.sum(np.logical_and(reconstructed[0], image > 0)) / np.sum( - np.logical_or(reconstructed[0], image > 0) + iou = np.sum(np.logical_and(reconstructed[0] > 0, image > 0)) / np.sum( + np.logical_or(reconstructed[0] > 0, image > 0) ) assert iou > 0.8