sem2surface 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {sem2surface-0.2.0/src/sem2surface.egg-info → sem2surface-0.2.2}/PKG-INFO +40 -51
- {sem2surface-0.2.0 → sem2surface-0.2.2}/README.md +39 -50
- {sem2surface-0.2.0 → sem2surface-0.2.2}/pyproject.toml +1 -1
- {sem2surface-0.2.0 → sem2surface-0.2.2/src/sem2surface.egg-info}/PKG-INFO +40 -51
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.py +54 -11
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface_cli.py +1 -6
- {sem2surface-0.2.0 → sem2surface-0.2.2}/tests/test_cli.py +1 -1
- {sem2surface-0.2.0 → sem2surface-0.2.2}/tests/test_core.py +38 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/LICENSE +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/setup.cfg +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.egg-info/SOURCES.txt +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.egg-info/dependency_links.txt +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.egg-info/entry_points.txt +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.egg-info/requires.txt +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface.egg-info/top_level.txt +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/src/sem2surface_gui.py +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/tests/test_fft.py +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/tests/test_gui_preview.py +0 -0
- {sem2surface-0.2.0 → sem2surface-0.2.2}/tests/test_reference_example.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sem2surface
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 3D surface reconstruction from multi-detector SEM/BSE images
|
|
5
5
|
Author: Vladislav A. Yastrebov
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -39,6 +39,9 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
# sem2surface
|
|
41
41
|
|
|
42
|
+
[](https://pypi.org/project/sem2surface/)
|
|
43
|
+
[](https://github.com/vyastreb/sem2surface/blob/master/LICENSE)
|
|
44
|
+
|
|
42
45
|
`sem2surface` reconstructs a three-dimensional surface from three to five
|
|
43
46
|
multi-detector SEM/BSE images. It extracts two normalized principal images,
|
|
44
47
|
identifies their orientation with a Radon transform, and integrates the resulting
|
|
@@ -54,78 +57,49 @@ calibration procedure.
|
|
|
54
57
|
|
|
55
58
|
## Installation
|
|
56
59
|
|
|
57
|
-
Python 3.10 or newer is required.
|
|
58
|
-
recommended so the application does not conflict with system Python packages.
|
|
59
|
-
|
|
60
|
-
### Linux and macOS
|
|
61
|
-
|
|
62
|
-
Create an environment and install a released version:
|
|
60
|
+
Python 3.10 or newer is required. Install `sem2surface` from PyPI with:
|
|
63
61
|
|
|
64
62
|
```bash
|
|
65
|
-
|
|
66
|
-
source .venv/bin/activate
|
|
67
|
-
python -m pip install --upgrade pip
|
|
68
|
-
python -m pip install sem2surface
|
|
69
|
-
sem2surface-gui
|
|
63
|
+
pip install sem2surface
|
|
70
64
|
```
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
this source checkout instead:
|
|
66
|
+
If several Python installations are present, use the interpreter explicitly:
|
|
74
67
|
|
|
75
68
|
```bash
|
|
76
|
-
|
|
69
|
+
python3 -m pip install sem2surface # Linux or macOS
|
|
77
70
|
```
|
|
78
71
|
|
|
79
|
-
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
80
|
-
users can install it with their system package manager as `python3-tk` before
|
|
81
|
-
creating the environment.
|
|
82
|
-
|
|
83
72
|
### Windows
|
|
84
73
|
|
|
85
74
|
Install Python 3.10 or newer from
|
|
86
75
|
[python.org](https://www.python.org/downloads/windows/). Keep the standard
|
|
87
|
-
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell
|
|
88
|
-
|
|
76
|
+
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell or
|
|
77
|
+
Command Prompt and run:
|
|
89
78
|
|
|
90
79
|
```powershell
|
|
91
|
-
py -
|
|
92
|
-
.\.venv\Scripts\Activate.ps1
|
|
93
|
-
python -m pip install --upgrade pip
|
|
94
|
-
python -m pip install sem2surface
|
|
80
|
+
py -m pip install sem2surface
|
|
95
81
|
sem2surface-gui
|
|
96
82
|
```
|
|
97
83
|
|
|
98
|
-
If
|
|
99
|
-
PowerShell process and retry activation:
|
|
84
|
+
If Windows cannot find the installed launcher, start the GUI through Python:
|
|
100
85
|
|
|
101
86
|
```powershell
|
|
102
|
-
|
|
103
|
-
.\.venv\Scripts\Activate.ps1
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
In Command Prompt, activate the same environment with:
|
|
107
|
-
|
|
108
|
-
```bat
|
|
109
|
-
.venv\Scripts\activate.bat
|
|
87
|
+
py -m sem2surface_gui
|
|
110
88
|
```
|
|
111
89
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
```powershell
|
|
115
|
-
.\.venv\Scripts\sem2surface-gui.exe
|
|
116
|
-
```
|
|
90
|
+
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
91
|
+
users can install `python3-tk` with their system package manager.
|
|
117
92
|
|
|
118
|
-
|
|
119
|
-
|
|
93
|
+
A virtual environment is optional. It is useful when the system Python is
|
|
94
|
+
externally managed, installation permissions are restricted, or other packages
|
|
95
|
+
have conflicting dependencies:
|
|
120
96
|
|
|
121
|
-
```
|
|
122
|
-
|
|
97
|
+
```bash
|
|
98
|
+
python3 -m venv .venv
|
|
99
|
+
source .venv/bin/activate # Windows PowerShell: .\.venv\Scripts\Activate.ps1
|
|
100
|
+
python -m pip install sem2surface
|
|
123
101
|
```
|
|
124
102
|
|
|
125
|
-
Use `deactivate` to leave the environment on any platform. Do not copy a
|
|
126
|
-
virtual environment between computers or move it after creation; create a new
|
|
127
|
-
one and reinstall the package instead.
|
|
128
|
-
|
|
129
103
|
VTK export is optional because VTK is a large dependency:
|
|
130
104
|
|
|
131
105
|
```bash
|
|
@@ -212,6 +186,12 @@ decomposition, Radon search, and oriented gradients.
|
|
|
212
186
|
When timestamps are disabled, an existing output with the same name is replaced.
|
|
213
187
|
Choose a dedicated output folder or enable timestamps when results must be kept.
|
|
214
188
|
|
|
189
|
+
For TIFF acquisitions, `sem2surface` automatically removes a microscope
|
|
190
|
+
annotation footer when it detects a nearly uniform bright separator followed by
|
|
191
|
+
a statistically distinct lower band. The detector works with both 8-bit Zeiss
|
|
192
|
+
and 16-bit FEI exports, leaves footer-free images unchanged, and records every
|
|
193
|
+
detected crop in the processing log.
|
|
194
|
+
|
|
215
195
|
## Reference examples and scaling
|
|
216
196
|
|
|
217
197
|
The [examples directory](https://github.com/vyastreb/sem2surface/tree/master/examples)
|
|
@@ -232,6 +212,15 @@ python examples/Surface_1/test_without_gui.py
|
|
|
232
212
|
<!-- PyPI cannot resolve repository-relative images. Keep this absolute URL. -->
|
|
233
213
|

|
|
234
214
|
|
|
215
|
+
## Changes in 0.2.2
|
|
216
|
+
|
|
217
|
+
- Replaced the historical hard-coded footer threshold with bit-depth-independent
|
|
218
|
+
SEM annotation-band detection.
|
|
219
|
+
- Added safeguards against cropping isolated bright lines in specimen data.
|
|
220
|
+
- Added per-image logging of detected footers and removed row counts.
|
|
221
|
+
- Added regression coverage for 8-bit Zeiss, 16-bit FEI, footer-free, and
|
|
222
|
+
isolated-line images.
|
|
223
|
+
|
|
235
224
|
## Development
|
|
236
225
|
|
|
237
226
|
Create an isolated environment and install the editable project with its test
|
|
@@ -266,7 +255,7 @@ For a first trial, create a separate account and API token on
|
|
|
266
255
|
version:
|
|
267
256
|
|
|
268
257
|
```bash
|
|
269
|
-
python -m twine upload --repository testpypi dist/sem2surface-0.2.
|
|
258
|
+
python -m twine upload --repository testpypi dist/sem2surface-0.2.2*
|
|
270
259
|
```
|
|
271
260
|
|
|
272
261
|
When prompted, use `__token__` as the username and the complete TestPyPI token,
|
|
@@ -274,7 +263,7 @@ including its `pypi-` prefix, as the password. Test the uploaded wheel in a new
|
|
|
274
263
|
environment without resolving dependencies from TestPyPI:
|
|
275
264
|
|
|
276
265
|
```bash
|
|
277
|
-
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.
|
|
266
|
+
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.2
|
|
278
267
|
sem2surface --version
|
|
279
268
|
```
|
|
280
269
|
|
|
@@ -282,7 +271,7 @@ For the real release, create a PyPI account and API token at
|
|
|
282
271
|
[pypi.org](https://pypi.org/), then run:
|
|
283
272
|
|
|
284
273
|
```bash
|
|
285
|
-
python -m twine upload dist/sem2surface-0.2.
|
|
274
|
+
python -m twine upload dist/sem2surface-0.2.2*
|
|
286
275
|
```
|
|
287
276
|
|
|
288
277
|
PyPI and TestPyPI use separate accounts and tokens. Never commit a token or put
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# sem2surface
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/sem2surface/)
|
|
4
|
+
[](https://github.com/vyastreb/sem2surface/blob/master/LICENSE)
|
|
5
|
+
|
|
3
6
|
`sem2surface` reconstructs a three-dimensional surface from three to five
|
|
4
7
|
multi-detector SEM/BSE images. It extracts two normalized principal images,
|
|
5
8
|
identifies their orientation with a Radon transform, and integrates the resulting
|
|
@@ -15,78 +18,49 @@ calibration procedure.
|
|
|
15
18
|
|
|
16
19
|
## Installation
|
|
17
20
|
|
|
18
|
-
Python 3.10 or newer is required.
|
|
19
|
-
recommended so the application does not conflict with system Python packages.
|
|
20
|
-
|
|
21
|
-
### Linux and macOS
|
|
22
|
-
|
|
23
|
-
Create an environment and install a released version:
|
|
21
|
+
Python 3.10 or newer is required. Install `sem2surface` from PyPI with:
|
|
24
22
|
|
|
25
23
|
```bash
|
|
26
|
-
|
|
27
|
-
source .venv/bin/activate
|
|
28
|
-
python -m pip install --upgrade pip
|
|
29
|
-
python -m pip install sem2surface
|
|
30
|
-
sem2surface-gui
|
|
24
|
+
pip install sem2surface
|
|
31
25
|
```
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
this source checkout instead:
|
|
27
|
+
If several Python installations are present, use the interpreter explicitly:
|
|
35
28
|
|
|
36
29
|
```bash
|
|
37
|
-
|
|
30
|
+
python3 -m pip install sem2surface # Linux or macOS
|
|
38
31
|
```
|
|
39
32
|
|
|
40
|
-
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
41
|
-
users can install it with their system package manager as `python3-tk` before
|
|
42
|
-
creating the environment.
|
|
43
|
-
|
|
44
33
|
### Windows
|
|
45
34
|
|
|
46
35
|
Install Python 3.10 or newer from
|
|
47
36
|
[python.org](https://www.python.org/downloads/windows/). Keep the standard
|
|
48
|
-
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell
|
|
49
|
-
|
|
37
|
+
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell or
|
|
38
|
+
Command Prompt and run:
|
|
50
39
|
|
|
51
40
|
```powershell
|
|
52
|
-
py -
|
|
53
|
-
.\.venv\Scripts\Activate.ps1
|
|
54
|
-
python -m pip install --upgrade pip
|
|
55
|
-
python -m pip install sem2surface
|
|
41
|
+
py -m pip install sem2surface
|
|
56
42
|
sem2surface-gui
|
|
57
43
|
```
|
|
58
44
|
|
|
59
|
-
If
|
|
60
|
-
PowerShell process and retry activation:
|
|
45
|
+
If Windows cannot find the installed launcher, start the GUI through Python:
|
|
61
46
|
|
|
62
47
|
```powershell
|
|
63
|
-
|
|
64
|
-
.\.venv\Scripts\Activate.ps1
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
In Command Prompt, activate the same environment with:
|
|
68
|
-
|
|
69
|
-
```bat
|
|
70
|
-
.venv\Scripts\activate.bat
|
|
48
|
+
py -m sem2surface_gui
|
|
71
49
|
```
|
|
72
50
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```powershell
|
|
76
|
-
.\.venv\Scripts\sem2surface-gui.exe
|
|
77
|
-
```
|
|
51
|
+
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
52
|
+
users can install `python3-tk` with their system package manager.
|
|
78
53
|
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
A virtual environment is optional. It is useful when the system Python is
|
|
55
|
+
externally managed, installation permissions are restricted, or other packages
|
|
56
|
+
have conflicting dependencies:
|
|
81
57
|
|
|
82
|
-
```
|
|
83
|
-
|
|
58
|
+
```bash
|
|
59
|
+
python3 -m venv .venv
|
|
60
|
+
source .venv/bin/activate # Windows PowerShell: .\.venv\Scripts\Activate.ps1
|
|
61
|
+
python -m pip install sem2surface
|
|
84
62
|
```
|
|
85
63
|
|
|
86
|
-
Use `deactivate` to leave the environment on any platform. Do not copy a
|
|
87
|
-
virtual environment between computers or move it after creation; create a new
|
|
88
|
-
one and reinstall the package instead.
|
|
89
|
-
|
|
90
64
|
VTK export is optional because VTK is a large dependency:
|
|
91
65
|
|
|
92
66
|
```bash
|
|
@@ -173,6 +147,12 @@ decomposition, Radon search, and oriented gradients.
|
|
|
173
147
|
When timestamps are disabled, an existing output with the same name is replaced.
|
|
174
148
|
Choose a dedicated output folder or enable timestamps when results must be kept.
|
|
175
149
|
|
|
150
|
+
For TIFF acquisitions, `sem2surface` automatically removes a microscope
|
|
151
|
+
annotation footer when it detects a nearly uniform bright separator followed by
|
|
152
|
+
a statistically distinct lower band. The detector works with both 8-bit Zeiss
|
|
153
|
+
and 16-bit FEI exports, leaves footer-free images unchanged, and records every
|
|
154
|
+
detected crop in the processing log.
|
|
155
|
+
|
|
176
156
|
## Reference examples and scaling
|
|
177
157
|
|
|
178
158
|
The [examples directory](https://github.com/vyastreb/sem2surface/tree/master/examples)
|
|
@@ -193,6 +173,15 @@ python examples/Surface_1/test_without_gui.py
|
|
|
193
173
|
<!-- PyPI cannot resolve repository-relative images. Keep this absolute URL. -->
|
|
194
174
|

|
|
195
175
|
|
|
176
|
+
## Changes in 0.2.2
|
|
177
|
+
|
|
178
|
+
- Replaced the historical hard-coded footer threshold with bit-depth-independent
|
|
179
|
+
SEM annotation-band detection.
|
|
180
|
+
- Added safeguards against cropping isolated bright lines in specimen data.
|
|
181
|
+
- Added per-image logging of detected footers and removed row counts.
|
|
182
|
+
- Added regression coverage for 8-bit Zeiss, 16-bit FEI, footer-free, and
|
|
183
|
+
isolated-line images.
|
|
184
|
+
|
|
196
185
|
## Development
|
|
197
186
|
|
|
198
187
|
Create an isolated environment and install the editable project with its test
|
|
@@ -227,7 +216,7 @@ For a first trial, create a separate account and API token on
|
|
|
227
216
|
version:
|
|
228
217
|
|
|
229
218
|
```bash
|
|
230
|
-
python -m twine upload --repository testpypi dist/sem2surface-0.2.
|
|
219
|
+
python -m twine upload --repository testpypi dist/sem2surface-0.2.2*
|
|
231
220
|
```
|
|
232
221
|
|
|
233
222
|
When prompted, use `__token__` as the username and the complete TestPyPI token,
|
|
@@ -235,7 +224,7 @@ including its `pypi-` prefix, as the password. Test the uploaded wheel in a new
|
|
|
235
224
|
environment without resolving dependencies from TestPyPI:
|
|
236
225
|
|
|
237
226
|
```bash
|
|
238
|
-
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.
|
|
227
|
+
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.2
|
|
239
228
|
sem2surface --version
|
|
240
229
|
```
|
|
241
230
|
|
|
@@ -243,7 +232,7 @@ For the real release, create a PyPI account and API token at
|
|
|
243
232
|
[pypi.org](https://pypi.org/), then run:
|
|
244
233
|
|
|
245
234
|
```bash
|
|
246
|
-
python -m twine upload dist/sem2surface-0.2.
|
|
235
|
+
python -m twine upload dist/sem2surface-0.2.2*
|
|
247
236
|
```
|
|
248
237
|
|
|
249
238
|
PyPI and TestPyPI use separate accounts and tokens. Never commit a token or put
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sem2surface
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 3D surface reconstruction from multi-detector SEM/BSE images
|
|
5
5
|
Author: Vladislav A. Yastrebov
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -39,6 +39,9 @@ Dynamic: license-file
|
|
|
39
39
|
|
|
40
40
|
# sem2surface
|
|
41
41
|
|
|
42
|
+
[](https://pypi.org/project/sem2surface/)
|
|
43
|
+
[](https://github.com/vyastreb/sem2surface/blob/master/LICENSE)
|
|
44
|
+
|
|
42
45
|
`sem2surface` reconstructs a three-dimensional surface from three to five
|
|
43
46
|
multi-detector SEM/BSE images. It extracts two normalized principal images,
|
|
44
47
|
identifies their orientation with a Radon transform, and integrates the resulting
|
|
@@ -54,78 +57,49 @@ calibration procedure.
|
|
|
54
57
|
|
|
55
58
|
## Installation
|
|
56
59
|
|
|
57
|
-
Python 3.10 or newer is required.
|
|
58
|
-
recommended so the application does not conflict with system Python packages.
|
|
59
|
-
|
|
60
|
-
### Linux and macOS
|
|
61
|
-
|
|
62
|
-
Create an environment and install a released version:
|
|
60
|
+
Python 3.10 or newer is required. Install `sem2surface` from PyPI with:
|
|
63
61
|
|
|
64
62
|
```bash
|
|
65
|
-
|
|
66
|
-
source .venv/bin/activate
|
|
67
|
-
python -m pip install --upgrade pip
|
|
68
|
-
python -m pip install sem2surface
|
|
69
|
-
sem2surface-gui
|
|
63
|
+
pip install sem2surface
|
|
70
64
|
```
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
this source checkout instead:
|
|
66
|
+
If several Python installations are present, use the interpreter explicitly:
|
|
74
67
|
|
|
75
68
|
```bash
|
|
76
|
-
|
|
69
|
+
python3 -m pip install sem2surface # Linux or macOS
|
|
77
70
|
```
|
|
78
71
|
|
|
79
|
-
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
80
|
-
users can install it with their system package manager as `python3-tk` before
|
|
81
|
-
creating the environment.
|
|
82
|
-
|
|
83
72
|
### Windows
|
|
84
73
|
|
|
85
74
|
Install Python 3.10 or newer from
|
|
86
75
|
[python.org](https://www.python.org/downloads/windows/). Keep the standard
|
|
87
|
-
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell
|
|
88
|
-
|
|
76
|
+
`pip`, Tcl/Tk, and Python Launcher components enabled. Then open PowerShell or
|
|
77
|
+
Command Prompt and run:
|
|
89
78
|
|
|
90
79
|
```powershell
|
|
91
|
-
py -
|
|
92
|
-
.\.venv\Scripts\Activate.ps1
|
|
93
|
-
python -m pip install --upgrade pip
|
|
94
|
-
python -m pip install sem2surface
|
|
80
|
+
py -m pip install sem2surface
|
|
95
81
|
sem2surface-gui
|
|
96
82
|
```
|
|
97
83
|
|
|
98
|
-
If
|
|
99
|
-
PowerShell process and retry activation:
|
|
84
|
+
If Windows cannot find the installed launcher, start the GUI through Python:
|
|
100
85
|
|
|
101
86
|
```powershell
|
|
102
|
-
|
|
103
|
-
.\.venv\Scripts\Activate.ps1
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
In Command Prompt, activate the same environment with:
|
|
107
|
-
|
|
108
|
-
```bat
|
|
109
|
-
.venv\Scripts\activate.bat
|
|
87
|
+
py -m sem2surface_gui
|
|
110
88
|
```
|
|
111
89
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
```powershell
|
|
115
|
-
.\.venv\Scripts\sem2surface-gui.exe
|
|
116
|
-
```
|
|
90
|
+
On Linux, Tkinter may be packaged separately. For example, Ubuntu and Debian
|
|
91
|
+
users can install `python3-tk` with their system package manager.
|
|
117
92
|
|
|
118
|
-
|
|
119
|
-
|
|
93
|
+
A virtual environment is optional. It is useful when the system Python is
|
|
94
|
+
externally managed, installation permissions are restricted, or other packages
|
|
95
|
+
have conflicting dependencies:
|
|
120
96
|
|
|
121
|
-
```
|
|
122
|
-
|
|
97
|
+
```bash
|
|
98
|
+
python3 -m venv .venv
|
|
99
|
+
source .venv/bin/activate # Windows PowerShell: .\.venv\Scripts\Activate.ps1
|
|
100
|
+
python -m pip install sem2surface
|
|
123
101
|
```
|
|
124
102
|
|
|
125
|
-
Use `deactivate` to leave the environment on any platform. Do not copy a
|
|
126
|
-
virtual environment between computers or move it after creation; create a new
|
|
127
|
-
one and reinstall the package instead.
|
|
128
|
-
|
|
129
103
|
VTK export is optional because VTK is a large dependency:
|
|
130
104
|
|
|
131
105
|
```bash
|
|
@@ -212,6 +186,12 @@ decomposition, Radon search, and oriented gradients.
|
|
|
212
186
|
When timestamps are disabled, an existing output with the same name is replaced.
|
|
213
187
|
Choose a dedicated output folder or enable timestamps when results must be kept.
|
|
214
188
|
|
|
189
|
+
For TIFF acquisitions, `sem2surface` automatically removes a microscope
|
|
190
|
+
annotation footer when it detects a nearly uniform bright separator followed by
|
|
191
|
+
a statistically distinct lower band. The detector works with both 8-bit Zeiss
|
|
192
|
+
and 16-bit FEI exports, leaves footer-free images unchanged, and records every
|
|
193
|
+
detected crop in the processing log.
|
|
194
|
+
|
|
215
195
|
## Reference examples and scaling
|
|
216
196
|
|
|
217
197
|
The [examples directory](https://github.com/vyastreb/sem2surface/tree/master/examples)
|
|
@@ -232,6 +212,15 @@ python examples/Surface_1/test_without_gui.py
|
|
|
232
212
|
<!-- PyPI cannot resolve repository-relative images. Keep this absolute URL. -->
|
|
233
213
|

|
|
234
214
|
|
|
215
|
+
## Changes in 0.2.2
|
|
216
|
+
|
|
217
|
+
- Replaced the historical hard-coded footer threshold with bit-depth-independent
|
|
218
|
+
SEM annotation-band detection.
|
|
219
|
+
- Added safeguards against cropping isolated bright lines in specimen data.
|
|
220
|
+
- Added per-image logging of detected footers and removed row counts.
|
|
221
|
+
- Added regression coverage for 8-bit Zeiss, 16-bit FEI, footer-free, and
|
|
222
|
+
isolated-line images.
|
|
223
|
+
|
|
235
224
|
## Development
|
|
236
225
|
|
|
237
226
|
Create an isolated environment and install the editable project with its test
|
|
@@ -266,7 +255,7 @@ For a first trial, create a separate account and API token on
|
|
|
266
255
|
version:
|
|
267
256
|
|
|
268
257
|
```bash
|
|
269
|
-
python -m twine upload --repository testpypi dist/sem2surface-0.2.
|
|
258
|
+
python -m twine upload --repository testpypi dist/sem2surface-0.2.2*
|
|
270
259
|
```
|
|
271
260
|
|
|
272
261
|
When prompted, use `__token__` as the username and the complete TestPyPI token,
|
|
@@ -274,7 +263,7 @@ including its `pypi-` prefix, as the password. Test the uploaded wheel in a new
|
|
|
274
263
|
environment without resolving dependencies from TestPyPI:
|
|
275
264
|
|
|
276
265
|
```bash
|
|
277
|
-
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.
|
|
266
|
+
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sem2surface==0.2.2
|
|
278
267
|
sem2surface --version
|
|
279
268
|
```
|
|
280
269
|
|
|
@@ -282,7 +271,7 @@ For the real release, create a PyPI account and API token at
|
|
|
282
271
|
[pypi.org](https://pypi.org/), then run:
|
|
283
272
|
|
|
284
273
|
```bash
|
|
285
|
-
python -m twine upload dist/sem2surface-0.2.
|
|
274
|
+
python -m twine upload dist/sem2surface-0.2.2*
|
|
286
275
|
```
|
|
287
276
|
|
|
288
277
|
PyPI and TestPyPI use separate accounts and tokens. Never commit a token or put
|
|
@@ -23,7 +23,7 @@ from scipy.ndimage import gaussian_filter
|
|
|
23
23
|
from skimage.transform import radon
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
__version__ = "0.2.
|
|
26
|
+
__version__ = "0.2.2"
|
|
27
27
|
DEFAULT_PIXEL_SIZE = 1e-6
|
|
28
28
|
_PIXEL_WIDTH_TAGS = ("PixelWidth=", "Image Pixel Size =")
|
|
29
29
|
|
|
@@ -261,6 +261,49 @@ def _read_image(path: Path) -> np.ndarray:
|
|
|
261
261
|
return np.asarray(convert_to_grayscale(array), dtype=np.float64)
|
|
262
262
|
|
|
263
263
|
|
|
264
|
+
def _find_sem_data_rows(image: np.ndarray) -> int:
|
|
265
|
+
"""Return the number of rows above a SEM annotation footer.
|
|
266
|
+
|
|
267
|
+
FEI and Zeiss exports use different bit depths and footer layouts, but both
|
|
268
|
+
place a nearly uniform bright separator near the bottom of the image. A
|
|
269
|
+
separator is accepted only when the pixels below it have a substantially
|
|
270
|
+
different median intensity, which avoids treating an isolated bright line
|
|
271
|
+
in the specimen as a footer.
|
|
272
|
+
"""
|
|
273
|
+
array = np.asarray(image, dtype=np.float64)
|
|
274
|
+
if array.ndim != 2:
|
|
275
|
+
raise ValueError("Footer detection requires a two-dimensional image")
|
|
276
|
+
rows, columns = array.shape
|
|
277
|
+
if rows < 16 or columns < 16:
|
|
278
|
+
return rows
|
|
279
|
+
|
|
280
|
+
finite = array[np.isfinite(array)]
|
|
281
|
+
if finite.size == 0:
|
|
282
|
+
return rows
|
|
283
|
+
low, high = np.percentile(finite, (0.1, 99.9))
|
|
284
|
+
intensity_span = float(high - low)
|
|
285
|
+
if not np.isfinite(intensity_span) or intensity_span <= 0:
|
|
286
|
+
return rows
|
|
287
|
+
|
|
288
|
+
# The 3% tolerance includes FEI separator values (64512) when a few
|
|
289
|
+
# saturated specimen pixels raise the 99.9th percentile to 65535.
|
|
290
|
+
bright_threshold = high - 0.03 * intensity_span
|
|
291
|
+
bright_fraction = np.mean(array >= bright_threshold, axis=1)
|
|
292
|
+
first_candidate = max(1, int(np.ceil(0.5 * rows)))
|
|
293
|
+
minimum_footer_rows = max(4, int(np.ceil(0.01 * rows)))
|
|
294
|
+
candidate_stop = rows - minimum_footer_rows + 1
|
|
295
|
+
|
|
296
|
+
for row in range(first_candidate, candidate_stop):
|
|
297
|
+
if bright_fraction[row] < 0.98 or bright_fraction[row - 1] >= 0.5:
|
|
298
|
+
continue
|
|
299
|
+
preceding_rows = min(64, row)
|
|
300
|
+
specimen_median = float(np.nanmedian(array[row - preceding_rows : row]))
|
|
301
|
+
footer_median = float(np.nanmedian(array[row:]))
|
|
302
|
+
if abs(footer_median - specimen_median) >= 0.10 * intensity_span:
|
|
303
|
+
return row
|
|
304
|
+
return rows
|
|
305
|
+
|
|
306
|
+
|
|
264
307
|
def _plot_image_decomposition(
|
|
265
308
|
imgs: np.ndarray,
|
|
266
309
|
intensity: np.ndarray,
|
|
@@ -537,16 +580,16 @@ def _construct_surface(
|
|
|
537
580
|
if len(widths) != 1:
|
|
538
581
|
raise ValueError("All detector images must have the same width")
|
|
539
582
|
|
|
540
|
-
#
|
|
541
|
-
#
|
|
542
|
-
crop_rows
|
|
543
|
-
for image in images:
|
|
544
|
-
crop
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
583
|
+
# Detect each footer independently, then apply the smallest common crop so
|
|
584
|
+
# all detector images retain identical dimensions.
|
|
585
|
+
crop_rows = [_find_sem_data_rows(image) for image in images]
|
|
586
|
+
for path, image, crop in zip(paths, images, crop_rows):
|
|
587
|
+
if crop < image.shape[0]:
|
|
588
|
+
log(
|
|
589
|
+
log_file,
|
|
590
|
+
f"SEM annotation footer detected in {path.name}: "
|
|
591
|
+
f"removed {image.shape[0] - crop} rows",
|
|
592
|
+
)
|
|
550
593
|
cut_y = min(crop_rows)
|
|
551
594
|
if cut_y < 2:
|
|
552
595
|
raise ValueError("Automatic footer detection left too little image data")
|
|
@@ -3,15 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
from typing import Sequence
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
try:
|
|
12
|
-
VERSION = version("sem2surface")
|
|
13
|
-
except PackageNotFoundError: # Running directly from an unpacked source tree.
|
|
14
|
-
VERSION = "0.2.0"
|
|
9
|
+
from sem2surface import __version__ as VERSION
|
|
15
10
|
|
|
16
11
|
|
|
17
12
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -8,7 +8,7 @@ def test_cli_exposes_version(capsys):
|
|
|
8
8
|
with pytest.raises(SystemExit) as raised:
|
|
9
9
|
parser.parse_args(["--version"])
|
|
10
10
|
assert raised.value.code == 0
|
|
11
|
-
assert "0.2.
|
|
11
|
+
assert "0.2.2" in capsys.readouterr().out
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def test_cli_requires_three_images(capsys):
|
|
@@ -4,6 +4,7 @@ import numpy as np
|
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
6
|
from sem2surface import (
|
|
7
|
+
_find_sem_data_rows,
|
|
7
8
|
compute_image_gradients,
|
|
8
9
|
construct_surface,
|
|
9
10
|
convert_to_grayscale,
|
|
@@ -58,3 +59,40 @@ def test_caller_owned_log_is_not_closed():
|
|
|
58
59
|
with pytest.raises(ValueError):
|
|
59
60
|
construct_surface([], log_file=stream)
|
|
60
61
|
assert not stream.closed
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_footer_detection_handles_fei_16_bit_separator():
|
|
65
|
+
rng = np.random.default_rng(3)
|
|
66
|
+
image = rng.integers(28000, 48000, size=(80, 64), dtype=np.uint16)
|
|
67
|
+
image.flat[:12] = 65535 # Saturated specimen pixels above the separator.
|
|
68
|
+
image[64] = 64512
|
|
69
|
+
image[65:] = 1024
|
|
70
|
+
image[68:76:3, 5:59:7] = 64512
|
|
71
|
+
image[-1] = 64512
|
|
72
|
+
|
|
73
|
+
assert _find_sem_data_rows(image) == 64
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_footer_detection_handles_zeiss_8_bit_band():
|
|
77
|
+
rng = np.random.default_rng(5)
|
|
78
|
+
image = rng.integers(90, 190, size=(120, 64), dtype=np.uint8)
|
|
79
|
+
image[101] = 0
|
|
80
|
+
image[102:] = 255
|
|
81
|
+
image[105:117:3, 4:60:6] = 0
|
|
82
|
+
|
|
83
|
+
assert _find_sem_data_rows(image) == 102
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_footer_detection_leaves_footer_free_image_unchanged():
|
|
87
|
+
rng = np.random.default_rng(7)
|
|
88
|
+
image = rng.integers(40, 220, size=(120, 64), dtype=np.uint8)
|
|
89
|
+
|
|
90
|
+
assert _find_sem_data_rows(image) == image.shape[0]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_footer_detection_ignores_isolated_bright_specimen_line():
|
|
94
|
+
rng = np.random.default_rng(11)
|
|
95
|
+
image = rng.integers(80, 180, size=(120, 64), dtype=np.uint8)
|
|
96
|
+
image[75] = 255
|
|
97
|
+
|
|
98
|
+
assert _find_sem_data_rows(image) == image.shape[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|