Compare commits
8 Commits
1.0.0_rc.6
...
1.0.0_rc.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 8809b8d0d5 | |||
| 86b3934387 | |||
| 7d9dcf2721 | |||
| 953c3fa972 | |||
| 5d7a4e1ff2 | |||
| f85b87bfd2 | |||
| 500222bdcc | |||
| c4d677f05b |
@@ -316,11 +316,8 @@ release:
|
||||
- export PACKAGE_VERSION=`head -n1 VERSION`
|
||||
- export PACKAGE_REGISTRY_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/jungfraujoch/${PACKAGE_VERSION}"
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms.el8.noarch.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-writer.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_driver.tar.gz "${PACKAGE_REGISTRY_URL}/jfjoch_driver.tar.gz"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_frontend.tar.gz "${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_fpga_pcie_100g.mcs "${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs"'
|
||||
@@ -331,9 +328,6 @@ release:
|
||||
--assets-link "{\"name\":\"jfjoch_frontend.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz\"}"
|
||||
--assets-link "{\"name\":\"jfjoch_fpga_pcie_8x10g.mcs\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_8x10g.mcs\"}"
|
||||
--assets-link "{\"name\":\"jfjoch_fpga_pcie_100g.mcs\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch.el8.x86_64.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-writer.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer.el8.x86_64.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-driver-dkms.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms.el8.noarch.rpm\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"link_type\":\"package\"}"
|
||||
|
||||
@@ -161,6 +161,7 @@ SET(CPACK_RPM_DRIVER-DKMS_PACKAGE_REQUIRES "dkms, gcc, bash, sed")
|
||||
SET(CPACK_RPM_DRIVER-DKMS_PACKAGE_ARCHITECTURE "noarch")
|
||||
SET(CPACK_RPM_DRIVER-DKMS_POST_INSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/postinstall.sh)
|
||||
SET(CPACK_RPM_DRIVER-DKMS_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/preuninstall.sh)
|
||||
SET(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src)
|
||||
|
||||
# Set The Vendor Name
|
||||
SET(CPACK_PACKAGE_VENDOR "Paul Scherrer Institut")
|
||||
|
||||
@@ -7,6 +7,7 @@ Citation: F. Leonarski, M. Bruckner, C. Lopez-Cuenca, A. Mozzanica, H.-C. Stadle
|
||||
The project is supported by :
|
||||
* Innosuisse via Innovation Project "NextGenDCU high data rate acquisition system for X-ray detectors in structural biology applications" (101.535.1 IP-ENG; Apr 2023 - Sep 2025).
|
||||
* ETH Domain via Open Research Data Contribute project (Jan - Dec 2023)
|
||||
* AMD University Program with donation of licenses of Ethernet IP cores and Vivado software
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "JFJochBrokerHttp.h"
|
||||
#include "gen/model/Error_message.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
// From https://en.cppreference.com/w/cpp/string/byte/tolower
|
||||
inline std::string str_tolower(std::string s) {
|
||||
@@ -22,6 +23,10 @@ inline SpotFindingSettings Convert(const org::openapitools::server::model::Spot_
|
||||
ret.enable = input.isEnable();
|
||||
ret.indexing = input.isIndexing();
|
||||
ret.indexing_tolerance = input.getIndexingTolerance();
|
||||
if (input.filterPowderRingsIsSet())
|
||||
ret.filter_spots_powder_ring = input.isFilterPowderRings();
|
||||
if (input.minSpotCountPowderRingIsSet())
|
||||
ret.min_spot_count_powder_ring = input.getMinSpotCountPowderRing();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -36,6 +41,8 @@ inline org::openapitools::server::model::Spot_finding_settings Convert(const Spo
|
||||
ret.setEnable(input.enable);
|
||||
ret.setIndexing(input.indexing);
|
||||
ret.setIndexingTolerance(input.indexing_tolerance);
|
||||
ret.setFilterPowderRings(input.filter_spots_powder_ring);
|
||||
ret.setMinSpotCountPowderRing(input.min_spot_count_powder_ring);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -317,6 +324,7 @@ inline PreviewJPEGSettings Convert(const org::openapitools::server::model::Previ
|
||||
ret.saturation_value = input.getSaturation();
|
||||
ret.show_roi = input.isShowRoi();
|
||||
ret.show_indexed = input.isShowIndexed();
|
||||
ret.show_user_mask = input.isShowUserMask();
|
||||
if (input.resolutionRingIsSet())
|
||||
ret.resolution_ring = input.getResolutionRing();
|
||||
return ret;
|
||||
@@ -655,18 +663,33 @@ void JFJochBrokerHttp::preview_image_tiff_get(Pistache::Http::ResponseWriter &re
|
||||
void JFJochBrokerHttp::config_internal_generator_image_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
int64_t image_number = 0;
|
||||
auto number_query = request.query().get("number");
|
||||
auto number_query = request.query().get("id");
|
||||
if (number_query)
|
||||
image_number = std::stoi(number_query.value());
|
||||
|
||||
if ((image_number < 0) || (image_number > 127))
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "image_number must be in range 0-127");
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "id must be in range 0-127");
|
||||
|
||||
state_machine.LoadInternalGeneratorImage(request.body().data(), request.body().size(), image_number);
|
||||
logger.Info("Internal generator image #{} loaded", image_number);
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
|
||||
void JFJochBrokerHttp::config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
int64_t image_number = 0;
|
||||
auto number_query = request.query().get("id");
|
||||
if (number_query)
|
||||
image_number = std::stoi(number_query.value());
|
||||
|
||||
if ((image_number < 0) || (image_number > 127))
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "id must be in range 0-127");
|
||||
|
||||
state_machine.LoadInternalGeneratorImageTIFF(request.body(), image_number);
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::roi_box_get(Pistache::Http::ResponseWriter &response) {
|
||||
ProcessOutput(Convert(state_machine.GetBoxROI()), response);
|
||||
}
|
||||
@@ -782,3 +805,19 @@ void JFJochBrokerHttp::plot_strong_pixel_get(const std::optional<int32_t> &binni
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
GenericPlot(PlotType::StrongPixels, binning, response);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_mask_tiff_get(Pistache::Http::ResponseWriter &response) {
|
||||
std::string s = state_machine.GetFullPixelMaskTIFF();
|
||||
response.send(Pistache::Http::Code::Ok, s, Pistache::Http::Mime::MediaType::fromString("image/tiff"));
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) {
|
||||
std::string s = state_machine.GetUserPixelMaskTIFF();
|
||||
response.send(Pistache::Http::Code::Ok, s, Pistache::Http::Mime::MediaType::fromString("image/tiff"));
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_user_mask_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
state_machine.SetUserPixelMask(request.body());
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,16 @@ class JFJochBrokerHttp : public org::openapitools::server::api::DefaultApi {
|
||||
void xfel_event_code_get(Pistache::Http::ResponseWriter &response) override;
|
||||
void xfel_pulse_id_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_mask_tiff_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_user_mask_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void GetStaticFile(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
std::pair<Pistache::Http::Code, std::string> handleOperationException(const std::exception &ex) const noexcept override;
|
||||
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
|
||||
JFJochServices::JFJochServices(Logger &in_logger) : logger(in_logger) {}
|
||||
|
||||
void JFJochServices::Start(const DiffractionExperiment& experiment, const JFCalibration &calibration) {
|
||||
void JFJochServices::Start(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration &calibration) {
|
||||
logger.Info("Measurement start for: {}", experiment.GetFilePrefix());
|
||||
|
||||
if (receiver != nullptr) {
|
||||
logger.Info(" ... receiver start");
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion)
|
||||
receiver->Start(experiment, &calibration);
|
||||
receiver->Start(experiment, pixel_mask, &calibration);
|
||||
else
|
||||
receiver->Start(experiment, nullptr);
|
||||
receiver->Start(experiment, pixel_mask, nullptr);
|
||||
|
||||
if (detector && !experiment.IsUsingInternalPacketGen()) {
|
||||
logger.Info(" ... detector start");
|
||||
|
||||
@@ -23,7 +23,9 @@ public:
|
||||
void On(const DiffractionExperiment& experiment);
|
||||
void Off();
|
||||
void ConfigureDetector(const DiffractionExperiment& experiment);
|
||||
void Start(const DiffractionExperiment& experiment, const JFCalibration &calibration);
|
||||
void Start(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration &calibration);
|
||||
JFJochServicesOutput Stop();
|
||||
void Cancel();
|
||||
void Trigger();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "JFJochStateMachine.h"
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
void ApplyDetectorSettings(DiffractionExperiment& experiment, const DetectorSettings &settings) {
|
||||
auto tmp = experiment;
|
||||
@@ -66,8 +66,10 @@ void ApplyRadialIntegrationSettings(DiffractionExperiment& experiment, const Rad
|
||||
}
|
||||
|
||||
JFJochStateMachine::JFJochStateMachine(JFJochServices &in_services, Logger &in_logger)
|
||||
: services(in_services), logger(in_logger),
|
||||
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()) {
|
||||
: services(in_services),
|
||||
logger(in_logger),
|
||||
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()),
|
||||
pixel_mask(experiment) {
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +128,7 @@ void JFJochStateMachine::TakePedestalInternalAll(std::unique_lock<std::mutex> &u
|
||||
}
|
||||
}
|
||||
services.ConfigureDetector(experiment);
|
||||
pixel_mask.LoadDetectorBadPixelMask(calibration->CalculateMask());
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Pedestal sequence error {}", e.what());
|
||||
state = JFJochState::Error;
|
||||
@@ -158,7 +161,7 @@ void JFJochStateMachine::TakePedestalInternalG0(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -197,7 +200,7 @@ void JFJochStateMachine::TakePedestalInternalG1(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -236,7 +239,7 @@ void JFJochStateMachine::TakePedestalInternalG2(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -326,7 +329,7 @@ void JFJochStateMachine::Start(const DatasetSettings& settings) {
|
||||
try {
|
||||
state = JFJochState::Busy;
|
||||
services.SetSpotFindingSettings(GetSpotFindingSettings());
|
||||
services.Start(experiment, *calibration);
|
||||
services.Start(experiment, pixel_mask, *calibration);
|
||||
|
||||
state = JFJochState::Measuring;
|
||||
measurement = std::async(std::launch::async, &JFJochStateMachine::MeasurementThread, this);
|
||||
@@ -562,6 +565,7 @@ void JFJochStateMachine::AddDetectorSetup(const DetectorSetup &setup) {
|
||||
experiment.Detector(setup);
|
||||
gain_calibration = setup.GetGainCalibration();
|
||||
current_detector_setup = 0;
|
||||
pixel_mask = PixelMask(setup);
|
||||
}
|
||||
detector_setup.emplace_back(setup);
|
||||
}
|
||||
@@ -599,6 +603,7 @@ void JFJochStateMachine::SelectDetector(int64_t id) {
|
||||
try {
|
||||
experiment.Detector(detector_setup[id]);
|
||||
gain_calibration = detector_setup[id].GetGainCalibration();
|
||||
pixel_mask = PixelMask(detector_setup[id]);
|
||||
state = JFJochState::Inactive;
|
||||
current_detector_setup = id;
|
||||
} catch (JFJochException &e) {
|
||||
@@ -705,6 +710,21 @@ void JFJochStateMachine::LoadInternalGeneratorImage(const void *data, size_t siz
|
||||
services.LoadInternalGeneratorImage(experiment, image, image_number);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::LoadInternalGeneratorImageTIFF(const std::string &s, uint64_t image_number) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw WrongDAQStateException ("Can change internal generator image only when detector in Idle state");
|
||||
|
||||
uint32_t cols, lines;
|
||||
auto v = ReadTIFFFromString16(s, cols, lines);
|
||||
if (((cols == experiment.GetXPixelsNum()) && (lines == experiment.GetYPixelsNum()))
|
||||
|| ((cols == RAW_MODULE_SIZE) && (lines == RAW_MODULE_LINES * experiment.GetModulesNum())))
|
||||
services.LoadInternalGeneratorImage(experiment, v, image_number);
|
||||
else
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image size doesn't match current detector");
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetBoxROI(const std::vector<ROIBox> &input) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
@@ -744,3 +764,33 @@ std::vector<uint64_t> JFJochStateMachine::GetXFELEventCode() const {
|
||||
services.GetXFELEventCode(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string JFJochStateMachine::GetFullPixelMaskTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
std::vector v = pixel_mask.GetMask(experiment);
|
||||
return WriteTIFFToString(v.data(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum(),
|
||||
sizeof(uint32_t), false);
|
||||
}
|
||||
|
||||
std::string JFJochStateMachine::GetUserPixelMaskTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
std::vector v = pixel_mask.GetUserMask(experiment);
|
||||
return WriteTIFFToString(v.data(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum(),
|
||||
sizeof(uint32_t), false);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetUserPixelMask(const std::string &s) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw WrongDAQStateException ("User mask can be only modified in Idle state");
|
||||
|
||||
try {
|
||||
uint32_t cols, lines;
|
||||
auto v = ReadTIFFFromString32(s, cols, lines);
|
||||
pixel_mask.LoadUserMask(experiment, v);
|
||||
} catch (const JFJochException &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Problem handling user mask " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ class JFJochStateMachine {
|
||||
volatile JFJochState state = JFJochState::Inactive;
|
||||
volatile bool cancel_sequence = false;
|
||||
std::unique_ptr<JFCalibration> calibration;
|
||||
PixelMask pixel_mask;
|
||||
|
||||
std::vector<JFModuleGainCalibration> gain_calibration;
|
||||
std::vector<DetectorSetup> detector_setup;
|
||||
int64_t current_detector_setup;
|
||||
@@ -172,6 +174,7 @@ public:
|
||||
std::string GetPedestalTIFF(size_t gain_level, size_t sc) const;
|
||||
|
||||
void LoadInternalGeneratorImage(const void *data, size_t size, uint64_t image_number);
|
||||
void LoadInternalGeneratorImageTIFF(const std::string &s, uint64_t image_number);
|
||||
|
||||
// Not thread safe - only for configuration in serial context
|
||||
DiffractionExperiment& NotThreadSafe_Experiment();
|
||||
@@ -187,6 +190,10 @@ public:
|
||||
|
||||
std::vector<uint64_t> GetXFELPulseID() const;
|
||||
std::vector<uint64_t> GetXFELEventCode() const;
|
||||
|
||||
std::string GetFullPixelMaskTIFF() const;
|
||||
std::string GetUserPixelMaskTIFF() const;
|
||||
void SetUserPixelMask(const std::string &v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -37,12 +37,16 @@ void DefaultApi::setupRoutes() {
|
||||
Routes::Get(*router, base + "/config/detector", Routes::bind(&DefaultApi::config_detector_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/detector", Routes::bind(&DefaultApi::config_detector_put_handler, this));
|
||||
Routes::Put(*router, base + "/config/internal_generator_image", Routes::bind(&DefaultApi::config_internal_generator_image_put_handler, this));
|
||||
Routes::Put(*router, base + "/config/internal_generator_image.tiff", Routes::bind(&DefaultApi::config_internal_generator_image_tiff_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/mask.tiff", Routes::bind(&DefaultApi::config_mask_tiff_get_handler, this));
|
||||
Routes::Get(*router, base + "/config/rad_int", Routes::bind(&DefaultApi::config_rad_int_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/rad_int", Routes::bind(&DefaultApi::config_rad_int_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/select_detector", Routes::bind(&DefaultApi::config_select_detector_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/select_detector", Routes::bind(&DefaultApi::config_select_detector_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/spot_finding", Routes::bind(&DefaultApi::config_spot_finding_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/spot_finding", Routes::bind(&DefaultApi::config_spot_finding_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/user_mask.tiff", Routes::bind(&DefaultApi::config_user_mask_tiff_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/user_mask.tiff", Routes::bind(&DefaultApi::config_user_mask_tiff_put_handler, this));
|
||||
Routes::Post(*router, base + "/deactivate", Routes::bind(&DefaultApi::deactivate_post_handler, this));
|
||||
Routes::Get(*router, base + "/detector/status", Routes::bind(&DefaultApi::detector_status_get_handler, this));
|
||||
Routes::Post(*router, base + "/initialize", Routes::bind(&DefaultApi::initialize_post_handler, this));
|
||||
@@ -192,6 +196,45 @@ void DefaultApi::config_internal_generator_image_put_handler(const Pistache::Res
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_internal_generator_image_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
try {
|
||||
this->config_internal_generator_image_tiff_put(request, response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> 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::config_mask_tiff_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
|
||||
try {
|
||||
this->config_mask_tiff_get(response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> 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::config_rad_int_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
@@ -351,6 +394,45 @@ void DefaultApi::config_spot_finding_put_handler(const Pistache::Rest::Request &
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_user_mask_tiff_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
|
||||
try {
|
||||
this->config_user_mask_tiff_get(response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> 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::config_user_mask_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
try {
|
||||
this->config_user_mask_tiff_put(request, response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> 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::deactivate_post_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
@@ -61,12 +61,16 @@ private:
|
||||
void config_detector_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_detector_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_internal_generator_image_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_internal_generator_image_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_mask_tiff_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_rad_int_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_rad_int_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_select_detector_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_select_detector_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_spot_finding_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_spot_finding_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_user_mask_tiff_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_user_mask_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void deactivate_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void detector_status_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void initialize_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
@@ -150,6 +154,20 @@ private:
|
||||
/// </remarks>
|
||||
virtual void config_internal_generator_image_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Load TIFF image for internal FPGA generator
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Load image for internal FPGA generator. This can only happen in Idle state of the detector. Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates (depending on detector settings).
|
||||
/// </remarks>
|
||||
virtual void config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get full pixel mask of the detector See NXmx standard for meaning of pixel values
|
||||
/// </remarks>
|
||||
virtual void config_mask_tiff_get(Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get radial integration configuration
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -195,6 +213,20 @@ private:
|
||||
/// <param name="spotFindingSettings"> (optional)</param>
|
||||
virtual void config_spot_finding_put(const org::openapitools::server::model::Spot_finding_settings &spotFindingSettings, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get user mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked
|
||||
/// </remarks>
|
||||
virtual void config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Upload user mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be in `Idle` state. Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions. If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates. In the latter case - module gaps are ignored and don't need to be assigned value. Mask is expected as TIFF (4-byte; unsigned). 0 - good pixel, other value - masked User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration. User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
/// </remarks>
|
||||
virtual void config_user_mask_tiff_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Prepare detector to turn off
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
||||
@@ -26,11 +26,13 @@ Preview_settings::Preview_settings()
|
||||
m_Show_spotsIsSet = false;
|
||||
m_Show_roi = false;
|
||||
m_Show_roiIsSet = false;
|
||||
m_Jpeg_quality = 0L;
|
||||
m_Jpeg_quality = 100L;
|
||||
m_Jpeg_qualityIsSet = false;
|
||||
m_Show_indexed = false;
|
||||
m_Show_indexedIsSet = false;
|
||||
m_Resolution_ring = 0.0f;
|
||||
m_Show_user_mask = false;
|
||||
m_Show_user_maskIsSet = false;
|
||||
m_Resolution_ring = 0.1f;
|
||||
m_Resolution_ringIsSet = false;
|
||||
|
||||
}
|
||||
@@ -92,7 +94,7 @@ bool Preview_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (resolutionRingIsSet())
|
||||
{
|
||||
const float& value = m_Resolution_ring;
|
||||
@@ -136,6 +138,9 @@ bool Preview_settings::operator==(const Preview_settings& rhs) const
|
||||
((!showIndexedIsSet() && !rhs.showIndexedIsSet()) || (showIndexedIsSet() && rhs.showIndexedIsSet() && isShowIndexed() == rhs.isShowIndexed())) &&
|
||||
|
||||
|
||||
((!showUserMaskIsSet() && !rhs.showUserMaskIsSet()) || (showUserMaskIsSet() && rhs.showUserMaskIsSet() && isShowUserMask() == rhs.isShowUserMask())) &&
|
||||
|
||||
|
||||
((!resolutionRingIsSet() && !rhs.resolutionRingIsSet()) || (resolutionRingIsSet() && rhs.resolutionRingIsSet() && getResolutionRing() == rhs.getResolutionRing()))
|
||||
|
||||
;
|
||||
@@ -158,6 +163,8 @@ void to_json(nlohmann::json& j, const Preview_settings& o)
|
||||
j["jpeg_quality"] = o.m_Jpeg_quality;
|
||||
if(o.showIndexedIsSet())
|
||||
j["show_indexed"] = o.m_Show_indexed;
|
||||
if(o.showUserMaskIsSet())
|
||||
j["show_user_mask"] = o.m_Show_user_mask;
|
||||
if(o.resolutionRingIsSet())
|
||||
j["resolution_ring"] = o.m_Resolution_ring;
|
||||
|
||||
@@ -186,6 +193,11 @@ void from_json(const nlohmann::json& j, Preview_settings& o)
|
||||
j.at("show_indexed").get_to(o.m_Show_indexed);
|
||||
o.m_Show_indexedIsSet = true;
|
||||
}
|
||||
if(j.find("show_user_mask") != j.end())
|
||||
{
|
||||
j.at("show_user_mask").get_to(o.m_Show_user_mask);
|
||||
o.m_Show_user_maskIsSet = true;
|
||||
}
|
||||
if(j.find("resolution_ring") != j.end())
|
||||
{
|
||||
j.at("resolution_ring").get_to(o.m_Resolution_ring);
|
||||
@@ -270,6 +282,23 @@ void Preview_settings::unsetShow_indexed()
|
||||
{
|
||||
m_Show_indexedIsSet = false;
|
||||
}
|
||||
bool Preview_settings::isShowUserMask() const
|
||||
{
|
||||
return m_Show_user_mask;
|
||||
}
|
||||
void Preview_settings::setShowUserMask(bool const value)
|
||||
{
|
||||
m_Show_user_mask = value;
|
||||
m_Show_user_maskIsSet = true;
|
||||
}
|
||||
bool Preview_settings::showUserMaskIsSet() const
|
||||
{
|
||||
return m_Show_user_maskIsSet;
|
||||
}
|
||||
void Preview_settings::unsetShow_user_mask()
|
||||
{
|
||||
m_Show_user_maskIsSet = false;
|
||||
}
|
||||
float Preview_settings::getResolutionRing() const
|
||||
{
|
||||
return m_Resolution_ring;
|
||||
|
||||
@@ -91,6 +91,13 @@ public:
|
||||
bool showIndexedIsSet() const;
|
||||
void unsetShow_indexed();
|
||||
/// <summary>
|
||||
/// Show user mask
|
||||
/// </summary>
|
||||
bool isShowUserMask() const;
|
||||
void setShowUserMask(bool const value);
|
||||
bool showUserMaskIsSet() const;
|
||||
void unsetShow_user_mask();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
float getResolutionRing() const;
|
||||
@@ -111,6 +118,8 @@ protected:
|
||||
bool m_Jpeg_qualityIsSet;
|
||||
bool m_Show_indexed;
|
||||
bool m_Show_indexedIsSet;
|
||||
bool m_Show_user_mask;
|
||||
bool m_Show_user_maskIsSet;
|
||||
float m_Resolution_ring;
|
||||
bool m_Resolution_ringIsSet;
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ Spot_finding_settings::Spot_finding_settings()
|
||||
{
|
||||
m_Enable = true;
|
||||
m_Indexing = true;
|
||||
m_Filter_powder_rings = false;
|
||||
m_Filter_powder_ringsIsSet = false;
|
||||
m_Min_spot_count_powder_ring = 20L;
|
||||
m_Min_spot_count_powder_ringIsSet = false;
|
||||
m_Signal_to_noise_threshold = 0.0f;
|
||||
m_Photon_count_threshold = 0L;
|
||||
m_Min_pix_per_spot = 0L;
|
||||
@@ -52,7 +56,21 @@ bool Spot_finding_settings::validate(std::stringstream& msg, const std::string&
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "Spot_finding_settings" : pathPrefix;
|
||||
|
||||
|
||||
|
||||
if (minSpotCountPowderRingIsSet())
|
||||
{
|
||||
const int64_t& value = m_Min_spot_count_powder_ring;
|
||||
const std::string currentValuePath = _pathPrefix + ".minSpotCountPowderRing";
|
||||
|
||||
|
||||
if (value < 5ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 5;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Signal_to_noise_threshold */ {
|
||||
const float& value = m_Signal_to_noise_threshold;
|
||||
@@ -142,6 +160,12 @@ bool Spot_finding_settings::operator==(const Spot_finding_settings& rhs) const
|
||||
(isIndexing() == rhs.isIndexing())
|
||||
&&
|
||||
|
||||
|
||||
((!filterPowderRingsIsSet() && !rhs.filterPowderRingsIsSet()) || (filterPowderRingsIsSet() && rhs.filterPowderRingsIsSet() && isFilterPowderRings() == rhs.isFilterPowderRings())) &&
|
||||
|
||||
|
||||
((!minSpotCountPowderRingIsSet() && !rhs.minSpotCountPowderRingIsSet()) || (minSpotCountPowderRingIsSet() && rhs.minSpotCountPowderRingIsSet() && getMinSpotCountPowderRing() == rhs.getMinSpotCountPowderRing())) &&
|
||||
|
||||
(getSignalToNoiseThreshold() == rhs.getSignalToNoiseThreshold())
|
||||
&&
|
||||
|
||||
@@ -176,6 +200,10 @@ void to_json(nlohmann::json& j, const Spot_finding_settings& o)
|
||||
j = nlohmann::json();
|
||||
j["enable"] = o.m_Enable;
|
||||
j["indexing"] = o.m_Indexing;
|
||||
if(o.filterPowderRingsIsSet())
|
||||
j["filter_powder_rings"] = o.m_Filter_powder_rings;
|
||||
if(o.minSpotCountPowderRingIsSet())
|
||||
j["min_spot_count_powder_ring"] = o.m_Min_spot_count_powder_ring;
|
||||
j["signal_to_noise_threshold"] = o.m_Signal_to_noise_threshold;
|
||||
j["photon_count_threshold"] = o.m_Photon_count_threshold;
|
||||
j["min_pix_per_spot"] = o.m_Min_pix_per_spot;
|
||||
@@ -190,6 +218,16 @@ void from_json(const nlohmann::json& j, Spot_finding_settings& o)
|
||||
{
|
||||
j.at("enable").get_to(o.m_Enable);
|
||||
j.at("indexing").get_to(o.m_Indexing);
|
||||
if(j.find("filter_powder_rings") != j.end())
|
||||
{
|
||||
j.at("filter_powder_rings").get_to(o.m_Filter_powder_rings);
|
||||
o.m_Filter_powder_ringsIsSet = true;
|
||||
}
|
||||
if(j.find("min_spot_count_powder_ring") != j.end())
|
||||
{
|
||||
j.at("min_spot_count_powder_ring").get_to(o.m_Min_spot_count_powder_ring);
|
||||
o.m_Min_spot_count_powder_ringIsSet = true;
|
||||
}
|
||||
j.at("signal_to_noise_threshold").get_to(o.m_Signal_to_noise_threshold);
|
||||
j.at("photon_count_threshold").get_to(o.m_Photon_count_threshold);
|
||||
j.at("min_pix_per_spot").get_to(o.m_Min_pix_per_spot);
|
||||
@@ -216,6 +254,40 @@ void Spot_finding_settings::setIndexing(bool const value)
|
||||
{
|
||||
m_Indexing = value;
|
||||
}
|
||||
bool Spot_finding_settings::isFilterPowderRings() const
|
||||
{
|
||||
return m_Filter_powder_rings;
|
||||
}
|
||||
void Spot_finding_settings::setFilterPowderRings(bool const value)
|
||||
{
|
||||
m_Filter_powder_rings = value;
|
||||
m_Filter_powder_ringsIsSet = true;
|
||||
}
|
||||
bool Spot_finding_settings::filterPowderRingsIsSet() const
|
||||
{
|
||||
return m_Filter_powder_ringsIsSet;
|
||||
}
|
||||
void Spot_finding_settings::unsetFilter_powder_rings()
|
||||
{
|
||||
m_Filter_powder_ringsIsSet = false;
|
||||
}
|
||||
int64_t Spot_finding_settings::getMinSpotCountPowderRing() const
|
||||
{
|
||||
return m_Min_spot_count_powder_ring;
|
||||
}
|
||||
void Spot_finding_settings::setMinSpotCountPowderRing(int64_t const value)
|
||||
{
|
||||
m_Min_spot_count_powder_ring = value;
|
||||
m_Min_spot_count_powder_ringIsSet = true;
|
||||
}
|
||||
bool Spot_finding_settings::minSpotCountPowderRingIsSet() const
|
||||
{
|
||||
return m_Min_spot_count_powder_ringIsSet;
|
||||
}
|
||||
void Spot_finding_settings::unsetMin_spot_count_powder_ring()
|
||||
{
|
||||
m_Min_spot_count_powder_ringIsSet = false;
|
||||
}
|
||||
float Spot_finding_settings::getSignalToNoiseThreshold() const
|
||||
{
|
||||
return m_Signal_to_noise_threshold;
|
||||
|
||||
@@ -58,16 +58,30 @@ public:
|
||||
/// Spot_finding_settings members
|
||||
|
||||
/// <summary>
|
||||
/// Enable spot finding
|
||||
/// Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection. Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
/// </summary>
|
||||
bool isEnable() const;
|
||||
void setEnable(bool const value);
|
||||
/// <summary>
|
||||
/// Enable indexing
|
||||
/// Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
/// </summary>
|
||||
bool isIndexing() const;
|
||||
void setIndexing(bool const value);
|
||||
/// <summary>
|
||||
/// Filter spots which form powder rings (e.g., ice rings)
|
||||
/// </summary>
|
||||
bool isFilterPowderRings() const;
|
||||
void setFilterPowderRings(bool const value);
|
||||
bool filterPowderRingsIsSet() const;
|
||||
void unsetFilter_powder_rings();
|
||||
/// <summary>
|
||||
/// Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
/// </summary>
|
||||
int64_t getMinSpotCountPowderRing() const;
|
||||
void setMinSpotCountPowderRing(int64_t const value);
|
||||
bool minSpotCountPowderRingIsSet() const;
|
||||
void unsetMin_spot_count_powder_ring();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
float getSignalToNoiseThreshold() const;
|
||||
@@ -110,6 +124,10 @@ protected:
|
||||
|
||||
bool m_Indexing;
|
||||
|
||||
bool m_Filter_powder_rings;
|
||||
bool m_Filter_powder_ringsIsSet;
|
||||
int64_t m_Min_spot_count_powder_ring;
|
||||
bool m_Min_spot_count_powder_ringIsSet;
|
||||
float m_Signal_to_noise_threshold;
|
||||
|
||||
int64_t m_Photon_count_threshold;
|
||||
|
||||
@@ -333,11 +333,23 @@ components:
|
||||
enable:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Enable spot finding
|
||||
description: |
|
||||
Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
indexing:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Enable indexing
|
||||
description: |
|
||||
Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
filter_powder_rings:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Filter spots which form powder rings (e.g., ice rings)
|
||||
min_spot_count_powder_ring:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 5
|
||||
description: Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
signal_to_noise_threshold:
|
||||
type: number
|
||||
format: float
|
||||
@@ -620,14 +632,21 @@ components:
|
||||
type: integer
|
||||
description: "Quality of JPEG image (100 - highest; 0 - lowest)"
|
||||
format: int64
|
||||
default: 100
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
show_indexed:
|
||||
type: boolean
|
||||
description: "Preview indexed images only"
|
||||
default: false
|
||||
show_user_mask:
|
||||
type: boolean
|
||||
description: "Show user mask"
|
||||
default: false
|
||||
resolution_ring:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.1
|
||||
minimum: 0.1
|
||||
maximum: 100.0
|
||||
error_message:
|
||||
@@ -967,9 +986,9 @@ paths:
|
||||
Requires binary blob with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
(depending on detector settings).
|
||||
parameters:
|
||||
- name: number
|
||||
- name: id
|
||||
in: query
|
||||
description: Image number to upload
|
||||
description: Image id to upload
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
@@ -992,6 +1011,38 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
description: Exception error
|
||||
/config/internal_generator_image.tiff:
|
||||
put:
|
||||
summary: Load TIFF image for internal FPGA generator
|
||||
description: |
|
||||
Load image for internal FPGA generator. This can only happen in Idle state of the detector.
|
||||
Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
(depending on detector settings).
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: Image ID to upload
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 127
|
||||
requestBody:
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
"200":
|
||||
description: Everything OK
|
||||
"400":
|
||||
description: Input parsing or validation error
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: Exception error
|
||||
/config/select_detector:
|
||||
put:
|
||||
summary: Select detector
|
||||
@@ -1564,6 +1615,63 @@ paths:
|
||||
format: binary
|
||||
"404":
|
||||
description: No preview image recorded so far
|
||||
/config/mask.tiff:
|
||||
get:
|
||||
summary: Get mask of the detector
|
||||
description: |
|
||||
Get full pixel mask of the detector
|
||||
See NXmx standard for meaning of pixel values
|
||||
responses:
|
||||
"200":
|
||||
description: Pixel mask in TIFF format (4 byte; unsigned)
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
/config/user_mask.tiff:
|
||||
get:
|
||||
summary: Get user mask of the detector
|
||||
description: "Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked"
|
||||
responses:
|
||||
"200":
|
||||
description: User mask in TIFF format (4 byte; unsigned)
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
put:
|
||||
summary: Upload user mask of the detector
|
||||
description: |
|
||||
Should be in `Idle` state.
|
||||
Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions.
|
||||
If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates.
|
||||
In the latter case - module gaps are ignored and don't need to be assigned value.
|
||||
Mask is expected as TIFF (4-byte; unsigned).
|
||||
0 - good pixel, other value - masked
|
||||
User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration.
|
||||
User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
requestBody:
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
"200":
|
||||
description: All good
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
"500":
|
||||
description: Error within Jungfraujoch code - see output message.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/error_message'
|
||||
/preview/pedestal.tiff:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -29,8 +29,6 @@ DiffractionExperiment::DiffractionExperiment(const DetectorSetup& det_setup)
|
||||
internal_fpga_packet_generator = false;
|
||||
internal_fpga_packet_generator_images = 1;
|
||||
|
||||
debug_pixel_mask = false;
|
||||
|
||||
ndatastreams = 1;
|
||||
|
||||
frame_time = std::chrono::microseconds(MIN_FRAME_TIME_HALF_SPEED_IN_US);
|
||||
@@ -684,6 +682,7 @@ void DiffractionExperiment::CheckDataProcessingSettings(const SpotFindingSetting
|
||||
check_min("Spot finding low resolution limit", settings.low_resolution_limit, 1.0);
|
||||
check_max("Spot finding low resolution limit", settings.low_resolution_limit, 50.0);
|
||||
}
|
||||
check_min("min spot count for powder ring detection", settings.min_spot_count_powder_ring, 5);
|
||||
check_min("indexing tolerance", settings.indexing_tolerance, 0.0);
|
||||
check_max("indexing tolerance", settings.indexing_tolerance, 1.0);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ class DiffractionExperiment {
|
||||
std::string instrument_name_short;
|
||||
bool pulsed_source;
|
||||
|
||||
bool debug_pixel_mask;
|
||||
Coord default_omega_axis;
|
||||
|
||||
bool conversion_on_fpga;
|
||||
|
||||
@@ -12,7 +12,7 @@ PixelMask::PixelMask(const DetectorSetup &detector)
|
||||
PixelMask::PixelMask(const DiffractionExperiment& experiment)
|
||||
: PixelMask(experiment.GetDetectorSetup()) {}
|
||||
|
||||
void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask, uint8_t bit) {
|
||||
void PixelMask::LoadMask(const std::vector<uint32_t> &input_mask, uint8_t bit) {
|
||||
if (input_mask.size() != mask.size())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Input match doesn't fit the detector ");
|
||||
@@ -25,7 +25,11 @@ void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment) {
|
||||
void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask) {
|
||||
LoadMask(input_mask, ErrorPixelBit);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment, bool conv) const {
|
||||
std::vector<uint32_t> mask_out = mask;
|
||||
|
||||
// apply edge mask
|
||||
@@ -39,7 +43,7 @@ std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment
|
||||
|| (line == RAW_MODULE_LINES - 1)
|
||||
|| (col == 0)
|
||||
|| (col == RAW_MODULE_COLS - 1))
|
||||
mask_out[pixel] |= (1 << 30);
|
||||
mask_out[pixel] |= (1 << ModuleEdgePixelBit);
|
||||
}
|
||||
|
||||
if (experiment.GetMaskChipEdges()) {
|
||||
@@ -47,33 +51,39 @@ std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment
|
||||
|| (col == 511) || (col == 512)
|
||||
|| (col == 767) || (col == 768)
|
||||
|| (line == 255) || (line== 256))
|
||||
mask_out[pixel] |= (1 << 31);
|
||||
mask_out[pixel] |= (1 << ChipGapPixelBit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion) {
|
||||
std::vector<uint32_t> tmp(experiment.GetPixelsNum(), 1); // nonfuctional areas (i.e. gaps) are filled with 1
|
||||
if (conv && (experiment.GetDetectorMode() == DetectorMode::Conversion)) {
|
||||
std::vector<uint32_t> tmp(experiment.GetPixelsNum(), 1<<ModuleGapPixelBit); // nonfunctional areas (i.e. gaps) are filled with 1
|
||||
RawToConvertedGeometry<uint32_t, uint32_t>(experiment, tmp.data(), mask_out.data());
|
||||
return tmp;
|
||||
} else
|
||||
return mask_out;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetUserMask(const DiffractionExperiment &experiment, bool conv) const {
|
||||
std::vector<uint32_t> ret = GetMask(experiment, conv);
|
||||
for (auto &i: ret)
|
||||
i = ((i & (1 << UserMaskedPixelBit)) != 0) ? 1 : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PixelMask::LoadUserMask(const DiffractionExperiment &experiment,
|
||||
const std::vector<uint32_t> &in_mask,
|
||||
uint8_t bit) {
|
||||
const std::vector<uint32_t> &in_mask) {
|
||||
if (experiment.GetModulesNum() != nmodules)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Mismatch in module size");
|
||||
|
||||
if (in_mask.size() == nmodules * RAW_MODULE_SIZE) {
|
||||
LoadDetectorBadPixelMask(in_mask, bit);
|
||||
LoadMask(in_mask, UserMaskedPixelBit);
|
||||
} else if (in_mask.size() == experiment.GetPixelsNum()) {
|
||||
std::vector<uint32_t> raw_mask(nmodules * RAW_MODULE_SIZE);
|
||||
ConvertedToRawGeometry(experiment, raw_mask.data(), in_mask.data());
|
||||
LoadDetectorBadPixelMask(in_mask, bit);
|
||||
LoadMask(raw_mask, UserMaskedPixelBit);
|
||||
} else
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Size of input user mask invalid");
|
||||
|
||||
@@ -9,12 +9,21 @@
|
||||
class PixelMask {
|
||||
size_t nmodules;
|
||||
std::vector<uint32_t> mask;
|
||||
void LoadMask(const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
public:
|
||||
// NXmx bits
|
||||
constexpr static const uint8_t ModuleGapPixelBit = 0;
|
||||
constexpr static const uint8_t ErrorPixelBit = 1;
|
||||
constexpr static const uint8_t UserMaskedPixelBit = 8;
|
||||
constexpr static const uint8_t ChipGapPixelBit = 31;
|
||||
constexpr static const uint8_t ModuleEdgePixelBit = 30;
|
||||
|
||||
PixelMask(const DetectorSetup& detector);
|
||||
PixelMask(const DiffractionExperiment& experiment);
|
||||
void LoadUserMask(const DiffractionExperiment& experiment, const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
void LoadDetectorBadPixelMask(const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
std::vector<uint32_t> GetMask(const DiffractionExperiment& experiment);
|
||||
void LoadUserMask(const DiffractionExperiment& experiment, const std::vector<uint32_t>& mask);
|
||||
void LoadDetectorBadPixelMask(const std::vector<uint32_t>& mask);
|
||||
std::vector<uint32_t> GetMask(const DiffractionExperiment& experiment, bool conv = true) const;
|
||||
std::vector<uint32_t> GetUserMask(const DiffractionExperiment& experiment, bool conv = true) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
enum class PlotType {BkgEstimate, RadInt, RadIntPerTimePoint, SpotCount, IndexingRate, IndexingRatePerTimePoint,
|
||||
ErrorPixels, ImageCollectionEfficiency, ReceiverDelay, ReceiverFreeSendBuf, StrongPixels,
|
||||
ROISum, ROIMaxCount, ROIPixels};
|
||||
enum class PlotType {
|
||||
BkgEstimate, RadInt, RadIntPerTimePoint, SpotCount, SpotCountInRings,
|
||||
IndexingRate, IndexingRatePerTimePoint,
|
||||
ErrorPixels, ImageCollectionEfficiency, ReceiverDelay, ReceiverFreeSendBuf, StrongPixels,
|
||||
ROISum, ROIMaxCount, ROIPixels
|
||||
};
|
||||
|
||||
struct PlotRequest {
|
||||
PlotType type;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
#include "Plot.h"
|
||||
|
||||
@@ -37,6 +38,11 @@ template <class T> class StatusVector {
|
||||
return ret;
|
||||
}
|
||||
public:
|
||||
void AddElement(uint32_t id, std::optional<T> val) {
|
||||
if (val.has_value())
|
||||
AddElement(id, val.value());
|
||||
}
|
||||
|
||||
void AddElement(uint32_t id, T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (id >= content.size()) {
|
||||
|
||||
@@ -13,13 +13,18 @@
|
||||
struct spot_finder_packet {
|
||||
ap_uint<768> data;
|
||||
ap_uint<32> mask;
|
||||
ap_uint<32> strong_pixel;
|
||||
ap_int<32> count_threshold;
|
||||
ap_uint<32> snr_threshold;
|
||||
ap_uint<1> user;
|
||||
ap_uint<1> last;
|
||||
};
|
||||
|
||||
void spot_finder_in_stream(STREAM_768 &data_in,
|
||||
hls::stream<ap_uint<32>> &mask_in,
|
||||
hls::stream<spot_finder_packet> &data_out) {
|
||||
hls::stream<spot_finder_packet> &data_out,
|
||||
volatile ap_int<32> &in_count_threshold,
|
||||
volatile ap_uint<32> &in_snr_threshold) {
|
||||
ap_uint<32> mask;
|
||||
packet_768_t packet_in;
|
||||
{
|
||||
@@ -33,12 +38,53 @@ void spot_finder_in_stream(STREAM_768 &data_in,
|
||||
while (!packet_in.user) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
mask_in >> mask;
|
||||
data_out << spot_finder_packet{.data = packet_in.data, .mask = mask, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << spot_finder_packet{
|
||||
.data = packet_in.data,
|
||||
.mask = mask,
|
||||
.strong_pixel = 0,
|
||||
.count_threshold = in_count_threshold,
|
||||
.snr_threshold = in_snr_threshold,
|
||||
.user = packet_in.user,
|
||||
.last = packet_in.last
|
||||
};
|
||||
data_in >> packet_in;
|
||||
}
|
||||
data_out << spot_finder_packet{.data = packet_in.data, .mask = 0, .user = packet_in.user, .last = packet_in.last};
|
||||
}
|
||||
|
||||
void spot_finder_out_stream(hls::stream<spot_finder_packet> &data_in,
|
||||
STREAM_768 &data_out,
|
||||
hls::stream<ap_axiu<32,1,1,1>> &strong_pixel_out) {
|
||||
spot_finder_packet packet_in;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
data_in >> packet_in;
|
||||
ap_wait();
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
ap_wait();
|
||||
}
|
||||
|
||||
data_in >> packet_in;
|
||||
while (!packet_in.user) {
|
||||
ap_uint<32> count_threshold = packet_in.count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = packet_in.snr_threshold;
|
||||
for (int i = 0; i < RAW_MODULE_SIZE * sizeof(uint16_t) / 64; i++) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = packet_in.strong_pixel, .user = 0};
|
||||
data_in >> packet_in;
|
||||
}
|
||||
|
||||
// Save module statistics
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = count_threshold, .user = 0};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = snr_threshold_u32, .user = 0};
|
||||
for (int i = 0; i < 14;i++)
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 0};
|
||||
}
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 1};
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
}
|
||||
|
||||
ap_int<SUM_BITWIDTH> calc_sum(ap_int<24> val[32], ap_uint<32> mask) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
ap_int<SUM_BITWIDTH> ret = 0;
|
||||
@@ -73,6 +119,8 @@ void spot_finder_prepare(hls::stream<spot_finder_packet> &data_in,
|
||||
hls::stream<ap_int<SUM_BITWIDTH>> &sum_out,
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>> &sum2_out,
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_out) {
|
||||
ap_uint<32> strong_pixel;
|
||||
|
||||
spot_finder_packet packet;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
@@ -95,6 +143,7 @@ void spot_finder_prepare(hls::stream<spot_finder_packet> &data_in,
|
||||
for (ap_uint<15> i = 0; i < RAW_MODULE_SIZE * sizeof(uint16_t) / 64; i++) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
data_out << packet;
|
||||
|
||||
ap_int<24> val[32];
|
||||
unpack32(packet.data, val);
|
||||
ap_uint<32> mask = packet.mask;
|
||||
@@ -167,31 +216,28 @@ ap_uint<32> spot_finder_count_threshold(ap_int<24> val[32], ap_int<32> &count_th
|
||||
}
|
||||
|
||||
void spot_finder_apply_threshold(hls::stream<spot_finder_packet> &data_in,
|
||||
STREAM_768 &data_out,
|
||||
hls::stream<spot_finder_packet> &data_out,
|
||||
hls::stream<ap_int<SUM_BITWIDTH>> &sum_in,
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>> &sum2_in,
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_in,
|
||||
hls::stream<ap_axiu<32,1,1,1>> &strong_pixel_out,
|
||||
volatile ap_int<32> &in_count_threshold,
|
||||
volatile ap_uint<32> &in_snr_threshold) {
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_in) {
|
||||
ap_uint<32> strong_pixel_prev;
|
||||
spot_finder_packet packet_in;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
data_in >> packet_in;
|
||||
ap_wait();
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << packet_in;
|
||||
ap_wait();
|
||||
}
|
||||
|
||||
|
||||
ap_int<SUM_BITWIDTH> sum[32];
|
||||
ap_int<SUM2_BITWIDTH> sum2[32];
|
||||
ap_int<VALID_BITWIDTH> valid[32];
|
||||
|
||||
data_in >> packet_in;
|
||||
while (!packet_in.user) {
|
||||
ap_int<32> count_threshold = in_count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = in_snr_threshold;
|
||||
ap_int<32> count_threshold = packet_in.count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = packet_in.snr_threshold;
|
||||
float_uint32 thr;
|
||||
thr.u = snr_threshold_u32;
|
||||
ap_ufixed<10,8, AP_RND_CONV> snr_threshold = thr.f;
|
||||
@@ -212,34 +258,28 @@ void spot_finder_apply_threshold(hls::stream<spot_finder_packet> &data_in,
|
||||
valid_in >> valid[i % 32];
|
||||
}
|
||||
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
ap_int<24> data_unpacked[32];
|
||||
unpack32(packet_in.data, data_unpacked);
|
||||
|
||||
ap_uint<32> strong_pixel = spot_finder_count_threshold(data_unpacked, count_threshold) &
|
||||
spot_finder_snr_threshold(data_unpacked, snr_threshold_2,
|
||||
sum[i % 32], sum2[i % 32], valid[i % 32]);
|
||||
|
||||
strong_pixel = strong_pixel & packet_in.mask;
|
||||
spot_finder_snr_threshold(data_unpacked, snr_threshold_2,
|
||||
sum[i % 32], sum2[i % 32], valid[i % 32]);
|
||||
|
||||
if ((snr_threshold == 0) && (count_threshold <= 0))
|
||||
strong_pixel = 0;
|
||||
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = strong_pixel, .user = 0};
|
||||
strong_pixel = strong_pixel & packet_in.mask;
|
||||
|
||||
packet_in.mask |= strong_pixel; // mask strong pixels
|
||||
packet_in.strong_pixel |= strong_pixel; // add strong pixels to the output
|
||||
|
||||
data_out << packet_in;
|
||||
data_in >> packet_in;
|
||||
}
|
||||
|
||||
// Save module statistics
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = count_threshold, .user = 0};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = snr_threshold_u32, .user = 0};
|
||||
for (int i = 0; i < 14;i++)
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 0};
|
||||
}
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 1};
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << packet_in;
|
||||
}
|
||||
|
||||
|
||||
void spot_finder(STREAM_768 &data_in,
|
||||
hls::stream<ap_uint<32>> &mask_in,
|
||||
STREAM_768 &data_out,
|
||||
@@ -256,22 +296,38 @@ void spot_finder(STREAM_768 &data_in,
|
||||
|
||||
hls::stream<spot_finder_packet, 2> data_0;
|
||||
hls::stream<spot_finder_packet, 1080> data_1;
|
||||
hls::stream<spot_finder_packet, 8> data_2;
|
||||
hls::stream<spot_finder_packet, 1080> data_3;
|
||||
hls::stream<spot_finder_packet, 8> data_4;
|
||||
|
||||
#pragma HLS BIND_STORAGE variable=data_1 type=fifo impl=bram
|
||||
#pragma HLS BIND_STORAGE variable=data_3 type=fifo impl=bram
|
||||
|
||||
hls::stream<ap_int<SUM_BITWIDTH>, 24> sum_0;
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>, 24> sum2_0;
|
||||
hls::stream<ap_int<VALID_BITWIDTH>, 24> valid_0;
|
||||
|
||||
hls::stream<ap_int<SUM_BITWIDTH>, 24> sum_1;
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>, 24> sum2_1;
|
||||
hls::stream<ap_int<VALID_BITWIDTH>, 24> valid_1;
|
||||
|
||||
#ifndef JFJOCH_HLS_NOSYNTH
|
||||
spot_finder_in_stream(data_in, mask_in, data_0);
|
||||
spot_finder_in_stream(data_in, mask_in, data_0, in_count_threshold, in_snr_threshold);
|
||||
spot_finder_prepare(data_0, data_1, sum_0, sum2_0, valid_0);
|
||||
spot_finder_apply_threshold(data_1, data_out, sum_0, sum2_0, valid_0, strong_pixel_out,
|
||||
in_count_threshold, in_snr_threshold);
|
||||
spot_finder_apply_threshold(data_1, data_2, sum_0, sum2_0, valid_0);
|
||||
spot_finder_prepare(data_2, data_3, sum_1, sum2_1, valid_1);
|
||||
spot_finder_apply_threshold(data_3, data_4, sum_1, sum2_1, valid_1);
|
||||
spot_finder_out_stream(data_4, data_out, strong_pixel_out);
|
||||
#else
|
||||
std::vector<std::thread> spot_finder_cores;
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_in_stream(data_in, mask_in, data_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_in_stream(data_in, mask_in, data_0, in_count_threshold,
|
||||
in_snr_threshold);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_prepare(data_0, data_1, sum_0, sum2_0, valid_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_1, data_out, sum_0, sum2_0, valid_0,
|
||||
strong_pixel_out, in_count_threshold,
|
||||
in_snr_threshold);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_1, data_2, sum_0, sum2_0, valid_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_prepare(data_2, data_3, sum_1, sum2_1, valid_1);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_3, data_4, sum_1, sum2_1, valid_1);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_out_stream(data_4, data_out, strong_pixel_out);});
|
||||
|
||||
for (auto &i : spot_finder_cores)
|
||||
i.join();
|
||||
#endif
|
||||
|
||||
@@ -74,8 +74,8 @@ void spot_finder_mask(STREAM_768 &data_in,
|
||||
|| (((col == 0) || (col == 8) || (col == 16) || (col == 24)) && (j == 0))
|
||||
|| (pixel_val[j] == INT24_MIN)
|
||||
|| (pixel_val[j] == INT24_MAX)
|
||||
|| ((d[j] != 0) && (d[j] < min_d_value))
|
||||
|| ((d[j] != 0) && (d[j] > max_d_value)))
|
||||
|| (d[j] < min_d_value)
|
||||
|| (d[j] > max_d_value))
|
||||
mask_val[j] = 0;
|
||||
else
|
||||
mask_val[j] = 1;
|
||||
|
||||
@@ -54,12 +54,13 @@ int jfjoch_check_version(struct pci_dev *pdev) {
|
||||
release_level = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_RELEASE_LEVEL);
|
||||
|
||||
if (action_type != JFJOCH_FPGA_MAGIC) {
|
||||
dev_err(dev, "Mismatch in JFJoch action type (%x)\n", action_type);
|
||||
dev_err(dev, "Very likely the FPGA is not flashed with Jungfraujoch design, given that action type register value is wrong (%x).\n", action_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (release_level != JFJOCH_FPGA_RELEASE) {
|
||||
dev_err(dev, "Mismatch in JFJoch release level (%x)\n", release_level);
|
||||
dev_err(dev, "Jungfraujoch FPGA is flashed with design made with release %x, while driver is compiled with release %x.\n", release_level, JFJOCH_FPGA_RELEASE);
|
||||
dev_err(dev, "Given FPGA releases have braking changes in FPGA-driver interface (register map, data structure size, etc.)\n it is not safe and not supported at the moment to operate multiple releases with one driver.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -456,6 +456,8 @@ namespace {
|
||||
message.user_data = GetCBORString(value);
|
||||
else if (key == "spots")
|
||||
GetCBORSpots(message, value);
|
||||
else if (key == "spot_count_in_rings")
|
||||
message.spot_count_in_rings = GetCBORUInt(value);
|
||||
else if (key == "az_int_profile")
|
||||
GetCBORFloatArray(value, message.az_int_profile);
|
||||
else if (key == "indexing_result")
|
||||
|
||||
@@ -450,6 +450,8 @@ void CBORStream2Serializer::SerializeImage(const DataMessage& message) {
|
||||
CBOR_ENC_RATIONAL(mapEncoder, "end_time", message.timestamp + message.exptime, message.timestamp_base);
|
||||
|
||||
CBOR_ENC(mapEncoder, "spots", message.spots);
|
||||
CBOR_ENC(mapEncoder, "spot_count_in_rings", message.spot_count_in_rings);
|
||||
|
||||
CBOR_ENC(mapEncoder, "az_int_profile", message.az_int_profile);
|
||||
CBOR_ENC(mapEncoder, "indexing_result", message.indexing_result);
|
||||
if (!message.indexing_lattice.empty())
|
||||
|
||||
@@ -45,6 +45,7 @@ struct DataMessage {
|
||||
float image_collection_efficiency;
|
||||
|
||||
std::vector<SpotToSave> spots;
|
||||
std::optional<uint64_t> spot_count_in_rings = 0;
|
||||
|
||||
std::vector<float> az_int_profile;
|
||||
float bkg_estimate;
|
||||
|
||||
10
frontend_ui/package-lock.json
generated
10
frontend_ui/package-lock.json
generated
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "jungfrau-frontend",
|
||||
"version": "0.1.0",
|
||||
"name": "jungfraujoch-frontend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jungfrau-frontend",
|
||||
"version": "0.1.0",
|
||||
"license": "GPL-3.0",
|
||||
"name": "jungfraujoch-frontend",
|
||||
"version": "1.0.0",
|
||||
"license": "Proprietary",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.4",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jungfrau-frontend",
|
||||
"version": "0.1.0",
|
||||
"license": "GPL-3.0",
|
||||
"name": "jungfraujoch-frontend",
|
||||
"version": "1.0.0",
|
||||
"license": "Proprietary",
|
||||
"private": true,
|
||||
"homepage": "/frontend",
|
||||
"dependencies": {
|
||||
@@ -29,7 +29,7 @@
|
||||
"redocly": "redocly build-docs ../broker/jfjoch_api.yaml --output=build/openapi.html",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"openapi": "openapi --input ../broker/jfjoch_api.yaml --output ./src/openapi"
|
||||
"openapi": "./node_modules/openapi-typescript-codegen/bin/index.js -i ../broker/jfjoch_api.yaml --output ./src/openapi"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
||||
@@ -80,7 +80,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 630, width: "100%" }}>
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 700, width: "100%" }}>
|
||||
<Toolbar>
|
||||
|
||||
<Grid container sx={{ minWidth: 500 }} >
|
||||
|
||||
@@ -17,6 +17,8 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
s: {
|
||||
enable: true,
|
||||
indexing: true,
|
||||
filter_powder_rings: false,
|
||||
min_spot_count_powder_ring: 20,
|
||||
photon_count_threshold: 8,
|
||||
signal_to_noise_threshold: 3.0,
|
||||
min_pix_per_spot: 2,
|
||||
@@ -50,10 +52,6 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
this.interval = setInterval(() => this.getValues(), 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
setPhotonCountThreshold = (event: Event, newValue: number | number[]) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
@@ -64,6 +62,7 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
}
|
||||
));
|
||||
this.putValues();
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
setSignalToNoiseThreshold = (event: Event, newValue: number | number[]) => {
|
||||
@@ -132,8 +131,28 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
enableFilterPowderRingToggle = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let x = this.state;
|
||||
x.s.filter_powder_rings = event.target.checked;
|
||||
this.putValues2(x);
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
setMinSpotCountPowderRing = (event: Event, newValue: number | number[]) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
s : {
|
||||
...prevState.s,
|
||||
min_spot_count_powder_ring: newValue as number
|
||||
}
|
||||
}
|
||||
));
|
||||
this.putValues();
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 630, width: '100%' }}>
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 700, width: '100%' }}>
|
||||
<Grid container spacing={0}>
|
||||
|
||||
<Grid item xs={1}/>
|
||||
@@ -141,43 +160,53 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
<br/><strong>Spot finding parameters</strong><br/><br/>
|
||||
<Switch onChange={this.enableSpotFindingToggle} checked={this.state.s.enable}
|
||||
disabled={this.state.connection_error}/>
|
||||
Enable spot finding
|
||||
<Switch onChange={this.enableIndexingToggle} checked={this.state.s.indexing}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Enable indexing <br/><br/>
|
||||
Spot finding
|
||||
<br/><br/>
|
||||
|
||||
<Typography gutterBottom> Count threshold </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.photon_count_threshold)}
|
||||
onChange={this.setPhotonCountThreshold}
|
||||
min={1} max={50} step={1} valueLabelDisplay="auto" />
|
||||
min={1} max={50} step={1} valueLabelDisplay="auto"/>
|
||||
|
||||
<br/><Typography> Signal-to-noise threshold </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.signal_to_noise_threshold)}
|
||||
onChange={this.setSignalToNoiseThreshold}
|
||||
min={2} max={10} step={0.5} valueLabelDisplay="auto" />
|
||||
min={2} max={10} step={0.5} valueLabelDisplay="auto"/>
|
||||
|
||||
<br/><Typography> Minimum pixel / spot </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.min_pix_per_spot)}
|
||||
onChange={this.setMinPixPerSpot}
|
||||
min={1} max={8} step={1} valueLabelDisplay="auto" />
|
||||
min={1} max={8} step={1} valueLabelDisplay="auto"/>
|
||||
<Typography> High resolution limit [Å] </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.high_resolution_limit)}
|
||||
onChange={this.setHighResolutionLimit}
|
||||
min={1} max={5} step={0.2} valueLabelDisplay="auto" />
|
||||
min={1} max={5} step={0.2} valueLabelDisplay="auto"/>
|
||||
<br/><br/>
|
||||
<Switch onChange={this.enableFilterPowderRingToggle}
|
||||
checked={this.state.s.filter_powder_rings}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Filter spots in powder rings (<i>e.g.</i>, ice)
|
||||
<br/><br/>
|
||||
<Typography> Min spots to filter powder ring </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable || !this.state.s.filter_powder_rings}
|
||||
value={Number(this.state.s.filter_powder_rings)}
|
||||
onChange={this.setMinSpotCountPowderRing}
|
||||
min={5} max={50} step={1} valueLabelDisplay="auto"/>
|
||||
<br/> <br/>
|
||||
<Switch onChange={this.enableIndexingToggle} checked={this.state.s.indexing}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Indexing <br/><br/>
|
||||
<Typography> Indexing spot acceptance tolerance </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable || !this.state.s.indexing}
|
||||
value={Number(this.state.s.indexing_tolerance)}
|
||||
onChange={this.setIndexingTolerance}
|
||||
min={0.0} max={0.3} step={0.01} valueLabelDisplay="auto" />
|
||||
<br/><br/>
|
||||
min={0.0} max={0.3} step={0.01} valueLabelDisplay="auto"/>
|
||||
</Grid>
|
||||
<Grid item xs={1}/>
|
||||
|
||||
|
||||
</Grid>
|
||||
</Paper>
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
show_spots: true,
|
||||
show_roi: false,
|
||||
show_indexed: false,
|
||||
show_user_mask: false,
|
||||
resolution_ring: 0.5
|
||||
},
|
||||
s_url: null,
|
||||
@@ -94,6 +95,17 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
);
|
||||
}
|
||||
|
||||
showUserMaskToggle = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
settings : {
|
||||
...prevState.settings,
|
||||
show_user_mask: event.target.checked
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getValues() {
|
||||
if (!this.state.update)
|
||||
return;
|
||||
@@ -125,7 +137,7 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Paper sx={{height: 1050, width: 850, m: 2}}
|
||||
return <Paper sx={{height: 1250, width: 1000, m: 2}}
|
||||
component={Stack}
|
||||
direction="column">
|
||||
<br/>
|
||||
@@ -143,6 +155,9 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
<Switch disabled={this.state.connection_error} checked={this.state.settings.show_indexed}
|
||||
onChange={this.showIndexedToggle} name="Show ROI"/>
|
||||
Show only indexed images
|
||||
<Switch disabled={this.state.connection_error} checked={this.state.settings.show_user_mask}
|
||||
onChange={this.showUserMaskToggle} name="Show user mask"/>
|
||||
Show user mask
|
||||
<Box sx={{width: 200}}>
|
||||
<Slider disabled={this.state.connection_error}
|
||||
value={Number(this.state.settings.saturation)} min={1} max={80}
|
||||
|
||||
@@ -27,6 +27,10 @@ export type preview_settings = {
|
||||
* Preview indexed images only
|
||||
*/
|
||||
show_indexed?: boolean;
|
||||
/**
|
||||
* Show user mask
|
||||
*/
|
||||
show_user_mask?: boolean;
|
||||
resolution_ring?: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,13 +5,24 @@
|
||||
|
||||
export type spot_finding_settings = {
|
||||
/**
|
||||
* Enable spot finding
|
||||
* Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
* Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
*
|
||||
*/
|
||||
enable: boolean;
|
||||
/**
|
||||
* Enable indexing
|
||||
* Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
*
|
||||
*/
|
||||
indexing: boolean;
|
||||
/**
|
||||
* Filter spots which form powder rings (e.g., ice rings)
|
||||
*/
|
||||
filter_powder_rings?: boolean;
|
||||
/**
|
||||
* Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
*/
|
||||
min_spot_count_powder_ring?: number;
|
||||
signal_to_noise_threshold: number;
|
||||
photon_count_threshold: number;
|
||||
min_pix_per_spot: number;
|
||||
|
||||
@@ -280,20 +280,20 @@ export class DefaultService {
|
||||
* Requires binary blob with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
* (depending on detector settings).
|
||||
*
|
||||
* @param number Image number to upload
|
||||
* @param id Image id to upload
|
||||
* @param requestBody
|
||||
* @returns any Everything OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigInternalGeneratorImage(
|
||||
number?: number,
|
||||
id?: number,
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/internal_generator_image',
|
||||
query: {
|
||||
'number': number,
|
||||
'id': id,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/octet-stream',
|
||||
@@ -303,6 +303,35 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load TIFF image for internal FPGA generator
|
||||
* Load image for internal FPGA generator. This can only happen in Idle state of the detector.
|
||||
* Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
* (depending on detector settings).
|
||||
*
|
||||
* @param id Image ID to upload
|
||||
* @param requestBody
|
||||
* @returns any Everything OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigInternalGeneratorImageTiff(
|
||||
id?: number,
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/internal_generator_image.tiff',
|
||||
query: {
|
||||
'id': id,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'image/tiff',
|
||||
errors: {
|
||||
400: `Input parsing or validation error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select detector
|
||||
* Jungfraujoch allows to control multiple detectors and/or region-of-interests.
|
||||
@@ -847,6 +876,63 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mask of the detector
|
||||
* Get full pixel mask of the detector
|
||||
* See NXmx standard for meaning of pixel values
|
||||
*
|
||||
* @returns binary Pixel mask in TIFF format (4 byte; unsigned)
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getConfigMaskTiff(): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/config/mask.tiff',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user mask of the detector
|
||||
* Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked
|
||||
* @returns binary User mask in TIFF format (4 byte; unsigned)
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getConfigUserMaskTiff(): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/config/user_mask.tiff',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload user mask of the detector
|
||||
* Should be in `Idle` state.
|
||||
* Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions.
|
||||
* If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates.
|
||||
* In the latter case - module gaps are ignored and don't need to be assigned value.
|
||||
* Mask is expected as TIFF (4-byte; unsigned).
|
||||
* 0 - good pixel, other value - masked
|
||||
* User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration.
|
||||
* User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
*
|
||||
* @param requestBody
|
||||
* @returns binary All good
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigUserMaskTiff(
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/user_mask.tiff',
|
||||
body: requestBody,
|
||||
mediaType: 'application/octet-stream',
|
||||
errors: {
|
||||
500: `Error within Jungfraujoch code - see output message.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pedestal G0 in TIFF format
|
||||
* @param gainLevel Gain level (0, 1, 2)
|
||||
|
||||
@@ -1,86 +1,125 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "CPUSpotFinder.h"
|
||||
#define NUM_PASS 2
|
||||
|
||||
template <int N>
|
||||
void FindSpots(DeviceOutput &output,
|
||||
int big_column, int big_row,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array) {
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel) {
|
||||
auto image = (int16_t *) output.pixels;
|
||||
|
||||
int64_t sum = 0;
|
||||
int64_t sum2 = 0;
|
||||
int64_t valid_count = 0;
|
||||
std::vector<uint8_t> mask(N * N, 0);
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
size_t line = big_row * N + y;
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t coord = (big_row * N + y) * RAW_MODULE_COLS + big_column * N + x;
|
||||
if ((image[coord] != INT16_MIN) || (image[coord] != INT16_MAX)) {
|
||||
sum += image[coord];
|
||||
sum2 += image[coord] * image[coord];
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t variance = valid_count * sum2 - sum * sum;
|
||||
float threshold = variance * settings.signal_to_noise_threshold * settings.signal_to_noise_threshold;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t line = big_row * N + y;
|
||||
size_t col = big_column * N + x;
|
||||
size_t coord = line * RAW_MODULE_COLS + col;
|
||||
|
||||
bool strong_pixel = true;
|
||||
|
||||
uint8_t bad_pixel = 0;
|
||||
if ((line == 255) || (line == 256)
|
||||
|| (col == 255) || (col == 256)
|
||||
|| (col == 511) || (col == 512)
|
||||
|| (col == 767) || (col == 768))
|
||||
strong_pixel = false;
|
||||
bad_pixel = 1;
|
||||
|
||||
if (d_array[coord] != 0) {
|
||||
if ((d_array[coord] < settings.high_resolution_limit)
|
||||
|| (d_array[coord] > settings.low_resolution_limit))
|
||||
if ((d_array[coord] < settings.high_resolution_limit)
|
||||
|| (d_array[coord] > settings.low_resolution_limit))
|
||||
bad_pixel = 1;
|
||||
|
||||
if ((image[coord] == INT16_MIN) || (image[coord] > 32760))
|
||||
bad_pixel = 1;
|
||||
|
||||
mask[y * N + x] = bad_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_PASS; i++) {
|
||||
int64_t sum = 0;
|
||||
int64_t sum2 = 0;
|
||||
int64_t valid_count = 0;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t coord = (big_row * N + y) * RAW_MODULE_COLS + big_column * N + x;
|
||||
if (mask[y * N + x] == 0) {
|
||||
sum += image[coord];
|
||||
sum2 += image[coord] * image[coord];
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t variance = valid_count * sum2 - sum * sum;
|
||||
float threshold = variance * settings.signal_to_noise_threshold * settings.signal_to_noise_threshold;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
size_t line = big_row * N + y;
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t col = big_column * N + x;
|
||||
size_t coord = line * RAW_MODULE_COLS + col;
|
||||
|
||||
arr_valid_count[coord] = valid_count;
|
||||
if (valid_count > 0) {
|
||||
arr_mean[coord] = static_cast<float>(sum) / static_cast<float>(valid_count);
|
||||
arr_stddev[coord] = sqrtf(
|
||||
static_cast<float>(variance) / static_cast<float>(valid_count * valid_count));
|
||||
} else {
|
||||
arr_mean[coord] = -1;
|
||||
arr_stddev[coord] = -1;
|
||||
}
|
||||
|
||||
bool strong_pixel = true;
|
||||
|
||||
if (mask[y * N + x])
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if ((settings.photon_count_threshold < 0)
|
||||
&& (settings.signal_to_noise_threshold <= 0))
|
||||
strong_pixel = false;
|
||||
|
||||
if ((settings.photon_count_threshold >= 0)
|
||||
&& (image[coord] <= settings.photon_count_threshold)) {
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (image[coord] > 32760)
|
||||
strong_pixel = false;
|
||||
|
||||
if (settings.signal_to_noise_threshold > 0) {
|
||||
int64_t in_minus_mean = image[coord] * valid_count - sum;
|
||||
if ((in_minus_mean * in_minus_mean <= threshold)
|
||||
|| (in_minus_mean <= 0)
|
||||
|| (valid_count < N * N / 2))
|
||||
if ((settings.photon_count_threshold < 0)
|
||||
&& (settings.signal_to_noise_threshold <= 0))
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (strong_pixel) {
|
||||
output.spot_finding_result.strong_pixel[coord / 8] |= (1 << (coord % 8));
|
||||
output.spot_finding_result.strong_pixel_count++;
|
||||
if ((settings.photon_count_threshold >= 0)
|
||||
&& (image[coord] <= settings.photon_count_threshold)) {
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (settings.signal_to_noise_threshold > 0) {
|
||||
int64_t in_minus_mean = image[coord] * valid_count - sum;
|
||||
if ((in_minus_mean * in_minus_mean <= threshold)
|
||||
|| (in_minus_mean <= 0)
|
||||
|| (valid_count <= N * N / 2))
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (strong_pixel) {
|
||||
mask[y * N + x] = 1;
|
||||
arr_strong_pixel[coord]++;
|
||||
output.spot_finding_result.strong_pixel[coord / 8] |= (1 << (coord % 8));
|
||||
output.spot_finding_result.strong_pixel_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindSpots(DeviceOutput &output, const SpotFindingSettings& settings, const float *d_array) {
|
||||
void FindSpots(DeviceOutput &output,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel) {
|
||||
for (auto &i: output.spot_finding_result.strong_pixel)
|
||||
i = 0;
|
||||
|
||||
for (int i = 0; i < RAW_MODULE_LINES / 32; i++) {
|
||||
for (int j = 0; j < RAW_MODULE_COLS / 32; j++)
|
||||
FindSpots<32>(output, j, i, settings, d_array);
|
||||
FindSpots<32>(output, j, i, settings, d_array, arr_mean, arr_stddev, arr_valid_count, arr_strong_pixel);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,13 @@
|
||||
#include "SpotFindingSettings.h"
|
||||
#include "StrongPixelSet.h"
|
||||
|
||||
void FindSpots(DeviceOutput &output, const SpotFindingSettings& settings, const float *d_array);
|
||||
void FindSpots(DeviceOutput &output,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel);
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_CPUSPOTFINDER_H
|
||||
|
||||
@@ -8,13 +8,17 @@ void IndexerWrapper::Setup(const UnitCell &cell) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord, float indexing_threshold) {
|
||||
std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord, float indexing_threshold, int nspots) {
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
std::vector<IndexingResult> ret;
|
||||
|
||||
if (coord.size() <= viable_cell_min_spots)
|
||||
if (nspots > coord.size())
|
||||
nspots = coord.size();
|
||||
|
||||
if (nspots <= viable_cell_min_spots)
|
||||
return ret;
|
||||
|
||||
assert(nspots <= MAX_SPOT_COUNT);
|
||||
assert(coord.size() <= MAX_SPOT_COUNT);
|
||||
|
||||
for (int i = 0; i < coord.size(); i++) {
|
||||
@@ -24,9 +28,9 @@ std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord,
|
||||
}
|
||||
|
||||
// Index
|
||||
indexer.index(1, coord.size());
|
||||
indexer.index(1, nspots);
|
||||
|
||||
fast_feedback::refine::indexer_ifssr<float>::refine(indexer.spotM().topRows(coord.size()),
|
||||
fast_feedback::refine::indexer_ifssr<float>::refine(indexer.spotM().topRows(nspots),
|
||||
indexer.oCellM(),
|
||||
indexer.oScoreV(),
|
||||
cifssr);
|
||||
@@ -38,30 +42,28 @@ std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord,
|
||||
// Get best cell
|
||||
auto id = fast_feedback::refine::best_cell(indexer.oScoreV());
|
||||
|
||||
bool indexed = fast_feedback::refine::is_viable_cell(indexer.oCell(id), indexer.Spots(),
|
||||
indexing_threshold, viable_cell_min_spots,
|
||||
false);
|
||||
auto cell = indexer.oCell(id).colwise().reverse();
|
||||
fast_feedback::refine::make_right_handed(cell);
|
||||
|
||||
// Check if result is viable
|
||||
if (indexed) {
|
||||
auto cell = indexer.oCell(id).colwise().reverse();
|
||||
fast_feedback::refine::make_right_handed(cell);
|
||||
using M3x = Eigen::MatrixX3<float>;
|
||||
M3x resid = indexer.spotM().topRows(coord.size()) * cell.transpose();
|
||||
const M3x miller = round(resid.array());
|
||||
resid -= miller;
|
||||
|
||||
// get indexed spots
|
||||
using M3x = Eigen::MatrixX3<float>;
|
||||
M3x resid = indexer.spotM().topRows(coord.size()) * cell.transpose();
|
||||
const M3x miller = round(resid.array());
|
||||
const M3x predicted = miller * cell.transpose().inverse();
|
||||
auto indexed_spots = (resid.rowwise().norm().array() < indexing_threshold);
|
||||
auto indexed_spot_count = indexed_spots.topRows(nspots).count();
|
||||
|
||||
// Conditions for indexing:
|
||||
// * There must be 9 AND 20% of all spots indexed
|
||||
// * Decision is only based on spots used for indexing (not all spots)
|
||||
if ((indexed_spot_count > viable_cell_min_spots) && (indexed_spot_count > 0.20 * nspots)) {
|
||||
IndexingResult result;
|
||||
result.l = CrystalLattice(cell);
|
||||
|
||||
result.predicted_spots.resize(coord.size());
|
||||
result.indexed_spot.resize(coord.size());
|
||||
|
||||
for (int i = 0; i < coord.size(); i++)
|
||||
result.predicted_spots[i] = Coord(predicted.coeff(i, 0),
|
||||
predicted.coeff(i, 1),
|
||||
predicted.coeff(i, 2));
|
||||
result.indexed_spot[i] = indexed_spots(i);
|
||||
|
||||
ret.emplace_back(result);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
struct IndexingResult {
|
||||
CrystalLattice l;
|
||||
std::vector<Coord> predicted_spots;
|
||||
std::vector<uint8_t> indexed_spot;
|
||||
};
|
||||
|
||||
class IndexerWrapper {
|
||||
@@ -36,7 +36,7 @@ class IndexerWrapper {
|
||||
constexpr const static uint32_t viable_cell_min_spots = 9;
|
||||
public:
|
||||
void Setup(const UnitCell &cell);
|
||||
std::vector<IndexingResult> Run(const std::vector<Coord> &coord, float indexing_threshold);
|
||||
std::vector<IndexingResult> Run(const std::vector<Coord> &coord, float indexing_threshold, int nspots = -1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ MXAnalyzer::MXAnalyzer(const DiffractionExperiment &in_experiment)
|
||||
throw JFJochException(JFJochExceptionCategory::GPUCUDAError, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (experiment.IsSpotFindingEnabled())
|
||||
find_spots = true;
|
||||
|
||||
}
|
||||
|
||||
void MXAnalyzer::ReadFromFPGA(const DeviceOutput *output, const SpotFindingSettings &settings, size_t module_number) {
|
||||
@@ -60,18 +60,70 @@ void MXAnalyzer::ReadFromCPU(const int16_t *image, const SpotFindingSettings &se
|
||||
|
||||
CalcSpotFinderResolutionMap(d_map.data(), experiment, module_number);
|
||||
|
||||
FindSpots(output, settings, d_map.data());
|
||||
arr_mean.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_sttdev.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_valid_count.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_strong_pixel.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
|
||||
FindSpots(output,
|
||||
settings,
|
||||
d_map.data(),
|
||||
arr_mean.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_sttdev.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_valid_count.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_strong_pixel.data() + module_number * RAW_MODULE_SIZE);
|
||||
|
||||
ReadFromFPGA(&output, settings, module_number);
|
||||
}
|
||||
|
||||
uint32_t MXAnalyzer::FilterSpotsInPowderRings(const std::vector<DiffractionSpot> &spots_filter,
|
||||
std::vector<DiffractionSpot> &spots_out,
|
||||
int64_t min_spot_count_ring) {
|
||||
uint32_t ret = 0;
|
||||
|
||||
double high_q = 5.0;
|
||||
double low_q = 0;
|
||||
double q_spacing = 0.01;
|
||||
|
||||
size_t bin_count = (high_q - low_q) / q_spacing + 1;
|
||||
|
||||
std::vector<std::vector<uint32_t> > bins(bin_count);
|
||||
|
||||
for (int i = 0; i < spots_filter.size(); i++) {
|
||||
double q = 2 * M_PI / spots_filter[i].GetResolution(experiment);
|
||||
if ((q >= low_q) && (q < high_q)) {
|
||||
int32_t q_bin = std::floor((q - low_q) / q_spacing);
|
||||
bins[q_bin].push_back(i);
|
||||
} else // spots outside of azim. int. range are not filtered
|
||||
spots_out.push_back(spots_filter[i]);
|
||||
}
|
||||
|
||||
for (auto & bin : bins) {
|
||||
if (bin.size() > min_spot_count_ring)
|
||||
ret += bin.size();
|
||||
else {
|
||||
for (auto &iter: bin)
|
||||
spots_out.push_back(spots_filter[iter]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MXAnalyzer::Process(DataMessage &message, const SpotFindingSettings& settings) {
|
||||
message.indexing_result = false;
|
||||
if (!find_spots)
|
||||
return;
|
||||
|
||||
std::vector<DiffractionSpot> spots_out;
|
||||
FilterSpotsByCount(experiment, spots, spots_out);
|
||||
if (settings.filter_spots_powder_ring) {
|
||||
std::vector<DiffractionSpot> spots_no_rings;
|
||||
message.spot_count_in_rings = FilterSpotsInPowderRings(spots,
|
||||
spots_no_rings,
|
||||
settings.min_spot_count_powder_ring);
|
||||
FilterSpotsByCount(experiment, spots_no_rings, spots_out);
|
||||
} else
|
||||
FilterSpotsByCount(experiment, spots, spots_out);
|
||||
|
||||
spots.clear();
|
||||
|
||||
for (const auto &spot: spots_out)
|
||||
@@ -83,22 +135,39 @@ void MXAnalyzer::Process(DataMessage &message, const SpotFindingSettings& settin
|
||||
for (const auto &i: spots_out)
|
||||
recip.push_back(i.ReciprocalCoord(experiment));
|
||||
|
||||
auto indexer_result = indexer->Run(recip, settings.indexing_tolerance);
|
||||
std::vector<IndexingResult> indexer_result;
|
||||
|
||||
// If there is a really large number of spots detected, it is better to start with a smaller subset
|
||||
if (recip.size() > 80)
|
||||
indexer_result = indexer->Run(recip, settings.indexing_tolerance, 50);
|
||||
if (indexer_result.empty())
|
||||
indexer_result = indexer->Run(recip, settings.indexing_tolerance);
|
||||
|
||||
if (!indexer_result.empty()) {
|
||||
message.indexing_result = true;
|
||||
assert(indexer_result[0].indexed_spot.size() == recip.size());
|
||||
|
||||
// identify indexed spots
|
||||
for (int i = 0; i < recip.size(); i++) {
|
||||
auto predicted_pos = RecipToDector(experiment, indexer_result[0].predicted_spots[i]);
|
||||
float x_diff = predicted_pos.first - spots_out[i].RawCoord().x;
|
||||
float y_diff = predicted_pos.second - spots_out[i].RawCoord().y;
|
||||
message.spots[i].indexed = (x_diff * x_diff + y_diff * y_diff
|
||||
< spot_distance_threshold_pxl * spot_distance_threshold_pxl);
|
||||
}
|
||||
for (int i = 0; i < recip.size(); i++)
|
||||
message.spots[i].indexed = indexer_result[0].indexed_spot[i];
|
||||
|
||||
indexer_result[0].l.Save(message.indexing_lattice);
|
||||
message.indexing_unit_cell = indexer_result[0].l.GetUnitCell();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<float> &MXAnalyzer::GetCPUMean() const {
|
||||
return arr_mean;
|
||||
}
|
||||
|
||||
const std::vector<float> &MXAnalyzer::GetCPUStdDev() const {
|
||||
return arr_sttdev;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &MXAnalyzer::GetCPUValidCount() const {
|
||||
return arr_valid_count;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &MXAnalyzer::GetCPUStrongPixel() const {
|
||||
return arr_strong_pixel;
|
||||
}
|
||||
@@ -13,7 +13,17 @@ class MXAnalyzer {
|
||||
bool find_spots = false;
|
||||
std::vector<DiffractionSpot> spots;
|
||||
constexpr static const float spot_distance_threshold_pxl = 2.0f;
|
||||
|
||||
std::vector<float> arr_mean;
|
||||
std::vector<float> arr_sttdev;
|
||||
std::vector<uint32_t> arr_valid_count;
|
||||
std::vector<uint32_t> arr_strong_pixel;
|
||||
public:
|
||||
const std::vector<float> &GetCPUMean() const;
|
||||
const std::vector<float> &GetCPUStdDev() const;
|
||||
const std::vector<uint32_t> &GetCPUValidCount() const;
|
||||
const std::vector<uint32_t> &GetCPUStrongPixel() const;
|
||||
|
||||
explicit MXAnalyzer(const DiffractionExperiment& experiment);
|
||||
|
||||
void ReadFromFPGA(const DeviceOutput* output,
|
||||
@@ -26,6 +36,10 @@ public:
|
||||
|
||||
void Process(DataMessage &message,
|
||||
const SpotFindingSettings& settings);
|
||||
|
||||
uint32_t FilterSpotsInPowderRings(const std::vector<DiffractionSpot> &spots_filter,
|
||||
std::vector<DiffractionSpot> &spots_out,
|
||||
int64_t min_spot_count_ring);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,17 @@
|
||||
|
||||
struct SpotFindingSettings {
|
||||
bool enable = true;
|
||||
bool indexing = true;
|
||||
float signal_to_noise_threshold = 3; // STRONG_PIXEL in XDS
|
||||
int64_t photon_count_threshold = 10; // Threshold in photon counts
|
||||
int64_t min_pix_per_spot = 2; // Minimum pixels per spot
|
||||
int64_t max_pix_per_spot = 50; // Maximum pixels per spot
|
||||
float high_resolution_limit = 2.5;
|
||||
float high_resolution_limit = 2.0;
|
||||
float low_resolution_limit = 50.0;
|
||||
|
||||
bool filter_spots_powder_ring = false;
|
||||
int64_t min_spot_count_powder_ring = 20;
|
||||
|
||||
bool indexing = true;
|
||||
float indexing_tolerance = 0.1;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "JFCalibration.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
JFCalibration::JFCalibration(size_t in_nmodules, size_t in_nstorage_cells) :
|
||||
nmodules(in_nmodules),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
FIND_PACKAGE(JPEG REQUIRED)
|
||||
|
||||
ADD_LIBRARY(JFJochPreview STATIC
|
||||
WriteTIFF.cpp WriteTIFF.h
|
||||
WriteJPEG.cpp WriteJPEG.h
|
||||
JFJochTIFF.cpp JFJochTIFF.h
|
||||
JFJochJPEG.cpp JFJochJPEG.h
|
||||
PreviewCounter.cpp PreviewCounter.h
|
||||
PreviewImage.cpp PreviewImage.h)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
#include "WriteJPEG.h"
|
||||
#include "JFJochJPEG.h"
|
||||
|
||||
|
||||
std::string WriteJPEGToMem(const std::vector<uint8_t> &input, size_t width, size_t height, int quality) {
|
||||
110
preview/JFJochTIFF.cpp
Normal file
110
preview/JFJochTIFF.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
#include <sstream>
|
||||
|
||||
#include "JFJochTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
|
||||
void WriteTIFF(TIFF *tiff, void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
|
||||
if (tiff == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFStreamOpen error");
|
||||
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, cols); // set the width of the image
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, lines); // set the height of the image
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1); // set number of channels per pixel
|
||||
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, elem_size * 8); // set the size of the channels
|
||||
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE); // setc ompression to LZW
|
||||
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, lines);
|
||||
if (is_signed)
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
|
||||
else
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||
|
||||
if (TIFFWriteEncodedStrip(tiff, 0, buff, cols * lines * elem_size) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFWriteEncodedStrip error");
|
||||
}
|
||||
|
||||
std::string WriteTIFFToString(void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
std::stringstream os;
|
||||
|
||||
TIFF *tiff = TIFFStreamOpen("x", (std::ostream *) &os);
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
TIFFClose(tiff);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void WriteTIFFToFile(const std::string &filename, void *buff, size_t cols, size_t lines, size_t elem_size,
|
||||
bool is_signed) {
|
||||
TIFF *tiff = TIFFOpen(filename.c_str(), "w");
|
||||
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
|
||||
TIFFClose(tiff);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ReadTIFFFromString32(const std::string &s, uint32_t &cols, uint32_t &lines) {
|
||||
uint32_t rows_per_string = 0;
|
||||
|
||||
std::vector<uint32_t> ret;
|
||||
|
||||
uint16_t elem_size;
|
||||
|
||||
std::istringstream input_TIFF_stream(s);
|
||||
TIFF* tiff = TIFFStreamOpen("MemTIFF", &input_TIFF_stream);
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &cols); // get the width of the image
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &lines); // get the height of the image
|
||||
TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &elem_size); // get the size of the channels
|
||||
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_string);
|
||||
|
||||
if (elem_size != 32)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "Only 32-bit format supported");
|
||||
|
||||
ret.resize(cols * lines);
|
||||
|
||||
if (cols * sizeof(uint32_t) != TIFFScanlineSize(tiff))
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFScanlineSize mismatch");
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
if (TIFFReadScanline(tiff, ret.data() + i * cols, i, 0) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFReadScanline error");
|
||||
}
|
||||
|
||||
TIFFClose(tiff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> ReadTIFFFromString16(const std::string &s, uint32_t &cols, uint32_t &lines) {
|
||||
uint32_t rows_per_string = 0;
|
||||
|
||||
std::vector<uint16_t> ret;
|
||||
|
||||
uint16_t elem_size;
|
||||
|
||||
std::istringstream input_TIFF_stream(s);
|
||||
TIFF* tiff = TIFFStreamOpen("MemTIFF", &input_TIFF_stream);
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &cols); // get the width of the image
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &lines); // get the height of the image
|
||||
TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &elem_size); // get the size of the channels
|
||||
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_string);
|
||||
|
||||
if (elem_size != 16)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "Only 16-bit format supported");
|
||||
|
||||
ret.resize(cols * lines);
|
||||
|
||||
if (cols * sizeof(uint16_t) != TIFFScanlineSize(tiff))
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFScanlineSize mismatch");
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
if (TIFFReadScanline(tiff, ret.data() + i * cols, i, 0) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFReadScanline error");
|
||||
}
|
||||
|
||||
TIFFClose(tiff);
|
||||
return ret;
|
||||
}
|
||||
@@ -4,9 +4,12 @@
|
||||
#define JUNGFRAUJOCH_WRITETIFF_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::string WriteTIFFToString(void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed = false);
|
||||
void WriteTIFFToFile(const std::string &filename, void *buff, size_t cols, size_t lines, size_t elem_size,
|
||||
bool is_signed = false);
|
||||
std::vector<uint32_t> ReadTIFFFromString32(const std::string& s, uint32_t &cols, uint32_t &lines);
|
||||
std::vector<uint16_t> ReadTIFFFromString16(const std::string& s, uint32_t &cols, uint32_t &lines);
|
||||
|
||||
#endif //JUNGFRAUJOCH_WRITETIFF_H
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "WriteJPEG.h"
|
||||
#include "WriteTIFF.h"
|
||||
#include "JFJochJPEG.h"
|
||||
#include "JFJochTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
#include "../common/DiffractionGeometry.h"
|
||||
|
||||
@@ -31,28 +31,7 @@ constexpr const static rgb plotly[] = {{0x1f, 0x77, 0xb4},
|
||||
constexpr const static rgb indigo = {.r = 0x3f, .g = 0x51, .b = 0xb5};
|
||||
constexpr const static rgb gray = {.r = 0xbe, .g = 0xbe, .b = 0xbe};
|
||||
|
||||
PreviewImage::PreviewImage(const DiffractionExperiment &in_experiment) :
|
||||
experiment(in_experiment),
|
||||
initialized(false),
|
||||
xpixel(experiment.GetXPixelsNum()),
|
||||
ypixel(experiment.GetYPixelsNum()),
|
||||
beam_x(experiment.GetBeamX_pxl()),
|
||||
beam_y(experiment.GetBeamY_pxl()),
|
||||
pixel_depth_bytes(experiment.GetPixelDepth()),
|
||||
pixel_is_signed(experiment.IsPixelSigned()),
|
||||
uncompressed_image(experiment.GetPixelsNum() * experiment.GetPixelDepth()),
|
||||
roi_map(experiment.ROI()),
|
||||
counter(experiment.GetPreviewPeriod()) {}
|
||||
|
||||
void PreviewImage::UpdateImage(const void *in_uncompressed_image,
|
||||
const std::vector<SpotToSave> &in_spots) {
|
||||
if (counter.GeneratePreview()) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
initialized = true;
|
||||
memcpy(uncompressed_image.data(), in_uncompressed_image, xpixel * ypixel * pixel_depth_bytes);
|
||||
spots = in_spots;
|
||||
}
|
||||
}
|
||||
PreviewImage::PreviewImage(std::chrono::microseconds period) : counter(period) {}
|
||||
|
||||
void colormap(std::vector<unsigned char>& ret, float v, size_t pixel) {
|
||||
if ((v < 0.0) || (v > 1.0)) {
|
||||
@@ -106,55 +85,6 @@ std::vector<unsigned char> GenerateRGB(const T* value, size_t npixel, uint32_t s
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings &settings) const {
|
||||
std::vector<unsigned char> v;
|
||||
{
|
||||
// JPEG compression is outside the critical loop protected by m
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
if (!pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<uint16_t>((uint16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT16_MAX);
|
||||
else
|
||||
v = GenerateRGB<uint32_t>((uint32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT32_MAX);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<int16_t>((int16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT16_MIN);
|
||||
else
|
||||
v = GenerateRGB<int32_t>((int32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT32_MIN);
|
||||
}
|
||||
if (settings.show_spots)
|
||||
AddSpots(v);
|
||||
}
|
||||
|
||||
if (settings.show_roi)
|
||||
AddROI(v);
|
||||
|
||||
if (settings.resolution_ring)
|
||||
AddResolutionRing(v, settings.resolution_ring.value());
|
||||
|
||||
AddBeamCenter(v);
|
||||
|
||||
return WriteJPEGToMem(v, xpixel, ypixel, settings.jpeg_quality);
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::string s = WriteTIFFToString(const_cast<uint8_t *>(uncompressed_image.data()),
|
||||
xpixel, ypixel, pixel_depth_bytes, pixel_is_signed);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<uint16_t> GenerateDioptasPreview(const void* input, size_t xpixel, size_t ypixel, T special_value) {
|
||||
auto input_ptr = (T *) input;
|
||||
@@ -174,26 +104,6 @@ std::vector<uint16_t> GenerateDioptasPreview(const void* input, size_t xpixel, s
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFFDioptas() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::vector<uint16_t> vec;
|
||||
if (pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<int16_t>(uncompressed_image.data(), xpixel, ypixel, INT16_MIN);
|
||||
else
|
||||
vec = GenerateDioptasPreview<int32_t>(uncompressed_image.data(), xpixel, ypixel, INT32_MIN);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<uint16_t>(uncompressed_image.data(), xpixel, ypixel, UINT16_MAX);
|
||||
else
|
||||
vec = GenerateDioptasPreview<uint32_t>(uncompressed_image.data(), xpixel, ypixel, UINT32_MAX);
|
||||
}
|
||||
return WriteTIFFToString(vec.data(), xpixel, ypixel, 2, false);
|
||||
}
|
||||
|
||||
void PreviewImage::AddBeamCenter(std::vector<uint8_t> &rgb_image) const {
|
||||
size_t beam_x_int = std::lround(beam_x);
|
||||
size_t beam_y_int = std::lround(beam_y);
|
||||
@@ -230,7 +140,7 @@ void PreviewImage::AddSpots(std::vector<uint8_t> &rgb_image) const {
|
||||
void PreviewImage::AddROI(std::vector<uint8_t> &rgb_image) const {
|
||||
int64_t roi_counter = 0;
|
||||
|
||||
for (const auto &box: roi_map.GetROIBox()) {
|
||||
for (const auto &box: experiment.ROI().GetROIBox()) {
|
||||
int rectangle_width = 5;
|
||||
|
||||
for (auto x = box.GetXMin() - rectangle_width; x <= box.GetXMax() + rectangle_width; x++) {
|
||||
@@ -249,7 +159,7 @@ void PreviewImage::AddROI(std::vector<uint8_t> &rgb_image) const {
|
||||
roi_counter++;
|
||||
}
|
||||
|
||||
for (const auto &circle: roi_map.GetROICircle()) {
|
||||
for (const auto &circle: experiment.ROI().GetROICircle()) {
|
||||
int width = 5;
|
||||
|
||||
for (int64_t y = std::floor(circle.GetY() - circle.GetRadius_pxl() - width);
|
||||
@@ -281,3 +191,132 @@ void PreviewImage::AddResolutionRing(std::vector<uint8_t> &rgb_image, float d) c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewImage::AddUserMask(std::vector<uint8_t> &rgb_image) const {
|
||||
for (int y = 0; y < ypixel; y++) {
|
||||
for (int x = 0; x < xpixel; x++) {
|
||||
if (user_mask[y * xpixel + x] != 0)
|
||||
color_pixel(rgb_image, x, y, gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewImage::Configure(const DiffractionExperiment &in_experiment, const PixelMask& mask) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
experiment = in_experiment;
|
||||
initialized = false;
|
||||
xpixel = experiment.GetXPixelsNum();
|
||||
ypixel = experiment.GetYPixelsNum();
|
||||
beam_x = experiment.GetBeamX_pxl();
|
||||
beam_y = experiment.GetBeamY_pxl();
|
||||
pixel_depth_bytes = experiment.GetPixelDepth();
|
||||
pixel_is_signed = experiment.IsPixelSigned();
|
||||
uncompressed_image.resize(experiment.GetPixelsNum() * experiment.GetPixelDepth());
|
||||
memset(uncompressed_image.data(), 0, experiment.GetPixelsNum() * experiment.GetPixelDepth());
|
||||
user_mask = mask.GetUserMask(experiment);
|
||||
if (user_mask.size() != experiment.GetPixelsNum())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "User pixel mask size incorrect");
|
||||
}
|
||||
|
||||
void PreviewImage::Configure() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
initialized = false;
|
||||
xpixel = 0;
|
||||
ypixel = 0;
|
||||
}
|
||||
|
||||
void PreviewImage::UpdateImage(const void *in_uncompressed_image,
|
||||
const std::vector<SpotToSave> &in_spots) {
|
||||
if (counter.GeneratePreview()) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (xpixel * ypixel == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Preview not configured");
|
||||
|
||||
initialized = true;
|
||||
memcpy(uncompressed_image.data(), in_uncompressed_image, xpixel * ypixel * pixel_depth_bytes);
|
||||
spots = in_spots;
|
||||
}
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings &settings) const {
|
||||
std::vector<unsigned char> v;
|
||||
size_t local_xpixel;
|
||||
size_t local_ypixel;
|
||||
|
||||
{
|
||||
// JPEG compression is outside the critical loop protected by m
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
local_xpixel = xpixel;
|
||||
local_ypixel = ypixel;
|
||||
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
if (!pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<uint16_t>((uint16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT16_MAX);
|
||||
else
|
||||
v = GenerateRGB<uint32_t>((uint32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT32_MAX);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<int16_t>((int16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT16_MIN);
|
||||
else
|
||||
v = GenerateRGB<int32_t>((int32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT32_MIN);
|
||||
}
|
||||
|
||||
if (settings.show_user_mask)
|
||||
AddUserMask(v);
|
||||
|
||||
if (settings.show_spots)
|
||||
AddSpots(v);
|
||||
|
||||
if (settings.show_roi)
|
||||
AddROI(v);
|
||||
|
||||
if (settings.resolution_ring)
|
||||
AddResolutionRing(v, settings.resolution_ring.value());
|
||||
|
||||
AddBeamCenter(v);
|
||||
}
|
||||
|
||||
return WriteJPEGToMem(v, local_xpixel, local_ypixel, settings.jpeg_quality);
|
||||
}
|
||||
|
||||
|
||||
std::string PreviewImage::GenerateTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::string s = WriteTIFFToString(const_cast<uint8_t *>(uncompressed_image.data()),
|
||||
xpixel, ypixel, pixel_depth_bytes, pixel_is_signed);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFFDioptas() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::vector<uint16_t> vec;
|
||||
if (pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<int16_t>(uncompressed_image.data(), xpixel, ypixel, INT16_MIN);
|
||||
else
|
||||
vec = GenerateDioptasPreview<int32_t>(uncompressed_image.data(), xpixel, ypixel, INT32_MIN);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<uint16_t>(uncompressed_image.data(), xpixel, ypixel, UINT16_MAX);
|
||||
else
|
||||
vec = GenerateDioptasPreview<uint32_t>(uncompressed_image.data(), xpixel, ypixel, UINT32_MAX);
|
||||
}
|
||||
return WriteTIFFToString(vec.data(), xpixel, ypixel, 2, false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../common/SpotToSave.h"
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../common/PixelMask.h"
|
||||
#include "PreviewCounter.h"
|
||||
|
||||
struct PreviewJPEGSettings {
|
||||
@@ -16,6 +17,7 @@ struct PreviewJPEGSettings {
|
||||
bool show_spots = true;
|
||||
bool show_roi = false;
|
||||
bool show_indexed = false;
|
||||
bool show_user_mask = false;
|
||||
std::optional<float> resolution_ring;
|
||||
};
|
||||
|
||||
@@ -29,21 +31,22 @@ class PreviewImage {
|
||||
mutable std::mutex m;
|
||||
DiffractionExperiment experiment;
|
||||
|
||||
bool initialized;
|
||||
const ROIMap roi_map;
|
||||
bool initialized = false;
|
||||
std::vector<uint8_t> uncompressed_image;
|
||||
std::vector<uint32_t> user_mask;
|
||||
std::vector<SpotToSave> spots;
|
||||
size_t xpixel;
|
||||
size_t ypixel;
|
||||
size_t pixel_depth_bytes;
|
||||
bool pixel_is_signed;
|
||||
float beam_x;
|
||||
float beam_y;
|
||||
size_t xpixel = 0;
|
||||
size_t ypixel = 0;
|
||||
size_t pixel_depth_bytes = 2;
|
||||
bool pixel_is_signed = false;
|
||||
float beam_x = 0;
|
||||
float beam_y = 0;
|
||||
|
||||
void AddResolutionRing(std::vector<uint8_t> &rgb_image, float d) const;
|
||||
void AddBeamCenter(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddSpots(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddROI(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddUserMask(std::vector<uint8_t> &rgb_image) const;
|
||||
PreviewCounter counter;
|
||||
|
||||
void color_pixel(std::vector<unsigned char>& ret, int64_t xpixel, int64_t ypixel, const rgb &color) const;
|
||||
@@ -52,7 +55,9 @@ class PreviewImage {
|
||||
void beam_center_mark(std::vector<unsigned char>& ret, int64_t xpixel, int64_t ypixel) const;
|
||||
|
||||
public:
|
||||
explicit PreviewImage(const DiffractionExperiment& experiment);
|
||||
explicit PreviewImage(std::chrono::microseconds period = std::chrono::seconds(1));
|
||||
void Configure(const DiffractionExperiment& experiment, const PixelMask& mask);
|
||||
void Configure();
|
||||
void UpdateImage(const void *uncompressed_image, const std::vector<SpotToSave> &spots);
|
||||
[[nodiscard]] std::string GenerateJPEG(const PreviewJPEGSettings& settings) const;
|
||||
[[nodiscard]] std::string GenerateTIFF() const;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
#include <sstream>
|
||||
|
||||
#include "WriteTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
|
||||
void WriteTIFF(TIFF *tiff, void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
|
||||
if (tiff == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFStreamOpen error");
|
||||
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, cols); // set the width of the image
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, lines); // set the height of the image
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1); // set number of channels per pixel
|
||||
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, elem_size * 8); // set the size of the channels
|
||||
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE); // setc ompression to LZW
|
||||
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, lines);
|
||||
if (is_signed)
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
|
||||
else
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||
|
||||
if (TIFFWriteEncodedStrip(tiff, 0, buff, cols * lines * elem_size) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFWriteEncodedStrip error");
|
||||
}
|
||||
|
||||
std::string WriteTIFFToString(void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
std::stringstream os;
|
||||
|
||||
TIFF *tiff = TIFFStreamOpen("x", (std::ostream *) &os);
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
TIFFClose(tiff);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void WriteTIFFToFile(const std::string &filename, void *buff, size_t cols, size_t lines, size_t elem_size,
|
||||
bool is_signed) {
|
||||
TIFF *tiff = TIFFOpen(filename.c_str(), "w");
|
||||
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
|
||||
TIFFClose(tiff);
|
||||
}
|
||||
@@ -11,12 +11,15 @@
|
||||
#include "../common/PixelMask.h"
|
||||
|
||||
JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *in_calibration,
|
||||
AcquisitionDeviceGroup &in_aq_device,
|
||||
ImagePusher &in_image_sender,
|
||||
Logger &in_logger, int64_t in_forward_and_sum_nthreads,
|
||||
const NUMAHWPolicy &in_numa_policy,
|
||||
const SpotFindingSettings &in_spot_finding_settings,
|
||||
PreviewImage &in_preview_image,
|
||||
PreviewImage &in_preview_image_indexed,
|
||||
SendBuffer &buf) :
|
||||
experiment(in_experiment),
|
||||
calibration(nullptr),
|
||||
@@ -33,9 +36,10 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
az_int_mapping(experiment),
|
||||
plots(experiment, az_int_mapping),
|
||||
spot_finding_settings(in_spot_finding_settings),
|
||||
preview_image(experiment),
|
||||
preview_image_indexed(experiment),
|
||||
serialmx_filter(experiment)
|
||||
preview_image(in_preview_image),
|
||||
preview_image_indexed(in_preview_image_indexed),
|
||||
serialmx_filter(experiment),
|
||||
user_mask_raw_coord(pixel_mask.GetUserMask(experiment, false))
|
||||
{
|
||||
if (experiment.GetDetectorSetup().GetDetectorType() == DetectorType::JUNGFRAU)
|
||||
calibration = in_calibration;
|
||||
@@ -60,7 +64,7 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
logger.Info("Data acquisition devices ready");
|
||||
|
||||
if (experiment.GetImageNum() > 0) {
|
||||
SendStartMessage();
|
||||
SendStartMessage(pixel_mask);
|
||||
SendCalibration();
|
||||
|
||||
for (int i = 0; i < experiment.GetImageNum(); i++)
|
||||
@@ -88,7 +92,7 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
logger.Info("Receiving data started");
|
||||
}
|
||||
|
||||
void JFJochReceiver::SendStartMessage() {
|
||||
void JFJochReceiver::SendStartMessage(const PixelMask &pixel_mask) {
|
||||
if (!push_images_to_writer)
|
||||
return;
|
||||
|
||||
@@ -99,11 +103,6 @@ void JFJochReceiver::SendStartMessage() {
|
||||
message.az_int_bin_to_q = az_int_mapping.GetBinToQ();
|
||||
message.write_master_file = true;
|
||||
|
||||
PixelMask pixel_mask(experiment);
|
||||
|
||||
if (calibration)
|
||||
pixel_mask.LoadDetectorBadPixelMask(calibration->CalculateMask(), 1);
|
||||
|
||||
std::vector<uint32_t> nexus_mask;
|
||||
nexus_mask = pixel_mask.GetMask(experiment);
|
||||
|
||||
@@ -178,6 +177,10 @@ void JFJochReceiver::AcquireThread(uint16_t data_stream) {
|
||||
std::vector<float> tmp(RAW_MODULE_SIZE);
|
||||
for (int m = 0; m < experiment.GetModulesNum(data_stream); m++) {
|
||||
CalcSpotFinderResolutionMap(tmp.data(), experiment, m_offset + m);
|
||||
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
||||
if (user_mask_raw_coord[(m_offset + m) * RAW_MODULE_SIZE + i] != 0)
|
||||
tmp[i] = 0.0f;
|
||||
}
|
||||
acquisition_device[data_stream].InitializeSpotFinderResolutionMap(tmp.data(), m);
|
||||
|
||||
CalcAzIntCorrRawCoord(tmp.data(), experiment, m_offset + m);
|
||||
@@ -573,20 +576,6 @@ JFJochReceiverStatus JFJochReceiver::GetStatus() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string JFJochReceiver::GetTIFF(bool calibration_run) const {
|
||||
if (calibration_run)
|
||||
return preview_image.GenerateTIFFDioptas();
|
||||
else
|
||||
return preview_image.GenerateTIFF();
|
||||
}
|
||||
|
||||
std::string JFJochReceiver::GetJPEG(const PreviewJPEGSettings &settings) const {
|
||||
if (settings.show_indexed)
|
||||
return preview_image_indexed.GenerateJPEG(settings);
|
||||
else
|
||||
return preview_image.GenerateJPEG(settings);
|
||||
}
|
||||
|
||||
void JFJochReceiver::GetXFELEventCode(std::vector<uint64_t> &v) const {
|
||||
if (experiment.IsPulsedSource())
|
||||
plots.GetXFELEventCode(v);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "../common/ThreadSafeFIFO.h"
|
||||
#include "../common/NUMAHWPolicy.h"
|
||||
#include "../common/SendBufferControl.h"
|
||||
#include "../common/PixelMask.h"
|
||||
|
||||
#include "../image_analysis/StrongPixelSet.h"
|
||||
#include "../image_analysis/AzimuthalIntegrationMapping.h"
|
||||
@@ -108,11 +109,13 @@ class JFJochReceiver {
|
||||
|
||||
NUMAHWPolicy numa_policy;
|
||||
|
||||
PreviewImage preview_image;
|
||||
PreviewImage preview_image_indexed;
|
||||
PreviewImage &preview_image;
|
||||
PreviewImage &preview_image_indexed;
|
||||
|
||||
LossyFilter serialmx_filter;
|
||||
|
||||
std::vector<uint32_t> user_mask_raw_coord;
|
||||
|
||||
void AcquireThread(uint16_t data_stream);
|
||||
void FrameTransformationThread(uint32_t threadid);
|
||||
void Cancel(const JFJochException &e);
|
||||
@@ -121,16 +124,19 @@ class JFJochReceiver {
|
||||
SpotFindingSettings GetSpotFindingSettings();
|
||||
void UpdateMaxImage(uint64_t image_number);
|
||||
void UpdateMaxDelay(uint64_t delay);
|
||||
void SendStartMessage();
|
||||
void SendStartMessage(const PixelMask &pixel_mask);
|
||||
void SendCalibration();
|
||||
public:
|
||||
JFJochReceiver(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *calibration,
|
||||
AcquisitionDeviceGroup &acquisition_devices,
|
||||
ImagePusher &image_pusher,
|
||||
Logger &logger, int64_t forward_and_sum_nthreads,
|
||||
const NUMAHWPolicy &numa_policy,
|
||||
const SpotFindingSettings &spot_finding_settings,
|
||||
PreviewImage &preview_image,
|
||||
PreviewImage &preview_image_indexed,
|
||||
SendBuffer &buffer);
|
||||
~JFJochReceiver();
|
||||
JFJochReceiver(const JFJochReceiver &other) = delete;
|
||||
@@ -148,9 +154,6 @@ public:
|
||||
|
||||
MultiLinePlot GetPlots(const PlotRequest& request);
|
||||
|
||||
std::string GetTIFF(bool calibration) const;
|
||||
std::string GetJPEG(const PreviewJPEGSettings &settings) const;
|
||||
|
||||
void GetXFELEventCode(std::vector<uint64_t> &v) const;
|
||||
void GetXFELPulseID(std::vector<uint64_t> &v) const;
|
||||
};
|
||||
|
||||
@@ -23,15 +23,15 @@ JFJochReceiverPlots::JFJochReceiverPlots(const DiffractionExperiment &experiment
|
||||
void JFJochReceiverPlots::Add(const DataMessage &msg, uint64_t file_number, const AzimuthalIntegrationProfile &profile) {
|
||||
bkg_estimate.AddElement(msg.number, msg.bkg_estimate);
|
||||
spot_count.AddElement(msg.number, msg.spots.size());
|
||||
spot_count_in_rings.AddElement(msg.number, msg.spot_count_in_rings);
|
||||
|
||||
saturated_pixels.AddElement(msg.number, msg.saturated_pixel_count);
|
||||
error_pixels.AddElement(msg.number, msg.error_pixel_count);
|
||||
strong_pixels.AddElement(msg.number, msg.strong_pixel_count);
|
||||
|
||||
image_collection_efficiency.AddElement(msg.number, msg.image_collection_efficiency);
|
||||
if (msg.receiver_aq_dev_delay)
|
||||
receiver_delay.AddElement(msg.number, msg.receiver_aq_dev_delay.value());
|
||||
if (msg.receiver_free_send_buf)
|
||||
receiver_free_send_buf.AddElement(msg.number, msg.receiver_free_send_buf.value());
|
||||
receiver_delay.AddElement(msg.number, msg.receiver_aq_dev_delay);
|
||||
receiver_free_send_buf.AddElement(msg.number, msg.receiver_free_send_buf);
|
||||
|
||||
indexing_solution.AddElement(msg.number, msg.indexing_result);
|
||||
indexing_solution_per_time_point.Add(file_number, msg.indexing_result);
|
||||
@@ -39,11 +39,8 @@ void JFJochReceiverPlots::Add(const DataMessage &msg, uint64_t file_number, cons
|
||||
az_int_profile += profile;
|
||||
az_int_profile_per_time_point.at(file_number) += profile;
|
||||
|
||||
if (msg.xfel_pulse_id)
|
||||
xfel_pulse_id.AddElement(msg.number, msg.xfel_pulse_id.value());
|
||||
|
||||
if (msg.xfel_event_code)
|
||||
xfel_event_code.AddElement(msg.number, msg.xfel_event_code.value());
|
||||
xfel_pulse_id.AddElement(msg.number, msg.xfel_pulse_id);
|
||||
xfel_event_code.AddElement(msg.number, msg.xfel_event_code);
|
||||
|
||||
for (const auto &[key, value] : msg.roi) {
|
||||
if (roi_sum.contains(key) && roi_sum[key])
|
||||
@@ -69,7 +66,15 @@ MultiLinePlot JFJochReceiverPlots::GetPlots(const PlotRequest &request) {
|
||||
case PlotType::RadInt:
|
||||
return az_int_profile.GetPlot();
|
||||
case PlotType::SpotCount:
|
||||
return spot_count.GetMeanPlot(nbins);
|
||||
tmp = spot_count.GetMeanPlot(nbins);
|
||||
tmp.at(0).title = "Spots (crystal)";
|
||||
ret.emplace_back(tmp.at(0));
|
||||
|
||||
tmp = spot_count_in_rings.GetMeanPlot(nbins);
|
||||
tmp.at(0).title = "Spots (rings)";
|
||||
ret.emplace_back(tmp.at(0));
|
||||
|
||||
return ret;
|
||||
case PlotType::IndexingRate:
|
||||
return indexing_solution.GetMeanPlot(nbins);
|
||||
case PlotType::BkgEstimate:
|
||||
|
||||
@@ -18,6 +18,7 @@ class JFJochReceiverPlots {
|
||||
|
||||
StatusVector<float> bkg_estimate;
|
||||
StatusVector<uint64_t> spot_count;
|
||||
StatusVector<uint64_t> spot_count_in_rings;
|
||||
StatusVector<uint64_t> indexing_solution;
|
||||
StatusVector<uint64_t> saturated_pixels;
|
||||
StatusVector<uint64_t> error_pixels;
|
||||
@@ -25,7 +26,6 @@ class JFJochReceiverPlots {
|
||||
StatusVector<uint64_t> receiver_delay;
|
||||
StatusVector<uint64_t> receiver_free_send_buf;
|
||||
StatusVector<float> image_collection_efficiency;
|
||||
StatusVector<float> unit_cell[6];
|
||||
SetAverage<uint64_t> indexing_solution_per_time_point;
|
||||
|
||||
std::map<std::string, std::unique_ptr<StatusVector<int64_t>>> roi_sum;
|
||||
|
||||
@@ -45,18 +45,26 @@ std::optional<JFJochReceiverStatus> JFJochReceiverService::GetStatus() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void JFJochReceiverService::Start(const DiffractionExperiment &experiment, const JFCalibration *calibration) {
|
||||
void JFJochReceiverService::Start(const DiffractionExperiment &experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *calibration) {
|
||||
std::unique_lock ul_state(state_mutex); // unique lock, as it will destroy and create receiver object
|
||||
if (state != ReceiverState::Idle)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Receiver not idle, cannot start");
|
||||
|
||||
try {
|
||||
preview_image.Configure(experiment, pixel_mask);
|
||||
preview_image_indexed.Configure(experiment, pixel_mask);
|
||||
|
||||
// Thanks to properties of unique_ptr, starting new measurement will call destructor of JFJochReceiver, which will
|
||||
// ensure that everything was rolled back
|
||||
receiver = std::make_unique<JFJochReceiver>(experiment, calibration,
|
||||
receiver = std::make_unique<JFJochReceiver>(experiment, pixel_mask,
|
||||
calibration,
|
||||
aq_devices, image_pusher,
|
||||
logger, nthreads, numa_policy,
|
||||
spot_finding_settings,
|
||||
preview_image,
|
||||
preview_image_indexed,
|
||||
buffer);
|
||||
try {
|
||||
// Don't want to stop
|
||||
@@ -128,19 +136,17 @@ std::vector<AcquisitionDeviceNetConfig> JFJochReceiverService::GetNetworkConfig(
|
||||
}
|
||||
|
||||
std::string JFJochReceiverService::GetTIFF(bool calibration) const {
|
||||
std::unique_lock ul(state_mutex);
|
||||
if (receiver)
|
||||
return receiver->GetTIFF(calibration);
|
||||
if (calibration)
|
||||
return preview_image.GenerateTIFFDioptas();
|
||||
else
|
||||
return "";
|
||||
return preview_image.GenerateTIFF();
|
||||
}
|
||||
|
||||
std::string JFJochReceiverService::GetJPEG(const PreviewJPEGSettings &settings) const {
|
||||
std::unique_lock ul(state_mutex);
|
||||
if (receiver)
|
||||
return receiver->GetJPEG(settings);
|
||||
if (settings.show_indexed)
|
||||
return preview_image_indexed.GenerateJPEG(settings);
|
||||
else
|
||||
return "";
|
||||
return preview_image.GenerateJPEG(settings);
|
||||
}
|
||||
|
||||
void JFJochReceiverService::LoadInternalGeneratorImage(const DiffractionExperiment &experiment,
|
||||
|
||||
@@ -25,6 +25,9 @@ class JFJochReceiverService {
|
||||
std::future<void> measurement;
|
||||
void FinalizeMeasurement();
|
||||
SpotFindingSettings spot_finding_settings;
|
||||
|
||||
PreviewImage preview_image;
|
||||
PreviewImage preview_image_indexed;
|
||||
public:
|
||||
JFJochReceiverService(AcquisitionDeviceGroup &aq_devices,
|
||||
Logger &logger,
|
||||
@@ -37,7 +40,9 @@ public:
|
||||
void LoadInternalGeneratorImage(const DiffractionExperiment& experiment,
|
||||
const std::vector<uint16_t> &raw_expected_image,
|
||||
uint64_t image_number);
|
||||
void Start(const DiffractionExperiment &experiment, const JFCalibration *calibration);
|
||||
void Start(const DiffractionExperiment &experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *calibration);
|
||||
void Cancel(bool silent);
|
||||
JFJochReceiverOutput Stop();
|
||||
void SetSpotFindingSettings(const SpotFindingSettings& settings);
|
||||
|
||||
@@ -54,6 +54,8 @@ bool JFJochReceiverTest(JFJochReceiverOutput &output, Logger &logger,
|
||||
size_t send_buf_size_MiB) {
|
||||
|
||||
JFCalibration calib = GeneratePedestalCalibration(x);
|
||||
PixelMask mask(x);
|
||||
mask.LoadDetectorBadPixelMask(calib.CalculateMask());
|
||||
|
||||
int64_t image_number = x.GetImageNum() - 1;
|
||||
|
||||
@@ -75,7 +77,7 @@ bool JFJochReceiverTest(JFJochReceiverOutput &output, Logger &logger,
|
||||
settings.max_pix_per_spot = 200;
|
||||
service.SetSpotFindingSettings(settings);
|
||||
|
||||
service.Start(x, &calib);
|
||||
service.Start(x, mask, &calib);
|
||||
output = service.Stop();
|
||||
|
||||
bool no_errors = true;
|
||||
|
||||
@@ -345,6 +345,7 @@ TEST_CASE("CBORSerialize_Image", "[CBOR]") {
|
||||
.image = image,
|
||||
.image_collection_efficiency = 0.11,
|
||||
.spots = spots,
|
||||
.spot_count_in_rings = 157,
|
||||
.bkg_estimate = 12.345f,
|
||||
.indexing_result = 1,
|
||||
.adu_histogram = {3, 4, 5, 8},
|
||||
@@ -401,6 +402,7 @@ TEST_CASE("CBORSerialize_Image", "[CBOR]") {
|
||||
REQUIRE(image_array.image_collection_efficiency == message.image_collection_efficiency);
|
||||
REQUIRE(image_array.user_data == message.user_data);
|
||||
REQUIRE(image_array.original_number == message.original_number);
|
||||
REQUIRE(image_array.spot_count_in_rings == message.spot_count_in_rings);
|
||||
}
|
||||
|
||||
TEST_CASE("CBORSerialize_Image_2", "[CBOR]") {
|
||||
|
||||
@@ -1513,6 +1513,7 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_count_threshol
|
||||
|
||||
HLSSimulatedDevice test(0, 64);
|
||||
|
||||
std::vector<float> d_map(RAW_MODULE_SIZE, 3.0);
|
||||
std::vector<uint16_t> frame(RAW_MODULE_SIZE, 0);
|
||||
frame [ 1*1024 + 1] = 11;
|
||||
frame [123*1024 + 578] = 10;
|
||||
@@ -1520,14 +1521,17 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_count_threshol
|
||||
frame [ 89*1024 + 300] = 8;
|
||||
frame [300*1024 + 0] = 9;
|
||||
|
||||
for (int m = 0; m < x.GetModulesNum(); m++)
|
||||
for (int m = 0; m < x.GetModulesNum(); m++) {
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), m);
|
||||
}
|
||||
|
||||
SpotFindingSettings parameters{
|
||||
.signal_to_noise_threshold = 0.0,
|
||||
.photon_count_threshold = 9,
|
||||
.min_pix_per_spot = 1
|
||||
.signal_to_noise_threshold = 0.0,
|
||||
.photon_count_threshold = 9,
|
||||
.min_pix_per_spot = 1,
|
||||
.high_resolution_limit = 2.5,
|
||||
.low_resolution_limit = 3.1
|
||||
};
|
||||
test.SetSpotFinderParameters(parameters);
|
||||
|
||||
@@ -1577,14 +1581,19 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_mask", "[FPGA]
|
||||
frame [ 256*1024 + 876] = 50;
|
||||
frame [ 320*1024 + 400] = 50;
|
||||
|
||||
for (int m = 0; m < x.GetModulesNum(); m++)
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
std::vector<float> d_map(RAW_MODULE_SIZE, 3.0);
|
||||
|
||||
for (int m = 0; m < x.GetModulesNum(); m++) {
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), m);
|
||||
}
|
||||
|
||||
SpotFindingSettings parameters{
|
||||
.signal_to_noise_threshold = 0.0,
|
||||
.photon_count_threshold = 45,
|
||||
.min_pix_per_spot = 1
|
||||
.min_pix_per_spot = 1,
|
||||
.high_resolution_limit = 2.0,
|
||||
.low_resolution_limit = 4.0
|
||||
};
|
||||
test.SetSpotFinderParameters(parameters);
|
||||
|
||||
@@ -1616,6 +1625,8 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_min_pix_per_sp
|
||||
|
||||
HLSSimulatedDevice test(0, 64);
|
||||
|
||||
std::vector<float> d_map(RAW_MODULE_SIZE, 3.0);
|
||||
|
||||
std::vector<uint16_t> frame(RAW_MODULE_SIZE, 0);
|
||||
frame [1*1024 + 1] = 11;
|
||||
frame [1*1024 + 2] = 10;
|
||||
@@ -1630,13 +1641,17 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_min_pix_per_sp
|
||||
frame [101*1024+200] = 12;
|
||||
frame [RAW_MODULE_SIZE - 1 - 1024] = 20;
|
||||
|
||||
for (int m = 0; m < x.GetModulesNum(); m++)
|
||||
for (int m = 0; m < x.GetModulesNum(); m++) {
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), m);
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
}
|
||||
|
||||
SpotFindingSettings parameters{
|
||||
.signal_to_noise_threshold = 0.0,
|
||||
.photon_count_threshold = 9,
|
||||
.min_pix_per_spot = 2
|
||||
.min_pix_per_spot = 2,
|
||||
.high_resolution_limit = 2.0,
|
||||
.low_resolution_limit = 4.0
|
||||
};
|
||||
test.SetSpotFinderParameters(parameters);
|
||||
|
||||
@@ -1685,9 +1700,10 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_d_min_max", "[
|
||||
frame [1024+4] = 12;
|
||||
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), 0);
|
||||
for (int m = 0; m < x.GetModulesNum(); m++)
|
||||
for (int m = 0; m < x.GetModulesNum(); m++) {
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), m);
|
||||
}
|
||||
|
||||
SpotFindingSettings parameters{
|
||||
.signal_to_noise_threshold = 0.0,
|
||||
@@ -1727,6 +1743,7 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_snr_threshold"
|
||||
|
||||
HLSSimulatedDevice test(0, 64);
|
||||
|
||||
std::vector<float> d_map(RAW_MODULE_SIZE, 3.0);
|
||||
std::vector<uint16_t> frame(RAW_MODULE_SIZE);
|
||||
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
||||
frame[i] = ((i / RAW_MODULE_COLS) + (i % RAW_MODULE_COLS)) % 2;
|
||||
@@ -1741,13 +1758,17 @@ TEST_CASE("HLS_C_Simulation_internal_packet_generator_spot_finder_snr_threshold"
|
||||
frame [ 89*1024 + 300] = 7;
|
||||
frame [300*1024 + 5] = 3;
|
||||
|
||||
for (int m = 0; m < x.GetModulesNum(); m++)
|
||||
for (int m = 0; m < x.GetModulesNum(); m++) {
|
||||
test.SetInternalGeneratorFrame(frame.data(), m);
|
||||
test.InitializeSpotFinderResolutionMap(d_map.data(), m);
|
||||
}
|
||||
|
||||
SpotFindingSettings parameters{
|
||||
.signal_to_noise_threshold = 10.0,
|
||||
.photon_count_threshold = 0,
|
||||
.min_pix_per_spot = 1
|
||||
.min_pix_per_spot = 1,
|
||||
.high_resolution_limit = 2.0,
|
||||
.low_resolution_limit = 3.1
|
||||
};
|
||||
test.SetSpotFinderParameters(parameters);
|
||||
|
||||
|
||||
@@ -226,7 +226,11 @@ TEST_CASE("HDF5Writer_Socket", "[HDF5][Full]") {
|
||||
std::vector<SpotToSave> spots;
|
||||
|
||||
x.FilePrefix("test05").ImagesPerTrigger(5).ImagesPerFile(2).Compression(CompressionAlgorithm::NO_COMPRESSION)
|
||||
.HeaderAppendix("{\"z\":567}");
|
||||
.HeaderAppendix("{\"z\":567}").DetectorDistance_mm(155).BeamX_pxl(1606.62).BeamY_pxl(1669.59)
|
||||
.FrameTime(std::chrono::microseconds(1000), std::chrono::microseconds(100))
|
||||
.PhotonEnergy_keV(12.07).SetUnitCell(UnitCell{.a = 97, .b = 97, .c = 38, .alpha= 90, .beta = 90, .gamma = 90})
|
||||
.SpaceGroupNumber(96);
|
||||
|
||||
StartMessage start_message;
|
||||
x.FillMessage(start_message);
|
||||
|
||||
@@ -259,11 +263,15 @@ TEST_CASE("HDF5Writer_Socket", "[HDF5][Full]") {
|
||||
nlohmann::json j;
|
||||
|
||||
REQUIRE(s.Receive(msg, true));
|
||||
|
||||
j = nlohmann::json::parse(std::string((char *) msg.data(), msg.size()));
|
||||
REQUIRE(j["filename"] == "test05_data_000001.h5");
|
||||
REQUIRE(j["nimages"] == 2);
|
||||
REQUIRE(j["photon_energy_eV"] == Catch::Approx(x.GetPhotonEnergy_keV() * 1000.0));
|
||||
REQUIRE(j["space_group_number"] == 96);
|
||||
REQUIRE(j.contains("user_data"));
|
||||
REQUIRE(j["user_data"]["z"] == 567);
|
||||
std::cout << j.dump(4) << std::endl;
|
||||
|
||||
REQUIRE(s.Receive(msg, true));
|
||||
j = nlohmann::json::parse(std::string((char *) msg.data(), msg.size()));
|
||||
|
||||
@@ -491,6 +491,8 @@ TEST_CASE("JFJochReceiverTest_PacketLost_Raw", "[JFJochReceiver]") {
|
||||
x.PedestalG0Frames(0).NumTriggers(1)
|
||||
.UseInternalPacketGenerator(false).ImagesPerTrigger(4).PhotonEnergy_keV(12.4);
|
||||
|
||||
PixelMask pixel_mask(x);
|
||||
|
||||
AcquisitionDeviceGroup aq_devices;
|
||||
for (int i = 0; i < x.GetDataStreamsNum(); i++) {
|
||||
auto test = std::make_unique<HLSSimulatedDevice>(i, 64);
|
||||
@@ -517,7 +519,7 @@ TEST_CASE("JFJochReceiverTest_PacketLost_Raw", "[JFJochReceiver]") {
|
||||
JFJochReceiverService service(aq_devices, logger, pusher);
|
||||
service.NumThreads(nthreads);
|
||||
|
||||
service.Start(x, &calib);
|
||||
service.Start(x, pixel_mask, &calib);
|
||||
auto receiver_out = service.Stop();
|
||||
|
||||
const auto image = pusher.GetImage();
|
||||
@@ -551,6 +553,8 @@ TEST_CASE("JFJochReceiverTest_Cancel", "[JFJochReceiver]") {
|
||||
x.PedestalG0Frames(0).NumTriggers(1)
|
||||
.UseInternalPacketGenerator(false).ImagesPerTrigger(4).PhotonEnergy_keV(12.4);
|
||||
|
||||
PixelMask pixel_mask(x);
|
||||
|
||||
AcquisitionDeviceGroup aq_devices;
|
||||
for (int i = 0; i < x.GetDataStreamsNum(); i++) {
|
||||
auto test = std::make_unique<HLSSimulatedDevice>(i, 64);
|
||||
@@ -570,7 +574,7 @@ TEST_CASE("JFJochReceiverTest_Cancel", "[JFJochReceiver]") {
|
||||
JFJochReceiverService service(aq_devices, logger, pusher);
|
||||
service.NumThreads(nthreads);
|
||||
|
||||
service.Start(x, &calib);
|
||||
service.Start(x, pixel_mask, &calib);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(60));
|
||||
service.Cancel(false);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "../writer/HDF5Objects.h"
|
||||
#include "../receiver/JFJochReceiverService.h"
|
||||
#include "../common/DiffractionGeometry.h"
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
#include "../common/ZMQWrappers.h"
|
||||
#include "../frame_serialize/ZMQStream2Pusher.h"
|
||||
#include "../writer/StreamWriter.h"
|
||||
@@ -26,6 +26,7 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index", "[JFJochReceiver]
|
||||
.FilePrefix("lyso_test").ConversionOnFPGA(false)
|
||||
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
|
||||
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
|
||||
PixelMask pixel_mask(experiment);
|
||||
|
||||
// Load example image
|
||||
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
|
||||
@@ -64,9 +65,11 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index", "[JFJochReceiver]
|
||||
settings.photon_count_threshold = 5;
|
||||
settings.min_pix_per_spot = 1;
|
||||
settings.max_pix_per_spot = 200;
|
||||
settings.high_resolution_limit = 2.0;
|
||||
settings.low_resolution_limit = 50.0;
|
||||
service.SetSpotFindingSettings(settings);
|
||||
|
||||
service.Start(experiment, nullptr);
|
||||
service.Start(experiment, pixel_mask, nullptr);
|
||||
auto receiver_out = service.Stop();
|
||||
|
||||
std::string jpeg;
|
||||
@@ -80,7 +83,8 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index", "[JFJochReceiver]
|
||||
f.write(jpeg.data(), jpeg.size());
|
||||
|
||||
CHECK(receiver_out.efficiency == 1.0);
|
||||
CHECK(receiver_out.status.indexing_rate == 1.0);
|
||||
REQUIRE(receiver_out.status.indexing_rate);
|
||||
CHECK(receiver_out.status.indexing_rate.value() == 1.0);
|
||||
CHECK(receiver_out.status.images_sent == experiment.GetImageNum());
|
||||
CHECK(!receiver_out.status.cancelled);
|
||||
|
||||
@@ -99,6 +103,7 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index_min_pix_2", "[JFJoc
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
|
||||
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
|
||||
PixelMask pixel_mask(experiment);
|
||||
|
||||
// Load example image
|
||||
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
|
||||
@@ -138,7 +143,7 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index_min_pix_2", "[JFJoc
|
||||
settings.max_pix_per_spot = 200;
|
||||
service.SetSpotFindingSettings(settings);
|
||||
|
||||
service.Start(experiment, nullptr);
|
||||
service.Start(experiment, pixel_mask, nullptr);
|
||||
auto receiver_out = service.Stop();
|
||||
|
||||
std::string jpeg;
|
||||
@@ -195,6 +200,8 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_ROI", "[JFJochReceiver]") {
|
||||
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
|
||||
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
|
||||
|
||||
PixelMask pixel_mask(experiment);
|
||||
|
||||
experiment.ROI().SetROIBox({ROIBox("roi0", 100, 120, 20,30)});
|
||||
experiment.ROI().SetROICircle({ROICircle("roi1", 500, 800, 10)});
|
||||
|
||||
@@ -242,7 +249,7 @@ TEST_CASE("JFJochIntegrationTest_ZMQ_ROI", "[JFJochReceiver]") {
|
||||
JFJochReceiverService service(aq_devices, logger, pusher);
|
||||
service.NumThreads(nthreads);
|
||||
|
||||
service.Start(experiment, nullptr);
|
||||
service.Start(experiment, pixel_mask, nullptr);
|
||||
auto receiver_out = service.Stop();
|
||||
|
||||
std::string jpeg;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include "../preview//WriteJPEG.h"
|
||||
#include "../preview//JFJochJPEG.h"
|
||||
#include "../preview//PreviewImage.h"
|
||||
#include "../writer/HDF5Objects.h"
|
||||
#include "../common/RawToConvertedGeometry.h"
|
||||
@@ -24,7 +24,19 @@ TEST_CASE("JPEGTest","[JPEG]") {
|
||||
f.write(s.data(), s.size());
|
||||
}
|
||||
|
||||
TEST_CASE("PreviewImage_NotConfigured","[JPEG]") {
|
||||
std::vector<int16_t> image_conv_2(67878);
|
||||
std::vector<SpotToSave> spots;
|
||||
|
||||
PreviewImage image;
|
||||
REQUIRE_THROWS(image.UpdateImage(image_conv_2.data(), spots));
|
||||
REQUIRE(image.GenerateJPEG(PreviewJPEGSettings()).empty());
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("PreviewImage_GenerateJPEG","[JPEG]") {
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
@@ -57,7 +69,11 @@ TEST_CASE("PreviewImage_GenerateJPEG","[JPEG]") {
|
||||
{.x = 800, .y = 1000, .indexed = false},
|
||||
{.x = 1200, .y = 500, .indexed = true}
|
||||
};
|
||||
PreviewImage image(experiment);
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
PreviewImage image;
|
||||
image.Configure(experiment, mask);
|
||||
|
||||
PreviewJPEGSettings preview_settings{
|
||||
.saturation_value = 5,
|
||||
@@ -69,7 +85,6 @@ TEST_CASE("PreviewImage_GenerateJPEG","[JPEG]") {
|
||||
|
||||
image.UpdateImage(image_conv_2.data(), spots);
|
||||
|
||||
|
||||
std::string s;
|
||||
REQUIRE_NOTHROW(s = image.GenerateJPEG(preview_settings));
|
||||
std::ofstream f("lyso_diff.jpeg", std::ios::binary);
|
||||
@@ -77,6 +92,8 @@ TEST_CASE("PreviewImage_GenerateJPEG","[JPEG]") {
|
||||
}
|
||||
|
||||
TEST_CASE("PreviewImage_GenerateJPEG_ROI","[JPEG]") {
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
@@ -111,7 +128,11 @@ TEST_CASE("PreviewImage_GenerateJPEG_ROI","[JPEG]") {
|
||||
{.x = 800, .y = 1000, .indexed = false},
|
||||
{.x = 1200, .y = 500, .indexed = true}
|
||||
};
|
||||
PreviewImage image(experiment);
|
||||
PreviewImage image;
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
image.Configure(experiment, mask);
|
||||
image.UpdateImage(image_conv_2.data(), spots);
|
||||
|
||||
PreviewJPEGSettings preview_settings{
|
||||
@@ -128,6 +149,8 @@ TEST_CASE("PreviewImage_GenerateJPEG_ROI","[JPEG]") {
|
||||
}
|
||||
|
||||
TEST_CASE("PreviewImage_GenerateJPEG_resolution","[JPEG]") {
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
@@ -152,8 +175,11 @@ TEST_CASE("PreviewImage_GenerateJPEG_resolution","[JPEG]") {
|
||||
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
|
||||
RawToConvertedGeometry(experiment, image_conv_2.data(), image_raw_geom.data());
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<SpotToSave> spots = {};
|
||||
PreviewImage image(experiment);
|
||||
PreviewImage image;
|
||||
image.Configure(experiment, mask);
|
||||
image.UpdateImage(image_conv_2.data(), spots);
|
||||
|
||||
PreviewJPEGSettings preview_settings{
|
||||
|
||||
@@ -57,22 +57,119 @@ TEST_CASE("PixelMask_CalculateNexusMask","[PixelMask]") {
|
||||
v[2] = 1;
|
||||
v[4] = 1;
|
||||
v[5] = 1;
|
||||
v[3 * RAW_MODULE_SIZE + 780] = 1;
|
||||
|
||||
REQUIRE_NOTHROW(mask.LoadDetectorBadPixelMask(v, 2));
|
||||
REQUIRE_NOTHROW(mask.LoadDetectorBadPixelMask(v));
|
||||
|
||||
auto mask_v = mask.GetMask(experiment);
|
||||
|
||||
REQUIRE(mask_v.size() == experiment.GetPixelsNum() );
|
||||
REQUIRE(mask_v[0] == 0);
|
||||
REQUIRE(mask_v[1] == 0);
|
||||
REQUIRE(mask_v[2] == 4);
|
||||
REQUIRE(mask_v[2] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[3] == 0);
|
||||
REQUIRE(mask_v[4] == 4);
|
||||
REQUIRE(mask_v[5] == 4);
|
||||
REQUIRE(mask_v[4] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[5] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[6] == 0);
|
||||
REQUIRE(mask_v[1030 * 700 + 300] == 0);
|
||||
REQUIRE(mask_v[(514+36)*1030*3 + 780 + 6] == (1 << PixelMask::ErrorPixelBit));
|
||||
}
|
||||
|
||||
TEST_CASE("PixelMask_CalculateNexusMask_GetMaskRaw","[PixelMask]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(4, 1, 8, 36, false));
|
||||
experiment.MaskModuleEdges(false).MaskChipEdges(false);
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<uint32_t> v(4 * RAW_MODULE_SIZE, 0);
|
||||
v[2] = 1;
|
||||
v[4] = 1;
|
||||
v[5] = 1;
|
||||
v[3 * RAW_MODULE_SIZE + 780] = 1;
|
||||
|
||||
REQUIRE_NOTHROW(mask.LoadDetectorBadPixelMask(v));
|
||||
|
||||
auto mask_v = mask.GetMask(experiment, false);
|
||||
|
||||
REQUIRE(mask_v.size() == 4 * RAW_MODULE_SIZE );
|
||||
REQUIRE(mask_v[0] == 0);
|
||||
REQUIRE(mask_v[1] == 0);
|
||||
REQUIRE(mask_v[2] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[3] == 0);
|
||||
REQUIRE(mask_v[4] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[5] == (1 << PixelMask::ErrorPixelBit));
|
||||
REQUIRE(mask_v[6] == 0);
|
||||
|
||||
REQUIRE(mask_v[3 * RAW_MODULE_SIZE + 780] == (1 << PixelMask::ErrorPixelBit));
|
||||
}
|
||||
|
||||
TEST_CASE("PixelMask_CalculateNexusMask_UserMaskRaw","[PixelMask]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(4, 1, 8, 36, false));
|
||||
experiment.MaskModuleEdges(false).MaskChipEdges(false);
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<uint32_t> v(4 * RAW_MODULE_SIZE, 0);
|
||||
v[2] = 1;
|
||||
v[4] = 1;
|
||||
v[5] = 1;
|
||||
|
||||
REQUIRE_NOTHROW(mask.LoadUserMask(experiment, v));
|
||||
|
||||
auto mask_v = mask.GetMask(experiment);
|
||||
|
||||
REQUIRE(mask_v.size() == experiment.GetPixelsNum() );
|
||||
REQUIRE(mask_v[0] == 0);
|
||||
REQUIRE(mask_v[1] == 0);
|
||||
REQUIRE(mask_v[2] == (1 << PixelMask::UserMaskedPixelBit));
|
||||
REQUIRE(mask_v[3] == 0);
|
||||
REQUIRE(mask_v[4] == (1 << PixelMask::UserMaskedPixelBit));
|
||||
REQUIRE(mask_v[5] == (1 << PixelMask::UserMaskedPixelBit));
|
||||
REQUIRE(mask_v[6] == 0);
|
||||
REQUIRE(mask_v[1030 * 700 + 300] == 0);
|
||||
REQUIRE(mask_v[(1030+8)*514] == (1 << PixelMask::ModuleGapPixelBit));
|
||||
|
||||
auto user_mask_v = mask.GetUserMask(experiment);
|
||||
|
||||
REQUIRE(user_mask_v.size() == experiment.GetPixelsNum() );
|
||||
REQUIRE(user_mask_v[0] == 0);
|
||||
REQUIRE(user_mask_v[1] == 0);
|
||||
REQUIRE(user_mask_v[2] == 1);
|
||||
REQUIRE(user_mask_v[3] == 0);
|
||||
REQUIRE(user_mask_v[4] == 1);
|
||||
REQUIRE(user_mask_v[5] == 1);
|
||||
REQUIRE(user_mask_v[6] == 0);
|
||||
REQUIRE(user_mask_v[1030 * 700 + 300] == 0);
|
||||
|
||||
REQUIRE(user_mask_v[(1030+8)*514] == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("PixelMask_CalculateNexusMask_UserMaskConv","[PixelMask]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(4, 1, 8, 36, false));
|
||||
experiment.MaskModuleEdges(false).MaskChipEdges(false);
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<uint32_t> v(experiment.GetPixelsNum(), 0);
|
||||
v[1030 * 700 + 300] = 1;
|
||||
|
||||
REQUIRE_NOTHROW(mask.LoadUserMask(experiment, v));
|
||||
|
||||
auto mask_v = mask.GetMask(experiment);
|
||||
|
||||
REQUIRE(mask_v.size() == experiment.GetPixelsNum() );
|
||||
REQUIRE(mask_v[1030 * 700 + 300] == (1 << PixelMask::UserMaskedPixelBit));
|
||||
REQUIRE(mask_v[(1030+8)*514] == (1 << PixelMask::ModuleGapPixelBit));
|
||||
|
||||
auto user_mask_v = mask.GetUserMask(experiment);
|
||||
|
||||
REQUIRE(user_mask_v.size() == experiment.GetPixelsNum() );
|
||||
REQUIRE(user_mask_v[1030 * 700 + 300] == 1);
|
||||
|
||||
REQUIRE(user_mask_v[(1030+8)*514] == 0);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("PixelMask_MaskDetectorGaps","[PixelMask]") {
|
||||
DiffractionExperiment experiment(DetectorGeometry(8, 2, 8, 36, true));
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ TEST_CASE("StreamWriterTest_ZMQ","[JFJochWriter]") {
|
||||
x.FilePrefix("subdir/JFJochWriterTest").NumTriggers(1).ImagesPerTrigger(5)
|
||||
.UseInternalPacketGenerator(true).Mode(DetectorMode::Raw).PedestalG0Frames(0);
|
||||
|
||||
PixelMask pixel_mask(x);
|
||||
|
||||
JFModuleGainCalibration gain;
|
||||
AcquisitionDeviceGroup aq_devices;
|
||||
for (int i = 0; i < x.GetDataStreamsNum(); i++)
|
||||
@@ -32,7 +34,7 @@ TEST_CASE("StreamWriterTest_ZMQ","[JFJochWriter]") {
|
||||
REQUIRE(pusher_addr.size() == 1);
|
||||
REQUIRE_NOTHROW(writer = std::make_unique<StreamWriter>(logger, pusher_addr[0]));
|
||||
CHECK (writer->GetStatistics().state == StreamWriterState::Idle);
|
||||
REQUIRE_NOTHROW(fpga_receiver_service.Start(x, nullptr));
|
||||
REQUIRE_NOTHROW(fpga_receiver_service.Start(x, pixel_mask, nullptr));
|
||||
|
||||
REQUIRE_NOTHROW(writer->Run());
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
#include "../preview/PreviewImage.h"
|
||||
#include "../writer/HDF5Objects.h"
|
||||
#include "../common/RawToConvertedGeometry.h"
|
||||
@@ -14,6 +14,25 @@ TEST_CASE("TIFFTest","[TIFF]") {
|
||||
REQUIRE_NOTHROW(WriteTIFFToString(values.data(), 1024, 512, 2));
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TEST_CASE("TIFFTest_Write_Read","[TIFF]") {
|
||||
std::vector<uint32_t> values(512*1024), values_out;
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
values[i] = (i * 17 + 2);
|
||||
}
|
||||
|
||||
std::string s;
|
||||
REQUIRE_NOTHROW(s = WriteTIFFToString(values.data(), 1024, 512, 4));
|
||||
uint32_t lines, cols;
|
||||
|
||||
REQUIRE_NOTHROW(values_out = ReadTIFFFromString32(s, cols, lines));
|
||||
REQUIRE(lines == 512);
|
||||
REQUIRE(cols == 1024);
|
||||
REQUIRE(values.size() == values_out.size());
|
||||
REQUIRE(memcmp(values.data(), values_out.data(), cols * lines * sizeof(uint32_t)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TIFFTest_File","[TIFF]") {
|
||||
std::vector<uint16_t> values(512*1024);
|
||||
|
||||
@@ -33,6 +52,8 @@ TEST_CASE("TIFFTest_File_signed","[TIFF]") {
|
||||
}
|
||||
|
||||
TEST_CASE("PreviewImage_GenerateTIFF","[TIFF]") {
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
@@ -57,8 +78,11 @@ TEST_CASE("PreviewImage_GenerateTIFF","[TIFF]") {
|
||||
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
|
||||
RawToConvertedGeometry(experiment, image_conv_2.data(), image_raw_geom.data());
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<SpotToSave> spots;
|
||||
PreviewImage image(experiment);
|
||||
PreviewImage image;
|
||||
image.Configure(experiment, mask);
|
||||
image.UpdateImage(image_conv_2.data(), spots);
|
||||
|
||||
std::string s;
|
||||
@@ -69,6 +93,8 @@ TEST_CASE("PreviewImage_GenerateTIFF","[TIFF]") {
|
||||
|
||||
|
||||
TEST_CASE("PreviewImage_GenerateTIFFDioptas","[TIFF]") {
|
||||
RegisterHDF5Filter();
|
||||
|
||||
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
|
||||
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.FilePrefix("lyso_test_min_pix_2").ConversionOnFPGA(false)
|
||||
@@ -93,8 +119,11 @@ TEST_CASE("PreviewImage_GenerateTIFFDioptas","[TIFF]") {
|
||||
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
|
||||
RawToConvertedGeometry(experiment, image_conv_2.data(), image_raw_geom.data());
|
||||
|
||||
PixelMask mask(experiment);
|
||||
|
||||
std::vector<SpotToSave> spots;
|
||||
PreviewImage image(experiment);
|
||||
PreviewImage image;
|
||||
image.Configure(experiment, mask);
|
||||
image.UpdateImage(image_conv_2.data(), spots);
|
||||
|
||||
std::string s;
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
#include "../image_analysis/MXAnalyzer.h"
|
||||
#include "../preview/PreviewImage.h"
|
||||
#include "../receiver/LossyFilter.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
Logger logger("jfjoch_spot_finding_test");
|
||||
|
||||
bool write_jpeg = false;
|
||||
bool write_debug = false;
|
||||
|
||||
void print_usage() {
|
||||
logger.Info("Usage ./jfjoch_spot_finding_test {options} <name of master dataset> <name of data dataset>");
|
||||
@@ -34,6 +36,8 @@ void print_usage() {
|
||||
logger.Info("-I enable indexing");
|
||||
logger.Info("-t<float> indexing tolerance (0.0-1.0)");
|
||||
logger.Info("-l<float> lossy filter probability (0.0-1.0)");
|
||||
logger.Info("-r{<int>} filter powder rings (min spots)");
|
||||
logger.Info("-g write debug images");
|
||||
logger.Info("");
|
||||
}
|
||||
|
||||
@@ -76,12 +80,14 @@ int parse_options(DiffractionExperiment& experiment,
|
||||
{"indexing_tolerance", required_argument, 0, 't'},
|
||||
{"nimages", required_argument, 0, 'n'},
|
||||
{"lossy", required_argument, 0, 'l'},
|
||||
{"filter_rings", optional_argument, 0, 'r'},
|
||||
{"debug", no_argument, 0, 'g'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, "c:s:o:p:d:D:j?hS:Ii:t:l:",long_options, &option_index)) != -1 ) {
|
||||
while ((opt = getopt_long(argc, argv, "c:s:o:p:d:D:j?hS:Ii:t:l:r::g",long_options, &option_index)) != -1 ) {
|
||||
switch (opt) {
|
||||
case 'j':
|
||||
write_jpeg = true;
|
||||
@@ -119,7 +125,14 @@ int parse_options(DiffractionExperiment& experiment,
|
||||
case 'l':
|
||||
experiment.DataReductionFactorSerialMX(atof(optarg));
|
||||
break;
|
||||
case '?':
|
||||
case 'r':
|
||||
settings.filter_spots_powder_ring = true;
|
||||
if (optarg != nullptr)
|
||||
settings.min_spot_count_powder_ring = atol(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
write_debug = true;
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
@@ -209,7 +222,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
MXAnalyzer analyzer(x);
|
||||
|
||||
PreviewImage preview(x);
|
||||
PixelMask mask(x);
|
||||
|
||||
PreviewImage preview;
|
||||
preview.Configure(x, mask);
|
||||
|
||||
uint64_t indexed_images = 0;
|
||||
|
||||
LossyFilter filter(x);
|
||||
@@ -263,6 +280,33 @@ int main(int argc, char **argv) {
|
||||
static_cast<float>(indexed_images) / static_cast<float>(i+1) * 100.0f);
|
||||
}
|
||||
fileset.reset();
|
||||
|
||||
if (write_debug) {
|
||||
std::vector<float> convg(x.GetPixelsNum());
|
||||
std::vector<int32_t> convg_u32(x.GetPixelsNum());
|
||||
|
||||
if (!analyzer.GetCPUMean().empty()) {
|
||||
RawToConvertedGeometry(x, convg.data(), analyzer.GetCPUMean().data());
|
||||
for (int i = 0; i < convg.size(); i++)
|
||||
convg_u32[i] = std::lround(convg[i] * 10.0f);
|
||||
WriteTIFFToFile("spot_finding_mean.tiff", convg_u32.data(), x.GetXPixelsNum(), x.GetYPixelsNum(), 4, true);
|
||||
}
|
||||
|
||||
if (!analyzer.GetCPUStdDev().empty()) {
|
||||
RawToConvertedGeometry(x, convg.data(), analyzer.GetCPUStdDev().data());
|
||||
for (int i = 0; i < convg.size(); i++)
|
||||
convg_u32[i] = std::lround(convg[i] * 10.0f);
|
||||
WriteTIFFToFile("spot_finding_stddev.tiff", convg_u32.data(), x.GetXPixelsNum(), x.GetYPixelsNum(), 4, true);
|
||||
}
|
||||
|
||||
if (!analyzer.GetCPUStrongPixel().empty()) {
|
||||
RawToConvertedGeometry(x, convg_u32.data(), analyzer.GetCPUStrongPixel().data());
|
||||
WriteTIFFToFile("spot_finding_strong_pixel.tiff", convg_u32.data(), x.GetXPixelsNum(), x.GetYPixelsNum(), 4, true);
|
||||
}
|
||||
|
||||
WriteTIFFToFile("spot_finding_original_image.tiff", original_image.data(), x.GetXPixelsNum(), x.GetYPixelsNum(), 2, true);
|
||||
}
|
||||
|
||||
if (settings.indexing)
|
||||
logger.Info("Indexing rate: {:0.1f}%", static_cast<float>(indexed_images) / static_cast<float>(nimages) * 100.0f);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ void HDF5DataFilePluginMX::OpenFile(HDF5File &data_file, const DataMessage &msg)
|
||||
strong_pixel_count.reserve(RESERVE_IMAGES);
|
||||
indexed.reserve(RESERVE_IMAGES);
|
||||
indexed_lattice.reserve(9 * RESERVE_IMAGES);
|
||||
spot_count_rings.reserve(RESERVE_IMAGES);
|
||||
}
|
||||
|
||||
void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number) {
|
||||
@@ -33,6 +34,9 @@ void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number)
|
||||
strong_pixel_count.resize(max_image_number + 1);
|
||||
indexed.resize(max_image_number + 1);
|
||||
indexed_lattice.resize((max_image_number + 1) * 9);
|
||||
|
||||
if (msg.spot_count_in_rings)
|
||||
spot_count_rings.resize(max_image_number + 1);
|
||||
}
|
||||
|
||||
uint32_t spot_cnt = std::min(msg.spots.size(), max_spots);
|
||||
@@ -46,6 +50,9 @@ void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number)
|
||||
npeaks[image_number] = spot_cnt;
|
||||
strong_pixel_count[image_number] = msg.strong_pixel_count;
|
||||
indexed[image_number] = msg.indexing_result;
|
||||
if (msg.spot_count_in_rings)
|
||||
spot_count_rings[image_number] = msg.spot_count_in_rings.value();
|
||||
|
||||
if (msg.indexing_lattice.size() == 9) {
|
||||
for (int i = 0; i < 9; i++)
|
||||
indexed_lattice[image_number * 9 + i] = msg.indexing_lattice[i];
|
||||
@@ -64,6 +71,9 @@ void HDF5DataFilePluginMX::WriteFinal(HDF5File &data_file) {
|
||||
data_file.SaveVector("/entry/MX/strongPixels", strong_pixel_count);
|
||||
}
|
||||
|
||||
if (!spot_count_rings.empty())
|
||||
data_file.SaveVector("/entry/MX/nPeaksRingFiltered", spot_count_rings);
|
||||
|
||||
if (!indexed.empty())
|
||||
data_file.SaveVector("/entry/MX/imageIndexed", indexed);
|
||||
if (!indexed_lattice.empty())
|
||||
|
||||
@@ -15,7 +15,7 @@ class HDF5DataFilePluginMX : public HDF5DataFilePlugin {
|
||||
|
||||
std::vector<uint32_t> npeaks;
|
||||
std::vector<uint32_t> strong_pixel_count;
|
||||
|
||||
std::vector<uint32_t> spot_count_rings;
|
||||
// indexing
|
||||
std::vector<uint8_t> indexed;
|
||||
std::vector<float> indexed_lattice;
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
HDF5Writer::HDF5Writer(const StartMessage &request)
|
||||
: images_per_file(request.images_per_file),
|
||||
file_prefix(request.file_prefix),
|
||||
max_spot_count(request.max_spot_count),
|
||||
az_int_bin_to_q(request.az_int_bin_to_q),
|
||||
user_data(request.user_data) {}
|
||||
: start_message(request) {
|
||||
|
||||
}
|
||||
|
||||
void HDF5Writer::Write(const DataMessage& message) {
|
||||
std::lock_guard<std::mutex> lock(hdf5_mutex);
|
||||
@@ -21,23 +19,23 @@ void HDF5Writer::Write(const DataMessage& message) {
|
||||
|
||||
size_t file_number = 0;
|
||||
size_t image_number = message.number;
|
||||
if (images_per_file > 0) {
|
||||
file_number = message.number / images_per_file;
|
||||
image_number = message.number % images_per_file;
|
||||
if (start_message.images_per_file > 0) {
|
||||
file_number = message.number / start_message.images_per_file;
|
||||
image_number = message.number % start_message.images_per_file;
|
||||
}
|
||||
if (files.size() <= file_number)
|
||||
files.resize(file_number + 1);
|
||||
|
||||
if (!files[file_number])
|
||||
files[file_number] = std::make_unique<HDF5DataFile>(HDF5Metadata::DataFileName(file_prefix, file_number),
|
||||
az_int_bin_to_q,
|
||||
file_number * images_per_file,
|
||||
max_spot_count);
|
||||
files[file_number] = std::make_unique<HDF5DataFile>(HDF5Metadata::DataFileName(start_message.file_prefix, file_number),
|
||||
start_message.az_int_bin_to_q,
|
||||
file_number * start_message.images_per_file,
|
||||
start_message.max_spot_count);
|
||||
// Ignore zero size images
|
||||
if (message.image.size > 0)
|
||||
files[file_number]->Write(message, image_number);
|
||||
|
||||
if (files[file_number]->GetNumImages() == images_per_file)
|
||||
if (files[file_number]->GetNumImages() == start_message.images_per_file)
|
||||
AddStats(files[file_number]->Close());
|
||||
}
|
||||
|
||||
@@ -59,14 +57,35 @@ void HDF5Writer::AddStats(const std::optional<HDF5DataFileStatistics>& s) {
|
||||
nlohmann::json j;
|
||||
j["filename"] = s->filename;
|
||||
j["nimages"] = s->total_images;
|
||||
if (!user_data.empty()) {
|
||||
j["detector_distance_m"] = start_message.detector_distance;
|
||||
j["beam_x_pxl"] = start_message.beam_center_x;
|
||||
j["beam_y_pxl"] = start_message.beam_center_y;
|
||||
j["pixel_size_m"] = start_message.pixel_size_x;
|
||||
j["detector_width_pxl"] = start_message.image_size_x;
|
||||
j["detector_height_pxl"] = start_message.image_size_y;
|
||||
j["photon_energy_eV"] = start_message.incident_energy;
|
||||
j["saturation"] = start_message.saturation_value;
|
||||
if (start_message.unit_cell) {
|
||||
j["unit_cell"]["a"] = start_message.unit_cell->a;
|
||||
j["unit_cell"]["b"] = start_message.unit_cell->b;
|
||||
j["unit_cell"]["c"] = start_message.unit_cell->c;
|
||||
j["unit_cell"]["alpha"] = start_message.unit_cell->alpha;
|
||||
j["unit_cell"]["beta"] = start_message.unit_cell->beta;
|
||||
j["unit_cell"]["gamma"] = start_message.unit_cell->gamma;
|
||||
}
|
||||
if (start_message.space_group_number > 0)
|
||||
j["space_group_number"] = start_message.space_group_number;
|
||||
if (start_message.error_value)
|
||||
j["underload"] = start_message.error_value.value();
|
||||
|
||||
if (!start_message.user_data.empty()) {
|
||||
nlohmann::json j_userdata;
|
||||
|
||||
// if user_data is valid json, interpret it as such, otherwise embed as string
|
||||
try {
|
||||
j_userdata = nlohmann::json::parse(user_data);
|
||||
j_userdata = nlohmann::json::parse(start_message.user_data);
|
||||
} catch (...) {
|
||||
j_userdata = user_data;
|
||||
j_userdata = start_message.user_data;
|
||||
}
|
||||
j["user_data"] = j_userdata;
|
||||
}
|
||||
|
||||
@@ -10,13 +10,10 @@
|
||||
#include "../common/ZMQWrappers.h"
|
||||
|
||||
class HDF5Writer {
|
||||
StartMessage start_message;
|
||||
|
||||
std::vector<std::unique_ptr<HDF5DataFile> > files;
|
||||
int64_t images_per_file;
|
||||
std::string file_prefix;
|
||||
uint64_t max_spot_count;
|
||||
std::vector<float> az_int_bin_to_q;
|
||||
std::vector<HDF5DataFileStatistics> stats;
|
||||
std::string user_data;
|
||||
|
||||
std::unique_ptr<ZMQSocket> socket;
|
||||
|
||||
|
||||
@@ -45,9 +45,24 @@ Creates PUB socket to inform about finalized data files. For each closed file, t
|
||||
"filename": <string>: HDF5 data file name (relative to writer root directory),
|
||||
"nimages": <int> number of images in the file,
|
||||
"user_data": <string> or <json> user_data
|
||||
"beam_x_pxl": <float> beam center (X) in pixels,
|
||||
"beam_y_pxl": <float> beam center (Y) in pixels,
|
||||
"detector_distance_m": <float> detector distance (X) in m,
|
||||
"detector_height_pxl": <int> detector size (X) in pixels,
|
||||
"detector_width_pxl": <int> detector size (Y) in pixels,
|
||||
"photon_energy_eV": <float> photon energy of the X-ray beam,
|
||||
"pixel_size_m": <float> pixel size in meter (assuming pixel X == Y),
|
||||
"saturation": <int> this count and higher mean saturation,
|
||||
"space_group_number": <int> space group number (optional),
|
||||
"underload": <int> pixels with this count should be excluded,
|
||||
"unit_cell": <optinal> unit cell dimensions in Angstrom/degree {
|
||||
"a": <float>, "b": <float>, "c": <float>,
|
||||
"alpha": <float>, "beta": <float>, "gamma": <float>
|
||||
},
|
||||
}
|
||||
```
|
||||
`user_data` is defined as `header_appendix` in the `/start` operation in the `jfjoch_broker`.
|
||||
Other metadata are also carried over from `/start` operation.
|
||||
|
||||
If the `header_appendix` is a string with valid JSON meaning, it will be embedded as JSON, otherwise it will be escaped as string.
|
||||
For example `header_appendix` of `{"param1": "test1", "param2": ["test1", "test2"]}`, than example message will look as follows:
|
||||
@@ -55,6 +70,26 @@ For example `header_appendix` of `{"param1": "test1", "param2": ["test1", "test2
|
||||
{
|
||||
"filename": "dataset_name_data_000001.h5",
|
||||
"nimages": 1000,
|
||||
"beam_x_pxl": 1200,
|
||||
"beam_y_pxl": 1500,
|
||||
"detector_distance_m": 0.155,
|
||||
"detector_height_pxl": 2164,
|
||||
"detector_width_pxl": 2068,
|
||||
"image_time_s": 0.001,
|
||||
"nimages": 2,
|
||||
"photon_energy_eV": 12400.0,
|
||||
"pixel_size_m": 7.5e-05,
|
||||
"saturation": 32766,
|
||||
"space_group_number": 96,
|
||||
"underload": -32768,
|
||||
"unit_cell": {
|
||||
"a": 78.0,
|
||||
"alpha": 90.0,
|
||||
"b": 78.0,
|
||||
"beta": 90.0,
|
||||
"c": 39.0,
|
||||
"gamma": 90.0
|
||||
},
|
||||
"user_data": {
|
||||
"param1": "test1",
|
||||
"param2": ["test1", "test2"]
|
||||
@@ -68,6 +103,10 @@ Notifications for finalized files are optional, if notification port number is o
|
||||
Jungfraujoch aims to generate files compliant with NXmx format, as well as make them as close as possible to files
|
||||
written by DECTRIS Filewriter. This ensures the file compatibility of Neggia and Durin XDS plugins, as well as Albula viewer.
|
||||
|
||||
If spot finding is enabled, spots are written in the [CXI format](https://raw.githubusercontent.com/cxidb/CXI/master/cxi_file_format.pdf) and is recoginzed by CrystFEL.
|
||||
If spot finding is enabled, spots are written in the [CXI format](https://raw.githubusercontent.com/cxidb/CXI/master/cxi_file_format.pdf) and are recognized by CrystFEL. The following ahs to be added to the geometry file:
|
||||
```
|
||||
peak_list = /opt/MX
|
||||
peak_list_type = cxi
|
||||
```
|
||||
|
||||
There are custom extension to NXmx format. These will be documented in the future.
|
||||
Reference in New Issue
Block a user