Categories: C/C++

ITK 儲存、載入影像 Transform 轉換參數教學與範例

介紹如何使用 ITK 的 TransformFileWriterTransformFileReader 寫入與讀取各種 Transform 影像轉換與參數。

寫入與讀取轉換

以下是一個簡單的儲存與載入轉換範例,先以 AffineTransformBSplineTransform 組合建立一個 CompositeTransform 轉換,再使用 TransformFileWriterCompositeTransform 轉換與其參數寫入檔案,最後再以 TransformFileReader 將轉換從檔案中讀取出來使用。

#include <itkTransformFileReader.h>
#include <itkTransformFileWriter.h>
#include <itkAffineTransform.h>
#include <itkBSplineTransform.h>
#include <itkCompositeTransform.h>
#include <itkTransformFactory.h>

int main(int argc, char* argv[]) {
  if (argc < 2) {
    std::cerr << "Usage: " << argv[0] << " transformFile" << std::endl;
    return EXIT_FAILURE;
  }
  const char* transformFileName = argv[1];

  // ================================== //
  // 建立測試用 CompositeTransform 轉換 //
  // ================================== //

  // 指定轉換數值類型
  using ScalarType = double;

  // 影像維度
  constexpr unsigned int Dimension = 3;

  // 建立 CompositeTransform
  using CompositeTransformType =
    itk::CompositeTransform<ScalarType, Dimension>;
  CompositeTransformType::Pointer composite = CompositeTransformType::New();

  // 建立 AffineTransform
  using AffineTransformType = itk::AffineTransform<ScalarType, Dimension>;
  AffineTransformType::Pointer affine = AffineTransformType::New();

  // 設定旋轉中心點
  AffineTransformType::InputPointType center;
  center.Fill(12);
  affine->SetCenter(center);

  // 將 AffineTransform 加入 CompositeTransform
  composite->AddTransform(affine);

  // 建立 BSplineTransform
  constexpr unsigned int SplineOrder = 5;
  using BSplineTransformType =
    itk::BSplineTransform<ScalarType, Dimension, SplineOrder>;
  using BSplineTransformFType =
    itk::BSplineTransform<float, Dimension, SplineOrder>;

  // 在預設狀況下只有 3 階 BSpline 的轉換有註冊,
  // 此處手動註冊 5 階 BSpline 的轉換(包含 double 與 float)
  // 這樣才能讀取到正確的轉換
  itk::TransformFactory<BSplineTransformType>::RegisterTransform();
  itk::TransformFactory<BSplineTransformFType>::RegisterTransform();

  // 建立 BSplineTransform
  BSplineTransformType::Pointer bspline = BSplineTransformType::New();

  // 設定 BSplineTransform 相關參數
  BSplineTransformType::OriginType origin;
  origin.Fill(100);
  bspline->SetTransformDomainOrigin(origin);

  BSplineTransformType::PhysicalDimensionsType dimensions;
  dimensions.Fill(1.5 * 9.0);
  bspline->SetTransformDomainPhysicalDimensions(dimensions);

  BSplineTransformType::ParametersType parameters(
    bspline->GetNumberOfParameters());
  bspline->SetParameters(parameters);
  bspline->SetIdentity();

  // 將 BSplineTransform 加入 CompositeTransform
  composite->AddTransform(bspline);

  // ================================== //
  // 將 CompositeTransform 轉換寫入檔案 //
  // ================================== //

  // 建立 ScalarType 數值類型的 TransformFileWriter
  // 在寫入時數值類型會在必要時自動轉換
  using TransformWriterType = itk::TransformFileWriterTemplate<ScalarType>;
  TransformWriterType::Pointer writer = TransformWriterType::New();

  // TransformFileWriter 可接受各種 Transform 類型的轉換物件
  writer->SetInput(composite);

  // 設定檔案名稱
  writer->SetFileName(transformFileName);

  // 將轉換寫入檔案
  try {
    writer->Update();
  } catch (itk::ExceptionObject & excp) {
    std::cerr << "Error while saving the transforms" << std::endl;
    std::cerr << excp << std::endl;
    return EXIT_FAILURE;
  }

  // ================================== //
  // 從檔案讀取 CompositeTransform 轉換 //
  // ================================== //

  // 指定轉換數值類型
  using ReadScalarType = float;

  // 建立 ReadScalarType 數值類型的 TransformFileReader
  // 在讀取時數值類型會在必要時自動轉換
  using TransformReaderType =
    itk::TransformFileReaderTemplate<ReadScalarType>;
  TransformReaderType::Pointer reader = TransformReaderType::New();

  // 設定檔案名稱
  reader->SetFileName(transformFileName);

  // 讀取轉換
  try {
    reader->Update();
  } catch (itk::ExceptionObject & excp) {
    std::cerr << "Error while reading the transform file" << std::endl;
    std::cerr << excp << std::endl;
    std::cerr << "[FAILED]" << std::endl;
    return EXIT_FAILURE;
  }

  // 取得轉換列表
  const TransformReaderType::TransformListType * transforms =
    reader->GetTransformList();

  // 轉換數量
  std::cout << "Number of transforms = " << transforms->size() << std::endl;

  // 各個轉換可以使用 STL 的 iterator 取出,再轉型為適合的轉換型別
  using ReadCompositeTransformType =
    itk::CompositeTransform<ReadScalarType, Dimension>;
  auto it = transforms->begin();
  if (!strcmp((*it)->GetNameOfClass(), "CompositeTransform")) {
    ReadCompositeTransformType::Pointer compositeRead =
      static_cast<ReadCompositeTransformType *>((*it).GetPointer());
    compositeRead->Print(std::cout);
  }

  return EXIT_SUCCESS;
}

將此程式碼儲存為 TransformReadWrite.cxx,搭配以下 CMakeLists.txt 以 CMake 編譯:

cmake_minimum_required(VERSION 3.10.2)

# 設定專案名稱
project(TransformReadWrite)

# 尋找並引入 ITK 函式庫
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})

# 增加一個執行檔
add_executable(TransformReadWrite TransformReadWrite.cxx)

# 定義執行檔連結方式
target_link_libraries(TransformReadWrite ${ITK_LIBRARIES})

以下是編譯與執行的指令:

# 編譯程式
mkdir build
cd build
cmake ..

# 執行程式(以 txt 檔案儲存)
./TransformReadWrite transform.txt

# 執行程式(以 HDF5 檔案儲存)
./TransformReadWrite transform.h5

參考資料:TransformReadWrite.cxx

Share
Published by
Office Guide
Tags: ITK

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago