#include "SkullRemovalService.h" #include "connectITKVTK.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { using InternalPixelType = float; constexpr unsigned int Dimension = 3; using InternalImageType = itk::Image; using MaskPixelType = unsigned char; using MaskImageType = itk::Image; using ImportFilterType = itk::VTKImageImport; using CastingINVFilterType = itk::CastImageFilter; using CurvatureFlowImageFilterType = itk::CurvatureFlowImageFilter; using ConnectedThresholdImageFilterType = itk::ConnectedThresholdImageFilter; using CastingFilterType = itk::CastImageFilter; using StructuringElementType = itk::BinaryBallStructuringElement; using DilateFilterType = itk::BinaryDilateImageFilter; using ErodeFilterType = itk::BinaryErodeImageFilter; using MaskNegatedFilterType = itk::MaskNegatedImageFilter; } // namespace vtkImageData* SkullRemovalService::run(vtkImageData* inputVolume, double thr_low, double thr_up, std::atomic_bool* abortFlag, std::function progressCb) { if (!inputVolume) throw std::runtime_error("SkullRemovalService::run: inputVolume is null"); auto isAborted = [&]() -> bool { return abortFlag && abortFlag->load(); }; if (progressCb) progressCb("Skull masking: preparing pipeline", 0.0); if (isAborted()) return nullptr; // VTK -> ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputData(inputVolume); auto itkImporter = ImportFilterType::New(); ConnectPipelines(vtkExporter.GetPointer(), itkImporter); // Pipeline (mirrors old gSkullRemoval::runFilter in a pure form) auto smoothing = CurvatureFlowImageFilterType::New(); auto connectedThreshold = ConnectedThresholdImageFilterType::New(); auto caster_mask = CastingFilterType::New(); auto binaryDilateFilter = DilateFilterType::New(); auto binaryErodeFilter = ErodeFilterType::New(); auto maskNegatedFilter = MaskNegatedFilterType::New(); smoothing->SetNumberOfIterations(10); smoothing->SetTimeStep(0.125); smoothing->SetInput(itkImporter->GetOutput()); connectedThreshold->SetInput(smoothing->GetOutput()); connectedThreshold->SetLower(static_cast(thr_low)); connectedThreshold->SetUpper(static_cast(thr_up)); connectedThreshold->SetReplaceValue(255); // Seed at volume center connectedThreshold->UpdateOutputInformation(); InternalImageType::Pointer in = itkImporter->GetOutput(); const auto region = in->GetBufferedRegion(); const auto size = region.GetSize(); const auto start = region.GetIndex(); InternalImageType::IndexType seed; seed[0] = start[0] + static_cast(size[0] / 2); seed[1] = start[1] + static_cast(size[1] / 2); seed[2] = start[2] + static_cast(size[2] / 2); connectedThreshold->SetSeed(seed); caster_mask->SetInput(connectedThreshold->GetOutput()); StructuringElementType kernel; kernel.SetRadius(10); kernel.CreateStructuringElement(); binaryDilateFilter->SetKernel(kernel); binaryErodeFilter->SetKernel(kernel); binaryDilateFilter->SetDilateValue(255); binaryErodeFilter->SetErodeValue(255); binaryDilateFilter->SetInput(caster_mask->GetOutput()); binaryErodeFilter->SetInput(binaryDilateFilter->GetOutput()); maskNegatedFilter->SetInput1(itkImporter->GetOutput()); maskNegatedFilter->SetInput2(binaryErodeFilter->GetOutput()); if (progressCb) progressCb("Skull masking: running", 0.5); if (isAborted()) return nullptr; try { maskNegatedFilter->Update(); } catch (const itk::ExceptionObject& ex) { if (isAborted()) return nullptr; throw std::runtime_error(std::string("SkullRemovalService: ITK exception: ") + ex.GetDescription()); } if (isAborted()) return nullptr; // ITK -> VTK vtkSmartPointer vtkImporter = vtkSmartPointer::New(); auto itkExporter = itk::VTKImageExport::New(); itkExporter->SetInput(maskNegatedFilter->GetOutput()); ConnectPipelines(itkExporter, vtkImporter); vtkImporter->Update(); vtkImageData* out = vtkImageData::New(); out->DeepCopy(vtkImporter->GetOutput()); if (progressCb) progressCb("Skull masking: done", 1.0); return out; }