Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STYLE: Factor out itkOMEZarrNGFFCommon.h #65

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ set(CMAKE_CXX_STANDARD 17)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

project(IOOMEZarrNGFF CXX C ASM_NASM ASM)

set(IOOMEZarrNGFF_LIBRARIES IOOMEZarrNGFF)

if(NOT ITK_SOURCE_DIR)
find_package(ITK 5.0 REQUIRED)
find_package(ITK 5.4 REQUIRED)
Comment on lines -15 to +14
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upgrade of ITK seems unrelated to/independent of the rest of the commit ("Factor out itkOMEZarrNGFFCommon.h"). If so, preferably make it a separate commit.

list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
include(ITKModuleExternal)
Expand Down
134 changes: 134 additions & 0 deletions include/itkOMEZarrNGFFCommon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

#ifndef itkOMEZarrNGFFCommon_h
#define itkOMEZarrNGFFCommon_h
#include "IOOMEZarrNGFFExport.h"

#include <string>

#include "itkIOCommon.h"

#include "tensorstore/container_kind.h"
#include "tensorstore/context.h"
#include "tensorstore/index_space/dim_expression.h"
#include "tensorstore/open.h"
#include "tensorstore/index_space/index_domain.h"
#include "tensorstore/index_space/index_domain_builder.h"
#include "tensorstore/index_space/dim_expression.h"

#include <nlohmann/json.hpp>

namespace itk
{
/** \class OMEZarrNGFFAxis
*
* \brief Represent an OME-Zarr NGFF axis
*
* Open Microscopy Environment Zarr Next Generation File Format
* specification can be found at https://github.com/ome/ngff
*
* \ingroup IOOMEZarrNGFF
*/
struct IOOMEZarrNGFF_EXPORT OMEZarrAxis
{
std::string name;
std::string type;
std::string unit;
};

// Update an existing "read" specification for an "http" driver to retrieve remote files.
// Note that an "http" driver specification may operate on an HTTP or HTTPS connection.
void
makeKVStoreHTTPDriverSpec(nlohmann::json & spec, const std::string & fullPath);

tensorstore::DataType
itkToTensorstoreComponentType(const IOComponentEnum itkComponentType);

// Returns TensorStore KvStore driver name appropriate for this path.
// Options are file, zip. TODO: http, gcs (GoogleCouldStorage), etc.
std::string
getKVstoreDriver(std::string path);

// JSON file path, e.g. "C:/Dev/ITKIOOMEZarrNGFF/v0.4/cyx.ome.zarr/.zgroup"
void
writeJson(nlohmann::json json, std::string path, std::string driver);

// JSON file path, e.g. "C:/Dev/ITKIOOMEZarrNGFF/v0.4/cyx.ome.zarr/.zattrs"
bool
jsonRead(const std::string path, nlohmann::json & result, std::string driver);

// We call tensorstoreToITKComponentType for each type.
// Hopefully compiler will optimize it away via constant propagation and inlining.
#define READ_ELEMENT_IF(typeName) \
else if (tensorstoreToITKComponentType(tensorstore::dtype_v<typeName>) == this->GetComponentType()) \
{ \
ReadFromStore<typeName>(store, storeIORegion, reinterpret_cast<typeName *>(buffer)); \
}

// We need to specify dtype for opening. As dtype is dependent on component type, this macro is long.
#define ELEMENT_WRITE(typeName) \
else if (tensorstoreToITKComponentType(tensorstore::dtype_v<typeName>) == this->GetComponentType()) \
{ \
if (sizeof(typeName) == 1) \
{ \
dtype = "|"; \
} \
if (std::numeric_limits<typeName>::is_integer) \
{ \
if (std::numeric_limits<typeName>::is_signed) \
{ \
dtype += 'i'; \
} \
else \
{ \
dtype += 'u'; \
} \
} \
else \
{ \
dtype += 'f'; \
} \
dtype += std::to_string(sizeof(typeName)); \
\
auto openFuture = tensorstore::Open( \
{ \
{ "driver", "zarr" }, \
{ "kvstore", { { "driver", driver }, { "path", this->m_FileName + "/" + path } } }, \
{ "metadata", \
{ \
{ "compressor", { { "id", "blosc" } } }, \
{ "dtype", dtype }, \
{ "shape", shape }, \
} }, \
}, \
tsContext, \
tensorstore::OpenMode::create | tensorstore::OpenMode::delete_existing, \
tensorstore::ReadWriteMode::read_write); \
TS_EVAL_CHECK(openFuture); \
\
auto writeStore = openFuture.value(); \
auto * p = reinterpret_cast<typeName const *>(buffer); \
auto arr = tensorstore::Array(p, shape, tensorstore::c_order); \
auto writeFuture = tensorstore::Write(tensorstore::UnownedToShared(arr), writeStore); \
TS_EVAL_CHECK(writeFuture); \
}

} // end namespace itk

