Skip to content

Using ShapeWorks as a library

ShapeWorks can be used as a library, for example as part of an ITK based application. To do so, when building ShapeWorks, specify CMAKE_INSTALL_PREFIX and use make install:

$ ccmake -DCMAKE_INSTALL_PREFIX=</path/to/sw/install>
$ make install

An example using ShapeWorks as a library in an ITK application is given in Examples/C++

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10.2)

project(HelloShapeWorksItk)

set(CMAKE_CXX_STANDARD 17)

# Find ShapeWorks
find_package(ShapeWorks REQUIRED)

SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--disable-new-dtags")

# Find ITK
#find_package(ITK REQUIRED)
#include(${ITK_USE_FILE})

add_executable(HelloShapeWorksItk HelloShapeWorksItk.cpp)

target_link_libraries(HelloShapeWorksItk
  ${ITK_LIBRARIES}
  shapeworks::Optimize
  shapeworks::Groom
  shapeworks::Analyze
  pybind11::embed
  )

HelloShapeWorksItk.cpp:

#include <iostream>

// itk includes
#include "itkImage.h"
#include "itkMesh.h"
#include "itkRegularSphereMeshSource.h"
#include "itkMeshFileWriter.h"
#include "itkMeshIOFactory.h"
#include "itkVTKPolyDataMeshIOFactory.h"

// shapeworks includes
#include <Project/Project.h>
#include <Groom/Groom.h>
#include <Optimize/OptimizeParameters.h>
#include <Optimize/Optimize.h>
#include <Analyze/Analyze.h>

constexpr unsigned int Dimension = 3;
using TCoordinate = float;
using TMesh = itk::Mesh<TCoordinate, Dimension>;
using TSphere = itk::RegularSphereMeshSource<TMesh>;
using TMeshWriter = itk::MeshFileWriter<TMesh>;

void create_sphere(double radius, std::string name) {
  // Create the sphere source.
  auto sphere = TSphere::New();

  TSphere::VectorType scale;
  scale.Fill( radius );
  sphere->SetScale( scale );
  sphere->SetResolution( 5 );
  sphere->Update();

  // We now assign it to a mesh pointer.
  TMesh::Pointer mesh = sphere->GetOutput();

  // It is necessary to disconnect the mesh from the pipeline;
  // otherwise, the point and cell data will be deallocated
  // when we call "Update()" on the writer later in the program.
  mesh->DisconnectPipeline();

  auto mesh_writer = TMeshWriter::New();
  mesh_writer->SetFileName(name);
  mesh_writer->SetInput(mesh);
  mesh_writer->Update();
}

int main() {

  itk::VTKPolyDataMeshIOFactory::RegisterOneFactory();
  // auto registeredIOs = itk::ObjectFactoryBase::CreateAllInstance( "itkMeshIOBase" );

  typedef itk::Image< unsigned short, 3 > ImageType;
  ImageType::Pointer image = ImageType::New();

  std::cout << "Hello ShapeWorks ITK World!" << std::endl;

  // create a ShapeWorks Project
  auto project = std::make_shared<shapeworks::Project>();

  std::cout << "Step 1: Use ITK to generate input data\n";
  for (int i=5;i<9;i++) {
    auto filename = "mesh" + std::to_string(i) + ".vtk";
    std::cout << "Creating input file: " << filename << "\n";
    create_sphere(i, filename);
    auto subject = std::make_shared<shapeworks::Subject>();
    subject->set_original_filenames({filename});
    project->get_subjects().push_back(subject);
  }

  // groom the inputs
  std::cout << "Step 2: Groom Inputs\n";
  shapeworks::Groom groom{project};
  groom.run();

  // create a ShapeWorks Optimizer
  std::cout << "Step 3: Creating Shape Model\n";
  shapeworks::Optimize optimize;
  shapeworks::OptimizeParameters params(project);
  params.set_up_optimize(&optimize);
  optimize.SetProject(project);

  // run the optimizer
  bool success = optimize.Run();
  project->save("HelloShapeWorksItk.swproj");

  // perform analysis
  std::cout << "Step 4: Analyze\n";
  shapeworks::Analyze analyze{project};
  analyze.run_offline_analysis("analysis.json");

  return 0;
}

Configure using -DShapeWorks_DIR and the prefix module path used for building ShapeWorks. For example:

cd /path/to/shapeworks/Examples/C++
mkdir build
cd build
cmake .. -DShapeWorks_DIR=</path/to/sw/install>/lib/cmake/ShapeWorks -DCMAKE_PREFIX_PATH=</path/to/shapeworks/dependencies/install>

Build:

make

Run:

./HelloShapeWorksItk

Output:

Hello ShapeWorks ITK World!
Step 1: Use ITK to generate input data
Creating input file: mesh5.vtk
Creating input file: mesh6.vtk
Creating input file: mesh7.vtk
Creating input file: mesh8.vtk
Step 2: Groom Inputs
Step 3: Creating Shape Model
ShapeWorks: TBB using 16 threads
Verbosity 0: This will be the only output on your screen, unless there are any errors. Increase the verbosity if needed.
Step 4: Analyze
[2022-10-02 23:31:39.663] [info] ShapeWorks Offline Analysis
[2022-10-02 23:31:39.663] [info] number of subjects: 4
[2022-10-02 23:31:39.665] [info] Computing stats...
[2022-10-02 23:31:39.665] [info] Computed stats successfully
[2022-10-02 23:31:39.665] [info] number of modes: 3
[2022-10-02 23:31:39.676] [info] eigen value [0]: 213.72417259473602
[2022-10-02 23:31:39.676] [info] explained_variance [0]: 99.12
[2022-10-02 23:31:39.676] [info] cumulative_explained_variance [0]: 99.12
[2022-10-02 23:31:39.793] [info] eigen value [1]: 1.8944878261819007
[2022-10-02 23:31:39.793] [info] explained_variance [1]: 0.88
[2022-10-02 23:31:39.793] [info] cumulative_explained_variance [1]: 100.00
[2022-10-02 23:31:39.902] [info] eigen value [2]: 0.0016418139547566765
[2022-10-02 23:31:39.902] [info] explained_variance [2]: 0.00
[2022-10-02 23:31:39.902] [info] cumulative_explained_variance [2]: 100.00