Closed MichaelDausmann closed 7 months ago
Hi, this is an interesting use-case, thanks for sharing. I downloaded the CZI-document you provided, and if I get this right, your goal is to remove the presence of "S-index" in the CZI. So, the document looks something like this and you want to get rid of the S-coordinate.
What I put together is this
#include "../libCZI/libCZI.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace libCZI;
static void RemoveSIndex(const std::shared_ptr<ICziReaderWriter>& reader_writer, int index, const SubBlockInfo& info)
{
AddSubBlockInfo add_sub_block_info;
add_sub_block_info.Clear();
add_sub_block_info.coordinate = info.coordinate;
add_sub_block_info.coordinate.Clear(DimensionIndex::S);
// we also set the m-index to invalid - we would have to re-create an m-index scheme when removing the S-index,
// because "m-index is scene-scoped". So, in order to have a well-formed CZI, we'd need to re-create the m-index.
// We don't do this here (yet), and I reckon it is better to have no m-index than an invalid one.
add_sub_block_info.mIndexValid = false;
add_sub_block_info.mIndex = numeric_limits<int>::max();
add_sub_block_info.x = info.logicalRect.x;
add_sub_block_info.y = info.logicalRect.y;
add_sub_block_info.logicalWidth = info.logicalRect.w;
add_sub_block_info.logicalHeight = info.logicalRect.h;
add_sub_block_info.physicalWidth = info.physicalSize.w;
add_sub_block_info.physicalHeight = info.physicalSize.h;
add_sub_block_info.PixelType = info.pixelType;
add_sub_block_info.pyramid_type = info.pyramidType;
// note: there is a bug in libCZI here (info.compressionModeRaw is not valid here, we work around
// this by using the compression mode of the sub-block which we have to read in any case)
add_sub_block_info.compressionModeRaw = info.compressionModeRaw;
auto subBlock = reader_writer->ReadSubBlock(index);
size_t sub_block_data_size;
auto sub_block_data = subBlock->GetRawData(ISubBlock::MemBlkType::Data, &sub_block_data_size);
size_t sub_block_metadata_size;
auto sub_block_metadata = subBlock->GetRawData(ISubBlock::MemBlkType::Metadata, &sub_block_metadata_size);
size_t sub_block_attachment_size;
auto sub_block_attachment = subBlock->GetRawData(ISubBlock::MemBlkType::Attachment, &sub_block_attachment_size);
// workaround for above bug in libCZI
add_sub_block_info.compressionModeRaw = subBlock->GetSubBlockInfo().compressionModeRaw;
add_sub_block_info.sizeData = sub_block_data_size;
add_sub_block_info.getData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate that offset is is less than sub_block_data_size (and - offset should never be non-zero)
ptr = static_cast<const uint8_t*>(sub_block_data.get()) + offset;
size = sub_block_data_size - offset;
return true;
};
add_sub_block_info.sizeMetadata = sub_block_metadata_size;
if (add_sub_block_info.sizeMetadata > 0)
{
add_sub_block_info.getMetaData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_metadata.get()) + offset;
size = sub_block_metadata_size - offset;
return true;
};
}
add_sub_block_info.sizeAttachment = sub_block_attachment_size;
if (add_sub_block_info.sizeAttachment > 0)
{
add_sub_block_info.getAttachment =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_attachment.get()) + offset;
size = sub_block_attachment_size - offset;
return true;
};
}
reader_writer->ReplaceSubBlock(index, add_sub_block_info);
}
int main()
{
std::cout << "Opening input/output stream" << std::endl;
const auto io_stream = libCZI::CreateInputOutputStreamForFile(LR"(D:\Data\CZI\libczi#94\test.czi)");
// create the reader-writer-object
auto reader_writer = libCZI::CreateCZIReaderWriter();
// open the CZI-file
reader_writer->Create(io_stream);
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
RemoveSIndex(reader_writer, index, info);
return true;
});
reader_writer->Close();
return 0;
}
which seems to do the right thing - the processed CZI now looks like this:
Please find a more complete test-application over here - branch "jbl/#94-remove-S-index".
Having said this - maybe it works for your use-case nevertheless. But be aware that well-formedness of the CZIs which come out here is questionable.
Let me know if this does works for you. And - point taken regarding the documentation, thanks.
Thanks so much @ptahmose for this example. Re: my goal. yes, nearly right, rather than clear the S dimension, I want to set every S dimension to 0, effectively merging the scenes together.
I have managed to reproduce and refine a little your initial attempt and verify that the resulting file can be successfully imported.
here is my second attempt... still very crude I think..
#include "../libCZI/libCZI.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace libCZI;
// I would call this instead 'MergeSIndex'
static void RemoveSIndex(const std::shared_ptr<ICziReaderWriter>& reader_writer, int index, const SubBlockInfo& info)
{
AddSubBlockInfo add_sub_block_info;
add_sub_block_info.Clear();
add_sub_block_info.coordinate = info.coordinate;
//add_sub_block_info.coordinate.Clear(DimensionIndex::S);
add_sub_block_info.coordinate.Set(DimensionIndex::S,0); //rather than clear the S index, it seems to work better if I 'merge' all the scenes into one with S0
// I have set the mIndex just using the enumeration index
add_sub_block_info.mIndexValid = true;
add_sub_block_info.mIndex = index;
add_sub_block_info.x = info.logicalRect.x;
add_sub_block_info.y = info.logicalRect.y;
add_sub_block_info.logicalWidth = info.logicalRect.w;
add_sub_block_info.logicalHeight = info.logicalRect.h;
add_sub_block_info.physicalWidth = info.physicalSize.w;
add_sub_block_info.physicalHeight = info.physicalSize.h;
add_sub_block_info.PixelType = info.pixelType;
add_sub_block_info.pyramid_type = info.pyramidType;
// note: there is a bug in libCZI here (info.compressionModeRaw is not valid here, we work around
// this by using the compression mode of the sub-block which we have to read in any case)
add_sub_block_info.compressionModeRaw = info.compressionModeRaw;
auto subBlock = reader_writer->ReadSubBlock(index);
size_t sub_block_data_size;
auto sub_block_data = subBlock->GetRawData(ISubBlock::MemBlkType::Data, &sub_block_data_size);
size_t sub_block_metadata_size;
auto sub_block_metadata = subBlock->GetRawData(ISubBlock::MemBlkType::Metadata, &sub_block_metadata_size);
size_t sub_block_attachment_size;
auto sub_block_attachment = subBlock->GetRawData(ISubBlock::MemBlkType::Attachment, &sub_block_attachment_size);
// workaround for above bug in libCZI
add_sub_block_info.compressionModeRaw = subBlock->GetSubBlockInfo().compressionModeRaw;
add_sub_block_info.sizeData = sub_block_data_size;
add_sub_block_info.getData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate that offset is is less than sub_block_data_size (and - offset should never be non-zero)
ptr = static_cast<const uint8_t*>(sub_block_data.get()) + offset;
size = sub_block_data_size - offset;
return true;
};
add_sub_block_info.sizeMetadata = sub_block_metadata_size;
if (add_sub_block_info.sizeMetadata > 0)
{
add_sub_block_info.getMetaData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_metadata.get()) + offset;
size = sub_block_metadata_size - offset;
return true;
};
}
add_sub_block_info.sizeAttachment = sub_block_attachment_size;
if (add_sub_block_info.sizeAttachment > 0)
{
add_sub_block_info.getAttachment =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_attachment.get()) + offset;
size = sub_block_attachment_size - offset;
return true;
};
}
reader_writer->ReplaceSubBlock(index, add_sub_block_info);
}
int main()
{
std::cout << "Opening input/output stream" << std::endl;
const auto io_stream = libCZI::CreateInputOutputStreamForFile(LR"(test.czi)");
// create the reader-writer-object
auto reader_writer = libCZI::CreateCZIReaderWriter();
// open the CZI-file
reader_writer->Create(io_stream);
// I do an initial pass to 'chop the top off' the pyramids so that they all have the same number of levels.
// If I don't do this, the 'odd' pyramid with less detail (the '1' in my sample file) does not appear in Omero.
// This approach is very crude
// a Slightly better approach would be to do an initial pass to establish the minimum zoom that is available on all the pyramids
// and then use that as a threshold. in this example I have done it manually because I know my test file needs only one level chopped off.
// Perhaps a more sophisticated approach would be to 'build up' the shorter pyramids by creating the upper levels based on the pixels of the ones below
// but that would be more difficult.
std::vector<int> indexesToRemove;
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
double zoom = info.GetZoom();
if(zoom==0.015625){
std::cout << "Removing Index " << index << " Zoom " << zoom << ": " << libCZI::Utils::DimCoordinateToString(&info.coordinate) << " Rect=" << info.logicalRect << " PhysicalSize=" << info.physicalSize << std::endl;
indexesToRemove.push_back(index);
}
return true;
});
// I do the actual removal out of the EnumerateSubBlocks loop to avoid a segmentation fault
for(int index : indexesToRemove) {
reader_writer->RemoveSubBlock(index);
}
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
RemoveSIndex(reader_writer, index, info);
return true;
});
reader_writer->Close();
return 0;
}
'''
And a screenshot showing the file loaded into Omero. I have been able to zoom into the tip of the '1'
![Screenshot 2024-02-22 at 12 50 30 pm](https://github.com/ZEISS/libczi/assets/67457186/1224a107-8d40-4d31-83b1-e24337bef4e1)
I will continue to run more tests. I may also attempt to open a PR onto your "jbl/https://github.com/ZEISS/libczi/issues/94-remove-S-index" branch.
ok, did a bit more work, now it figures out where to 'chop' the pyramids by inspecting the zoom
#include "../libCZI/libCZI.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace libCZI;
// I would call this instead 'MergeSIndex'
static void RemoveSIndex(const std::shared_ptr<ICziReaderWriter>& reader_writer, int index, const SubBlockInfo& info)
{
AddSubBlockInfo add_sub_block_info;
add_sub_block_info.Clear();
add_sub_block_info.coordinate = info.coordinate;
//add_sub_block_info.coordinate.Clear(DimensionIndex::S);
add_sub_block_info.coordinate.Set(DimensionIndex::S,0); //rather than clear the S index, it seems to work better if I 'merge' all the scenes into one with S0
// I have set the mIndex just using the enumeration index
add_sub_block_info.mIndexValid = true;
add_sub_block_info.mIndex = index;
add_sub_block_info.x = info.logicalRect.x;
add_sub_block_info.y = info.logicalRect.y;
add_sub_block_info.logicalWidth = info.logicalRect.w;
add_sub_block_info.logicalHeight = info.logicalRect.h;
add_sub_block_info.physicalWidth = info.physicalSize.w;
add_sub_block_info.physicalHeight = info.physicalSize.h;
add_sub_block_info.PixelType = info.pixelType;
add_sub_block_info.pyramid_type = info.pyramidType;
// note: there is a bug in libCZI here (info.compressionModeRaw is not valid here, we work around
// this by using the compression mode of the sub-block which we have to read in any case)
add_sub_block_info.compressionModeRaw = info.compressionModeRaw;
auto subBlock = reader_writer->ReadSubBlock(index);
size_t sub_block_data_size;
auto sub_block_data = subBlock->GetRawData(ISubBlock::MemBlkType::Data, &sub_block_data_size);
size_t sub_block_metadata_size;
auto sub_block_metadata = subBlock->GetRawData(ISubBlock::MemBlkType::Metadata, &sub_block_metadata_size);
size_t sub_block_attachment_size;
auto sub_block_attachment = subBlock->GetRawData(ISubBlock::MemBlkType::Attachment, &sub_block_attachment_size);
// workaround for above bug in libCZI
add_sub_block_info.compressionModeRaw = subBlock->GetSubBlockInfo().compressionModeRaw;
add_sub_block_info.sizeData = sub_block_data_size;
add_sub_block_info.getData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate that offset is is less than sub_block_data_size (and - offset should never be non-zero)
ptr = static_cast<const uint8_t*>(sub_block_data.get()) + offset;
size = sub_block_data_size - offset;
return true;
};
add_sub_block_info.sizeMetadata = sub_block_metadata_size;
if (add_sub_block_info.sizeMetadata > 0)
{
add_sub_block_info.getMetaData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_metadata.get()) + offset;
size = sub_block_metadata_size - offset;
return true;
};
}
add_sub_block_info.sizeAttachment = sub_block_attachment_size;
if (add_sub_block_info.sizeAttachment > 0)
{
add_sub_block_info.getAttachment =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_attachment.get()) + offset;
size = sub_block_attachment_size - offset;
return true;
};
}
reader_writer->ReplaceSubBlock(index, add_sub_block_info);
}
int main()
{
std::cout << "Opening input/output stream" << std::endl;
const auto io_stream = libCZI::CreateInputOutputStreamForFile(LR"(test2.czi)");
// create the reader-writer-object
auto reader_writer = libCZI::CreateCZIReaderWriter();
// open the CZI-file
reader_writer->Create(io_stream);
// //take a peaky at the stats
// auto statistics = reader_writer->GetStatistics();
// statistics.dimBounds.EnumValidDimensions(
// [&](libCZI::DimensionIndex dim, int start, int size)->bool
// {
// std::cout << "DimensionIndex: " << static_cast<unsigned int>(dim) << "Start: " << start << " Size=" << size << std::endl;
// return true;
// });
std::map<int, double> minimumZoomPerScene;
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) -> bool
{
libCZI::CDimCoordinate coord = info.coordinate;
int sValue;
if (coord.TryGetPosition(DimensionIndex::S, &sValue))
{
double zoom = info.GetZoom();
// Check if sValue is already in minimumZoomPerScene and if zoom is smaller
auto it = minimumZoomPerScene.find(sValue);
if (it == minimumZoomPerScene.end() || it->second > zoom) // if not found or found with a larger zoom
{
minimumZoomPerScene[sValue] = zoom; // Update with the new smaller zoom
}
}
return true;
});
double minimumCommonZoom = 0;
for(const auto& pair : minimumZoomPerScene) {
std::cout << "Scene: " << pair.first << ", Minimum Zoom: " << pair.second << std::endl;
if(pair.second > minimumCommonZoom){
minimumCommonZoom = pair.second;
}
}
std::cout << "Minimum Common Zoom : " << minimumCommonZoom << std::endl;
// I do an initial pass to 'chop the top off' the pyramids so that they all have the same number of levels.
// If I don't do this, the 'odd' pyramid with less detail (the '1' in my sample file) does not appear in Omero.
// This approach is very crude
// a Slightly better approach would be to do an initial pass to establish the minimum zoom that is available on all the pyramids
// and then use that as a threshold. in this example I have done it manually because I know my test file needs only one level chopped off.
// Perhaps a more sophisticated approach would be to 'build up' the shorter pyramids by creating the upper levels based on the pixels of the ones below
// but that would be more difficult.
std::vector<int> indexesToRemove;
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
double zoom = info.GetZoom();
if(zoom<minimumCommonZoom){
std::cout << "Removing Index " << index << " Zoom " << zoom << ": " << libCZI::Utils::DimCoordinateToString(&info.coordinate) << " Rect=" << info.logicalRect << " PhysicalSize=" << info.physicalSize << std::endl;
indexesToRemove.push_back(index);
}
return true;
});
// I do the actual removal out of the EnumerateSubBlocks loop to avoid a segmentation fault
for(int index : indexesToRemove) {
reader_writer->RemoveSubBlock(index);
}
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
RemoveSIndex(reader_writer, index, info);
return true;
});
reader_writer->Close();
return 0;
}
I ran czi check on a merged file and it is mostly green except for some document statistics...
./CZICheck -s test2_s0_mi_remtop.czi
Test "check subblock's coordinates for 'consistent dimensions'" : OK
Test "SubBlock-Segment in SubBlockDirectory within file" : OK
Test "SubBlock-Segments in SubBlockDirectory are valid" : OK
Test "check subblock's coordinates being unique" : OK
Test "check whether the document uses the deprecated 'B-index'" : OK
Test "check that the subblocks of a channel have the same pixeltype" : OK
Test "Check that planes indices start at 0" : OK
Test "Check that planes have consecutive indices" : OK
Test "check if all subblocks have the M index" : OK
Test "Basic semantic checks of the XML-metadata" :DimensionIndex: 5StartStats: 0 StartMeta=0 EndStats=1EndMeta: 4
For the following dimensions the start/size given in metadata differs from document statistics: S
WARN
Test "check if subblocks at pyramid-layer 0 of different scenes are overlapping" : OK
Result: With Warnings
how would I fiddle with the S document Statistics? I think I need to use CSbBlkStatisticsUpdater but can't figure that out.
@ptahmose here is my 'final' version that addiitonally adjusts the document metadata so the merged file can pass CZICheck and opened in fiji..
#include "../libCZI/libCZI.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace libCZI;
// I would call this instead 'MergeSIndex'
static void RemoveSIndex(const std::shared_ptr<ICziReaderWriter>& reader_writer, int index, const SubBlockInfo& info)
{
AddSubBlockInfo add_sub_block_info;
add_sub_block_info.Clear();
add_sub_block_info.coordinate = info.coordinate;
//add_sub_block_info.coordinate.Clear(DimensionIndex::S);
add_sub_block_info.coordinate.Set(DimensionIndex::S,0); //rather than clear the S index, it seems to work better if I 'merge' all the scenes into one with S0
// I have set the mIndex just using the enumeration index
add_sub_block_info.mIndexValid = true;
add_sub_block_info.mIndex = index;
add_sub_block_info.x = info.logicalRect.x;
add_sub_block_info.y = info.logicalRect.y;
add_sub_block_info.logicalWidth = info.logicalRect.w;
add_sub_block_info.logicalHeight = info.logicalRect.h;
add_sub_block_info.physicalWidth = info.physicalSize.w;
add_sub_block_info.physicalHeight = info.physicalSize.h;
add_sub_block_info.PixelType = info.pixelType;
add_sub_block_info.pyramid_type = info.pyramidType;
// note: there is a bug in libCZI here (info.compressionModeRaw is not valid here, we work around
// this by using the compression mode of the sub-block which we have to read in any case)
add_sub_block_info.compressionModeRaw = info.compressionModeRaw;
auto subBlock = reader_writer->ReadSubBlock(index);
size_t sub_block_data_size;
auto sub_block_data = subBlock->GetRawData(ISubBlock::MemBlkType::Data, &sub_block_data_size);
size_t sub_block_metadata_size;
auto sub_block_metadata = subBlock->GetRawData(ISubBlock::MemBlkType::Metadata, &sub_block_metadata_size);
size_t sub_block_attachment_size;
auto sub_block_attachment = subBlock->GetRawData(ISubBlock::MemBlkType::Attachment, &sub_block_attachment_size);
// workaround for above bug in libCZI
add_sub_block_info.compressionModeRaw = subBlock->GetSubBlockInfo().compressionModeRaw;
add_sub_block_info.sizeData = sub_block_data_size;
add_sub_block_info.getData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate that offset is is less than sub_block_data_size (and - offset should never be non-zero)
ptr = static_cast<const uint8_t*>(sub_block_data.get()) + offset;
size = sub_block_data_size - offset;
return true;
};
add_sub_block_info.sizeMetadata = sub_block_metadata_size;
if (add_sub_block_info.sizeMetadata > 0)
{
add_sub_block_info.getMetaData =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_metadata.get()) + offset;
size = sub_block_metadata_size - offset;
return true;
};
}
add_sub_block_info.sizeAttachment = sub_block_attachment_size;
if (add_sub_block_info.sizeAttachment > 0)
{
add_sub_block_info.getAttachment =
[&](int callCnt, size_t offset, const void*& ptr, size_t& size) -> bool
{
// TODO: validate offset
ptr = static_cast<const uint8_t*>(sub_block_attachment.get()) + offset;
size = sub_block_attachment_size - offset;
return true;
};
}
reader_writer->ReplaceSubBlock(index, add_sub_block_info);
}
int main()
{
std::cout << "Opening input/output stream" << std::endl;
const auto io_stream = libCZI::CreateInputOutputStreamForFile(LR"(test2.czi)");
// create the reader-writer-object
auto reader_writer = libCZI::CreateCZIReaderWriter();
// open the CZI-file
reader_writer->Create(io_stream);
std::map<int, double> minimumZoomPerScene;
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) -> bool
{
libCZI::CDimCoordinate coord = info.coordinate;
int sValue;
if (coord.TryGetPosition(DimensionIndex::S, &sValue))
{
double zoom = info.GetZoom();
// Check if sValue is already in minimumZoomPerScene and if zoom is smaller
auto it = minimumZoomPerScene.find(sValue);
if (it == minimumZoomPerScene.end() || it->second > zoom) // if not found or found with a larger zoom
{
minimumZoomPerScene[sValue] = zoom; // Update with the new smaller zoom
}
}
return true;
});
double minimumCommonZoom = 0;
for(const auto& pair : minimumZoomPerScene) {
std::cout << "Scene: " << pair.first << ", Minimum Zoom: " << pair.second << std::endl;
if(pair.second > minimumCommonZoom){
minimumCommonZoom = pair.second;
}
}
std::cout << "Minimum Common Zoom : " << minimumCommonZoom << std::endl;
// I do an initial pass to 'chop the top off' the pyramids so that they all have the same number of levels.
// If I don't do this, the 'odd' pyramid with less detail (the '1' in my sample file) does not appear in Omero.
// Perhaps a more sophisticated approach would be to 'build up' the shorter pyramids by creating the upper levels based on the pixels of the ones below
// but that would be more difficult.
std::vector<int> indexesToRemove;
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
double zoom = info.GetZoom();
if(zoom<minimumCommonZoom){
std::cout << "Removing Index " << index << " Zoom " << zoom << ": " << libCZI::Utils::DimCoordinateToString(&info.coordinate) << " Rect=" << info.logicalRect << " PhysicalSize=" << info.physicalSize << std::endl;
indexesToRemove.push_back(index);
}
return true;
});
// I do the actual removal out of the EnumerateSubBlocks loop to avoid a segmentation fault
for(int index : indexesToRemove) {
reader_writer->RemoveSubBlock(index);
}
reader_writer->EnumerateSubBlocks(
[&](int index, const SubBlockInfo& info) ->bool
{
RemoveSIndex(reader_writer, index, info);
return true;
});
// read the metadata-segment
const auto metadata_segment = reader_writer->ReadMetadataSegment();
// construct the metadata-object from the metadata-segment (use this to query information if required)
const auto metadata = metadata_segment->CreateMetaFromMetadataSegment();
// now, create a metadata-builder-object from the XML
const auto metadata_builder = CreateMetadataBuilderFromXml(metadata->GetXml());
// modify a node
const auto comment_node = metadata_builder->GetRootNode()->GetOrCreateChildNode("Metadata/Information/Document/Comment");
comment_node->SetValue("Scenes Merged"); //TODO - some useful information here about original scenes
const auto size_s_node = metadata_builder->GetRootNode()->GetOrCreateChildNode("Metadata/Information/Image/SizeS");
size_s_node->SetValue("1");
// and now, write the modified metadata into the file
WriteMetadataInfo metadata_info;
metadata_info.Clear();
const string source_metadata_xml = metadata_builder->GetXml();
metadata_info.szMetadata = source_metadata_xml.c_str();
metadata_info.szMetadataSize = source_metadata_xml.size();
reader_writer->SyncWriteMetadata(metadata_info);
reader_writer->Close();
return 0;
}
@ptahmose I cleaned this up and opened a PR with my changes
Is your feature request related to a problem? Please describe. I want to re-write my .czi files to merge scene coordinates so that there is only one scene in the image. I think that will permit me to import data into Omero as a single Image so that my pathology team can look at a single document in that system rather than multiple documents per slide. (full discussion here https://forum.image.sc/t/zoom-from-overview-to-detailed-scan-for-imported-czi-files/85002/13)
Describe the solution you'd like A Clear and concise working example of code that will enable me to 're-write' a .czi file with altered scene metadata
Describe alternatives you've considered Here is my best attempt. it runs but a corrupted file results. I took some leads from Src/libCZI_UnitTests/test_readerwriter.cpp. My test file is here (https://zenodo.org/record/8423633)
Additional context I know that Omero is not necessarily your problem but this workaround would be a pragmatic solution to an important issue for my group and it seems it should be possible. FWIW, I thought the documentation on 'writing a CZI File' (https://zeiss.github.io/libczi/writing_czi.html) was a little thin