#endif // itkOMEZarrNGFFCommon_h
133 changes: 133 additions & 0 deletions include/itkOMEZarrNGFFTransformIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

#ifndef itkOMEZarrNGFFImageIO_h
#define itkOMEZarrNGFFImageIO_h
#include "IOOMEZarrNGFFExport.h"


#include <fstream>
#include <string>
#include <vector>

#include "itkImageIOBase.h"

#include "itkOMEZarrNGFFCommon.h"

namespace itk
{

/** \class OMEZarrNGFFTransformIO
*
* \brief Read and write OME-Zarr coordinate transformations.
*
* Open Microscopy Environment Zarr Next Generation File Format
* specification can be found at https://github.com/ome/ngff
*
* \ingroup IOFilters
* \ingroup IOOMEZarrNGFF
*/
template <typename TParametersValueType>
class ITK_TEMPLATE_EXPORT OMEZarrNGFFTransformIOTemplate : public TransformIOBaseTemplate<TParametersValueType>
{
public:
/** Standard class typedefs. */
using Self = OMEZarrNGFFTransformIOTemplate;
using Superclass = TransformIOBaseTemplate<TParametersValueType>;
using Pointer = SmartPointer<Self>;
using typename Superclass::TransformListType;
using typename Superclass::TransformPointer;
using typename Superclass::TransformType;
using ParametersType = typename TransformType::ParametersType;
using ParametersValueType = typename TransformType::ParametersValueType;
using FixedParametersType = typename TransformType::FixedParametersType;
using FixedParametersValueType = typename TransformType::FixedParametersValueType;

using ConstTransformListType = typename TransformIOBaseTemplate<ParametersValueType>::ConstTransformListType;

/** Method for creation through the object factory. */
itkNewMacro(Self);

/** Run-time type information (and related methods). */
itkTypeMacro(OMEZarrNGFFTransformIOTemplate, TransformIOBaseTemplate);

static constexpr unsigned MaximumDimension = 5; // OME-NGFF specifies up to 5D data
static constexpr int INVALID_INDEX = -1; // for specifying enumerated axis slice indices
using AxesCollectionType = std::vector<OMEZarrAxis>;

/** Special in-memory zip interface. An address needs to be provided in
* the "file name", using pattern address.memory, where address is a
* decimal representation of BufferInfo's address.
* Sample filename: "12341234.memory". */
using BufferInfo = struct
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like it should be part of the common class for imageIO and transformIO.

{
char * pointer;
size_t size;
};

/** Construct a "magic" file name from the provided bufferInfo. */
static std::string
MakeMemoryFileName(const BufferInfo & bufferInfo)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also should be common for imageIO and transformIO.

{
size_t bufferInfoAddress = reinterpret_cast<size_t>(&bufferInfo);
return std::to_string(bufferInfoAddress) + ".memory";
}

/*-------- This part of the interfaces deals with reading data. ----- */

/** Reads the data from disk into the memory buffer provided. */
void
Read() override;

bool
CanReadFile(const char *) override;

/*-------- This part of the interfaces deals with writing data. ----- */

/** Determine the file type. Returns true if this TransformIO can write the
* file specified. */
bool
CanWriteFile(const char *) override;

void
Write() override;

protected:
OMEZarrNGFFTransformIOTemplate();
~OMEZarrNGFFTransformIOTemplate() override = default;

void
PrintSelf(std::ostream & os, Indent indent) const override;

/** Read a single array and set relevant metadata. */
void
ReadArrayMetadata(std::string path, std::string driver);

private:
// An empty zip file consists of 22 bytes of "end of central directory" record. More:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also common for imageIO and transformIO

// https://github.com/google/tensorstore/blob/45565464b9f9e2567144d780c3bef365ee3c125a/tensorstore/internal/compression/zip_details.h#L64-L76
constexpr static unsigned m_EmptyZipSize = 22;
char m_EmptyZip[m_EmptyZipSize] = "PK\x05\x06"; // the rest is filled with zeroes
const BufferInfo m_EmptyZipBufferInfo{ m_EmptyZip, m_EmptyZipSize };
const std::string m_EmptyZipFileName = MakeMemoryFileName(m_EmptyZipBufferInfo);

ITK_DISALLOW_COPY_AND_MOVE(OMEZarrNGFFTransformIOTemplate);
};
} // end namespace itk

