napari-myelin-quantifier 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ try:
2
+ from ._version import version as __version__
3
+ except ImportError:
4
+ __version__ = "unknown"
5
+
6
+ from ._widget import myelin_quantifier_widget
7
+
8
+ __all__ = ["myelin_quantifier_widget"]
@@ -0,0 +1,206 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ from scipy import ndimage as ndi
8
+ from skimage import morphology
9
+ from skimage.color import rgb2hsv
10
+ from skimage.measure import label, regionprops
11
+ from skimage.segmentation import clear_border
12
+
13
+
14
+ @dataclass
15
+ class QuantParams:
16
+ px_size_um: float = 0.02
17
+
18
+ # Myelin extraction / cleanup
19
+ v_thresh: float = 0.15
20
+ min_myelin_obj: int = 30
21
+ close_radius: int = 3
22
+ open_radius: int = 1
23
+
24
+ # Axon interior filtering
25
+ min_axon_area: int = 150
26
+ min_solidity: float = 0.60
27
+
28
+ # Edge handling
29
+ remove_border_objects: bool = True
30
+ edge_margin_px: int = 8
31
+
32
+ # Optional crop for screenshots with UI borders
33
+ auto_crop: bool = True
34
+ crop_frac_thresh: float = 0.15 # fraction threshold used for content crop
35
+
36
+
37
+ def _find_content_crop(
38
+ img_rgb: np.ndarray, v_thresh: float = 0.15, frac_thresh: float = 0.15
39
+ ):
40
+ """Auto-crop away UI/borders by finding dense colored region."""
41
+ hsv = rgb2hsv(img_rgb / 255.0)
42
+ v = hsv[..., 2]
43
+ mask = v > v_thresh
44
+
45
+ col_frac = mask.mean(axis=0)
46
+ row_frac = mask.mean(axis=1)
47
+
48
+ xs = np.where(col_frac > frac_thresh)[0]
49
+ ys = np.where(row_frac > frac_thresh)[0]
50
+
51
+ if len(xs) < 10 or len(ys) < 10:
52
+ return img_rgb, (0, 0)
53
+
54
+ x0, x1 = xs.min(), xs.max() + 1
55
+ y0, y1 = ys.min(), ys.max() + 1
56
+ return img_rgb[y0:y1, x0:x1], (y0, x0)
57
+
58
+
59
+ def _touches_margin(bbox, H: int, W: int, margin: int) -> bool:
60
+ minr, minc, maxr, maxc = bbox
61
+ return (
62
+ (minr <= margin)
63
+ or (minc <= margin)
64
+ or (maxr >= H - margin)
65
+ or (maxc >= W - margin)
66
+ )
67
+
68
+
69
+ def quantify_myelinated_axons(
70
+ img_rgb: np.ndarray,
71
+ params: QuantParams,
72
+ ) -> tuple[np.ndarray, np.ndarray, pd.DataFrame, tuple[int, int]]:
73
+ """
74
+ Returns:
75
+ labels_full: 2D int32 label image in full image coordinates (axon_id 1..N; 0=bg)
76
+ overlay_full: 3D uint8 RGB overlay in full image coordinates (boundary drawn white)
77
+ df: measurements table; axon_id matches labels/text
78
+ (y_off, x_off): crop offset applied
79
+ """
80
+ if img_rgb.ndim != 3 or img_rgb.shape[2] not in (3, 4):
81
+ raise ValueError(
82
+ f"Expected 2D RGB/RGBA image with shape (Y, X, 3/4). Got: {img_rgb.shape}"
83
+ )
84
+
85
+ if img_rgb.shape[2] == 4:
86
+ img_rgb = img_rgb[..., :3]
87
+
88
+ img_rgb = img_rgb.astype(np.uint8)
89
+
90
+ # Optional crop (useful for screenshots like your example)
91
+ if params.auto_crop:
92
+ img_c, (y_off, x_off) = _find_content_crop(
93
+ img_rgb,
94
+ v_thresh=params.v_thresh,
95
+ frac_thresh=params.crop_frac_thresh,
96
+ )
97
+ else:
98
+ img_c, (y_off, x_off) = img_rgb, (0, 0)
99
+
100
+ Hc, Wc, _ = img_c.shape
101
+
102
+ # -----------------------
103
+ # MYELIN MASK
104
+ # -----------------------
105
+ hsv = rgb2hsv(img_c / 255.0)
106
+ v = hsv[..., 2]
107
+ myelin = v > params.v_thresh
108
+
109
+ myelin = morphology.remove_small_objects(myelin, params.min_myelin_obj)
110
+ myelin = morphology.binary_closing(
111
+ myelin, morphology.disk(params.close_radius)
112
+ )
113
+ myelin = morphology.binary_opening(
114
+ myelin, morphology.disk(params.open_radius)
115
+ )
116
+
117
+ # -----------------------
118
+ # FILL HOLES (NO AREA CAP)
119
+ # -----------------------
120
+ filled = ndi.binary_fill_holes(myelin)
121
+ axon = filled & (~myelin)
122
+
123
+ axon = morphology.remove_small_objects(axon, params.min_axon_area)
124
+
125
+ if params.remove_border_objects:
126
+ axon = clear_border(axon)
127
+
128
+ # -----------------------
129
+ # LABEL + MEASURE
130
+ # -----------------------
131
+ lab_raw = label(axon)
132
+ props = regionprops(lab_raw)
133
+
134
+ rows = []
135
+ for p in props:
136
+ if params.edge_margin_px > 0 and _touches_margin(
137
+ p.bbox, axon.shape[0], axon.shape[1], params.edge_margin_px
138
+ ):
139
+ continue
140
+
141
+ area_px = float(p.area)
142
+ area_um2 = area_px * (params.px_size_um**2)
143
+ eq_diam_um = float(p.equivalent_diameter) * params.px_size_um
144
+
145
+ rows.append(
146
+ {
147
+ "raw_label": int(p.label),
148
+ "area_px": area_px,
149
+ "area_um2": area_um2,
150
+ "eq_diam_um": eq_diam_um,
151
+ "eccentricity": float(p.eccentricity),
152
+ "solidity": float(p.solidity),
153
+ "cy_crop": float(p.centroid[0]),
154
+ "cx_crop": float(p.centroid[1]),
155
+ "cy_img": float(p.centroid[0] + y_off),
156
+ "cx_img": float(p.centroid[1] + x_off),
157
+ }
158
+ )
159
+
160
+ df = pd.DataFrame(rows)
161
+
162
+ # Filter by shape (your “verified axons/myelin” gate)
163
+ if len(df) > 0:
164
+ df_f = df[
165
+ (df["area_px"] >= params.min_axon_area)
166
+ & (df["solidity"] >= params.min_solidity)
167
+ ].copy()
168
+ df_f = df_f.sort_values(["cy_img", "cx_img"]).reset_index(drop=True)
169
+ df_f.insert(0, "axon_id", np.arange(1, len(df_f) + 1))
170
+ else:
171
+ df_f = df.copy()
172
+ df_f.insert(0, "axon_id", [])
173
+
174
+ # Relabel into axon_id space (1..N) inside crop coords
175
+ labels_crop = np.zeros((Hc, Wc), dtype=np.int32)
176
+ if len(df_f) > 0:
177
+ raw_to_new = {
178
+ int(r.raw_label): int(r.axon_id)
179
+ for r in df_f.itertuples(index=False)
180
+ }
181
+ # Map raw labels -> new ids
182
+ # Fast remap via LUT
183
+ max_raw = int(lab_raw.max())
184
+ lut = np.zeros(max_raw + 1, dtype=np.int32)
185
+ for k, v in raw_to_new.items():
186
+ if 0 <= k <= max_raw:
187
+ lut[k] = v
188
+ labels_crop = lut[lab_raw].astype(np.int32)
189
+
190
+ # Boundary overlay (crop coords)
191
+ axon_crop_mask = labels_crop > 0
192
+ boundary = morphology.dilation(
193
+ axon_crop_mask, morphology.disk(1)
194
+ ) ^ morphology.erosion(axon_crop_mask, morphology.disk(1))
195
+ overlay_crop = img_c.copy()
196
+ overlay_crop[boundary] = [255, 255, 255]
197
+
198
+ # Paste back into full-size arrays
199
+ H, W, _ = img_rgb.shape
200
+ labels_full = np.zeros((H, W), dtype=np.int32)
201
+ overlay_full = img_rgb.copy()
202
+
203
+ labels_full[y_off : y_off + Hc, x_off : x_off + Wc] = labels_crop
204
+ overlay_full[y_off : y_off + Hc, x_off : x_off + Wc] = overlay_crop
205
+
206
+ return labels_full, overlay_full, df_f, (y_off, x_off)
@@ -0,0 +1,85 @@
1
+ """
2
+ This module is an example of a barebones numpy reader plugin for napari.
3
+
4
+ It implements the Reader specification, but your plugin may choose to
5
+ implement multiple readers or even other plugin contributions. see:
6
+ https://napari.org/stable/plugins/building_a_plugin/guides.html#readers
7
+ """
8
+
9
+ import numpy as np
10
+
11
+
12
+ def napari_get_reader(path):
13
+ """A basic implementation of a Reader contribution.
14
+
15
+ Parameters
16
+ ----------
17
+ path : str or list of str
18
+ Path to file, or list of paths.
19
+
20
+ Returns
21
+ -------
22
+ function or None
23
+ If the path is a recognized format, return a function that accepts the
24
+ same path or list of paths, and returns a list of layer data tuples.
25
+ """
26
+ if isinstance(path, list):
27
+ # reader plugins may be handed single path, or a list of paths.
28
+ # if it is a list, it is assumed to be an image stack...
29
+ # so we are only going to look at the first file.
30
+ path = path[0]
31
+
32
+ # the get_reader function should make as many checks as possible
33
+ # (without loading the full file) to determine if it can read
34
+ # the path. Here, we check the dtype of the array by loading
35
+ # it with memmap, so that we don't actually load the full array into memory.
36
+ # We pretend that this reader can only read integer arrays.
37
+ try:
38
+ arr = np.load(path, mmap_mode="r")
39
+ if arr.dtype != np.int_:
40
+ return None
41
+ # napari_get_reader should never raise an exception, because napari
42
+ # raises its own specific errors depending on what plugins are
43
+ # available for the given path, so we catch
44
+ # the OSError that np.load might raise if the file is malformed
45
+ except OSError:
46
+ return None
47
+
48
+ # otherwise we return the *function* that can read ``path``.
49
+ return reader_function
50
+
51
+
52
+ def reader_function(path):
53
+ """Take a path or list of paths and return a list of LayerData tuples.
54
+
55
+ Readers are expected to return data as a list of tuples, where each tuple
56
+ is (data, [add_kwargs, [layer_type]]), "add_kwargs" and "layer_type" are
57
+ both optional.
58
+
59
+ Parameters
60
+ ----------
61
+ path : str or list of str
62
+ Path to file, or list of paths.
63
+
64
+ Returns
65
+ -------
66
+ layer_data : list of tuples
67
+ A list of LayerData tuples where each tuple in the list contains
68
+ (data, metadata, layer_type), where data is a numpy array, metadata is
69
+ a dict of keyword arguments for the corresponding viewer.add_* method
70
+ in napari, and layer_type is a lower-case string naming the type of
71
+ layer. Both "meta", and "layer_type" are optional. napari will
72
+ default to layer_type=="image" if not provided
73
+ """
74
+ # handle both a string and a list of strings
75
+ paths = [path] if isinstance(path, str) else path
76
+ # load all files into array
77
+ arrays = [np.load(_path) for _path in paths]
78
+ # stack arrays into single array
79
+ data = np.squeeze(np.stack(arrays))
80
+
81
+ # optional kwargs for the corresponding viewer.add_* method
82
+ add_kwargs = {}
83
+
84
+ layer_type = "image" # optional, default is "image"
85
+ return [(data, add_kwargs, layer_type)]
@@ -0,0 +1,22 @@
1
+ """
2
+ This module is an example of a barebones sample data provider for napari.
3
+
4
+ It implements the "sample data" specification.
5
+ see: https://napari.org/stable/plugins/building_a_plugin/guides.html#sample-data
6
+
7
+ Replace code below according to your needs.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import numpy
13
+
14
+
15
+ def make_sample_data():
16
+ """Generates an image"""
17
+ # Return list of tuples
18
+ # [(data1, add_image_kwargs1), (data2, add_image_kwargs2)]
19
+ # Check the documentation for more information about the
20
+ # add_image_kwargs
21
+ # https://napari.org/stable/api/napari.Viewer.html#napari.Viewer.add_image
22
+ return [(numpy.random.rand(512, 512), {})]
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.1.0'
32
+ __version_tuple__ = version_tuple = (0, 1, 0)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,152 @@
1
+ import napari
2
+ import numpy as np
3
+ import pandas as pd
4
+ from magicgui import magic_factory
5
+ from napari.layers import Image # <-- real class (magicgui can resolve)
6
+ from qtpy.QtWidgets import QFileDialog
7
+
8
+ from ._axon_quant import QuantParams, quantify_myelinated_axons
9
+
10
+
11
+ def _as_uint8_rgb(data: np.ndarray) -> np.ndarray:
12
+ """Convert napari image data into uint8 RGB (Y,X,3)."""
13
+ arr = np.asarray(data)
14
+
15
+ # Expect (Y, X, 3) or (Y, X, 4)
16
+ if arr.ndim == 3 and arr.shape[-1] in (3, 4):
17
+ if arr.dtype != np.uint8:
18
+ a = arr.astype(np.float32)
19
+ a = a - np.nanmin(a)
20
+ denom = (np.nanmax(a) - np.nanmin(a)) + 1e-9
21
+ a = a / denom
22
+ a = (a * 255.0).clip(0, 255).astype(np.uint8)
23
+ arr = a
24
+ return arr[..., :3]
25
+
26
+ raise ValueError(
27
+ f"Expected RGB/RGBA image data. Got shape={arr.shape}, dtype={arr.dtype}"
28
+ )
29
+
30
+
31
+ @magic_factory(
32
+ call_button="Run quantification",
33
+ # Top 3 critical knobs
34
+ v_thresh={
35
+ "label": "V-threshold (myelin brightness)",
36
+ "min": 0.0,
37
+ "max": 1.0,
38
+ "step": 0.01,
39
+ },
40
+ min_axon_area={
41
+ "label": "Min axon area (px)",
42
+ "min": 0,
43
+ "max": 20000,
44
+ "step": 10,
45
+ },
46
+ min_solidity={
47
+ "label": "Min solidity",
48
+ "min": 0.0,
49
+ "max": 1.0,
50
+ "step": 0.01,
51
+ },
52
+ # Secondary knobs
53
+ close_radius={"label": "Closing radius", "min": 0, "max": 20, "step": 1},
54
+ open_radius={"label": "Opening radius", "min": 0, "max": 20, "step": 1},
55
+ edge_margin_px={
56
+ "label": "Edge margin exclusion (px)",
57
+ "min": 0,
58
+ "max": 200,
59
+ "step": 1,
60
+ },
61
+ px_size_um={
62
+ "label": "Pixel size (µm/px)",
63
+ "min": 0.0,
64
+ "max": 10.0,
65
+ "step": 0.001,
66
+ },
67
+ )
68
+ def myelin_quantifier_widget(
69
+ viewer: napari.Viewer,
70
+ image_layer: Image, # <-- key change
71
+ v_thresh: float = 0.15,
72
+ min_axon_area: int = 150,
73
+ min_solidity: float = 0.60,
74
+ close_radius: int = 3,
75
+ open_radius: int = 1,
76
+ edge_margin_px: int = 8,
77
+ px_size_um: float = 0.02,
78
+ remove_border_objects: bool = True,
79
+ auto_crop_screenshot: bool = True,
80
+ export_csv: bool = True,
81
+ ) -> None:
82
+ """
83
+ Runs myelinated axon quantification on an RGB image layer.
84
+
85
+ Outputs:
86
+ - Labels layer: axon interiors labeled 1..N (axon_id)
87
+ - Image layer: boundary overlay for verification
88
+ - Points layer: ID text anchored at centroids
89
+ - Optional CSV export with axon_id + measurements
90
+ """
91
+ if image_layer is None:
92
+ raise ValueError("Select an RGB image layer.")
93
+
94
+ img_rgb = _as_uint8_rgb(image_layer.data)
95
+
96
+ params = QuantParams(
97
+ px_size_um=px_size_um,
98
+ v_thresh=v_thresh,
99
+ min_axon_area=min_axon_area,
100
+ min_solidity=min_solidity,
101
+ close_radius=close_radius,
102
+ open_radius=open_radius,
103
+ remove_border_objects=remove_border_objects,
104
+ edge_margin_px=edge_margin_px,
105
+ auto_crop=auto_crop_screenshot,
106
+ )
107
+
108
+ labels_full, overlay_full, df, _ = quantify_myelinated_axons(
109
+ img_rgb, params
110
+ )
111
+
112
+ base = image_layer.name
113
+
114
+ # 1) Labels layer
115
+ viewer.add_labels(labels_full, name=f"{base} | axon_ids")
116
+
117
+ # 2) Overlay image layer
118
+ viewer.add_image(overlay_full, name=f"{base} | axon_overlay", opacity=0.8)
119
+
120
+ # 3) Text IDs as Points layer
121
+ if len(df) > 0:
122
+ points = df[["cy_img", "cx_img"]].to_numpy(dtype=float) # (y,x)
123
+ features = pd.DataFrame(
124
+ {"axon_id": df["axon_id"].astype(str).to_numpy()}
125
+ )
126
+
127
+ pts = viewer.add_points(
128
+ points,
129
+ name=f"{base} | axon_id_text",
130
+ size=0, # text only
131
+ features=features,
132
+ )
133
+ pts.text = {
134
+ "string": "{axon_id}",
135
+ "size": 10,
136
+ "color": "yellow",
137
+ "anchor": "center",
138
+ }
139
+
140
+ # 4) Export CSV
141
+ if export_csv:
142
+ default_name = f"{base}_axon_measurements.csv"
143
+ save_path, _ = QFileDialog.getSaveFileName(
144
+ None,
145
+ "Save axon measurements CSV",
146
+ default_name,
147
+ "CSV files (*.csv)",
148
+ )
149
+ if save_path:
150
+ df.to_csv(save_path, index=False)
151
+
152
+ viewer.status = f"Myelin Quantifier: detected {len(df)} axons (filtered)."
@@ -0,0 +1,66 @@
1
+ """
2
+ This module is an example of a barebones writer plugin for napari.
3
+
4
+ It implements the Writer specification.
5
+ see: https://napari.org/stable/plugins/building_a_plugin/guides.html#writers
6
+
7
+ Replace code below according to your needs.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Sequence
13
+ from typing import TYPE_CHECKING, Any, Union
14
+
15
+ if TYPE_CHECKING:
16
+ DataType = Union[Any, Sequence[Any]]
17
+ FullLayerData = tuple[DataType, dict, str]
18
+
19
+
20
+ def write_single_image(path: str, data: Any, meta: dict) -> list[str]:
21
+ """Writes a single image layer.
22
+
23
+ Parameters
24
+ ----------
25
+ path : str
26
+ A string path indicating where to save the image file.
27
+ data : The layer data
28
+ The `.data` attribute from the napari layer.
29
+ meta : dict
30
+ A dictionary containing all other attributes from the napari layer
31
+ (excluding the `.data` layer attribute).
32
+
33
+ Returns
34
+ -------
35
+ [path] : A list containing the string path to the saved file.
36
+ """
37
+
38
+ # implement your writer logic here ...
39
+
40
+ # return path to any file(s) that were successfully written
41
+ return [path]
42
+
43
+
44
+ def write_multiple(path: str, data: list[FullLayerData]) -> list[str]:
45
+ """Writes multiple layers of different types.
46
+
47
+ Parameters
48
+ ----------
49
+ path : str
50
+ A string path indicating where to save the data file(s).
51
+ data : A list of layer tuples.
52
+ Tuples contain three elements: (data, meta, layer_type)
53
+ `data` is the layer data
54
+ `meta` is a dictionary containing all other metadata attributes
55
+ from the napari layer (excluding the `.data` layer attribute).
56
+ `layer_type` is a string, eg: "image", "labels", "surface", etc.
57
+
58
+ Returns
59
+ -------
60
+ [path] : A list containing (potentially multiple) string paths to the saved file(s).
61
+ """
62
+
63
+ # implement your writer logic here ...
64
+
65
+ # return path to any file(s) that were successfully written
66
+ return [path]
@@ -0,0 +1,18 @@
1
+ name: napari-myelin-quantifier
2
+ display_name: Myelin Quantifier
3
+ visibility: public
4
+
5
+ contributions:
6
+ commands:
7
+ - id: napari-myelin-quantifier.myelin_quantifier_widget
8
+ python_name: napari_myelin_quantifier._widget:myelin_quantifier_widget
9
+ title: Myelin Quantifier (Axon Counter)
10
+
11
+ widgets:
12
+ - command: napari-myelin-quantifier.myelin_quantifier_widget
13
+ display_name: Myelin Quantifier
14
+
15
+ menus:
16
+ napari/plugins:
17
+ - command: napari-myelin-quantifier.myelin_quantifier_widget
18
+ when: always
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: napari-myelin-quantifier
3
+ Version: 0.1.0
4
+ Summary: Quantify myelinated axons with label tracking
5
+ Author: Napari User
6
+ Author-email: wulinteo.usa2@gmail.com
7
+ License:
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2026 Napari User
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in
20
+ all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ THE SOFTWARE.
29
+
30
+ Project-URL: Bug Tracker, https://github.com/wulinteousa2-hash/napari-myelin-quantifier/issues
31
+ Project-URL: Documentation, https://github.com/wulinteousa2-hash/napari-myelin-quantifier#README.md
32
+ Project-URL: Source Code, https://github.com/wulinteousa2-hash/napari-myelin-quantifier
33
+ Project-URL: User Support, https://github.com/wulinteousa2-hash/napari-myelin-quantifier/issues
34
+ Classifier: Development Status :: 2 - Pre-Alpha
35
+ Classifier: Framework :: napari
36
+ Classifier: Intended Audience :: Developers
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Operating System :: OS Independent
39
+ Classifier: Programming Language :: Python
40
+ Classifier: Programming Language :: Python :: 3
41
+ Classifier: Programming Language :: Python :: 3 :: Only
42
+ Classifier: Programming Language :: Python :: 3.10
43
+ Classifier: Programming Language :: Python :: 3.11
44
+ Classifier: Programming Language :: Python :: 3.12
45
+ Classifier: Programming Language :: Python :: 3.13
46
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
47
+ Requires-Python: >=3.10
48
+ Description-Content-Type: text/markdown
49
+ License-File: LICENSE
50
+ Requires-Dist: numpy
51
+ Requires-Dist: magicgui
52
+ Requires-Dist: qtpy
53
+ Requires-Dist: scikit-image
54
+ Provides-Extra: all
55
+ Requires-Dist: napari[all]; extra == "all"
56
+ Dynamic: license-file
57
+
58
+ # napari-myelin-quantifier
59
+
60
+ [![License MIT](https://img.shields.io/pypi/l/napari-myelin-quantifier.svg?color=green)](https://github.com/wulinteousa2-hash/napari-myelin-quantifier/raw/main/LICENSE)
61
+ [![PyPI](https://img.shields.io/pypi/v/napari-myelin-quantifier.svg?color=green)](https://pypi.org/project/napari-myelin-quantifier)
62
+ [![Python Version](https://img.shields.io/pypi/pyversions/napari-myelin-quantifier.svg?color=green)](https://python.org)
63
+ [![tests](https://github.com/wulinteousa2-hash/napari-myelin-quantifier/workflows/tests/badge.svg)](https://github.com/wulinteousa2-hash/napari-myelin-quantifier/actions)
64
+ [![codecov](https://codecov.io/gh/wulinteousa2-hash/napari-myelin-quantifier/branch/main/graph/badge.svg)](https://codecov.io/gh/wulinteousa2-hash/napari-myelin-quantifier)
65
+ [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-myelin-quantifier)](https://napari-hub.org/plugins/napari-myelin-quantifier)
66
+ [![npe2](https://img.shields.io/badge/plugin-npe2-blue?link=https://napari.org/stable/plugins/index.html)](https://napari.org/stable/plugins/index.html)
67
+ [![Copier](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/copier-org/copier/master/img/badge/badge-grayscale-inverted-border-purple.json)](https://github.com/copier-org/copier)
68
+
69
+ Quantify myelinated axons with label tracking
70
+
71
+ ----------------------------------
72
+
73
+ This [napari] plugin was generated with [copier] using the [napari-plugin-template] (None).
74
+
75
+ <!--
76
+ Don't miss the full getting started guide to set up your new package:
77
+ https://github.com/napari/napari-plugin-template#getting-started
78
+
79
+ and review the napari docs for plugin developers:
80
+ https://napari.org/stable/plugins/index.html
81
+ -->
82
+
83
+ ## Installation
84
+
85
+ You can install `napari-myelin-quantifier` via [pip]:
86
+
87
+ ```
88
+ pip install napari-myelin-quantifier
89
+ ```
90
+
91
+ If napari is not already installed, you can install `napari-myelin-quantifier` with napari and Qt via:
92
+
93
+ ```
94
+ pip install "napari-myelin-quantifier[all]"
95
+ ```
96
+
97
+
98
+ To install latest development version :
99
+
100
+ ```
101
+ pip install git+https://github.com/wulinteousa2-hash/napari-myelin-quantifier.git
102
+ ```
103
+
104
+
105
+
106
+ ## Contributing
107
+
108
+ Contributions are very welcome. Tests can be run with [tox], please ensure
109
+ the coverage at least stays the same before you submit a pull request.
110
+
111
+ ## License
112
+
113
+ Distributed under the terms of the [MIT] license,
114
+ "napari-myelin-quantifier" is free and open source software
115
+
116
+ ## Issues
117
+
118
+ If you encounter any problems, please [file an issue] along with a detailed description.
119
+
120
+ [napari]: https://github.com/napari/napari
121
+ [copier]: https://copier.readthedocs.io/en/stable/
122
+ [@napari]: https://github.com/napari
123
+ [MIT]: http://opensource.org/licenses/MIT
124
+ [BSD-3]: http://opensource.org/licenses/BSD-3-Clause
125
+ [GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
126
+ [GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
127
+ [Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
128
+ [Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
129
+ [napari-plugin-template]: https://github.com/napari/napari-plugin-template
130
+
131
+ [file an issue]: https://github.com/wulinteousa2-hash/napari-myelin-quantifier/issues
132
+
133
+ [napari]: https://github.com/napari/napari
134
+ [tox]: https://tox.readthedocs.io/en/latest/
135
+ [pip]: https://pypi.org/project/pip/
136
+ [PyPI]: https://pypi.org/
@@ -0,0 +1,14 @@
1
+ napari_myelin_quantifier/__init__.py,sha256=PQoEuuclJDku-ttiJPycAIgmCybSXFHRAGiKoNDj7nw,189
2
+ napari_myelin_quantifier/_axon_quant.py,sha256=kL9bpR57MdpwNsQ9nMFaJVP4fKuvOb4xP-221BcTOlM,6430
3
+ napari_myelin_quantifier/_reader.py,sha256=bAlIoUWx8N0IYF1InuqDUO1kL5N7L9r9jtMLm3eFlcY,3127
4
+ napari_myelin_quantifier/_sample_data.py,sha256=uHTGOqeoHhn1g-P11jEIFi7DOFtud9cB9aDnIVDvEig,662
5
+ napari_myelin_quantifier/_version.py,sha256=bauHj-EPGxrNjRoP5OYKhJo32mQBOMvEa2cW00W0jHk,738
6
+ napari_myelin_quantifier/_widget.py,sha256=h84e_rmy8HmuAi3RbXIdYonGykcEl_yCN3TMdWoR5KY,4485
7
+ napari_myelin_quantifier/_writer.py,sha256=R8DYqFtpA2TinwioQYcspMDDwqrZqkssATt6SwjDrVs,1958
8
+ napari_myelin_quantifier/napari.yaml,sha256=B270m-3v0ffZinsgx08jy_Nu1IIS8CVPoCHmQMKUP6c,526
9
+ napari_myelin_quantifier-0.1.0.dist-info/licenses/LICENSE,sha256=d1ACW9b17QHlefOPYOe_0-5E8vCJe80eRDPS-U5wkA8,1079
10
+ napari_myelin_quantifier-0.1.0.dist-info/METADATA,sha256=iiOrHYmpaUwAEcHt6WoNSFFbdb7YV9u1iBa0C-AnSeQ,6203
11
+ napari_myelin_quantifier-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ napari_myelin_quantifier-0.1.0.dist-info/entry_points.txt,sha256=QcAh7UGBW1p1HqQ6fSnZKzAjld9srLFZQZBLdhf7Ga8,82
13
+ napari_myelin_quantifier-0.1.0.dist-info/top_level.txt,sha256=DfQ-A26y__Y-MqVsnToCv-BUMrAmc5y26dggT7SMruE,25
14
+ napari_myelin_quantifier-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [napari.manifest]
2
+ napari-myelin-quantifier = napari_myelin_quantifier:napari.yaml
@@ -0,0 +1,22 @@
1
+
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2026 Napari User
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1 @@
1
+ napari_myelin_quantifier