Skip to content

Commit

Permalink
feat(image-io): Add --information-only flag
Browse files Browse the repository at this point in the history
Support only reading and writing image metadata as opposed to pixel
data.

Also output whether an IO can read or write a image with a json output
that contains a boolean.
  • Loading branch information
thewtex committed Oct 3, 2023
1 parent 34d9762 commit 564e678
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 81 deletions.
11 changes: 7 additions & 4 deletions include/itkInputImageIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,13 @@ bool lexical_cast(const std::string &input, InputImageIO &inputImageIO)
const rapidjson::Value & dataJson = document["data"];
const std::string dataString( dataJson.GetString() );
const char * dataPtr = reinterpret_cast< char * >( std::strtoull(dataString.substr(35).c_str(), nullptr, 10) );
WasmImageIOBase::PixelDataContainerType * pixelDataContainer = wasmImageIOBase->GetPixelDataContainer();
const size_t pixelDataBytes = wasmImageIO->GetImageSizeInBytes();
pixelDataContainer->resize(pixelDataBytes);
pixelDataContainer->assign(dataPtr, dataPtr + pixelDataBytes);
if (dataPtr != nullptr)
{
WasmImageIOBase::PixelDataContainerType * pixelDataContainer = wasmImageIOBase->GetPixelDataContainer();
const size_t pixelDataBytes = wasmImageIO->GetImageSizeInBytes();
pixelDataContainer->resize(pixelDataBytes);
pixelDataContainer->assign(dataPtr, dataPtr + pixelDataBytes);
}
wasmImageIOBase->SetImageIO(wasmImageIO, false);
wasmImageIOBase->SetJSON(json);

Expand Down
29 changes: 25 additions & 4 deletions include/itkOutputImageIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ namespace wasm
class OutputImageIO
{
public:

/** Set whether to only read image metadata. Do not read the pixel data. */
void SetInformationOnly(bool informationOnly) {
this->m_InformationOnly = informationOnly;
}

void Set(ImageIOBase * imageIO) {
this->m_ImageIO = imageIO;
}
Expand Down Expand Up @@ -76,13 +82,19 @@ class OutputImageIO
wasmImageIOBase->SetImageIO(this->m_ImageIO);
setMemoryStoreOutputDataObject(0, index, wasmImageIOBase);

const auto directionAddress = reinterpret_cast< size_t >( &(wasmImageIOBase->GetDirectionContainer()->at(0)) );
const auto directionSize = wasmImageIOBase->GetDirectionContainer()->size() * sizeof(double);
setMemoryStoreOutputArray(0, index, 1, directionAddress, directionSize);

if (this->m_InformationOnly)
{
return;
}

const auto dataAddress = reinterpret_cast< size_t >( &(wasmImageIOBase->GetPixelDataContainer()->at(0)) );
const auto dataSize = wasmImageIOBase->GetPixelDataContainer()->size();
setMemoryStoreOutputArray(0, index, 0, dataAddress, dataSize);

const auto directionAddress = reinterpret_cast< size_t >( &(wasmImageIOBase->GetDirectionContainer()->at(0)) );
const auto directionSize = wasmImageIOBase->GetDirectionContainer()->size() * sizeof(double);
setMemoryStoreOutputArray(0, index, 1, directionAddress, directionSize);
}
#else
std::cerr << "Memory IO not supported" << std::endl;
Expand Down Expand Up @@ -117,12 +129,19 @@ class OutputImageIO
{
ioRegion.SetSize(dim, this->m_ImageIO->GetDimensions( dim ));
}

this->m_ImageIO->SetIORegion( ioRegion );
this->m_ImageIO->SetUseStreamedReading(false);
this->m_ImageIO->Read(reinterpret_cast< void * >( &(pixelData.at(0)) ));

wasmImageIO->SetFileName(this->m_Identifier);
wasmImageIO->WriteImageInformation();

if (this->m_InformationOnly)
{
return;
}

this->m_ImageIO->Read(reinterpret_cast< void * >( &(pixelData.at(0)) ));
wasmImageIO->SetIORegion( ioRegion );
wasmImageIO->Write(reinterpret_cast< void * >( &(pixelData.at(0)) ));
}
Expand All @@ -136,6 +155,8 @@ class OutputImageIO
typename ImageIOBase::Pointer m_ImageIO;

std::string m_Identifier;

bool m_InformationOnly{ false };
};

bool lexical_cast(const std::string &input, OutputImageIO &outputImageIO)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"build:wasi:dicom": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/dicom -b wasi-build build",
"build:wasi:compare-images": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/compare-images -b wasi-build build",
"build:wasi:image-io": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/image-io -b wasi-build build",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify && npm run build:wasi:compare-images && npm run build:bindgen:python:compare-images && npm run build:wasi:dicom && npm run build:bindgen:python:dicom && npm run build:wasi:dicom && npm run build:bindgen:python:image-io",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify && npm run build:wasi:compare-images && npm run build:bindgen:python:compare-images && npm run build:wasi:dicom && npm run build:bindgen:python:dicom && npm run build:wasi:image-io && npm run build:bindgen:python:image-io",
"cypress:open": "npx cypress open",
"cypress:run": "npx cypress run --config defaultCommandTimeout=8000",
"cypress:install": "npx cypress install",
Expand Down
79 changes: 44 additions & 35 deletions packages/image-io/read-image.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "itkImageIOBase.h"
#include "itkImage.h"
#include "itkOutputImageIO.h"
#include "itkOutputTextStream.h"

