Skip to content

Commit 669928a

Browse files
committed
feat: add itk::wasm::OutputTransform
1 parent c7d6676 commit 669928a

9 files changed

+286
-20
lines changed

include/itkOutputTransform.h

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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 itkOutputTransform_h
19+
#define itkOutputTransform_h
20+
21+
#include "itkPipeline.h"
22+
23+
#ifndef ITK_WASM_NO_MEMORY_IO
24+
#include "itkWasmExports.h"
25+
#include "itkWasmTransform.h"
26+
#include "itkTransformToWasmTransformFilter.h"
27+
#include "itkCompositeTransform.h"
28+
#include "itkCompositeTransformIOHelper.h"
29+
#endif
30+
#ifndef ITK_WASM_NO_FILESYSTEM_IO
31+
#include "itkTransformFileWriter.h"
32+
#endif
33+
34+
namespace itk
35+
{
36+
namespace wasm
37+
{
38+
/**
39+
*\class OutputTransform
40+
* \brief Output transform for an itk::wasm::Pipeline
41+
*
42+
* This transform is written to the filesystem or memory when it goes out of scope.
43+
*
44+
* Call `GetTransform()` to get the TTransform * to use an input to a pipeline.
45+
*
46+
* \ingroup WebAssemblyInterface
47+
*/
48+
template <typename TTransform>
49+
class ITK_TEMPLATE_EXPORT OutputTransform
50+
{
51+
public:
52+
using TransformType = TTransform;
53+
54+
void Set(const TransformType * transform) {
55+
this->m_Transform = transform;
56+
}
57+
58+
const TransformType * Get() const {
59+
return this->m_Transform.GetPointer();
60+
}
61+
62+
/** FileName or output index. */
63+
void SetIdentifier(const std::string & identifier)
64+
{
65+
this->m_Identifier = identifier;
66+
}
67+
const std::string & GetIdentifier() const
68+
{
69+
return this->m_Identifier;
70+
}
71+
72+
OutputTransform() = default;
73+
~OutputTransform() {
74+
if(wasm::Pipeline::get_use_memory_io())
75+
{
76+
#ifndef ITK_WASM_NO_MEMORY_IO
77+
if (!this->m_Transform.IsNull() && !this->m_Identifier.empty())
78+
{
79+
using TransformToWasmTransformFilterType = TransformToWasmTransformFilter<TransformType>;
80+
auto transformToWasmTransformFilter = TransformToWasmTransformFilterType::New();
81+
transformToWasmTransformFilter->SetTransform(this->m_Transform.GetPointer());
82+
transformToWasmTransformFilter->Update();
83+
auto wasmTransform = transformToWasmTransformFilter->GetOutput();
84+
const auto index = std::stoi(this->m_Identifier);
85+
setMemoryStoreOutputDataObject(0, index, wasmTransform);
86+
87+
using ParametersValueType = typename TransformType::ParametersValueType;
88+
using FixedParametersValueType = typename TransformType::FixedParametersValueType;
89+
using CompositeTransformType = CompositeTransform<ParametersValueType, TransformType::InputSpaceDimension>;
90+
auto compositeTransform = dynamic_cast<const CompositeTransformType *>(wasmTransform->GetTransform());
91+
if (compositeTransform)
92+
{
93+
unsigned int dataCount = 0;
94+
CompositeTransformIOHelperTemplate<ParametersValueType> helper;
95+
auto transformList = helper.GetTransformList(compositeTransform);
96+
// composite transform
97+
transformList.pop_front();
98+
// iterate through the transform list and store each transfrom's fixed parameters and parameters
99+
for (const auto & transform: transformList)
100+
{
101+
const auto fixedParamsAddress = reinterpret_cast< size_t >( transform->GetFixedParameters().data_block() );
102+
const auto fixedParamsSize = transform->GetFixedParameters().Size() * sizeof(FixedParametersValueType);
103+
setMemoryStoreOutputArray(0, index, dataCount, fixedParamsAddress, fixedParamsSize);
104+
++dataCount;
105+
106+
const auto paramsAddress = reinterpret_cast< size_t >( transform->GetParameters().data_block() );
107+
const auto paramsSize = transform->GetParameters().Size() * sizeof(ParametersValueType);
108+
setMemoryStoreOutputArray(0, index, dataCount, paramsAddress, paramsSize);
109+
++dataCount;
110+
}
111+
}
112+
else
113+
{
114+
const auto fixedParamsAddress = reinterpret_cast< size_t >( wasmTransform->GetTransform()->GetFixedParameters().data_block() );
115+
const auto fixedParamsSize = wasmTransform->GetTransform()->GetFixedParameters().Size() * sizeof(FixedParametersValueType);
116+
setMemoryStoreOutputArray(0, index, 0, fixedParamsAddress, fixedParamsSize);
117+
118+
const auto paramsAddress = reinterpret_cast< size_t >( wasmTransform->GetTransform()->GetParameters().data_block() );
119+
const auto paramsSize = wasmTransform->GetTransform()->GetParameters().Size() * sizeof(ParametersValueType);
120+
setMemoryStoreOutputArray(0, index, 1, paramsAddress, paramsSize);
121+
}
122+
}
123+
#else
124+
std::cerr << "Memory IO not supported" << std::endl;
125+
abort();
126+
#endif
127+
}
128+
else
129+
{
130+
#ifndef ITK_WASM_NO_FILESYSTEM_IO
131+
if (!this->m_Transform.IsNull() && !this->m_Identifier.empty())
132+
{
133+
using ParametersValueType = typename TransformType::ParametersValueType;
134+
using WriterType = TransformFileWriterTemplate<ParametersValueType>;
135+
auto writer = WriterType::New();
136+
writer->SetFileName(this->m_Identifier);
137+
writer->SetInput(this->m_Transform);
138+
writer->Update();
139+
}
140+
#else
141+
std::cerr << "Filesystem IO not supported" << std::endl;
142+
abort();
143+
#endif
144+
}
145+
}
146+
protected:
147+
typename TTransform::ConstPointer m_Transform;
148+
149+
std::string m_Identifier;
150+
};
151+
152+
template <typename TTransform>
153+
bool lexical_cast(const std::string &input, OutputTransform<TTransform> &outputTransform)
154+
{
155+
outputTransform.SetIdentifier(input);
156+
return true;
157+
}
158+
159+
} // namespace wasm
160+
} // namespace itk
161+
162+
#endif

