From 101c39d535fc88e0165c9ca0fe827751714aeeb1 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sat, 5 Nov 2022 23:16:22 +0900 Subject: [PATCH 01/10] updating reference... --- docs/reference.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference.rst b/docs/reference.rst index 10dab73a..ab7a4e3a 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -34,3 +34,9 @@ score calculation utilities .. automodule:: laptrack.scores :members: + +metric utilities +------------------------------- + +.. automodule:: laptrack.metric_utils + :members: From 202b9ec0d24fab97406a073ac8c78d00a97a2e3f Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 13:21:30 +0900 Subject: [PATCH 02/10] updated reference... --- docs/examples/overlap_tracking.ipynb | 2 +- docs/reference.rst | 1 + src/laptrack/metric_utils.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/examples/overlap_tracking.ipynb b/docs/examples/overlap_tracking.ipynb index 36aa5ecf..b7e17aed 100644 --- a/docs/examples/overlap_tracking.ipynb +++ b/docs/examples/overlap_tracking.ipynb @@ -415,7 +415,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Here is the core of this example: we can define arbitrary function as the metric to measure how points are close.\n", + "Here is the core of this example: we can define an arbitrary function as the metric to measure how points are close.\n", "\n", "In this example, `metric` function retrive the pre-computed overlap between the segmented regions, and use\n", "$$\n", diff --git a/docs/reference.rst b/docs/reference.rst index ab7a4e3a..309f4bf6 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -40,3 +40,4 @@ metric utilities .. automodule:: laptrack.metric_utils :members: + :special-members: __init__ diff --git a/src/laptrack/metric_utils.py b/src/laptrack/metric_utils.py index 041147c3..7c583618 100644 --- a/src/laptrack/metric_utils.py +++ b/src/laptrack/metric_utils.py @@ -38,7 +38,7 @@ def __init__(self, label_images: IntArray): Parameters ---------- label_images : IntArray - The labeled images. The first dimension is interpreted as the time dimension. + The labeled images. The first dimension is interpreted as the frame dimension. """ self.label_images = label_images self.ndim = label_images.ndim - 1 From 4153ccb1b968a43b330a335a83cd5c8093d7efa3 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 13:35:22 +0900 Subject: [PATCH 03/10] added version and bumped patch --- pyproject.toml | 4 +++- src/laptrack/__init__.py | 2 ++ src/laptrack/metric_utils.py | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f72ac64c..5baba9f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "laptrack" -version = "0.9.0" +version = "0.9.1" description = "LapTrack" authors = ["Yohsuke Fukai "] license = "BSD-3-Clause" @@ -83,3 +83,5 @@ show_error_context = true [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.poetry_bumpversion.file."src/laptrack/__init__.py"] diff --git a/src/laptrack/__init__.py b/src/laptrack/__init__.py index 3e955602..55db1f06 100644 --- a/src/laptrack/__init__.py +++ b/src/laptrack/__init__.py @@ -15,3 +15,5 @@ "scores", "metric_utils", ] + +__version__ = "0.9.1" diff --git a/src/laptrack/metric_utils.py b/src/laptrack/metric_utils.py index 7c583618..57c3b38d 100644 --- a/src/laptrack/metric_utils.py +++ b/src/laptrack/metric_utils.py @@ -40,6 +40,9 @@ def __init__(self, label_images: IntArray): label_images : IntArray The labeled images. The first dimension is interpreted as the frame dimension. """ + label_images = np.array(label_images) + if label_images.ndim < 3: + raise ValueError("label_images dimension must be >=3.") self.label_images = label_images self.ndim = label_images.ndim - 1 dfs = [] From 1851cf6c88bcbc26b4d76e2e604cd41152fd4c55 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 13:45:34 +0900 Subject: [PATCH 04/10] fixing doc typo --- docs/examples/api_example.ipynb | 8 +++++--- src/laptrack/metric_utils.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/examples/api_example.ipynb b/docs/examples/api_example.ipynb index 5a577e54..85a3c85e 100644 --- a/docs/examples/api_example.ipynb +++ b/docs/examples/api_example.ipynb @@ -64,8 +64,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "Load the example point coordinates from a CSV file.\n", + "\n", "`spots_df` has columns `[\"frame\", \"position_x\", \"position_y\"]` \n", - "(can be arbitrary, and the column names for trackign will be specified later)" + "(can be arbitrary, and the column names for tracking will be specified later)." ] }, { @@ -111,8 +113,8 @@ " track_dist_metric=\"sqeuclidean\", # The similarity metric for particles. See `scipy.spatial.distance.cdist` for allowed values.\n", " splitting_dist_metric=\"sqeuclidean\",\n", " merging_dist_metric=\"sqeuclidean\",\n", - " track_cost_cutoff=max_distance\n", - " ** 2, # the square of the cutoff distance for the \"sqeuclidean\" metric\n", + " # the square of the cutoff distance for the \"sqeuclidean\" metric\n", + " track_cost_cutoff=max_distance**2,\n", " splitting_cost_cutoff=max_distance**2, # or False for non-splitting case\n", " merging_cost_cutoff=max_distance**2, # or False for non-merging case\n", ")" diff --git a/src/laptrack/metric_utils.py b/src/laptrack/metric_utils.py index 57c3b38d..e0222375 100644 --- a/src/laptrack/metric_utils.py +++ b/src/laptrack/metric_utils.py @@ -40,7 +40,8 @@ def __init__(self, label_images: IntArray): label_images : IntArray The labeled images. The first dimension is interpreted as the frame dimension. """ - label_images = np.array(label_images) + if not isinstance(label_images, np.ndarray): + label_images = np.array(label_images) if label_images.ndim < 3: raise ValueError("label_images dimension must be >=3.") self.label_images = label_images From 3043dd558d4ff57e627708209636b9f1e9ed94d4 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 13:56:15 +0900 Subject: [PATCH 05/10] mentioned poetry-bumpversion --- CONTRIBUTING.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 681ff836..5404728f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -48,6 +48,7 @@ You need Python 3.8+ and the following tools: - Poetry_ - Nox_ - nox-poetry_ +- poetry-bumpversion_ Install the package with development requirements: @@ -61,11 +62,11 @@ or the command-line interface: .. code:: console $ poetry run python - $ poetry run laptrack .. _Poetry: https://python-poetry.org/ .. _Nox: https://nox.thea.codes/ .. _nox-poetry: https://nox-poetry.readthedocs.io/ +.. _poetry-bumpversion: https://github.com/monim67/poetry-bumpversion How to test the project From f252aeb627e282ed9b509b3e801c1ac48cbcb594 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 14:02:13 +0900 Subject: [PATCH 06/10] updating contributing explanations --- CONTRIBUTING.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5404728f..0c1125f9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -56,12 +56,11 @@ Install the package with development requirements: $ poetry install -You can now run an interactive Python session, -or the command-line interface: +You can now run an interactive shell session: .. code:: console - $ poetry run python + $ poetry shell .. _Poetry: https://python-poetry.org/ .. _Nox: https://nox.thea.codes/ From 52b0a8ddf2df62ed1c9a983e3b8af363b88b9834 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 14:10:04 +0900 Subject: [PATCH 07/10] updated colab example --- docs/examples/api_example.ipynb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/examples/api_example.ipynb b/docs/examples/api_example.ipynb index 85a3c85e..cb4017b1 100644 --- a/docs/examples/api_example.ipynb +++ b/docs/examples/api_example.ipynb @@ -29,6 +29,13 @@ " %pip install -q --upgrade laptrack matplotlib pandas" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Important note** please restart the runtime when you're executing this notebook in Google Colaboratory." + ] + }, { "cell_type": "markdown", "metadata": {}, From 3ac3723a5bffb93f1ba9607c8c14d1eb27852eb6 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 14:11:28 +0900 Subject: [PATCH 08/10] update test --- tests/test_metric_utils.py | 39 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/test_metric_utils.py b/tests/test_metric_utils.py index 5db713db..df157ca8 100644 --- a/tests/test_metric_utils.py +++ b/tests/test_metric_utils.py @@ -13,22 +13,25 @@ def test_label_overlap() -> None: [[[0, 1, 1, 1, 0], [5, 5, 5, 5, 5]], [[0, 1, 1, 1, 0], [0, 1, 1, 1, 0]]], ] ) - lo = LabelOverlap(labels) - frame_labels = [np.unique(label) for label in labels] - frame_labels = [x[x > 0] for x in frame_labels] - for f1, f2 in [(0, 0), (0, 1), (1, 2)]: - for l1, l2 in product(frame_labels[f1], frame_labels[f2]): - b1 = labels[f1] == l1 - b2 = labels[f2] == l2 + labelss = [labels, list(labels)] - intersect = np.sum(b1 & b2) - union = np.sum(b1 | b2) - r1 = np.sum(b1) - r2 = np.sum(b2) - res = lo.calc_overlap(f1, l1, f2, l2) - assert ( - intersect, - (intersect / union), - (intersect / r1), - (intersect / r2), - ) == res + for labels in labelss: + lo = LabelOverlap(labels) + frame_labels = [np.unique(label) for label in labels] + frame_labels = [x[x > 0] for x in frame_labels] + for f1, f2 in [(0, 0), (0, 1), (1, 2)]: + for l1, l2 in product(frame_labels[f1], frame_labels[f2]): + b1 = labels[f1] == l1 + b2 = labels[f2] == l2 + + intersect = np.sum(b1 & b2) + union = np.sum(b1 | b2) + r1 = np.sum(b1) + r2 = np.sum(b2) + res = lo.calc_overlap(f1, l1, f2, l2) + assert ( + intersect, + (intersect / union), + (intersect / r1), + (intersect / r2), + ) == res From 720815bbf9f209d3e7bd6142ee9b5b43ec4ee0b3 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 15:22:43 +0900 Subject: [PATCH 09/10] solved mypy issue --- src/laptrack/metric_utils.py | 6 ++++-- tests/test_metric_utils.py | 30 +++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/laptrack/metric_utils.py b/src/laptrack/metric_utils.py index e0222375..946b3674 100644 --- a/src/laptrack/metric_utils.py +++ b/src/laptrack/metric_utils.py @@ -1,5 +1,7 @@ """Utilities for metric calculation.""" +from typing import List from typing import Tuple +from typing import Union import numpy as np import pandas as pd @@ -32,12 +34,12 @@ def _union_bbox(self, r1, r2): bbox.append((y0, y1)) return bbox - def __init__(self, label_images: IntArray): + def __init__(self, label_images: Union[IntArray, List[IntArray]]): """Summarise the segmentation properties and initialize the object. Parameters ---------- - label_images : IntArray + label_images : Union[IntArray,List[IntArray]] The labeled images. The first dimension is interpreted as the frame dimension. """ if not isinstance(label_images, np.ndarray): diff --git a/tests/test_metric_utils.py b/tests/test_metric_utils.py index df157ca8..5fece351 100644 --- a/tests/test_metric_utils.py +++ b/tests/test_metric_utils.py @@ -1,28 +1,32 @@ from itertools import product +from typing import List +from typing import Union import numpy as np +from laptrack._typing_utils import IntArray from laptrack.metric_utils import LabelOverlap def test_label_overlap() -> None: - labels = np.array( - [ - [[[0, 1, 1, 1, 0], [0, 1, 2, 2, 2]], [[0, 1, 2, 2, 2], [3, 3, 3, 1, 0]]], - [[[0, 1, 1, 1, 2], [0, 4, 1, 2, 2]], [[0, 4, 4, 4, 4], [0, 4, 4, 4, 4]]], - [[[0, 1, 1, 1, 0], [5, 5, 5, 5, 5]], [[0, 1, 1, 1, 0], [0, 1, 1, 1, 0]]], - ] - ) - labelss = [labels, list(labels)] + labels = [ + [[[0, 1, 1, 1, 0], [0, 1, 2, 2, 2]], [[0, 1, 2, 2, 2], [3, 3, 3, 1, 0]]], + [[[0, 1, 1, 1, 2], [0, 4, 1, 2, 2]], [[0, 4, 4, 4, 4], [0, 4, 4, 4, 4]]], + [[[0, 1, 1, 1, 0], [5, 5, 5, 5, 5]], [[0, 1, 1, 1, 0], [0, 1, 1, 1, 0]]], + ] + labelss: List[Union[IntArray, List[IntArray]]] = [ + np.array(labels), + [np.array(label).astype(np.int64) for label in labels], + ] - for labels in labelss: - lo = LabelOverlap(labels) - frame_labels = [np.unique(label) for label in labels] + for _labels in labelss: + lo = LabelOverlap(_labels) + frame_labels = [np.unique(label) for label in _labels] frame_labels = [x[x > 0] for x in frame_labels] for f1, f2 in [(0, 0), (0, 1), (1, 2)]: for l1, l2 in product(frame_labels[f1], frame_labels[f2]): - b1 = labels[f1] == l1 - b2 = labels[f2] == l2 + b1 = _labels[f1] == l1 + b2 = _labels[f2] == l2 intersect = np.sum(b1 & b2) union = np.sum(b1 | b2) From 784ef53f85b0dfd7619080930cfdc46a6b8de428 Mon Sep 17 00:00:00 2001 From: Yohsuke Fukai Date: Sun, 6 Nov 2022 21:52:33 +0900 Subject: [PATCH 10/10] added test to check value error --- tests/test_metric_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_metric_utils.py b/tests/test_metric_utils.py index 5fece351..15766376 100644 --- a/tests/test_metric_utils.py +++ b/tests/test_metric_utils.py @@ -3,6 +3,7 @@ from typing import Union import numpy as np +import pytest from laptrack._typing_utils import IntArray from laptrack.metric_utils import LabelOverlap @@ -39,3 +40,5 @@ def test_label_overlap() -> None: (intersect / r1), (intersect / r2), ) == res + with pytest.raises(ValueError): + LabelOverlap(np.array([[1, 2], [0, 1]]))