Ejemplo n.º 1
0
void Mask::ApplyToRGBImage(TImage* const image, const TColor& color) const
{
  // Using generics, we allow any Color class that has .red(), .green(), and .blue() member functions
  // to be used to specify the color.
  if(image->GetLargestPossibleRegion() != this->GetLargestPossibleRegion())
  {
    std::cerr << "Image and mask must be the same size!" << std::endl
              << "Image region: " << image->GetLargestPossibleRegion() << std::endl
              << "Mask region: " << this->GetLargestPossibleRegion() << std::endl;
    return;
  }

  // Color the hole pixels in the image.
  typename TImage::PixelType holeValue;
  holeValue.SetSize(image->GetNumberOfComponentsPerPixel());
  holeValue.Fill(0);
  if(image->GetNumberOfComponentsPerPixel() >= 3)
  {
    holeValue[0] = color.red();
    holeValue[1] = color.green();
    holeValue[2] = color.blue();
  }

  itk::ImageRegionConstIterator<Mask> maskIterator(this, this->GetLargestPossibleRegion());

  while(!maskIterator.IsAtEnd())
  {
    if(this->IsHole(maskIterator.GetIndex()))
    {
      image->SetPixel(maskIterator.GetIndex(), holeValue);
    }

    ++maskIterator;
  }
}
void ManualPatchSelectionDialog<TImage>::slot_UpdateSource(const itk::ImageRegion<2>& sourceRegion,
                                                           const itk::ImageRegion<2>& targetRegion)
{
  // This function needs the targetRegion because this is the region of the Mask that is used to mask the source patch.
  // std::cout << "Update source." << std::endl;

  if(!this->Image->GetLargestPossibleRegion().IsInside(sourceRegion))
  {
    std::cerr << "Source region is outside the image!" << std::endl;
    return;
  }
  
  if(MaskImage->CountHolePixels(sourceRegion) > 0)
  {
    std::cerr << "The source patch must not have any hole pixels!" << std::endl;
    btnAccept->setVisible(false);
  }
  else
  {
    btnAccept->setVisible(true);
  }

  typename TImage::Pointer tempImage = TImage::New();
  ITKHelpers::ConvertTo3Channel(this->Image, tempImage.GetPointer());

  typename TImage::PixelType zeroPixel(3);
  zeroPixel.Fill(0);
  this->MaskImage->ApplyToImage(tempImage.GetPointer(), zeroPixel);
  QImage maskedSourceImage = ITKQtHelpers::GetQImageColor(tempImage.GetPointer(), sourceRegion);
  QGraphicsPixmapItem* item = this->SourcePatchScene->addPixmap(QPixmap::fromImage(maskedSourceImage));
  gfxSource->fitInView(item);

  // Refresh the image
  //ITKVTKHelpers::ITKImageToVTKRGBImage(this->Image, this->ImageLayer.ImageData);
  unsigned char green[3] = {0, 255, 0};

  MaskOperations::ITKImageToVTKImageMasked(tempImage, this->MaskImage,
                                          this->ImageLayer.ImageData, green);

  // Outline the source patch
  unsigned char blue[3] = {0, 0, 255};
  ITKVTKHelpers::OutlineRegion(this->ImageLayer.ImageData, sourceRegion, blue);

  this->qvtkWidget->GetRenderWindow()->Render();
}
ManualPatchSelectionDialog<TImage>::ManualPatchSelectionDialog(TImage* const image, Mask* const mask,
                                                               const itk::ImageRegion<2>& targetRegion)
