Skip to content

Commit

Permalink
Merge pull request #1165 from jadh4v/dcmqi-seg
Browse files Browse the repository at this point in the history
Dcmqi seg
  • Loading branch information
thewtex authored Aug 23, 2024
2 parents 9d2a2c6 + 3f8c02e commit d18468d
Show file tree
Hide file tree
Showing 128 changed files with 5,853 additions and 101 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
pnpm run --aggregate-output --filter "@itk-wasm/${{ matrix.package }}-build" build:python:wasi
- name: Test python on system
if: ${{ matrix.os == 'ubuntu-22.04' || (matrix.package != 'mesh-io' && matrix.package != 'image-io' && matrix.package != 'downsample' ) }}
if: ${{ matrix.package != 'dicom' && matrix.os == 'ubuntu-22.04' || (matrix.package != 'mesh-io' && matrix.package != 'dicom' && matrix.package != 'image-io' && matrix.package != 'downsample' ) }}
run: |
pnpm run --aggregate-output --filter "@itk-wasm/${{ matrix.package }}-build" test:python:wasi
Expand All @@ -81,6 +81,6 @@ jobs:
browser-version: latest

- name: Test python on chrome
if: ${{ matrix.python-minor-version > 10 && matrix.os == 'ubuntu-22.04' || (matrix.package != 'mesh-io' && matrix.package != 'image-io' && matrix.package != 'dicom' && matrix.os != 'macos-14' )}}
if: ${{ matrix.python-minor-version > 10 && matrix.package != 'dicom' && matrix.os == 'ubuntu-22.04' || (matrix.package != 'mesh-io' && matrix.package != 'image-io' && matrix.package != 'dicom' && matrix.os != 'macos-14' )}}
run: |
pnpm run --aggregate-output --filter "@itk-wasm/${{ matrix.package }}-build" test:python:emscripten

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const defaultImageTag = '20240810-4e9a4bf0'
const defaultImageTag = '20240822-c390b350'
export default defaultImageTag
172 changes: 172 additions & 0 deletions packages/dicom/dcmtk/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
find_package(ITK REQUIRED
COMPONENTS
ITKDCMTK
ITKLabelMap
ITKThresholding
WebAssemblyInterface
)
include(${ITK_USE_FILE})

if(POLICY CMP0077)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
endif()

include(FetchContent)
# DCMQI
set(DCMQI_GIT_REPOSITORY "https://github.com/jadh4v/dcmqi.git")
set(DCMQI_GIT_TAG "3d6401cf7a30c2c7db019784b8baee874590dea7")
set(DCMQI_SUPERBUILD OFF)
set(DCMQI_BUILD_SLICER_EXTENSION OFF)
set(DCMQI_BUILD_APPS OFF)
set(DCMQI_BUILD_DOC OFF)
#set(DCMQI_BUILD_TESTING ON)
set(DCMTK_DIR ${ITK_DIR}/Modules/ThirdParty/DCMTK/ITKDCMTK_ExtProject-build)
FetchContent_Declare(
dcmqi_lib
GIT_REPOSITORY ${DCMQI_GIT_REPOSITORY}
GIT_TAG ${DCMQI_GIT_TAG}
)

FetchContent_MakeAvailable(dcmqi_lib)
#set(BUILD_TESTING OFF)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)

list(APPEND DCMQI_INCLUDE_DIRS "${dcmqi_lib_SOURCE_DIR}/include" "${dcmqi_lib_SOURCE_DIR}/jsoncpp" "${dcmqi_lib_BINARY_DIR}/include")
include_directories(${DCMQI_INCLUDE_DIRS})

add_executable(structured-report-to-text structured-report-to-text.cxx)
target_link_libraries(structured-report-to-text PUBLIC ${ITK_LIBRARIES})

Expand All @@ -24,3 +54,145 @@ target_link_libraries(structured-report-to-html PUBLIC ${ITK_LIBRARIES})

add_executable(read-dicom-encapsulated-pdf read-dicom-encapsulated-pdf.cxx)
target_link_libraries(read-dicom-encapsulated-pdf PUBLIC ${ITK_LIBRARIES})

add_executable(read-segmentation read-segmentation.cxx)
target_link_libraries(read-segmentation PUBLIC ${ITK_LIBRARIES} dcmqi)

add_executable(read-overlapping-segmentation read-overlapping-segmentation.cxx)
target_link_libraries(read-overlapping-segmentation PUBLIC ${ITK_LIBRARIES} dcmqi)

add_executable(write-segmentation write-segmentation.cxx)
target_link_libraries(write-segmentation PUBLIC ${ITK_LIBRARIES} dcmqi)

add_executable(write-multi-segmentation write-multi-segmentation.cxx)
target_link_libraries(write-multi-segmentation PUBLIC ${ITK_LIBRARIES} dcmqi)

add_executable(write-overlapping-segmentation write-overlapping-segmentation.cxx)
target_link_libraries(write-overlapping-segmentation PUBLIC ${ITK_LIBRARIES} dcmqi)

