diff --git a/common/JFJochMessages.h b/common/JFJochMessages.h index de830204..585e85dc 100644 --- a/common/JFJochMessages.h +++ b/common/JFJochMessages.h @@ -259,6 +259,7 @@ struct StartMessage { std::vector az_int_bin_to_phi; std::optional az_int_q_bin_count; std::optional az_int_phi_bin_count; + std::vector az_int_map; std::map> pixel_mask; diff --git a/docs/CBOR.md b/docs/CBOR.md index d9b0a1b1..7731bc35 100644 --- a/docs/CBOR.md +++ b/docs/CBOR.md @@ -74,6 +74,7 @@ There are minor differences at the moment: | az_int_bin_to_q | Array(float) | Q value for each azimuthal integration bin \[angstrom^-1\] | | | az_int_bin_to_two_theta | Array(float) | Two theta angle value for each azimuthal integration bin \[deg\] | | | az_int_bin_to_phi | Array(float) | Phi value for each azimuthal integration bin \[deg\] | | +| az_int_map | Image | Mapping between pixel and bin number | | | summation | uint64 | Factor of frame summation | | | user_data | string | JSON serialized to string that can contain the following fields (all fields are optional): | X | | - file_prefix | string | File prefix | | @@ -157,6 +158,8 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s | spot_count_indexed | uint64 | Number of spots which fit indexing solution within a given tolerance | | | | az_int_profile | Array(float) | Azimuthal integration results, use az_int_bin_to_q from start message for legend | | | | | | NaN is used for empty bins and has to be taken care by the receiver | | | +| az_int_std | Array(float) | Standard deviation for azimuthal integration. (NaN for less than 2 samples) | | | +| az_int_count | Array(uint64) | Number of pixels contributing to azimuthal bin | | | | indexing_result | bool | Indexing successful | | | | indexing_lattice | Array(9 * float) | Indexing result real lattice; present only if indexed | | X | | indexing_unit_cell | object | Indexing result unit cell: a, b, c \[angstrom\] and alpha, beta, gamma \[degree\]; present only if indexed | | X | diff --git a/frame_serialize/CBORStream2Deserializer.cpp b/frame_serialize/CBORStream2Deserializer.cpp index 0b8e0a57..645059f2 100644 --- a/frame_serialize/CBORStream2Deserializer.cpp +++ b/frame_serialize/CBORStream2Deserializer.cpp @@ -1024,6 +1024,14 @@ namespace { cborErr(cbor_value_leave_container(&value, &map_value)); } + void ProcessAzintMapElement(StartMessage &message, CborValue &value) { + CompressedImage image = GetCBORMultidimTypedArray(value); + if (image.GetMode() == CompressedImageMode::Uint16) + JFJochDecompress(message.az_int_map, image.GetCompressionAlgorithm(), + image.GetCompressed(), image.GetCompressedSize(), + image.GetWidth() * image.GetHeight()); + } + std::optional ProcessHDF5Format(int input) { auto tmp = static_cast(input); switch (tmp) { @@ -1185,6 +1193,8 @@ namespace { message.run_number = GetCBORUInt(value); else if (key == "pixel_mask") ProcessPixelMaskElement(message, value); + else if (key == "az_int_map") + ProcessAzintMapElement(message, value); else if (key == "channels") GetCBORStringArray(value, message.channels); else if (key == "detector_translation") diff --git a/frame_serialize/CBORStream2Serializer.cpp b/frame_serialize/CBORStream2Serializer.cpp index 4966be47..1e255573 100644 --- a/frame_serialize/CBORStream2Serializer.cpp +++ b/frame_serialize/CBORStream2Serializer.cpp @@ -423,6 +423,23 @@ inline void CBOR_ENC_PIXEL_MASK(CborEncoder &encoder, const StartMessage &msg) { cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); } +inline void CBOR_ENC_AZINT_MAP(CborEncoder &encoder, const StartMessage &msg) { + if (msg.az_int_map.empty()) + return; + + if (msg.az_int_map.size() != msg.image_size_x * msg.image_size_y) + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, + "Mismatch in size of pixel mask"); + + JFJochBitShuffleCompressor compressor(CompressionAlgorithm::BSHUF_LZ4); + auto mask_compressed = compressor.Compress(msg.az_int_map); + CompressedImage image(mask_compressed.data(), mask_compressed.size(), + msg.image_size_x, msg.image_size_y, + CompressedImageMode::Uint16, + CompressionAlgorithm::BSHUF_LZ4, "az_int_map"); + CBOR_ENC_2D_TYPED_ARRAY(encoder, image); +} + inline void CBOR_ENC(CborEncoder &encoder, const char* key, const UnitCell &val) { CborEncoder mapEncoder; @@ -637,6 +654,7 @@ void CBORStream2Serializer::SerializeSequenceStart(const StartMessage& message) CBOR_ENC(mapEncoder, "geometry_transformation_enabled", message.geometry_transformation_enabled); CBOR_ENC_PIXEL_MASK(mapEncoder, message); + CBOR_ENC_AZINT_MAP(mapEncoder, message); CBOR_ENC(mapEncoder, "channels", message.channels); CBOR_ENC(mapEncoder, "max_spot_count", message.max_spot_count); diff --git a/tests/CBORTest.cpp b/tests/CBORTest.cpp index 8690f51a..da3ecd21 100644 --- a/tests/CBORTest.cpp +++ b/tests/CBORTest.cpp @@ -822,10 +822,38 @@ TEST_CASE("CBORSerialize_Image_Rad_Int_Profile", "[CBOR]") { else CHECK(message.az_int_profile_std[i] == Catch::Approx(image_array.az_int_profile_std[i])); } - REQUIRE(image_array.az_int_profile_std == message.az_int_profile_std); REQUIRE(image_array.az_int_profile_count == message.az_int_profile_count); } +TEST_CASE("CBORSerialize_Start_AzintMap", "[CBOR]") { + std::vector buffer(8 * 1024 * 1024); + CBORStream2Serializer serializer(buffer.data(), buffer.size()); + + std::vector test(512); + for (int i = 0; i < test.size(); i++) + test[i] = (i * 253 + 56) % 256; + + std::vector azint_map(32 * 15, 57); + azint_map[0] = 123; + azint_map[32*14] = UINT16_MAX; + + StartMessage msg{ + .image_size_x = 32, + .image_size_y = 15, + .az_int_map = azint_map + }; + + + REQUIRE_NOTHROW(serializer.SerializeSequenceStart(msg)); + + auto deserialized = CBORStream2Deserialize(buffer.data(), serializer.GetBufferSize()); + REQUIRE(deserialized); + REQUIRE(deserialized->msg_type == CBORImageType::START); + REQUIRE(deserialized->start_message); + CHECK(deserialized->start_message->az_int_map == azint_map); +} + + TEST_CASE("CBORSerialize_Image_Spots", "[CBOR]") { std::vector buffer(8 * 1024 * 1024); CBORStream2Serializer serializer(buffer.data(), buffer.size()); diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index 13c03e39..0a119323 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -724,6 +724,9 @@ void NXmx::AzimuthalIntegration(const StartMessage &start, const EndMessage &end if (x != "image") az_int_group.SaveVector(x, y, dim); } + + if (!start.az_int_map.empty() && start.az_int_map.size() == start.image_size_y * start.image_size_x) + az_int_group.SaveVector("map", start.az_int_map, {start.image_size_y, start.image_size_x}); } }