#endif // itkOMEZarrNGFFImageIO_h
71 changes: 71 additions & 0 deletions include/itkOMEZarrNGFFTransformIOFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef itkOMEZarrNGFFTransformIOFactory_h
#define itkOMEZarrNGFFTransformIOFactory_h
#include "IOOMEZarrNGFFExport.h"

#include "itkObjectFactoryBase.h"
#include "itkTransformIOBase.h"

namespace itk
{
/** \class OMEZarrNGFFTransformIOFactory
* \brief Create instances of OMEZarrNGFFTransformIO objects using an object factory.
* \ingroup ITKIOOMEZarrNGFF
*/
class IOOMEZarrNGFF_EXPORT OMEZarrNGFFTransformIOFactory : public ObjectFactoryBase
{
public:
/** Standard class typedefs. */
using Self = OMEZarrNGFFTransformIOFactory;
using Superclass = ObjectFactoryBase;
using Pointer = SmartPointer<Self>;
using ConstPointer = SmartPointer<const Self>;

/** Class methods used to interface with the registered factories. */
const char *
GetITKSourceVersion() const override;

const char *
GetDescription() const override;

/** Method for class instantiation. */
itkFactorylessNewMacro(Self);

/** Run-time type information (and related methods). */
itkTypeMacro(OMEZarrNGFFTransformIOFactory, ObjectFactoryBase);

/** Register one factory of this type */
static void
RegisterOneFactory()
{
OMEZarrNGFFTransformIOFactory::Pointer zarrFactory = OMEZarrNGFFTransformIOFactory::New();

ObjectFactoryBase::RegisterFactoryInternal(zarrFactory);
}

protected:
OMEZarrNGFFTransformIOFactory();
~OMEZarrNGFFTransformIOFactory() override = default;

private:
ITK_DISALLOW_COPY_AND_MOVE(OMEZarrNGFFTransformIOFactory);
};
} // end namespace itk

#endif
2 changes: 2 additions & 0 deletions itk-module.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ file(READ "${MY_CURRENT_DIR}/README.md" DOCUMENTATION)
itk_module(IOOMEZarrNGFF
DEPENDS
ITKIOImageBase
ITKIOTransformBase
ITKIOHDF5
ITKZLIB
TEST_DEPENDS
ITKTestKernel
ITKMetaIO
FACTORY_NAMES
ImageIO::OMEZarrNGFF
TransformIO::OMEZarrNGFF
DESCRIPTION
"IO for images stored in zarr-backed OME-NGFF"
EXCLUDE_FROM_DEFAULT
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
set(IOOMEZarrNGFF_SRCS
itkOMEZarrNGFFCommon.cxx
itkOMEZarrNGFFImageIO.cxx
itkOMEZarrNGFFImageIOFactory.cxx
itkOMEZarrNGFFTransformIO.cxx
itkOMEZarrNGFFTransformIOFactory.cxx
)


Expand Down
Loading
Loading