@@ -5,6 +5,18 @@ import "regenerator-runtime/runtime";
5
5
6
6
import DICOM_TAG_DICT from './dicomTags'
7
7
8
+ function concatenate ( resultConstructor , arrays ) {
9
+ const totalLength = arrays . reduce ( ( total , arr ) => {
10
+ return total + arr . length
11
+ } , 0 ) ;
12
+ const result = new resultConstructor ( totalLength ) ;
13
+ arrays . reduce ( ( offset , arr ) => {
14
+ result . set ( arr , offset ) ;
15
+ return offset + arr . length ;
16
+ } , 0 ) ;
17
+ return result ;
18
+ }
19
+
8
20
class DICOMEntity {
9
21
constructor ( ) {
10
22
this . metaData = { }
@@ -120,6 +132,72 @@ class DICOMSeries extends DICOMEntity {
120
132
}
121
133
this . images [ imageNumber ] = new DICOMImage ( metaData , file )
122
134
}
135
+
136
+ getImageData ( ) {
137
+ function numArrayFromString ( str , separator = '\\' ) {
138
+ const strArray = str . split ( separator )
139
+ return strArray . map ( Number )
140
+ }
141
+
142
+ const slices = Object . values ( this . images )
143
+ const meta = slices [ 0 ] . metaData
144
+ const origin = numArrayFromString ( meta . ImagePositionPatient )
145
+ const spacing = numArrayFromString ( meta . PixelSpacing )
146
+ spacing . push ( Number ( meta . SliceThickness ) )
147
+ const size = [
148
+ meta . Rows ,
149
+ meta . Columns ,
150
+ Object . keys ( this . images ) . length
151
+ ]
152
+ const directionCosines = numArrayFromString ( meta . ImageOrientationPatient )
153
+ const iDirCos = directionCosines . slice ( 0 , 3 )
154
+ const jDirCos = directionCosines . slice ( 3 , 6 )
155
+ const kDirCos = [
156
+ iDirCos [ 1 ] * jDirCos [ 2 ] - iDirCos [ 2 ] * jDirCos [ 1 ] ,
157
+ iDirCos [ 2 ] * jDirCos [ 0 ] - iDirCos [ 0 ] * jDirCos [ 2 ] ,
158
+ iDirCos [ 0 ] * jDirCos [ 1 ] - iDirCos [ 1 ] * jDirCos [ 0 ] ,
159
+ ]
160
+ const direction = [
161
+ iDirCos [ 0 ] , jDirCos [ 0 ] , kDirCos [ 0 ] ,
162
+ iDirCos [ 1 ] , jDirCos [ 1 ] , kDirCos [ 1 ] ,
163
+ iDirCos [ 2 ] , jDirCos [ 2 ] , kDirCos [ 2 ] ,
164
+ ]
165
+
166
+ const unsigned = ( meta . PixelRepresentation === 0 )
167
+ const bits = meta . BitsAllocated
168
+ let ArrayType
169
+ switch ( bits ) {
170
+ case 8 :
171
+ ArrayType = unsigned ? Uint8Array : Int8Array
172
+ break
173
+ case 16 :
174
+ ArrayType = unsigned ? Uint16Array : Int16Array
175
+ break
176
+ case 32 :
177
+ ArrayType = unsigned ? Uint32Array : Int32Array
178
+ break
179
+ }
180
+
181
+ const pixelDataArrays = slices . map ( ( image ) => {
182
+ const value = image . metaData . PixelData
183
+ return new ArrayType ( value . buffer , value . offset )
184
+ } )
185
+ const data = concatenate ( ArrayType , pixelDataArrays )
186
+ const imageType = {
187
+ // TODO: should be based on PhotometricInterpretation instead?
188
+ // pixelType: meta.PixelRepresentation,
189
+ components : meta . SamplesPerPixel
190
+ }
191
+
192
+ return {
193
+ imageType,
194
+ origin,
195
+ spacing,
196
+ direction,
197
+ size,
198
+ data
199
+ }
200
+ }
123
201
}
124
202
125
203
class DICOMImage extends DICOMEntity {
@@ -147,6 +225,7 @@ class DICOMImage extends DICOMEntity {
147
225
'BitsStored' ,
148
226
'HighBit' ,
149
227
'PixelRepresentation' ,
228
+ 'PixelData'
150
229
]
151
230
}
152
231
@@ -228,55 +307,63 @@ async function parseDicomFiles(fileList, ignoreFailedFiles = false) {
228
307
return
229
308
}
230
309
231
- let vr = element . vr
232
- if ( vr === undefined ) {
233
- if ( tagInfo === undefined || tagInfo . vr === undefined ) {
234
- console . warn ( `${ tagName } vr is unknown, skipping` )
310
+ let value = undefined
311
+
312
+ if ( tagName === 'PixelData' ) {
313
+ value = {
314
+ buffer : dataSet . byteArray . buffer ,
315
+ offset : element . dataOffset ,
316
+ length : element . length
317
+ }
318
+ } else {
319
+ let vr = element . vr
320
+ if ( vr === undefined ) {
321
+ if ( tagInfo === undefined || tagInfo . vr === undefined ) {
322
+ console . warn ( `${ tagName } vr is unknown, skipping` )
323
+ }
324
+ vr = tagInfo . vr
235
325
}
236
- vr = tagInfo . vr
237
- }
238
326
239
- let value = undefined
240
- switch ( vr ) {
241
- case 'US' :
242
- value = dataSet . uint16 ( tag )
243
- break
244
- case 'SS' :
245
- value = dataSet . int16 ( tag )
246
- break
247
- case 'UL' :
248
- value = dataSet . uint32 ( tag )
249
- break
250
- case 'US' :
251
- value = dataSet . int32 ( tag )
252
- break
253
- case 'FD' :
254
- value = dataSet . double ( tag )
255
- break
256
- case 'FL' :
257
- value = dataSet . float ( tag )
258
- break
259
- case 'AT' :
260
- value = `(${ dataSet . uint16 ( tag , 0 ) } ,${ dataSet . uint16 ( tag , 1 ) } )`
261
- break
262
- case 'OB' :
263
- case 'OW' :
264
- case 'UN' :
265
- case 'OF' :
266
- case 'UT' :
267
- // TODO: binary data? is this correct?
268
- if ( element . length === 2 ) {
327
+ switch ( vr ) {
328
+ case 'US' :
269
329
value = dataSet . uint16 ( tag )
270
- } else if ( element . length === 4 ) {
330
+ break
331
+ case 'SS' :
332
+ value = dataSet . int16 ( tag )
333
+ break
334
+ case 'UL' :
271
335
value = dataSet . uint32 ( tag )
272
- } else {
273
- // don't store binary data, only meta data
274
- return
275
- }
276
- break
277
- default : //string
278
- value = dataSet . string ( tag )
279
- break
336
+ break
337
+ case 'US' :
338
+ value = dataSet . int32 ( tag )
339
+ break
340
+ case 'FD' :
341
+ value = dataSet . double ( tag )
342
+ break
343
+ case 'FL' :
344
+ value = dataSet . float ( tag )
345
+ break
346
+ case 'AT' :
347
+ value = `(${ dataSet . uint16 ( tag , 0 ) } ,${ dataSet . uint16 ( tag , 1 ) } )`
348
+ break
349
+ case 'OB' :
350
+ case 'OW' :
351
+ case 'UN' :
352
+ case 'OF' :
353
+ case 'UT' :
354
+ // TODO: binary data? is this correct?
355
+ if ( element . length === 2 ) {
356
+ value = dataSet . uint16 ( tag )
357
+ } else if ( element . length === 4 ) {
358
+ value = dataSet . uint32 ( tag )
359
+ } else {
360
+ return
361
+ }
362
+ break
363
+ default : //string
364
+ value = dataSet . string ( tag )
365
+ break
366
+ }
280
367
}
281
368
282
369
metaData [ tagName ] = value
0 commit comments