void CriminisiInpainting::ComputeIsophotes() { try { Helpers::DebugWriteImageConditional<FloatVectorImageType>(this->CurrentImage, "Debug/ComputeIsophotes.input.mha", this->DebugImages); /* // This only works when the image is RGB typedef itk::VectorMagnitudeImageFilter<FloatVectorImageType, UnsignedCharScalarImageType> VectorMagnitudeFilterType; VectorMagnitudeFilterType::Pointer magnitudeFilter = VectorMagnitudeFilterType::New(); magnitudeFilter->SetInput(this->OriginalImage); // We use the original image here because the image that has been painted green inside the hole has a strong gradient around the hole. magnitudeFilter->Update(); */ RGBImageType::Pointer rgbImage = RGBImageType::New(); Helpers::VectorImageToRGBImage(this->OriginalImage, rgbImage); Helpers::DebugWriteImageConditional<RGBImageType>(rgbImage, "Debug/ComputeIsophotes.rgb.mha", this->DebugImages); typedef itk::RGBToLuminanceImageFilter< RGBImageType, FloatScalarImageType > LuminanceFilterType; LuminanceFilterType::Pointer luminanceFilter = LuminanceFilterType::New(); luminanceFilter->SetInput(rgbImage); luminanceFilter->Update(); Helpers::DebugWriteImageConditional<FloatScalarImageType>(luminanceFilter->GetOutput(), "Debug/ComputeIsophotes.luminance.mha", this->DebugImages); // Blur the image to compute better gradient estimates typedef itk::DiscreteGaussianImageFilter<FloatScalarImageType, FloatScalarImageType > BlurFilterType; BlurFilterType::Pointer blurFilter = BlurFilterType::New(); blurFilter->SetInput(luminanceFilter->GetOutput()); blurFilter->SetVariance(2); blurFilter->Update(); Helpers::DebugWriteImageConditional<FloatScalarImageType>(blurFilter->GetOutput(), "Debug/ComputeIsophotes.blurred.mha", true); // Compute the gradient // Template parameters are <TInputImage, TOperatorValueType, TOutputValueType> typedef itk::GradientImageFilter<FloatScalarImageType, float, float> GradientFilterType; GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput(blurFilter->GetOutput()); gradientFilter->Update(); Helpers::DebugWriteImageConditional<FloatVector2ImageType>(gradientFilter->GetOutput(), "Debug/ComputeIsophotes.gradient.mha", this->DebugImages); // Rotate the gradient 90 degrees to obtain isophotes from gradient typedef itk::UnaryFunctorImageFilter<FloatVector2ImageType, FloatVector2ImageType, RotateVectors< FloatVector2ImageType::PixelType, FloatVector2ImageType::PixelType> > FilterType; FilterType::Pointer rotateFilter = FilterType::New(); rotateFilter->SetInput(gradientFilter->GetOutput()); rotateFilter->Update(); Helpers::DebugWriteImageConditional<FloatVector2ImageType>(rotateFilter->GetOutput(), "Debug/ComputeIsophotes.rotatedGradient.mha", this->DebugImages); // Mask the isophote image with the expanded version of the inpainting mask. // That is, keep only the values outside of the expanded mask. To do this, we have to first invert the mask. // Invert the mask typedef itk::InvertIntensityImageFilter <Mask> InvertIntensityImageFilterType; InvertIntensityImageFilterType::Pointer invertMaskFilter = InvertIntensityImageFilterType::New(); invertMaskFilter->SetInput(this->CurrentMask); invertMaskFilter->Update(); if(this->DebugImages) { Helpers::WriteImage<Mask>(invertMaskFilter->GetOutput(), "Debug/ComputeIsophotes.invertedMask.mha"); } //std::cout << "rotateFilter: " << rotateFilter->GetOutput()->GetLargestPossibleRegion() << std::endl; //std::cout << "invertMaskFilter: " << invertMaskFilter->GetOutput()->GetLargestPossibleRegion() << std::endl; // Keep only values outside the masked region typedef itk::MaskImageFilter< FloatVector2ImageType, Mask, FloatVector2ImageType > MaskFilterType; MaskFilterType::Pointer maskFilter = MaskFilterType::New(); maskFilter->SetInput1(rotateFilter->GetOutput()); maskFilter->SetInput2(invertMaskFilter->GetOutput()); maskFilter->Update(); if(this->DebugImages) { Helpers::WriteImage<FloatVector2ImageType>(maskFilter->GetOutput(), "Debug/ComputeIsophotes.maskedIsophotes.mha"); } Helpers::DeepCopy<FloatVector2ImageType>(maskFilter->GetOutput(), this->IsophoteImage); } catch( itk::ExceptionObject & err ) { std::cerr << "ExceptionObject caught in ComputeIsophotes!" << std::endl; std::cerr << err << std::endl; exit(-1); } }
void CriminisiInpainting::ComputeBoundaryNormals() { try { // Blur the mask, compute the gradient, then keep the normals only at the original mask boundary if(this->DebugImages) { Helpers::WriteImage<UnsignedCharScalarImageType>(this->BoundaryImage, "Debug/ComputeBoundaryNormals.BoundaryImage.mha"); Helpers::WriteImage<Mask>(this->CurrentMask, "Debug/ComputeBoundaryNormals.CurrentMask.mha"); } // Blur the mask typedef itk::DiscreteGaussianImageFilter< Mask, FloatScalarImageType > BlurFilterType; BlurFilterType::Pointer gaussianFilter = BlurFilterType::New(); gaussianFilter->SetInput(this->CurrentMask); gaussianFilter->SetVariance(2); gaussianFilter->Update(); if(this->DebugImages) { Helpers::WriteImage<FloatScalarImageType>(gaussianFilter->GetOutput(), "Debug/ComputeBoundaryNormals.BlurredMask.mha"); } // Compute the gradient of the blurred mask typedef itk::GradientImageFilter< FloatScalarImageType, float, float> GradientFilterType; GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput(gaussianFilter->GetOutput()); gradientFilter->Update(); if(this->DebugImages) { Helpers::WriteImage<FloatVector2ImageType>(gradientFilter->GetOutput(), "Debug/ComputeBoundaryNormals.BlurredMaskGradient.mha"); } // Only keep the normals at the boundary typedef itk::MaskImageFilter< FloatVector2ImageType, UnsignedCharScalarImageType, FloatVector2ImageType > MaskFilterType; MaskFilterType::Pointer maskFilter = MaskFilterType::New(); //maskFilter->SetInput1(gradientFilter->GetOutput()); //maskFilter->SetInput2(this->BoundaryImage); maskFilter->SetInput(gradientFilter->GetOutput()); maskFilter->SetMaskImage(this->BoundaryImage); maskFilter->Update(); if(this->DebugImages) { Helpers::WriteImage<FloatVector2ImageType>(maskFilter->GetOutput(), "Debug/ComputeBoundaryNormals.BoundaryNormalsUnnormalized.mha"); } //this->BoundaryNormals = maskFilter->GetOutput(); //this->BoundaryNormals->Graft(maskFilter->GetOutput()); Helpers::DeepCopy<FloatVector2ImageType>(maskFilter->GetOutput(), this->BoundaryNormals); // Normalize the vectors because we just care about their direction (the Data term computation calls for the normalized boundary normal) itk::ImageRegionIterator<FloatVector2ImageType> boundaryNormalsIterator(this->BoundaryNormals, this->BoundaryNormals->GetLargestPossibleRegion()); itk::ImageRegionConstIterator<UnsignedCharScalarImageType> boundaryIterator(this->BoundaryImage, this->BoundaryImage->GetLargestPossibleRegion()); while(!boundaryNormalsIterator.IsAtEnd()) { if(boundaryIterator.Get()) // The pixel is on the boundary { FloatVector2ImageType::PixelType p = boundaryNormalsIterator.Get(); p.Normalize(); boundaryNormalsIterator.Set(p); } ++boundaryNormalsIterator; ++boundaryIterator; } if(this->DebugImages) { Helpers::WriteImage<FloatVector2ImageType>(this->BoundaryNormals, "Debug/ComputeBoundaryNormals.BoundaryNormals.mha"); } } catch( itk::ExceptionObject & err ) { std::cerr << "ExceptionObject caught in ComputeBoundaryNormals!" << std::endl; std::cerr << err << std::endl; exit(-1); } }
void BoundaryNormals::ComputeBoundaryNormals(TNormalsImage* const boundaryNormalsImage, const float maskBlurVariance) { // Blur the mask, compute the gradient, then keep the normals only at the original mask boundary // Compute the boundary of the mask typedef itk::Image<unsigned char, 2> BoundaryImageType; BoundaryImageType::Pointer boundaryImage = BoundaryImageType::New(); this->MaskImage->CreateBoundaryImage(boundaryImage, Mask::VALID, 255); if(this->IsDebugOn()) { ITKHelpers::WriteImage(boundaryImage.GetPointer(), "ComputeBoundaryNormals.BoundaryImage.mha"); } // Blur the mask so that the normals are not quantized so much. Also, pixels with only diagonal // valid neighbors have undefined gradients without this blurring. typedef itk::DiscreteGaussianImageFilter<Mask, ITKHelpers::FloatScalarImageType> BlurFilterType; BlurFilterType::Pointer gaussianFilter = BlurFilterType::New(); gaussianFilter->SetInput(this->MaskImage); gaussianFilter->SetVariance(maskBlurVariance); gaussianFilter->Update(); if(this->IsDebugOn()) { ITKHelpers::WriteImage(gaussianFilter->GetOutput(), "ComputeBoundaryNormals.BlurredMask.mha"); } // Compute the gradient of the blurred mask typedef itk::GradientImageFilter<ITKHelpers::FloatScalarImageType, float, float> GradientFilterType; GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput(gaussianFilter->GetOutput()); gradientFilter->Update(); if(this->IsDebugOn()) { ITKHelpers::WriteImage(gradientFilter->GetOutput(), "ComputeBoundaryNormals.BlurredMaskGradient.mha"); } // Only keep the normals at the boundary typedef itk::MaskImageFilter<TNormalsImage, ITKHelpers::UnsignedCharScalarImageType, TNormalsImage> MaskFilterType; typename MaskFilterType::Pointer maskFilter = MaskFilterType::New(); maskFilter->SetInput(gradientFilter->GetOutput()); maskFilter->SetMaskImage(boundaryImage); maskFilter->Update(); if(this->IsDebugOn()) { ITKHelpers::WriteImage(maskFilter->GetOutput(), "ComputeBoundaryNormals.BoundaryNormalsUnnormalized.mha"); } // Allocate the image to return // ITKHelpers::DeepCopy(maskFilter->GetOutput(), boundaryNormalsImage); ITKHelpers::InitializeImage(boundaryNormalsImage, this->MaskImage->GetLargestPossibleRegion()); // Normalize the vectors because we just care about their direction std::vector<itk::Index<2> > boundaryPixels = ITKHelpers::GetNonZeroPixels(boundaryImage.GetPointer()); for(std::vector<itk::Index<2> >::const_iterator boundaryPixelIterator = boundaryPixels.begin(); boundaryPixelIterator != boundaryPixels.end(); ++boundaryPixelIterator) { typename TNormalsImage::PixelType p = maskFilter->GetOutput()->GetPixel(*boundaryPixelIterator); p.Normalize(); boundaryNormalsImage->SetPixel(*boundaryPixelIterator, p); } }