@@ -5,6 +5,18 @@ import "regenerator-runtime/runtime";
55
66import DICOM_TAG_DICT from './dicomTags'
77
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+
820class DICOMEntity {
921 constructor ( ) {
1022 this . metaData = { }
@@ -120,6 +132,72 @@ class DICOMSeries extends DICOMEntity {
120132 }
121133 this . images [ imageNumber ] = new DICOMImage ( metaData , file )
122134 }
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+ }
123201}
124202
125203class DICOMImage extends DICOMEntity {
@@ -147,6 +225,7 @@ class DICOMImage extends DICOMEntity {
147225 'BitsStored' ,
148226 'HighBit' ,
149227 'PixelRepresentation' ,
228+ 'PixelData'
150229 ]
151230 }
152231
@@ -228,55 +307,63 @@ async function parseDicomFiles(fileList, ignoreFailedFiles = false) {
228307 return
229308 }
230309
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
235325 }
236- vr = tagInfo . vr
237- }
238326
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' :
269329 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' :
271335 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+ }
280367 }
281368
282369 metaData [ tagName ] = value
0 commit comments