Skip to content

Commit

Permalink
add check pyramid needed (ie: if czi doesnt already have a pyramid, a…
Browse files Browse the repository at this point in the history
…nd is greater than 4096*4096)
  • Loading branch information
CraigZeiss committed Dec 11, 2024
1 parent 04316fe commit 9208677
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
99 changes: 99 additions & 0 deletions _pylibCZIrw/src/api/CZIreadAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,102 @@ SubBlockCacheInfo CZIreadAPI::GetCacheInfo() {

return cacheInfo;
}

bool CZIreadAPI::NeedsPyramid(uint32_t max_extent_of_image) {
ThresholdParameters parameters{ max_extent_of_image };

const auto statistics = this->spReader->GetStatistics();

// First, check the overall bounding box
if (!CheckOverallBoundingBoxForNecessityOfPyramid(statistics, parameters)) {
return false;
}

// Check per-scene bounding boxes
const auto per_scene_result = CheckPerSceneBoundingBoxesForNecessityOfPyramid(statistics, parameters);
if (per_scene_result.value_or(true) == false) {
return false;
}

// Check if pyramid is already present
const auto pyramid_statistics = this->spReader->GetPyramidStatistics();

if (CheckIfPyramidIsPresent(statistics, pyramid_statistics, parameters)) {
return false;
}

return true;
}

bool CZIreadAPI::CheckOverallBoundingBoxForNecessityOfPyramid(const libCZI::SubBlockStatistics& statistics, const ThresholdParameters& threshold_parameters) {
if (IsRectangleAboveThreshold(statistics.boundingBoxLayer0Only, threshold_parameters)) {
return true;
}
return false;
}

std::optional<bool> CZIreadAPI::CheckPerSceneBoundingBoxesForNecessityOfPyramid(const libCZI::SubBlockStatistics& statistics, const ThresholdParameters& threshold_parameters) {
if (statistics.sceneBoundingBoxes.empty()) {
return std::nullopt;
}

for (const auto& sceneBoundingBox : statistics.sceneBoundingBoxes) {
if (IsRectangleAboveThreshold(sceneBoundingBox.second.boundingBoxLayer0, threshold_parameters)) {
return true;
}
}

return false;
}

bool CZIreadAPI::IsRectangleAboveThreshold(const libCZI::IntRect& rectangle, const ThresholdParameters& threshold_parameters) {
if (static_cast<uint32_t>(rectangle.w) > threshold_parameters.max_extent_of_image || static_cast<uint32_t>(rectangle.h) > threshold_parameters.max_extent_of_image) {
return true;
}
return false;
}

bool CZIreadAPI::CheckIfPyramidIsPresent(const libCZI::SubBlockStatistics& statistics, const libCZI::PyramidStatistics& pyramid_statistics, const ThresholdParameters& threshold_parameters) {
if (statistics.sceneBoundingBoxes.empty()) {
// No S-index used; check overall bounding box
if (CheckOverallBoundingBoxForNecessityOfPyramid(statistics, threshold_parameters)) {
const auto& pyramid_layer_statistics_iterator = pyramid_statistics.scenePyramidStatistics.find(std::numeric_limits<int>::max());
if (pyramid_layer_statistics_iterator == pyramid_statistics.scenePyramidStatistics.end()) {
// Unexpected; there should always be a pyramid-layer-0
return false;
}

if (!DoesContainPyramidLayer(pyramid_layer_statistics_iterator->second)) {
return false;
}
}
} else {
// Document contains scenes; check per-scene bounding boxes
if (CheckPerSceneBoundingBoxesForNecessityOfPyramid(statistics, threshold_parameters)) {
for (const auto& sceneBoundingBox : statistics.sceneBoundingBoxes) {
if (IsRectangleAboveThreshold(sceneBoundingBox.second.boundingBoxLayer0, threshold_parameters)) {
const auto& pyramid_layer_statistics_iterator = pyramid_statistics.scenePyramidStatistics.find(sceneBoundingBox.first);
if (pyramid_layer_statistics_iterator == pyramid_statistics.scenePyramidStatistics.end()) {
// Unexpected; there should always be a pyramid-layer-0
return false;
}

if (!DoesContainPyramidLayer(pyramid_layer_statistics_iterator->second)) {
return false;
}
}
}
}
}

return true;
}

