Skip to content

Commit 3986ede

Browse files
authored
Merge pull request #1386 from thewtex/support-input-transform-type
feat: add itk::wasm::SupportInputTransformTypes
2 parents 590844c + 346b7f5 commit 3986ede

10 files changed

Lines changed: 330 additions & 4 deletions

ITKKWStyleOverwrite.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ itkSupportInputImageTypesMemoryIOTest\.cxx Namespace Disable
55
itkSupportInputMeshTypesTest\.cxx Namespace Disable
66
itkSupportInputMeshTypesMemoryIOTest\.cxx Namespace Disable
77
itkSupportInputPolyDataTypesTest\.cxx Namespace Disable
8+
itkSupportInputTransformTypesTest\.cxx Namespace Disable
89
itkFloatTypesJSON\.h Namespace Disable
910
itkOutputTransformIO\.h Namespace Disable

include/itkSupportInputPolyDataTypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class PipelineFunctor
6161
6262
using InputPolyDataType = itk::wasm::InputPolyData<PolyDataType>;
6363
InputPolyDataType inputPolyData;
64-
pipeline.add_option("InputPolyData", inputPolyData, "The input polydata")->required();
64+
pipeline.add_option("input-polydata", inputPolyData, "The input polydata")->required();
6565
```
6666
[...]
6767
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#ifndef itkSupportInputTransformTypes_h
19+
#define itkSupportInputTransformTypes_h
20+
#include "itkPipeline.h"
21+
#include "itkWasmPixelTypeFromIOPixelEnum.h"
22+
#include "itkWasmComponentTypeFromIOComponentEnum.h"
23+
#include "itkWasmMapComponentType.h"
24+
25+
#include "itkTransform.h"
26+
#include "itkTransformIOBase.h"
27+
#include "itkTransformIOFactory.h"
28+
#include "WebAssemblyInterfaceExport.h"
29+
30+
#include "itkTransformJSON.h"
31+
32+
namespace itk
33+
{
34+
35+
WebAssemblyInterface_EXPORT bool
36+
lexical_cast(const std::string & input, TransformTypeJSON & transformType);
37+
38+
namespace wasm
39+
{
40+
41+
/** \class SupportInputTransformTypes
42+
*
43+
* \brief Instantiatiate a Pipeline functor over multiple transform parameter types and dimensions and match to the input transform type.
44+
*
45+
* Instantiate the PipelineFunctor (function object) over multiple transform types.
46+
* If the input transform matches these parameter types and dimensions, use the compile-time optimized pipeline for that transform type.
47+
* Otherwise, exit the pipeline with an error identifying the unsupported transform type.
48+
*
49+
* Example usage:
50+
*
51+
```
52+
template<typename TTransform>
53+
class PipelineFunctor
54+
{
55+
public:
56+
int operator()(itk::wasm::Pipeline & pipeline)
57+
{
58+
using TransformType = TTransform;
59+
60+
using InputTransformType = itk::wasm::InputTransform<TransformType>;
61+
InputTransformType inputTransform;
62+
pipeline.add_option("input-transform", inputTransform, "The input polydata")->required();
63+
```
64+
[...]
65+
66+
```
67+
int
68+
main(int argc, char * argv[])
69+
{
70+
itk::wasm::Pipeline pipeline("support-multiple", "Test supporting multiple input polydata types", argc, argv);
71+
72+
// Supports the parameters types float, double, and dimensions 2 and 3
73+
return itk::wasm::SupportInputTransformTypes<PipelineFunctor, float, double>
74+
::Dimensions<2U, 3U>("input-transform", pipeline);
75+
}
76+
```
77+
* It is assumed that the input dimension and output dimension will be the same.
78+
*
79+
* \ingroup WebAssemblyInterface
80+
*/
81+
template <template <typename TTransform> class TPipelineFunctor, typename... TParameterValuesSupported>
82+
class SupportInputTransformTypes
83+
{
84+
public:
85+
template <unsigned int... VDimensions>
86+
static int
87+
Dimensions(const std::string & inputTransformOptionName, Pipeline & pipeline)
88+
{
89+
TransformTypeJSON transformType;
90+
91+
const auto iwpArgc = pipeline.get_argc();
92+
const auto iwpArgv = pipeline.get_argv();
93+
bool passThrough = false;
94+
for (int ii = 0; ii < iwpArgc; ++ii)
95+
{
96+
const std::string arg(iwpArgv[ii]);
97+
if (arg == "-h" || arg == "--help" || arg == "--interface-json" || arg == "--version")
98+
{
99+
passThrough = true;
100+
}
101+
}
102+
if (passThrough)
103+
{
104+
return IterateDimensions<VDimensions...>(pipeline, transformType, passThrough);
105+
}
106+
107+
auto tempOption = pipeline.add_option(inputTransformOptionName, transformType, "Read Transform type.");
108+
109+
ITK_WASM_PRE_PARSE(pipeline);
110+
111+
pipeline.remove_option(tempOption);
112+
113+
return IterateDimensions<VDimensions...>(pipeline, transformType);
114+
}
115+
116+
private:
117+
template <unsigned int VDimension, typename TParameterValues, typename... TParameterValuesRest>
118+
static int
119+
IterateParameterValueTypes(Pipeline & pipeline, const TransformTypeJSON & transformType, bool passThrough = false)
120+
{
121+
using ParameterValueType = TParameterValues;
122+
123+
if (passThrough ||
124+
transformType.parametersValueType == MapComponentType<ParameterValueType>::JSONFloatTypeEnum)
125+
{
126+
using TransformType = Transform<ParameterValueType, VDimension, VDimension>;
127+
128+
using PipelineType = TPipelineFunctor<TransformType>;
129+
return PipelineType()(pipeline);
130+
}
131+
132+
if constexpr (sizeof...(TParameterValuesRest) > 0)
133+
{
134+
return IterateParameterValueTypes<VDimension, TParameterValuesRest...>(pipeline, transformType);
135+
}
136+
137+
std::ostringstream ostrm;
138+
std::string transformTypeString = glz::write_json(transformType).value_or("error");
139+
ostrm << "Unsupported transform type: " << transformTypeString << std::endl;
140+
CLI::Error err("Runtime error", ostrm.str(), 1);
141+
return pipeline.exit(err);
142+
}
143+
144+
template <unsigned int VDimension, unsigned int... VDimensions>
145+
static int
146+
IterateDimensions(Pipeline & pipeline, const TransformTypeJSON & transformType, bool passThrough = false)
147+
{
148+
if (passThrough || VDimension == transformType.inputDimension)
149+
{
150+
return IterateParameterValueTypes<VDimension, TParameterValuesSupported...>(pipeline, transformType);
151+
}
152+
153+
if constexpr (sizeof...(VDimensions) > 0)
154+
{
155+
return IterateDimensions<VDimensions...>(pipeline, transformType);
156+
}
157+
158+
std::ostringstream ostrm;
159+
ostrm << "Unsupported transform dimension: " << transformType.inputDimension;
160+
CLI::Error err("Runtime error", ostrm.str(), 1);
161+
return pipeline.exit(err);
162+
}
163+
};
164+
165+
} // end namespace wasm
166+
} // end namespace itk
167+
168+
#endif

include/itkWasmTransformIO.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ namespace itk
3232
*
3333
* \brief Read and write the an itk::Transform in a format for interfacing in WebAssembly (Wasm).
3434
*
35-
* This format is intended to facilitate data exchange in itk-wasm.
36-
* It reads and writes an itk-wasm Transform object where TypedArrays are
35+
* This format is intended to facilitate data exchange in ITK-Wasm.
36+
* It reads and writes an ITK-Wasm Transform object where TypedArrays are
3737
* replaced by binary files on the filesystem or in a CBOR file.
3838
*
3939
* The file extensions used are .iwt and .iwt.cbor.

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ set(WebAssemblyInterface_SRCS
2525
itkSupportInputImageTypes.cxx
2626
itkSupportInputMeshTypes.cxx
2727
itkSupportInputPolyDataTypes.cxx
28+
itkSupportInputTransformTypes.cxx
2829
itktransformParameterizationString.cxx
2930
itkioComponentEnumFromJSON.cxx
3031
itkioPixelEnumFromJSON.cxx
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
#include "itkSupportInputTransformTypes.h"
19+
#include "itkWasmExports.h"
20+
21+
#include "itkjsonFromIOComponentEnum.h"
22+
#include "itkjsonFromIOPixelEnum.h"
23+
#include "itkTransformJSON.h"
24+
25+
namespace itk
26+
{
27+
28+
bool
29+
lexical_cast(const std::string & input, TransformTypeJSON & transformType)
30+
{
31+
if (wasm::Pipeline::get_use_memory_io())
32+
{
33+
#ifndef ITK_WASM_NO_MEMORY_IO
34+
const unsigned int index = std::stoi(input);
35+
auto json = wasm::getMemoryStoreInputJSON(0, index);
36+
std::string deserialized;
37+
auto deserializedAttempt = glz::read_json<itk::TransformJSON>(json);
38+
if (!deserializedAttempt)
39+
{
40+
const std::string descriptiveError = glz::format_error(deserializedAttempt, json);
41+
throw std::runtime_error("Failed to deserialize TransformJSON: " + descriptiveError);
42+
}
43+
auto transformJSON = deserializedAttempt.value();
44+
transformType = transformJSON.transformType;
45+
#else
46+
return false;
47+
#endif
48+
}
49+
else
50+
{
51+
#ifndef ITK_WASM_NO_FILESYSTEM_IO
52+
using TransformIOFactoryFloatType = itk::TransformIOFactoryTemplate<float>;
53+
TransformIOBaseTemplate<float>::Pointer transformIOFloat = TransformIOFactoryFloatType::CreateTransformIO(input.c_str(), CommonEnums::IOFileMode::ReadMode);
54+
if (transformIOFloat.IsNull())
55+
{
56+
std::cerr << "IO not available for: " << input << std::endl;
57+
return false;
58+
}
59+
60+
transformIOFloat->SetFileName(input);
61+
transformIOFloat->Read();
62+
63+
const auto transformList = transformIOFloat->GetReadTransformList();
64+
const auto firstTransform = transformList.front();
65+
const std::string transformName = firstTransform->GetTransformTypeAsString();
66+
// if the transformName contains "float", then set the transformType to float
67+
if (transformName.find("float") != std::string::npos)
68+
{
69+
transformType.parametersValueType = JSONFloatTypesEnum::float32;
70+
}
71+
else
72+
{
73+
transformType.parametersValueType = JSONFloatTypesEnum::float64;
74+
}
75+
transformType.inputDimension = firstTransform->GetInputSpaceDimension();
76+
transformType.outputDimension = firstTransform->GetOutputSpaceDimension();
77+
// transformType.transformParameterization = firstTransform->GetNameOfClass();
78+
#else
79+
return false;
80+
#endif
81+
}
82+
return true;
83+
}
84+
85+
} // end namespace itk

test/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ set(WebAssemblyInterfaceTests
7070
itkSupportInputMeshTypesTest.cxx
7171
itkSupportInputMeshTypesMemoryIOTest.cxx
7272
itkSupportInputPolyDataTypesTest.cxx
73+
itkSupportInputTransformTypesTest.cxx
7374
itkTransformJSONTest.cxx
7475
itkWasmTransformInterfaceTest.cxx
7576
itkWasmTransformInterfaceCompositeTest.cxx
@@ -287,6 +288,13 @@ itk_add_test(NAME itkSupportInputPolyDataTypesTest
287288
${ITK_TEST_OUTPUT_DIR}/itkSupportInputPolyDataTypesTest.iwm
288289
)
289290

291+
itk_add_test(NAME itkSupportInputTransformTypesTest
292+
COMMAND WebAssemblyInterfaceTestDriver
293+
itkSupportInputTransformTypesTest
294+
DATA{Input/LinearTransform.iwt.cbor}
295+
${ITK_TEST_OUTPUT_DIR}/itkSupportInputTransformTypesTest.iwt
296+
)
297+
290298
itk_add_test(NAME itkTransformJSONTest
291299
COMMAND WebAssemblyInterfaceTestDriver
292300
itkTransformJSONTest
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bafkreib3spljpfflhairm3aooy4pwkgcczwm7z6ksnzntg7ui7xbyeqllu

test/itkSupportInputPolyDataTypesTest.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ int
5353
itkSupportInputPolyDataTypesTest(int argc, char * argv[])
5454
{
5555
itk::wasm::Pipeline pipeline(
56-
"support-input-polydata-types-test", "Test supporting multiple input mesh types", argc, argv);
56+
"support-input-polydata-types-test", "Test supporting multiple input polydata types", argc, argv);
5757

5858
itk::WasmMeshIOFactory::RegisterOneFactory();
5959

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#include "itkPipeline.h"
20+
#include "itkInputTransform.h"
21+
#include "itkOutputTransform.h"
22+
#include "itkSupportInputTransformTypes.h"
23+
#include "itkWasmTransformIOFactory.h"
24+
#include "itkAffineTransform.h"
25+
26+
template <typename TTransform>
27+
class PipelineFunctor
28+
{
29+
public:
30+
int
31+
operator()(itk::wasm::Pipeline & pipeline)
32+
{
33+
using TransformType = itk::AffineTransform<typename TTransform::ParametersValueType, TTransform::InputSpaceDimension>;
34+
35+
using InputTransformType = itk::wasm::InputTransform<TransformType>;
36+
InputTransformType inputTransform;
37+
pipeline.add_option("input-transform", inputTransform, "The input transform")->required()->type_name("INPUT_TRANSFORM");
38+
39+
using OutputTransformType = itk::wasm::OutputTransform<TransformType>;
40+
OutputTransformType outputTransform;
41+
pipeline.add_option("output-transform", outputTransform, "The output transform")
42+
->required()
43+
->type_name("OUTPUT_TRANSFORM");
44+
45+
ITK_WASM_PARSE(pipeline);
46+
47+
outputTransform.Set(inputTransform.Get());
48+
49+
return EXIT_SUCCESS;
50+
}
51+
};
52+
53+
int
54+
itkSupportInputTransformTypesTest(int argc, char * argv[])
55+
{
56+
itk::wasm::Pipeline pipeline(
57+
"support-input-transform-types-test", "Test supporting multiple input transform types", argc, argv);
58+
59+
itk::WasmTransformIOFactory::RegisterOneFactory();
60+
61+
return itk::wasm::SupportInputTransformTypes<PipelineFunctor, float, double>::Dimensions<2U, 3U>("input-transform", pipeline);
62+
}

0 commit comments

Comments
 (0)