Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
73541d6
Basic libasdf detection at cmake-time and minimal stub for AsdfFile t…
embray Aug 14, 2025
72f0d1b
fix file attribution
embray Aug 14, 2025
6fb744d
First prototype of ImageFileReader interface
embray Sep 1, 2025
fe4a03e
Add a WCS::identity static constructor
embray Sep 1, 2025
8641432
Refactor DetectionImageConfig to use ImageFileReader...
embray Sep 1, 2025
77cc386
Fix: make FitsReader iteration more sane--it can iterate over all image
embray Sep 2, 2025
d30f61f
TODO: Fix the redundant code mentioned in this comment later
embray Sep 3, 2025
01f8cb0
Add some new capabilities to AsdfFile to allow getting some primitive
embray Sep 5, 2025
e836139
Implement AsdfFile::Ndarray::fileImageTile with asdf_ndarray_read_til…
embray Sep 10, 2025
2d37d2e
complete implementation of the basic AsdfImageSource and AsdfReader
embray Sep 15, 2025
69420f4
support specifying the desired ImageType for the ImageSources returned
embray Sep 15, 2025
5590487
Support image type conversion in AsdfImageSource, building on top of
embray Oct 3, 2025
d0fe03f
Support reading ExternalFlagConfig from ASDF files
embray Oct 3, 2025
9ca3005
Basic support for reading ASDF images in MeasurementImageConfig
embray Oct 3, 2025
aabcf19
Basic support for reading ASDF files in WeightImageConfig
embray Oct 3, 2025
d500ccc
Replace the ASDF test files with ones that contain the same data as the
embray Oct 3, 2025
813d64b
Add tests for AsdfImageSource approximately mirroring the applicable
embray Oct 3, 2025
88ac0ee
Replace the FitsReader::readFile convenience method with
embray Oct 6, 2025
cad4a1f
Add tests for AsdfReader mirroring FitsReader_test
embray Oct 6, 2025
94e1329
refactor: allow WCS() to be instantiated from any ImageSource (in
embray Oct 27, 2025
52cda33
Finish implementing reading of WCS data from the ASDF file (where
embray Oct 28, 2025
8f6ee10
fix: clean up this bit of cargo cult; in this case nwcs = 1 always
embray Oct 28, 2025
01bb894
add ability to get the path of an ndarray (and later an AsdfImageSource)
embray Oct 28, 2025
5b449a6
add extra options, when compiled with ASDF support, for setting the WCS
embray Oct 31, 2025
cecb22f
Implement parsing of ASDF WCS path mappings and add tests
embray Oct 31, 2025
e4077f4
build: ensure libasdf was compiled with GWCS support
embray Oct 31, 2025
67ef443
fix: a handful of memory leaks in the ASDF code
embray Nov 3, 2025
96bb8d9
fix: improve handling of the wcsprm pointer and fix some minor memory
embray Nov 3, 2025
ab4ab83
delete stale TODO comment--this is now done
embray Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions SEFramework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,25 @@ elements_depends_on_subdirs(ModelFitting)
find_package(GMock)
find_package(CCfits)
find_package(BoostDLL)
# TODO: For ASDF support we also need optional GWCS support to work with WCS
# from ASDF files; this needs to be checked as well
find_package(ASDF)
find_package(FFTW COMPONENTS single double)
find_package(WCSLIB REQUIRED)


#===============================================================================
# ASDF support is optional--don't compile it if libasdf was not found
#===============================================================================
if(NOT ASDF_FOUND)
message("libasdf not found, compiling without ASDF support")
else()
file(GLOB ASDF_SRC src/lib/ASDF/*.cpp)
add_definitions(-DWITH_ASDF)
endif()



#===============================================================================
# Declare the library dependencies here
# Example:
Expand All @@ -57,10 +72,12 @@ elements_add_library(SEFramework
src/lib/FITS/*.cpp
src/lib/Frame/*.cpp
src/lib/CoordinateSystem/*.cpp
${ASDF_SRC}
LINK_LIBRARIES
ElementsKernel SEUtils Table Configuration MathUtils FilePool
WCSLIB ${CCFITS_LIBRARIES} ${FFTW_LIBRARIES}
INCLUDE_DIRS SEUtils ${CCFITS_INCLUDE_DIRS} ${BoostDLL_INCLUDE_DIRS} ${FFTW_INCLUDE_DIRS}
WCSLIB ${CCFITS_LIBRARIES} ${FFTW_LIBRARIES} ${ASDF_LIBRARIES}
INCLUDE_DIRS SEUtils ${CCFITS_INCLUDE_DIRS} ${BoostDLL_INCLUDE_DIRS}
${FFTW_INCLUDE_DIRS} ${ASDF_INCLUDE_DIRS}
PUBLIC_HEADERS SEFramework)

#===============================================================================
Expand Down Expand Up @@ -187,6 +204,18 @@ elements_add_unit_test(TemporaryFitsSource_test tests/src/FITS/TemporaryFitsSour
elements_add_unit_test(WCS_test tests/src/CoordinateSystem/WCS_test.cpp
LINK_LIBRARIES SEFramework
TYPE Boost)

if(ASDF_FOUND)
elements_add_unit_test(AsdfFile_test tests/src/ASDF/AsdfFile_test.cpp
LINK_LIBRARIES SEFramework
TYPE Boost)
elements_add_unit_test(AsdfImageSource_test tests/src/ASDF/AsdfImageSource_test.cpp
LINK_LIBRARIES SEFramework
TYPE Boost)
elements_add_unit_test(AsdfReader_test tests/src/ASDF/AsdfReader_test.cpp
LINK_LIBRARIES SEFramework
TYPE Boost)
endif()
#===============================================================================
# Declare the Python programs here
# Examples :
Expand Down
229 changes: 229 additions & 0 deletions SEFramework/SEFramework/ASDF/AsdfFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/** Copyright © 2019 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3.0 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

/*
* AsdfFile.h
*
* Created on: Aug 14, 2025
* Author: embray
*/

#ifndef _SEFRAMEWORK_ASDF_ASDFFILE_H_
#define _SEFRAMEWORK_ASDF_ASDFFILE_H_

#include <cstdint>

#include <boost/filesystem/path.hpp>
#include <asdf.h>
#include <asdf/gwcs/gwcs.h>

#include <ElementsKernel/Exception.h>

#include "SEFramework/Image/ImageSourceWithMetadata.h"
#include "SEFramework/Image/ImageTile.h"

namespace SourceXtractor {

/**
* @class AsdfFile
* @brief represents access to a whole ASDF file
*
*/
class AsdfFile {
public:
/**
* Exception thrown when trying to access a value that does not exist in the ASDF tree
*/
class AsdfValueNotFoundException : public Elements::Exception {};

/**
* Exception thrown when trying to access a value of the wrong type from the ASDF tree
*/
class AsdfValueTypeMismatchException : public Elements::Exception {};

/**
* Exception thrown when an ASDF ndarray contains a datatype not (currently) supported by
* SE++
*/
class AsdfUnsupportedDatatypeException: public Elements::Exception {};

AsdfFile(const boost::filesystem::path& path, bool writeable);

AsdfFile(const boost::filesystem::path& path) : AsdfFile(path, false) {}

AsdfFile(AsdfFile&&) = default;

virtual ~AsdfFile();

asdf_file_t* getAsdfFilePtr() const { return m_asdf_ptr.get(); }

/**
* Wrapper around asdf_ndarray_t
*/
class Ndarray {
public:
friend class AsdfFile;

~Ndarray() {
asdf_ndarray_destroy(m_ndarray_ptr);
}

uint32_t ndim() const { return m_ndarray_ptr->ndim; }

std::vector<uint64_t>& shape() const {
if (!m_shape) {
m_shape = std::make_unique<std::vector<uint64_t>>(
m_ndarray_ptr->shape,
m_ndarray_ptr->shape + m_ndarray_ptr->ndim
);
}
return *m_shape;
}

ImageTile::ImageType getImageType() const { return m_image_type; }

/**
* Convenience method to test whether the ndarray can be read as an image by SE++
*
* That is, it has ndim == 2 || ndim == 3 (for data cubes) and a supported datatype.
*/
bool isSupportedImage() const;

/**
* Given a shared pointer to an already allocated ImageTile (i.e. from ImageTile::create)
* copy a tile from the raw ndarray data into the ImageTile.
*/
void fillImageTile(const std::shared_ptr<ImageTile> image_tile, int layer);

const std::string& getPath() const { return m_path; }

private:
/**
* Private constructor for creating the `Ndarray` wrapper from a raw asdf_value_t *
*/
explicit Ndarray(const AsdfFile& file, asdf_value_t *ptr);
/**
* Private constructor for creating the `Ndarray` wrapper from a raw asdf_ndarray_t *
*/
explicit Ndarray(asdf_ndarray_t *ptr)
: m_ndarray_ptr(ptr) {}

asdf_ndarray_t* m_ndarray_ptr;
// Store the JSON Path to the ndarray
std::string m_path;
ImageTile::ImageType m_image_type;
mutable std::unique_ptr<std::vector<uint64_t>> m_shape;
};

/**
* Wrapper around asdf_gwcs_fits_t, which represents a FITS- (WCSLIB)
* compatible WCS for use with SourceXtractor::WCS
*/
class FitsWCS {
public:
friend class AsdfFile;

~FitsWCS() {
asdf_gwcs_destroy(m_gwcs_ptr);
m_gwcs_fits_ptr = nullptr;
m_gwcs_ptr = nullptr;
}

std::array<double, 2> crpix() const noexcept {
return {m_gwcs_fits_ptr->crpix[0], m_gwcs_fits_ptr->crpix[1]};
}

std::array<double, 2> crval() const noexcept {
return {m_gwcs_fits_ptr->crval[0], m_gwcs_fits_ptr->crval[1]};
}

std::array<double, 2> cdelt() const noexcept {
return {m_gwcs_fits_ptr->cdelt[0], m_gwcs_fits_ptr->cdelt[1]};
}

std::array<std::array<double, 2>, 2> pc() const noexcept {
return {{
{m_gwcs_fits_ptr->pc[0][0], m_gwcs_fits_ptr->pc[0][1]},
{m_gwcs_fits_ptr->pc[1][0], m_gwcs_fits_ptr->pc[1][1]}
}};
}

std::array<std::string_view, 2> ctype() const noexcept {
return {{
m_gwcs_fits_ptr->ctype[0] ? std::string_view(m_gwcs_fits_ptr->ctype[0]) : std::string_view(),
m_gwcs_fits_ptr->ctype[1] ? std::string_view(m_gwcs_fits_ptr->ctype[1]) : std::string_view()
}};
}

private:
/**
* Private constructor for creating the `Ndarray` wrapper from a raw asdf_value_t *
*/
explicit FitsWCS(const AsdfFile& file, asdf_value_t *ptr);

asdf_gwcs_t *m_gwcs_ptr;
asdf_gwcs_fits_t* m_gwcs_fits_ptr;
};

/**
* Return the N-th ndarray from the top-level of the ASDF tree iterating the top-level
* keys in order.
*/
std::unique_ptr<Ndarray> getNdarray(int index);

/**
* Return any ndarray from any path in the ASDF tree
*
* If the given path does not exist an AsdfValueNotFoundException is thrown.
* If the given path exists but is not an ndarray, an AsdfValueTypeMismatchException is
* thrown.
*/
std::unique_ptr<Ndarray> getNdarray(const std::string& path);

/* TODO: More general methods for reading metadata from the ASDF tree; for the first version
* not needed though. */
/**
* Return FITS-compatible WCS metadata (wrapped in `AsdfFile::FitsWCS`)
*
* If called without any arguments it will look for the first applicable GWCS
* object in the ASDF metadata tree. Called with a path argument it will
* look for one specifically at that path. In either case if no matching
* GWCS is found will return a null-ish pointer.
*/
std::unique_ptr<FitsWCS> getFitsWCS();
std::unique_ptr<FitsWCS> getFitsWCS(const std::string& path);

private:
struct AsdfValueDestroy {
void operator()(asdf_value_t *v) const noexcept {
asdf_value_destroy(v);
}
};

using AsdfValuePtr = std::unique_ptr<asdf_value_t, AsdfValueDestroy>;

AsdfValuePtr getValue(const std::string& path);

boost::filesystem::path m_path;
std::unique_ptr<asdf_file_t, void (*)(asdf_file_t*)> m_asdf_ptr;

void open();
};

} // namespace SourceXtractor

#endif /* _SEFRAMEWORK_ASDF_ASDFFILE_H_ */
Loading