Skip to content

Commit dc4efb4

Browse files
committed
feat(formats): add initial DICOM format support
Reads a single DICOM file. Eventually we will also move to a DCMTK backend (see #10) and support multi-file series.
1 parent ddaf302 commit dc4efb4

8 files changed

Lines changed: 181 additions & 34 deletions

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ cmake_minimum_required(VERSION 2.8.9)
22
project(BridgeJavaScript)
33

44
set(BridgeJavaScript_IOModules
5-
"ITKIOPNG"
5+
"ITKIOGDCM"
66
"ITKIONRRD"
7+
"ITKIOPNG"
78
CACHE STRING
89
"String delimited list of ITK IO modules to support.")
910
set(imageio_ITKIOPNG itkPNGImageIO)
1011
set(imageio_ITKIONRRD itkNrrdImageIO)
12+
set(imageio_ITKIOGDCM itkGDCMImageIO)
1113

1214

1315
if(NOT ITK_SOURCE_DIR)

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
[![CircleCI](https://circleci.com/gh/InsightSoftwareConsortium/ITKBridgeJavaScript.svg?style=svg)](https://circleci.com/gh/InsightSoftwareConsortium/ITKBridgeJavaScript)
44

5+
Provides general scientific image IO capability and bridges
6+
[ITK](https://itk.org) code running in the
7+
[Emscripten](http://emscripten.org/) runtime environment.
8+
59
## Supported file formats
610

11+
- [DICOM](http://dicom.nema.org/)
712
- [NRRD](http://teem.sourceforge.net/nrrd/format.html)
813
- [Portable Network Graphics (PNG)](https://en.wikipedia.org/wiki/Portable_Network_Graphics)
914

@@ -30,8 +35,8 @@ npm test
3035

3136
### Contributing
3237

33-
We use semantic-release for handling change log and version.
34-
Therefore we recommend using the following command line when
38+
We use semantic-release for handling change log and version.
39+
Therefore we recommend using the following command line when
3540
creating a commit:
3641

3742
```sh

src/itkExtensionToIO.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
let extensionToIO = {}
22

3-
extensionToIO['png'] = 'itkPNGImageIOJSBinding'
4-
extensionToIO['PNG'] = 'itkPNGImageIOJSBinding'
3+
extensionToIO['dcm'] = 'itkGDCMImageIOJSBinding'
4+
55
extensionToIO['nrrd'] = 'itkNrrdImageIOJSBinding'
66
extensionToIO['NRRD'] = 'itkNrrdImageIOJSBinding'
7+
78
extensionToIO['nhdr'] = 'itkNrrdImageIOJSBinding'
89
extensionToIO['NHDR'] = 'itkNrrdImageIOJSBinding'
10+
extensionToIO['png'] = 'itkPNGImageIOJSBinding'
11+
extensionToIO['PNG'] = 'itkPNGImageIOJSBinding'
912

1013
module.exports = extensionToIO

src/itkGDCMImageIOJSBinding.cxx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*=========================================================================
2+
*
3+
* Copyright Insight Software Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#include <emscripten.h>
20+
#include <emscripten/bind.h>
21+
22+
#include "itkGDCMImageIO.h"
23+
24+
#include "itkImageIOBaseJSBinding.h"
25+
26+
typedef itk::ImageIOBaseJSBinding< itk::GDCMImageIO > GDCMImageIOJSBindingType;
27+
28+
EMSCRIPTEN_BINDINGS(itk_gdcm_image_io_js_binding) {
29+
emscripten::register_vector<double>("AxisDirectionType");
30+
emscripten::enum_<GDCMImageIOJSBindingType::IOPixelType>("IOPixelType")
31+
.value("UNKNOWNPIXELTYPE", itk::ImageIOBase::UNKNOWNPIXELTYPE)
32+
.value("SCALAR", itk::ImageIOBase::SCALAR)
33+
.value("RGB", itk::ImageIOBase::RGB)
34+
.value("RGBA", itk::ImageIOBase::RGBA)
35+
.value("OFFSET", itk::ImageIOBase::OFFSET)
36+
.value("VECTOR", itk::ImageIOBase::VECTOR)
37+
.value("POINT", itk::ImageIOBase::POINT)
38+
.value("COVARIANTVECTOR", itk::ImageIOBase::COVARIANTVECTOR)
39+
.value("SYMMETRICSECONDRANKTENSOR", itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
40+
.value("POINT", itk::ImageIOBase::POINT)
41+
.value("COVARIANTVECTOR", itk::ImageIOBase::COVARIANTVECTOR)
42+
.value("SYMMETRICSECONDRANKTENSOR", itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR)
43+
.value("DIFFUSIONTENSOR3D", itk::ImageIOBase::DIFFUSIONTENSOR3D)
44+
.value("COMPLEX", itk::ImageIOBase::COMPLEX)
45+
.value("FIXEDARRAY", itk::ImageIOBase::FIXEDARRAY)
46+
.value("MATRIX", itk::ImageIOBase::MATRIX)
47+
;
48+
emscripten::enum_<GDCMImageIOJSBindingType::IOComponentType>("IOComponentType")
49+
.value("UNKNOWNCOMPONENTTYPE", itk::ImageIOBase::UNKNOWNCOMPONENTTYPE)
50+
.value("UCHAR", itk::ImageIOBase::UCHAR)
51+
.value("CHAR", itk::ImageIOBase::CHAR)
52+
.value("USHORT", itk::ImageIOBase::USHORT)
53+
.value("SHORT", itk::ImageIOBase::SHORT)
54+
.value("UINT", itk::ImageIOBase::UINT)
55+
.value("INT", itk::ImageIOBase::INT)
56+
.value("ULONG", itk::ImageIOBase::ULONG)
57+
.value("LONG", itk::ImageIOBase::LONG)
58+
.value("FLOAT", itk::ImageIOBase::FLOAT)
59+
.value("DOUBLE", itk::ImageIOBase::DOUBLE)
60+
;
61+
emscripten::class_<GDCMImageIOJSBindingType>("ITKImageIO")
62+
.constructor<>()
63+
.function("SetNumberOfDimensions", &GDCMImageIOJSBindingType::SetNumberOfDimensions)
64+
.function("GetNumberOfDimensions", &GDCMImageIOJSBindingType::GetNumberOfDimensions)
65+
.function("SetFileName", &GDCMImageIOJSBindingType::SetFileName)
66+
.function("GetFileName", &GDCMImageIOJSBindingType::GetFileName)
67+
.function("CanReadFile", &GDCMImageIOJSBindingType::CanReadFile)
68+
.function("ReadImageInformation", &GDCMImageIOJSBindingType::ReadImageInformation)
69+
.function("SetDimensions", &GDCMImageIOJSBindingType::SetDimensions)
70+
.function("GetDimensions", &GDCMImageIOJSBindingType::GetDimensions)
71+
.function("SetOrigin", &GDCMImageIOJSBindingType::SetOrigin)
72+
.function("GetOrigin", &GDCMImageIOJSBindingType::GetOrigin)
73+
.function("SetSpacing", &GDCMImageIOJSBindingType::SetSpacing)
74+
.function("GetSpacing", &GDCMImageIOJSBindingType::GetSpacing)
75+
.function("SetDirection", &GDCMImageIOJSBindingType::SetDirection)
76+
.function("GetDirection", &GDCMImageIOJSBindingType::GetDirection)
77+
.function("GetDefaultDirection", &GDCMImageIOJSBindingType::GetDefaultDirection)
78+
.function("SetPixelType", &GDCMImageIOJSBindingType::SetPixelType)
79+
.function("GetPixelType", &GDCMImageIOJSBindingType::GetPixelType)
80+
.function("SetComponentType", &GDCMImageIOJSBindingType::SetComponentType)
81+
.function("GetComponentType", &GDCMImageIOJSBindingType::GetComponentType)
82+
.class_function("GetPixelTypeAsString", &GDCMImageIOJSBindingType::GetPixelTypeAsString)
83+
.class_function("GetComponentTypeAsString", &GDCMImageIOJSBindingType::GetComponentTypeAsString)
84+
.function("GetImageSizeInPixels", &GDCMImageIOJSBindingType::GetImageSizeInPixels)
85+
.function("GetImageSizeInBytes", &GDCMImageIOJSBindingType::GetImageSizeInBytes)
86+
.function("GetImageSizeInComponents", &GDCMImageIOJSBindingType::GetImageSizeInComponents)
87+
.function("SetNumberOfComponents", &GDCMImageIOJSBindingType::SetNumberOfComponents)
88+
.function("GetNumberOfComponents", &GDCMImageIOJSBindingType::GetNumberOfComponents)
89+
.function("Read", &GDCMImageIOJSBindingType::Read)
90+
;
91+
}

src/itkMimeToIO.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
let mimeToIO = {}
22

33
mimeToIO['image/png'] = 'itkPNGImageIOJSBinding'
4+
mimeToIO['application/dicom'] = 'itkGDCMImageIOJSBinding'
45

56
module.exports = mimeToIO

test/formatsTest.js

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,80 @@ itkConfig.imageIOsPath = path.resolve(__dirname, '..', 'dist', 'itkImageIOs')
1111
test('Test reading a PNG file', t => {
1212
const testFilePath = path.resolve(__dirname, '..', 'build', 'ExternalData', 'test', 'Input', 'cthead1.png')
1313
return readImageLocalFile(testFilePath).then(function (image) {
14-
t.is(image.imageType.dimension, 2)
15-
t.is(image.imageType.componentType, IntTypes.UInt8)
16-
t.is(image.imageType.pixelType, PixelTypes.RGB)
17-
t.is(image.imageType.components, 3)
18-
t.is(image.origin[0], 0.0)
19-
t.is(image.origin[1], 0.0)
20-
t.is(image.spacing[0], 1.0)
21-
t.is(image.spacing[1], 1.0)
22-
t.is(image.direction.getElement(0, 0), 1.0)
23-
t.is(image.direction.getElement(0, 1), 0.0)
24-
t.is(image.direction.getElement(1, 0), 0.0)
25-
t.is(image.direction.getElement(1, 1), 1.0)
26-
t.is(image.size[0], 256)
27-
t.is(image.size[1], 256)
28-
t.is(image.buffer.length, 196608)
14+
t.is(image.imageType.dimension, 2, 'dimension')
15+
t.is(image.imageType.componentType, IntTypes.UInt8, 'componentType')
16+
t.is(image.imageType.pixelType, PixelTypes.RGB, 'pixelType')
17+
t.is(image.imageType.components, 3, 'components')
18+
t.is(image.origin[0], 0.0, 'origin[0]')
19+
t.is(image.origin[1], 0.0, 'origin[1]')
20+
t.is(image.spacing[0], 1.0, 'spacing[0]')
21+
t.is(image.spacing[1], 1.0, 'spacing[1]')
22+
t.is(image.direction.getElement(0, 0), 1.0, 'direction (0, 0)')
23+
t.is(image.direction.getElement(0, 1), 0.0, 'direction (0, 1)')
24+
t.is(image.direction.getElement(1, 0), 0.0, 'direction (1, 0)')
25+
t.is(image.direction.getElement(1, 1), 1.0, 'direction (1, 1)')
26+
t.is(image.size[0], 256, 'size[0]')
27+
t.is(image.size[1], 256, 'size[1]')
28+
t.is(image.buffer.length, 196608, 'buffer.length')
2929
})
3030
})
3131

3232
test('Test reading a NRRD file', t => {
3333
const testFilePath = path.resolve(__dirname, '..', 'build', 'ExternalData', 'test', 'Input', 'vol-raw-little.nrrd')
3434
return readImageLocalFile(testFilePath).then(function (image) {
35-
t.is(image.imageType.dimension, 3)
36-
t.is(image.imageType.componentType, FloatTypes.Float64)
35+
t.is(image.imageType.dimension, 3, 'dimension')
36+
t.is(image.imageType.componentType, FloatTypes.Float64, 'componentType')
37+
t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType')
38+
t.is(image.imageType.components, 1, 'components')
39+
t.is(image.origin[0], 0.0, 'origin[0]')
40+
t.is(image.origin[1], 0.0, 'origin[1]')
41+
t.is(image.origin[2], 0.0, 'origin[2]')
42+
t.is(image.spacing[0], 1.0, 'spacing[0]')
43+
t.is(image.spacing[1], 1.0, 'spacing[1]')
44+
t.is(image.spacing[2], 1.0, 'spacing[2]')
45+
t.is(image.direction.getElement(0, 0), 1.0, 'direction (0, 0)')
46+
t.is(image.direction.getElement(0, 1), 0.0, 'direction (0, 1)')
47+
t.is(image.direction.getElement(0, 2), 0.0, 'direction (0, 2)')
48+
t.is(image.direction.getElement(1, 0), 0.0, 'direction (1, 0)')
49+
t.is(image.direction.getElement(1, 1), 1.0, 'direction (1, 1)')
50+
t.is(image.direction.getElement(1, 2), 0.0, 'direction (1, 2)')
51+
t.is(image.direction.getElement(2, 0), 0.0, 'direction (2, 0)')
52+
t.is(image.direction.getElement(2, 1), 0.0, 'direction (2, 1)')
53+
t.is(image.direction.getElement(2, 2), 1.0, 'direction (2, 2)')
54+
t.is(image.size[0], 5, 'size[0]')
55+
t.is(image.size[1], 6, 'size[1]')
56+
t.is(image.size[2], 7, 'size[2]')
57+
t.is(image.buffer.length, 210, 'buffer.length')
58+
t.is(image.buffer[2], 5.0, 'buffer[2]')
59+
})
60+
})
61+
62+
test('Test reading a DICOM file', t => {
63+
const testFilePath = path.resolve(__dirname, '..', 'build', 'ExternalData', 'test', 'Input', '1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.100.0.dcm')
64+
return readImageLocalFile(testFilePath).then(function (image) {
65+
t.is(image.imageType.dimension, 3, 'dimension')
66+
t.is(image.imageType.componentType, IntTypes.Int16)
3767
t.is(image.imageType.pixelType, PixelTypes.Scalar)
3868
t.is(image.imageType.components, 1)
39-
t.is(image.origin[0], 0.0)
40-
t.is(image.origin[1], 0.0)
41-
t.is(image.origin[2], 0.0)
69+
t.is(image.origin[0], -32.9551)
70+
t.is(image.origin[1], -133.9286)
71+
t.is(image.origin[2], 116.7857)
4272
t.is(image.spacing[0], 1.0)
4373
t.is(image.spacing[1], 1.0)
4474
t.is(image.spacing[2], 1.0)
45-
t.is(image.direction.getElement(0, 0), 1.0)
46-
t.is(image.direction.getElement(0, 1), 0.0)
47-
t.is(image.direction.getElement(1, 0), 0.0)
48-
t.is(image.direction.getElement(1, 1), 1.0)
49-
t.is(image.size[0], 5)
50-
t.is(image.size[1], 6)
51-
t.is(image.size[2], 7)
52-
t.is(image.buffer.length, 210)
53-
t.is(image.buffer[2], 5.0)
75+
t.is(image.direction.getElement(0, 0), 0.0, 'direction (0, 0)')
76+
t.is(image.direction.getElement(0, 1), 0.0, 'direction (0, 1)')
77+
t.is(image.direction.getElement(0, 2), -1.0, 'direction (0, 2)')
78+
t.is(image.direction.getElement(1, 0), 1.0, 'direction (1, 0)')
79+
t.is(image.direction.getElement(1, 1), 0.0, 'direction (1, 1)')
80+
t.is(image.direction.getElement(1, 2), 0.0, 'direction (1, 2)')
81+
t.is(image.direction.getElement(2, 0), 0.0, 'direction (2, 0)')
82+
t.is(image.direction.getElement(2, 1), -1.0, 'direction (2, 1)')
83+
t.is(image.direction.getElement(2, 2), 0.0, 'direction (2, 2)')
84+
t.is(image.size[0], 256, 'size[0]')
85+
t.is(image.size[1], 256, 'size[1]')
86+
t.is(image.size[2], 1, 'size[2]')
87+
t.is(image.buffer.length, 65536, 'buffer.length')
88+
t.is(image.buffer[1000], 3, 'buffer[1000]')
5489
})
5590
})

test/itkExtensionToIOTest.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ test('nhdr maps to itkNrrdImageIOJSBinding', t => {
1717
let io = ExtensionToIO['nhdr']
1818
t.is(io, 'itkNrrdImageIOJSBinding')
1919
})
20+
21+
test('dcm maps to itkGDCMImageIOJSBinding', t => {
22+
let io = ExtensionToIO['dcm']
23+
t.is(io, 'itkGDCMImageIOJSBinding')
24+
})

test/itkMimeToIOTest.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ test('image/png maps to itkPNGImageIOJSBinding', t => {
77
let io = MimeToIO['image/png']
88
t.is(io, 'itkPNGImageIOJSBinding')
99
})
10+
11+
test('application/dicom maps to itkGDCMImageIOJSBinding', t => {
12+
let io = MimeToIO['application/dicom']
13+
t.is(io, 'itkGDCMImageIOJSBinding')
14+
})

0 commit comments

Comments
 (0)