: Image(image), MaskImage(mask), TargetRegion(targetRegion)
{
  // Allow the type itkImageRegion to be used in signals/slots.
  qRegisterMetaType<itk::ImageRegion<2> >("itkImageRegion");

  this->setupUi(this);

  this->ImageLayer.ImageSlice->SetDragable(false);
  this->ImageLayer.ImageSlice->SetPickable(false);

  typename TImage::Pointer tempImage = TImage::New();
  ITKHelpers::DeepCopy(image, tempImage.GetPointer());
  typename TImage::PixelType zeroPixel(tempImage->GetNumberOfComponentsPerPixel());
  zeroPixel.Fill(0);
  mask->ApplyToImage(tempImage.GetPointer(), zeroPixel);
  ITKVTKHelpers::ITKVectorImageToVTKImageFromDimension(tempImage.GetPointer(), this->ImageLayer.ImageData);

  SetupScenes();

  this->Renderer = vtkSmartPointer<vtkRenderer>::New();
  this->InteractorStyle = vtkSmartPointer<InteractorStyleImageWithDrag>::New();

  this->qvtkWidget->GetRenderWindow()->AddRenderer(this->Renderer);

  this->Renderer->AddViewProp(ImageLayer.ImageSlice);

  // Per the comment in InteractorStyleImageWithDrag, the next 3 lines must be in this order
  this->InteractorStyle->SetCurrentRenderer(this->Renderer);
  this->qvtkWidget->GetRenderWindow()->GetInteractor()->SetInteractorStyle(this->InteractorStyle);
  this->InteractorStyle->Init();

  this->PatchSelector = new MovablePatch(this->TargetRegion.GetSize()[0]/2, this->InteractorStyle, this->gfxSource);

  // slot_UpdateImage();

  this->Renderer->ResetCamera();
  this->qvtkWidget->GetRenderWindow()->Render();
  // this->Camera = new ImageCamera(this->Renderer);

  connect(this->PatchSelector, SIGNAL(signal_PatchMoved()), this, SLOT(slot_PatchMoved()));
}
void ManualPatchSelectionDialog<TImage>::slot_UpdateTarget(const itk::ImageRegion<2>& region)
{
  // std::cout << "Update target." << std::endl;

  unsigned char red[3] = {255, 0, 0};
  ITKVTKHelpers::OutlineRegion(this->ImageLayer.ImageData, region, red);

  this->qvtkWidget->GetRenderWindow()->Render();

  // Masked target patch
  typename TImage::Pointer tempImage = TImage::New();
  ITKHelpers::ConvertTo3Channel(this->Image, tempImage.GetPointer());
  typename TImage::PixelType zeroPixel(3);
  zeroPixel.Fill(0);
  this->MaskImage->ApplyToImage(tempImage.GetPointer(), zeroPixel);
  QImage maskedTargetImage = ITKQtHelpers::GetQImageColor(tempImage.GetPointer(), region);
  QGraphicsPixmapItem* maskedItem = this->TargetPatchScene->addPixmap(QPixmap::fromImage(maskedTargetImage));
  gfxTarget->fitInView(maskedItem);
}
void TopPatchesDialog<TImage>::SetQueryNode(const Node& queryNode)
{
    this->QueryNode = queryNode;

    itk::Index<2> queryIndex = ITKHelpers::CreateIndex(queryNode);
    itk::ImageRegion<2> queryRegion = ITKHelpers::GetRegionInRadiusAroundPixel(queryIndex, PatchHalfWidth);

    typename TImage::Pointer tempImage = TImage::New();
    ITKHelpers::ConvertTo3Channel(this->Image, tempImage.GetPointer());

    typename TImage::PixelType zeroPixel(3);
    zeroPixel.Fill(0);
    this->MaskImage->ApplyToImage(tempImage.GetPointer(), zeroPixel);

    QImage maskedQueryPatch = ITKQtHelpers::GetQImageColor(tempImage.GetPointer(), queryRegion);
    MaskedQueryPatchItem = this->QueryPatchScene->addPixmap(QPixmap::fromImage(maskedQueryPatch));

    // We do this here because we could potentially call SetQueryNode after the widget is constructed as well.
    gfxQueryPatch->fitInView(MaskedQueryPatchItem);
}
  /** The mask is inpainted with ValidValue in this function. */
  void FinishVertex(VertexDescriptorType targetNode, VertexDescriptorType sourceNode)// __attribute__((optimize(0))) // This supposedly makes this function build in debug mode (-g -O0) when the rest of the program is built in -O3 or similar)
  {
    // Mark this pixel and the area around it as filled, and mark the mask in this region as filled.
    // Determine the new boundary, and setup the nodes in the boundary queue.

//    std::cout << "InpaintingVisitor::FinishVertex()" << std::endl;

    // Construct the region around the vertex
    itk::Index<2> indexToFinish = ITKHelpers::CreateIndex(targetNode);

    itk::ImageRegion<2> regionToFinishFull = ITKHelpers::GetRegionInRadiusAroundPixel(indexToFinish, this->PatchHalfWidth);

    // Copy this region so that we can change (crop) the regionToFinish and still have a copy of the original region
    itk::ImageRegion<2> regionToFinish = regionToFinishFull;

    // Make sure the region is entirely inside the image
    // (because we allow target patches to not be entirely inside the image to handle the case where
    // the hole boundary is near the image boundary)
    regionToFinish.Crop(this->Image->GetLargestPossibleRegion());

    if(this->DebugImages)
    {
      ITKHelpers::WriteImage(this->MaskImage, Helpers::GetSequentialFileName("Mask_Before", this->NumberOfFinishedPatches, "png", 3));
    }

    // Mark all the pixels in this region as filled in the mask.
    ITKHelpers::SetRegionToConstant(this->MaskImage, regionToFinish, this->MaskImage->GetValidValue());

    if(this->DebugImages)
    {
      ITKHelpers::WriteImage(this->MaskImage, Helpers::GetSequentialFileName("Mask_After", this->NumberOfFinishedPatches, "png", 3));

      typename TImage::PixelType red;
      red.Fill(0);
      red[0] = 255;

      typename TImage::PixelType green;
      green.Fill(0);
      green[1] = 255;

      typename TImage::Pointer patchesCopiedImage = TImage::New();
      ITKHelpers::DeepCopy(this->Image, patchesCopiedImage.GetPointer());
      ITKHelpers::OutlineRegion(patchesCopiedImage.GetPointer(), regionToFinish, red);

      itk::Index<2> sourceRegionCenter = ITKHelpers::CreateIndex(sourceNode);
      itk::ImageRegion<2> sourceRegion = ITKHelpers::GetRegionInRadiusAroundPixel(sourceRegionCenter, this->PatchHalfWidth);

      sourceRegion = ITKHelpers::CropRegionAtPosition(sourceRegion, this->Image->GetLargestPossibleRegion(), regionToFinishFull);

      ITKHelpers::OutlineRegion(patchesCopiedImage.GetPointer(), sourceRegion, green);

      ITKHelpers::WriteRGBImage(patchesCopiedImage.GetPointer(), Helpers::GetSequentialFileName("PatchesCopied", this->NumberOfFinishedPatches, "png", 3));
    }

    // Update the priority function. This must be done AFTER the mask is filled,
    // as some of the Priority functors only compute things on the hole boundary, or only
    // use data from the valid region of the image (indicated by valid pixels in the mask).
    this->PriorityFunction->Update(sourceNode, targetNode, this->NumberOfFinishedPatches);

    // Initialize (if requested) all vertices in the newly filled region because they may now be valid source nodes.
    // (You may not want to do this in some cases (i.e. if the descriptors needed cannot be
    // computed on newly filled regions))

    if(this->AllowNewPatches)
    {
      itk::ImageRegionConstIteratorWithIndex<TImage> gridIterator(Image, regionToFinish);
      while(!gridIterator.IsAtEnd())
      {
        VertexDescriptorType v = Helpers::ConvertFrom<VertexDescriptorType, itk::Index<2> >(gridIterator.GetIndex());

        InitializeVertex(v);
        ++gridIterator;
      }
    }

    // Add pixels that are on the new boundary to the queue, and mark other pixels as not in the queue.
    itk::ImageRegionConstIteratorWithIndex<Mask> imageIterator(this->MaskImage, regionToFinish);

    typedef typename TBoundaryNodeQueue::HandleType HandleType;

    while(!imageIterator.IsAtEnd())
    {
      VertexDescriptorType v = Helpers::ConvertFrom<VertexDescriptorType, itk::Index<2> >(imageIterator.GetIndex());

      if(this->MaskImage->HasHoleNeighbor(imageIterator.GetIndex()))
      {
        float priority = this->PriorityFunction->ComputePriority(imageIterator.GetIndex());
        this->BoundaryNodeQueue.push_or_update(v, priority);
      }
      else
      {
        this->BoundaryNodeQueue.mark_as_invalid(v);
      }

      ++imageIterator;
    }

    // std::cout << "FinishVertex after traversing finishing region there are "
    //           << BoostHelpers::CountValidQueueNodes(BoundaryNodeQueue, BoundaryStatusMap)
    //           << " valid nodes in the queue." << std::endl;
    
    // Sometimes pixels that are not in the finishing region that were boundary pixels are no longer
    // boundary pixels after the filling. Check for these.
    // E.g. (H=hole, B=boundary, V=valid, Q=query, F=filled, N=new boundary,
    // R=old boundary pixel that needs to be removed because it is no longer on the boundary)
    // Before filling

    /* V V V B H H H H
     * V V V B H H H H
     * V V V B H H H H
     * V V V B B Q B B
     * V V V V V V V V
     */
    // After filling

    /* V V V B H H H H
     * V V V B H H H H
     * V V V B F F F H
     * V V V B F F F B
     * V V V V F F F V
     */
    // New boundary
    /* V V V B H H H H
     * V V V B H H H H
     * V V V B N N N H
     * V V V R F F N B
     * V V V V F F F V
     */

    // Expand the region
    itk::ImageRegion<2> expandedRegion = ITKHelpers::GetRegionInRadiusAroundPixel(indexToFinish, PatchHalfWidth + 1);
    // Make sure the region is entirely inside the image (to allow for target regions near the image boundary)
    expandedRegion.Crop(this->Image->GetLargestPossibleRegion());
    std::vector<itk::Index<2> > boundaryPixels = ITKHelpers::GetBoundaryPixels(expandedRegion);
    for(unsigned int i = 0; i < boundaryPixels.size(); ++i)
    {
      // the region (the entire image) can be omitted, as this function automatically checks if the pixels are inside the image
      if(!this->MaskImage->HasHoleNeighbor(boundaryPixels[i]))
      {
        VertexDescriptorType v = Helpers::ConvertFrom<VertexDescriptorType, itk::Index<2> >(boundaryPixels[i]);

        put(this->BoundaryNodeQueue.BoundaryStatusMap, v, false);
      }
    }

    // std::cout << "FinishVertex after removing stale nodes outside finishing region there are "
    //               << BoostHelpers::CountValidQueueNodes(BoundaryNodeQueue, BoundaryStatusMap)
    //               << " valid nodes in the queue." << std::endl;

    this->NumberOfFinishedPatches++;

//    std::cout << "Leave InpaintingVisitor::FinishVertex()" << std::endl;
  } // end FinishVertex