diff --git a/CMakeLists.txt b/CMakeLists.txt index 414b64cd..c256957d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,13 @@ SET(JFJOCH_WRITER_ONLY OFF CACHE BOOL "Compile HDF5 writer only") INCLUDE_DIRECTORIES(include) INCLUDE(CheckIncludeFile) +#This is to supress error in TORCH + +IF ((NOT EXISTS /usr/bin/python) AND (EXISTS /usr/bin/python3)) + SET(PYTHON_EXECUTABLE /usr/bin/python3) +ENDIF() + +FIND_PACKAGE(Torch HINTS /opt/libtorch/share/cmake/Torch/) FIND_LIBRARY(NUMA_LIBRARY NAMES numa DOC "NUMA Library") CHECK_INCLUDE_FILE(numaif.h HAS_NUMAIF) CHECK_INCLUDE_FILE(numa.h HAS_NUMA_H) @@ -54,6 +61,7 @@ ELSE() ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(tools) ADD_SUBDIRECTORY(export_images) + ADD_SUBDIRECTORY(resonet) SET(jfjoch_executables jfjoch_broker jfjoch_writer CatchTest CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get) ENDIF() diff --git a/README.md b/README.md index 4e3e55b9..8f26259d 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Optional: * CUDA compiler version 11 or newer - image analysis features won't work without it * NUMA library * Node.js - to make frontend +* libtorch - for resolution estimation using model from Stanford - see below provided as GIT submodules: SLS Detector Package, tinycbor (Intel), Zstandard (Facebook), Pistache webserver and fast replacement for bitshuffle filter (DECTRIS). @@ -90,5 +91,16 @@ Automated test routine is then accessible as `tests/CatchTest`. There are also b In addition, tests are executed to verify that datasets written by Jungfraujoch are readable with XDS Durin plugin and CrystFEL. Input files for these programs are placed in `xds_durin` and `crystfel` folders. See `.gitlab-ci.yml` for details. +## Resolution estimation +Resolution estimation can be done with a recent deep learning model by D. Mendez et al. +(see [Acta Cryst D, 80, 26-43](https://doi.org/10.1107/S2059798323010586)), adapted to Jungfraujoch. +Model used in the original paper is located in the [resonet/](resonet) directory, after converting to TorchScript format. + +To use the feature it is necessary to install [libtorch](https://pytorch.org/) library, preferably in `/opt/libtorch` location. The C++11 ABI version needs to be chosen. +For RHEL 8 systems, please download older version 2.1.0, as version 2.2.0 requires newer `glibc` library than available with the operating system. +Version 2.1.0 can be downloaded with the following command: +``` +wget https://download.pytorch.org/libtorch/cu121/libtorch-cxx11-abi-shared-with-deps-2.1.0%2Bcu121.zip +``` diff --git a/acquisition_device/CMakeLists.txt b/acquisition_device/CMakeLists.txt index a1d99752..5f8510ef 100644 --- a/acquisition_device/CMakeLists.txt +++ b/acquisition_device/CMakeLists.txt @@ -1,4 +1,4 @@ -ADD_LIBRARY(JungfraujochAcqusitionDevice STATIC +ADD_LIBRARY(JFJochAcquisitionDevice STATIC AcquisitionDevice.cpp AcquisitionDevice.h AcquisitionCounters.cpp AcquisitionCounters.h HLSSimulatedDevice.cpp HLSSimulatedDevice.h @@ -8,4 +8,4 @@ ADD_LIBRARY(JungfraujochAcqusitionDevice STATIC AcquisitionDeviceGroup.cpp AcquisitionDeviceGroup.h) -TARGET_LINK_LIBRARIES(JungfraujochAcqusitionDevice JungfraujochDevice CommonFunctions HLSSimulation JFCalibration) \ No newline at end of file +TARGET_LINK_LIBRARIES(JFJochAcquisitionDevice JFJochDevice JFJochCommon JFJochHLSSimulation JFCalibration) \ No newline at end of file diff --git a/broker/CMakeLists.txt b/broker/CMakeLists.txt index 6cc36bdb..6002e971 100644 --- a/broker/CMakeLists.txt +++ b/broker/CMakeLists.txt @@ -9,7 +9,7 @@ ADD_LIBRARY(JFJochBroker STATIC JFJochServices.cpp JFJochServices.h JFJochBrokerHttp.cpp JFJochBrokerHttp.h JFJochBrokerParser.cpp JFJochBrokerParser.h) -TARGET_LINK_LIBRARIES(JFJochBroker JFJochReceiver JFJochDetector CommonFunctions JFJochAPI) +TARGET_LINK_LIBRARIES(JFJochBroker JFJochReceiver JFJochDetector JFJochCommon JFJochAPI) ADD_EXECUTABLE(jfjoch_broker jfjoch_broker.cpp) TARGET_LINK_LIBRARIES(jfjoch_broker JFJochBroker) diff --git a/broker/JFJochBrokerHttp.cpp b/broker/JFJochBrokerHttp.cpp index 87b81c8c..f6288d4d 100644 --- a/broker/JFJochBrokerHttp.cpp +++ b/broker/JFJochBrokerHttp.cpp @@ -703,3 +703,9 @@ void JFJochBrokerHttp::preview_jpeg_settings_put(const org::openapitools::server state_machine.SetPreviewJPEGSettings(Convert(previewSettings)); response.send(Pistache::Http::Code::Ok); } + +void JFJochBrokerHttp::plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) { + PlotRequest req{.type = PlotType::ResEstimation, .binning = 0}; + auto plot = state_machine.GetPlots(req); + ProcessOutput(Convert(plot), response); +} diff --git a/broker/JFJochBrokerHttp.h b/broker/JFJochBrokerHttp.h index 70105b49..28497432 100644 --- a/broker/JFJochBrokerHttp.h +++ b/broker/JFJochBrokerHttp.h @@ -71,6 +71,8 @@ class JFJochBrokerHttp : public org::openapitools::server::api::DefaultApi { void plot_spot_count_post(const org::openapitools::server::model::Plot_request &plotRequest, Pistache::Http::ResponseWriter &response) override; + void plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) override; + void statistics_calibration_get(Pistache::Http::ResponseWriter &response) override; void statistics_data_collection_get(Pistache::Http::ResponseWriter &response) override; @@ -102,6 +104,7 @@ class JFJochBrokerHttp : public org::openapitools::server::api::DefaultApi { void preview_jpeg_settings_put(const org::openapitools::server::model::Preview_settings &previewSettings, Pistache::Http::ResponseWriter &response) override; + void GetStaticFile(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); std::pair handleOperationException(const std::exception &ex) const noexcept override; diff --git a/broker/JFJochBrokerParser.cpp b/broker/JFJochBrokerParser.cpp index 7953f978..ad490ab7 100644 --- a/broker/JFJochBrokerParser.cpp +++ b/broker/JFJochBrokerParser.cpp @@ -273,6 +273,7 @@ void ParseFacilityConfiguration(const nlohmann::json &input, const std::string& "omega_axis must be float array of 3"); } + experiment.NeuralNetModelPath(GET_STR(j, "neural_net_model", "")); experiment.PedestalG0Frames(GET_I64(j, "pedestal_g0_frames")); experiment.PedestalG1Frames(GET_I64(j, "pedestal_g1_frames")); experiment.PedestalG2Frames(GET_I64(j, "pedestal_g2_frames")); diff --git a/broker/gen/api/DefaultApi.cpp b/broker/gen/api/DefaultApi.cpp index b365f23c..b9248fd2 100644 --- a/broker/gen/api/DefaultApi.cpp +++ b/broker/gen/api/DefaultApi.cpp @@ -54,6 +54,7 @@ void DefaultApi::setupRoutes() { Routes::Get(*router, base + "/plot/rad_int", Routes::bind(&DefaultApi::plot_rad_int_get_handler, this)); Routes::Get(*router, base + "/plot/rad_int_per_file", Routes::bind(&DefaultApi::plot_rad_int_per_file_get_handler, this)); Routes::Post(*router, base + "/plot/receiver_delay", Routes::bind(&DefaultApi::plot_receiver_delay_post_handler, this)); + Routes::Get(*router, base + "/plot/resolution_estimate_histogram", Routes::bind(&DefaultApi::plot_resolution_estimate_histogram_get_handler, this)); Routes::Post(*router, base + "/plot/roi_sum", Routes::bind(&DefaultApi::plot_roi_sum_post_handler, this)); Routes::Post(*router, base + "/plot/saturated_pixel", Routes::bind(&DefaultApi::plot_saturated_pixel_post_handler, this)); Routes::Post(*router, base + "/plot/spot_count", Routes::bind(&DefaultApi::plot_spot_count_post_handler, this)); @@ -628,6 +629,26 @@ void DefaultApi::plot_receiver_delay_post_handler(const Pistache::Rest::Request response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); } +} +void DefaultApi::plot_resolution_estimate_histogram_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { + try { + + + try { + this->plot_resolution_estimate_histogram_get(response); + } catch (Pistache::Http::HttpError &e) { + response.send(static_cast(e.code()), e.what()); + return; + } catch (std::exception &e) { + const std::pair errorInfo = this->handleOperationException(e); + response.send(errorInfo.first, errorInfo.second); + return; + } + + } catch (std::exception &e) { + response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); + } + } void DefaultApi::plot_roi_sum_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) { try { diff --git a/broker/gen/api/DefaultApi.h b/broker/gen/api/DefaultApi.h index da868364..202dadf7 100644 --- a/broker/gen/api/DefaultApi.h +++ b/broker/gen/api/DefaultApi.h @@ -78,6 +78,7 @@ private: void plot_rad_int_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void plot_rad_int_per_file_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void plot_receiver_delay_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); + void plot_resolution_estimate_histogram_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void plot_roi_sum_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void plot_saturated_pixel_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void plot_spot_count_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); @@ -268,6 +269,13 @@ private: /// (optional) virtual void plot_receiver_delay_post(const org::openapitools::server::model::Plot_request &plotRequest, Pistache::Http::ResponseWriter &response) = 0; /// + /// Generate resolution estimate histogram + /// + /// + /// Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model + /// + virtual void plot_resolution_estimate_histogram_get(Pistache::Http::ResponseWriter &response) = 0; + /// /// Generate ROI sum plot /// /// diff --git a/broker/gen/model/Detector_status.cpp b/broker/gen/model/Detector_status.cpp index 7a851d81..19ed51ab 100644 --- a/broker/gen/model/Detector_status.cpp +++ b/broker/gen/model/Detector_status.cpp @@ -22,14 +22,9 @@ namespace org::openapitools::server::model Detector_status::Detector_status() { m_State = ""; - m_StateIsSet = false; m_Powerchip = ""; - m_PowerchipIsSet = false; m_Server_version = ""; - m_Server_versionIsSet = false; m_Number_of_triggers_left = 0L; - m_Number_of_triggers_leftIsSet = false; - m_Fpga_temp_degCIsSet = false; } @@ -53,8 +48,8 @@ bool Detector_status::validate(std::stringstream& msg, const std::string& pathPr const std::string _pathPrefix = pathPrefix.empty() ? "Detector_status" : pathPrefix; - if (fpgaTempDegCIsSet()) - { + + /* Fpga_temp_degC */ { const std::vector& value = m_Fpga_temp_degC; const std::string currentValuePath = _pathPrefix + ".fpgaTempDegC"; @@ -82,20 +77,20 @@ bool Detector_status::operator==(const Detector_status& rhs) const return + (getState() == rhs.getState()) + && - ((!stateIsSet() && !rhs.stateIsSet()) || (stateIsSet() && rhs.stateIsSet() && getState() == rhs.getState())) && + (getPowerchip() == rhs.getPowerchip()) + && + (getServerVersion() == rhs.getServerVersion()) + && - ((!powerchipIsSet() && !rhs.powerchipIsSet()) || (powerchipIsSet() && rhs.powerchipIsSet() && getPowerchip() == rhs.getPowerchip())) && + (getNumberOfTriggersLeft() == rhs.getNumberOfTriggersLeft()) + && + (getFpgaTempDegC() == rhs.getFpgaTempDegC()) - ((!serverVersionIsSet() && !rhs.serverVersionIsSet()) || (serverVersionIsSet() && rhs.serverVersionIsSet() && getServerVersion() == rhs.getServerVersion())) && - - - ((!numberOfTriggersLeftIsSet() && !rhs.numberOfTriggersLeftIsSet()) || (numberOfTriggersLeftIsSet() && rhs.numberOfTriggersLeftIsSet() && getNumberOfTriggersLeft() == rhs.getNumberOfTriggersLeft())) && - - - ((!fpgaTempDegCIsSet() && !rhs.fpgaTempDegCIsSet()) || (fpgaTempDegCIsSet() && rhs.fpgaTempDegCIsSet() && getFpgaTempDegC() == rhs.getFpgaTempDegC())) ; } @@ -108,46 +103,21 @@ bool Detector_status::operator!=(const Detector_status& rhs) const void to_json(nlohmann::json& j, const Detector_status& o) { j = nlohmann::json(); - if(o.stateIsSet()) - j["state"] = o.m_State; - if(o.powerchipIsSet()) - j["powerchip"] = o.m_Powerchip; - if(o.serverVersionIsSet()) - j["server_version"] = o.m_Server_version; - if(o.numberOfTriggersLeftIsSet()) - j["number_of_triggers_left"] = o.m_Number_of_triggers_left; - if(o.fpgaTempDegCIsSet() || !o.m_Fpga_temp_degC.empty()) - j["fpga_temp_degC"] = o.m_Fpga_temp_degC; + j["state"] = o.m_State; + j["powerchip"] = o.m_Powerchip; + j["server_version"] = o.m_Server_version; + j["number_of_triggers_left"] = o.m_Number_of_triggers_left; + j["fpga_temp_degC"] = o.m_Fpga_temp_degC; } void from_json(const nlohmann::json& j, Detector_status& o) { - if(j.find("state") != j.end()) - { - j.at("state").get_to(o.m_State); - o.m_StateIsSet = true; - } - if(j.find("powerchip") != j.end()) - { - j.at("powerchip").get_to(o.m_Powerchip); - o.m_PowerchipIsSet = true; - } - if(j.find("server_version") != j.end()) - { - j.at("server_version").get_to(o.m_Server_version); - o.m_Server_versionIsSet = true; - } - if(j.find("number_of_triggers_left") != j.end()) - { - j.at("number_of_triggers_left").get_to(o.m_Number_of_triggers_left); - o.m_Number_of_triggers_leftIsSet = true; - } - if(j.find("fpga_temp_degC") != j.end()) - { - j.at("fpga_temp_degC").get_to(o.m_Fpga_temp_degC); - o.m_Fpga_temp_degCIsSet = true; - } + j.at("state").get_to(o.m_State); + j.at("powerchip").get_to(o.m_Powerchip); + j.at("server_version").get_to(o.m_Server_version); + j.at("number_of_triggers_left").get_to(o.m_Number_of_triggers_left); + j.at("fpga_temp_degC").get_to(o.m_Fpga_temp_degC); } @@ -158,15 +128,6 @@ std::string Detector_status::getState() const void Detector_status::setState(std::string const& value) { m_State = value; - m_StateIsSet = true; -} -bool Detector_status::stateIsSet() const -{ - return m_StateIsSet; -} -void Detector_status::unsetState() -{ - m_StateIsSet = false; } std::string Detector_status::getPowerchip() const { @@ -175,15 +136,6 @@ std::string Detector_status::getPowerchip() const void Detector_status::setPowerchip(std::string const& value) { m_Powerchip = value; - m_PowerchipIsSet = true; -} -bool Detector_status::powerchipIsSet() const -{ - return m_PowerchipIsSet; -} -void Detector_status::unsetPowerchip() -{ - m_PowerchipIsSet = false; } std::string Detector_status::getServerVersion() const { @@ -192,15 +144,6 @@ std::string Detector_status::getServerVersion() const void Detector_status::setServerVersion(std::string const& value) { m_Server_version = value; - m_Server_versionIsSet = true; -} -bool Detector_status::serverVersionIsSet() const -{ - return m_Server_versionIsSet; -} -void Detector_status::unsetServer_version() -{ - m_Server_versionIsSet = false; } int64_t Detector_status::getNumberOfTriggersLeft() const { @@ -209,15 +152,6 @@ int64_t Detector_status::getNumberOfTriggersLeft() const void Detector_status::setNumberOfTriggersLeft(int64_t const value) { m_Number_of_triggers_left = value; - m_Number_of_triggers_leftIsSet = true; -} -bool Detector_status::numberOfTriggersLeftIsSet() const -{ - return m_Number_of_triggers_leftIsSet; -} -void Detector_status::unsetNumber_of_triggers_left() -{ - m_Number_of_triggers_leftIsSet = false; } std::vector Detector_status::getFpgaTempDegC() const { @@ -226,15 +160,6 @@ std::vector Detector_status::getFpgaTempDegC() const void Detector_status::setFpgaTempDegC(std::vector const value) { m_Fpga_temp_degC = value; - m_Fpga_temp_degCIsSet = true; -} -bool Detector_status::fpgaTempDegCIsSet() const -{ - return m_Fpga_temp_degCIsSet; -} -void Detector_status::unsetFpga_temp_degC() -{ - m_Fpga_temp_degCIsSet = false; } diff --git a/broker/gen/model/Detector_status.h b/broker/gen/model/Detector_status.h index abb9e2e8..b648bb75 100644 --- a/broker/gen/model/Detector_status.h +++ b/broker/gen/model/Detector_status.h @@ -64,50 +64,40 @@ public: /// std::string getState() const; void setState(std::string const& value); - bool stateIsSet() const; - void unsetState(); /// /// Power on of ASICs /// std::string getPowerchip() const; void setPowerchip(std::string const& value); - bool powerchipIsSet() const; - void unsetPowerchip(); /// /// Detector server (on read-out boards) version /// std::string getServerVersion() const; void setServerVersion(std::string const& value); - bool serverVersionIsSet() const; - void unsetServer_version(); /// /// Remaining triggers to the detector (max of all modules) /// int64_t getNumberOfTriggersLeft() const; void setNumberOfTriggersLeft(int64_t const value); - bool numberOfTriggersLeftIsSet() const; - void unsetNumber_of_triggers_left(); /// /// Temperature of detector FPGAs /// std::vector getFpgaTempDegC() const; void setFpgaTempDegC(std::vector const value); - bool fpgaTempDegCIsSet() const; - void unsetFpga_temp_degC(); friend void to_json(nlohmann::json& j, const Detector_status& o); friend void from_json(const nlohmann::json& j, Detector_status& o); protected: std::string m_State; - bool m_StateIsSet; + std::string m_Powerchip; - bool m_PowerchipIsSet; + std::string m_Server_version; - bool m_Server_versionIsSet; + int64_t m_Number_of_triggers_left; - bool m_Number_of_triggers_leftIsSet; + std::vector m_Fpga_temp_degC; - bool m_Fpga_temp_degCIsSet; + }; diff --git a/broker/jfjoch_api.yaml b/broker/jfjoch_api.yaml index 234cc2a0..2ffe9651 100644 --- a/broker/jfjoch_api.yaml +++ b/broker/jfjoch_api.yaml @@ -1131,6 +1131,17 @@ paths: application/json: schema: $ref: '#/components/schemas/plot' + /plot/resolution_estimate_histogram: + get: + summary: Generate resolution estimate histogram + description: Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model + responses: + "200": + description: Everything OK + content: + application/json: + schema: + $ref: '#/components/schemas/plot' /plot/rad_int: get: summary: Generate radial integration profile diff --git a/broker/redoc-static.html b/broker/redoc-static.html index 802b3a84..43f6c283 100644 --- a/broker/redoc-static.html +++ b/broker/redoc-static.html @@ -341,7 +341,7 @@ data-styled.g137[id="sc-cMdfCE"]{content:"dvQijr,"}/*!sc*/ -

