The post shows how to cut image by a plane smoothly.

Header And Macros

#include <vtkVersion.h>
#include <vtkImageData.h>
#include <vtkImageMapper3D.h>
#include <vtkImageStencil.h>
#include <vtkImageStencilData.h>
#include <vtkImageToImageStencil.h>
#include <vtkJPEGReader.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkPolyDataToImageStencil.h>
#include <vtkPolyData.h>
#include <vtkSTLReader.h>
#include <vtkProperty.h>
#include <vtkImageProperty.h>
#include <vtkSphereSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkImageDataGeometryFilter.h>
#include <vtkImageMarchingCubes.h>
#include <vtkPolyDataNormals.h>
#include <vtkFillHolesFilter.h>
#include <vtkCleanPolyData.h>
#include <vtkTriangleFilter.h>
#include <vtkAppendPolyData.h>
#include <vtkImageMarchingCubes.h>
#include <vtkPlane.h>
#include <vtkImageWeightedSum.h>
#include <vtkConeSource.h>
#include <vtkPolyDataConnectivityFilter.h>
#include <vtkXMLImageDataWriter.h>
#include <vtkXMLImageDataReader.h>

#include "../vtkLearn/tool.h"

#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();

unsigned char inval = 255;
unsigned char outval = 0;
double spacingVal = 0.1;

Read And Write Image Data

    vtkSPtrNew( writer, vtkXMLImageDataWriter );
    writer->SetInputData( _sphereImage );
    writer->SetFileName( "image.vti" );
    writer->Write();

    vtkSPtrNew( reader, vtkXMLImageDataReader );
    reader->SetFileName( "image.vti" );
    reader->Update();

    vtkSPtr<vtkImageData> sphereImage = reader->GetOutput();

Convert Image To PolyData

vtkSPtr<vtkPolyData> imageToPolyData( vtkSPtr<vtkImageData> image )
{
    vtkSPtrNew( marchingCubes, vtkImageMarchingCubes );
    marchingCubes->SetInputData( image );
    marchingCubes->SetValue( 0, (inval + outval)/2 );
    marchingCubes->SetNumberOfContours( 1 );
    marchingCubes->Update();

    auto fillHoles = vtkSmartPointer<vtkFillHolesFilter>::New();
    fillHoles->SetInputConnection( marchingCubes->GetOutputPort() );
    fillHoles->SetHoleSize(1000.0);

    auto normals = vtkSmartPointer<vtkPolyDataNormals>::New();
    normals->SetInputConnection(fillHoles->GetOutputPort());
    // Make the triangle winding order consistent
    normals->ConsistencyOn();
    normals->SplittingOff();
    normals->GetOutput()->GetPointData()->SetNormals(
        marchingCubes->GetOutput()->GetPointData()->GetNormals());
    normals->Update();

    return normals->GetOutput();
}

Convert PolyData To Image

vtkSPtr<vtkImageData> ConvertPolydataToImage(vtkPolyData *polydata)
{
    double bounds[6];
    polydata->GetBounds(bounds);

    int dim[3];
    for (int i = 0; i < 3; i++)
    {
        dim[i] = static_cast<int>( ceil((bounds[2 * i + 1] - bounds[2 * i]) / spacingVal) );
    }
    double origin[3];
    origin[0] = bounds[0] + spacingVal / 2;
    origin[1] = bounds[2] + spacingVal / 2;
    origin[2] = bounds[4] + spacingVal / 2;

    vtkSPtrNew(imageData, vtkImageData)
    imageData->SetSpacing(spacingVal, spacingVal, spacingVal);
    imageData->SetDimensions(dim);
    imageData->SetOrigin(origin);
    imageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
    imageData->SetExtent( 0, dim[0] - 1, 0, dim[1] - 1, 0, dim[2] - 1 );

    // fill the imageData with foreground voxels
    vtkIdType count = imageData->GetNumberOfPoints();
    for (vtkIdType i = 0; i < count; ++i)
    {
        imageData->GetPointData()->GetScalars()->SetTuple1(i, inval);
    }

    // polygonal data --> imageData stencil:
    vtkSPtrNew(pd2stenc, vtkPolyDataToImageStencil);
    pd2stenc->SetInputData(polydata);
    pd2stenc->SetOutputOrigin(origin);
    pd2stenc->SetOutputSpacing(spacingVal, spacingVal, spacingVal);
    pd2stenc->SetOutputWholeExtent(imageData->GetExtent());
    pd2stenc->Update();

    // cut the corresponding white imageData and set the background:
    vtkSPtrNew(imgstenc, vtkImageStencil);
    imgstenc->SetInputData(imageData);
    imgstenc->SetStencilConnection(pd2stenc->GetOutputPort());
    imgstenc->ReverseStencilOff();
    imgstenc->SetBackgroundValue( outval );
    imgstenc->Update();

    imageData->DeepCopy(imgstenc->GetOutput());
    return imageData;
}

