From c49438ce8b41000795723435674b32d9c986bc3d Mon Sep 17 00:00:00 2001 From: Vasu Jaganath Date: Mon, 22 Dec 2025 10:52:13 -0500 Subject: [PATCH 1/3] fix typos, setup.py and add build info & pyproject.toml --- README.md | 8 ++++---- ci-utils/install_prereq_linux.sh | 8 ++++---- pyproject.toml | 3 +++ setup.py | 4 ++-- src/python/argolid/pyramid_generator.py | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 pyproject.toml diff --git a/README.md b/README.md index 45e7d73..cbb116e 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,14 @@ Here is an example of building and installing `Argolid` in a Python virtual envi ``` python -m virtualenv venv source venv/bin/activate -pip install cmake -git clone https://github.com/sameeul/argolid.git +pip install cmake setuptools looseversion +git clone https://github.com/polusai/argolid.git cd argolid mkdir build_deps cd build_deps -sh ../ci_utils/install_prereq_linux.sh +sh ../ci-utils/install_prereq_linux.sh # only linux better use bash instead of sh cd ../ -export ARGOLID_DER_DIR=./build_deps/local_install +export ARGOLID_DEP_DIR=./build_deps/local_install python setup.py install ``` diff --git a/ci-utils/install_prereq_linux.sh b/ci-utils/install_prereq_linux.sh index a055d74..2bc3afb 100755 --- a/ci-utils/install_prereq_linux.sh +++ b/ci-utils/install_prereq_linux.sh @@ -38,11 +38,11 @@ if [[ "$OSTYPE" == "darwin"* ]]; then cd zlib-1.3.1 mkdir build_man cd build_man - cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_INSTALL_PREFIX=/usr/local .. - cmake --build . - cmake --build . --target install + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_INSTALL_PREFIX=/usr/local .. + cmake --build . + cmake --build . --target install cd ../../ - + curl -L https://github.com/libjpeg-turbo/libjpeg-turbo/archive/refs/tags/3.1.0.zip -o 3.1.0.zip unzip 3.1.0.zip cd libjpeg-turbo-3.1.0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2e73259 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel", "looseversion", "versioneer", "cmake"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/setup.py b/setup.py index 78fad08..b06beee 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,14 @@ import os import re import sys -import versioneer import platform import subprocess -from distutils.version import LooseVersion +from looseversion import LooseVersion from setuptools import setup, find_packages, Extension from setuptools.command.build_ext import build_ext +import versioneer class CMakeExtension(Extension): def __init__(self, name, sourcedir=""): diff --git a/src/python/argolid/pyramid_generator.py b/src/python/argolid/pyramid_generator.py index cd52e7f..04bde46 100644 --- a/src/python/argolid/pyramid_generator.py +++ b/src/python/argolid/pyramid_generator.py @@ -40,8 +40,8 @@ def __init__(self, log_level = None) -> None: def generate_from_single_image(self, input_file, output_dir, min_dim, vis_type, ds_dict = {}): channel_ds_dict = {} - for c, ds in ds_dict: - channel_ds_dict[c] = self.ds_types_dict[ds] + for c in ds_dict: + channel_ds_dict[c] = self.ds_types_dict[ds_dict[c]] self._pyr_generator.GenerateFromSingleFile(input_file, output_dir, min_dim, self.vis_types_dict[vis_type], channel_ds_dict) def generate_from_image_collection(self, collection_path, pattern , image_name, output_dir, min_dim, vis_type, ds_dict = {}): From acd548e10b65c3a8aa548c3bf310c58c19456d88 Mon Sep 17 00:00:00 2001 From: Vasu Jaganath Date: Fri, 26 Dec 2025 11:07:29 -0500 Subject: [PATCH 2/3] fix the max_level calculation in generatefromcollection --- .gitignore | 4 +++- README.md | 2 +- src/cpp/core/ome_tiff_to_chunked_pyramid.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 9b22bec..cd25085 100644 --- a/.gitignore +++ b/.gitignore @@ -567,4 +567,6 @@ scratch/ build/ build_man/ -venv/ \ No newline at end of file +venv/ +CmakeFiles +CMakeCache.txt \ No newline at end of file diff --git a/README.md b/README.md index cbb116e..bddf940 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ git clone https://github.com/polusai/argolid.git cd argolid mkdir build_deps cd build_deps -sh ../ci-utils/install_prereq_linux.sh # only linux better use bash instead of sh +sh ../ci-utils/install_prereq_linux.sh # on linux prefer bash over sh cd ../ export ARGOLID_DEP_DIR=./build_deps/local_install python setup.py install diff --git a/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp b/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp index 25eb99f..224b2d1 100644 --- a/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp +++ b/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp @@ -47,7 +47,7 @@ void OmeTiffToChunkedPyramid::GenerateFromCollection( int base_level_key = 0; PLOG_INFO << "Assembling base image..."; auto whole_image =_tiff_coll_to_chunk.Assemble(collection_path, stitch_vector_file, chunked_file_dir, std::to_string(base_level_key), v, _th_pool); - int max_level = static_cast(ceil(log2(std::max({whole_image._full_image_width, whole_image._full_image_width})))); + int max_level = static_cast(ceil(log2(std::max({whole_image._full_image_width, whole_image._full_image_height})))); int min_level = static_cast(ceil(log2(min_dim))); auto max_level_key = max_level-min_level+1+base_level_key; PLOG_INFO << "Generating image pyramids..."; From 9327dc6cdb2a81e7d0d589ce44e8315573609b87 Mon Sep 17 00:00:00 2001 From: Vasu Jaganath Date: Fri, 2 Jan 2026 22:06:51 +0000 Subject: [PATCH 3/3] warn when empty or incorrect stitched dimensions are found --- .gitignore | 3 +- src/cpp/core/chunked_pyramid_assembler.cpp | 46 ++++++++++++-------- src/cpp/core/ome_tiff_to_chunked_pyramid.cpp | 10 +++++ src/cpp/utilities/utilities.cpp | 10 ++--- src/python/argolid/pyramid_generator.py | 16 ++++++- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index cd25085..cce0baa 100644 --- a/.gitignore +++ b/.gitignore @@ -569,4 +569,5 @@ build/ build_man/ venv/ CmakeFiles -CMakeCache.txt \ No newline at end of file +CMakeCache.txt +.devcontainer/ \ No newline at end of file diff --git a/src/cpp/core/chunked_pyramid_assembler.cpp b/src/cpp/core/chunked_pyramid_assembler.cpp index 0c59f82..64f305d 100644 --- a/src/cpp/core/chunked_pyramid_assembler.cpp +++ b/src/cpp/core/chunked_pyramid_assembler.cpp @@ -66,7 +66,7 @@ ImageInfo OmeTiffCollToChunked::Assemble(const std::string& input_dir, int grid_x_max = 0, grid_y_max = 0, grid_c_max = 0; int grid_x_min = INT_MAX, grid_y_min = INT_MAX, grid_c_min = INT_MAX; std::vector image_vec; - ImageInfo whole_image; + ImageInfo whole_image {}; auto fp = std::make_unique (input_dir, pattern); auto files = fp->getFiles(); @@ -163,22 +163,34 @@ ImageInfo OmeTiffCollToChunked::Assemble(const std::string& input_dir, array).value(); tensorstore::IndexTransform<> transform = tensorstore::IdentityTransform(dest.domain()); - if(v == VisType::PCNG){ - transform = (std::move(transform) | tensorstore::Dims("z", "channel").IndexSlice({z, i._c_grid-grid_c_min}) - | tensorstore::Dims(y_dim).SizedInterval((i._y_grid-grid_y_min)*whole_image._chunk_size_y, image_height) - | tensorstore::Dims(x_dim).SizedInterval((i._x_grid-grid_x_min)*whole_image._chunk_size_x, image_width) - | tensorstore::Dims(x_dim, y_dim).Transpose({y_dim, x_dim})).value(); - - } else if (v == VisType::NG_Zarr){ - transform = (std::move(transform) | tensorstore::Dims(c_dim).SizedInterval(i._c_grid-grid_c_min, 1) - | tensorstore::Dims(z_dim).SizedInterval(z, 1) - | tensorstore::Dims(y_dim).SizedInterval((i._y_grid-grid_y_min)*whole_image._chunk_size_y, image_height) - | tensorstore::Dims(x_dim).SizedInterval((i._x_grid-grid_x_min)*whole_image._chunk_size_x, image_width)).value(); - } else if (v == VisType::Viv){ - transform = (std::move(transform) | tensorstore::Dims(c_dim).SizedInterval(i._c_grid-grid_c_min, 1) - | tensorstore::Dims(z_dim).SizedInterval(z, 1) - | tensorstore::Dims(y_dim).SizedInterval((i._y_grid-grid_y_min)*whole_image._chunk_size_y, image_height) - | tensorstore::Dims(x_dim).SizedInterval((i._x_grid-grid_x_min)*whole_image._chunk_size_x, image_width)).value(); + auto yy = (i._y_grid - grid_y_min) * whole_image._chunk_size_y; + auto xx = (i._x_grid - grid_x_min) * whole_image._chunk_size_x; + auto cc = (i._c_grid - grid_c_min); + + switch (v) { + case VisType::PCNG: + transform = + (std::move(transform) + | tensorstore::Dims("z", "channel").IndexSlice({z, cc}) + | tensorstore::Dims(y_dim).SizedInterval(yy, image_height) + | tensorstore::Dims(x_dim).SizedInterval(xx, image_width) + | tensorstore::Dims(x_dim, y_dim).Transpose({y_dim, x_dim})) + .value(); + break; + + case VisType::NG_Zarr: // same as Viv + case VisType::Viv: + transform = + (std::move(transform) + | tensorstore::Dims(c_dim).SizedInterval(cc, 1) + | tensorstore::Dims(z_dim).SizedInterval(z, 1) + | tensorstore::Dims(y_dim).SizedInterval(yy, image_height) + | tensorstore::Dims(x_dim).SizedInterval(xx, image_width)) + .value(); + break; + + default: + throw std::invalid_argument("Unsupported VisType"); } tensorstore::Write(array, dest | transform).value(); }); diff --git a/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp b/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp index 224b2d1..a2564cb 100644 --- a/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp +++ b/src/cpp/core/ome_tiff_to_chunked_pyramid.cpp @@ -1,5 +1,6 @@ #include "ome_tiff_to_chunked_pyramid.h" #include +#include namespace fs = std::filesystem; @@ -47,6 +48,15 @@ void OmeTiffToChunkedPyramid::GenerateFromCollection( int base_level_key = 0; PLOG_INFO << "Assembling base image..."; auto whole_image =_tiff_coll_to_chunk.Assemble(collection_path, stitch_vector_file, chunked_file_dir, std::to_string(base_level_key), v, _th_pool); + + if (whole_image._full_image_height <= 0 || + whole_image._full_image_width <= 0) { + PLOG_WARNING << "Assembled image height and width must be positive " + << "(height=" + std::to_string(whole_image._full_image_height) + ", width=" + std::to_string(whole_image._full_image_width) + ")\n" + << "No (tiff) images images found in the collection_path : " + collection_path + " please check the path" + << "\n"; + return; + } int max_level = static_cast(ceil(log2(std::max({whole_image._full_image_width, whole_image._full_image_height})))); int min_level = static_cast(ceil(log2(min_dim))); auto max_level_key = max_level-min_level+1+base_level_key; diff --git a/src/cpp/utilities/utilities.cpp b/src/cpp/utilities/utilities.cpp index 64f08ea..d2e0691 100644 --- a/src/cpp/utilities/utilities.cpp +++ b/src/cpp/utilities/utilities.cpp @@ -248,12 +248,12 @@ void WriteTSZattrFilePlateImage( } void WriteVivZattrFile(const std::string& tiff_file_name, const std::string& zattr_file_loc, int min_level, int max_level){ - json scale_metadata_list = json::array(); - for(int i=min_level; i<=max_level; ++i){ - json scale_metadata; - scale_metadata["path"] = std::to_string(i); - scale_metadata_list.push_back(scale_metadata); + + for (int i = min_level; i <= max_level; ++i) { + scale_metadata_list.emplace_back(json{ + {"path", std::to_string(i)} + }); } json combined_metadata; diff --git a/src/python/argolid/pyramid_generator.py b/src/python/argolid/pyramid_generator.py index 04bde46..17e42de 100644 --- a/src/python/argolid/pyramid_generator.py +++ b/src/python/argolid/pyramid_generator.py @@ -1,7 +1,18 @@ from pydantic import BaseModel, Field, field_validator from typing import Dict, Optional, List +from enum import IntEnum from .libargolid import OmeTiffToChunkedPyramidCPP, VisType, DSType, PyramidViewCPP +class LogLevel(IntEnum): + NONE = 0 + FATAL = 1 + ERROR = 2 + WARNING = 3 + INFO = 4 + DEBUG = 5 + VERBOSE = 6 + + class Downsample(BaseModel): channel_name: str method: str @@ -10,7 +21,7 @@ class Downsample(BaseModel): def check_method_config(cls, v): if v not in {"mean", "mode_max", "mode_min"}: raise ValueError(f'Value must be "mean", mode_max or "mode_min".') - return v + return v class PlateVisualizationMetadata(BaseModel): output_type: str @@ -32,10 +43,11 @@ def check_output_type_config(cls, v): return v class PyramidGenerartor: - def __init__(self, log_level = None) -> None: + def __init__(self, log_level = LogLevel.NONE) -> None: self._pyr_generator = OmeTiffToChunkedPyramidCPP() self.vis_types_dict ={ "NG_Zarr" : VisType.NG_Zarr, "PCNG" : VisType.PCNG, "Viv" : VisType.Viv} self.ds_types_dict = {"mean" : DSType.Mean, "mode_max" : DSType.Mode_Max, "mode_min" : DSType.Mode_Min} + self._pyr_generator.SetLogLevel(log_level) def generate_from_single_image(self, input_file, output_dir, min_dim, vis_type, ds_dict = {}):