From b85204d7ca925685cf52d14cb3b2ffe1a93519b9 Mon Sep 17 00:00:00 2001 From: Alexey Kozhevin Date: Fri, 9 May 2025 10:00:24 +0400 Subject: [PATCH 1/6] Fix plot (#782) * Fix plot * Updates for torch.GradScaler --- batchflow/models/torch/base.py | 2 +- batchflow/plotter/plot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/batchflow/models/torch/base.py b/batchflow/models/torch/base.py index e0d1f47e3..2334fd927 100755 --- a/batchflow/models/torch/base.py +++ b/batchflow/models/torch/base.py @@ -683,7 +683,7 @@ def make_infrastructure(self): self.make_loss() self.make_optimizer() self.make_decay() - self.scaler = torch.cuda.amp.GradScaler() + self.scaler = torch.GradScaler("cuda") self.setup_gradient_clipping() self.setup_weights_averaging() diff --git a/batchflow/plotter/plot.py b/batchflow/plotter/plot.py index 653196104..d05d3fb3d 100644 --- a/batchflow/plotter/plot.py +++ b/batchflow/plotter/plot.py @@ -855,10 +855,10 @@ def clear(self): self.annotations = {} - self.ax.clear() for layer in self.layers: for obj in layer.objects: obj.remove() + self.ax.clear() self.layers = [] From e81a745fa4660aadac5485a554e497c4852f8d47 Mon Sep 17 00:00:00 2001 From: Roman Kh Date: Tue, 10 Jun 2025 12:22:47 +0400 Subject: [PATCH 2/6] Update status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a74f2417d..8cb79333c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyTorch](https://img.shields.io/badge/PyTorch-2.0-orange.svg)](https://pytorch.org) [![codecov](https://codecov.io/gh/analysiscenter/batchflow/branch/master/graph/badge.svg)](https://codecov.io/gh/analysiscenter/batchflow) [![PyPI](https://badge.fury.io/py/batchflow.svg)](https://badge.fury.io/py/batchflow) -[![Status](https://github.com/analysiscenter/batchflow/workflows/status/badge.svg)](https://github.com/analysiscenter/batchflow/actions?query=workflow%3Astatus) +[![Status](https://github.com/analysiscenter/batchflow/workflows/status.yml/badge.svg?branch=master)](https://github.com/analysiscenter/batchflow/actions?query=workflow%3Astatus) # BatchFlow From 1c48ac835cc3d91562d57579fc296943cb3f0feb Mon Sep 17 00:00:00 2001 From: Roman Kh Date: Tue, 10 Jun 2025 12:31:14 +0400 Subject: [PATCH 3/6] Fix status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8cb79333c..3483da2a1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyTorch](https://img.shields.io/badge/PyTorch-2.0-orange.svg)](https://pytorch.org) [![codecov](https://codecov.io/gh/analysiscenter/batchflow/branch/master/graph/badge.svg)](https://codecov.io/gh/analysiscenter/batchflow) [![PyPI](https://badge.fury.io/py/batchflow.svg)](https://badge.fury.io/py/batchflow) -[![Status](https://github.com/analysiscenter/batchflow/workflows/status.yml/badge.svg?branch=master)](https://github.com/analysiscenter/batchflow/actions?query=workflow%3Astatus) +[![Status](https://github.com/analysiscenter/batchflow/actions/workflows/status.yml/badge.svg?branch=master)](https://github.com/analysiscenter/batchflow/actions?query=workflow%3Astatus) # BatchFlow From 3556a207c3d833742b92baabf093bf187f9e9884 Mon Sep 17 00:00:00 2001 From: Roman Kh Date: Wed, 11 Jun 2025 08:12:04 +0000 Subject: [PATCH 4/6] Fix copilot's duplicating code --- .github/workflows/test-install.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 69732a4b7..a26ac2d45 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -198,11 +198,3 @@ jobs: run: | cd tests uv run python -m pytest --disable-pytest-warnings -v dataset_test.py filesindex_test.py datasetindex_test.py - - name: Run basic tests - run: | - cd tests - uv run python -m pytest --disable-pytest-warnings -v dataset_test.py filesindex_test.py datasetindex_test.py - - name: Run basic tests - run: | - cd tests - uv run python -m pytest --disable-pytest-warnings -v dataset_test.py filesindex_test.py datasetindex_test.py From d96b1244788420340081ad2063ecbf6bf444c312 Mon Sep 17 00:00:00 2001 From: Alexey Kozhevin Date: Wed, 11 Jun 2025 13:09:46 +0400 Subject: [PATCH 5/6] Change version in pyproject --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4b5acc7cc..6bd6d1cf6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "batchflow" -version = "0.8.11" +version = "0.8.12" description = "ML pipelines, model configuration and batch management" authors = [{ name = "Roman Kh", email = "rhudor@gmail.com" }] license = {text = "Apache License 2.0"} From b1ee4dd9431395dd21957137ea45f9e8d02ef263 Mon Sep 17 00:00:00 2001 From: alexeykozhevin Date: Fri, 27 Jun 2025 13:24:26 +0000 Subject: [PATCH 6/6] Add dilate and erode --- batchflow/plotter/morphology.py | 125 ++++++++++++++++++++++++++++++++ batchflow/plotter/plot.py | 4 +- 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 batchflow/plotter/morphology.py diff --git a/batchflow/plotter/morphology.py b/batchflow/plotter/morphology.py new file mode 100644 index 000000000..3f3c09d48 --- /dev/null +++ b/batchflow/plotter/morphology.py @@ -0,0 +1,125 @@ +"""Morphological operations implemented with numba to replace cv2 dependency.""" + +import numpy as np +from numba import njit, prange + + +@njit +def dilate(image, kernel, iterations=1): + """Dilate an image using a structuring element. + + Parameters + ---------- + image : numpy.ndarray + Input image to dilate. + kernel : numpy.ndarray + Structuring element (kernel) for dilation. Should contain 1s where + the structuring element is active and 0s elsewhere. + iterations : int, optional + Number of times to apply the dilation. Default is 1. + + Returns + ------- + numpy.ndarray + Dilated image with the same shape and dtype as input. + + """ + result = image.copy() + + for _ in range(iterations): + result = _single_dilate(result, kernel) + + return result + +@njit +def erode(image, kernel, iterations=1): + """Erode an image using a structuring element. + + Parameters + ---------- + image : numpy.ndarray + Input image to erode. + kernel : numpy.ndarray + Structuring element (kernel) for erosion. Should contain 1s where + the structuring element is active and 0s elsewhere. + iterations : int, optional + Number of times to apply the erosion. Default is 1. + + Returns + ------- + numpy.ndarray + Eroded image with the same shape and dtype as input. + + """ + result = image.copy() + + for _ in range(iterations): + result = _single_erode(result, kernel) + + return result + +@njit(parallel=True) +def _single_dilate(image, kernel): + """Single iteration of dilation operation.""" + height, width = image.shape + kh, kw = kernel.shape + kh_half, kw_half = kh // 2, kw // 2 + + # Create output array + result = np.zeros_like(image) + + # Apply dilation - for each output pixel, find max in kernel neighborhood + for i in prange(height): + for j in range(width): + max_val = image[i, j] # Start with current pixel value + + for ki in range(kh): + for kj in range(kw): + if kernel[ki, kj] > 0: # Only consider active kernel elements + # Calculate the source image coordinates + img_i = i + ki - kh_half + img_j = j + kj - kw_half + + # Check bounds + if 0 <= img_i < height and 0 <= img_j < width: + if image[img_i, img_j] > max_val: + max_val = image[img_i, img_j] + + result[i, j] = max_val + + return result + +@njit(parallel=True) +def _single_erode(image, kernel): + """Single iteration of erosion operation.""" + height, width = image.shape + kh, kw = kernel.shape + kh_half, kw_half = kh // 2, kw // 2 + + # Create output array + result = np.zeros_like(image) + + # Apply erosion - for each output pixel, find min in kernel neighborhood + for i in prange(height): + for j in range(width): + min_val = image[i, j] # Start with current pixel value + + for ki in range(kh): + for kj in range(kw): + if kernel[ki, kj] > 0: # Only consider active kernel elements + # Calculate the source image coordinates + img_i = i + ki - kh_half + img_j = j + kj - kw_half + + # Check bounds - treat out of bounds as 0 for erosion + if 0 <= img_i < height and 0 <= img_j < width: + if image[img_i, img_j] < min_val: + min_val = image[img_i, img_j] + else: + # Outside bounds treated as 0, so erosion result should be 0 + min_val = 0 + break + + result[i, j] = min_val + + return result diff --git a/batchflow/plotter/plot.py b/batchflow/plotter/plot.py index d05d3fb3d..bba584351 100644 --- a/batchflow/plotter/plot.py +++ b/batchflow/plotter/plot.py @@ -103,7 +103,7 @@ def flatten(self, data): def dilate(self, data): """ Apply dilation to array. """ - import cv2 + from .morphology import dilate dilation_config = self.config.get('dilate', False) default_kernel = np.ones((3, 1), dtype=np.uint8) @@ -116,7 +116,7 @@ def dilate(self, data): dilation_config = {'kernel': np.ones(dilation_config, dtype=np.uint8)} elif 'kernel' in dilation_config and isinstance(dilation_config['kernel'], tuple): dilation_config['kernel'] = np.ones(dilation_config['kernel'], dtype=np.uint8) - data = cv2.dilate(data.astype(np.float32), **dilation_config) + data = dilate(data.astype(np.float32), **dilation_config) return data def mask(self, data):