Running in "simulator" mode - no detector present

Response samples

Content type
application/json
{
  • "state": "idle",
  • "powerchip": "on",
  • "server_version": "string",
  • "number_of_triggers_left": 0,
  • "fpga_temp_degC": [
    ]
}

Generate background estimate plot

Response samples

Content type
application/json
{
  • "state": "Idle",
  • "powerchip": "On",
  • "server_version": "string",
  • "number_of_triggers_left": 0,
  • "fpga_temp_degC": [
    ]
}

Generate background estimate plot

Mean intensity for d = 3 - 5 A per image; binning is configurable

Request Body schema: application/json
binning
integer <int64>

Responses

Responses

Response samples

Content type
application/json
{
  • "x": [
    ],
  • "y": [
    ]
}

Generate radial integration profile

Response samples

Content type
application/json
{
  • "x": [
    ],
  • "y": [
    ]
}

Generate resolution estimate histogram

Generate histogram of crystal resolutions from 1.0 to 5.0 A based on ML model

+

Responses

Response samples

Content type
application/json
{
  • "x": [
    ],
  • "y": [
    ]
}

Generate radial integration profile

Generate average radial integration profile

Responses

Response samples

Content type
application/json
{
  • "x": [
    ],
  • "y": [
    ]
}

Generate radial integration profiles per file

