25
25
#include < vector>
26
26
27
27
#include " itkPoint.h"
28
+ #include " itkSize.h"
29
+
30
+ // move to .hxx file
31
+ #include " double-conversion/double-conversion.h"
32
+
33
+ #include < cassert>
34
+ #include < fstream>
35
+ #include < limits>
36
+ #include < sstream>
28
37
29
38
namespace itk
30
39
{
31
40
template <unsigned Dimension>
32
- struct Tile
41
+ struct ITK_TEMPLATE_EXPORT Tile
33
42
{
34
43
using PointType = Point <double , Dimension>;
35
44
@@ -38,6 +47,290 @@ struct Tile
38
47
std::string FileName;
39
48
};
40
49
50
+
51
+ template <unsigned Dimension>
52
+ class ITK_TEMPLATE_EXPORT TileConfiguration
53
+ {
54
+ public:
55
+ using PointType = typename Tile<Dimension>::PointType;
56
+ using TileIndexType = Size <Dimension>;
57
+ using TileND = Tile<Dimension>;
58
+
59
+ TileIndexType AxisSizes;
60
+
61
+ std::vector<TileND> Tiles;
62
+
63
+ size_t
64
+ LinearSize () const
65
+ {
66
+ size_t linearSize = 1u ;
67
+ for (unsigned d = 0 ; d < Dimension; d++)
68
+ {
69
+ linearSize *= AxisSizes[d];
70
+ }
71
+ return linearSize;
72
+ }
73
+
74
+ size_t
75
+ nDIndexToLinearIndex (TileIndexType nDIndex) const
76
+ {
77
+ size_t ind = 0 ;
78
+ SizeValueType stride = 1u ;
79
+ for (unsigned d = 0 ; d < Dimension; d++)
80
+ {
81
+ itkAssertOrThrowMacro (nDIndex[d] < AxisSizes[d],
82
+ " Tile index " << nDIndex << " exceeds axis size " << AxisSizes << " at dimension " << d);
83
+ ind += nDIndex[d] * stride;
84
+ stride *= AxisSizes[d];
85
+ }
86
+ return ind;
87
+ }
88
+
89
+ TileIndexType
90
+ LinearIndexToNDIndex (size_t linearIndex) const
91
+ {
92
+ TileIndexType ind;
93
+ SizeValueType stride = 1u ;
94
+ for (unsigned d = 0 ; d < Dimension; d++)
95
+ {
96
+ stride *= AxisSizes[d];
97
+ ind[d] = linearIndex % AxisSizes[d];
98
+ linearIndex /= AxisSizes[d];
99
+ }
100
+ itkAssertOrThrowMacro (linearIndex < stride,
101
+ " Linear tile index " << linearIndex << " exceeds total montage size " << stride);
102
+ return ind;
103
+ }
104
+
105
+ // tries parsing the file, return first file name and set dimension
106
+ static std::string
107
+ TryParse (const std::string & pathToFile, unsigned & dimension)
108
+ {
109
+ std::ifstream tileFile (pathToFile);
110
+ if (!tileFile)
111
+ {
112
+ throw std::runtime_error (" Could not open for reading: " + pathToFile);
113
+ }
114
+
115
+ std::string temp = getNextNonCommentLine (tileFile);
116
+ if (temp.substr (0 , 6 ) == " dim = " )
117
+ {
118
+ dimension = std::stoul (temp.substr (6 ));
119
+ temp = TileConfiguration<Dimension>::getNextNonCommentLine (tileFile); // get next line
120
+ }
121
+
122
+ std::string timePointID;
123
+ Tile<Dimension> tile = parseLine (temp, timePointID);
124
+ return tile.FileName ;
125
+ }
126
+
127
+ void
128
+ Parse (const std::string & pathToFile)
129
+ {
130
+ std::ifstream tileFile (pathToFile);
131
+ if (!tileFile)
132
+ {
133
+ throw std::runtime_error (" Could not open for reading: " + pathToFile);
134
+ }
135
+ std::string line = getNextNonCommentLine (tileFile);
136
+ if (line.substr (0 , 6 ) == " dim = " )
137
+ {
138
+ unsigned dim = std::stoul (line.substr (6 ));
139
+ if (dim != Dimension)
140
+ {
141
+ throw std::runtime_error (" Expected dimension " + std::to_string (Dimension) + " , but got " +
142
+ std::to_string (dim) + " from string:\n\n " + line);
143
+ }
144
+ line = TileConfiguration<Dimension>::getNextNonCommentLine (tileFile); // get next line
145
+ }
146
+
147
+ AxisSizes.Fill (1 );
148
+ Tiles.clear ();
149
+ TileIndexType cInd;
150
+ cInd.Fill (0 );
151
+ unsigned initializedDimensions = 0 ; // no dimension has been initialized
152
+
153
+ std::string timePoint;
154
+ itk::Tile<Dimension> tile = parseLine (line, timePoint);
155
+ Tiles.push_back (tile);
156
+ line = getNextNonCommentLine (tileFile);
157
+
158
+ while (tileFile)
159
+ {
160
+ tile = parseLine (line, timePoint);
161
+ // determine dominant axis change
162
+ unsigned maxAxis = 0 ; // (0=x, 1=y, 2=z etc)
163
+ double maxDiff = tile.Position [0 ] - Tiles.back ().Position [0 ];
164
+ for (unsigned d = 1 ; d < Dimension; d++)
165
+ {
166
+ double diff = tile.Position [d] - Tiles.back ().Position [d];
167
+ if (diff > maxDiff)
168
+ {
169
+ maxDiff = diff;
170
+ maxAxis = d;
171
+ }
172
+ }
173
+
174
+ if (maxAxis > initializedDimensions) // we now know the size along this dimension
175
+ {
176
+ AxisSizes[maxAxis - 1 ] = cInd[maxAxis - 1 ] + 1 ;
177
+ initializedDimensions = maxAxis;
178
+ }
179
+
180
+ // check consistency with previously established size
181
+ for (unsigned d = 0 ; d < maxAxis; d++)
182
+ {
183
+ SizeValueType axisSize = (d == maxAxis - 1 ) ? AxisSizes[d] : AxisSizes[d] - 1 ;
184
+ itkAssertOrThrowMacro (cInd[d] == AxisSizes[d] - 1 ,
185
+ " Axis sizes: " << AxisSizes << " current index: " << cInd
186
+ << " . We have reached the end along axis " << maxAxis
187
+ << " \n Index along axis " << d << " is " << cInd[d] << " , but it should be "
188
+ << AxisSizes[d] - 1 );
189
+ }
190
+
191
+ // update current tile index
192
+ for (unsigned d = 0 ; d < maxAxis; d++)
193
+ {
194
+ cInd[d] = 0 ;
195
+ }
196
+ ++cInd[maxAxis];
197
+
198
+
199
+ if (maxAxis < initializedDimensions) // check bounds, if bounds are established
200
+ {
201
+ itkAssertOrThrowMacro (cInd[maxAxis] < AxisSizes[maxAxis],
202
+ " Axis sizes: " << AxisSizes << " , but we reached index " << cInd[maxAxis]
203
+ << " . Violation along axis " << maxAxis);
204
+ }
205
+
206
+ Tiles.push_back (tile);
207
+ line = getNextNonCommentLine (tileFile);
208
+ }
209
+ AxisSizes[Dimension - 1 ] = cInd[Dimension - 1 ] + 1 ;
210
+
211
+ size_t expectedSize = this ->LinearSize ();
212
+ itkAssertOrThrowMacro (expectedSize == Tiles.size (),
213
+ " Incorrect number of tiles: " << Tiles.size () << " . Expected: " << expectedSize);
214
+ }
215
+
216
+ void
217
+ Write (const std::string & pathToFile)
218
+ {
219
+ std::ofstream tileFile (pathToFile);
220
+ if (!tileFile)
221
+ {
222
+ throw std::runtime_error (" Could not open for writing: " + pathToFile);
223
+ }
224
+
225
+ tileFile << " # Tile coordinates are in index space, not physical space\n " ;
226
+ tileFile << " dim = " << Dimension << " \n\n " ;
227
+ char buffer[20 ];
228
+ double_conversion::StringBuilder conversionResult (buffer, 20 );
229
+
230
+ size_t totalTiles = this ->LinearSize ();
231
+ for (SizeValueType linearIndex = 0 ; linearIndex < totalTiles; linearIndex++)
232
+ {
233
+ TileIndexType ind = this ->LinearIndexToNDIndex (linearIndex);
234
+ tileFile << Tiles[linearIndex].FileName << " ;;(" ;
235
+
236
+ for (unsigned d = 0 ; d < Dimension; d++)
237
+ {
238
+ if (d > 0 )
239
+ {
240
+ tileFile << " , " ;
241
+ }
242
+
243
+ doubleConverter.ToShortest (Tiles[linearIndex].Position [d], &conversionResult);
244
+ tileFile << conversionResult.Finalize ();
245
+ conversionResult.Reset ();
246
+ }
247
+ tileFile << ' )' << std::endl;
248
+ }
249
+
250
+ if (!tileFile)
251
+ {
252
+ throw std::runtime_error (" Writing not successful to: " + pathToFile);
253
+ }
254
+ }
255
+
256
+ protected:
257
+ static double_conversion::StringToDoubleConverter stringConverter;
258
+ static double_conversion::DoubleToStringConverter doubleConverter;
259
+
260
+ static std::string
261
+ getNextNonCommentLine (std::istream & in)
262
+ {
263
+ std::string temp;
264
+ while (std::getline (in, temp))
265
+ {
266
+ if (temp.empty () || temp[0 ] == ' #' )
267
+ {
268
+ continue ; // this is either an empty line or a comment
269
+ }
270
+ if (temp.size () == 1 && temp[0 ] == ' \r ' )
271
+ {
272
+ continue ; // empty line ending in CRLF
273
+ }
274
+ if (temp[temp.size () - 1 ] == ' \r ' )
275
+ {
276
+ temp.erase (temp.size () - 1 , 1 );
277
+ }
278
+ break ; // temp has interesting content
279
+ }
280
+ return temp;
281
+ }
282
+
283
+ static Tile<Dimension>
284
+ parseLine (const std::string line, std::string & timePointID)
285
+ {
286
+ itk::Tile<Dimension> tile;
287
+ std::stringstream ss (line);
288
+ std::string temp;
289
+
290
+ std::getline (ss, temp, ' ;' );
291
+ tile.FileName = temp;
292
+ std::getline (ss, temp, ' ;' );
293
+ if (timePointID.empty ())
294
+ {
295
+ timePointID = temp;
296
+ }
297
+ else
298
+ {
299
+ itkAssertOrThrowMacro (temp == timePointID,
300
+ " Only a single time point is supported. " << timePointID << " != " << temp);
301
+ }
302
+ std::getline (ss, temp, ' (' );
303
+
304
+ using PointType = itk::Point <double , Dimension>;
305
+ PointType p;
306
+ for (unsigned d = 0 ; d < Dimension; d++)
307
+ {
308
+ std::getline (ss, temp, ' ,' );
309
+ int processed = 0 ;
310
+ p[d] = stringConverter.StringToDouble (temp.c_str (), temp.length (), &processed);
311
+ }
312
+ tile.Position = p;
313
+
314
+ return tile;
315
+ }
316
+ };
317
+
318
+ template <unsigned Dimension>
319
+ double_conversion::StringToDoubleConverter TileConfiguration<Dimension>::stringConverter(
320
+ double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK |
321
+ double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
322
+ double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES,
323
+ 0.0 ,
324
+ std::numeric_limits<double >::quiet_NaN(),
325
+ nullptr ,
326
+ nullptr );
327
+
328
+ template <unsigned Dimension>
329
+ double_conversion::DoubleToStringConverter TileConfiguration<
330
+ Dimension>::doubleConverter(double_conversion::DoubleToStringConverter::NO_FLAGS, nullptr , nullptr , ' e' , 0 , 17 , 1 , 0 );
331
+
332
+ // add #ifdef legacy
333
+
41
334
using Tile2D = Tile<2 >;
42
335
using TileRow2D = std::vector<Tile2D>;
43
336
using TileLayout2D = std::vector<TileRow2D>;
0 commit comments