pybeamprofiler 0.0.1__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.
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ # Allow the 3.14 prerelease job to fail without blocking the overall status
13
+ continue-on-error: ${{ matrix.experimental == true }}
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+ experimental: [false]
19
+ include:
20
+ - python-version: "3.14"
21
+ experimental: true
22
+
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v7
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Set up Python ${{ matrix.python-version }}
32
+ run: uv python install ${{ matrix.python-version }}
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --locked --extra dev
36
+
37
+ - name: Lint with ruff
38
+ run: uv run ruff check src tests
39
+
40
+ - name: Format check with ruff
41
+ run: uv run ruff format --check src tests
42
+
43
+ - name: Type check with ty
44
+ run: uv run ty check src tests
45
+
46
+ - name: Test with pytest
47
+ run: uv run pytest tests/ --cov=pybeamprofiler --cov-report=term-missing
@@ -0,0 +1,156 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+
62
+ # Flask stuff:
63
+ instance/
64
+ .webassets-cache
65
+
66
+ # Scrapy stuff:
67
+ .scrapy
68
+
69
+ # Sphinx documentation
70
+ docs/_build/
71
+
72
+ # PyBuilder
73
+ .pybuilder/
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+ *-checkpoint.ipynb
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ Pipfile.lock
89
+
90
+ # poetry
91
+ poetry.lock
92
+
93
+ # pdm
94
+ .pdm.toml
95
+
96
+ # PEP 582
97
+ __pypackages__/
98
+
99
+ # Celery stuff
100
+ celerybeat-schedule
101
+ celerybeat.pid
102
+
103
+ # SageMath parsed files
104
+ *.sage.py
105
+
106
+ # Environments
107
+ .env
108
+ .venv
109
+ env/
110
+ venv/
111
+ ENV/
112
+ env.bak/
113
+ venv.bak/
114
+
115
+ # Spyder project settings
116
+ .spyderproject
117
+ .spyproject
118
+
119
+ # Rope project settings
120
+ .ropeproject
121
+
122
+ # mkdocs documentation
123
+ /site
124
+
125
+ # mypy
126
+ .mypy_cache/
127
+ .dmypy.json
128
+ dmypy.json
129
+
130
+ # Pyre type checker
131
+ .pyre/
132
+
133
+ # pytype static type analyzer
134
+ .pytype/
135
+
136
+ # Cython debug symbols
137
+ cython_debug/
138
+
139
+ # IDEs
140
+ .vscode/
141
+ .idea/
142
+ *.swp
143
+ *.swo
144
+ *~
145
+ .DS_Store
146
+
147
+ # Project specific
148
+ summary/
149
+
150
+ # Output files
151
+ *.png
152
+ *.jpg
153
+ *.jpeg
154
+ *.bmp
155
+ *.tif
156
+ *.tiff
@@ -0,0 +1,33 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-merge-conflict
6
+ - id: check-json
7
+ - id: check-yaml
8
+ args: [--unsafe]
9
+ - id: end-of-file-fixer
10
+ - id: no-commit-to-branch
11
+ - id: sort-simple-yaml
12
+ - id: trailing-whitespace
13
+
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ rev: v0.14.0
16
+ hooks:
17
+ - id: ruff-format
18
+ - id: ruff
19
+ args: [
20
+ --fix,
21
+ --exit-non-zero-on-fix,
22
+ --config,
23
+ 'lint.extend-fixable = ["F401","F841"]',
24
+ ]
25
+
26
+ - repo: local
27
+ hooks:
28
+ - id: ty
29
+ name: ty type check
30
+ entry: uv run ty check src tests
31
+ language: system
32
+ types_or: [python, pyi]
33
+ pass_filenames: false
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C.-A. Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,457 @@
1
+ Metadata-Version: 2.4
2
+ Name: pybeamprofiler
3
+ Version: 0.0.1
4
+ Summary: Real-time laser beam profiler with Gaussian fitting for GenICam cameras
5
+ Project-URL: Homepage, https://github.com/acechenx/pybeamprofiler
6
+ Project-URL: Bug Tracker, https://github.com/acechenx/pybeamprofiler/issues
7
+ Author-email: "C.-A. Chen" <acechen@cirx.org>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 C.-A. Chen
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 all
20
+ 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 THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: basler,beam-profiling,camera,flir,gaussian-fitting,genicam,laser
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Intended Audience :: Science/Research
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Programming Language :: Python :: 3.13
40
+ Classifier: Programming Language :: Python :: 3.14
41
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
42
+ Classifier: Topic :: Scientific/Engineering :: Physics
43
+ Requires-Python: >=3.10
44
+ Requires-Dist: dash[async]
45
+ Requires-Dist: harvesters>=1.4.0
46
+ Requires-Dist: ipywidgets
47
+ Requires-Dist: numpy
48
+ Requires-Dist: pillow
49
+ Requires-Dist: plotly
50
+ Requires-Dist: scipy
51
+ Provides-Extra: dev
52
+ Requires-Dist: pre-commit>=4.5.0; extra == 'dev'
53
+ Requires-Dist: pytest-cov>=7.0; extra == 'dev'
54
+ Requires-Dist: pytest>=9.0; extra == 'dev'
55
+ Requires-Dist: ruff>=0.15.0; extra == 'dev'
56
+ Requires-Dist: ty>=0.0.25; extra == 'dev'
57
+ Provides-Extra: matplotlib
58
+ Requires-Dist: matplotlib>=3.0; extra == 'matplotlib'
59
+ Provides-Extra: test
60
+ Requires-Dist: pytest-cov>=7.0; extra == 'test'
61
+ Requires-Dist: pytest>=9.0; extra == 'test'
62
+ Description-Content-Type: text/markdown
63
+
64
+ # pyBeamprofiler
65
+
66
+ Real-time laser beam profiler with Gaussian fitting for GenICam cameras.
67
+
68
+ [![Python 3.10–3.14](https://img.shields.io/badge/python-3.10--3.14-blue.svg)](https://www.python.org/downloads/)
69
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
70
+
71
+ ## Features
72
+
73
+ * **Fast Gaussian fitting:** 850+ fps for 1D, 95 fps for 2D
74
+ * **Live streaming:** Dash web interface (10 Hz) or Jupyter notebooks (6-10 Hz)
75
+ * **Multiple fitting methods:** 1D projections, 2D Gaussian, linecut
76
+ * **Multiple width definitions:** Gaussian (1/e²), FWHM, D4σ (ISO 11146)
77
+ * **Interactive controls:** Jupyter widgets for camera settings
78
+ * **Flexible inputs:** Static images or camera streams
79
+ * **Hardware support:** FLIR, Basler cameras via GenICam/[Harvesters](https://github.com/genicam/harvesters)
80
+ * **Simulated camera:** Included for testing without hardware
81
+ * **Auto-configuration:** Pixel size detection, auto-exposure/gain disabled by default
82
+ * **ROI support:** Region of Interest with full sensor default
83
+
84
+ ## Quick Start
85
+
86
+ ```bash
87
+ # Install (creates/updates a reproducible environment from the lockfile)
88
+ uv sync
89
+
90
+ # Run with simulated camera (no hardware needed)
91
+ uv run pybeamprofiler --camera simulated
92
+
93
+ # Browser opens automatically at http://127.0.0.1:8050
94
+ ```
95
+
96
+ ## Installation
97
+
98
+ ### Basic Installation
99
+
100
+ ```bash
101
+ uv sync
102
+ ```
103
+
104
+ `uv sync` installs the exact versions recorded in `uv.lock`, giving you a reproducible environment. This is the recommended path for both users and CI.
105
+
106
+ > **Installing into an existing environment?** If you are managing your own virtualenv or conda environment and do not want to use the lockfile, you can run `uv pip install .` (or plain `pip install .`) instead. Be aware that this resolves dependencies independently and may produce a different set of package versions than the lockfile.
107
+
108
+ ### Development Installation
109
+
110
+ ```bash
111
+ uv sync --extra dev
112
+ pre-commit install # Optional: enable git hooks
113
+ ```
114
+
115
+ ### Optional Dependencies
116
+
117
+ ```bash
118
+ uv sync --extra matplotlib # Matplotlib fallback for CLI
119
+ uv sync --extra test # Testing tools only
120
+ uv sync --extra dev # All development tools
121
+ ```
122
+
123
+ ## Usage
124
+
125
+ ### Command Line Interface
126
+
127
+ ```bash
128
+ # Simulated camera (no hardware needed)
129
+ pybeamprofiler --camera simulated
130
+
131
+ # Real cameras (requires SDK installation)
132
+ pybeamprofiler --camera flir # FLIR/Spinnaker
133
+ pybeamprofiler --camera basler # Basler/Pylon
134
+
135
+ # Single shot acquisition
136
+ pybeamprofiler --num-img 1
137
+
138
+ # Static image analysis
139
+ pybeamprofiler --file beam.png
140
+
141
+ # Custom fitting and definitions
142
+ pybeamprofiler --fit 2d --definition fwhm
143
+
144
+ # Set exposure time (in seconds)
145
+ pybeamprofiler --exposure-time 0.05
146
+
147
+ # Fast display mode (heatmap only)
148
+ pybeamprofiler --heatmap-only
149
+
150
+ # See all options
151
+ pybeamprofiler --help
152
+ ```
153
+
154
+ > **Tip:** If you installed via `uv sync`, prefix commands with `uv run` (e.g., `uv run pybeamprofiler --help`).
155
+ > You can also use `python -m pybeamprofiler` as an alternative.
156
+
157
+ **Continuous streaming** automatically opens a browser with live beam profile and fitting results at http://127.0.0.1:8050.
158
+
159
+ ### Python API
160
+
161
+ #### Basic Usage
162
+
163
+ ```python
164
+ from pybeamprofiler import BeamProfiler
165
+
166
+ # Initialize with simulated camera
167
+ bp = BeamProfiler(camera="simulated")
168
+ bp.plot() # Opens Dash web interface
169
+
170
+ # For real hardware
171
+ bp = BeamProfiler(camera="flir") # or camera="basler"
172
+ bp.plot()
173
+ ```
174
+
175
+ #### Single Measurement
176
+
177
+ ```python
178
+ from pybeamprofiler import BeamProfiler
179
+
180
+ bp = BeamProfiler(camera="simulated")
181
+ bp.plot(num_img=1)
182
+
183
+ # Access results
184
+ print(f"Beam width: {bp.width:.1f} μm")
185
+ print(f"Width X: {bp.width_x:.1f} μm, Y: {bp.width_y:.1f} μm")
186
+ print(f"Center: ({bp.center_x:.1f}, {bp.center_y:.1f}) pixels")
187
+ print(f"Peak intensity: {bp.peak_value:.0f}")
188
+ ```
189
+
190
+ #### Static Image Analysis
191
+
192
+ ```python
193
+ from pybeamprofiler import BeamProfiler
194
+
195
+ bp = BeamProfiler(file="beam_image.png")
196
+ bp.plot(num_img=1)
197
+
198
+ print(f"Width X: {bp.width_x:.1f} μm")
199
+ print(f"Width Y: {bp.width_y:.1f} μm")
200
+ ```
201
+
202
+ #### Camera Control
203
+
204
+ ```python
205
+ # List available cameras
206
+ from pybeamprofiler import print_camera_info
207
+ print_camera_info()
208
+
209
+ # Set exposure time (three ways)
210
+ bp = BeamProfiler(camera="simulated", exposure_time=0.05) # 1. During init
211
+
212
+ bp.exposure_time = 0.01 # 2. Direct attribute
213
+
214
+ bp.setting(exposure_time=0.05) # 3. Via setting() method
215
+
216
+ # Set multiple parameters
217
+ bp.setting(
218
+ exposure_time=0.025,
219
+ Gain=10.0,
220
+ ExposureAuto=False,
221
+ GammaEnable=False
222
+ )
223
+
224
+ # Interactive widget (Jupyter only)
225
+ bp.setting() # Opens interactive control panel
226
+ ```
227
+
228
+ #### Fitting Methods
229
+
230
+ ```python
231
+ # 1D Gaussian fitting (fastest, 850+ fps)
232
+ bp.fit_method = '1d'
233
+ bp.plot(num_img=1)
234
+
235
+ # 2D Gaussian fitting with rotation
236
+ bp.fit_method = '2d'
237
+ bp.plot(num_img=1)
238
+ print(f"Rotation angle: {bp.angle_deg:.1f}°")
239
+
240
+ # Linecut through peak
241
+ bp.fit_method = 'linecut'
242
+ bp.plot(num_img=1)
243
+ ```
244
+
245
+ #### Width Definitions
246
+
247
+ ```python
248
+ # Gaussian (1/e²) - standard laser beam width
249
+ bp.definition = 'gaussian'
250
+ bp.plot(num_img=1)
251
+
252
+ # Full Width at Half Maximum
253
+ bp.definition = 'fwhm'
254
+ bp.plot(num_img=1)
255
+
256
+ # D4σ (ISO 11146 second moment)
257
+ bp.definition = 'd4s'
258
+ bp.plot(num_img=1)
259
+
260
+ # Access all width metrics
261
+ print(f"1/e² width: {bp.fw_1e2_x:.1f} μm")
262
+ print(f"FWHM: {bp.fwhm_x:.1f} μm")
263
+ ```
264
+
265
+ #### Region of Interest (ROI)
266
+
267
+ ```python
268
+ # Get ROI info
269
+ roi = bp.camera.roi_info
270
+ print(f"ROI: {roi['width']}×{roi['height']} at ({roi['offset_x']}, {roi['offset_y']})")
271
+
272
+ # Set ROI (GenICam cameras only)
273
+ bp.camera.set_roi(offset_x=100, offset_y=100, width=800, height=600)
274
+
275
+ # Reset to full sensor
276
+ bp.camera.set_roi(offset_x=0, offset_y=0, width=None, height=None)
277
+ ```
278
+
279
+ ## Jupyter Notebook
280
+
281
+ pyBeamprofiler works natively in Jupyter notebooks with interactive widgets:
282
+ - Camera discovery and initialization
283
+ - Interactive controls with widgets (`bp.setting()`)
284
+ - Single-shot and continuous acquisition
285
+ - Multiple fitting methods and width definitions
286
+ - Programmatic camera control
287
+
288
+ ## Hardware Setup
289
+
290
+ ### FLIR Cameras ([Spinnaker SDK](https://www.teledynevisionsolutions.com/products/spinnaker-sdk/))
291
+
292
+ **macOS/Linux:**
293
+ ```bash
294
+ # Install Spinnaker SDK from FLIR website
295
+ # Then set environment variable
296
+ export GENICAM_GENTL64_PATH=/usr/local/lib/spinnaker-gentl
297
+ ```
298
+
299
+ **Windows:**
300
+ ```cmd
301
+ set GENICAM_GENTL64_PATH=C:\Program Files\FLIR Systems\Spinnaker\cti64\vs2015
302
+ ```
303
+
304
+ ### Basler Cameras ([Pylon SDK](https://www.baslerweb.com/en-us/software/pylon/sdk/))
305
+
306
+ **macOS:**
307
+ ```bash
308
+ export GENICAM_GENTL64_PATH=/Library/Frameworks/pylon.framework/Libraries/gentlproducer/gtl
309
+ ```
310
+
311
+ **Linux:**
312
+ ```bash
313
+ export GENICAM_GENTL64_PATH=/opt/pylon/lib64/gentlproducer/gtl
314
+ ```
315
+
316
+ **Windows:**
317
+ ```cmd
318
+ set GENICAM_GENTL64_PATH=C:\Program Files\Basler\pylon\Runtime\x64
319
+ ```
320
+
321
+ **Note:** GenICam cameras can only be accessed by one application at a time. Close other camera software before using pyBeamprofiler.
322
+
323
+ ## Supported Cameras
324
+
325
+ ### FLIR
326
+ - Blackfly S (BFS-PGE, BFS-U3, etc.)
327
+ - Grasshopper3 (GS3)
328
+ - Auto-detected sensors: Sony IMX273, IMX174, IMX183, IMX250, IMX252, etc.
329
+
330
+ ### Basler
331
+ - ace (acA series) - USB3, GigE
332
+ - ace 2 (a2A series)
333
+ - Auto-detected sensors: Sony IMX253, IMX226, IMX249, IMX255, etc.
334
+
335
+ ### Pixel Size Auto-Detection
336
+ Automatic pixel size detection for 40+ sensor models including:
337
+ - Sony IMX series (IMX174, IMX183, IMX226, IMX249, IMX250, IMX252, IMX253, IMX255, IMX264, IMX265, IMX273, IMX287, IMX290, IMX291, IMX304, IMX392, IMX412, IMX477, IMX485, IMX530, IMX531, IMX540, IMX541, IMX542, IMX547)
338
+ - Direct Basler model lookups (acA4024-8gm, acA4024-29um, acA1920-155um, acA2440-75um, acA3800-14um)
339
+
340
+ ## Performance
341
+
342
+ - **Gaussian fitting:** 850+ fps (1D), 95 fps (2D) - Not the bottleneck!
343
+ - **Display rates:**
344
+ - Jupyter notebook: 6-10 Hz (standard), 25-30 Hz (heatmap only)
345
+ - Dash web interface: 10 Hz
346
+ - Matplotlib fallback: ~5 Hz
347
+
348
+ ## Dependencies
349
+
350
+ **Core:**
351
+ - numpy, scipy - Numerical computing and optimization
352
+ - plotly, dash - Interactive visualization and web interface
353
+ - ipywidgets - Jupyter notebook controls
354
+ - Pillow - Image file loading
355
+ - harvesters - GenICam camera interface
356
+
357
+ **Optional:**
358
+ - matplotlib - Fallback plotting (CLI only)
359
+
360
+ **Development:**
361
+ - pytest, pytest-cov - Testing framework
362
+ - ruff - Fast linter and formatter
363
+ - ty - Static type checking
364
+ - pre-commit - Git hooks for code quality
365
+
366
+ ## Testing
367
+
368
+ ```bash
369
+ # Run all tests (coverage is enabled by default via pyproject.toml)
370
+ uv run pytest
371
+
372
+ # Run with HTML coverage report
373
+ uv run pytest --cov-report=html
374
+
375
+ # Run specific test file
376
+ uv run pytest tests/test_fitting.py -v
377
+ ```
378
+
379
+ ## Development
380
+
381
+ ```bash
382
+ # Install in development mode
383
+ uv sync --extra dev
384
+
385
+ # Install pre-commit hooks
386
+ pre-commit install
387
+
388
+ # Run linter
389
+ uv run ruff check src tests
390
+
391
+ # Run formatter
392
+ uv run ruff format src tests
393
+
394
+ # Run type checker
395
+ uv run ty check src tests
396
+ ```
397
+
398
+ ## Troubleshooting
399
+
400
+ ### Camera Not Found
401
+
402
+ 1. **Check SDK installation:**
403
+ - FLIR: Install [Spinnaker SDK](https://www.teledynevisionsolutions.com/products/spinnaker-sdk/)
404
+ - Basler: Install [Pylon SDK](https://www.baslerweb.com/en-us/software/pylon/sdk/)
405
+
406
+ 2. **Set GENICAM_GENTL64_PATH:**
407
+ ```bash
408
+ export GENICAM_GENTL64_PATH=/path/to/cti/files
409
+ ```
410
+
411
+ 3. **Check camera connection:**
412
+ ```python
413
+ from pybeamprofiler import print_camera_info
414
+ print_camera_info() # Lists all detected cameras
415
+ ```
416
+
417
+ 4. **Access denied error:**
418
+ - Close other camera software (Spinnaker GUI, Pylon Viewer, etc.)
419
+ - GenICam cameras allow only one connection at a time
420
+
421
+ ### GigE vs USB3
422
+ - Basler cameras: Code auto-detects and prefers GigE over USB3
423
+ - For USB3 cameras, explicitly pass the USB3 CTI file path:
424
+ ```python
425
+ from pybeamprofiler.basler import BaslerCamera
426
+ cam = BaslerCamera(cti_file="/path/to/ProducerU3V.cti")
427
+ ```
428
+
429
+ ### Jupyter Kernel Restart
430
+ When re-initializing cameras in Jupyter, restart the kernel first:
431
+ - Kernel → Restart Kernel
432
+ - This releases the camera hardware lock
433
+
434
+ ## License
435
+
436
+ MIT License - see [LICENSE](LICENSE) file for details.
437
+
438
+ ## Author
439
+
440
+ C.-A. Chen (acechen@cirx.org)
441
+
442
+ ## Contributing
443
+
444
+ Contributions welcome! Please:
445
+ 1. Fork the repository
446
+ 2. Create a feature branch
447
+ 3. Add tests for new functionality
448
+ 4. Ensure all tests pass: `uv run pytest`
449
+ 5. Run code quality checks: `uv run ruff check src tests` and `uv run ruff format src tests`
450
+ 6. Submit a pull request
451
+
452
+ ## Acknowledgments
453
+
454
+ - Built on [Harvesters](https://github.com/genicam/harvesters) for GenICam camera interface
455
+ - Uses [Plotly/Dash](https://plotly.com/dash/) for interactive visualization
456
+ - Inspired by various beam profiling tools: LaseView (old freeware version), [ptomato/Beams](https://github.com/ptomato/Beams), [jordens/bullseye](https://github.com/jordens/bullseye)
457
+ - FLIR and Basler cameras loaned from [Atom Computing](https://atom-computing.com/) for testing