Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6672bbe
Add hardware provider architecture
curlh Aug 16, 2026
d22e69d
Complete hardware provider architecture
curlh Aug 19, 2026
a8e9aa5
Add extensible processing and tool workflows
curlh Aug 20, 2026
fd6bc01
Unify image workspace workflows
curlh Aug 24, 2026
0f34424
Add external plugin SDK and examples
curlh Aug 24, 2026
aeaee70
Unify dock panels and streamline preview controls
curlh Aug 25, 2026
b9e19fe
Add composable FFT mask and frequency processing
curlh Aug 25, 2026
d75281f
Fix raw intensity profile updates
curlh Aug 29, 2026
710f6d2
Fix
curlh Aug 31, 2026
745ee3e
Add TIFF input to DHM tool
curlh Aug 31, 2026
f1c5856
Fix multi-camera live processing preview
curlh Sep 1, 2026
4f1297f
Add cursor HUD readout, tile maximize toggle, and preview layout impr…
curlh Sep 1, 2026
79c897c
Fix Analyze layer panel layout
curlh Sep 1, 2026
9d3cea6
Refactor preview viewport transforms and unify cursor readout
curlh Sep 1, 2026
75cca46
Refine image gallery interactions
curlh Sep 1, 2026
2d8ac56
Modernize viewport toolbar
curlh Sep 1, 2026
7a4b548
Prepare console API dispatch
curlh Sep 1, 2026
eda9c16
Add hybrid console and simplify gallery actions
curlh Sep 1, 2026
0a4ba35
Unify processing pipeline and histogram controls
curlh Sep 1, 2026
ae300c7
Update
curlh Sep 1, 2026
ce14940
Migrate DAQ and tool plugins to Core SDK
curlh Sep 2, 2026
766490f
Add standard colormap LUTs
curlh Sep 2, 2026
62d844b
Add 3D surface view to PreviewWidget and refine DHM unwrapping
curlh Sep 2, 2026
f8d0648
Apply widget style settings and simplify plugin status
curlh Sep 3, 2026
29d6faf
Unify static image import as layers and modernize DHM input pipeline
curlh Sep 3, 2026
4df5675
Update
curlh Sep 3, 2026
ecc609c
Restructure plugin categories and split CUDA filters
curlh Sep 3, 2026
eccc23a
Move common CUDA filters into internal runtime
curlh Sep 3, 2026
915b309
Add independent 3D layer views and layer management
curlh Sep 6, 2026
cfd1a1a
Unify gallery layers in main viewer and constrain slice slider within…
curlh Sep 7, 2026
b920bfe
Add offline entire stack processing support for layer stacks
curlh Sep 7, 2026
9dc894b
Fix GUI startup initialization and lazy-load DAQ plugins
curlh Sep 8, 2026
5ff2328
Refine Python examples and fix DHM input preview
curlh Sep 8, 2026
fc67553
Add simulated PMT scan source
curlh Sep 8, 2026
85067b1
Align CUDA processing modules with CPU
curlh Sep 8, 2026
d59acfa
Document Python package installation
curlh Sep 8, 2026
f208824
Move layer FPS tracking into ScopeOneCore
curlh Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
!/src/**
!/ScopeOneCore/
!/ScopeOneCore/**
!/plugins/
!/plugins/**
!/ScopeOneCuda/
!/ScopeOneCuda/**
/ScopeOneCore/install/
/ScopeOneCore/build/
/ScopeOneCore/external/*
Expand Down
39 changes: 33 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,19 @@ set(SCOPEONE_UI_SOURCES
src/MainWindow.cpp
src/ImageProcessingWidget.cpp
src/ImageGalleryWidget.cpp
src/ImageWorkspace.cpp
src/ImageToolsDialog.cpp
src/PreviewWidget.cpp
src/ConsoleWidget.cpp
src/AboutDialog.cpp
src/SettingsDialog.cpp
src/PluginManagerDialog.cpp
src/DeviceControlWidget.cpp
src/RecordingWidget.cpp
src/DevicePropertyWidget.cpp
src/ConfigPresetWidget.cpp
src/ScopeOneLocalApiServer.cpp
src/ScopeOneToolPlugin.cpp
${CMAKE_SOURCE_DIR}/resources/resources.qrc
)

Expand All @@ -89,16 +92,19 @@ set(SCOPEONE_UI_HEADERS
src/MainWindow.h
src/ImageProcessingWidget.h
src/ImageGalleryWidget.h
src/ImageWorkspace.h
src/ImageToolsDialog.h
src/PreviewWidget.h
src/ConsoleWidget.h
src/AboutDialog.h
src/SettingsDialog.h
src/PluginManagerDialog.h
src/DeviceControlWidget.h
src/RecordingWidget.h
src/DevicePropertyWidget.h
src/ConfigPresetWidget.h
src/ScopeOneLocalApiServer.h
src/ScopeOneToolPlugin.h
)

if (WIN32)
Expand Down Expand Up @@ -176,11 +182,20 @@ install(FILES
DESTINATION .
)

file(GLOB SCOPEONE_CORE_RUNTIME_FILES
LIST_DIRECTORIES false
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/*.dll"
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/ScopeOne_Agent.exe"
)
if (WIN32)
file(GLOB SCOPEONE_CORE_RUNTIME_FILES
LIST_DIRECTORIES false
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/*.dll"
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/ScopeOne_DriverHost.exe"
)
else ()
file(GLOB SCOPEONE_CORE_RUNTIME_FILES
LIST_DIRECTORIES false
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/ScopeOne_DriverHost"
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_LIBDIR}/libScopeOneCore.so*"
"${ScopeOneCore_ROOT}/${CMAKE_INSTALL_LIBDIR}/libScopeWriter.so*"
)
endif ()

if (SCOPEONE_CORE_RUNTIME_FILES)
add_custom_command(TARGET ScopeOne POST_BUILD
Expand All @@ -191,6 +206,18 @@ if (SCOPEONE_CORE_RUNTIME_FILES)
)
endif ()

foreach (_plugin_kind IN ITEMS processing hardware tools)
set(_plugin_dir "${ScopeOneCore_ROOT}/${CMAKE_INSTALL_BINDIR}/plugins/${_plugin_kind}")
if (EXISTS "${_plugin_dir}")
add_custom_command(TARGET ScopeOne POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${_plugin_dir}"
"$<TARGET_FILE_DIR:ScopeOne>/plugins/${_plugin_kind}"
)
install(DIRECTORY "${_plugin_dir}/" DESTINATION "plugins/${_plugin_kind}")
endif ()
endforeach ()

if (WIN32)
if (WINDEPLOYQT_EXECUTABLE)
add_custom_command(TARGET ScopeOne POST_BUILD
Expand Down Expand Up @@ -227,7 +254,7 @@ if (WIN32)
install(CODE "
file(GLOB _core_runtime_files
\"$<TARGET_FILE_DIR:scopeone::ScopeOneCore>/*.dll\"
\"$<TARGET_FILE_DIR:scopeone::ScopeOneCore>/ScopeOne_Agent.exe\"
\"$<TARGET_FILE_DIR:scopeone::ScopeOneCore>/ScopeOne_DriverHost.exe\"
)
if (_core_runtime_files)
file(INSTALL
Expand Down
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<!-- <a href="https://doi.org/10.48550/arXiv.2606.19384"><img src="https://img.shields.io/badge/arXiv-2606.19384-b31b1b.svg" alt="Preprint DOI"></a> -->
</p>

ScopeOne is an open-source microscopy control software for multi-camera imaging, originally developed for in-house lab use. Built with C++ and Qt, it uses a native [MMCore](https://github.com/micro-manager/mmCoreAndDevices) backend for single-camera operation and one isolated camera agent per device for simultaneous multi-camera preview and acquisition.
It retains full compatibility with the [Micro-Manager](https://micro-manager.org/) device ecosystem and adds a modular real-time image processing pipeline with support for background calibration, temporal filtering, FFT analysis, and more.
ScopeOne is open-source microscopy control software built with C++ and Qt. Hardware is accessed through provider-independent device contracts; [Micro-Manager](https://micro-manager.org/) is the built-in provider, and isolated devices run through ScopeOne DriverHost processes.
It retains compatibility with the Micro-Manager device ecosystem while allowing native vendor providers and adds a modular real-time image processing pipeline with support for background calibration, temporal filtering, FFT analysis, and more.

<p align="center">
<img src="resources/MainWindow.png" width="720"><br>
<img src="resources/Screenshot 2026-09-01 140040.png" width="720"><br>
<sub> Graphical User Interface of ScopeOne</sub>
</p>

Expand Down Expand Up @@ -63,6 +63,7 @@ The expected layout is:
```text
ScopeOne/
ScopeOneCore/
include/scopeone/ Core API and external plugin contracts
external/
mmCoreAndDevices/
opencv-4.12.0/
Expand All @@ -71,6 +72,21 @@ ScopeOne/

ScopeWriter contains its filesystem Zarr V3 writer and carries libtiff, zlib, zstd and crc32c under its own `third_party` directory. It builds these dependencies from source without downloading packages during CMake configuration.

### Plugin layout

ScopeOne loads external plugins from these directories beside the application:

- `plugins/processing` adds processing modules to the shared pipeline
- `plugins/tools` adds optional workflow windows to the Tools menu
- `plugins/hardware` adds isolated hardware providers hosted by `ScopeOne_DriverHost`
- `plugins/hardware` also contains DAQ devices and signal source plugins

All plugins use the installed `scopeone::PluginSDK` CMake target and the public contracts in `ScopeOneCore/include/scopeone`. The Core package owns the stable plugin-facing headers for image frames, hardware providers, DAQ devices, signal sources, processing modules, tool plugins, shared frames and manifests. A common manifest contains `id`, `name`, `version`, `scopeOneApi`, and `kind`. **Tools > Plugin Manager** installs plugins into the current user's application data directory. Hardware plugins can also be enabled and configured there; changes take effect after restart. Reference plugins are organized under `plugins/hardware`, `plugins/processing`, and `plugins/tools`.

Micro-Manager remains the built-in camera provider and continues to load its Device Adapters from `.cfg` files. Native camera devices that do not belong in Micro-Manager use the `HardwareProvider` plugin contract. DAQ and signal acquisition are separate plugin contracts and are not linked into `ScopeOne.exe`.

The Image Processing panel can process all live cameras or one selected camera. Recorded images and stacks open in independent windows, and the active image viewer becomes the target for Layers, Inspect, Image Processing, and Save As. Processing the current image or complete stack runs in the background and opens the result in a new window; stack results also remain available in Gallery. Temporal module state is preserved across each stack without changing live pipeline state.

**Windows Build Steps:**

1. Build and install `ScopeOneCore`:
Expand Down Expand Up @@ -230,7 +246,7 @@ To use it:

Use `scopeone` as the server name, `stdio` as the transport, the absolute path to `ScopeOneMcpServer.exe` as the command, and no command-line arguments. The exact configuration syntax depends on the agent host.

The MCP tool set mirrors the Local API operation catalog, including system state, configuration, preview layers, automatic display levels, source alignment, markups, device properties, exposure, ROI, stages, stage mosaics, processing, experiments, recording sessions, frame transfer, and analysis. Agents can read the current frame of any image layer, monitor live acquisition and writer progress, and optionally export or display particle masks. ScopeOne remains the authority for parameter validation and hardware read-back, and MCP tool calls are visible in the desktop UI through the same application state used by manual controls.
The MCP tool set mirrors the Local API operation catalog, including system state, configuration, preview layers, independent image windows, automatic display levels, source alignment, markups, device properties, exposure, ROI, stages, stage mosaics, processing, experiments, recording sessions, frame transfer, and analysis. Agents can list, open, activate, process, save, and close independent image windows, read the current frame of any image layer, monitor live acquisition and writer progress, and optionally export or display particle masks. ScopeOne remains the authority for parameter validation and hardware read-back, and MCP tool calls are visible in the desktop UI through the same application state used by manual controls.

Configuration loading and unloading report an explicit lifecycle state through the Local API and MCP. A configuration with non-camera initialization warnings is reported as `partially_loaded` with failed device labels; camera backend startup failures are cleaned up and reported as errors. During `loading` or `unloading`, hardware mutations are rejected until the operation finishes.

Expand Down
89 changes: 72 additions & 17 deletions ScopeOneCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if (WIN32)

set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/opencv-4.12.0/build")

find_package(OpenCV REQUIRED COMPONENTS core imgproc)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
set(SCOPEONE_OPENCV_LIBRARIES opencv_world)

file(GLOB MMCORE_RUNTIME_DLLS "${MMCORE_BIN_DIR}/*.dll")
Expand All @@ -51,7 +51,7 @@ else ()
# Linux/Unix: use system OpenCV and link the MMCore static libraries
# built from the sibling micro-manager checkout (symlinked into
# external/mmCoreAndDevices). No runtime DLLs to stage.
find_package(OpenCV REQUIRED COMPONENTS core imgproc)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
set(SCOPEONE_OPENCV_LIBRARIES ${OpenCV_LIBS})
set(MMCORE_BIN_DIR "")

Expand All @@ -77,53 +77,107 @@ endfunction()

set(CORE_SOURCES
src/ExperimentDocument.cpp
src/AcquisitionEngine.cpp
src/DriverHostProviderProxy.cpp
src/DaqDeviceManager.cpp
src/HardwareProvider.cpp
src/PluginManifest.cpp
src/HardwareRuntime.cpp
src/MicroManagerProvider.cpp
src/SimulatorProvider.cpp
src/ImageSceneModel.cpp
src/MMCoreManager.cpp
src/FrameBufferUtils.cpp
src/ParticleAnalysis.cpp
src/StageMosaicManager.cpp
src/ImageProcessingFramework.cpp
src/ProcessingPipeline.cpp
src/ToolTask.cpp
src/ToolFrameStream.cpp
src/ProcessingModuleRegistry.cpp
src/SpatiotemporalBinningModule.cpp
src/GaussianBlurModule.cpp
src/FFTModule.cpp
src/FrequencyDomainFilterModule.cpp
src/MaskModule.cpp
src/IFFTModule.cpp
src/BackgroundCalibrationModule.cpp
src/DifferentialRollingModule.cpp
src/MDAManager.cpp
src/RecordingManager.cpp
src/CameraBackend.cpp
src/CameraManager.cpp
src/NativeCameraBackend.cpp
src/AgentCameraBackend.cpp
src/DriverHostCameraBackend.cpp
src/ScopeOneCore.cpp
src/ScanImageAssembler.cpp
src/SignalSourceManager.cpp
)

set(CORE_HEADERS
include/scopeone/ExperimentDocument.h
include/scopeone/CameraProvider.h
include/scopeone/DaqDevice.h
include/scopeone/DriverHostProviderPlugin.h
include/scopeone/HardwareCapabilities.h
include/scopeone/HardwareProvider.h
include/scopeone/HardwareTypes.h
include/scopeone/ImageFrame.h
include/scopeone/PluginManifest.h
include/scopeone/PluginSDK.h
include/scopeone/ProcessingPlugin.h
include/scopeone/ScanImageAssembler.h
include/scopeone/scopeone_sdk_export.h
include/scopeone/SharedFrame.h
include/scopeone/SignalSource.h
include/scopeone/ToolFrameStream.h
include/scopeone/ToolPlugin.h
include/scopeone/ToolTask.h
internal/AcquisitionEngine.h
internal/CameraRuntimeControl.h
include/scopeone/SimulatorProvider.h
include/scopeone/ImageSceneModel.h
internal/HardwareRuntime.h
internal/MicroManagerProvider.h
internal/MMCoreManager.h
internal/FrameBufferUtils.h
internal/ParticleAnalysis.h
internal/StageMosaicManager.h
internal/ImageProcessingFramework.h
internal/ProcessingModule.h
internal/AgentProtocol.h
internal/ProcessingModuleRegistry.h
include/scopeone/ProcessingPipeline.h
internal/DriverHostProtocol.h
internal/SharedFrameRing.h
internal/DriverHostProviderProxy.h
internal/DaqDeviceManager.h
internal/SignalSourceManager.h
internal/SpatiotemporalBinningModule.h
internal/GaussianBlurModule.h
internal/FFTModule.h
internal/FrequencyDomainFilterModule.h
internal/MaskModule.h
internal/IFFTModule.h
internal/BackgroundCalibrationModule.h
internal/DifferentialRollingModule.h
internal/MDAManager.h
internal/RecordingManager.h
internal/CameraBackend.h
internal/CameraManager.h
include/scopeone/ImageFrame.h
include/scopeone/SharedFrame.h
include/scopeone/scopeone_core_export.h
include/scopeone/ScopeOneCore.h
)

add_library(ScopeOneCore SHARED ${CORE_SOURCES} ${CORE_HEADERS})
add_library(scopeone::ScopeOneCore ALIAS ScopeOneCore)
add_library(ScopeOnePluginSDK INTERFACE)
add_library(scopeone::PluginSDK ALIAS ScopeOnePluginSDK)
set_target_properties(ScopeOnePluginSDK PROPERTIES EXPORT_NAME PluginSDK)
target_link_libraries(ScopeOnePluginSDK INTERFACE ScopeOneCore Qt::Core Qt::Gui)
target_include_directories(ScopeOnePluginSDK INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_link_libraries(ScopeOneCore
PUBLIC
Qt::Core
Expand Down Expand Up @@ -187,24 +241,25 @@ add_custom_command(TARGET ScopeOneCore POST_BUILD
$<TARGET_FILE_DIR:ScopeOneCore>
)

add_executable(ScopeOne_Agent
src/AgentMain.cpp
add_executable(ScopeOne_DriverHost
src/DriverHostMain.cpp
)
target_link_libraries(ScopeOne_Agent
target_link_libraries(ScopeOne_DriverHost
ScopeOneCore
MMCore
Qt::Core
Qt::Network
)
target_compile_definitions(ScopeOne_Agent PRIVATE MMDEVICE_CLIENT_BUILD)
target_include_directories(ScopeOne_Agent PRIVATE
target_compile_definitions(ScopeOne_DriverHost PRIVATE MMDEVICE_CLIENT_BUILD)
target_include_directories(ScopeOne_DriverHost PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
${MMCORE_INCLUDE_DIR}
${MMDEVICE_INCLUDE_DIR}
)
target_link_directories(ScopeOne_Agent PRIVATE ${MMCORE_BIN_DIR})
target_link_directories(ScopeOne_DriverHost PRIVATE ${MMCORE_BIN_DIR})

scopeone_add_runtime_copy_commands(ScopeOne_Agent $<TARGET_FILE_DIR:ScopeOne_Agent>)
scopeone_add_runtime_copy_commands(ScopeOne_DriverHost $<TARGET_FILE_DIR:ScopeOne_DriverHost>)

# Fast Release build
# if (MSVC)
Expand All @@ -222,10 +277,10 @@ if (MSVC)
$<$<CONFIG:Release>:/LTCG>
)

target_compile_options(ScopeOne_Agent PRIVATE
target_compile_options(ScopeOne_DriverHost PRIVATE
$<$<CONFIG:Release>:/GL /fp:fast /arch:AVX2>
)
target_link_options(ScopeOne_Agent PRIVATE
target_link_options(ScopeOne_DriverHost PRIVATE
$<$<CONFIG:Release>:/LTCG>
)
endif ()
Expand All @@ -242,13 +297,13 @@ write_basic_package_version_file(
COMPATIBILITY SameMajorVersion
)

install(TARGETS ScopeOneCore
install(TARGETS ScopeOneCore ScopeOnePluginSDK
EXPORT ScopeOneCoreTargets
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
install(TARGETS ScopeOne_Agent
install(TARGETS ScopeOne_DriverHost
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
Expand Down
Loading
Loading