Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
39b71cc
Switch submodule libs/libCZIrw to https://github.com/ptahmose/libczi-…
ptahmose Sep 17, 2025
f4a9ca8
Merge branch 'main' of github.com:ptahmose/zeiss-pylibczirw
ptahmose Sep 17, 2025
6ef2fb1
Add atomic lock management to CMemBitmapWrapper
ptahmose Sep 17, 2025
8ea26bd
Add ReaderOptions to CZI reader initialization
ptahmose Sep 18, 2025
d79f6c4
Switch submodule libs/libCZIrw to https://github.com/ptahmose/libczi-…
ptahmose Sep 18, 2025
5105b72
add support for "reader-options"
ptahmose Sep 19, 2025
a1fd846
add to "API.md"
ptahmose Sep 19, 2025
a2ebd51
linter
ptahmose Sep 19, 2025
09c757c
fix syntax error
ptahmose Sep 19, 2025
a0dd704
linter
ptahmose Sep 19, 2025
18b0d4c
linter
ptahmose Sep 19, 2025
32692e7
linter
ptahmose Sep 19, 2025
f879748
linter
ptahmose Sep 19, 2025
5af71fe
linter
ptahmose Sep 19, 2025
65c8cda
linter
ptahmose Sep 19, 2025
e8a7e3e
lint
ptahmose Sep 19, 2025
fef6e44
linter
ptahmose Sep 19, 2025
df8f7b1
linter
ptahmose Sep 19, 2025
fc0641d
remove settings.json
ptahmose Sep 19, 2025
cc6a3fe
linter
ptahmose Sep 19, 2025
5f7de91
linter
ptahmose Sep 19, 2025
0c103fc
linter
ptahmose Sep 19, 2025
09604ca
linter
ptahmose Sep 19, 2025
c70950d
linter
ptahmose Sep 19, 2025
073071d
linter
ptahmose Sep 19, 2025
916153e
Improve comments in CZIreadAPI.h for clarity
ptahmose Sep 19, 2025
f5585b7
linter
ptahmose Sep 19, 2025
467c24b
linter
ptahmose Sep 19, 2025
11ccaa7
linter
ptahmose Sep 19, 2025
d9bc647
linter
ptahmose Sep 19, 2025
dc3f6a8
linter
ptahmose Sep 19, 2025
f16919b
fix
ptahmose Sep 19, 2025
aabd658
review
ptahmose Sep 19, 2025
3c2af79
review
ptahmose Sep 19, 2025
401bde9
refactor: improve docstrings for ReaderOptions and open_czi function
ptahmose Sep 19, 2025
e066478
fix: remove unnecessary whitespace in ReaderOptions class
ptahmose Sep 19, 2025
461126d
fix: correct formatting in open_czi function docstring
ptahmose Sep 19, 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
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[submodule "libs/libCZIrw"]
path = libs/libCZIrw
url = https://github.com/ZEISS/libczi
url = https://github.com/ZEISS/libczi.git
branch = main
[submodule "libs/pybind11"]
path = libs/pybind11
url = https://github.com/pybind/pybind11
22 changes: 22 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ with czi.open_czi(file_path, cache_options=cache_options) as czi:
...
```

### Specifying additional reader-options
The `open_czi` method accepts a `ReaderOptions` structure where additional configurations
can be given controlling the operations. There are two options currently available:
- `enable_mask_awareness` - whether the tile-composition uses mask-information. This is by default `false`.
- `enable_visibility_check_optimization` - in the tile-composition, do a visibility-check before reading sub-blocks, potentially reducing the amount of data that must be loaded. This is by default `true`.

The `ReaderOptions` can be passed to `open_czi` like in this example:

```python
cache_options = CacheOptions(
type = CacheType.Standard,
max_memory_usge = 500 * 1024**2 # 500 Megabytes
max_sub_block_count = 100,
)
reader_options = ReaderOptions(
enable_mask_awareness = True
)
with czi.open_czi(file_path, cache_options=cache_options, reader_options=reader_options) as czi:
...
```


## Reading a CZI

The following calls all relate to reading information from the CZI. And, whenever they're called, the file's last write date will be evaluated and cached. **If the file was changed while opened, all file caches will be invalidated.**
Expand Down
3 changes: 2 additions & 1 deletion _pylibCZIrw/src/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ add_library(
site.cpp
StaticContext.cpp
StaticContext.h
SubBlockCache.h)
SubBlockCache.h
ReaderOptions.h)

target_include_directories(_pylibCZIrw_API PRIVATE ${libCZI_SOURCE_DIR})
target_link_libraries(_pylibCZIrw_API INTERFACE libCZIStatic JxrDecodeStatic)
Expand Down
15 changes: 13 additions & 2 deletions _pylibCZIrw/src/api/CZIreadAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ CZIreadAPI::CZIreadAPI(const std::string &stream_class_name,

CZIreadAPI::CZIreadAPI(const std::string &stream_class_name,
const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions) {
const SubBlockCacheOptions &subBlockCacheOptions)
: CZIreadAPI(stream_class_name, fileName, subBlockCacheOptions,
ReaderOptions()) {}

CZIreadAPI::CZIreadAPI(const std::string &stream_class_name,
const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions,
const ReaderOptions &readerOptions) {
shared_ptr<IStream> stream;
if (stream_class_name.empty() || stream_class_name == "standard") {
stream = StreamsFactory::CreateDefaultStreamForFile(fileName.c_str());
Expand Down Expand Up @@ -49,6 +56,7 @@ CZIreadAPI::CZIreadAPI(const std::string &stream_class_name,
this->spAccessor = reader->CreateSingleChannelScalingTileAccessor();
this->spReader = reader;
this->subBlockCacheOptions = subBlockCacheOptions;
this->readerOptions = readerOptions;
if (subBlockCacheOptions.cacheType == CacheType::Standard) {
this->spSubBlockCache = libCZI::CreateSubBlockCache();
} else if (subBlockCacheOptions.cacheType != CacheType::None) {
Expand Down Expand Up @@ -117,8 +125,11 @@ std::unique_ptr<PImage> CZIreadAPI::GetSingleChannelScalingTileAccessorData(

libCZI::ISingleChannelScalingTileAccessor::Options scstaOptions;
scstaOptions.Clear();
// set the flag "visibility check optimization" as configured by the user
scstaOptions.useVisibilityCheckOptimization =
true; // enable the "visibility check optimization"
this->readerOptions.enableVisibilityCheckOptimization;
// set the flag "mask awareness" as configured by the user
scstaOptions.maskAware = this->readerOptions.enableMaskAwareness;
scstaOptions.backGroundColor = bgColor;
if (this->spSubBlockCache) {
scstaOptions.subBlockCache = this->spSubBlockCache;
Expand Down
54 changes: 44 additions & 10 deletions _pylibCZIrw/src/api/CZIreadAPI.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#pragma once

#include "PImage.h"
#include "ReaderOptions.h"
#include "SubBlockCache.h"
#include "inc_libCzi.h"
#include <iostream>
#include <optional>

/// Class used to represent a CZI reader object in pylibCZIrw.
/// It gathers the libCZI features needed for reading in the pylibCZIrw project.
/// It gathers the libCZI features needed for reading in the pylibCZIrw
/// project.
/// CZIrwAPI will be exposed to python via pybind11 as a czi class.
class CZIreadAPI {

Expand All @@ -21,6 +23,9 @@ class CZIreadAPI {
///< null (in which case no caching is done)
SubBlockCacheOptions
subBlockCacheOptions; ///< Options for using the subblock cache
ReaderOptions
readerOptions; ///< Options for the reader, such as enabling mask
///< awareness and visibility check optimizations

public:
/// Constructor which constructs a CZIrwAPI object from the given wstring.
Expand All @@ -47,9 +52,10 @@ class CZIreadAPI {
/// Creates a spReader and spAccessor (SingleChannelTilingScalingAccessor) for
/// the czi document pointed by the given filepath.
/// This constructor allows defining a subblock cache to be used for
/// performance optimization. \param fileName Filename of the
/// file. \param subBlockCacheOptions Options for initializing the
/// subblock cache.
/// performance optimization.
/// \param fileName Filename of the file.
/// \param subBlockCacheOptions Options for initializing the
/// subblock cache.
CZIreadAPI(const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions);

Expand All @@ -61,16 +67,44 @@ class CZIreadAPI {
/// optimization.
///
/// \param stream_class_name A string identifying the stream class to
/// be used (note that this string is *not* the same string the libCZI-streams
/// factory uses. There is a mapping between
/// the two strings done in the CZIrwAPI
/// constructor.
/// be used (note that this string is *not*
/// the same string the libCZI-streams
/// factory uses. There is a mapping between
/// the two strings done in the CZIrwAPI
/// constructor.
/// \param fileName Filename (or URI) of the file (the
/// interpretation of the string is stream class specific). \param
/// subBlockCacheOptions Options for initializing the subblock cache.
/// interpretation of the string is stream
/// class specific).
/// \param subBlockCacheOptions Options for initializing the subblock
/// cache.
CZIreadAPI(const std::string &stream_class_name, const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions);

/// Constructor which constructs a CZIrwAPI object, allowing to specify a
/// stream class name. Possible stream class names are: "standard" for reading
/// files in the file system, and "curl" for reading files from a web server.
/// "curl" is mapped to the libCZI-streams class "curl_http_inputstream". This
/// constructor allows defining a subblock cache to be used for performance
/// optimization. In addition, is allows to specify reader options.
///
/// \param stream_class_name A string identifying the stream class to
/// be used (note that this string is *not*
/// the same string the libCZI-streams
/// factory uses. There is a mapping between
/// the two strings done in the CZIrwAPI
/// constructor.
/// \param fileName Filename (or URI) of the file (the
/// interpretation of the string is stream
/// class specific).
/// \param subBlockCacheOptions Options for initializing the
/// subblock cache.
/// \param readerOptions Options controlling the reader
/// operation, such as enabling mask
/// aware tile-composition.
CZIreadAPI(const std::string &stream_class_name, const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions,
const ReaderOptions &readerOptions);

/// Close the Opened czi document
void close() { this->spReader->Close(); }

Expand Down
18 changes: 18 additions & 0 deletions _pylibCZIrw/src/api/ReaderOptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include "inc_libCzi.h"

/// This POD ("plain-old-data") structure gathers open-time options for the
/// CZI reader. It is used to configure the reader's behavior, such as enabling
/// mask awareness and visibility check optimizations.
struct ReaderOptions {
/// whether to enable mask awareness
bool enableMaskAwareness = false;

/// whether to enable visibility check optimization
bool enableVisibilityCheckOptimization = true;

void Clear() {
this->enableMaskAwareness = false;
this->enableVisibilityCheckOptimization = true;
}
};
15 changes: 15 additions & 0 deletions _pylibCZIrw/src/bindings/CZIrw.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "../api/CZIreadAPI.h"
#include "../api/CZIwriteAPI.h"
#include "../api/PImage.h"
#include "../api/ReaderOptions.h"
#include "../api/SubBlockCache.h"
#include "../api/site.h"
#include "PbHelper.h"
Expand Down Expand Up @@ -33,6 +34,13 @@ PYBIND11_MODULE(_pylibCZIrw, m) {
return new CZIreadAPI(stream_class_name, fileName,
subBlockCacheOptions);
}))
.def(py::init([](const std::string &stream_class_name,
const std::wstring &fileName,
const SubBlockCacheOptions &subBlockCacheOptions,
const ReaderOptions &readerOptions) {
return new CZIreadAPI(stream_class_name, fileName, subBlockCacheOptions,
readerOptions);
}))
.def("close", &CZIreadAPI::close)
.def("GetXmlMetadata", &CZIreadAPI::GetXmlMetadata)
.def("GetSubBlockStats", &CZIreadAPI::GetSubBlockStats)
Expand Down Expand Up @@ -219,6 +227,13 @@ PYBIND11_MODULE(_pylibCZIrw, m) {
.def_readwrite("elements_count", &SubBlockCacheInfo::elementsCount)
.def_readwrite("memory_usage", &SubBlockCacheInfo::memoryUsage);

py::class_<ReaderOptions>(m, "ReaderOptions", py::module_local())
.def(py::init<>())
.def_readwrite("enableMaskAwareness", &ReaderOptions::enableMaskAwareness)
.def_readwrite("enableVisibilityCheckOptimization",
&ReaderOptions::enableVisibilityCheckOptimization)
.def("Clear", &ReaderOptions::Clear);

// perform one-time-initialization of libCZI
OneTimeSiteInitialization();
}
9 changes: 8 additions & 1 deletion _pylibCZIrw/src/bindings/PbHelper.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "../api/CZIreadAPI.h"
#include "include_python.h"
#include <atomic>
#include <pybind11/chrono.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h>
Expand All @@ -19,6 +20,7 @@ class CMemBitmapWrapper : public libCZI::IBitmapData {
std::uint32_t width;
std::uint32_t height;
std::uint32_t stride;
std::atomic<int> lock_count = ATOMIC_VAR_INIT(0);

public:
CMemBitmapWrapper(libCZI::PixelType pixeltype, std::uint32_t width,
Expand All @@ -42,10 +44,15 @@ class CMemBitmapWrapper : public libCZI::IBitmapData {
bitmapLockInfo.ptrDataRoi = this->ptrData;
bitmapLockInfo.stride = this->stride;
bitmapLockInfo.size = this->stride * static_cast<size_t>(this->height);
std::atomic_fetch_add(&this->lock_count, 1);
return bitmapLockInfo;
}

virtual void Unlock() {}
virtual void Unlock() { std::atomic_fetch_sub(&this->lock_count, 1); }

virtual int GetLockCount() const {
return std::atomic_load(&this->lock_count);
}
};

/// Returns format descriptor corresponding to each libCZI::PixelType.
Expand Down
4 changes: 4 additions & 0 deletions _pylibCZIrw/src/pylibCZIrw_Config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

// Make the project version available on the C++ side
#define PROJECT_VERSION ""
2 changes: 1 addition & 1 deletion libs/libCZIrw
Submodule libCZIrw updated 312 files
52 changes: 48 additions & 4 deletions pylibCZIrw/czi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Module implementing the czi interface

The open method will create a czi document.
This czi document can be use to read and write czi.
This czi document can be used to read and write czi.
"""

import contextlib
Expand Down Expand Up @@ -69,6 +69,19 @@ class CacheOptions:
max_sub_block_count: Optional[int] = None


@dataclass
class ReaderOptions:
"""Reader options data structure.

Configuration options for CZI reader.
"""

enable_mask_awareness: bool = False # whether the accessor will use the valid-pixel-mask for the
# tile-composition
enable_visibility_check_optimization: bool = True # whether the accessor will use the
# visibility-check-optimization for the tile-composition


@dataclass
class Rgb8Color:
"""Rgb8Color class.
Expand Down Expand Up @@ -151,6 +164,7 @@ def __init__(
filepath: str,
file_input_type: ReaderFileInputTypes = ReaderFileInputTypes.Standard,
cache_options: Optional[CacheOptions] = None,
reader_options: Optional[ReaderOptions] = None,
) -> None:
"""Creates a czi reader object, should only be called through the open_czi() function.

Expand All @@ -164,20 +178,22 @@ def __init__(
The configuration of a subblock cache to be used.
"""
libczi_cache_options = self._create_default_cache_options(cache_options=cache_options)
libczi_reader_options = self._create_reader_options(reader_options=reader_options)

if file_input_type is ReaderFileInputTypes.Curl:
if validators.url(filepath):
# When reading from CURL stream we assume that the connection is slow
# And therefore also cache uncompressed subblocks.
libczi_cache_options.cacheOnlyCompressed = False
self._czi_reader = _pylibCZIrw.czi_reader(
ReaderFileInputTypes.Curl.value, filepath, libczi_cache_options
ReaderFileInputTypes.Curl.value, filepath, libczi_cache_options, libczi_reader_options
)
else:
raise FileNotFoundError(f"{filepath} is not a valid URL.")
else:
# When reading from disk we only cache compressed subblocks.
libczi_cache_options.cacheOnlyCompressed = True
self._czi_reader = _pylibCZIrw.czi_reader(filepath, libczi_cache_options)
self._czi_reader = _pylibCZIrw.czi_reader("", filepath, libczi_cache_options, libczi_reader_options)
self._stats = self._czi_reader.GetSubBlockStats()

@classmethod
Expand All @@ -196,6 +212,29 @@ def close(self) -> None:
"""Close the document and finalize the reading"""
self._czi_reader.close()

@staticmethod
def _create_reader_options(reader_options: Optional[ReaderOptions]) -> _pylibCZIrw.ReaderOptions:
"""Creates a ReaderOptions object from the ReaderOptions dataclass.

Parameters
----------
reader_options : ReaderOptions
Reader options dataclass

Returns
-------
: _pylibCZIrw.ReaderOptions
Reader options object.
"""
libczi_reader_options = _pylibCZIrw.ReaderOptions()
libczi_reader_options.Clear()
if reader_options:
libczi_reader_options.enableMaskAwareness = reader_options.enable_mask_awareness
libczi_reader_options.enableVisibilityCheckOptimization = (
reader_options.enable_visibility_check_optimization
)
return libczi_reader_options

@staticmethod
def _compute_index_ranges(
rectangle: _pylibCZIrw.IntRect,
Expand Down Expand Up @@ -1270,6 +1309,8 @@ def open_czi(
filepath: str,
file_input_type: ReaderFileInputTypes = ReaderFileInputTypes.Standard,
cache_options: Optional[CacheOptions] = None,
*,
reader_options: Optional[ReaderOptions] = None,
) -> Generator:
"""Initialize a czi reader object and returns it.
Opens the filepath and hands it over to the low-level function.
Expand All @@ -1282,13 +1323,16 @@ def open_czi(
The type of file input, default is local file.
cache_options : CacheOptions, optional
The configuration of a subblock cache to be used. Per default no cache is used.
reader_options: ReaderOptions, optional
Additional configuration options for the reader. Note that there is a keyword-only argument
boundary before the reader_options argument.

Returns
----------
: czi
CziReader document as a czi object
"""
reader = CziReader(filepath, file_input_type, cache_options=cache_options)
reader = CziReader(filepath, file_input_type, cache_options=cache_options, reader_options=reader_options)
try:
yield reader
finally:
Expand Down