1- import { VLWholeSlideMicroscopyImage , getFrameMapping } from './metadata.js'
1+ import { getFrameMapping } from './metadata.js'
22import *
33as DICOMwebClient from 'dicomweb-client'
44import {
@@ -119,68 +119,75 @@ class _Channel {
119119 * determine the image pyramid structure, i.e. the size and resolution
120120 * images at the different pyramid levels.
121121 */
122-
123122 const geometryArrays = _Channel . deriveImageGeometry ( this )
124-
123+ const opticalPathIdentifier = this . blendingInformation . opticalPathIdentifier
125124 // Check frame of reference
126125 if ( referenceFrameOfReferenceUID !== this . FrameOfReferenceUID ) {
127126 throw new Error (
128- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
129- ' image has different FrameOfReferenceUID respect to the reference optical path ' +
130- referenceOpticalPathIdentifier
127+ `Image with optical path "${ opticalPathIdentifier } "` +
128+ 'has different FrameOfReferenceUID with respect to the reference ' +
129+ 'image with optical path ' +
130+ `"${ referenceOpticalPathIdentifier } ".`
131131 )
132132 }
133133
134134 // Check container identifier
135135 if ( referenceContainerIdentifier !== this . ContainerIdentifier ) {
136136 throw new Error (
137- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
138- ' image has different ContainerIdentifier respect to the reference optical path ' +
139- referenceOpticalPathIdentifier
137+ `Image with optical path "${ opticalPathIdentifier } "` +
138+ 'has different ContainerIdentifier with respect to the reference ' +
139+ 'image with optical path ' +
140+ `"${ referenceOpticalPathIdentifier } ".`
140141 )
141142 }
142143
143144 // Check that all the channels have the same pyramid parameters
144145 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 0 ] , referenceExtent ) ) {
145146 throw new Error (
146- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
147- ' image has incompatible extent respect to the reference optical path ' +
148- referenceOpticalPathIdentifier
147+ `Image with optical path "${ opticalPathIdentifier } "` +
148+ 'has an incompatible extent with respect to the reference ' +
149+ 'image with optical path ' +
150+ `"${ referenceOpticalPathIdentifier } ".`
149151 )
150152 }
151153 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 1 ] , referenceOrigins ) ) {
152154 throw new Error (
153- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
154- ' image has incompatible origins respect to the reference optical path ' +
155- referenceOpticalPathIdentifier
155+ `Image with optical path "${ opticalPathIdentifier } "` +
156+ 'has incompatible origins with respect to the reference ' +
157+ 'image with optical path ' +
158+ `"${ referenceOpticalPathIdentifier } ".`
156159 )
157160 }
158161 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 2 ] , referenceResolutions ) ) {
159162 throw new Error (
160- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
161- ' image has incompatible resolutions respect to the reference optical path ' +
162- referenceOpticalPathIdentifier
163+ `Image with optical path "${ opticalPathIdentifier } "` +
164+ 'has incompatible resolutions with respect to the reference ' +
165+ 'image with optical path ' +
166+ `"${ referenceOpticalPathIdentifier } ".`
163167 )
164168 }
165169 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 3 ] , referenceGridSizes ) ) {
166170 throw new Error (
167- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
168- ' image has incompatible grid sizes respect to the reference optical path ' +
169- referenceOpticalPathIdentifier
171+ `Image with optical path "${ opticalPathIdentifier } "` +
172+ 'has incompatible grid sizes with respect to the reference ' +
173+ 'image with optical path ' +
174+ `"${ referenceOpticalPathIdentifier } ".`
170175 )
171176 }
172177 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 4 ] , referenceTileSizes ) ) {
173178 throw new Error (
174- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
175- ' image has incompatible tile sizes respect to the reference optical path ' +
176- referenceOpticalPathIdentifier
179+ `Image with optical path "${ opticalPathIdentifier } "` +
180+ 'has incompatible tile sizes with respect to the reference ' +
181+ 'image with optical path ' +
182+ `"${ referenceOpticalPathIdentifier } ".`
177183 )
178184 }
179185 if ( ! are2DArraysAlmostEqual ( geometryArrays [ 5 ] , referencePixelSpacings ) ) {
180186 throw new Error (
181- 'Optical path ' + this . blendingInformation . opticalPathIdentifier +
182- ' image has incompatible pixel spacings respect to the reference optical path ' +
183- referenceOpticalPathIdentifier
187+ `Image with optical path "${ opticalPathIdentifier } "` +
188+ 'has incompatible pixel spacings with respect to the reference ' +
189+ 'image with optical path ' +
190+ `"${ referenceOpticalPathIdentifier } ".`
184191 )
185192 }
186193
@@ -197,7 +204,6 @@ class _Channel {
197204 * viewport. Note that this is in contrast to the nomenclature used
198205 * by Openlayers.
199206 */
200-
201207 const z = tileCoord [ 0 ]
202208 const y = tileCoord [ 1 ] + 1
203209 const x = tileCoord [ 2 ] + 1
@@ -226,9 +232,9 @@ class _Channel {
226232 const z = tile . tileCoord [ 0 ]
227233 const columns = this . pyramidMetadata [ z ] . Columns
228234 const rows = this . pyramidMetadata [ z ] . Rows
229- const samplesPerPixel = this . pyramidMetadata [ z ] . SamplesPerPixel // number of colors for pixel
230- const bitsAllocated = this . pyramidMetadata [ z ] . BitsAllocated // memory for pixel
231- const pixelRepresentation = this . pyramidMetadata [ z ] . PixelRepresentation // 0 unsigned, 1 signed
235+ const samplesPerPixel = this . pyramidMetadata [ z ] . SamplesPerPixel
236+ const bitsAllocated = this . pyramidMetadata [ z ] . BitsAllocated
237+ const pixelRepresentation = this . pyramidMetadata [ z ] . PixelRepresentation
232238
233239 if ( src !== null && samplesPerPixel === 1 ) {
234240 const studyInstanceUID = DICOMwebClient . utils . getStudyInstanceUIDFromUri ( src )
@@ -241,12 +247,13 @@ class _Channel {
241247 tile . isLoading = true
242248
243249 if ( options . retrieveRendered ) {
244- // allowed mediaTypes: http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_8.7.4.html
245- // we use in order: jp2, jpeg.
246- // we could add png, but at the moment we don't have a png decoder library and we would have to draw
247- // to a canvas, retieve the imageData and then recompat the array from a RGBA to a 1 component array
248- // for the offscreen rendering engine, which provides poor perfomances.
249-
250+ /*
251+ * We could use PNG, but at the moment we don't have a PNG decoder
252+ * library and thus would have to draw to a canvas, retrieve the
253+ * imageData and then recompat the array from a RGBA to a 1 component
254+ * array for the offscreen rendering engine, which would result in
255+ * poor perfomance.
256+ */
250257 const jp2MediaType = 'image/jp2' // decoded with OpenJPEG
251258 const jpegMediaType = 'image/jpeg' // decoded with libJPEG-turbo
252259 const transferSyntaxUID = ''
@@ -288,15 +295,14 @@ class _Channel {
288295 rows
289296 }
290297
291- const rendered = renderingEngine . colorMonochromeImageFrame ( frameData )
298+ const rendered = renderingEngine . colorMonochromeImageFrame (
299+ frameData
300+ )
292301 tile . needToRerender = ! rendered
293302 tile . isLoading = false
294303 }
295304 )
296305 } else {
297- // allowed mediaTypes: http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_8.7.3.3.2.html
298- // we use in order: jls, jp2, jpx, jpeg. Finally octet-stream if the first retrieve will fail.
299-
300306 const jlsMediaType = 'image/jls' // decoded with CharLS
301307 const jlsTransferSyntaxUIDlossless = '1.2.840.10008.1.2.4.80'
302308 const jlsTransferSyntaxUID = '1.2.840.10008.1.2.4.81'
@@ -318,13 +324,34 @@ class _Channel {
318324 sopInstanceUID,
319325 frameNumbers,
320326 mediaTypes : [
321- { mediaType : jlsMediaType , transferSyntaxUID : jlsTransferSyntaxUIDlossless } ,
322- { mediaType : jlsMediaType , transferSyntaxUID : jlsTransferSyntaxUID } ,
323- { mediaType : jp2MediaType , transferSyntaxUID : jp2TransferSyntaxUIDlossless } ,
324- { mediaType : jp2MediaType , transferSyntaxUID : jp2TransferSyntaxUID } ,
325- { mediaType : jpxMediaType , transferSyntaxUID : jpxTransferSyntaxUIDlossless } ,
326- { mediaType : jpxMediaType , transferSyntaxUID : jpxTransferSyntaxUID } ,
327- { mediaType : jpegMediaType , transferSyntaxUID : jpegTransferSyntaxUID }
327+ {
328+ mediaType : jlsMediaType ,
329+ transferSyntaxUID : jlsTransferSyntaxUIDlossless
330+ } ,
331+ {
332+ mediaType : jlsMediaType ,
333+ transferSyntaxUID : jlsTransferSyntaxUID
334+ } ,
335+ {
336+ mediaType : jp2MediaType ,
337+ transferSyntaxUID : jp2TransferSyntaxUIDlossless
338+ } ,
339+ {
340+ mediaType : jp2MediaType ,
341+ transferSyntaxUID : jp2TransferSyntaxUID
342+ } ,
343+ {
344+ mediaType : jpxMediaType ,
345+ transferSyntaxUID : jpxTransferSyntaxUIDlossless
346+ } ,
347+ {
348+ mediaType : jpxMediaType ,
349+ transferSyntaxUID : jpxTransferSyntaxUID
350+ } ,
351+ {
352+ mediaType : jpegMediaType ,
353+ transferSyntaxUID : jpegTransferSyntaxUID
354+ }
328355 ]
329356 }
330357
@@ -350,7 +377,9 @@ class _Channel {
350377 rows
351378 }
352379
353- const rendered = renderingEngine . colorMonochromeImageFrame ( frameData )
380+ const rendered = renderingEngine . colorMonochromeImageFrame (
381+ frameData
382+ )
354383 tile . needToRerender = ! rendered
355384 tile . isLoading = false
356385 }
@@ -392,7 +421,9 @@ class _Channel {
392421 rows
393422 }
394423
395- const rendered = renderingEngine . colorMonochromeImageFrame ( frameData )
424+ const rendered = renderingEngine . colorMonochromeImageFrame (
425+ frameData
426+ )
396427 tile . needToRerender = ! rendered
397428 tile . isLoading = false
398429 }
@@ -452,39 +483,31 @@ class _Channel {
452483 * @static
453484 */
454485 static deriveImageGeometry ( image ) {
455- image . microscopyImages = [ ]
456- image . metadata . forEach ( m => {
457- const microscopyImage = new VLWholeSlideMicroscopyImage ( { metadata : m } )
458- if ( microscopyImage . ImageType [ 2 ] === 'VOLUME' ) {
459- image . microscopyImages . push ( microscopyImage )
460- }
461- } )
462-
463- if ( image . microscopyImages . length === 0 ) {
486+ if ( image . metadata . length === 0 ) {
464487 throw new Error ( 'No VOLUME image provided for Optioncal Path ID: ' +
465488 image . blendingInformation . opticalPathIdentifier )
466489 }
467490
468- image . FrameOfReferenceUID = image . microscopyImages [ 0 ] . FrameOfReferenceUID
469- for ( let i = 0 ; i < image . microscopyImages . length ; ++ i ) {
470- if ( image . FrameOfReferenceUID !== image . microscopyImages [ i ] . FrameOfReferenceUID ) {
491+ image . FrameOfReferenceUID = image . metadata [ 0 ] . FrameOfReferenceUID
492+ for ( let i = 0 ; i < image . metadata . length ; ++ i ) {
493+ if ( image . FrameOfReferenceUID !== image . metadata [ i ] . FrameOfReferenceUID ) {
471494 throw new Error ( 'Optioncal Path ID ' +
472495 image . blendingInformation . opticalPathIdentifier +
473496 ' has volume microscopy images with different FrameOfReferenceUID' )
474497 }
475498 }
476499
477- image . ContainerIdentifier = image . microscopyImages [ 0 ] . ContainerIdentifier
478- for ( let i = 0 ; i < image . microscopyImages . length ; ++ i ) {
479- if ( image . ContainerIdentifier !== image . microscopyImages [ i ] . ContainerIdentifier ) {
500+ image . ContainerIdentifier = image . metadata [ 0 ] . ContainerIdentifier
501+ for ( let i = 0 ; i < image . metadata . length ; ++ i ) {
502+ if ( image . ContainerIdentifier !== image . metadata [ i ] . ContainerIdentifier ) {
480503 throw new Error ( 'Optioncal Path ID ' +
481504 image . blendingInformation . opticalPathIdentifier +
482505 ' has volume microscopy images with different ContainerIdentifier' )
483506 }
484507 }
485508
486509 // Sort instances and optionally concatenation parts if present.
487- image . microscopyImages . sort ( ( a , b ) => {
510+ image . metadata . sort ( ( a , b ) => {
488511 const sizeDiff = a . TotalPixelMatrixColumns - b . TotalPixelMatrixColumns
489512 if ( sizeDiff === 0 ) {
490513 if ( a . ConcatenationFrameOffsetNumber !== undefined ) {
@@ -497,11 +520,11 @@ class _Channel {
497520
498521 image . pyramidMetadata = [ ]
499522 image . pyramidFrameMappings = [ ]
500- const frameMappings = image . microscopyImages . map ( m => getFrameMapping ( m ) )
501- for ( let i = 0 ; i < image . microscopyImages . length ; i ++ ) {
502- const cols = image . microscopyImages [ i ] . TotalPixelMatrixColumns
503- const rows = image . microscopyImages [ i ] . TotalPixelMatrixRows
504- const numberOfFrames = image . microscopyImages [ i ] . NumberOfFrames
523+ const frameMappings = image . metadata . map ( m => getFrameMapping ( m ) )
524+ for ( let i = 0 ; i < image . metadata . length ; i ++ ) {
525+ const cols = image . metadata [ i ] . TotalPixelMatrixColumns
526+ const rows = image . metadata [ i ] . TotalPixelMatrixRows
527+ const numberOfFrames = image . metadata [ i ] . NumberOfFrames
505528 /*
506529 * Instances may be broken down into multiple concatentation parts.
507530 * Therefore, we have to re-assemble instance metadata.
@@ -521,25 +544,25 @@ class _Channel {
521544 // Update with information obtained from current concatentation part.
522545 Object . assign ( image . pyramidFrameMappings [ index ] , frameMappings [ i ] )
523546 image . pyramidMetadata [ index ] . NumberOfFrames += numberOfFrames
524- if ( 'PerFrameFunctionalGroupsSequence' in image . microscopyImages [ index ] ) {
547+ if ( 'PerFrameFunctionalGroupsSequence' in image . metadata [ index ] ) {
525548 image . pyramidMetadata [ index ] . PerFrameFunctionalGroupsSequence . push (
526- ...image . microscopyImages [ i ] . PerFrameFunctionalGroupsSequence
549+ ...image . metadata [ i ] . PerFrameFunctionalGroupsSequence
527550 )
528551 }
529- if ( ! ( 'SOPInstanceUIDOfConcatenationSource' in image . microscopyImages [ i ] ) ) {
552+ if ( ! ( 'SOPInstanceUIDOfConcatenationSource' in image . metadata [ i ] ) ) {
530553 throw new Error (
531554 'Attribute "SOPInstanceUIDOfConcatenationSource" is required ' +
532555 'for concatenation parts.'
533556 )
534557 }
535- const sopInstanceUID = image . microscopyImages [ i ] . SOPInstanceUIDOfConcatenationSource
558+ const sopInstanceUID = image . metadata [ i ] . SOPInstanceUIDOfConcatenationSource
536559 image . pyramidMetadata [ index ] . SOPInstanceUID = sopInstanceUID
537560 delete image . pyramidMetadata [ index ] . SOPInstanceUIDOfConcatenationSource
538561 delete image . pyramidMetadata [ index ] . ConcatenationUID
539562 delete image . pyramidMetadata [ index ] . InConcatenationNumber
540563 delete image . pyramidMetadata [ index ] . ConcatenationFrameOffsetNumber
541564 } else {
542- image . pyramidMetadata . push ( image . microscopyImages [ i ] )
565+ image . pyramidMetadata . push ( image . metadata [ i ] )
543566 image . pyramidFrameMappings . push ( frameMappings [ i ] )
544567 }
545568 }
@@ -552,7 +575,7 @@ class _Channel {
552575 /*
553576 * Collect relevant information from DICOM metadata for each pyramid
554577 * level to construct the Openlayers map.
555- */
578+ */
556579 const imageTileSizes = [ ]
557580 const imageGridSizes = [ ]
558581 const imageResolutions = [ ]
@@ -627,7 +650,9 @@ class _Channel {
627650 * @param {object } metadata
628651 */
629652 addMetadata ( metadata ) {
630- this . metadata . push ( metadata )
653+ if ( metadata . ImageType [ 2 ] === 'VOLUME' ) {
654+ this . metadata . push ( metadata )
655+ }
631656 }
632657
633658 /** Gets the channel visualization/presentation parameters
0 commit comments