include/itkTransformJSON.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ namespace itk
3030
{
3131
enum class JSONTransformParameterizationEnum
3232
{
33-
Identity,
3433
Composite,
34+
Identity,
3535
Translation,
3636
Euler2D,
3737
Euler3D,
@@ -139,13 +139,13 @@ auto transformListToTransformListJSON(std::list<typename TTransformBase::ConstPo
139139
}
140140
tokens.push_back(transformType.substr(start));
141141
const std::string pString = tokens[0];
142-
if (pString == "IdentityTransform")
142+
if (pString == "CompositeTransform")
143143
{
144-
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Identity;
144+
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Composite;
145145
}
146-
else if (pString == "CompositeTransform")
146+
else if (pString == "IdentityTransform")
147147
{
148-
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Composite;
148+
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Identity;
149149
}
150150
else if (pString == "TranslationTransform")
151151
{

include/itkTransformToWasmTransformFilter.hxx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818
#ifndef itkTransformToWasmTransformFilter_hxx
1919
#define itkTransformToWasmTransformFilter_hxx
2020

21-
#include "itkCompositeTransform.h"
22-
#include "itkCompositeTransformIOHelper.h"
23-
2421
#include "itkWasmMapComponentType.h"
2522
#include "itkWasmMapPixelType.h"
2623
#include "itkTransformJSON.h"

model/itk-wasm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,8 @@ enums:
638638
639639
A detailed description of each transform type can be found in the ITK Software Guide: https://itk.org/ITKSoftwareGuide/html/Book1/ITKSoftwareGuide-Book1ch4.html
640640
permissible_values:
641-
Identity:
642641
Composite:
642+
Identity:
643643
Translation:
644644
Euler2D:
645645
Euler3D:

src/itkWasmTransformIO.cxx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,13 @@ WasmTransformIOTemplate<TParametersValueType>::ReadCBOR()
170170
const std::string transformParameterization(
171171
reinterpret_cast<char *>(cbor_string_handle(transformTypeHandle[kk].value)),
172172
cbor_string_length(transformTypeHandle[kk].value));
173-
if (transformParameterization == "Identity")
173+
if (transformParameterization == "Composite")
174174
{
175-
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Identity;
175+
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Composite;
176176
}
177-
else if (transformParameterization == "Composite")
177+
else if (transformParameterization == "Identity")
178178
{
179-
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Composite;
179+
transformJSON.transformType.transformParameterization = JSONTransformParameterizationEnum::Identity;
180180
}
181181
else if (transformParameterization == "Translation")
182182
{

src/itktransformParameterizationString.cxx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ transformParameterizationString(const TransformTypeJSON & json)
2626
std::string transformParameterization;
2727
switch (json.transformParameterization)
2828
{
29-
case JSONTransformParameterizationEnum::Identity:
29+
case JSONTransformParameterizationEnum::Composite:
3030
{
31-
transformParameterization = "Identity";
31+
transformParameterization = "Composite";
3232
break;
3333
}
34-
case JSONTransformParameterizationEnum::Composite:
34+
case JSONTransformParameterizationEnum::Identity:
3535
{
36-
transformParameterization = "Composite";
36+
transformParameterization = "Identity";
3737
break;
3838
}
3939
case JSONTransformParameterizationEnum::Translation:

test/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,9 @@ itk_add_test(NAME itkPipelineTest
215215
DATA{Input/cow.vtk}
216216
${ITK_TEST_OUTPUT_DIR}/itkPipelineTestOutputPolyData.vtk
217217
DATA{Input/LinearTransform.h5}
218-
# ${ITK_TEST_OUTPUT_DIR}/itkPipelineTestOutputTransform.h5
218+
${ITK_TEST_OUTPUT_DIR}/itkPipelineTestOutputTransform.h5
219+
DATA{Input/CompositeTransform.h5}
220+
${ITK_TEST_OUTPUT_DIR}/itkPipelineTestOutputCompositeTransform.h5
219221
)
220222

221223
itk_add_test(NAME itkPipelineMemoryIOTest
@@ -232,6 +234,10 @@ itk_add_test(NAME itkPipelineMemoryIOTest
232234
${ITK_TEST_OUTPUT_DIR}/itkPipelineMemoryIOTestOutputMesh.vtk
233235
DATA{Input/cow.vtk}
234236
${ITK_TEST_OUTPUT_DIR}/itkPipelineMemoryIOTestOutputPolyData.vtk
237+
DATA{Input/LinearTransform.h5}
238+
${ITK_TEST_OUTPUT_DIR}/itkPipelineMemoryIOTestOutputTransform.h5
239+
DATA{Input/CompositeTransform.h5}
240+
${ITK_TEST_OUTPUT_DIR}/itkPipelineMemoryIOTestOutputCompositeTransform.h5
235241
)
236242

237243
itk_add_test(NAME itkPipelineInterfaceJSONTest

test/itkPipelineMemoryIOTest.cxx

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,25 @@
3737
#include "itkPolyData.h"
3838
#include "itkPolyDataToWasmPolyDataFilter.h"
3939
#include "itkOutputPolyData.h"
40+
#include "itkTransformToWasmTransformFilter.h"
41+
#include "itkAffineTransform.h"
42+
#include "itkCompositeTransform.h"
43+
#include "itkOutputTransform.h"
44+
#include "itkHDF5TransformIOFactory.h"
45+
#include "itkInputTransform.h"
4046

4147
#include "itkImageFileReader.h"
4248
#include "itkMeshFileReader.h"
49+
#include "itkTransformFileReader.h"
50+
#include "itkHDF5TransformIOFactory.h"
4351
#include "itkMeshToPolyDataFilter.h"
4452
#include "itkPolyDataToMeshFilter.h"
4553

4654
int
4755
itkPipelineMemoryIOTest(int argc, char * argv[])
4856
{
57+
itk::HDF5TransformIOFactory::RegisterOneFactory();
58+
4959
constexpr unsigned int Dimension = 2;
5060
using PixelType = float;
5161
using ImageType = itk::Image<PixelType, Dimension>;
@@ -128,8 +138,62 @@ itkPipelineMemoryIOTest(int argc, char * argv[])
128138
void * readWasmPolyDataPointer = reinterpret_cast< void * >( itk_wasm_input_json_alloc(0, 4, readPolyDataJSON.size()));
129139
std::memcpy(readWasmPolyDataPointer, readPolyDataJSON.data(), readPolyDataJSON.size());
130140

131-
const char * mockArgv[] = {"itkPipelineMemoryIOTest", "--memory-io", "0", "0", "1", "1", "2", "2", "3", "3", "4", "4", NULL};
132-
itk::wasm::Pipeline pipeline("pipeline-test", "A test ITK Wasm Pipeline", 12, const_cast< char ** >(mockArgv));
141+
const char * inputTransformFile = argv[12];
142+
using TransformType = itk::AffineTransform<double, 3>;
143+
using ParametersValueType = typename TransformType::ParametersValueType;
144+
using TransformReaderType = itk::TransformFileReaderTemplate<ParametersValueType>;
145+
auto transformReader = TransformReaderType::New();
146+
transformReader->SetFileName(inputTransformFile);
147+
transformReader->Update();
148+
auto inputTransformList = transformReader->GetTransformList();
149+
auto readInputTransform = dynamic_cast<const TransformType *>(inputTransformList->front().GetPointer());
150+
using TransformToWasmTransformFilterType = itk::TransformToWasmTransformFilter<TransformType>;
151+
auto transformToWasmTransformFilter = TransformToWasmTransformFilterType::New();
152+
transformToWasmTransformFilter->SetTransform(readInputTransform);
153+
transformToWasmTransformFilter->Update();
154+
auto readWasmTransform = transformToWasmTransformFilter->GetOutput();
155+
156+
auto readTransformJSON = readWasmTransform->GetJSON();
157+
void * readWasmTransformPointer = reinterpret_cast< void * >( itk_wasm_input_json_alloc(0, 5, readTransformJSON.size()));
158+
std::memcpy(readWasmTransformPointer, readTransformJSON.data(), readTransformJSON.size());
159+
160+
// auto readWasmTransformFixedParams = reinterpret_cast< const void * >(readWasmTransform->GetTransform()->GetFixedParameters().data_block());
161+
// const auto readWasmTransformFixedParamsSize = readWasmTransform->GetTransform()->GetFixedParameters().Size();
162+
// const size_t readWasmTransformFixedParamsAddress = itk_wasm_input_array_alloc(0, 0, 0, readWasmTransformFixedParamsSize);
163+
// auto readWasmTransformFixedParamsPointer = reinterpret_cast< void * >(readWasmTransformFixedParamsAddress);
164+
// std::memcpy(readWasmTransformFixedParamsPointer, readWasmTransformFixedParams, readWasmTransformFixedParamsSize);
165+
166+
// auto direction = reinterpret_cast< const void * >( readWasmTransform->GetTransform()->GetDirection().GetVnlMatrix().begin() );
167+
// const auto directionSize = readWasmTransform->GetTransform()->GetDirection().GetVnlMatrix().size() * sizeof(double);
168+
// const size_t readWasmTransformDirectionPointerAddress = itk_wasm_array_alloc(0, 0, 1, directionSize);
169+
// auto readWasmTransformDirectionPointer = reinterpret_cast< void * >(readWasmTransformDirectionPointerAddress);
170+
// std::memcpy(readWasmTransformDirectionPointer, direction, directionSize);
171+
172+
// auto readTransformJSON = readWasmTransform->GetJSON();
173+
// void * readWasmTransformPointer = reinterpret_cast< void * >( itk_wasm_input_json_alloc(0, 0, readTransformJSON.size()));
174+
// std::memcpy(readWasmTransformPointer, readTransformJSON.data(), readTransformJSON.size());
175+
176+
const char * inputCompositeTransformFile = argv[14];
177+
using CompositeTransformType = itk::CompositeTransform<double, 2>;
178+
using CompositeParametersValueType = typename CompositeTransformType::ParametersValueType;
179+
using CompositeTransformReaderType = itk::TransformFileReaderTemplate<CompositeParametersValueType>;
180+
auto compositeTransformReader = CompositeTransformReaderType::New();
181+
compositeTransformReader->SetFileName(inputCompositeTransformFile);
182+
compositeTransformReader->Update();
183+
auto inputCompositeTransformList = compositeTransformReader->GetTransformList();
184+
auto readInputCompositeTransform = dynamic_cast<const CompositeTransformType *>(inputCompositeTransformList->front().GetPointer());
185+
using CompositeTransformToWasmTransformFilterType = itk::TransformToWasmTransformFilter<CompositeTransformType>;
186+
auto compositeTransformToWasmCompositeTransformFilter = CompositeTransformToWasmTransformFilterType::New();
187+
compositeTransformToWasmCompositeTransformFilter->SetTransform(readInputCompositeTransform);
188+
compositeTransformToWasmCompositeTransformFilter->Update();
189+
auto readWasmCompositeTransform = compositeTransformToWasmCompositeTransformFilter->GetOutput();
190+
191+
auto readCompositeTransformJSON = readWasmCompositeTransform->GetJSON();
192+
void * readWasmCompositeTransformPointer = reinterpret_cast< void * >( itk_wasm_input_json_alloc(0, 6, readCompositeTransformJSON.size()));
193+
std::memcpy(readWasmCompositeTransformPointer, readCompositeTransformJSON.data(), readCompositeTransformJSON.size());
194+
195+
const char * mockArgv[] = {"itkPipelineMemoryIOTest", "--memory-io", "0", "0", "1", "1", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", NULL};
196+
itk::wasm::Pipeline pipeline("pipeline-test", "A test ITK Wasm Pipeline", 16, const_cast< char ** >(mockArgv));
133197

134198
std::string example_string_option = "default";
135199
pipeline.add_option("-s,--string", example_string_option, "A help string");
@@ -182,6 +246,22 @@ itkPipelineMemoryIOTest(int argc, char * argv[])
182246
OutputPolyDataType outputPolyData;
183247
pipeline.add_option("output-polydata", outputPolyData, "The output polydata")->required()->type_name("OUTPUT_POLYDATA");
184248

249+
using InputTransformType = itk::wasm::InputTransform<TransformType>;
250+
InputTransformType inputTransform;
251+
pipeline.add_option("input-transform", inputTransform, "The input transform")->required()->type_name("INPUT_TRANSFORM");
252+
253+
using OutputTransformType = itk::wasm::OutputTransform<TransformType>;
254+
OutputTransformType outputTransform;
255+
pipeline.add_option("output-transform", outputTransform, "The output transform")->required()->type_name("OUTPUT_TRANSFORM");
256+
257+
using InputCompositeTransformType = itk::wasm::InputTransform<CompositeTransformType>;
258+
InputCompositeTransformType inputCompositeTransform;
259+
pipeline.add_option("input-composite-transform", inputCompositeTransform, "The input composite transform")->required()->type_name("INPUT_TRANSFORM");
260+
261+
using OutputCompositeTransformType = itk::wasm::OutputTransform<CompositeTransformType>;
262+
OutputCompositeTransformType outputCompositeTransform;
263+
pipeline.add_option("output-composite-transform", outputCompositeTransform, "The output composite transform")->required()->type_name("OUTPUT_TRANSFORM");
264+
185265
ITK_WASM_PARSE(pipeline);
186266

187267
outputImage.Set(inputImage.Get());
@@ -202,5 +282,8 @@ itkPipelineMemoryIOTest(int argc, char * argv[])
202282

203283
outputPolyData.Set(inputPolyData.Get());
204284

285+
outputTransform.Set(inputTransform.Get());
286+
outputCompositeTransform.Set(inputCompositeTransform.Get());
287+
205288
return EXIT_SUCCESS;
206289
}

0 commit comments

Comments
 (0)