A dockable strip of thumbnails for a handful of representative images, with
spots overlaid so a real pattern reads like a constellation; click opens that
image.
- JFJochViewerImageStrip: mode selector (evenly spaced / most spots / indexed,
computed from the dataset's per-image metrics) + a Spots toggle; a scrollable
row of thumbnail buttons; click emits imageSelected.
- The reading worker renders thumbnails off-thread and non-disruptively: load
each image via its reader, downsample by block-maximum (keeps Bragg spots),
map through the colour-scale LUT, optionally paint spots (coral indexed /
teal not), and emit thumbnailReady(image_number, QImage). File mode only.
- Docked in the bottom area, shown in the Processing perspective; the colour map
follows the display toolbar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- "Remove result" toolbar action drops a reprocessing run: reader RemoveSnapshot
(Original is protected; if the removed run was active the view falls back to
Original), worker RemoveRun, and the table row is removed.
- Opening a new file clears the jobs table (worker fileOpened -> clearJobs) to
match the reader, which already resets snapshots on ReadFile.
- Plot fixes: each run keeps a stable colour by its position (Original = blue),
so colours no longer swap when the active run changes; the current-image marker
is hidden from the legend (was the stray "[Image #N]" entry).
- The status bar shows processing rate (Hz) and estimated remaining time while a
job runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dataset-info plot now overlays every run as a separate named line instead of
replacing the plot when a snapshot is activated:
- Reader gains AllSnapshotDatasets() (every snapshot's dataset, Original first).
- The worker owns the run collection: a stable snapshot id plus an editable
display label (RunData), emitted as runsChanged(runs, active_id) on file open,
snapshot register, activate and rename. RenameRun(id, label) updates the legend
label without touching the reader key (decoupled id vs label).
- The chart view draws the active run as the primary series (markers, hover,
binning, axes) and the other runs as overlay lines sharing its axes, with a
legend shown when overlaying (appendSeries factored out for both).
- The dataset-info widget holds the run list + the live run, extracts the selected
metric from each (ExtractMetric), and unions the available metrics across runs.
- The live run is its own "Live" overlay (lightweight per-tick refresh, no combo
rebuild); it is cleared on finish so the persisted snapshot takes over.
- The processing jobs table gets a "Started" column and an editable Name column;
editing a name renames that run's legend label.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regressions from docking the processing panel + live plots:
- Default window size is now screen-aware (fits a laptop) instead of a fixed
1200x1200.
- Give the bottom dock area a guaranteed height (resizeDocks vertical) and a
minimum height on the dataset-info chart, so the plot and its axis labels stay
visible next to the processing panel.
- Activating a metadata snapshot (Show original / View results) no longer re-runs
full analysis: it re-reads the image against the snapshot's stored results with
reanalysis suppressed, instead of re-indexing on every switch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Processing settings:
- Split the spot/index settings widget into two tabs (Spot finding | Indexing)
via TakeSpotFindingPage()/TakeIndexingPage().
- Indexing tab now exposes the indexing algorithm (Auto/FFBIDX/FFT/FFTW/None) and
geometry refinement (None/Orientation/Beam center/Pixel refine) as radio groups
with short explanations.
- Fix: UpdateSpotFindingSettings dropped algorithm + geometry-refinement when
copying into indexing_settings (the geom checkbox was a no-op); both now flow
into jobs via curr_experiment.
Processing jobs window:
- Rotation indexing from the job dialog now also sets RotationIndexing(true) on
the experiment's IndexingSettings; otherwise IndexAndRefine builds no rotation
indexer and the two-pass pre-pass throws.
- Status cell shows a QProgressBar with "<done> / <expected>".
- Live results: JFJochProcessController::OnImageProcessed accumulates per-image
results into a JFJochReaderDataset and emits it throttled (~4 Hz) as liveDataset,
forwarded to the dataset-info plots so they update while a job runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One "Processing settings" window with tabs: Spot finding & indexing | Azimuthal | Bragg
integration | Scaling, replacing the two separate settings windows. The spot/index and azimuthal
tabs reuse the existing windows' widgets unchanged (their content is lifted into tabs via
takeCentralWidget), so all their logic/signals keep working; Bragg integration and scaling are new
editable panels (previously not adjustable in the GUI).
JFJochImageReadingWorker gains UpdateBraggIntegrationSettings / UpdateScalingSettings; both persist
as worker state and are re-imported into curr_experiment on file load / dataset update (like the
indexing/azint settings), so they apply to interactive analysis (Bragg) and flow into processing
jobs via GetReprocessingInputs (Bragg + scaling). Scaling only affects the job post-pass, so it is
just stored, not reanalyzed.
Verified: jfjoch_viewer builds and runs (offscreen) with the converged window.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Makes the viewer a processing frontend, not just an image viewer:
- JFJochProcessingJobsWindow (menu "Processing"): a table of processing jobs on the open
dataset. "New job" configures mode (full/azint), image count, threads, output, and (full)
rotation indexing + scale/merge, using the viewer's current processing settings. A job can
be Run locally (off the GUI thread via JFJochProcessController, with live status/progress and
Cancel) or its jfjoch_process command line copied for a cluster.
- A finished local run is registered as a reader metadata snapshot and becomes the active
dataset (bottom plots + per-image overlays follow it); "View results" / "Show original" switch
between runs - so several settings attempts on one dataset can be compared.
- JFJochImageReadingWorker gains GetReprocessingInputs() (one locked read of file + experiment +
mask + spot-finding settings, so jobs share the interactive settings) and
RegisterProcessingSnapshot/SetActiveSnapshot slots + snapshotsChanged signal over the reader's
snapshot API. Files only (not live HTTP).
First-cut / open ends: one job at a time; output lands in the working dir (FileWriter rejects
absolute prefixes); Bragg/scaling settings still use defaults until the converged settings
window lands. Viewer builds and runs (offscreen) cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restore the ROI-to-mask action on the new list: "Add to mask" and
"Subtract from mask" buttons rasterise the selected ROI into the user
mask (set or clear), through the same UpdateUserMask_i path. The ROI is
evaluated with per-pixel resolution and phi from the geometry, so box,
circle and azimuthal (sector) ROIs all map correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the scratch ROI panel gone, delete the now-orphaned
JFJochViewerImageROIStatistics(_Box/_Circle) widgets and the worker's
unused scratch-ROI plumbing (SetROIBox/SetROICircle, the ROIElement
member, AddROIToUserMask/SubtractROIFromUserMask). ROI-to-mask, if wanted
later, should act on the selected list ROI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two Mask-menu entries read a 16-bit TIFF (via ReadTIFFFromString16) and
apply it as the user mask: "replace" overwrites the current mask, "add"
unions the TIFF into it. The TIFF must match the detector dimensions; any
non-zero pixel masks. Both go through UpdateUserMask_i like the other
mask edits.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Download and "Upload to server" buttons to the ROI panel. The HTTP
reader gains GetROIDefinitions (GET /config/roi) and UploadROIDefinitions
(PUT /config/roi), converting between ROIDefinition and the generated
Roi_definitions model (including the optional azimuthal phi sector), the
same shapes OpenAPIConvert uses on the server. Download applies the
fetched ROIs through SetROIDefinition; upload pushes the current ones.
Both are no-ops unless the viewer is connected to a broker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Edited ROIs now override the file's for every subsequently loaded image
and survive settings changes, until a new file is opened (previously a
delete/add was lost on the next image). Tracked via an roi_override_ that
is re-applied to curr_experiment and to each loaded image's dataset.
- The ROI selector is a compact combobox instead of a list widget.
- "Show labels" and "Translucent fill" share one row to save space.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reading worker's experiment is the source of truth for ROIs. Add a
SetROIDefinition slot that updates curr_experiment.ROI(), rebuilds the
analysis ROI engine, mutates the dataset (so the canvas reflects the new
ROIs) and recomputes only the ROIs for the current image via RunROIOnly.
On image load when full re-analysis is off, ROIs are still computed via
AnalyzeROIOnly so the statistics stay current.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ROIAzimuthal can now be restricted to an azimuthal-angle sector in
addition to its Q/d range, enabling STXM-style directional ROIs. phi
bounds are optional (both-or-neither); absent means a full 360 ring, so
existing Q-only azimuthal ROIs are unchanged.
- ROIElement::CheckROI gains a phi_deg argument; box/circle ignore it.
- MarkROI gains an optional phi_map; ROIMap builds it from Phi_rad
alongside the resolution map only when azimuthal ROIs are present.
- phi bounds are normalized to [0,360) and wrap-around sectors are
supported (phi_min > phi_max).
- ROIConfigAzim carries phi_min/phi_max (kept trivially copyable for the
union; phi_min == phi_max means full ring).
No phi crosses the wire yet (CBOR/API wiring follows separately).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M_PI is a POSIX <math.h> extension that MSVC does not define without
_USE_MATH_DEFINES. std::numbers::pi (introduced in the viewer guard commit) is
C++20, but CUDA here is compiled as C++17 (CMAKE_CUDA_STANDARD 17) and several
common/ headers are pulled into .cu device translation units, so std::numbers is
not available there.
Add common/JFJochMath.h with a dependency-free `constexpr double PI` that works
in host code (including MSVC), in CUDA device code, and under C++17/20, and use
it everywhere:
- common/ and image_analysis/ (incl. CUDA .cu): 78 M_PI occurrences, 22 files
- broker/OpenAPIConvert.cpp
- viewer/: the 5 files that used std::numbers::pi now use PI, for one consistent
convention across the codebase
Verified to build: JFJochImageAnalysis (incl. CUDA), jfjoch_viewer, JFJochBroker.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Step toward building jfjoch_viewer on Windows/MSVC. No change to the Linux
build: D-Bus stays on by default and the XCB plugin is still used.
- CMake: make Qt6::DBus optional via JFJOCH_VIEWER_DBUS (ON on Linux, OFF on
Windows/macOS where Qt6::DBus does not exist); compile/link/install the dbus/
adaptor + service file only when enabled. Select the platform integration
plugin per-OS (QXcb on Linux, QWindows on Windows, QCocoa on macOS) instead
of hard-coding QXcbIntegrationPlugin.
- JFJochViewerWindow: wrap the adaptor include and D-Bus registration in
#ifdef JFJOCH_VIEWER_DBUS.
- JFJochImageReadingWorker: the POSIX open()/fstat()/NFS-errno preflight is now
#ifndef _WIN32, with a portable QFileInfo fallback elsewhere; POSIX-only
headers are guarded too.
- Replace the POSIX M_PI/M_PI_2 extension with C++20 std::numbers::pi across the
viewer (not defined by MSVC without _USE_MATH_DEFINES).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a status-bar cluster, shown when connected over HTTP, that surfaces
the broker state and live acquisition info:
- broker state box (QProgressBar) with progress drawn as the bar fill,
polled ~1 Hz on its own timer independent of image sync
- Live/Disconnected connection badge (host:port in tooltip)
- "+N new" badge and effective live-rate (Hz) readout
All widgets are fixed-width and only blanked in place, so the bar never
reflows when things appear/disappear.
Add a third autoload mode (HTTPSyncDataset): manually selecting an image
while following live now freezes the displayed image but keeps the dataset,
plots and image count updating. Surfaced via a tri-state HTTP Sync button
(off / live / data-only).
Also fix GetBrokerStatus() dropping the message field and a couple of
copy-pasted exception strings in JFJochHttpReader.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PixelRefine is now an intensity-only operation: geometry is fixed (refined
upstream by XtalOptimizer) and the only objective is the factored per-reflection
likelihood (FACTORED_MODEL.md Terms 1+2) - measured per-resolution profile width
R1 plus one Fisher-weighted intensity/scaling residual per reflection, fitting
the per-image scale G and B. Validated on crystal 2 (fixed_master.h5 as stills,
1.7 A): CC1/2 84-92%, CCref 77-92%, flat - reproduces the env-flag prototype and
matches the rotation path from the stills path.
Removed:
- the per-pixel ShoeboxResidual loss and PixelResidual cost functor;
- all in-PixelRefine geometry refinement (orientation/cell/beam/distance/R),
the regularised-orientation LSQ, signal-weighting, and the global sweep;
- Term 3 (per-spot recentring) - a confirmed no-op on both crystals;
- the diagnostic scaffolding (covariance, centroid, adaptive_R1) and the
PR_* env knobs + stderr dumps in IndexAndRefine;
- the PredictImage/ChiSquaredImage renderers and the entire viewer
PixelRefine window/table/params + worker bindings + shoebox overlay.
The sweep box-integrator background median became mean (consistency) by virtue
of removing the sweep. METHODS.md rewritten for the current model; findings
recorded in FINDINGS-2026-06.md. Net -2200 lines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This is an UNSTABLE release. The release has significant modifications for data processing - in case of troubles go back to 1.0.0-rc.144.
* jfjoch_viewer: Add reciprocal space viewer
* jfjoch_process: Two pass algorithm that does spot finding/indexing + integration of full dataset
* jfjoch_process: Improve logic for rotation indexer, to make execution more deterministic (still work in progress)
Reviewed-on: #57
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release. The release has significant modifications and bug fixes, if things go wrong, it is better to revert to 1.0.0-rc.132.
* jfjoch_broker: Azimuthal integration mapping is generated with parallel computations, significantly reducing setup times
* frontend: Fix selection of FFTW in indexing settings
Reviewed-on: #51
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release. The release has significant modifications and bug fixes, if things go wrong, it is better to revert to 1.0.0-rc.132.
* jfjoch_broker: Better track time for each operation in the processing stack
* jfjoch_broker: Rewrite preprocessing of diffraction images in the non-FPGA workflow to better use GPUs (work in progress)
* jfjoch_broker: Remove ROI calculation in the non-FPGA workflow (work in progress)
* jfjoch_viewer: Toolbar displays image number starting from 1 (instead of 0)
Reviewed-on: #46
This is an UNSTABLE release. The release has significant modifications and bug fixes, if things go wrong, it is better to revert to 1.0.0-rc.132.
* jfjoch_broker: Use httplib for HTTP server instead of Pistache
* jfjoch_broker: Drop OpenSSL support
* jfjoch_broker: Base work for multi-lattice support in the future
* Update dependencies to more recent versions (spdlog, HDF5, Catch2, httplib)
Reviewed-on: #41
This is an UNSTABLE release.
* jfjoch_broker: Add thresholding to prefer shorter vectors after FFT
* jfjoch_broker: Add experimental mosaicity estimation for rotation experiments (this is work in progress)
* jfjoch_viewer: Display file opening errors
* jfjoch_viewer: When loading files over DBus add retry/back-off till the file is available
Reviewed-on: #29
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release.
* jfjoch_broker: Report changes in the image buffer, so viewer doesn't reload constantly
* jfjoch_viewer: Improve performance of loading images
* jfjoch_viewer: Auto-throttle image loading in HTTP-sync / movie modes
* jfjoch_viewer: Auto-foreground calculated with histogram
* jfjoch_viewer: Fix rare segmentation fault
Reviewed-on: #28
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release and not recommended for production use (please use rc.111 instead).
* jfjoch_broker: Add binary export of data analysis plots over OpenAPI
* jfjoch_broker: Minor fixes to HTTP error handling
* jfjoch_viewer: Prefer binary plots over JSON plots
* jfjoch_viewer: Change foreground with F button + wheel
* jfjoch_viewer: Change way how angles are displayed
* jfjoch_viewer: Display resolution of the mouse cursor in top left corner
Reviewed-on: #26
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release and not recommended for production use (please use rc.111 instead).
* jfjoch_viewer: Fix issue when HTTP sync silently disconnected when it was enabled when the broker was starting measurement.
* jfjoch_broker: Add protections on time of geometry optimization and reduce rotation recalculations
Reviewed-on: #25
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release and not recommended for production use (please use rc.11 instead).
* jfjoch_broker: Experimental rotation (3D) indexing
* jfjoch_broker: Minor fix to error in optimizer potentially returning NaN values
Reviewed-on: #18
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release.
* jfjoch_viewer: Add keyboard shortcuts and option to copy image to clipboard
* jfjoch_broker: Fix bit-width and exposure time for PSI EIGER detectors
Reviewed-on: #15
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release.
* jfjoch_viewer: Auto load is better handling change of states
* jfjoch_viewer: Fix DBus registration
* jfjoch_viewer: Handle charts better with vertical lines on hover and status bar update
* jfjoch_viewer: Calculate ROI in a more efficient way
Reviewed-on: #6
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release and not recommended for production use (please use rc.96 instead).
* jfjoch_broker: For DECTRIS detectors add dark data collection during initialization for bad pixel mask
* jfjoch_broker: Refactor of calibration logic for more clear code (likely to introduce problems)
* jfjoch_viewer: Add option to handle user pixel mask (experimental)
* jfjoch_viewer: More options for ROI
* jfjoch_viewer: Add window to display calibration
Reviewed-on: #2
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
This is an UNSTABLE release.
* Fixes in CI pipeline
* jfjoch_broker: Remove PNG preview, no dependency on libpng
* jfjoch_writer: Fix UTC timestamp being generated wrong (mix between milli- and microseconds)
* jfjoch_viewer: Show data collection time in dataset tooltip
* jfjoch_viewer: Allow to choose the calibrant (presets for LaB6 and silver behenate)
* jfjoch_viewer: Auto foreground value
* Use external libjpeg-turbo and libtiff: simpler build stack, these are built and linked statically in automated Docker builds
* Remove OpenBLAS dependency
Reviewed-on: #1
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>