#ifndef IMAGE_IO_CLASS
#error "IMAGE_IO_CLASS definition must be provided"
Expand Down Expand Up @@ -74,24 +75,28 @@
#endif
#include "itkWasmImageIO.h"

#define PIPELINE_NAME (#IMAGE_IO_KEBAB_NAME "read-image")
#define VALUE(string) #string
#define TO_LITERAL(string) VALUE(string)

#include "itkPipeline.h"
#include "itkOutputImage.h"

template <typename TImageIO>
int readImage(const std::string & inputFileName, itk::wasm::OutputImageIO & outputImageIO, bool quiet)
int readImage(const std::string & inputFileName, itk::wasm::OutputTextStream & couldRead, itk::wasm::OutputImageIO & outputImageIO, bool informationOnly)
{
using ImageIOType = TImageIO;

auto imageIO = ImageIOType::New();

if(!imageIO->CanReadFile(inputFileName.c_str()))
outputImageIO.SetInformationOnly(informationOnly);

if (imageIO->CanReadFile(inputFileName.c_str()))
{
couldRead.Get() << "true\n";
}
else
{
if(!quiet)
{
std::cerr << "Could not read file: " << inputFileName << std::endl;
}
couldRead.Get() << "false\n";
return EXIT_FAILURE;
}

Expand All @@ -103,65 +108,69 @@ int readImage(const std::string & inputFileName, itk::wasm::OutputImageIO & outp

int main (int argc, char * argv[])
{
itk::wasm::Pipeline pipeline("PIPELINE_NAME", "Read an image file format and convert it to the itk-wasm file format", argc, argv);
const char * pipelineName = TO_LITERAL(IMAGE_IO_KEBAB_NAME) "-read-image";
itk::wasm::Pipeline pipeline(pipelineName, "Read an image file format and convert it to the itk-wasm file format", argc, argv);

std::string inputFileName;
pipeline.add_option("input-image", inputFileName, "Input image")->required()->check(CLI::ExistingFile)->type_name("INPUT_BINARY_FILE");
pipeline.add_option("serialized-image", inputFileName, "Input image serialized in the file format")->required()->check(CLI::ExistingFile)->type_name("INPUT_BINARY_FILE");

itk::wasm::OutputTextStream couldRead;
pipeline.add_option("could-read", couldRead, "Whether the input could be read. If false, the output image is not valid.")->type_name("OUTPUT_JSON");

itk::wasm::OutputImageIO outputImageIO;
pipeline.add_option("output-image", outputImageIO, "Output image")->required()->type_name("OUTPUT_IMAGE");
pipeline.add_option("image", outputImageIO, "Output image")->required()->type_name("OUTPUT_IMAGE");

bool quiet = false;
pipeline.add_flag("-q,--quiet", quiet, "Less verbose output");
bool informationOnly = false;
pipeline.add_flag("-i,--information-only", informationOnly, "Only read image metadata -- do not read pixel data.");

ITK_WASM_PARSE(pipeline);

#if IMAGE_IO_CLASS == 0
return readImage<itk::PNGImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::PNGImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 1
return readImage<itk::MetaImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::MetaImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 2
return readImage<itk::TIFFImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::TIFFImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 3
return readImage<itk::NiftiImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::NiftiImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 4
return readImage<itk::JPEGImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::JPEGImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 5
return readImage<itk::NrrdImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::NrrdImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 6
return readImage<itk::VTKImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::VTKImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 7
return readImage<itk::BMPImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::BMPImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 8
return readImage<itk::HDF5ImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::HDF5ImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 9
return readImage<itk::MINCImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::MINCImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 10
return readImage<itk::MRCImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::MRCImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 11
return readImage<itk::LSMImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::LSMImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 12
return readImage<itk::MGHImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::MGHImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 13
return readImage<itk::BioRadImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::BioRadImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 14
return readImage<itk::GiplImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::GiplImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 15
return readImage<itk::GE4ImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::GE4ImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 16
return readImage<itk::GE5ImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::GE5ImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 17
return readImage<itk::GEAdwImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::GEAdwImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 18
return readImage<itk::GDCMImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::GDCMImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 19
return readImage<itk::ScancoImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::ScancoImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 20
return readImage<itk::FDFImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::FDFImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 21
return readImage<itk::WasmImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::WasmImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#elif IMAGE_IO_CLASS == 22
return readImage<itk::WasmZstdImageIO>(inputFileName, outputImageIO, quiet);
return readImage<itk::WasmZstdImageIO>(inputFileName, couldRead, outputImageIO, informationOnly);
#else
#error "Unsupported IMAGE_IO_CLASS"
#endif
Expand Down
Loading

0 comments on commit 564e678

Please sign in to comment.