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