diff --git a/RELEASE.md b/RELEASE.md index 74d799b1..638b8b3b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -4,6 +4,8 @@ ### API Changes: +- Exposed ``ClusterVector.empty()`` in the Python API. +- Added ClusterVector.estimate_n_clusters - Removed the lmfit dependency and the legacy ``fit_gaus``, ``fit_pol1``, ``fit_scurve``, and ``fit_scurve2`` APIs. Use ``Gaussian``, ``Pol1``, ``RisingScurve``, or ``FallingScurve`` and call ``model.fit(...)`` (or @@ -11,6 +13,8 @@ - Removed the legacy ``gaus``, ``pol1``, ``scurve``, and ``scurve2`` function evaluators. Model objects are callable and provide the replacement, for example ``Gaussian()(x, par)``. +- ``NDView`` now converts to ``NDView``; + ``expand4to8bit`` and ``expand24to32bit`` accept const input views. ### Bugfixes: - Fixed broken reading of old (pre reordering) Moench03 @@ -165,4 +169,3 @@ dhanya.thattil@psi.ch - diff --git a/include/aare/ClusterFile.hpp b/include/aare/ClusterFile.hpp index 885d4b45..4e416c23 100644 --- a/include/aare/ClusterFile.hpp +++ b/include/aare/ClusterFile.hpp @@ -144,6 +144,19 @@ class ClusterFile { */ size_t chunk_size() const { return m_chunk_size; } + /** + * @brief Estimate the number of clusters in the file from its size + * + * Frame headers are included in the estimate, so the result may be slightly + * larger than the actual number of clusters. The file position is not + * changed. + * + * @throws std::runtime_error if the file is not opened for reading + */ + size_t estimate_n_clusters() const { + return std::filesystem::file_size(m_filename) / sizeof(ClusterType); + } + /** * @brief Set the region of interest to use when reading * clusters. If set only clusters within the ROI will be diff --git a/include/aare/NDView.hpp b/include/aare/NDView.hpp index 8c2432d4..c507b41f 100644 --- a/include/aare/NDView.hpp +++ b/include/aare/NDView.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace aare { @@ -92,6 +93,12 @@ class NDView : public ArrayExpr, Ndim> { : buffer_(buffer), strides_(c_strides(shape)), shape_(shape), size_(num_elements(shape)) {} + template , int> = 0> + NDView(const NDView &other) noexcept + : buffer_(other.buffer_), strides_(other.strides_), + shape_(other.shape_), size_(other.size_) {} + template std::enable_if_t operator()(Ix... index) { return buffer_[element_offset(strides_, index...)]; @@ -216,6 +223,8 @@ class NDView : public ArrayExpr, Ndim> { } private: + template friend class NDView; + T *buffer_{nullptr}; std::array strides_{}; std::array shape_{}; @@ -263,4 +272,4 @@ template NDView make_view(std::vector &vec) { return NDView(vec.data(), {static_cast(vec.size())}); } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/decode.hpp b/include/aare/decode.hpp index c3397093..a6efd187 100644 --- a/include/aare/decode.hpp +++ b/include/aare/decode.hpp @@ -35,7 +35,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset = {}); * @param offset Offset within the first byte to where the data starts (0-7 * bits) */ -void expand24to32bit(NDView input, NDView output, +void expand24to32bit(NDView input, NDView output, BitOffset offset = {}); /** @@ -43,7 +43,7 @@ void expand24to32bit(NDView input, NDView output, * @param input input buffer with 4 bit values packed into 8 bit * @param output output buffer with 8 bit values */ -void expand4to8bit(NDView input, NDView output); +void expand4to8bit(NDView input, NDView output); /** * @brief Apply custom weights to a 16-bit input value. Will sum up diff --git a/python/src/bind_ClusterFile.hpp b/python/src/bind_ClusterFile.hpp index fbdd1909..0831d64f 100644 --- a/python/src/bind_ClusterFile.hpp +++ b/python/src/bind_ClusterFile.hpp @@ -47,6 +47,8 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }) .def("set_roi", &ClusterFile::set_roi, py::arg("roi")) .def("tell", &ClusterFile::tell) + .def("estimate_n_clusters", + &ClusterFile::estimate_n_clusters) .def( "set_noise_map", [](ClusterFile &self, py::array_t noise_map) { @@ -82,4 +84,4 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/src/bind_ClusterVector.hpp b/python/src/bind_ClusterVector.hpp index 254f96a9..a3d59f57 100644 --- a/python/src/bind_ClusterVector.hpp +++ b/python/src/bind_ClusterVector.hpp @@ -72,6 +72,7 @@ void define_ClusterVector(py::module &m, const std::string &typestr) { R"(calculates sum of 2x2 subcluster with highest energy and index relative to cluster center 0: top_left, 1: top_right, 2: bottom_left, 3: bottom_right )") .def_property_readonly("size", &ClusterVector::size) + .def("empty", &ClusterVector::empty) .def("item_size", &ClusterVector::item_size) .def_property_readonly("fmt", [typestr](ClusterVector &self) { @@ -169,4 +170,4 @@ void define_3x3_reduction(py::module &m) { py::arg("clustervector")); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/tests/test_ClusterFile.py b/python/tests/test_ClusterFile.py index 5e032009..3e2fb81d 100644 --- a/python/tests/test_ClusterFile.py +++ b/python/tests/test_ClusterFile.py @@ -14,6 +14,8 @@ from conftest import test_data_path def test_cluster_file(test_data_path): """Test ClusterFile""" f = ClusterFile(test_data_path / "clust/single_frame_97_clustrers.clust") + assert f.estimate_n_clusters() == 97 + assert f.tell() == 0 cv = f.read_clusters(10) #conversion does not work @@ -62,4 +64,4 @@ def test_read_clusters_and_fill_histogram(test_data_path): hist_py = pickle.load(f) #Compare the two histograms - assert hist_aare == hist_py \ No newline at end of file + assert hist_aare == hist_py diff --git a/python/tests/test_ClusterVector.py b/python/tests/test_ClusterVector.py index 0ae96c3e..30f934fa 100644 --- a/python/tests/test_ClusterVector.py +++ b/python/tests/test_ClusterVector.py @@ -16,6 +16,7 @@ def test_create_cluster_vector(): assert cv.cluster_size_x == 3 assert cv.cluster_size_y == 3 assert cv.size == 0 + assert cv.empty() def test_push_back_on_cluster_vector(): @@ -27,6 +28,7 @@ def test_push_back_on_cluster_vector(): cluster = _aare.Cluster2x2i(19, 22, np.ones(4, dtype=np.int32)) cv.push_back(cluster) assert cv.size == 1 + assert not cv.empty() arr = np.array(cv, copy=False) assert arr[0]['x'] == 19 @@ -127,4 +129,4 @@ def test_masking(): assert cv_masked_array[0]["x"] == 1 assert cv_masked_array[0]["y"] == 2 - assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all() \ No newline at end of file + assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all() diff --git a/src/ClusterFile.test.cpp b/src/ClusterFile.test.cpp index 78ba3e61..15caf6bb 100644 --- a/src/ClusterFile.test.cpp +++ b/src/ClusterFile.test.cpp @@ -17,6 +17,8 @@ TEST_CASE("Read one frame from a cluster file", "[.with-data]") { REQUIRE(std::filesystem::exists(fpath)); ClusterFile> f(fpath); + CHECK(f.estimate_n_clusters() == 97); + CHECK(f.tell() == 0); auto clusters = f.read_frame(); CHECK(clusters.size() == 97); CHECK(clusters.frame_number() == 135); @@ -250,6 +252,8 @@ TEST_CASE("Read cluster from multiple frame file", "[.with-data]") { SECTION("Read clusters from both frames") { ClusterFile f(fpath); + CHECK(f.estimate_n_clusters() == 8); + CHECK(f.tell() == 0); auto clusters = f.read_clusters(2); REQUIRE(clusters.size() == 2); REQUIRE(clusters.frame_number() == 0); diff --git a/src/NDView.test.cpp b/src/NDView.test.cpp index b89d0cd5..1a8c6a2b 100644 --- a/src/NDView.test.cpp +++ b/src/NDView.test.cpp @@ -5,12 +5,16 @@ #include #include #include +#include #include using aare::NDView; using aare::num_elements; using aare::Shape; +static_assert(std::is_convertible_v, NDView>); +static_assert(!std::is_convertible_v, NDView>); + TEST_CASE("Calculate size from a shape") { Shape<3> shape{2, 3, 4}; REQUIRE(num_elements(shape) == 24); @@ -88,6 +92,17 @@ TEST_CASE("Element reference 1D with a const NDView") { } } +TEST_CASE("Convert a mutable NDView to a const NDView") { + std::vector vec{1, 2, 3, 4}; + NDView mutable_view(vec.data(), Shape<2>{2, 2}); + + NDView const_view = mutable_view; + + REQUIRE(const_view.data() == mutable_view.data()); + REQUIRE(const_view.shape() == mutable_view.shape()); + REQUIRE(const_view.strides() == mutable_view.strides()); +} + TEST_CASE("Element reference 2D") { std::vector vec(12); std::iota(vec.begin(), vec.end(), 0); @@ -279,4 +294,4 @@ TEST_CASE("NDView over byte") { auto v = aare::make_view(buf); REQUIRE(v.shape()[0] == 5); REQUIRE(v[0] == std::byte{0}); -} \ No newline at end of file +} diff --git a/src/decode.cpp b/src/decode.cpp index 157fed25..afe261f9 100644 --- a/src/decode.cpp +++ b/src/decode.cpp @@ -144,7 +144,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset) { return (input >> offset.value()) & mask24bits; } -void expand4to8bit(NDView input, NDView output) { +void expand4to8bit(NDView input, NDView output) { if (2 * input.size() != output.size()) throw std::runtime_error( @@ -162,7 +162,7 @@ void expand4to8bit(NDView input, NDView output) { } } -void expand24to32bit(NDView input, NDView output, +void expand24to32bit(NDView input, NDView output, BitOffset bit_offset) { ssize_t bytes_per_channel = 3; // 24bit diff --git a/src/decode.test.cpp b/src/decode.test.cpp index d7f3901d..bab7c38b 100644 --- a/src/decode.test.cpp +++ b/src/decode.test.cpp @@ -154,6 +154,20 @@ TEST_CASE("Expand container with 24 bit data to 32") { } } +TEST_CASE("Expand 24 bit values to 32 bit values from a const buffer") { + const uint8_t buffer[] = { + 0x0F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + }; + + aare::NDView input(buffer, {9}); + aare::NDArray out({3}); + aare::expand24to32bit(input, out.view()); + + CHECK(out(0) == 0xF); + CHECK(out(1) == 0xFF); + CHECK(out(2) == 0xFFFFFF); +} + TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") { { uint8_t buffer[] = { @@ -172,4 +186,25 @@ TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") { CHECK(out(i) == expected_output[i]); } } -} \ No newline at end of file +} + +TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values from a const " + "buffer") { + { + const uint8_t buffer[] = { + 0x00, 0xF0, 0xFF, 0x00, 0xF0, 0xFF, + }; + + aare::NDView input(&buffer[0], {6}); + aare::NDArray out({12}); + aare::expand4to8bit(input, out.view()); + + uint8_t expected_output[] = { + 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, + 0x0, 0x0, 0x0, 0xF, 0xF, 0xF}; // assuming little endian + + for (size_t i = 0; i < 12; ++i) { + CHECK(out(i) == expected_output[i]); + } + } +}