Mercurial > hg > orthanc-stone
comparison Framework/Volumes/VolumeReslicer.cpp @ 183:98da3a8d4820 wasm
SubvoxelReader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 15 Mar 2018 15:19:24 +0100 |
parents | 4da803580da9 |
children | 9523ce4f44cc |
comparison
equal
deleted
inserted
replaced
182:2cbfb08f3a95 | 183:98da3a8d4820 |
---|---|
1 #include "VolumeReslicer.h" | 1 #include "VolumeReslicer.h" |
2 | 2 |
3 #include "../Toolbox/GeometryToolbox.h" | 3 #include "../Toolbox/GeometryToolbox.h" |
4 #include "../Toolbox/SubvoxelReader.h" | |
4 | 5 |
5 #include <Core/Images/ImageTraits.h> | 6 #include <Core/Images/ImageTraits.h> |
6 #include <Core/Logging.h> | 7 #include <Core/Logging.h> |
7 #include <Core/OrthancException.h> | 8 #include <Core/OrthancException.h> |
8 | 9 |
110 ORTHANC_FORCE_INLINE | 111 ORTHANC_FORCE_INLINE |
111 void SetValue(OutputPixelType* pixel, | 112 void SetValue(OutputPixelType* pixel, |
112 const InputPixelType& value) const | 113 const InputPixelType& value) const |
113 { | 114 { |
114 SetValueInternal<InputPixelType>(pixel, value); | 115 SetValueInternal<InputPixelType>(pixel, value); |
115 } | |
116 }; | |
117 | |
118 | |
119 | |
120 class VoxelReaderBase : public boost::noncopyable | |
121 { | |
122 private: | |
123 const Orthanc::ImageAccessor& image_; | |
124 unsigned int width_; | |
125 unsigned int height_; | |
126 unsigned int depth_; | |
127 float widthFloat_; | |
128 float heightFloat_; | |
129 float depthFloat_; | |
130 | |
131 public: | |
132 VoxelReaderBase(const ImageBuffer3D& image) : | |
133 image_(image.GetInternalImage()), | |
134 width_(image.GetWidth()), | |
135 height_(image.GetHeight()), | |
136 depth_(image.GetDepth()), | |
137 widthFloat_(static_cast<float>(image.GetWidth())), | |
138 heightFloat_(static_cast<float>(image.GetHeight())), | |
139 depthFloat_(static_cast<float>(image.GetDepth())) | |
140 { | |
141 } | |
142 | |
143 const Orthanc::ImageAccessor& GetImage() const | |
144 { | |
145 return image_; | |
146 } | |
147 | |
148 const unsigned int GetImageWidth() const | |
149 { | |
150 return width_; | |
151 } | |
152 | |
153 const unsigned int GetImageHeight() const | |
154 { | |
155 return height_; | |
156 } | |
157 | |
158 const unsigned int GetImageDepth() const | |
159 { | |
160 return depth_; | |
161 } | |
162 | |
163 bool GetNearestCoordinates(unsigned int& imageX, | |
164 unsigned int& imageY, | |
165 unsigned int& imageZ, | |
166 float& fractionalX, | |
167 float& fractionalY, | |
168 float& fractionalZ, | |
169 float volumeX, | |
170 float volumeY, | |
171 float volumeZ) const | |
172 { | |
173 if (volumeX >= 0 && | |
174 volumeY >= 0 && | |
175 volumeZ >= 0) | |
176 { | |
177 const float x = volumeX * widthFloat_; | |
178 const float y = volumeY * heightFloat_; | |
179 const float z = volumeZ * depthFloat_; | |
180 | |
181 imageX = static_cast<unsigned int>(std::floor(x)); | |
182 imageY = static_cast<unsigned int>(std::floor(y)); | |
183 imageZ = static_cast<unsigned int>(std::floor(z)); | |
184 | |
185 if (imageX < width_ && | |
186 imageY < height_ && | |
187 imageZ < depth_) | |
188 { | |
189 fractionalX = x - static_cast<float>(imageX); | |
190 fractionalY = y - static_cast<float>(imageY); | |
191 fractionalZ = z - static_cast<float>(imageZ); | |
192 return true; | |
193 } | |
194 else | |
195 { | |
196 return false; | |
197 } | |
198 } | |
199 else | |
200 { | |
201 return false; | |
202 } | |
203 } | |
204 }; | |
205 | |
206 | |
207 template <Orthanc::PixelFormat InputFormat, | |
208 ImageInterpolation Interpolation> | |
209 class VoxelReader; | |
210 | |
211 | |
212 template <Orthanc::PixelFormat InputFormat> | |
213 class VoxelReader<InputFormat, ImageInterpolation_Nearest> : | |
214 public VoxelReaderBase | |
215 { | |
216 public: | |
217 typedef typename Orthanc::PixelTraits<InputFormat>::PixelType InputPixelType; | |
218 | |
219 VoxelReader(const ImageBuffer3D& image) : | |
220 VoxelReaderBase(image) | |
221 { | |
222 } | |
223 | |
224 ORTHANC_FORCE_INLINE | |
225 float GetFloatValue(float volumeX, | |
226 float volumeY, | |
227 float volumeZ) const | |
228 { | |
229 InputPixelType value; | |
230 GetValue(value, volumeX, volumeY, volumeZ); | |
231 return static_cast<float>(value); | |
232 } | |
233 | |
234 ORTHANC_FORCE_INLINE | |
235 void GetValue(InputPixelType& target, | |
236 float volumeX, | |
237 float volumeY, | |
238 float volumeZ) const | |
239 { | |
240 unsigned int imageX, imageY, imageZ; | |
241 float fractionalX, fractionalY, fractionalZ; // unused | |
242 | |
243 if (GetNearestCoordinates(imageX, imageY, imageZ, | |
244 fractionalX, fractionalY, fractionalZ, | |
245 volumeX, volumeY, volumeZ)) | |
246 { | |
247 Orthanc::ImageTraits<InputFormat>::GetPixel(target, GetImage(), imageX, | |
248 imageY + imageZ * GetImageHeight()); | |
249 } | |
250 else | |
251 { | |
252 target = std::numeric_limits<InputPixelType>::min(); | |
253 } | |
254 } | |
255 }; | |
256 | |
257 | |
258 template <Orthanc::PixelFormat InputFormat> | |
259 class VoxelReader<InputFormat, ImageInterpolation_Bilinear> : | |
260 public VoxelReaderBase | |
261 { | |
262 private: | |
263 float outOfVolume_; | |
264 | |
265 public: | |
266 VoxelReader(const ImageBuffer3D& image) : | |
267 VoxelReaderBase(image) | |
268 { | |
269 typedef typename Orthanc::PixelTraits<InputFormat>::PixelType Pixel; | |
270 outOfVolume_ = static_cast<float>(std::numeric_limits<Pixel>::min()); | |
271 } | |
272 | |
273 void SampleVoxels(float& f00, | |
274 float& f01, | |
275 float& f10, | |
276 float& f11, | |
277 unsigned int imageX, | |
278 unsigned int imageY, | |
279 unsigned int imageZ) const | |
280 { | |
281 f00 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel | |
282 (GetImage(), imageX, imageY + imageZ * GetImageHeight()); | |
283 | |
284 if (imageX + 1 < GetImageWidth()) | |
285 { | |
286 f01 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel | |
287 (GetImage(), imageX + 1, imageY + imageZ * GetImageHeight()); | |
288 } | |
289 else | |
290 { | |
291 f01 = f00; | |
292 } | |
293 | |
294 if (imageY + 1 < GetImageWidth()) | |
295 { | |
296 f10 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel | |
297 (GetImage(), imageX, imageY + 1 + imageZ * GetImageHeight()); | |
298 } | |
299 else | |
300 { | |
301 f10 = f00; | |
302 } | |
303 | |
304 if (imageX + 1 < GetImageWidth() && | |
305 imageY + 1 < GetImageHeight()) | |
306 { | |
307 f11 = Orthanc::ImageTraits<InputFormat>::GetFloatPixel | |
308 (GetImage(), imageX + 1, imageY + 1 + imageZ * GetImageHeight()); | |
309 } | |
310 else | |
311 { | |
312 f11 = f00; | |
313 } | |
314 } | |
315 | |
316 float GetOutOfVolume() const | |
317 { | |
318 return outOfVolume_; | |
319 } | |
320 | |
321 float GetFloatValue(float volumeX, | |
322 float volumeY, | |
323 float volumeZ) const | |
324 { | |
325 unsigned int imageX, imageY, imageZ; | |
326 float fractionalX, fractionalY, fractionalZ; | |
327 | |
328 if (GetNearestCoordinates(imageX, imageY, imageZ, | |
329 fractionalX, fractionalY, fractionalZ, | |
330 volumeX, volumeY, volumeZ)) | |
331 { | |
332 float f00, f01, f10, f11; | |
333 SampleVoxels(f00, f01, f10, f11, imageX, imageY, imageZ); | |
334 return GeometryToolbox::ComputeBilinearInterpolationUnitSquare | |
335 (fractionalX, fractionalY, f00, f01, f10, f11); | |
336 } | |
337 else | |
338 { | |
339 return outOfVolume_; | |
340 } | |
341 } | |
342 }; | |
343 | |
344 | |
345 template <Orthanc::PixelFormat InputFormat> | |
346 class VoxelReader<InputFormat, ImageInterpolation_Trilinear> : | |
347 public VoxelReaderBase | |
348 { | |
349 private: | |
350 typedef VoxelReader<InputFormat, ImageInterpolation_Bilinear> Bilinear; | |
351 | |
352 Bilinear bilinear_; | |
353 unsigned int imageDepth_; | |
354 | |
355 public: | |
356 VoxelReader(const ImageBuffer3D& image) : | |
357 VoxelReaderBase(image), | |
358 bilinear_(image), | |
359 imageDepth_(image.GetDepth()) | |
360 { | |
361 } | |
362 | |
363 float GetFloatValue(float volumeX, | |
364 float volumeY, | |
365 float volumeZ) const | |
366 { | |
367 unsigned int imageX, imageY, imageZ; | |
368 float fractionalX, fractionalY, fractionalZ; | |
369 | |
370 if (GetNearestCoordinates(imageX, imageY, imageZ, | |
371 fractionalX, fractionalY, fractionalZ, | |
372 volumeX, volumeY, volumeZ)) | |
373 { | |
374 float f000, f001, f010, f011; | |
375 bilinear_.SampleVoxels(f000, f001, f010, f011, imageX, imageY, imageZ); | |
376 | |
377 if (imageZ + 1 < imageDepth_) | |
378 { | |
379 float f100, f101, f110, f111; | |
380 bilinear_.SampleVoxels(f100, f101, f110, f111, imageX, imageY, imageZ + 1); | |
381 return GeometryToolbox::ComputeTrilinearInterpolationUnitSquare | |
382 (fractionalX, fractionalY, fractionalZ, | |
383 f000, f001, f010, f011, f100, f101, f110, f111); | |
384 } | |
385 else | |
386 { | |
387 return GeometryToolbox::ComputeBilinearInterpolationUnitSquare | |
388 (fractionalX, fractionalY, f000, f001, f010, f011); | |
389 } | |
390 } | |
391 else | |
392 { | |
393 return bilinear_.GetOutOfVolume(); | |
394 } | |
395 } | 116 } |
396 }; | 117 }; |
397 | 118 |
398 | 119 |
399 template <typename VoxelReader, | 120 template <typename VoxelReader, |
422 void Apply(typename PixelWriter::OutputPixelType* pixel, | 143 void Apply(typename PixelWriter::OutputPixelType* pixel, |
423 float volumeX, | 144 float volumeX, |
424 float volumeY, | 145 float volumeY, |
425 float volumeZ) | 146 float volumeZ) |
426 { | 147 { |
427 typename VoxelReader::InputPixelType image; | 148 typename VoxelReader::PixelType value; |
428 reader_.GetValue(image, volumeX, volumeY, volumeZ); | 149 |
429 writer_.SetValue(pixel, image); | 150 if (!reader_.GetValue(value, volumeX, volumeY, volumeZ)) |
151 { | |
152 VoxelReader::Traits::SetMinValue(value); | |
153 } | |
154 | |
155 writer_.SetValue(pixel, value); | |
430 } | 156 } |
431 }; | 157 }; |
432 | 158 |
433 | 159 |
434 template <typename VoxelReader, | 160 template <typename VoxelReader, |
436 class PixelShader<VoxelReader, PixelWriter, TransferFunction_Float> | 162 class PixelShader<VoxelReader, PixelWriter, TransferFunction_Float> |
437 { | 163 { |
438 private: | 164 private: |
439 VoxelReader reader_; | 165 VoxelReader reader_; |
440 PixelWriter writer_; | 166 PixelWriter writer_; |
167 float outOfVolume_; | |
441 | 168 |
442 public: | 169 public: |
443 PixelShader(const ImageBuffer3D& image, | 170 PixelShader(const ImageBuffer3D& image, |
444 float /* scaling */, | 171 float /* scaling */, |
445 float /* offset */) : | 172 float /* offset */) : |
446 reader_(image) | 173 reader_(image), |
174 outOfVolume_(static_cast<float>(std::numeric_limits<typename VoxelReader::PixelType>::min())) | |
447 { | 175 { |
448 } | 176 } |
449 | 177 |
450 ORTHANC_FORCE_INLINE | 178 ORTHANC_FORCE_INLINE |
451 void Apply(typename PixelWriter::OutputPixelType* pixel, | 179 void Apply(typename PixelWriter::OutputPixelType* pixel, |
452 float volumeX, | 180 float volumeX, |
453 float volumeY, | 181 float volumeY, |
454 float volumeZ) | 182 float volumeZ) |
455 { | 183 { |
456 writer_.SetFloatValue(pixel, reader_.GetFloatValue(volumeX, volumeY, volumeZ)); | 184 float value; |
457 } | 185 |
186 if (!reader_.GetFloatValue(value, volumeX, volumeY, volumeZ)) | |
187 { | |
188 value = outOfVolume_; | |
189 } | |
190 | |
191 writer_.SetFloatValue(pixel, value); | |
192 } | |
458 }; | 193 }; |
459 | 194 |
460 | 195 |
461 template <typename VoxelReader, | 196 template <typename VoxelReader, |
462 typename PixelWriter> | 197 typename PixelWriter> |
465 private: | 200 private: |
466 VoxelReader reader_; | 201 VoxelReader reader_; |
467 PixelWriter writer_; | 202 PixelWriter writer_; |
468 float scaling_; | 203 float scaling_; |
469 float offset_; | 204 float offset_; |
205 float outOfVolume_; | |
470 | 206 |
471 public: | 207 public: |
472 PixelShader(const ImageBuffer3D& image, | 208 PixelShader(const ImageBuffer3D& image, |
473 float scaling, | 209 float scaling, |
474 float offset) : | 210 float offset) : |
475 reader_(image), | 211 reader_(image), |
476 scaling_(scaling), | 212 scaling_(scaling), |
477 offset_(offset) | 213 offset_(offset), |
214 outOfVolume_(static_cast<float>(std::numeric_limits<typename VoxelReader::PixelType>::min())) | |
478 { | 215 { |
479 } | 216 } |
480 | 217 |
481 ORTHANC_FORCE_INLINE | 218 ORTHANC_FORCE_INLINE |
482 void Apply(typename PixelWriter::OutputPixelType* pixel, | 219 void Apply(typename PixelWriter::OutputPixelType* pixel, |
483 float volumeX, | 220 float volumeX, |
484 float volumeY, | 221 float volumeY, |
485 float volumeZ) | 222 float volumeZ) |
486 { | 223 { |
487 writer_.SetFloatValue(pixel, scaling_ * reader_.GetFloatValue(volumeX, volumeY, volumeZ) + offset_); | 224 float value; |
225 | |
226 if (reader_.GetFloatValue(value, volumeX, volumeY, volumeZ)) | |
227 { | |
228 value = scaling_ * value + offset_; | |
229 } | |
230 else | |
231 { | |
232 value = outOfVolume_; | |
233 } | |
234 | |
235 writer_.SetFloatValue(pixel, value); | |
488 } | 236 } |
489 }; | 237 }; |
490 | 238 |
491 | 239 |
492 | 240 |
614 const CoordinateSystem3D& plane, | 362 const CoordinateSystem3D& plane, |
615 const OrientedBoundingBox& box, | 363 const OrientedBoundingBox& box, |
616 float scaling, | 364 float scaling, |
617 float offset) | 365 float offset) |
618 { | 366 { |
619 typedef VoxelReader<InputFormat, Interpolation> Reader; | 367 typedef SubvoxelReader<InputFormat, Interpolation> Reader; |
620 typedef PixelWriter<InputFormat, OutputFormat> Writer; | 368 typedef PixelWriter<InputFormat, OutputFormat> Writer; |
621 typedef PixelShader<Reader, Writer, Function> Shader; | 369 typedef PixelShader<Reader, Writer, Function> Shader; |
622 | 370 |
623 const unsigned int outputWidth = slice.GetWidth(); | 371 const unsigned int outputWidth = slice.GetWidth(); |
624 const unsigned int outputHeight = slice.GetHeight(); | 372 const unsigned int outputHeight = slice.GetHeight(); |
625 | 373 |
374 const float sourceWidth = static_cast<float>(source.GetWidth()); | |
375 const float sourceHeight = static_cast<float>(source.GetHeight()); | |
376 const float sourceDepth = static_cast<float>(source.GetDepth()); | |
377 | |
626 Shader shader(source, scaling, offset); | 378 Shader shader(source, scaling, offset); |
627 | 379 |
628 for (unsigned int y = 0; y < outputHeight; y++) | 380 for (unsigned int y = 0; y < outputHeight; y++) |
629 { | 381 { |
630 typename Writer::OutputPixelType* p = | 382 typename Writer::OutputPixelType* p = |
634 | 386 |
635 for (unsigned int x = 0; x < outputWidth; x++, p++) | 387 for (unsigned int x = 0; x < outputWidth; x++, p++) |
636 { | 388 { |
637 float volumeX, volumeY, volumeZ; | 389 float volumeX, volumeY, volumeZ; |
638 it.GetVolumeCoordinates(volumeX, volumeY, volumeZ); | 390 it.GetVolumeCoordinates(volumeX, volumeY, volumeZ); |
639 shader.Apply(p, volumeX, volumeY, volumeZ); | 391 |
392 shader.Apply(p, | |
393 volumeX * sourceWidth, | |
394 volumeY * sourceHeight, | |
395 volumeZ * sourceDepth); | |
640 it.Next(); | 396 it.Next(); |
641 } | 397 } |
642 } | 398 } |
643 } | 399 } |
644 | 400 |