bool CZIreadAPI::DoesContainPyramidLayer(const std::vector<libCZI::PyramidStatistics::PyramidLayerStatistics>& pyramid_layer_statistics) {
for (const auto& layer_statistics : pyramid_layer_statistics) {
if (!layer_statistics.layerInfo.IsLayer0() && layer_statistics.count > 0) {
return true;
}
}
return false;
}
18 changes: 18 additions & 0 deletions _pylibCZIrw/src/api/CZIreadAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ class CZIreadAPI {
SubBlockCacheOptions
subBlockCacheOptions; ///< Options for using the subblock cache

struct ThresholdParameters {
uint32_t max_extent_of_image; ///< Helper struct for threshold parameters
};

/// Helper methods for pyramid checking
bool CheckOverallBoundingBoxForNecessityOfPyramid(const libCZI::SubBlockStatistics& statistics, const ThresholdParameters& threshold_parameters);
std::optional<bool> CheckPerSceneBoundingBoxesForNecessityOfPyramid(const libCZI::SubBlockStatistics& statistics, const ThresholdParameters& threshold_parameters);
bool IsRectangleAboveThreshold(const libCZI::IntRect& rectangle, const ThresholdParameters& threshold_parameters);
bool CheckIfPyramidIsPresent(const libCZI::SubBlockStatistics& statistics, const libCZI::PyramidStatistics& pyramid_statistics, const ThresholdParameters& threshold_parameters);
bool DoesContainPyramidLayer(const std::vector<libCZI::PyramidStatistics::PyramidLayerStatistics>& pyramid_layer_statistics);

public:
/// Constructor which constructs a CZIrwAPI object from the given wstring.
/// Creates a spReader and spAccessor (SingleChannelTilingScalingAccessor) for
Expand Down Expand Up @@ -105,4 +116,11 @@ class CZIreadAPI {
/// <returns>A SubBlockCacheInfo struct containing the cache
/// information.</returns>
SubBlockCacheInfo GetCacheInfo();

/// <summary>
/// Determines whether the CZI document needs a pyramid representation based on the specified threshold.
/// </summary>
/// <param name="max_extent_of_image>The maximum extent (width or height) of the image before a pyramid is considered necessary</param>
/// <returns>True if a pyramid is needed, false otherwise</returns>
bool NeedsPyramid(uint32_t max_extent_of_image);
};
4 changes: 3 additions & 1 deletion _pylibCZIrw/src/bindings/CZIrw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ PYBIND11_MODULE(_pylibCZIrw, m) {
pixeltype, roi, bgColor, zoom, coordinateString, SceneIndexes);
return result;
})
.def("GetCacheInfo", &CZIreadAPI::GetCacheInfo);
.def("GetCacheInfo", &CZIreadAPI::GetCacheInfo)
.def("NeedsPyramid", &CZIreadAPI::NeedsPyramid, py::arg("max_extent_of_image"),
"Determines whether the CZI document needs a pyramid representation based on the specified threshold.");

py::class_<CZIwriteAPI>(m, "czi_writer", py::module_local())
.def(py::init<const std::wstring &, const std::string &>())
Expand Down
15 changes: 15 additions & 0 deletions pylibCZIrw/czi.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,21 @@ def close(self) -> None:
"""Close the document and finalize the reading"""
self._czi_reader.close()

def needs_pyramid(self, max_extent_of_image: int = 4096) -> bool:
"""Determine whether the CZI document requires a pyramid representation.
Parameters
----------
max_extent_of_image : int, optional
The maximum extent (width or height) before a pyramid is required. Defaults to 4096.
Returns
----------
: bool
True if a pyramid is needed, False otherwise.
"""
return self._czi_reader.NeedsPyramid(max_extent_of_image)

@staticmethod
def _compute_index_ranges(
rectangle: _pylibCZIrw.IntRect,
Expand Down

0 comments on commit 9208677

Please sign in to comment.