Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert '-' to '_' in summary dataframe #215

Merged
merged 20 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 110 additions & 162 deletions src/skan/csr.py

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/skan/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def overlay_euclidean_skeleton_2d(
stats,
*,
image_cmap=None,
skeleton_color_source='branch-type',
skeleton_color_source='branch_type',
skeleton_colormap='viridis',
axes=None
):
Expand All @@ -124,7 +124,7 @@ def overlay_euclidean_skeleton_2d(

- skeleton-id: each individual skeleton (connected component) will
have a different colour.
- branch-type: each branch type (tip-tip, tip-junction,
- branch_type: each branch type (tip-tip, tip-junction,
junction-junction, path-path). This is the default.
- branch-distance: the curved length of the skeleton branch.
- euclidean-distance: the straight-line length of the skeleton branch.
Expand All @@ -142,8 +142,8 @@ def overlay_euclidean_skeleton_2d(
image = _normalise_image(image, image_cmap=image_cmap)
summary = stats
# transforming from row, col to x, y
coords_cols = (['image-coord-src-%i' % i for i in [1, 0]]
+ ['image-coord-dst-%i' % i for i in [1, 0]])
coords_cols = ['image_coord_src_%i' % i for i in [1, 0]
] + ['image_coord_dst_%i' % i for i in [1, 0]]
coords = summary[coords_cols].values.reshape((-1, 2, 2))
if axes is None:
fig, axes = plt.subplots()
Expand Down
20 changes: 10 additions & 10 deletions src/skan/pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
import multiprocessing as mp

CPU_COUNT = int(os.environ.get('CPU_COUNT', mp.cpu_count()))
CPU_COUNT = int(os.environ.get("CPU_COUNT", mp.cpu_count()))


def _get_scale(image, md_path_or_scale):
Expand Down Expand Up @@ -46,7 +46,7 @@ def _get_scale(image, md_path_or_scale):

def process_single_image(
filename, image_format, scale_metadata_path, threshold_radius,
smooth_radius, brightness_offset, crop_radius, smooth_method
smooth_radius, brightness_offset, crop_radius, smooth_method,
):
image = imageio.v2.imread(filename, format=image_format)
scale = _get_scale(image, scale_metadata_path)
Expand All @@ -61,17 +61,17 @@ def process_single_image(
sigma=pixel_smoothing_radius,
radius=pixel_threshold_radius,
offset=brightness_offset,
smooth_method=smooth_method
smooth_method=smooth_method,
)
quality = shape_index(image, sigma=pixel_smoothing_radius, mode='reflect')
skeleton = morphology.skeletonize(thresholded) * quality
framedata = csr.summarize(csr.Skeleton(skeleton, spacing=scale))
framedata['squiggle'] = np.log2(
framedata['branch-distance'] / framedata['euclidean-distance']
framedata['branch_distance'] / framedata['euclidean_distance']
)
framedata['scale'] = scale
framedata.rename(
columns={'mean-pixel-value': 'mean-shape-index'},
columns={'mean_pixel_value': 'mean_shape_index'},
inplace=True,
errors='raise',
)
Expand All @@ -88,7 +88,7 @@ def process_images(
scale_metadata_path,
crop_radius=0,
smooth_method='Gaussian',
num_threads=CPU_COUNT
num_threads=CPU_COUNT,
):
"""Full pipeline from images to skeleton stats with local median threshold.

Expand Down Expand Up @@ -128,15 +128,15 @@ def process_images(
Finally, after all the images have been processed, the pipeline yields
a DataFrame containing all the collated branch-level results.
"""
image_format = None if image_format == 'auto' else image_format
image_format = None if image_format == "auto" else image_format
results = []
image_results = []
with ThreadPoolExecutor(max_workers=num_threads) as ex:
future_data = {
ex.submit(
process_single_image, filename, image_format,
scale_metadata_path, threshold_radius, smooth_radius,
brightness_offset, crop_radius, smooth_method
brightness_offset, crop_radius, smooth_method,
): filename
for filename in filenames
}
Expand All @@ -152,9 +152,9 @@ def process_images(
image_stats['branch density'] = (
framedata.shape[0] / image_stats['area']
)
j2j = framedata[framedata['branch-type'] == 2]
j2j = framedata[framedata['branch_type'] == 2]
image_stats['mean J2J branch distance'] = (
j2j['branch-distance'].mean()
j2j['branch_distance'].mean()
)
image_results.append(image_stats)
yield filename, image, thresholded, skeleton, framedata
Expand Down
8 changes: 4 additions & 4 deletions src/skan/summary_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ def find_main_branches(summary: DataFrame) -> np.ndarray:
skeleton
"""
is_main = np.zeros(summary.shape[0], dtype=bool)
us = summary['node-id-src']
vs = summary['node-id-dst']
ws = summary['branch-distance']
us = summary['node_id_src']
vs = summary['node_id_dst']
ws = summary['branch_distance']

edge2idx = {(u, v): i for i, (u, v) in enumerate(zip(us, vs))}

Expand All @@ -40,7 +40,7 @@ def find_main_branches(summary: DataFrame) -> np.ndarray:
for src in p:
for dst in p[src]:
val = p[src][dst]
if (val is not None and np.isfinite(val) and val >= curr_val):
if val is not None and np.isfinite(val) and val >= curr_val:
curr_val = val
curr_pair = (src, dst)
for i, j in tz.sliding_window(2, nx.shortest_path(h,
Expand Down
81 changes: 48 additions & 33 deletions src/skan/test/test_csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _old_branch_statistics(
skeleton_image, spacing=spacing, value_is_height=value_is_height
)
summary = csr.summarize(skel, value_is_height=value_is_height)
columns = ['node-id-src', 'node-id-dst', 'branch-distance', 'branch-type']
columns = ['node_id_src', 'node_id_dst', 'branch_distance', 'branch_type']
return summary[columns].to_numpy()


Expand Down Expand Up @@ -55,22 +55,22 @@ def test_skeleton1_stats():

def test_2skeletons():
df = csr.summarize(csr.Skeleton(skeleton2))
assert_almost_equal(np.unique(df['euclidean-distance']), np.sqrt([5, 10]))
assert_equal(np.unique(df['skeleton-id']), [0, 1])
assert_equal(np.bincount(df['branch-type']), [0, 4, 4])
assert_almost_equal(np.unique(df['euclidean_distance']), np.sqrt([5, 10]))
assert_equal(np.unique(df['skeleton_id']), [0, 1])
assert_equal(np.bincount(df['branch_type']), [0, 4, 4])


def test_summarize_spacing():
df = csr.summarize(csr.Skeleton(skeleton2))
df2 = csr.summarize(csr.Skeleton(skeleton2, spacing=2))
assert_equal(np.array(df['node-id-src']), np.array(df2['node-id-src']))
assert_equal(np.array(df['node_id_src']), np.array(df2['node_id_src']))
assert_almost_equal(
np.array(df2['euclidean-distance']),
np.array(2 * df['euclidean-distance'])
np.array(df2['euclidean_distance']),
np.array(2 * df['euclidean_distance'])
)
assert_almost_equal(
np.array(df2['branch-distance']),
np.array(2 * df['branch-distance'])
np.array(df2['branch_distance']),
np.array(2 * df['branch_distance'])
)


Expand Down Expand Up @@ -108,8 +108,8 @@ def test_topograph_summary():
csr.Skeleton(topograph1d, spacing=2.5, value_is_height=True),
value_is_height=True,
)
assert stats.loc[0, 'euclidean-distance'] == 5.0
columns = ['coord-src-0', 'coord-src-1', 'coord-dst-0', 'coord-dst-1']
assert stats.loc[0, 'euclidean_distance'] == 5.0
columns = ['coord_src_0', 'coord_src_1', 'coord_dst_0', 'coord_dst_1']
assert_almost_equal(sorted(stats.loc[0, columns]), [0, 3, 3, 5])


Expand All @@ -124,20 +124,20 @@ def test_multiplicity_stats():
stats1 = csr.summarize(csr.Skeleton(skeleton0))
stats2 = csr.summarize(csr.Skeleton(skeleton0, spacing=2))
assert_almost_equal(
2 * stats1['branch-distance'].values,
stats2['branch-distance'].values
2 * stats1['branch_distance'].values,
stats2['branch_distance'].values
)
assert_almost_equal(
2 * stats1['euclidean-distance'].values,
stats2['euclidean-distance'].values
2 * stats1['euclidean_distance'].values,
stats2['euclidean_distance'].values
)


def test_pixel_values():
image = np.random.random((45,))
expected = np.mean(image)
stats = csr.summarize(csr.Skeleton(image))
assert_almost_equal(stats.loc[0, 'mean-pixel-value'], expected)
assert_almost_equal(stats.loc[0, 'mean_pixel_value'], expected)


def test_tip_junction_edges():
Expand Down Expand Up @@ -172,44 +172,59 @@ def test_transpose_image():
skeleton1 = csr.Skeleton(image)
skeleton2 = csr.Skeleton(image.T)

assert (skeleton1.n_paths == skeleton2.n_paths)
assert skeleton1.n_paths == skeleton2.n_paths
np.testing.assert_allclose(
np.sort(skeleton1.path_lengths()),
np.sort(skeleton2.path_lengths()),
)


@pytest.mark.parametrize(
"skeleton,prune_branch,target",
'skeleton,prune_branch,target',
[
(
skeleton1, 1,
np.array([[0, 1, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 1, 1, 0], [0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]])
skeleton1,
1,
np.array([
[0, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 1, 1, 0],
[0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
]),
),
(
skeleton1, 2,
np.array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0], [1, 0, 0, 2, 0, 0, 0],
[1, 0, 0, 0, 2, 2, 2]])
skeleton1,
2,
np.array([
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0],
[1, 0, 0, 2, 0, 0, 0],
[1, 0, 0, 0, 2, 2, 2],
]),
),
# There are no isolated cycles to be pruned
(
skeleton1, 3,
np.array([[0, 1, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 0, 1],
[0, 3, 2, 0, 1, 1, 0], [3, 0, 0, 4, 0, 0, 0],
[3, 0, 0, 0, 4, 4, 4]])
skeleton1,
3,
np.array([
[0, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 0, 1],
[0, 3, 2, 0, 1, 1, 0],
[3, 0, 0, 4, 0, 0, 0],
[3, 0, 0, 0, 4, 4, 4],
]),
),
]
],
)
def test_prune_paths(
skeleton: np.ndarray, prune_branch: int, target: np.ndarray
) -> None:
"""Test pruning of paths."""
s = csr.Skeleton(skeleton, keep_images=True)
summary = summarize(s)
indices_to_remove = summary.loc[summary['branch-type'] == prune_branch
indices_to_remove = summary.loc[summary['branch_type'] == prune_branch
].index
pruned = s.prune_paths(indices_to_remove)
np.testing.assert_array_equal(pruned, target)
Expand All @@ -220,7 +235,7 @@ def test_prune_paths_exception_single_point() -> None:
can not be created and returned."""
s = csr.Skeleton(skeleton0)
summary = summarize(s)
indices_to_remove = summary.loc[summary['branch-type'] == 1].index
indices_to_remove = summary.loc[summary['branch_type'] == 1].index
with pytest.raises(ValueError):
s.prune_paths(indices_to_remove)

Expand Down
2 changes: 1 addition & 1 deletion src/skan/test/test_draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_overlay_skeleton(test_image, test_skeleton):
def test_overlay_euclidean_skeleton(test_image, test_stats):
draw.overlay_euclidean_skeleton_2d(test_image, test_stats)
draw.overlay_euclidean_skeleton_2d(
test_image, test_stats, skeleton_color_source='branch-distance'
test_image, test_stats, skeleton_color_source='branch_distance'
)


Expand Down
6 changes: 3 additions & 3 deletions src/skan/test/test_napari_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_get_skeleton_simple():
labels_layer, skeleton_type
)

assert type(layer_kwargs["metadata"]["skeleton"]) is Skeleton
assert type(layer_kwargs['metadata']['skeleton']) is Skeleton
np.testing.assert_array_equal(
shapes_data[0],
[[5, 1], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8]]
Expand All @@ -41,7 +41,7 @@ def test_get_skeleton_horse():
)
assert len(shapes_data) == 24 # 24 line segments in the horse skeleton
assert 'features' in layer_kwargs
assert type(layer_kwargs["features"]) is pd.DataFrame
assert type(layer_kwargs['features']) is pd.DataFrame


def test_gui(make_napari_viewer):
Expand All @@ -56,7 +56,7 @@ def test_gui(make_napari_viewer):
dw, widget = viewer.window.add_plugin_dock_widget(
'skan', 'Color Skeleton Widget'
)
widget.feature_name.value = "euclidean-distance"
widget.feature_name.value = 'euclidean_distance'
widget()
layer = viewer.layers[-1]
assert layer.edge_colormap.name == 'viridis'
Expand Down
12 changes: 5 additions & 7 deletions src/skan/test/test_skeleton_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import pytest
from skan.csr import Skeleton, summarize

from skan._testdata import (
tinycycle, skeleton1, skeleton2, skeleton3d, skeleton4, junction_first
)
from skan._testdata import tinycycle, skeleton1, skeleton2, skeleton3d, skeleton4, junction_first


def test_tiny_cycle():
Expand Down Expand Up @@ -102,16 +100,16 @@ def test_skeleton_summarize():
image[skeleton2] = 1 + np.random.random(np.sum(skeleton2))
skeleton = Skeleton(image)
summary = summarize(skeleton)
assert set(summary['skeleton-id']) == {0, 1}
assert set(summary['skeleton_id']) == {0, 1}
assert (
np.all(summary['mean-pixel-value'] < 2)
and np.all(summary['mean-pixel-value'] > 1)
np.all(summary['mean_pixel_value'] < 2)
and np.all(summary['mean_pixel_value'] > 1)
)


def test_skeleton_label_image_strict():
"""Test that the skeleton pixel labels match the branch IDs.

This does pixel-wise pairing of the label image with the expected label
image. There should be the same number of unique pairs as there are unique
labels in the expected label image. This assumes that the branches are
Expand Down
9 changes: 4 additions & 5 deletions src/skan/test/test_summary_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ def test_find_main():
non_main_df = summary_df.loc[summary_df['main'] == False]
assert non_main_df.shape[0] == 1
coords = non_main_df[[
'coord-src-0', 'coord-src-1', 'coord-dst-0', 'coord-dst-1'
'coord_src_0', 'coord_src_1', 'coord_dst_0', 'coord_dst_1'
]].to_numpy()
assert (
np.all(coords == non_main_edge_start + non_main_edge_finish)
or np.all(coords == non_main_edge_finish + non_main_edge_start)
)
assert np.all(
coords == non_main_edge_start + non_main_edge_finish
) or np.all(coords == non_main_edge_finish + non_main_edge_start)
Loading