├── .github ├── changelog_config.json └── workflows │ ├── lint.yaml │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── LICENSE ├── README.md ├── notebooks ├── FakeTracks.xml └── demo.ipynb ├── pixi.lock ├── pyproject.toml ├── src └── pytrackmate │ ├── __init__.py │ └── _trackmate.py └── tests ├── FakeTracks.xml └── test_trackmate.py /.github/changelog_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "title": "## 🚀 Features", 5 | "labels": [ 6 | "feature" 7 | ] 8 | }, 9 | { 10 | "title": "## 🐛 Fixes", 11 | "labels": [ 12 | "fix" 13 | ] 14 | }, 15 | { 16 | "key": "tests", 17 | "title": "## 🧪 Tests", 18 | "labels": [ 19 | "test" 20 | ] 21 | }, 22 | { 23 | "key": "docs", 24 | "title": "## 📚 Documentation", 25 | "labels": [ 26 | "documentation" 27 | ] 28 | }, 29 | { 30 | "key": "chore", 31 | "title": "## 🧹 Chores", 32 | "labels": [ 33 | "chore" 34 | ] 35 | }, 36 | { 37 | "title": "## 📦 Other", 38 | "labels": [] 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: 8 | - "*" 9 | - "!gh-pages" 10 | 11 | concurrency: 12 | # Cancel previous builds if a new one is triggered. 13 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | tests: 18 | name: lint 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - uses: prefix-dev/setup-pixi@v0.8.1 25 | with: 26 | environments: dev 27 | activate-environment: true 28 | 29 | - name: Lint source code 30 | run: pixi run --environment dev lint 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release-version: 7 | description: "A valid Semver version string" 8 | required: true 9 | type: string 10 | 11 | permissions: 12 | contents: write 13 | 14 | concurrency: 15 | group: "release-${{ github.ref }}" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | release: 20 | name: release 21 | runs-on: ubuntu-latest 22 | 23 | env: 24 | PIXI_ENV_NAME: dev 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: prefix-dev/setup-pixi@v0.8.1 30 | with: 31 | environments: ${{ env.PIXI_ENV_NAME }} 32 | activate-environment: true 33 | 34 | # Check if the release string is valid and ok 35 | 36 | - name: Check the version is valid semver 37 | run: | 38 | RELEASE_VERSION="${{ inputs.release-version }}" 39 | 40 | { 41 | pysemver check $RELEASE_VERSION 42 | } || { 43 | echo "The version '$RELEASE_VERSION' is not a valid Semver version string." 44 | echo "Please use a valid semver version string. More details at https://semver.org/" 45 | echo "The release process is aborted." 46 | exit 1 47 | } 48 | 49 | - name: Check the version is higher than the latest one 50 | run: | 51 | # Retrieve the git tags first 52 | git fetch --prune --unshallow --tags &> /dev/null 53 | 54 | RELEASE_VERSION="${{ inputs.release-version }}" 55 | LATEST_VERSION=$(git describe --abbrev=0 --tags) 56 | 57 | IS_HIGHER_VERSION=$(pysemver compare $RELEASE_VERSION $LATEST_VERSION) 58 | 59 | if [ "$IS_HIGHER_VERSION" != "1" ]; then 60 | echo "The version '$RELEASE_VERSION' is not higher than the latest version '$LATEST_VERSION'." 61 | echo "The release process is aborted." 62 | exit 1 63 | fi 64 | 65 | # Install and check the source code 66 | 67 | - name: Build and install package 68 | env: 69 | SETUPTOOLS_SCM_PRETEND_VERSION: ${{ inputs.release-version }} 70 | run: pixi install --environment ${{ env.PIXI_ENV_NAME }} 71 | 72 | - name: Lint source code 73 | run: pixi run --environment ${{ env.PIXI_ENV_NAME }} lint 74 | 75 | - name: Run tests 76 | run: pixi run --environment ${{ env.PIXI_ENV_NAME }} test 77 | 78 | - name: Build the wheel and sdist 79 | env: 80 | SETUPTOOLS_SCM_PRETEND_VERSION: ${{ inputs.release-version }} 81 | run: python -m build --no-isolation --wheel --sdist 82 | 83 | # Release logic 84 | 85 | - name: Build Changelog 86 | id: github_release 87 | uses: mikepenz/release-changelog-builder-action@v5 88 | env: 89 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 90 | with: 91 | toTag: "main" 92 | configuration: ".github/changelog_config.json" 93 | 94 | - name: Configure git 95 | run: | 96 | git config --global user.name "${GITHUB_ACTOR}" 97 | git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" 98 | 99 | - name: Create and push git tag 100 | env: 101 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 102 | run: | 103 | # Sync with remote 104 | git pull origin main 105 | 106 | # Tag the release 107 | git tag -a "${{ inputs.release-version }}" -m "Release version ${{ inputs.release-version }}" 108 | 109 | # Checkout the git tag 110 | git checkout "${{ inputs.release-version }}" 111 | 112 | # Push the modified changelogs 113 | git push origin main 114 | 115 | # Push the tags 116 | git push origin "${{ inputs.release-version }}" 117 | 118 | - name: Create GitHub Release 119 | uses: softprops/action-gh-release@v2 120 | with: 121 | tag_name: ${{ inputs.release-version }} 122 | body: ${{steps.github_release.outputs.changelog}} 123 | 124 | upload-to-pypi: 125 | name: upload-to-pypi 126 | runs-on: ubuntu-latest 127 | needs: release 128 | 129 | env: 130 | PIXI_ENV_NAME: dev 131 | 132 | permissions: 133 | # IMPORTANT: this permission is mandatory for Trusted Publishing 134 | id-token: write 135 | 136 | steps: 137 | - uses: actions/checkout@v4 138 | 139 | - uses: prefix-dev/setup-pixi@v0.8.1 140 | with: 141 | environments: ${{ env.PIXI_ENV_NAME }} 142 | activate-environment: true 143 | 144 | # Upload the package to PyPI 145 | - name: Build the wheel and sdist 146 | env: 147 | SETUPTOOLS_SCM_PRETEND_VERSION: ${{ inputs.release-version }} 148 | run: python -m build --no-isolation --wheel --sdist 149 | 150 | - name: Upload to PyPI 151 | uses: pypa/gh-action-pypi-publish@release/v1 152 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: 8 | - "*" 9 | - "!gh-pages" 10 | 11 | concurrency: 12 | # Cancel previous builds if a new one is triggered. 13 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | tests: 18 | name: ${{ matrix.os }}, py${{ matrix.python-version }} 19 | runs-on: ${{ matrix.os }} 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | os: 25 | - ubuntu-latest 26 | # - windows-latest # disable because it's too slow 27 | - macos-14 28 | python-version: ["310", "311", "312", "313"] 29 | 30 | env: 31 | PIXI_ENV_NAME: test-py${{ matrix.python-version }} 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - uses: prefix-dev/setup-pixi@v0.8.1 37 | with: 38 | environments: ${{ env.PIXI_ENV_NAME }} 39 | activate-environment: true 40 | 41 | - name: Build and install package 42 | run: pixi install --environment ${{ env.PIXI_ENV_NAME }} 43 | 44 | - name: Run tests 45 | run: pixi run --environment ${{ env.PIXI_ENV_NAME }} test 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | #.idea/ 169 | 170 | # PyPI configuration file 171 | .pypirc 172 | 173 | # pixi environments 174 | .pixi 175 | *.egg-info 176 | *.conda 177 | output 178 | 179 | # vscode 180 | .vscode/ 181 | 182 | .ruff_cache/ 183 | .DS_Store 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2025 Hadrien Mary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pytrackmate 2 | 3 | [pypi-badge]: https://img.shields.io/pypi/v/pytrackmate 4 | [release-badge]: https://img.shields.io/github/v/release/hadim/pytrackmate?logo=github 5 | [test-badge]: https://github.com/hadim/pytrackmate/actions/workflows/test.yaml/badge.svg?branch=main 6 | [lint-badge]: https://github.com/hadim/pytrackmate/actions/workflows/lint.yaml/badge.svg?branch=main 7 | 8 | [![PyPI - Version][pypi-badge]](https://pypi.org/project/pytrackmate/) 9 | [![GitHub Release][release-badge]](https://github.com/hadim/pytrackmate/releases) 10 | [![Test CI][test-badge]](https://github.com/hadim/pytrackmate/actions/workflows/test.yaml) 11 | [![Lint CI][lint-badge]](https://github.com/hadim/pytrackmate/actions/workflows/lint.yaml) 12 | 13 | Import [Trackmate](https://imagej.net/TrackMate) XML files in Python as Pandas dataframe. 14 | 15 | ## Installation 📦 16 | 17 | ```bash 18 | # Pip 19 | pip install pytrackmate 20 | 21 | # Conda (mamba and micromamba) 22 | conda install -c conda-forge pytrackmate 23 | 24 | # Pixi 25 | pixi add pytrackmate 26 | ``` 27 | 28 | ## Usage 🚀 29 | 30 | Check the [notebook](notebooks/Trackmate.ipynb). 31 | 32 | ```python 33 | fname = "FakeTracks.xml" 34 | spots = trackmate_peak_import(fname) 35 | ``` 36 | 37 | `spots` is a dataframe with the following columns: 38 | 39 | - `t_stamp` 40 | - `t` 41 | - `x` 42 | - `y` 43 | - `z` 44 | - `w` 45 | - `q` 46 | - `spot_id` 47 | - `mean_intensity` 48 | - `median_intensity` 49 | - `min_intensity` 50 | - `max_intensity` 51 | - `total_intensity` 52 | - `std_intensity` 53 | - `contrast` 54 | - `snr` 55 | - `label` 56 | 57 | ## Development 🛠️ 58 | 59 | You need to use [pixi](https://pixi.sh). 60 | 61 | ```bash 62 | # Run tests 63 | pixi run -e dev test 64 | 65 | # Lint (ruff) 66 | pixi run -e dev lint 67 | 68 | # Format code (ruff) 69 | pixi run -e dev format 70 | ``` 71 | 72 | ## Release 🚢 73 | 74 | The package is released on [PyPi](https://pypi.org/project/pytrackmate/) and on conda-forge at . 75 | 76 | To cut a new release: 77 | 78 | - Trigger [the `release` workflow on the main branch](https://github.com/hadim/pytrackmate/actions/workflows/release.yaml). 79 | - A new GitHub Release will be created with the new version. 80 | - The new version will be uploaded on PyPi. 81 | - The conda-forge bot will create a PR to update the [feedstock](https://github.com/conda-forge/pytrackmate-feedstock). 82 | - Once the conda-forge PR merged, the new conda version will be available. 83 | -------------------------------------------------------------------------------- /notebooks/FakeTracks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | TrackMate v3.7.0 started on: 4 | Thu, 26 Apr 2018 12:27:20 5 | Please note that TrackMate is available through Fiji, and is based on a publication. If you use it successfully for your research please be so kind to cite our work: 6 | Tinevez, JY.; Perry, N. & Schindelin, J. et al. (2017), 'TrackMate: An open and extensible platform for single-particle tracking.', Methods 115: 80-90, PMID 27713081. 7 | https://www.ncbi.nlm.nih.gov/pubmed/27713081 8 | https://scholar.google.com/scholar?cluster=9846627681021220605 9 | Image data: 10 | For the image named: FakeTracks-1.tif. 11 | Matching file FakeTracks.tif in current folder. 12 | Geometry: 13 | X = 0 - 42, dx = 1.00000 14 | Y = 0 - 32, dy = 1.00000 15 | Z = 0 - 0, dz = 1.00000 16 | T = 0 - 5, dt = 1.00000 17 | Spot feature analyzers: 18 | - Manual spot color analyzer provides: Spot color; is manual. 19 | - Spot descriptive statistics provides: Mean, Median, Min, Max, Total int., Stdev. 20 | - Spot radius estimator provides: Diam. 21 | - Spot contrast and SNR provides: Constrast, SNR. 22 | Edge feature analyzers: 23 | - Edge target provides: Source ID, Target ID, Cost. 24 | - Edge mean location provides: T, X, Y, Z. 25 | - Edge velocity provides: V, D. 26 | - Manual edge color analyzer provides: Edge color; is manual. 27 | Track feature analyzers: 28 | - Branching analyzer provides: N spots, Gaps, Longest gap, Splits, Merges, Complex. 29 | - Track duration provides: Duration, T start, T stop, Displacement. 30 | - Track index provides: Index, ID. 31 | - Track location provides: X, Y, Z. 32 | - Velocity provides: Mean V, Max V, Min V, Median V, V std. 33 | - TRACK_SPOT_QUALITY provides: Mean Q, Max Q, Min Q, Median Q, Q std. 34 | Computing spot features. 35 | Computation done in 25 ms. 36 | Computing edge features: 37 | - Edge target in 0 ms. 38 | - Edge mean location in 0 ms. 39 | - Edge velocity in 0 ms. 40 | Computation done in 1 ms. 41 | Computing track features: 42 | - Branching analyzer in 0 ms. 43 | - Track duration in 0 ms. 44 | - Track index in 0 ms. 45 | - Track location in 0 ms. 46 | - Velocity in 0 ms. 47 | - TRACK_SPOT_QUALITY in 0 ms. 48 | Computation done in 0 ms. 49 | Starting detection using LoG detector 50 | with settings: 51 | - target channel: 1 52 | - threshold: 0.0 53 | - do median filtering: false 54 | - radius: 5.0 55 | - do subpixel localization: true 56 | Starting detection process using 8 threads. 57 | Detection processes 6 frames simultaneously and allocates 1 thread per frame. 58 | Found 68 spots. 59 | Detection done in 0.1 s. 60 | Computing spot quality histogram... 61 | Histogram calculated in 0.1 s. 62 | Initial thresholding with a quality threshold above 0.0 ... 63 | Starting initial filtering process. 64 | Retained 68 spots out of 68. 65 | Calculating spot features... 66 | Computing spot features. 67 | Computation done in 74 ms. 68 | Calculating features done in 0.1 s. 69 | Performing spot filtering on the following features: 70 | No feature threshold set, kept the 68 spots. 71 | Starting tracking using Simple LAP tracker 72 | with settings: 73 | Linking conditions: 74 | - max distance: 15.0 75 | - no feature penalties 76 | Gap-closing conditions: 77 | - max distance: 15.0 78 | - max frame gap: 2 79 | - no feature penalties 80 | Track splitting not allowed. 81 | Track merging not allowed. 82 | Starting tracking process. 83 | Found 16 tracks. 84 | Tracking done in 0.0 s. 85 | Performing spot filtering on the following features: 86 | - on Quality above 4.3 87 | Kept 12 spots out of 68. 88 | Starting tracking using Simple LAP tracker 89 | with settings: 90 | Linking conditions: 91 | - max distance: 15.0 92 | - no feature penalties 93 | Gap-closing conditions: 94 | - max distance: 15.0 95 | - max frame gap: 2 96 | - no feature penalties 97 | Track splitting not allowed. 98 | Track merging not allowed. 99 | Starting tracking process. 100 | Found 2 tracks. 101 | Tracking done in 0.0 s. 102 | Computing track features: 103 | - Branching analyzer in 3 ms. 104 | - Track duration in 2 ms. 105 | - Track index in 0 ms. 106 | - Track location in 2 ms. 107 | - Velocity in 2 ms. 108 | - TRACK_SPOT_QUALITY in 2 ms. 109 | Computation done in 16 ms. 110 | Thu, 26 Apr 2018 12:27:47 111 | Saving data... 112 | Warning: The source image does not match a file on the system.TrackMate won't be able to reload it when opening this XML file. 113 | To fix this, save the source image to a TIF file before saving the TrackMate session. 114 | Computing edge features: 115 | - Edge target in 7 ms. 116 | - Edge mean location in 6 ms. 117 | - Edge velocity in 4 ms. 118 | Computation done in 20 ms. 119 | Computing track features: 120 | - Branching analyzer in 4 ms. 121 | - Track duration in 12 ms. 122 | - Track index in 0 ms. 123 | - Track location in 19 ms. 124 | - Velocity in 13 ms. 125 | - TRACK_SPOT_QUALITY in 6 ms. 126 | Computation done in 55 ms. 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /notebooks/demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "
\n", 12 | "\n", 25 | "\n", 26 | " \n", 27 | " \n", 28 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 33 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 38 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 43 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | "
t_stamptxyzwqspot_idmean_intensitymedian_intensitymin_intensitymax_intensitytotal_intensitystd_intensitycontrastsnrlabel
00.00.028.8129135.6668300.02.07.66928135.050.22680428.00.0248.04872.058.9290720.2350600.3244340
70.00.019.2672825.7836390.02.08.05587110.051.22680430.02.0239.04969.059.2738180.2639990.3610101
161.01.016.3926578.4307580.02.09.14838045.049.35051526.00.0252.04787.060.8752210.4444370.4988762
171.01.029.5978189.5717520.02.08.73733146.049.43299030.00.0236.04795.057.5892260.4380470.5229423
222.02.015.5099829.5612140.02.09.91155756.053.64948532.00.0244.05204.062.4980070.4718000.5503504
\n", 151 | "
" 152 | ], 153 | "text/plain": [ 154 | " t_stamp t x y z w q spot_id \\\n", 155 | "0 0.0 0.0 28.812913 5.666830 0.0 2.0 7.669281 35.0 \n", 156 | "7 0.0 0.0 19.267282 5.783639 0.0 2.0 8.055871 10.0 \n", 157 | "16 1.0 1.0 16.392657 8.430758 0.0 2.0 9.148380 45.0 \n", 158 | "17 1.0 1.0 29.597818 9.571752 0.0 2.0 8.737331 46.0 \n", 159 | "22 2.0 2.0 15.509982 9.561214 0.0 2.0 9.911557 56.0 \n", 160 | "\n", 161 | " mean_intensity median_intensity min_intensity max_intensity \\\n", 162 | "0 50.226804 28.0 0.0 248.0 \n", 163 | "7 51.226804 30.0 2.0 239.0 \n", 164 | "16 49.350515 26.0 0.0 252.0 \n", 165 | "17 49.432990 30.0 0.0 236.0 \n", 166 | "22 53.649485 32.0 0.0 244.0 \n", 167 | "\n", 168 | " total_intensity std_intensity contrast snr label \n", 169 | "0 4872.0 58.929072 0.235060 0.324434 0 \n", 170 | "7 4969.0 59.273818 0.263999 0.361010 1 \n", 171 | "16 4787.0 60.875221 0.444437 0.498876 2 \n", 172 | "17 4795.0 57.589226 0.438047 0.522942 3 \n", 173 | "22 5204.0 62.498007 0.471800 0.550350 4 " 174 | ] 175 | }, 176 | "execution_count": 2, 177 | "metadata": {}, 178 | "output_type": "execute_result" 179 | } 180 | ], 181 | "source": [ 182 | "import matplotlib.pyplot as plt\n", 183 | "\n", 184 | "from pytrackmate import trackmate_peak_import\n", 185 | "\n", 186 | "fname = \"./FakeTracks.xml\"\n", 187 | "spots = trackmate_peak_import(fname)\n", 188 | "spots.head()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 7, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "" 200 | ] 201 | }, 202 | "execution_count": 7, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | }, 206 | { 207 | "data": { 208 | "image/png": "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7IixGdJFAzJ0+9h8jTqzR8TqBNB5hTx8rF27VlOmTNHll18uSerdu7cWL16skpKSUH8VgJMY3y9D4/tlqPxgnUrLD6uuoUmnORJ1Xl43ulkARFTIw8e4ceP0/PPPa9u2bTr77LP10Ucf6b333tPTTz/d6vZer1derzfw3uPxhLokoFNzpafIlZ4S6TIAICDk4ePBBx+U2+1W//79Zbfb5fP59Nhjj2natGmtbl9UVKRHH3001GUAAIAoFfIbTv/2t79p4cKFWrRokTZu3KgFCxbo17/+tRYsWNDq9rNnz5bb7Q68ysvLQ10SAACIIoZpmsEPCnASLpdLDz30kGbMmBFY9qtf/UoLFy7Up59+esr9PR6PnE6n3G630tLSQlkaAAAIk2Cu3yFv+airq5PN1vxj7XY7j9oCAABJYbjnY/LkyXrssceUm5urc845Rx9++KGeeuop3X777aH+KgAAEINC3u1SXV2tRx55REuWLFFVVZWys7M1bdo0/exnP1NSUtIp96fbBQCA2BPM9Tvk4aOjCB8AAMSeiN7zAQAAcDKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgKcIHAACwFOEDAABYivABAAAsRfgAAACWInwAAABLET4AAIClCB8AAMBShA8AAGApwgcAALAU4QMAAFiK8AEAACxF+AAAAJYifAAAAEsRPgAAgKUIHwAAwFKEDwAAYCnCBwAAsBThAwAAWIrwAQAALEX4AAAAliJ8AAAASxE+AACApQgfAADAUoQPAABgqbCEjz179uimm25Sjx49lJKSonPPPVcbNmwIx1cBAIAYkxDqDzx06JDGjh2rCRMm6M0331RGRoY+++wzdevWLdRfBaCT8zb59NbmSr21uVJf1njlsNt05umn6YbhLg06wxnp8gCcgGGaphnKD3zooYf0/vvv6913323X/h6PR06nU263W2lpaaEsDUCcME1Tf3y3TPNW7JD7SKNshuT/6pfMbjPk85sakuPUY1MHa3AOIQSwQjDX75B3uyxbtkzDhg3Tddddp4yMDA0dOlR/+MMfTri91+uVx+Np9gKAEzFNUw+/skmPvfGJ3EcaJX0dPCTJ99WbzXvcuvb5NVqzY38kygTCwlPfqJ37a1V+sE71jb5Il9NuIW/56NKliyTpvvvu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", 209 | "text/plain": [ 210 | "
" 211 | ] 212 | }, 213 | "metadata": {}, 214 | "output_type": "display_data" 215 | } 216 | ], 217 | "source": [ 218 | "plt.scatter(spots[\"x\"], spots[\"y\"], s=spots[\"w\"] * 50)" 219 | ] 220 | } 221 | ], 222 | "metadata": { 223 | "kernelspec": { 224 | "display_name": "Python 3", 225 | "language": "python", 226 | "name": "python3" 227 | }, 228 | "language_info": { 229 | "codemirror_mode": { 230 | "name": "ipython", 231 | "version": 3 232 | }, 233 | "file_extension": ".py", 234 | "mimetype": "text/x-python", 235 | "name": "python", 236 | "nbconvert_exporter": "python", 237 | "pygments_lexer": "ipython3", 238 | "version": "3.10.16" 239 | } 240 | }, 241 | "nbformat": 4, 242 | "nbformat_minor": 2 243 | } 244 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "pytrackmate" 3 | description = "Import Trackmate XML files in Python as Pandas dataframe." 4 | authors = [{ name = "Hadrien Mary", email = "hadrien.mary@gmail.com" }] 5 | license = { file = "LICENSE" } 6 | classifiers = [ 7 | "License :: OSI Approved :: MIT License", 8 | "Programming Language :: Python :: 3", 9 | "Programming Language :: Python :: 3 :: Only", 10 | "Programming Language :: Python :: 3.10", 11 | "Programming Language :: Python :: 3.11", 12 | "Programming Language :: Python :: 3.12", 13 | "Programming Language :: Python :: 3.13", 14 | "Programming Language :: Python :: Implementation :: CPython", 15 | "Programming Language :: Python :: Implementation :: PyPy", 16 | ] 17 | requires-python = ">=3.10" 18 | dependencies = ["numpy", "pandas"] 19 | dynamic = ["version", "readme"] 20 | 21 | [project.urls] 22 | homepage = "https://github.com/hadim/pytrackmate" 23 | 24 | # Build system - setuptools 25 | 26 | [build-system] 27 | requires = ["setuptools>=75", "setuptools-scm>=8"] 28 | build-backend = "setuptools.build_meta" 29 | 30 | [tool.setuptools.packages.find] 31 | where = ["./src/"] 32 | include = ["pytrackmate*"] 33 | 34 | [tool.setuptools.dynamic] 35 | readme = { file = ["README.md"], content-type = "text/markdown" } 36 | 37 | [tool.setuptools_scm] 38 | 39 | # Pixi configuration 40 | 41 | [tool.pixi.project] 42 | channels = ["conda-forge"] 43 | platforms = ["linux-64", "osx-64", "osx-arm64", "win-64", "linux-aarch64"] 44 | 45 | # Pixi dependencies and features 46 | 47 | [tool.pixi.dependencies] 48 | python = ">=3.10" 49 | numpy = "*" 50 | pandas = "*" 51 | 52 | [tool.pixi.pypi-dependencies] 53 | pytrackmate = { path = ".", editable = true } 54 | 55 | [tool.pixi.feature.dev.dependencies] 56 | pip = "*" 57 | setuptools = ">=75" 58 | setuptools-scm = ">=8" 59 | ruff = "*" 60 | jupyterlab = "*" 61 | matplotlib = "*" 62 | semver = "*" 63 | python-build = "*" 64 | pytest = "*" 65 | 66 | [tool.pixi.feature.dev.tasks] 67 | test = 'python -m pytest tests/ -vvv' 68 | lint = "ruff check -v" 69 | format = "ruff format -v" 70 | 71 | [tool.pixi.feature.py310.dependencies] 72 | python = "3.10.*" 73 | 74 | [tool.pixi.feature.py311.dependencies] 75 | python = "3.11.*" 76 | 77 | [tool.pixi.feature.py312.dependencies] 78 | python = "3.12.*" 79 | 80 | [tool.pixi.feature.py313.dependencies] 81 | python = "3.13.*" 82 | 83 | [tool.pixi.environments] 84 | dev = ["dev", "py310"] 85 | test-py310 = ["dev", "py310"] 86 | test-py311 = ["dev", "py311"] 87 | test-py312 = ["dev", "py312"] 88 | test-py313 = ["dev", "py313"] 89 | -------------------------------------------------------------------------------- /src/pytrackmate/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | 3 | from ._trackmate import trackmate_peak_import 4 | 5 | __version__ = importlib.metadata.version("pytrackmate") 6 | 7 | __all__ = ["trackmate_peak_import", "__version__"] 8 | -------------------------------------------------------------------------------- /src/pytrackmate/_trackmate.py: -------------------------------------------------------------------------------- 1 | import xml.etree.cElementTree as et 2 | import os 3 | 4 | import numpy as np 5 | import pandas as pd 6 | 7 | 8 | def _safe_find(element: et.Element, tag: str) -> et.Element: 9 | """Safely find a child element, raising ValueError if not found.""" 10 | found = element.find(tag) 11 | if found is None: 12 | raise ValueError(f"No '{tag}' tag found in the XML file.") 13 | return found 14 | 15 | 16 | def _safe_get(element: et.Element, attribute: str) -> str: 17 | """Safely get an attribute from an element, raising ValueError if not found.""" 18 | value = element.get(attribute) 19 | if value is None: 20 | raise ValueError(f"No attribute '{attribute}' found in the XML element.") 21 | return value 22 | 23 | 24 | def trackmate_peak_import( 25 | trackmate_xml_path: str | os.PathLike, 26 | get_tracks: bool = False, 27 | ) -> pd.DataFrame: 28 | """Import detected peaks with TrackMate Fiji plugin.""" 29 | 30 | with open(trackmate_xml_path, "r") as f: 31 | xml_string = f.read() 32 | root = et.fromstring(xml_string) 33 | 34 | object_labels = { 35 | "FRAME": "t_stamp", 36 | "POSITION_T": "t", 37 | "POSITION_X": "x", 38 | "POSITION_Y": "y", 39 | "POSITION_Z": "z", 40 | "ESTIMATED_DIAMETER": "w", 41 | "QUALITY": "q", 42 | "ID": "spot_id", 43 | "MEAN_INTENSITY": "mean_intensity", 44 | "MEDIAN_INTENSITY": "median_intensity", 45 | "MIN_INTENSITY": "min_intensity", 46 | "MAX_INTENSITY": "max_intensity", 47 | "TOTAL_INTENSITY": "total_intensity", 48 | "STANDARD_DEVIATION": "std_intensity", 49 | "CONTRAST": "contrast", 50 | "SNR": "snr", 51 | } 52 | 53 | model = _safe_find(root, "Model") 54 | feature_declarations = _safe_find(model, "FeatureDeclarations") 55 | spot_features = _safe_find(feature_declarations, "SpotFeatures") 56 | 57 | features = [c.get("feature") for c in list(spot_features)] + ["ID"] 58 | 59 | all_spots = _safe_find(model, "AllSpots") 60 | 61 | objects = [] 62 | for frame in all_spots.findall("SpotsInFrame"): 63 | for spot in frame.findall("Spot"): 64 | single_object = [] 65 | for label in features: 66 | if label is None: 67 | raise ValueError( 68 | f"Attribute '{label}' is missing in the XML spot element." 69 | ) 70 | 71 | value = spot.get(label) 72 | if value is None: 73 | raise ValueError( 74 | f"Attribute '{label}' is missing in the XML spot element." 75 | ) 76 | 77 | single_object.append(value) 78 | objects.append(single_object) 79 | 80 | trajs = pd.DataFrame(objects, columns=features) 81 | trajs = trajs.astype(float) 82 | 83 | # Apply initial filtering 84 | settings = _safe_find(root, "Settings") 85 | initial_filter = _safe_find(settings, "InitialSpotFilter") 86 | 87 | trajs = filter_spots( 88 | trajs, 89 | name=_safe_get(initial_filter, "feature"), 90 | value=float(_safe_get(initial_filter, "value")), 91 | isabove=_safe_get(initial_filter, "isabove") == "true", 92 | ) 93 | 94 | # Apply filters 95 | spot_filters = _safe_find(settings, "SpotFilterCollection") 96 | 97 | for spot_filter in spot_filters.findall("Filter"): 98 | trajs = filter_spots( 99 | trajs, 100 | name=_safe_get(spot_filter, "feature"), 101 | value=float(_safe_get(spot_filter, "value")), 102 | isabove=_safe_get(spot_filter, "isabove") == "true", 103 | ) 104 | 105 | trajs = trajs.reindex(columns=list(object_labels.keys())) 106 | trajs.columns = [object_labels[k] for k in object_labels.keys()] 107 | trajs["label"] = np.arange(trajs.shape[0]) 108 | 109 | # Get tracks 110 | if get_tracks: 111 | filtered_track_ids = [ 112 | int(_safe_get(track, "TRACK_ID")) 113 | for track in _safe_find(model, "FilteredTracks").findall("TrackID") 114 | ] 115 | 116 | label_id = 0 117 | trajs["label"] = np.nan 118 | 119 | all_tracks = _safe_find(model, "AllTracks") 120 | 121 | for track in all_tracks.findall("Track"): 122 | track_id = int(_safe_get(track, "TRACK_ID")) 123 | if track_id in filtered_track_ids: 124 | spot_ids = [ 125 | ( 126 | edge.get("SPOT_SOURCE_ID"), 127 | edge.get("SPOT_TARGET_ID"), 128 | edge.get("EDGE_TIME"), 129 | ) 130 | for edge in track.findall("Edge") 131 | ] 132 | spot_ids = np.array(spot_ids).astype("float")[:, :2] 133 | spot_ids = set(spot_ids.flatten()) 134 | 135 | trajs.loc[trajs["spot_id"].isin(spot_ids), "label"] = label_id 136 | label_id += 1 137 | 138 | # Label remaining columns 139 | single_track = trajs.loc[trajs["label"].isnull()] 140 | trajs.loc[trajs["label"].isnull(), "label"] = label_id + np.arange( 141 | 0, len(single_track) 142 | ) 143 | 144 | return trajs 145 | 146 | 147 | def filter_spots( 148 | spots: pd.DataFrame, name: str, value: float, isabove: bool 149 | ) -> pd.DataFrame: 150 | """Filter spots based on a given feature.""" 151 | if name not in spots.columns: 152 | raise ValueError(f"Feature '{name}' does not exist in the spots DataFrame.") 153 | 154 | if isabove: 155 | spots = spots[spots[name] > value] 156 | else: 157 | spots = spots[spots[name] < value] 158 | 159 | return spots 160 | -------------------------------------------------------------------------------- /tests/FakeTracks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | TrackMate v3.7.0 started on: 4 | Thu, 26 Apr 2018 12:27:20 5 | Please note that TrackMate is available through Fiji, and is based on a publication. If you use it successfully for your research please be so kind to cite our work: 6 | Tinevez, JY.; Perry, N. & Schindelin, J. et al. (2017), 'TrackMate: An open and extensible platform for single-particle tracking.', Methods 115: 80-90, PMID 27713081. 7 | https://www.ncbi.nlm.nih.gov/pubmed/27713081 8 | https://scholar.google.com/scholar?cluster=9846627681021220605 9 | Image data: 10 | For the image named: FakeTracks-1.tif. 11 | Matching file FakeTracks.tif in current folder. 12 | Geometry: 13 | X = 0 - 42, dx = 1.00000 14 | Y = 0 - 32, dy = 1.00000 15 | Z = 0 - 0, dz = 1.00000 16 | T = 0 - 5, dt = 1.00000 17 | Spot feature analyzers: 18 | - Manual spot color analyzer provides: Spot color; is manual. 19 | - Spot descriptive statistics provides: Mean, Median, Min, Max, Total int., Stdev. 20 | - Spot radius estimator provides: Diam. 21 | - Spot contrast and SNR provides: Constrast, SNR. 22 | Edge feature analyzers: 23 | - Edge target provides: Source ID, Target ID, Cost. 24 | - Edge mean location provides: T, X, Y, Z. 25 | - Edge velocity provides: V, D. 26 | - Manual edge color analyzer provides: Edge color; is manual. 27 | Track feature analyzers: 28 | - Branching analyzer provides: N spots, Gaps, Longest gap, Splits, Merges, Complex. 29 | - Track duration provides: Duration, T start, T stop, Displacement. 30 | - Track index provides: Index, ID. 31 | - Track location provides: X, Y, Z. 32 | - Velocity provides: Mean V, Max V, Min V, Median V, V std. 33 | - TRACK_SPOT_QUALITY provides: Mean Q, Max Q, Min Q, Median Q, Q std. 34 | Computing spot features. 35 | Computation done in 25 ms. 36 | Computing edge features: 37 | - Edge target in 0 ms. 38 | - Edge mean location in 0 ms. 39 | - Edge velocity in 0 ms. 40 | Computation done in 1 ms. 41 | Computing track features: 42 | - Branching analyzer in 0 ms. 43 | - Track duration in 0 ms. 44 | - Track index in 0 ms. 45 | - Track location in 0 ms. 46 | - Velocity in 0 ms. 47 | - TRACK_SPOT_QUALITY in 0 ms. 48 | Computation done in 0 ms. 49 | Starting detection using LoG detector 50 | with settings: 51 | - target channel: 1 52 | - threshold: 0.0 53 | - do median filtering: false 54 | - radius: 5.0 55 | - do subpixel localization: true 56 | Starting detection process using 8 threads. 57 | Detection processes 6 frames simultaneously and allocates 1 thread per frame. 58 | Found 68 spots. 59 | Detection done in 0.1 s. 60 | Computing spot quality histogram... 61 | Histogram calculated in 0.1 s. 62 | Initial thresholding with a quality threshold above 0.0 ... 63 | Starting initial filtering process. 64 | Retained 68 spots out of 68. 65 | Calculating spot features... 66 | Computing spot features. 67 | Computation done in 74 ms. 68 | Calculating features done in 0.1 s. 69 | Performing spot filtering on the following features: 70 | No feature threshold set, kept the 68 spots. 71 | Starting tracking using Simple LAP tracker 72 | with settings: 73 | Linking conditions: 74 | - max distance: 15.0 75 | - no feature penalties 76 | Gap-closing conditions: 77 | - max distance: 15.0 78 | - max frame gap: 2 79 | - no feature penalties 80 | Track splitting not allowed. 81 | Track merging not allowed. 82 | Starting tracking process. 83 | Found 16 tracks. 84 | Tracking done in 0.0 s. 85 | Performing spot filtering on the following features: 86 | - on Quality above 4.3 87 | Kept 12 spots out of 68. 88 | Starting tracking using Simple LAP tracker 89 | with settings: 90 | Linking conditions: 91 | - max distance: 15.0 92 | - no feature penalties 93 | Gap-closing conditions: 94 | - max distance: 15.0 95 | - max frame gap: 2 96 | - no feature penalties 97 | Track splitting not allowed. 98 | Track merging not allowed. 99 | Starting tracking process. 100 | Found 2 tracks. 101 | Tracking done in 0.0 s. 102 | Computing track features: 103 | - Branching analyzer in 3 ms. 104 | - Track duration in 2 ms. 105 | - Track index in 0 ms. 106 | - Track location in 2 ms. 107 | - Velocity in 2 ms. 108 | - TRACK_SPOT_QUALITY in 2 ms. 109 | Computation done in 16 ms. 110 | Thu, 26 Apr 2018 12:27:47 111 | Saving data... 112 | Warning: The source image does not match a file on the system.TrackMate won't be able to reload it when opening this XML file. 113 | To fix this, save the source image to a TIF file before saving the TrackMate session. 114 | Computing edge features: 115 | - Edge target in 7 ms. 116 | - Edge mean location in 6 ms. 117 | - Edge velocity in 4 ms. 118 | Computation done in 20 ms. 119 | Computing track features: 120 | - Branching analyzer in 4 ms. 121 | - Track duration in 12 ms. 122 | - Track index in 0 ms. 123 | - Track location in 19 ms. 124 | - Velocity in 13 ms. 125 | - TRACK_SPOT_QUALITY in 6 ms. 126 | Computation done in 55 ms. 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /tests/test_trackmate.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | from pytrackmate import trackmate_peak_import 4 | 5 | 6 | def test_import(): 7 | fname = pathlib.Path(__file__).parent / "FakeTracks.xml" 8 | spots = trackmate_peak_import(fname) 9 | assert spots.shape == (12, 17) 10 | 11 | assert spots.columns.to_list() == [ 12 | "t_stamp", 13 | "t", 14 | "x", 15 | "y", 16 | "z", 17 | "w", 18 | "q", 19 | "spot_id", 20 | "mean_intensity", 21 | "median_intensity", 22 | "min_intensity", 23 | "max_intensity", 24 | "total_intensity", 25 | "std_intensity", 26 | "contrast", 27 | "snr", 28 | "label", 29 | ] 30 | 31 | assert spots.iloc[0].to_list() == [ 32 | 0.0, 33 | 0.0, 34 | 28.81291282851087, 35 | 5.666830103137038, 36 | 0.0, 37 | 2.0000000141168894, 38 | 7.669281005859375, 39 | 35.0, 40 | 50.22680412371134, 41 | 28.0, 42 | 0.0, 43 | 248.0, 44 | 4872.0, 45 | 58.9290719768688, 46 | 0.2350598400953469, 47 | 0.32443401070344374, 48 | 0.0, 49 | ] 50 | --------------------------------------------------------------------------------