if (WASI)
# Currently crashing on exit?
return()
endif()

set(TEMP_DIR ${CMAKE_BINARY_DIR}/Testing/Temporary)
set(BASELINE ${dcmqi_lib_SOURCE_DIR}/data/segmentations)
set(JSON_DIR ${dcmqi_lib_SOURCE_DIR}/doc/examples)
set(DICOM_DIR ${BASELINE}/ct-3slice)
set(MODULE_TEMP_DIR ${TEMP_DIR}/seg)
make_directory(${MODULE_TEMP_DIR})

set(dcm2itk read-segmentation)
set(itk2dcm write-segmentation)

add_test(
NAME write-segmentation_help
COMMAND write-segmentation --help
)

add_test(
NAME write-multi-segmentation_help
COMMAND write-multi-segmentation --help
)

add_test(
NAME write-overlapping-segmentation_help
COMMAND write-overlapping-segmentation --help
)

add_test(
NAME write-segmentation_makeSEG
COMMAND write-segmentation
${BASELINE}/liver_seg.nrrd
${JSON_DIR}/seg-example.json
${CMAKE_CURRENT_SOURCE_DIR}/../test/data/output/write-read-overlapping-segmentation-output_makeSEG.dcm
--ref-dicom-series ${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/01.dcm
${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/02.dcm
${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/03.dcm
)

add_test(
NAME write-multi-segmentation_makeSEG_merged_segment_files_from_partial_overlap
COMMAND write-multi-segmentation
${dcmqi_lib_SOURCE_DIR}/doc/examples/seg-example_partial_overlaps.json
${CMAKE_CURRENT_SOURCE_DIR}/../test/data/output/partial_overlaps.dcm
--ref-dicom-series ${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/01.dcm ${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/02.dcm ${dcmqi_lib_SOURCE_DIR}/data/segmentations/ct-3slice/03.dcm
--seg-images ${BASELINE}/partial_overlaps-1.nrrd ${BASELINE}/partial_overlaps-2.nrrd ${BASELINE}/partial_overlaps-3.nrrd
-l
)

add_test(
NAME write-multi-segmentation_makeSEG_multiple_segment_files
COMMAND write-multi-segmentation
${dcmqi_lib_SOURCE_DIR}/doc/examples/seg-example_multiple_segments.json
${MODULE_TEMP_DIR}/liver_heart_seg.dcm
--ref-dicom-series ${DICOM_DIR}/01.dcm ${DICOM_DIR}/02.dcm ${DICOM_DIR}/03.dcm
--seg-images ${BASELINE}/liver_seg.nrrd ${BASELINE}/spine_seg.nrrd ${BASELINE}/heart_seg.nrrd
)

add_test(
NAME write-multi-segmentation_makeSEG_multiple_segment_files_reordered
COMMAND write-multi-segmentation
${dcmqi_lib_SOURCE_DIR}/doc/examples/seg-example_multiple_segments_reordered.json
${MODULE_TEMP_DIR}/liver_heart_seg_reordered.dcm
--ref-dicom-series ${DICOM_DIR}/01.dcm ${DICOM_DIR}/02.dcm ${DICOM_DIR}/03.dcm
--seg-images ${BASELINE}/heart_seg.nrrd ${BASELINE}/liver_seg.nrrd ${BASELINE}/spine_seg.nrrd
)

add_test(
NAME read-segmentation_help
COMMAND read-segmentation --help
)

add_test(
NAME read-overlapping-segmentation_help
COMMAND read-overlapping-segmentation --help
)

add_test(
NAME read-segmentation_makeNRRD
COMMAND
read-segmentation
${dcmqi_lib_SOURCE_DIR}/data/segmentations/liver.dcm
${MODULE_TEMP_DIR}/makeNRRD-1.nrrd
${MODULE_TEMP_DIR}/makeNRRD-1.json
)

add_test(
NAME ${dcm2itk}_makeNRRD_merged_segment_files_from_partial_overlaps
COMMAND
${dcm2itk}
${BASELINE}/partial_overlaps.dcm
${MODULE_TEMP_DIR}/makeNRRD_merged_segment_files_from_partial_overlaps.nrrd
${MODULE_TEMP_DIR}/makeNRRD_merged_segment_files_from_partial_overlaps.json
)

set(TEST_SEG_SIZES 24x38x3 23x38x3)

foreach(seg_size ${TEST_SEG_SIZES})

add_test(
NAME ${itk2dcm}_makeSEG_${seg_size}
COMMAND ${itk2dcm}
${BASELINE}/${seg_size}/nrrd/label.nrrd
${dcmqi_lib_SOURCE_DIR}/doc/examples/seg-example.json
${MODULE_TEMP_DIR}/${seg_size}_seg.dcm
--ref-dicom-series
${BASELINE}/${seg_size}/image/IMG0001.dcm
${BASELINE}/${seg_size}/image/IMG0002.dcm
${BASELINE}/${seg_size}/image/IMG0003.dcm
)

add_test(
NAME ${dcm2itk}_makeNRRD_${seg_size}
COMMAND ${dcm2itk}
${MODULE_TEMP_DIR}/${seg_size}_seg.dcm
${MODULE_TEMP_DIR}/${seg_size}.nrrd
${MODULE_TEMP_DIR}/${seg_size}.json
)

set_tests_properties(${dcm2itk}_makeNRRD_${seg_size}
PROPERTIES DEPENDS ${itk2dcm}_makeSEG_${seg_size}
)

endforeach()
126 changes: 126 additions & 0 deletions packages/dicom/dcmtk/read-overlapping-segmentation.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*=========================================================================
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/

// DCMQI includes
// #undef HAVE_SSTREAM // Avoid redefinition warning
#include "dcmqi/Dicom2ItkConverter.h"
#include "dcmqi/internal/VersionConfigure.h"

// DCMTK includes
#include "dcmtk/oflog/configrt.h"

// ITK includes
#include "itkComposeImageFilter.h"
#include "itkVectorImage.h"

// ITK-wasm includes
#include "itkPipeline.h"
#include "itkOutputImage.h"
#include "itkOutputTextStream.h"

typedef dcmqi::Helper helper;
constexpr unsigned int Dimension = 3;
using PixelType = short;
using ScalarImageType = itk::Image<PixelType, Dimension>;
using VectorImageType = itk::VectorImage<PixelType, Dimension>;

int runPipeline(
const std::string & inputSEGFileName,
itk::wasm::OutputImage<VectorImageType>& outputImage,
itk::wasm::OutputTextStream& outputMetaInfoJSON,
const bool mergeSegments)
{
#if !defined(NDEBUG) || defined(_DEBUG)
// Display DCMTK debug, warning, and error logs in the console
dcmtk::log4cplus::BasicConfigurator::doConfigure();
#endif

if(helper::isUndefinedOrPathDoesNotExist(inputSEGFileName, "Input DICOM file"))
{
std::cerr << "ERROR: " << inputSEGFileName.c_str() << " is undefined or path does not exist." << std::endl;
return EXIT_FAILURE;
}

DcmRLEDecoderRegistration::registerCodecs();

DcmFileFormat sliceFF;
// std::cout << "Loading DICOM SEG file " << inputSEGFileName << std::endl;
CHECK_COND(sliceFF.loadFile(inputSEGFileName.c_str()));
DcmDataset* dataset = sliceFF.getDataset();

try
{
dcmqi::Dicom2ItkConverter converter;
std::string metaInfo;
OFCondition result = converter.dcmSegmentation2itkimage(dataset, metaInfo, mergeSegments);
if (result.bad())
{
std::cerr << "ERROR: Failed to convert DICOM SEG to ITK image: " << result.text() << std::endl;
return EXIT_FAILURE;
}

using ImageToVectorImageFilterType = itk::ComposeImageFilter<ScalarImageType>;
auto imageToVectorImageFilter = ImageToVectorImageFilterType::New();
int inputNumber = 0;
for (auto itkImage = converter.begin(); itkImage != nullptr; itkImage = converter.next())
{
imageToVectorImageFilter->SetInput(inputNumber++, itkImage);
}
imageToVectorImageFilter->Update();
VectorImageType::Pointer vectorImage = imageToVectorImageFilter->GetOutput();
if (!vectorImage)
{
std::cerr << "Failed to create VectorImage." << std::endl;
return EXIT_FAILURE;
}

outputImage.Set(vectorImage);
outputMetaInfoJSON.Get() << metaInfo.c_str();
return EXIT_SUCCESS;
}
catch (int e)
{
std::cerr << "Fatal error encountered." << std::endl;
return EXIT_FAILURE;
}
}

int main(int argc, char * argv[])
{
itk::wasm::Pipeline pipeline("read-overlapping-segmentation", "Read DICOM segmentation object with overlapping segments into a VectorImage.", argc, argv);

std::string dicomFileName;
pipeline.add_option("dicom-file", dicomFileName, "Input DICOM file")->required()->check(CLI::ExistingFile)->type_name("INPUT_BINARY_FILE");

itk::wasm::OutputImage<VectorImageType> outputImage;
pipeline.add_option("seg-image", outputImage, "dicom segmentation object as an image")->required()->type_name("OUTPUT_IMAGE");

itk::wasm::OutputTextStream outputMetaInfoJSON;
pipeline.add_option("meta-info", outputMetaInfoJSON, "Output overlay information")->type_name("OUTPUT_JSON");

bool mergeSegments{false};
pipeline.add_flag("--merge-segments", mergeSegments, "Merge segments into a single image");

ITK_WASM_PARSE(pipeline);

// Pipeline code goes here
runPipeline(dicomFileName, outputImage, outputMetaInfoJSON, mergeSegments);

return EXIT_SUCCESS;
}

Loading

0 comments on commit d18468d

Please sign in to comment.