diff --git a/_pylibCZIrw/src/api/CZIwriteAPI.cpp b/_pylibCZIrw/src/api/CZIwriteAPI.cpp index 7459cc5..6018226 100644 --- a/_pylibCZIrw/src/api/CZIwriteAPI.cpp +++ b/_pylibCZIrw/src/api/CZIwriteAPI.cpp @@ -1,5 +1,6 @@ #include "CZIwriteAPI.h" #include "../pylibCZIrw_Config.h" +#include #include using namespace libCZI; @@ -58,8 +59,14 @@ bool CZIwriteAPI::AddTileEx(const std::string &coordinateString, } libCZI::CDimCoordinate coords; - bool conversion_to_cdim = - Utils::StringToDimCoordinate(coordinateString.c_str(), &coords); + (void)Utils::StringToDimCoordinate(coordinateString.c_str(), &coords); + + // Get per-channel pixel type + int cIndex = 0; + if (coords.TryGetPosition(libCZI::DimensionIndex::C, &cIndex)) { + channelPixelTypes_[cIndex] = plane->get_pixelType(); + } + auto sbmetadata = CZIwriteAPI::CreateSubBlockMetadataXml(retiling_id); CZIwriteAPI::AddSubBlock(coords, plane, actualCompressionOptions, this->spWriter_.get(), x, y, m, sbmetadata); @@ -166,6 +173,66 @@ void CZIwriteAPI::WriteMetadata( MetadataUtils::SetOrAddCustomKeyValuePair(mdBldr.get(), key, value); } + + // Infer ComponentBitCount + int imageBits = 0; + if (!channelPixelTypes_.empty()) { + bool allEqual = true; + int firstBits = BitsPerComponent(channelPixelTypes_.begin()->second); + int maxBits = firstBits; + for (const auto& kv : channelPixelTypes_) { + const int b = BitsPerComponent(kv.second); + if (b != firstBits) allEqual = false; + if (b > maxBits) maxBits = b; + } + imageBits = allEqual ? firstBits : maxBits; + } + if (imageBits > 0) { + const bool integerPixels = + (imageBits == 8 || imageBits == 16 || imageBits == 32) && + // treat float32 types specially: + (std::none_of(channelPixelTypes_.begin(), channelPixelTypes_.end(), + [](auto& kv){ return kv.second == libCZI::PixelType::Gray32Float + || kv.second == libCZI::PixelType::Bgr96Float; })); + mdBldr->GetRootNode() + ->GetOrCreateChildNode("Metadata/Information/Image/ComponentBitCount") + ->SetValue(imageBits); + if (integerPixels) { + const auto highVal = (imageBits >= 31) ? 0x7FFFFFFFu : ((1u << imageBits) - 1u); + mdBldr->GetRootNode() + ->GetOrCreateChildNode("Metadata/Information/Image/ComponentHighValue") + ->SetValue(highVal); + } + } + + // Per-channel bitcount + auto channelsNode = mdBldr->GetRootNode() + ->GetOrCreateChildNode("Metadata/Information/Image/Dimensions/Channels"); + + for (const auto& kv : channelPixelTypes_) { + const int c = kv.first; + const int bits = BitsPerComponent(kv.second); + if (bits <= 0) continue; + + // Find existing or create a new one + std::shared_ptr channelNode; + for (int i = 0;; ++i) { + auto child = channelsNode->GetChildNode("Channel", i); + if (!child) break; + auto idAttr = child->GetAttribute("Id"); + if (idAttr.has_value() && idAttr.value() == ("Channel:" + std::to_string(c))) { + channelNode = child; + break; + } + } + if (!channelNode) { + channelNode = channelsNode->CreateChildNode("Channel"); + channelNode->SetAttribute("Id", ("Channel:" + std::to_string(c)).c_str()); + } + + channelNode->GetOrCreateChildNode("ComponentBitCount")->SetValue(bits); + } + mdBldr->GetRootNode() ->GetOrCreateChildNode("Metadata/Information/Application/Name") ->SetValue("pylibCZIrw"); diff --git a/_pylibCZIrw/src/api/CZIwriteAPI.h b/_pylibCZIrw/src/api/CZIwriteAPI.h index f72dfe2..d99cdd0 100644 --- a/_pylibCZIrw/src/api/CZIwriteAPI.h +++ b/_pylibCZIrw/src/api/CZIwriteAPI.h @@ -3,6 +3,7 @@ #include "PImage.h" #include "inc_libCzi.h" #include +#include #include /// This enum specifies the "tinting-mode" - how the channel is false-colored. @@ -62,7 +63,26 @@ class CZIwriteAPI { spWriter_; ///< The pointer to the spWriter. libCZI::Utils::CompressionOption defaultCompressionOptions_; -public: + // Tracks the last-seen pixel type per C channel index during AddTile/AddTileEx + std::map channelPixelTypes_; + + static inline int BitsPerComponent(libCZI::PixelType pt) { + switch (pt) { + case libCZI::PixelType::Gray8: + case libCZI::PixelType::Bgr24: + return 8; + case libCZI::PixelType::Gray16: + case libCZI::PixelType::Bgr48: + return 16; + case libCZI::PixelType::Gray32Float: + case libCZI::PixelType::Bgr96Float: + return 32; + default: + return 0; + } + } + + public: /// Constructor which constructs a CZIwriteAPI object from the given wstring. /// Creates a spWriter for the czi document pointed by the given filepath. /// This constructor will use default options for compression, which is "no