Mercurial > hg > orthanc-stone
comparison Framework/Toolbox/FiniteProjectiveCamera.cpp @ 192:371da7fe2c0e wasm
FiniteProjectiveCamera::ApplyRaytracer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 16 Mar 2018 17:11:11 +0100 |
parents | 964118e7e6de |
children | e9c7a78a3e77 |
comparison
equal
deleted
inserted
replaced
191:46cb2eedc2e0 | 192:371da7fe2c0e |
---|---|
20 | 20 |
21 | 21 |
22 #include "FiniteProjectiveCamera.h" | 22 #include "FiniteProjectiveCamera.h" |
23 | 23 |
24 #include "GeometryToolbox.h" | 24 #include "GeometryToolbox.h" |
25 #include "SubpixelReader.h" | |
25 | 26 |
26 #include <Core/Logging.h> | 27 #include <Core/Logging.h> |
27 #include <Core/OrthancException.h> | 28 #include <Core/OrthancException.h> |
29 #include <Core/Images/Image.h> | |
30 #include <Core/Images/ImageProcessing.h> | |
28 | 31 |
29 namespace OrthancStone | 32 namespace OrthancStone |
30 { | 33 { |
31 void FiniteProjectiveCamera::ComputeMInverse() | 34 void FiniteProjectiveCamera::ComputeMInverse() |
32 { | 35 { |
286 { | 289 { |
287 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | 290 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); |
288 } | 291 } |
289 } | 292 } |
290 } | 293 } |
294 | |
295 | |
296 template <Orthanc::PixelFormat TargetFormat, | |
297 Orthanc::PixelFormat SourceFormat, | |
298 bool MIP> | |
299 static void ApplyRaytracerInternal(Orthanc::ImageAccessor& target, | |
300 const FiniteProjectiveCamera& camera, | |
301 const ImageBuffer3D& source, | |
302 VolumeProjection projection) | |
303 { | |
304 if (source.GetFormat() != SourceFormat || | |
305 target.GetFormat() != TargetFormat || | |
306 !std::numeric_limits<float>::is_iec559 || | |
307 sizeof(float) != 4) | |
308 { | |
309 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
310 } | |
311 | |
312 LOG(WARNING) << "Input volume size: " << source.GetWidth() << "x" | |
313 << source.GetHeight() << "x" << source.GetDepth(); | |
314 LOG(WARNING) << "Input pixel format: " << Orthanc::EnumerationToString(source.GetFormat()); | |
315 LOG(WARNING) << "Output image size: " << target.GetWidth() << "x" << target.GetHeight(); | |
316 LOG(WARNING) << "Output pixel format: " << Orthanc::EnumerationToString(target.GetFormat()); | |
317 | |
318 std::auto_ptr<OrthancStone::ParallelSlices> slices(source.GetGeometry(projection)); | |
319 const OrthancStone::Vector pixelSpacing = source.GetVoxelDimensions(projection); | |
320 const unsigned int targetWidth = target.GetWidth(); | |
321 const unsigned int targetHeight = target.GetHeight(); | |
322 | |
323 Orthanc::Image accumulator(Orthanc::PixelFormat_Float32, targetWidth, targetHeight, false); | |
324 Orthanc::Image counter(Orthanc::PixelFormat_Grayscale16, targetWidth, targetHeight, false); | |
325 Orthanc::ImageProcessing::Set(accumulator, 0); | |
326 Orthanc::ImageProcessing::Set(counter, 0); | |
327 | |
328 typedef SubpixelReader<SourceFormat, ImageInterpolation_Nearest> SourceReader; | |
329 | |
330 for (size_t z = 0; z < slices->GetSliceCount(); z++) | |
331 { | |
332 LOG(INFO) << "Applying raytracer on slice: " << z << "/" << slices->GetSliceCount(); | |
333 | |
334 const OrthancStone::CoordinateSystem3D& slice = slices->GetSlice(z); | |
335 OrthancStone::ImageBuffer3D::SliceReader sliceReader(source, projection, z); | |
336 | |
337 SourceReader pixelReader(sliceReader.GetAccessor()); | |
338 | |
339 for (unsigned int y = 0; y < targetHeight; y++) | |
340 { | |
341 float *qacc = reinterpret_cast<float*>(accumulator.GetRow(y)); | |
342 uint16_t *qcount = reinterpret_cast<uint16_t*>(counter.GetRow(y)); | |
343 | |
344 for (unsigned int x = 0; x < targetWidth; x++) | |
345 { | |
346 // Backproject the ray originating from the center of the target pixel | |
347 OrthancStone::Vector direction = camera.GetRayDirection(static_cast<double>(x + 0.5), | |
348 static_cast<double>(y + 0.5)); | |
349 | |
350 // Compute the 3D intersection of the ray with the slice plane | |
351 OrthancStone::Vector p; | |
352 if (slice.IntersectLine(p, camera.GetCenter(), direction)) | |
353 { | |
354 // Compute the 2D coordinates of the intersections, in slice coordinates | |
355 double ix, iy; | |
356 slice.ProjectPoint(ix, iy, p); | |
357 | |
358 ix /= pixelSpacing[0]; | |
359 iy /= pixelSpacing[1]; | |
360 | |
361 // Read and accumulate the value of the pixel | |
362 float pixel; | |
363 if (pixelReader.GetFloatValue(pixel, ix, iy)) | |
364 { | |
365 if (MIP) | |
366 { | |
367 // MIP rendering | |
368 if (*qcount == 0) | |
369 { | |
370 (*qacc) = pixel; | |
371 (*qcount) = 1; | |
372 } | |
373 else if (pixel > *qacc) | |
374 { | |
375 (*qacc) = pixel; | |
376 } | |
377 } | |
378 else | |
379 { | |
380 // Mean intensity | |
381 (*qacc) += pixel; | |
382 (*qcount) ++; | |
383 } | |
384 } | |
385 } | |
386 | |
387 qacc++; | |
388 qcount++; | |
389 } | |
390 } | |
391 } | |
392 | |
393 | |
394 typedef Orthanc::PixelTraits<TargetFormat> TargetTraits; | |
395 | |
396 // "Flatten" the accumulator image to create the target image | |
397 for (unsigned int y = 0; y < targetHeight; y++) | |
398 { | |
399 const float *qacc = reinterpret_cast<const float*>(accumulator.GetConstRow(y)); | |
400 const uint16_t *qcount = reinterpret_cast<const uint16_t*>(counter.GetConstRow(y)); | |
401 typename TargetTraits::PixelType *p = reinterpret_cast<typename TargetTraits::PixelType*>(target.GetRow(y)); | |
402 | |
403 for (unsigned int x = 0; x < targetWidth; x++) | |
404 { | |
405 if (*qcount == 0) | |
406 { | |
407 TargetTraits::SetZero(*p); | |
408 } | |
409 else | |
410 { | |
411 TargetTraits::FloatToPixel(*p, *qacc / static_cast<float>(*qcount)); | |
412 } | |
413 | |
414 p++; | |
415 qacc++; | |
416 qcount++; | |
417 } | |
418 } | |
419 } | |
420 | |
421 | |
422 Orthanc::ImageAccessor* | |
423 FiniteProjectiveCamera::ApplyRaytracer(const ImageBuffer3D& source, | |
424 Orthanc::PixelFormat targetFormat, | |
425 unsigned int targetWidth, | |
426 unsigned int targetHeight, | |
427 bool mip) const | |
428 { | |
429 // TODO - We consider the axial projection of the volume, but we | |
430 // should choose the projection that is the "most perpendicular" | |
431 // to the line joining the camera center and the principal point | |
432 const VolumeProjection projection = VolumeProjection_Axial; | |
433 | |
434 std::auto_ptr<Orthanc::ImageAccessor> target | |
435 (new Orthanc::Image(targetFormat, targetWidth, targetHeight, false)); | |
436 | |
437 if (targetFormat == Orthanc::PixelFormat_Grayscale16 && | |
438 source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && mip) | |
439 { | |
440 ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16, | |
441 Orthanc::PixelFormat_Grayscale16, true> | |
442 (*target, *this, source, projection); | |
443 } | |
444 else if (targetFormat == Orthanc::PixelFormat_Grayscale16 && | |
445 source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && !mip) | |
446 { | |
447 ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16, | |
448 Orthanc::PixelFormat_Grayscale16, false> | |
449 (*target, *this, source, projection); | |
450 } | |
451 else | |
452 { | |
453 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
454 } | |
455 | |
456 return target.release(); | |
457 } | |
291 } | 458 } |