From 0769f9ba16599762793f6d0e6b15b854fd58a910 Mon Sep 17 00:00:00 2001 From: bwmr <56264458+bwmr@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:31:33 +0200 Subject: [PATCH] added command-line interface --- .github/workflows/ruff.yml | 10 +++ pyproject.toml | 1 + sizepicker/interface.py | 141 +++++++++++++++++++++++++++++++++++++ sizepicker/picker.py | 19 ++--- 4 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ruff.yml create mode 100644 sizepicker/interface.py diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..9117573 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,10 @@ +name: Ruff +on: [push, pull_request] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: chartboost/ruff-action@v1 + with: + args: --fix \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f290dd1..f2bb353 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [build-system] requires = ["setuptools"] +build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] include = ["sizepicker"] diff --git a/sizepicker/interface.py b/sizepicker/interface.py new file mode 100644 index 0000000..afc7749 --- /dev/null +++ b/sizepicker/interface.py @@ -0,0 +1,141 @@ +from pathlib import Path + +import click + +from .picker import Picker + + +@click.command() +@click.option( + "--radius", + default=80, + show_default=True, + help="Particle radius [A].") +@click.option( + "--angpix", + default=None, + type=float, + show_default=True, + help="Override pixelsize in header [A/pix].") +@click.option( + "--contamination_binning", + default=16, + show_default=True, + help="Binning used to filter out contaminations.") +@click.option( + "--gaussian_cont", + is_flag=True, + default=False, + show_default=True, + help="Smooth background by subtracting gaussian-filtered tomogram.") +@click.option( + "--sigma_cont", + default=300, + show_default=True, + help="If smoothing background, apply gaussian filter with this sigma [A].") +@click.option( + "--stdtimes_cont", + default=2.5, + show_default=True, + help="Voxels this many SD below mean are considered contamination.") +@click.option( + "--minsize_cont", + default=50, + show_default=True, + help="Keep only contaminations of this size and above [binned px].") +@click.option( + "--dilate_cont", + default=200, + show_default=True, + help="Dilate contamination mask by this much [A].") +@click.option( + "--radius_times", + default=4, + show_default=True, + help="Picks can be this close together [particle radii].") +@click.option( + "--inhibit", + is_flag=True, + default=False, + show_default=True, + help="Use more elaborate algorithm to get more picks.") +@click.option( + "--detection_z", + default=200, + show_default=True, + help="Only consider picks in a central slab of the volume of this extent [px].") +@click.option( + "--stdtimes_pick", + default=1.5, + show_default=True, + help="Only consider picks with a central SD this many SD above mean.") +@click.option( + "--remove_edge", + is_flag=True, + default=False, + show_default=True, + help="Only keep particles with a higher foreground than background SD.") +@click.argument( + "tomograms", + nargs=-1, + type=click.Path(exists=True)) +@click.argument( + "output_dir", + nargs=1, + type=click.Path(writable=True, dir_okay=True)) +def cli(radius, + angpix, + contamination_binning, + gaussian_cont, + sigma_cont, + stdtimes_cont, + minsize_cont, + dilate_cont, + radius_times, + inhibit, + detection_z, + stdtimes_pick, + remove_edge, + tomograms, + output_dir): + """Pick particles based on diameter. + + Takes tomograms as input. Writes all outputs to output_dir. + + """ + for tomo in tomograms: + + tomo = Path(tomo) + + print("working on") + + p = Picker(tomo, + output_dir, + radius, + contamination_binning, + angpix) + + mask = p.getcont(gaussian_cont, + sigma_cont, + stdtimes_cont, + minsize_cont, + dilate_cont) + + print("Contamination Mask written to..") + + boxes, particles = p.detect(mask, + radius_times, + inhibit, + detection_z) + + print("Initial Picks") + + metrics, threshold = p.prefilt(particles, + stdtimes_pick) + + boxs_XYZ = p.filt(boxes, + metrics, + threshold, + remove_edge) + + print(f"Wrote {len(boxs_XYZ)} picks to ...") diff --git a/sizepicker/picker.py b/sizepicker/picker.py index 447e2c8..9207881 100644 --- a/sizepicker/picker.py +++ b/sizepicker/picker.py @@ -1,6 +1,5 @@ import subprocess from pathlib import Path -from typing import Optional import mrcfile import numpy as np @@ -61,20 +60,20 @@ def __init__(self, output: Path, radius: int = 100, contamination_binning: int = 1, - override_angpix: Optional(float) = None): + override_angpix: float = None): #noqa: RUF013 - self.name=tomogram.stem - self.output=output - self.radius=radius + self.name = tomogram.stem + self.output = output + self.radius = radius self.contamination_binning = contamination_binning self.rec = mrcfile.read(tomogram) if override_angpix is None: with mrcfile.mmap(tomogram, mode='r') as f: - self.pixelsize=float(f.voxel_size.x) + self.pixelsize = float(f.voxel_size.x) else: - self.pixelsize=override_angpix + self.pixelsize = override_angpix # box size for checking whether pick contains density self.tilesize = int(3 * radius / self.pixelsize) @@ -386,7 +385,11 @@ def filt(self,boxes,particles_metrics,stdthreshold,remove_edge): stdthreshold: actual SD threshold, from Picker.prefilt() remove_edge: remove particles with higher background than foreground SD? - Writes out coordinate file in XYZ order to output folder. + Returns + ------- + boxs_XYZ: XYZ coordiantes of retained particles. + + Also writes out coordinate file in XYZ order to output folder. """ boxs_ZYX = []