histoseg 0.1.7__py3-none-any.whl → 0.1.8.1__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.
histoseg/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.7'
32
- __version_tuple__ = version_tuple = (0, 1, 7)
31
+ __version__ = version = '0.1.8.1'
32
+ __version_tuple__ = version_tuple = (0, 1, 8, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,5 @@
1
+ from .tissue_boundary import generate_tissue_boundary
2
+
3
+ __all__ = [
4
+ "generate_tissue_boundary",
5
+ ]
@@ -0,0 +1,141 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from shapely.geometry import MultiPoint, Polygon, MultiPolygon
4
+ from shapely.ops import unary_union, triangulate
5
+
6
+
7
+ def _alpha_shape(points, alpha):
8
+ """
9
+ Compute an alpha shape (concave hull) from a set of 2D points.
10
+
11
+ Parameters
12
+ ----------
13
+ points : np.ndarray of shape (n_points, 2)
14
+ alpha : float
15
+
16
+ Returns
17
+ -------
18
+ shapely.geometry.Polygon or MultiPolygon
19
+ """
20
+ if len(points) < 4:
21
+ return MultiPoint(points).convex_hull
22
+
23
+ triangles = triangulate(MultiPoint(points))
24
+ kept = []
25
+
26
+ for tri in triangles:
27
+ coords = np.array(tri.exterior.coords[:3])
28
+ a, b, c = coords
29
+
30
+ side_lengths = [
31
+ np.linalg.norm(a - b),
32
+ np.linalg.norm(b - c),
33
+ np.linalg.norm(c - a),
34
+ ]
35
+
36
+ s = sum(side_lengths) / 2.0
37
+ area_sq = (
38
+ s
39
+ * (s - side_lengths[0])
40
+ * (s - side_lengths[1])
41
+ * (s - side_lengths[2])
42
+ )
43
+
44
+ if area_sq <= 0:
45
+ continue
46
+
47
+ area = np.sqrt(area_sq)
48
+ circumradius = (
49
+ side_lengths[0] * side_lengths[1] * side_lengths[2]
50
+ ) / (4.0 * area)
51
+
52
+ if circumradius < 1.0 / alpha:
53
+ kept.append(tri)
54
+
55
+ if not kept:
56
+ return MultiPoint(points).convex_hull
57
+
58
+ return unary_union(kept)
59
+
60
+
61
+ def generate_tissue_boundary(
62
+ cells_df,
63
+ x_col="x_centroid",
64
+ y_col="y_centroid",
65
+ method="alpha_shape",
66
+ alpha=0.05,
67
+ simplify_tolerance=None,
68
+ output_csv=None,
69
+ ):
70
+ """
71
+ Generate a tissue boundary polygon from cell spatial coordinates.
72
+
73
+ Parameters
74
+ ----------
75
+ cells_df : pandas.DataFrame
76
+ Cell-level table containing spatial coordinates.
77
+ x_col, y_col : str
78
+ Column names for x and y coordinates.
79
+ method : {"alpha_shape", "convex_hull"}
80
+ Geometry method for boundary estimation.
81
+ alpha : float
82
+ Alpha parameter for alpha-shape.
83
+ simplify_tolerance : float or None
84
+ Optional polygon simplification tolerance.
85
+ output_csv : str or Path or None
86
+ If provided, write boundary to CSV.
87
+
88
+ Returns
89
+ -------
90
+ pandas.DataFrame
91
+ Columns: ["x", "y", "order"]
92
+ """
93
+ if x_col not in cells_df or y_col not in cells_df:
94
+ raise ValueError(
95
+ f"cells_df must contain columns '{x_col}' and '{y_col}'"
96
+ )
97
+
98
+ points = (
99
+ cells_df[[x_col, y_col]]
100
+ .dropna()
101
+ .to_numpy()
102
+ )
103
+
104
+ if len(points) == 0:
105
+ raise ValueError("No valid spatial coordinates found.")
106
+
107
+ if method == "convex_hull":
108
+ polygon = MultiPoint(points).convex_hull
109
+ elif method == "alpha_shape":
110
+ polygon = _alpha_shape(points, alpha=alpha)
111
+ else:
112
+ raise ValueError(f"Unknown method: {method}")
113
+
114
+ if simplify_tolerance is not None:
115
+ polygon = polygon.simplify(simplify_tolerance)
116
+
117
+ # ------------------------------------------------------------------
118
+ # Normalize geometry:
119
+ # tissue boundary must be a single outer polygon
120
+ # (consistent with tissueboundary.txt behavior)
121
+ # ------------------------------------------------------------------
122
+ if isinstance(polygon, MultiPolygon):
123
+ polygon = max(polygon.geoms, key=lambda p: p.area)
124
+
125
+ if not isinstance(polygon, Polygon):
126
+ raise TypeError(
127
+ f"Expected Polygon after processing, got {type(polygon)}"
128
+ )
129
+
130
+ x, y = polygon.exterior.coords.xy
131
+
132
+ boundary_df = pd.DataFrame({
133
+ "x": x,
134
+ "y": y,
135
+ "order": np.arange(len(x)),
136
+ })
137
+
138
+ if output_csv is not None:
139
+ boundary_df.to_csv(output_csv, index=False)
140
+
141
+ return boundary_df
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: histoseg
3
- Version: 0.1.7
3
+ Version: 0.1.8.1
4
4
  Summary: ...
5
5
  Author-email: Taobo Hu <taobo.hu@scilifelab.se>
6
6
  License: Required Notice: Copyright (c) 2025 SPATHO AB.
@@ -86,7 +86,6 @@ Project-URL: Documentation, https://histoseg.readthedocs.io
86
86
  Requires-Python: >=3.10
87
87
  Description-Content-Type: text/markdown
88
88
  License-File: LICENSE
89
- License-File: AUTHORS.rst
90
89
  Dynamic: license-file
91
90
 
92
91
  <div align="center">
@@ -1,15 +1,16 @@
1
1
  histoseg/__init__.py,sha256=ssVwpgspXb7TWi1aAbWYA82FBJhGZH1g-JIvosVocTM,982
2
- histoseg/_version.py,sha256=szvPIs2C82UunpzuvVg3MbF4QhzbBYTsVJ8DmPfq6_E,704
2
+ histoseg/_version.py,sha256=DceX3WwB1oE06QTAMXFaSlGTc9twNCVWkTErPbrlF7o,709
3
3
  histoseg/contours/__init__.py,sha256=8YEy98MnGOhJg1BHkVZ_qPtTYUWPoIDAOT60F5EfHgY,32
4
4
  histoseg/contours/pattern1_isoline.py,sha256=6JVL26O7AZ0s0YyloDoiUdIHTJ7jp6gEooFWnMoNhSw,19592
5
+ histoseg/geometry/__init__.py,sha256=5Aep5GBj2u4k2415QIECd0vK3y2eVBfQZRIskOgzb78,101
6
+ histoseg/geometry/tissue_boundary.py,sha256=-12lyNYI93JkNlBDT_Fv7SLAeTLKMZ9fPhqpSTowFz0,3680
5
7
  histoseg/gui/__init__.py,sha256=SJvM-gRCwQ0X7fufPmdNO43X3wQvlnFHFmFNswt1hlw,133
6
8
  histoseg/gui/gui_app.py,sha256=copFjJzMEeg2T4HV4--KHq9GG-v58xeW-eqRQ0-uJww,15259
7
9
  histoseg/io/__init__.py,sha256=kH_F15ApTutYbEUGAkV9QxBv8Ho863xvWK0mRmKeOCA,27
8
10
  histoseg/io/huggingface.py,sha256=YL_aXAXiHMMk6T6bqX-zPXFFChO83CVhTxI6mX52Z7g,2124
9
11
  histoseg/sfplot/Searcher_Findee_Score.py,sha256=Y9UzWrqap029BOyOnFPklvK4v-2wm3r_PkmUh65DEqo,14951
10
- histoseg-0.1.7.dist-info/licenses/AUTHORS.rst,sha256=T3nRBgyOUCoWU3rxF4Iy_tdd-1kJjyLBfdBwEQQswyw,202
11
- histoseg-0.1.7.dist-info/licenses/LICENSE,sha256=z7Ztufk460DPfU3rgZEstjCQK3EbwHbD4JSmF_7y0qA,4764
12
- histoseg-0.1.7.dist-info/METADATA,sha256=UZ-r82SGkB8uG1UoAAUsLshObJzESpdUkx8QAB2KbXI,13216
13
- histoseg-0.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
- histoseg-0.1.7.dist-info/top_level.txt,sha256=dvphnIeqaZamvJULm-I5qztYoGO8WLLCY85z0xrXsb0,9
15
- histoseg-0.1.7.dist-info/RECORD,,
12
+ histoseg-0.1.8.1.dist-info/licenses/LICENSE,sha256=z7Ztufk460DPfU3rgZEstjCQK3EbwHbD4JSmF_7y0qA,4764
13
+ histoseg-0.1.8.1.dist-info/METADATA,sha256=LQR8kK-wQSM9bqc_8WM0SUmYXYGrsaoHB5_GBFeECSg,13192
14
+ histoseg-0.1.8.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
+ histoseg-0.1.8.1.dist-info/top_level.txt,sha256=dvphnIeqaZamvJULm-I5qztYoGO8WLLCY85z0xrXsb0,9
16
+ histoseg-0.1.8.1.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- =======
2
- Credits
3
- =======
4
-
5
- Development Lead
6
- ----------------
7
-
8
- * Taobo Hu <taobo.hu@scilifelab.se>
9
- * Mengping Long <mengping.long@scilifelab.se>
10
-
11
- Contributors
12
- ------------
13
-
14
- None yet. Why not be the first?