Response samples

Content type
application/json
{
  • "x": [
    ],
  • "y": [
    ]
}

Generate radial integration profiles per file

Radial integration plots for both the whole dataset and per file; useful for time-resolved measurements

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Get data collection statistics

Response samples

Content type
application/json
[
  • {
    }
]

Get data collection statistics

Results of the last data collection

Responses

Response samples

Content type
application/json
{
  • "file_prefix": "string",
  • "images_expected": 0,
  • "images_collected": 0,
  • "images_sent": 0,
  • "max_image_number_sent": 0,
  • "collection_efficiency": 1,
  • "compression_ratio": 5.3,
  • "cancelled": true,
  • "max_receiver_delay": 0,
  • "indexing_rate": 0.1,
  • "detector_width": 0,
  • "detector_height": 0,
  • "detector_pixel_depth": 2,
  • "bkg_estimate": 0.1
}

Get calibration statistics

Response samples

Content type
application/json
{
  • "file_prefix": "string",
  • "images_expected": 0,
  • "images_collected": 0,
  • "images_sent": 0,
  • "max_image_number_sent": 0,
  • "collection_efficiency": 1,
  • "compression_ratio": 5.3,
  • "cancelled": true,
  • "max_receiver_delay": 0,
  • "indexing_rate": 0.1,
  • "detector_width": 0,
  • "detector_height": 0,
  • "detector_pixel_depth": 2,
  • "bkg_estimate": 0.1
}

Get calibration statistics

Statistics are provided for each module/storage cell separately

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Get last preview image in JPEG format

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Get last preview image in JPEG format

Responses

Request samples

Content type
application/json
{
  • "saturation": 65535,
  • "show_spots": true,
  • "jpeg_quality": 100
}

Response samples

Content type
application/json
{
  • "msg": "Detector in wrong state",
  • "reason": "WrongDAQState"
}

Get JPEG rendering configuration

Request samples

Content type
application/json
{
  • "saturation": 65535,
  • "show_spots": true,
  • "jpeg_quality": 100
}

Response samples

Content type
application/json
{
  • "msg": "Detector in wrong state",
  • "reason": "WrongDAQState"
}

Get JPEG rendering configuration

Can be done anytime

Responses

Response samples

Content type
application/json
{
  • "saturation": 65535,
  • "show_spots": true,
  • "jpeg_quality": 100
}

Get last preview image in TIFF format

Responses

Response samples

Content type
application/json
{
  • "saturation": 65535,
  • "show_spots": true,
  • "jpeg_quality": 100
}

Get last preview image in TIFF format

Responses