Skip to content

Commit aec106b

Browse files
committed
ENH: Add transformix wasm pipeline
Resamples the input with the transform.
1 parent bb5c9b6 commit aec106b

File tree

6 files changed

+495
-117
lines changed

6 files changed

+495
-117
lines changed

wasm/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ include(${ITK_USE_FILE})
2828
add_executable(elastix elastix-wasm.cxx)
2929
target_link_libraries(elastix PUBLIC ${ITK_LIBRARIES})
3030

31+
add_executable(transformix transformix-wasm.cxx)
32+
target_link_libraries(transformix PUBLIC ${ITK_LIBRARIES})
33+
3134
add_executable(read-parameter-files read-parameter-files.cxx)
3235
target_link_libraries(read-parameter-files PUBLIC ${ITK_LIBRARIES})
3336

@@ -92,6 +95,24 @@ add_test(NAME elastix-wasm-2d-multiple-test
9295
-m ${CMAKE_CURRENT_SOURCE_DIR}/test/data/input/CT_2D_head_moving.iwi.cbor
9396
)
9497

98+
add_test(NAME transformix-wasm-test
99+
COMMAND transformix
100+
${CMAKE_CURRENT_SOURCE_DIR}/test/data/input/CT_3D_lung_moving.iwi.cbor
101+
${CMAKE_CURRENT_BINARY_DIR}/CT_3D_lung.json
102+
${CMAKE_CURRENT_BINARY_DIR}/CT_3D_lung_transformix.iwi.cbor
103+
)
104+
set_tests_properties(transformix-wasm-test PROPERTIES DEPENDS elastix-wasm-test)
105+
106+
# add_test(NAME elastix-wasm-2d-test
107+
# COMMAND elastix
108+
# ${CMAKE_CURRENT_SOURCE_DIR}/test/data/input/parameters_single.json
109+
# ${CMAKE_CURRENT_BINARY_DIR}/CT_2D_head_registered.iwi.cbor
110+
# ${CMAKE_CURRENT_BINARY_DIR}/CT_2D_head.h5
111+
# ${CMAKE_CURRENT_BINARY_DIR}/CT_2D_head.json
112+
# -f ${CMAKE_CURRENT_SOURCE_DIR}/test/data/input/CT_2D_head_fixed.iwi.cbor
113+
# -m ${CMAKE_CURRENT_SOURCE_DIR}/test/data/input/CT_2D_head_moving.iwi.cbor
114+
# )
115+
95116
add_test(NAME read-parameter-files-test
96117
COMMAND read-parameter-files
97118
${CMAKE_CURRENT_BINARY_DIR}/parameters_single.json

wasm/elastix-wasm.cxx

Lines changed: 5 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -36,66 +36,6 @@
3636
#include "itkElastixWasmParameterObject.h"
3737
#include "glaze/glaze.hpp"
3838

39-
std::string readParameterObject(const std::string & parameterObjectJson, elastix::ParameterObject * parameterObject)
40-
{
41-
using ParameterObjectType = elastix::ParameterObject;
42-
43-
itk::wasm::ParameterMapVector wasmParameterMaps;
44-
auto errorCode = glz::read_json<itk::wasm::ParameterMapVector>(wasmParameterMaps, parameterObjectJson);
45-
if (errorCode)
46-
{
47-
const std::string errorMessage = glz::format_error(errorCode, parameterObjectJson);
48-
return errorMessage;
49-
}
50-
51-
const auto numParameterMaps = wasmParameterMaps.size();
52-
ParameterObjectType::ParameterMapVectorType parameterMaps;
53-
parameterMaps.reserve(numParameterMaps);
54-
for (const auto wasmParameterMap : wasmParameterMaps)
55-
{
56-
ParameterObjectType::ParameterMapType parameterMap;
57-
for (const auto & parameter : wasmParameterMap)
58-
{
59-
ParameterObjectType::ParameterValueVectorType parameterValues;
60-
for (const auto & value : parameter.second)
61-
{
62-
if (value.index() == 0)
63-
{
64-
const auto & valueString = std::get<std::string>(value);
65-
parameterValues.push_back(valueString);
66-
}
67-
else if (value.index() == 1)
68-
{
69-
const auto & valueBool = std::get<bool>(value);
70-
if (valueBool)
71-
{
72-
parameterValues.push_back("true");
73-
}
74-
else
75-
{
76-
parameterValues.push_back("false");
77-
}
78-
}
79-
else if (value.index() == 2)
80-
{
81-
const auto & valueInt = std::get<int64_t>(value);
82-
parameterValues.push_back(std::to_string(valueInt));
83-
}
84-
else if (value.index() == 3)
85-
{
86-
const auto & valueDouble = std::get<double>(value);
87-
parameterValues.push_back(std::to_string(valueDouble));
88-
}
89-
}
90-
parameterMap[parameter.first] = parameterValues;
91-
}
92-
93-
parameterObject->AddParameterMap(parameterMap);
94-
}
95-
96-
return {};
97-
}
98-
9939
template <typename TImage>
10040
class PipelineFunctor
10141
{
@@ -174,7 +114,7 @@ class PipelineFunctor
174114
const auto parameterObject = ParameterObjectType::New();
175115
std::stringstream ss;
176116
ss << parameterObjectJson.Get().rdbuf();
177-
const std::string errorMessage = readParameterObject(ss.str(), parameterObject);
117+
const std::string errorMessage = itk::wasm::ReadParameterObject(ss.str(), parameterObject);
178118
if (!errorMessage.empty())
179119
{
180120
std::cerr << "Error reading parameter object JSON: " << errorMessage << std::endl;
@@ -198,7 +138,7 @@ class PipelineFunctor
198138
const auto initialTransformParameterObject = ParameterObjectType::New();
199139
std::stringstream ss;
200140
ss << initialTransformParameterObjectJson.Get().rdbuf();
201-
const std::string errorMessage = readParameterObject(ss.str(), initialTransformParameterObject);
141+
const std::string errorMessage = itk::wasm::ReadParameterObject(ss.str(), initialTransformParameterObject);
202142
if (!errorMessage.empty())
203143
{
204144
std::cerr << "Error reading transform parameter object JSON: " << errorMessage << std::endl;
@@ -247,35 +187,12 @@ class PipelineFunctor
247187
}
248188

249189
const auto transformParameterObject = registration->GetTransformParameterObject();
250-
itk::wasm::ParameterMapVector transformParameterMaps;
251-
252-
const auto numParameterMaps = parameterObject->GetNumberOfParameterMaps();
253-
for (unsigned int i = 0; i < numParameterMaps; ++i)
254-
{
255-
const auto & parameterMap = parameterObject->GetParameterMap(i);
256-
itk::wasm::ParameterMap wasmParameterMap;
257-
for (const auto & parameter : parameterMap)
258-
{
259-
const auto & parameterValueVector = parameter.second;
260-
261-
auto & wasmValues = wasmParameterMap[parameter.first];
262-
wasmValues.reserve(parameterValueVector.size());
263-
264-
// Convert each string into the variant
265-
for (const auto & val : parameterValueVector)
266-
{
267-
wasmValues.emplace_back(val);
268-
}
269-
}
270-
transformParameterMaps.push_back(wasmParameterMap);
271-
}
272190

273191
std::string serialized{};
274-
auto errorCode = glz::write<glz::opts{ .prettify = true }>(transformParameterMaps, serialized);
275-
if (errorCode)
192+
const std::string writeErrorMessage = itk::wasm::WriteParameterObject(transformParameterObject, serialized);
193+
if (!writeErrorMessage.empty())
276194
{
277-
const std::string errorMessage = glz::format_error(errorCode, serialized);
278-
std::cerr << "Error serializing parameter object: " << errorMessage << std::endl;
195+
std::cerr << "Error serializing parameter object: " << writeErrorMessage << std::endl;
279196
return EXIT_FAILURE;
280197
}
281198

wasm/itkElastixWasmParameterObject.h

Lines changed: 147 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,167 @@
1818
#ifndef itkElastixWasmParameterObject_h
1919
#define itkElastixWasmParameterObject_h
2020

21-
#include <map>
22-
#include <string>
23-
#include <variant>
24-
#include <vector>
21+
#include <map>
22+
#include <string>
23+
#include <variant>
24+
#include <vector>
2525

26+
#include "glaze/glaze.hpp"
27+
28+
#include "elxParameterObject.h"
29+
30+
#include "itkNumberToString.h"
2631
namespace itk
2732
{
2833
namespace wasm
2934
{
3035

3136
/**
32-
* @brief Type used to represent an elastix ParameterKey in ITK-Wasm pipelines
33-
* and for serializing elastix parameter files with glaze.
34-
*
35-
* This is the name of the parameter, e.g. "NumberOfResolutions"
36-
*/
37+
* @brief Type used to represent an elastix ParameterKey in ITK-Wasm pipelines
38+
* and for serializing elastix parameter files with glaze.
39+
*
40+
* This is the name of the parameter, e.g. "NumberOfResolutions"
41+
*/
3742
using ParameterKey = std::string;
3843

39-
/**
40-
* @brief Type used to represent an elastix ParameterValue in ITK-Wasm pipelines
41-
* and for serializing elastix parameter files with glaze.
42-
*
43-
* This is the type cast string held in a ParameterFile
44-
*/
44+
/**
45+
* @brief Type used to represent an elastix ParameterValue in ITK-Wasm pipelines
46+
* and for serializing elastix parameter files with glaze.
47+
*
48+
* This is the type cast string held in a ParameterFile
49+
*/
4550
using ParameterValue = std::variant<std::string, bool, int64_t, double>;
4651

4752
using ParameterValueVector = std::vector<ParameterValue>;
4853

49-
/**
50-
* @brief Type used to represent an elastix ParameterObject in ITK-Wasm pipelines
51-
* and for serializing elastix parameter files with glaze.
52-
*/
53-
using ParameterMap = std::map<ParameterKey, ParameterValueVector>;
54+
/**
55+
* @brief Type used to represent an elastix ParameterObject in ITK-Wasm pipelines
56+
* and for serializing elastix parameter files with glaze.
57+
*/
58+
using ParameterMap = std::map<ParameterKey, ParameterValueVector>;
59+
60+
/**
61+
* @brief Type used to represent a vector of elastix ParameterMaps in ITK-Wasm pipelines
62+
* and for serializing elastix parameter files with glaze.
63+
*/
64+
using ParameterMapVector = std::vector<ParameterMap>;
65+
66+
/**
67+
* @brief Read an itk::wasm::ParameterMapVector elastix parameter object JSON representation into an elastix::ParameterObject.
68+
*
69+
* @param parameterObjectJson JSON representation of the elastix parameter object
70+
* @param parameterObject Pointer to the elastix::ParameterObject to populate
71+
* @return std::string Error message if reading the parameter object fails, empty string otherwise.
72+
*/
73+
std::string ReadParameterObject(const std::string & parameterObjectJson, elastix::ParameterObject * parameterObject)
74+
{
75+
using ParameterObjectType = elastix::ParameterObject;
76+
77+
itk::wasm::ParameterMapVector wasmParameterMaps;
78+
auto errorCode = glz::read_json<itk::wasm::ParameterMapVector>(wasmParameterMaps, parameterObjectJson);
79+
if (errorCode)
80+
{
81+
const std::string errorMessage = glz::format_error(errorCode, parameterObjectJson);
82+
return errorMessage;
83+
}
84+
85+
const auto numParameterMaps = wasmParameterMaps.size();
86+
ParameterObjectType::ParameterMapVectorType parameterMaps;
87+
parameterMaps.reserve(numParameterMaps);
88+
for (const auto wasmParameterMap : wasmParameterMaps)
89+
{
90+
ParameterObjectType::ParameterMapType parameterMap;
91+
for (const auto & parameter : wasmParameterMap)
92+
{
93+
ParameterObjectType::ParameterValueVectorType parameterValues;
94+
for (const auto & value : parameter.second)
95+
{
96+
if (value.index() == 0)
97+
{
98+
const auto & valueString = std::get<std::string>(value);
99+
parameterValues.push_back(valueString);
100+
}
101+
else if (value.index() == 1)
102+
{
103+
const auto & valueBool = std::get<bool>(value);
104+
if (valueBool)
105+
{
106+
parameterValues.push_back("true");
107+
}
108+
else
109+
{
110+
parameterValues.push_back("false");
111+
}
112+
}
113+
else if (value.index() == 2)
114+
{
115+
const auto & valueInt = std::get<int64_t>(value);
116+
parameterValues.push_back(itk::ConvertNumberToString(valueInt));
117+
}
118+
else if (value.index() == 3)
119+
{
120+
const auto & valueDouble = std::get<double>(value);
121+
parameterValues.push_back(itk::ConvertNumberToString(valueDouble));
122+
}
123+
}
124+
parameterMap[parameter.first] = parameterValues;
125+
}
126+
127+
parameterObject->AddParameterMap(parameterMap);
128+
}
129+
130+
return {};
131+
}
132+
133+
/**
134+
* @brief Write an elastix::ParameterObject into an itk::wasm::ParameterMapVector JSON representation.
135+
*
136+
* @param parameterObject Pointer to the elastix::ParameterObject to serialize
137+
* @param parameterObjectJson JSON representation of the elastix parameter object (output)
138+
* @return std::string Error message if writing the parameter object fails, empty string otherwise.
139+
*/
140+
std::string WriteParameterObject(const elastix::ParameterObject * parameterObject, std::string & parameterObjectJson)
141+
{
142+
using ParameterObjectType = elastix::ParameterObject;
143+
144+
itk::wasm::ParameterMapVector wasmParameterMaps;
145+
146+
const auto numParameterMaps = parameterObject->GetNumberOfParameterMaps();
147+
wasmParameterMaps.reserve(numParameterMaps);
148+
149+
for (unsigned int i = 0; i < numParameterMaps; ++i)
150+
{
151+
const auto & parameterMap = parameterObject->GetParameterMap(i);
152+
itk::wasm::ParameterMap wasmParameterMap;
153+
154+
for (const auto & parameter : parameterMap)
155+
{
156+
const auto & parameterValueVector = parameter.second;
157+
auto & wasmValues = wasmParameterMap[parameter.first];
158+
wasmValues.reserve(parameterValueVector.size());
159+
160+
// Convert each string into the variant
161+
for (const auto & val : parameterValueVector)
162+
{
163+
wasmValues.emplace_back(val);
164+
}
165+
}
166+
wasmParameterMaps.push_back(wasmParameterMap);
167+
}
168+
169+
auto errorCode = glz::write<glz::opts{ .prettify = true }>(wasmParameterMaps, parameterObjectJson);
170+
if (errorCode)
171+
{
172+
const std::string errorMessage = glz::format_error(errorCode, parameterObjectJson);
173+
return errorMessage;
174+
}
175+
176+
return {};
177+
}
178+
54179

55-
/**
56-
* @brief Type used to represent a vector of elastix ParameterMaps in ITK-Wasm pipelines
57-
* and for serializing elastix parameter files with glaze.
58-
*/
59-
using ParameterMapVector = std::vector<ParameterMap>;
60180

61-
} // namespace wasm
62-
} // namespace itk
181+
} // namespace wasm
182+
} // namespace itk
63183

64184
#endif // itkElastixWasmParameterObject_h

0 commit comments

Comments
 (0)