Cut Image

void CutImage(vtkImageData *image, vtkPlane *cutPlane )
{
    if( nullptr == image || nullptr == cutPlane )
    {
        return ;
    }
    int negtiveCount = 0;
    PointStruct spaceVals( image->GetSpacing() );
    double maxDistance = sqrt(spaceVals[0] * spaceVals[0] + spaceVals[1] * spaceVals[1] + spaceVals[2] * spaceVals[2]);
    double threshold = 0.01;
    cout << "maxDistance: " << maxDistance << endl;
    auto isoSurfaceValue = (inval+outval)*0.5;
    for (int i = 0; i < image->GetNumberOfPoints(); i++)
    {
        double pointPos[3];
        image->GetPoint(i, pointPos);
        double scaleVal = image->GetPointData()->GetScalars()->GetTuple1(i);
        if (scaleVal < isoSurfaceValue ) //(outval + 0.1))  // speed up
            continue;

        double val = cutPlane->EvaluateFunction(pointPos);
        if( abs( val ) < maxDistance ) // smooth cut plane
        {
            auto value = (1 + val / maxDistance) * isoSurfaceValue;
            if( value > inval ){ value = inval; }
            if( value < outval ){ value = outval; }

            image->GetPointData()->GetScalars()->SetTuple1(i, value);
        }
        else if( val < 0 ) // clip part
        {
            image->GetPointData()->GetScalars()->SetTuple1(i, outval);
            negtiveCount++;
        }
    }
}

int main(int, char *[])
{
    setbuf( stdout, nullptr );

    vtkSPtrNew( sphereSource, vtkSphereSource );
    sphereSource->SetRadius(1);
    sphereSource->SetPhiResolution(10);
    sphereSource->SetThetaResolution(10);
    sphereSource->Update();

    vtkSPtr<vtkPolyData> spherePd = sphereSource->GetOutput();

    vtkSPtr<vtkImageData> sphereImage = ConvertPolydataToImage( spherePd );

    auto originImage = imageToPolyData( sphereImage );

    vtkSPtrNew( cutPlane, vtkPlane );
    cutPlane->SetOrigin( 0, 0, 0 );
    cutPlane->SetNormal( 0, -1, 0 );
    CutImage( sphereImage, cutPlane );

    auto newImage = imageToPolyData( sphereImage );

    vtkSPtrNew( leftMapper, vtkPolyDataMapper );
    leftMapper->SetInputData( originImage );
    leftMapper->Update();
    leftMapper->SetScalarVisibility( false );

    vtkSPtrNew( leftActor, vtkActor );
    leftActor->SetMapper( leftMapper );
    leftActor->GetProperty()->SetColor( 1, 1, 1 );
    // ============= Finish: show image data ===================


    vtkSPtrNew( rightMapper, vtkPolyDataMapper );
    rightMapper->SetInputData( newImage);
    rightMapper->SetScalarVisibility( false );
    rightMapper->Update();

    vtkSPtrNew( rightActor, vtkActor );
    rightActor->SetMapper( rightMapper );

    double leftViewport[4] = {0.0, 0.0, 0.5, 1.0};
    double rightViewport[4] = {0.5, 0.0, 1.0, 1.0};

    // Setup renderers
    vtkSPtrNew( leftRenderer, vtkRenderer );
    leftRenderer->SetViewport( leftViewport );
    leftRenderer->AddActor( leftActor );
    leftRenderer->SetBackground(.6, .5, .4);
    leftRenderer->ResetCamera();

    vtkSPtrNew( rightRenderer, vtkRenderer );
    rightRenderer->SetViewport(rightViewport);
    rightRenderer->AddActor( rightActor );
    rightRenderer->SetBackground(.4, .5, .6);
    rightRenderer->SetActiveCamera( leftRenderer->GetActiveCamera() );

    // Setup render window
    vtkSPtrNew( renderWindow, vtkRenderWindow );
    renderWindow->AddRenderer( leftRenderer );
    renderWindow->AddRenderer( rightRenderer );
    renderWindow->SetSize( 900, 300 );

    // Setup render window interactor
    vtkSPtrNew( renderWindowInteractor, vtkRenderWindowInteractor );

    // Render and start interaction
    renderWindowInteractor->SetRenderWindow(renderWindow);
    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}
Categories: VTK

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

3D Model Viewer: add grid plane and convex hull.

X
A prohibited operation
0
Would love your thoughts, please comment.x
()
x