A Python application for live laser-beam profiling with Basler cameras: spot detection, sub-pixel 2D Gaussian fits, beam-array (grid) statistics, an HTTP API and hardware sync outputs.
pip install -r requirements.txt
python -m beam_profiler # live camera (falls back to the simulator)
python -m beam_profiler --sim # simulated camera, no hardware needed
python pylon_camera.py # legacy entry point, same application--sim (or having no camera connected) renders a fixed grid of Gaussian spots
with realistic noise in real time, so every CV function — blob detection,
Gaussian fitting, row/column statistics, auto-exposure — can be exercised
without hardware:
python -m beam_profiler --sim --sim-grid 8x8 --sim-jitter 0 # static 8x8 grid
python -m beam_profiler --sim --sim-size 4096x4096 --sim-grid 80x80 # dense arrayThe spot sigma defaults to pitch/10 (beam waist = 1/5 of the spot spacing);
override with --sim-sigma <px>.
Fixed spot pictures with ground-truth JSON (for offline testing) are generated with:
python tools/make_test_images.py # writes test_images/*.png/.npy/.jsonbeam_profiler/
├── __main__.py CLI entry point
├── config.py paths, constants, camera_config.yaml loading
├── synthetic.py synthetic spot images (simulator, tests, tools)
├── roi.py ROI zoom model + sensor<->display mapping
├── cameras/ base.py (interface + auto-exposure), basler.py, simulated.py
├── processing/ blobs.py (detection + Gaussian fits), grid.py, stats.py
├── ui/ viewer.py (main loop, input), overlays.py (HUD, bars)
├── server.py Flask HTTP API
└── status.py pylon_camera.json mirror
Spot detection runs on a ≤1024 px downscale of the frame and refines each spot with a sub-pixel moment fit on a full-resolution crop, so even 25 MP sensors profile at interactive rates.
pip install pytest
pytestThe tests validate the CV pipeline against synthetic images with known ground truth (positions, widths, orientation, grid classification).
camera_config.yaml
cameras:
a2A5060-15umBAS: # must match the camera model name
default_roi: [5060, 5060, 4, 4] # [width, height, x_pad, y_pad]
pixel_size: 2.5e-6 # pixel size in meters, i.e. 2.5umFirst clone this repo and set up camera_config.yaml.
Check release to download the latest version: Releases
Put the pylon_camera.exe executable in the same directory as camera_config.yaml.
Saved frames (data/) and the live status file (pylon_camera.json) are written
next to the executable.
Esc: Quit the applicationarrow keys: up/down=change exposure by 10 times, left/right=change exposure by 10%a: toggle auto-exposure (auto exposure adjusts the exposure time to keep the max intensity to be near-saturated)f: toggle blob fittingg: toggle statistics showing (statistics of fitted blobs)h: toggle row/col statistics (works only for beam arrays)mouse drag: create a white rectangle in the canvasc: clear the white rectanglectrl + mouse drag: create a green rectangle in the canvas, only blobs inside the rectangle will be fittedv: clear the green rectangles: quick save the current frame (JPEG + raw.npy)d: save the current frame with dialogue box, allowing the user to choose the file location and namet: switch to the next camera (if multiple cameras are connected)o: open the spot-fitting settings window (crop size, re-crop stages, adaptive weighting, GPU backend)w: toggle the HTTP server (port 5000), together with the live pixel statistics of the white rectangle drawn on the canvasy: toggle the user-defined line output (Line3) used to sync external hardwaremouse wheel: zoom in/out the canvas (ctrl + wheel: change aspect ratio)
Press o for a settings window covering every parameter of the fit: the crop
size, how many times the crop is re-derived from the fitted width, whether to
use adaptive Gaussian weighting, and the CUDA backend. Changes take effect on
the next frame and persist to fit_config.json next to the app; the same
values are readable and writable over HTTP at /api/fit_config.
Each control explains itself in the help pane. The defaults are measured
optima rather than guesses. docs/fitting.md walks through
the fitting method step by step, and
docs/fitting-calibration.md records the sweeps
behind each default - the test suite re-runs those measurements.
Pressing w starts a small HTTP server on port 5000 that exposes the camera
status, the current frame, and the pixel statistics of the white / green
rectangles. Open http://localhost:5000 for the endpoint list, or see
docs/web_api.md for the full reference.
The current state (camera, exposure, ROI, rectangles) is also mirrored to
pylon_camera.json whenever it changes, so another process can read it without
starting the server.
The camera is armed with a software trigger, and drives two GPIO lines:
- Line2 - inverted
ExposureActive, i.e. it mirrors the exposure window. - Line3 - user-controlled output, toggled with
y. While enabled it is pulsed low for the duration of each acquisition.
Cameras that do not expose these lines (or the software trigger) log a warning at startup and keep running with that feature disabled.
