Repository navigation
feat(deps): move from dcmjs 0.52.0 to the @dcmjs-org/dcmjs 1.0 beta - #6397
Draft
wayfarer3130 wants to merge 23 commits into
Draft
wayfarer3130 wants to merge 23 commits into
wayfarer3130 wants to merge 23 commits into
Conversation
Replace the dcmjs dependency with @dcmjs-org/dcmjs pinned to 1.0.0-beta.5 and import its default export, which keeps the 0.52 API surface. - Rename the dependency in platform/app, platform/core and the extensions that declared it, and declare it in cornerstone-dicom-rt, cornerstone-dicom-seg and dicom-microscopy, which import it. - Exempt @dcmjs-org/* from minimumReleaseAge. - Keep the dcmjs 0.52.0 override for @cornerstonejs/adapters, metadata, utils and ai 5.12.0, which still depend on dcmjs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dcmjs 1.0 naturalizes a one-item sequence to the item object (not an array), keeps a one-value VM 1-n attribute as a list, gives a VM 1 PN as one object, and groups private tags into a creator block. Adapt the call sites that assumed the 0.x shapes, with asArray from @cornerstonejs/utils. - RT: load an RTSTRUCT with one ROI or one contour, and derive the referenced series from one-item reference sequences. - SR: show a PNAME item, render a container with one child, read a root content sequence with one item, and keep a numeric frame number. - Microscopy SR: read a root content sequence with one item. - Core: map an ultrasound image with one region. - TMTV: read the radiopharmaceutical item after an SUV metadata edit. - Video: match a one-value AvailableTransferSyntaxUID list. - Tag browser: list the private tags of a creator block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DICOMweb data source walked into a sequence only when the value was an array. With dcmjs 1.0 a one-item sequence is the item object, and a private element sits in a creator block, so bulk data there (ECG WaveformData, RTSTRUCT ContourData, SR GraphicData, the Philips SUV factor) got no absolute URI and no retrieveBulkData. - Move the walk to utils/forEachBulkDataValue, which also follows a one-item sequence and a private block, and test it. - Move the tag-browser key expansion to getDatasetKeys.ts and test it. - Use one guard, asArray(x ?? []), at the RT find and SR PNAME sites. - Name all CS3D packages that still bring dcmjs 0.52 in the override comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 of 6 tasks
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
✅ Deploy Preview for ohif-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
wayfarer3130
had a problem deploying
to
unrestricted
October 28, 2026 20:41 — with
GitHub Actions
Failure
Viewers
|
||||||||||||||||||||||||||||
| Project |
Viewers
|
| Branch Review |
feat/dcmjs-1.0-beta
|
| Run status |
|
| Run duration | 01m 59s |
| Commit |
|
| Committer | Bill Wallace |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
0
|
|
|
0
|
|
|
0
|
|
|
28
|
| View all changes introduced in this branch ↗︎ | |
…six extensions dicom-pdf, dicom-video, measurement-tracking, cornerstone-dynamic-volume and test-extension import nothing from dcmjs. cornerstone-dicom-sr uses it only in a unit test, so it moves to devDependencies there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The display set read ReferencedSeriesSequence.ReferencedInstanceSequence, which is undefined for a sequence with more than one item and for the sequence that the code builds from ReferencedFrameOfReferenceSequence. The bug existed with dcmjs 0.52. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent the dcmjs 1.0 shapes The DICOM JSON generator now writes the dcmjs 1.0 shapes. JSON keeps a private block (gggg:creator) but drops its non-enumerable hex tag aliases, so a read such as instance['70531000'] (Philips SUV scale factor) gave undefined. The data source now calls definePrivateAliases on each instance. A file that the 0.x generator wrote is not changed. The docs describe the shapes that the generator writes, and that OHIF reads the files of both generators. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…verlap save test dcmjs 1.0 gives the US 1-n value as a list. The test used the list as an object key and relied on the key coercion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wayfarer3130
had a problem deploying
to
unrestricted
October 29, 2026 15:03 — with
GitHub Actions
Failure
dcmjs 1.0 gives a private element as a list, also for one value, so (7053,1000) and (7053,1009) became [0.00038] and coerceNumber dropped them. PhilipsPETPrivateGroup was then missing, and SUV for Philips PET with Units CNTS lost its scale factor. Read asArray(value)[0] for the Philips scale factors and the GE post-injection date time. Add @cornerstonejs/utils 5.12.0 to the default extension for asArray, and a test that uses a dataset from the dcmjs 1.0 naturalizeDataset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lation Some DICOMweb sources write all CorrectedImage values as one string, for example ["DECY\\ATTN\\SCAT"]. dcmjs 0.52 gave the string, and the calculate-suv check for "ATTN" and "DECY" matched a substring. dcmjs 1.0 keeps the one-item list, so the check threw, the PT series got no SUV scaling, and the PT viewports showed raw values. This broke the RT, contour and TMTV CSV Playwright tests on the combined CS3D build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Some DICOMweb sources write all ImageType values as one string, for
example ["ORIGINAL\\PRIMARY\\VOLUME\\NONE"]. fixMultiValueKeys split only
a string, and dcmjs 1.0 keeps the one-item list, so denaturalizeDataset
truncated the value to "ORIGINAL\\PRIMARY". The WSI viewport and the
microscopy viewer then found no VOLUME image ("Reduce of empty array",
"Input metadata array is empty"). Split list items too, also for
FrameType, and use fixMultiValueKeys in the microscopy viewport.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wayfarer3130
temporarily deployed
to
unrestricted
October 29, 2026 19:51 — with
GitHub Actions
Inactive
…ate tag - getPTImageIdInstanceMetadata and fixMultiValueKeys use asMultiValueArray from @cornerstonejs/utils in place of local split code for CorrectedImage, ImageType and FrameType. - The GE post-injection date time (0009,100D) was read with the key '0009100d'. dcmjs (0.52 and 1.0) and DICOM JSON key private tags in upper-case hex, so the value was never found. calculate-suv now gets GEPrivatePostInjectionDateTime and uses it as the scan time for decay correction when SeriesDate/SeriesTime is after the earliest acquisition time, in place of that acquisition time. asMultiValueArray needs the CS3D release that contains it; the pinned @cornerstonejs/utils 5.12.0 does not have it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wayfarer3130
temporarily deployed
to
unrestricted
October 29, 2026 20:15 — with
GitHub Actions
Inactive
getPTImageIdInstanceMetadata reads the Philips (7053) and GE (0009) private tags by creator, so a multi-frame PT keeps its SUV factors after the combineFrameInstance copy. resolveBulkDataTags finds a registered private tag by its creator and writes the number into the dcmjs 1.0 private block; a registration can carry a creator. The tag browser reads private values from the block, and the DICOM JSON data source no longer adds the hex tag aliases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reader The generator writes no private elements. Describe the gggg:creator block of a JSON file from a dcmjs 1.0 naturalized dataset, and say that OHIF reads private tags with getPrivateTagValue, which also reads the hex-key form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
combineFrameInstance spreads the instance and merges the functional groups into Object.create levels. dcmjs 1.0 private tag aliases (70531009) are non-enumerable, so the spread and the merge lost them. Define the aliases on the parent copy, the shared level and each per-frame level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nce check The frame check of _measurementReferencesSOPInstanceUID read the raw value. dcmjs 1.0 gives an empty element as [], so Number([] || 1) was 0 and the measurement did not load. Read the frame with one rule in both places. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ew dcmjs package in a test comment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wayfarer3130
temporarily deployed
to
unrestricted
October 30, 2026 12:53 — with
GitHub Actions
Inactive
beta.9 adds dcmjs.data.frameDataset, gives each Object.create child its own private alias record, and naturalizes a zero-length string element as empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hared functional groups The getters read _parentInstance._shared, which existed only after a first combineFrameInstance call. A write now defines the value on the object that is written to, so a write to a frame view does not change the instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…js frameDataset The NM, RTDOSE and default positions move to a module-level frameAttributes, frameNumber to frameProperties. The local private alias workaround, the _parentInstance copy and the _shared and per-frame cache keys are gone; frameDataset caches the views in _frameDatasets. A per-frame ImagePositionPatient now wins over a root value (PS3.3 C.7.6.16). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aProvider Delete the OHIF copy of combineFrameInstance and its test, and call utilities.combineFrameInstance from @cornerstonejs/metadata. The OHIF copy placed RTDOSE frames wrong when GridFrameOffsetVector holds absolute z values (PS3.3 C.8.8.3.2). MetadataProvider keeps the NumberOfFrames < 2 guard, the parseInt of the frame number, and the split of the root ImageType that isDisplaySetReconstructable reads. Delete dicomSplit.ts, which has no other users; MetadataProvider uses the @cornerstonejs/metadata dicomSplit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
beta.10 removes a debug log of an InlineBinary value (dcmjs #636). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…0, frameNumber] Protects the #5811 behaviour now that MetadataProvider uses the Cornerstone3D combineFrameInstance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
This PR moves OHIF from
dcmjs@0.52.0to the dcmjs 1.0 beta. The npm package of the beta is@dcmjs-org/dcmjs@1.0.0-beta.10. The companion Cornerstone3D PR is cornerstonejs/cornerstone3D#3019.The default export of
@dcmjs-org/dcmjshas the same names as dcmjs 0.52. Thus most source files change only the import line. But dcmjs 1.0 changes the shape of naturalized datasets. The dcmjs migration guide describes the changes indocs/MIGRATION.md, "Naturalization: six changes":[0], alengthof 1, and an iterator.Array.isArray,map,forEach,findandpushdo not work on it. A spread, astructuredCloneor a JSON copy of the item object loses[0].1-nkeeps a list, also for one value. For example,WindowCenteris[40]andSpecificCharacterSetis["ISO_IR 100"].nullwhen it is empty.PatientNameis{ Alphabetic }, andPatientName[0]isundefined.`${gggg}:${creator}`, for example"7053:Philips PET Private Group": { "creator": "Philips PET Private Group", "reservedBlock": 16, "x09": [1.88] }. The hex key (70531009) is only a non-enumerable alias. A spread, a JSON copy or a clone loses the alias, but keeps the block.These shape changes broke some OHIF code. No unit test found the breakages. This PR fixes them.
CS3D_REF: feat/dcmjs-1.0-beta
The
CS3D_REFline above tells CI to build this PR together with the Cornerstone3D branch of cornerstonejs/cornerstone3D#3019. The Playwright workflow and the CircleCI jobs clone that branch, build it, and link it into OHIF. The combined deploy preview is atcs3d-pr-6397--ohif-dev.netlify.app. While the line names a branch, the "CS3D Branch Merge Guard" job shows a failure. This failure is correct, because this PR must not merge before the Cornerstone3D release.Merge order
Do not merge this PR before a Cornerstone3D release that contains cornerstonejs/cornerstone3D#3019. This PR pins
@cornerstonejs/*@5.12.0, and these packages read the data with dcmjs 0.52 code. In@cornerstonejs/adapters@5.12.0,generateToolStategivesframeNumberas[3]and not3for an SR that dcmjs 1.0 reads. The SR hydration of OHIF then uses an array where it expects a number. The Cornerstone3D branch gives3. A local check with the linked Cornerstone3D branch showed this result.CS3D_REFline for the tests and the preview.5.14.0, so the bump from5.12.0also brings the changes of5.13and5.14.node .scripts/cs3d-set-version.mjs <version>, and commit thepackage.jsonchanges and the lockfile.CS3D_REF: feat/dcmjs-1.0-beta now <version>. The merge guard then passes, because the PR depends on a published release.dcmjs: 0.52.0override inpnpm-workspace.yaml. Remove the override if no package needs dcmjs 0.52 after step 3.Do not merge this PR alone. Without the Cornerstone3D release, the viewer has two dcmjs copies, and the old adapters give an array as the SR frame number. Also, this PR imports
asMultiValueArray,asArrayFirstandgetPrivateTagValuefrom@cornerstonejs/utils.@cornerstonejs/utils@5.12.0does not export these functions, so a build without theCS3D_REFline fails until step 3.After step 3, only
dicom-microscopy-viewer@0.48.6needs the override. That package declaresdcmjs ^0.41.0, and OHIF loads its prebuiltdist/dynamic-importbundle, so webpack does not bundle that dcmjs copy. A removal of the override only installs dcmjs 0.41.x for that package.Changes & Results
Dependency
platform/appandplatform/coredepend on@dcmjs-org/dcmjs@1.0.0-beta.10(exact pin) in place ofdcmjs@0.52.0. The extensions that import dcmjs change their peer dependency in the same way.cornerstone-dicom-sruses dcmjs only in a test, so the dependency moves todevDependencies.cornerstone-dicom-rt,cornerstone-dicom-seganddicom-microscopyimport dcmjs but did not declare it. They got dcmjs through hoisting. These extensions now declare@dcmjs-org/dcmjs.cornerstone-dicom-rt,cornerstone-dicom-sr,tmtvanddicom-videonow declare@cornerstonejs/utils, because they importasArray.pnpm-workspace.yamladds@dcmjs-org/*tominimumReleaseAgeExclude. The exact pin of the beta can be newer than the 48-hour cutoff.pnpm-workspace.yamlkeeps thedcmjs: 0.52.0override, so that the copy in@cornerstonejs/adapters,metadata,utilsandai5.12.0 stays pinned. Until step 2 of the merge order, the viewer bundle has two dcmjs copies./\b(dcmjs)/already matches@dcmjs-org/dcmjs, so webpack needs no change.moduleNameMapper. OHIF imports only the default entry, and beta.10 has arequireentry.Fixes for the 1.0 shapes
loadRTStruct.js:forEachonROIContourSequenceandContourSequence, andfindonStructureSetROISequenceandRTROIObservationsSequence, threwTypeErrorfor an RTSTRUCT with one ROI or one contour. The local_toArraycopy is removed.getSopClassHandlerModule.ts:forEachon the four reference sequences. A missing sequence is now skipped, and the code does not throw. The referenced images now come from every item ofReferencedSeriesSequence. Before, the code readReferencedSeriesSequence.ReferencedInstanceSequence, which isundefinedfor a sequence with many items. That bug existed with 0.52.formatContentItem.ts: a PNAME item showed no name.OHIFCornerstoneSRContainer.tsx:contentSequence.mapthrew for a container with one child.getSopClassHandlerModule.ts:findon the rootContentSequence.ReferencedFrameNumberbecame[n], soannotation.data.frameNumberbecame an array. The value is now a number, and the default is 1. The reference check_measurementReferencesSOPInstanceUIDread the raw value. An emptyReferencedFrameNumberis not valid DICOM, but some writers make it. dcmjs 1.0 gives it as[], soNumber([] || 1)was0, and the measurement did not load. Both places now use one function,_getReferencedFrameNumber, and an empty value gives frame 1.loadSR.tsandDicomMicroscopySRSopClassHandler.js:findon the rootContentSequence.MetadataProvider.ts:SequenceOfUltrasoundRegions?.mapthrew for an ultrasound image with one region. One region is the usual case.commandsModule.ts: after an edit in the SUV panel,RadiopharmaceuticalInformationSequence[0]wasundefined, because the object spread of the panel removes the hidden[0].getSopClassHandlerModule.js: anAvailableTransferSyntaxUIDlist with one value did not match the list of video transfer syntaxes.addRetrieveBulkDataNaturalizedwent into a value only whenArray.isArray(value)was true. Thus bulk data in a sequence with one item got noretrieveBulkDataand no absolute URI. Examples are the ECGWaveformSequence, RTSTRUCT contour data with one ROI, and SRGraphicData. Private bulk data in a reserved block, for example the Philips SUV factor70531000, had the same problem. The walk is now inDicomWebDataSource/utils/forEachBulkDataValue.ts. The walk goes into arrays, one-item sequences and private blocks. The walk does not go into PN objects,_vrMap, typed arrays or bulk data descriptors.CorrectedImage: some DICOMweb servers store a multi-valued CS as one backslash-joined string, for exampleCorrectedImage: ["DECY\\ATTN\\SCAT"]. dcmjs 0.52 gave the string, and theATTNcheck found a substring. dcmjs 1.0 gives a list with one item, so the check failed, calculate-suv threw, and the PT had no SUV scaling. The code now splits the values withasMultiValueArrayfrom@cornerstonejs/utils.fixMultiValueKeys: the same storage form ofImageType(["ORIGINAL\\PRIMARY\\VOLUME\\NONE"]) was not split. dcmjs 1.0denaturalizeDatasetthen cut the value to 16 characters, and the microscopy viewer found no VOLUME image.fixMultiValueKeysnow splitsImageTypeandFrameTypewithasMultiValueArray, andDicomMicroscopyViewport.tsxusesfixMultiValueKeys.The other fixes use
asArrayandasArrayFirstfrom@cornerstonejs/utils. The SR, microscopy and tag browser code keep their localtoArrayhelpers. These helpers useArray.isArray, so they are already correct for 1.0.Private tags
OHIF reads each private tag with
getPrivateTagValue(dataset, group, creator, element)from@cornerstonejs/utils(cornerstonejs/cornerstone3D#3019). The function reads the dcmjs 1.0 block formgggg:creatorfirst. It also reads the hex-key form, because some Cornerstone3D paths still make that form. For the hex form, the function finds the creator elementgggg00xxand then readsggggxxEE. The function does not assume block0x10.70531000(SUV scale factor) and70531009(activity concentration scale factor) were[0.00038].coerceNumberrejected the list, and the Philips scale factors were lost for a series withUnits = CNTS.getPTImageIdInstanceMetadata.tsnow reads block7053:Philips PET Private Group, elementsx00andx09, withgetPrivateTagValue, and uses the first value.getPTImageIdInstanceMetadata.tsreads block0009:GEMS_PETD_01, elementx0D, withgetPrivateTagValue.combineFrameInstancecopied the instance with a spread. The spread lost the hex aliases, so a multi-frame PT had no Philips or GE values.combineFrameInstancenow uses the dcmjsframeDataset(see "Multi-frame instances: dcmjsframeDataset"). The frame views keep the private blocks and the hex aliases on each level. The optionprivateMacros: truealso merges a private sequence in a functional group item, as the old code did.resolveBulkDataTags.ts: the registry of private bulk data tags now keeps a creator for each tag. The two default tags (70531000,70531009) have the creatorPhilips PET Private Group. With a creator, the function finds the value withfindPrivateTag, and writes the number back into the block, so the number survives a later copy.registerResolvedBulkDataTagsalso accepts{ tag, creator }, and a hex string still works.definePrivateAliasescall. The readers now read the block, so this PR removes that call again.gggg:creatorkey, so the browser showed no private tags. The newgetDatasetEntries.tsgives each block element as a hex tag, with the value from the block.Multi-frame instances: dcmjs
frameDatasetdcmjs 1.0.0-beta.9 added
dcmjs.data.frameDataset(dataset, frameNumber, options)(dcmjs-org/dcmjs#631). The function gives the view of one frame. The view reads the per-frame functional groups, then the shared functional groups, then the root. dcmjs-org/dcmjs#628 is also in beta.9, so the views keep the private hex aliases with no workaround.combineFrameInstance.platform/core/src/classes/MetadataProvider.tscallsutilities.combineFrameInstancefrom@cornerstonejs/metadata(feat(deps): move from dcmjs 0.52 to the dcmjs 1.0 beta (@dcmjs-org/dcmjs) cornerstonejs/cornerstone3D#3019). That function callsframeDataset, and gives the NM and RTDOSE position fallbacks, theImageTypesplit andframeNumber.platform/core/src/utils/combineFrameInstance.tsandplatform/core/src/utils/dicomSplit.tsare deleted.@ohif/coredid not export them.MetadataProvider.tskeeps the guardNumberOfFrames >= 2, so a single-frame instance does not change. It also keeps theImageTypesplit on the root instance, becauseisNMReconstructablereadsImageType[2]on the root instance.definePrivateAliasesworkaround is removed._frameDatasetskey. The keys_parentInstanceand_shared, and the numeric frame keys, are removed.platform/core/src/utils/addProxyFields.jsread_parentInstance._shared. The function now readsSharedFunctionalGroupsSequence[0]directly. Its setter now writes to the frame view, and not to the root instance, because each view inherits from the root instance.extensions/default/src/utils/dicomWriter.tsno longer lists_parentInstanceas a runtime key.Changes that users and integrators see
Multi-frame position order. A per-frame
ImagePositionPatientnow has priority over a rootImagePositionPatient. The root value is a fallback only. PS3.3 C.7.6.16 gives this order. Before, the root value had priority.RTDOSE gets a position for each frame. Since fix(RTStruct): RTSTRUCT contours rendered on first slice for multi-frame images without IPP #5811, the old RTDOSE branch with
GridFrameOffsetVectordid not run, so each RTDOSE frame got the root position. Now each frame gets its own position. The code reads both encodings of PS3.3 C.8.8.3.2: relative offsets (the first value is 0), and absolute z positions.NM does not change the root instance. The old code wrote
ImageOrientationPatientfromDetectorInformationSequenceon the root instance. Now only the frame views get this value. When the root instance has no position, the position from the detector replaces a per-frame position.ImageTypesplit. TheImageTypesplit now also splits the dcmjs 1.0 form with one joined value, for example['ORIGINAL\\PRIMARY\\RECON TOMO']. Thus such an NM series can now count as reconstructable.A frame number above the last frame throws. A frame number above
NumberOfFramesnow throws aRangeErrorfromframeDataset. Before, the code gave the shared values and the root values. A frame number0is frame 1.A multi-frame image with no position keeps a position for each frame. A US cine, an XA run or a multi-frame secondary capture with no
ImagePositionPatientgets[0, 0, frameNumber]on each frame, as with fix(RTStruct): RTSTRUCT contours rendered on first slice for multi-frame images without IPP #5811. A test inMetadataProvider.test.tscovers this behaviour.An SR with a labelled measurement saves. The dcmjs 1.0 SR writer gives a one-item
ConceptCodeSequenceas the item object. The Cornerstone3DLabelDatanow reads the sequence as an array or as one item. Before this fix, "Save SR" and "Download SR" failed for a measurement with a label.Shared values on the root instance.
addProxyFieldsnow gives the sharedImageOrientationPatientandPixelSpacingon a multi-frame root instance from the start. Before, these values came only after the first frame lookup.Removed instance keys. An extension that read
_parentInstanceor_sharedfrom an instance must useframeDatasetor the instance itself.Philips PET in the hex-key form needs the creator element.
getPrivateTagValuereads70531000and70531009only when the creator element7053,00xxhas the valuePhilips PET Private Group. Before, the code read70531000and70531009also without a creator element. PS3.5 7.8.1 requires the creator element, and data from dcmjs and from a DICOMweb server has the creator. A dataset without the creator element now has no Philips scale factors. With this rule, the code does not read a wrong value when a different vendor uses group7053.GE PET SUV values can change.
getPTImageIdInstanceMetadata.tsread the GE private key'0009100d'in lower case. dcmjs and DICOM JSON use upper-case keys, so the key never matched, also with dcmjs 0.52. The code now reads the GE post-injection date and time (blockGEMS_PETD_01, elementx0D) withgetPrivateTagValue. calculate-suv uses this value only when SeriesDate/SeriesTime is later than the earliest AcquisitionDate/AcquisitionTime. In that case, the scan time for the decay correction is now the GE value, and not the earliest acquisition time. The new SUV is the old SUV × 2^((t_GE − t_acq) / T½). For example, a GE time 1800 s before the acquisition time with F-18 (T½ = 6586.2 s) gives a factor of about 0.827. If the GE time is before the injection start, calculate-suv throws "Decay time cannot be less than zero", and the series has no SUV scaling..scripts/dicom-json-generator.jsnow writes the 1.0 shapes. A sequence with one item is an object, and a VM1-nvalue with one value is a list. OHIF reads both shapes, becausewrapSequencesin the DICOM JSON data source wraps the sequences again. A third-party reader of the generated JSON can see a different format. The generator writes no private elements. This PR updatesplatform/docs/docs/configuration/dataSources/dicom-json.mdandintroduction.mdwith the 1.0 shapes. The docs also describe the private block form of a JSON file thatJSON.stringifymakes from a dcmjs 1.0 dataset, andgetPrivateTagValue.registerResolvedBulkDataTagsaccepts{ tag, creator }. A private bulk data tag that an extension registers without a creator still works with the hex-key form.Instance metadata in
DicomMetadataStorehas the 1.0 shapes. An extension that reads instance metadata and uses array methods on a sequence must useasArray, or a similar helper.Decisions: the compatible default export or the new API. Each use keeps the compatible default export. A new-API entry point was not both small and clearly better for any use.
DicomMetadataStore.ts:readFile,naturalizeDataset/eventStreamgives no gain here.dicomFileLoader.js:readFile,naturalizeDataset,namifyDataset/eventStreamfor large local files.retrieveMetadataLoaderAsync.js:naturalizeDatasetDicomWebDataSource:DicomDict,denaturalizeDatasetdicomWriter.ts:datasetToDictfromNaturalizedtoPart10Writer.DicomTagBrowser:DicomMetaDictionary,Tag.fromPString/dictionarydoes not make the bundle smaller.getSopClassHandlerModule.js:denaturalizeDatasetloadRTStruct.js:readFile,naturalizeDataset,ColorsColors.rgb2DICOMLAB,dicomlab2RGBdatasetToBlobon the RTSS from the adapterssr.*,adapters.DICOMMicroscopyViewer,datasetToBuffer,denaturalizeDataset,uid()srandadapters./encapsulatedand/mediahave nothing to replace.Follow-ups
MetadataProvider.tskeeps the guardNumberOfFrames >= 2, so an enhanced image with one frame gets no frame view.frameDatasetcan make a view for that image. A removal of the guard changes the values that the viewer shows, so this PR does not make that change.cornerstone-dicom-seg/src/getSopClassHandlerModule.ts:308andcornerstone-dicom-pmap/src/getSopClassHandlerModule.ts:76readReferencedInstanceSequencefrom the first referenced series only. The RT fix of this PR can apply to them too.Testing
All commands ran from the repository root.
npx jest(with the Cornerstone3D branch linked, dcmjs beta.10): 128 suites, 1529 tests pass.pnpm run compiler:coverage:ci: pass.pnpm run lint:compiler:ci: pass.pnpm build: pass.cornerstone-dicom-rt/src/loadRTStruct.test.js: an RTSTRUCT with one ROI and one contour loads. The RT extension had no jest setup, so this PR addsjest.config.jsand thetest:unitscript, as in the SEG extension.cornerstone-dicom-sr/src/utils/formatContentItem.test.ts: a PNAME item shows "Doe, John".DicomWebDataSource/utils/forEachBulkDataValue.test.ts: the walk finds bulk data in a one-itemWaveformSequence, in nested ROI contours, inPixelDataand in a private block.DicomTagBrowser/getDatasetEntries.test.ts: a private block gives its hex tags, with the values of a JSON copy.platform/core/src/utils/resolveBulkDataTags.test.ts: the number goes into the block (reserved block0x11), and a JSON copy keeps the number.getPTImageIdInstanceMetadata.test.ts: the Philips scale factors of a multi-frame PT throughcombineFrameInstance.cornerstone-dicom-sr/src/getSopClassHandlerModule.test.ts: a measurement with an emptyReferencedFrameNumberloads on frame 1.pnpm run cs3d:build, thenpnpm run cs3d:link), the same jest, compiler and build commands pass.CorrectedImage, andfixMultiValueKeys.test.ts. Each test failed before its fix.40897593586) had 11 failures: ContourCombineOperations (3), ContourSmoothOperation (2), RTDataOverlayForUnreferencedDisplaySetNoHydration, RTHydration, RTHydrationDisableConfirmation, TMTVCSVReport, MicroscopyPanel rename and WSI render. Three runs of other PRs with aCS3D_REFbranch on the same Cornerstone3D base had no failures. Thus this PR caused all 11 failures. TheCorrectedImagefix corrects the PET, RT, contour and TMTV failures. TheImageTypefix corrects the microscopy and WSI failures. Locally, with the Cornerstone3D branch linked, the 7 spec files of the failures pass (12 tests). The combined run on commit83f15c50d5passed with no failures. The private tag commits and the beta.9 commits after83f15c50d5get a new combined run. Playwright did not run locally for the beta.9 commits.To test by hand:
Checklist
PR
semantic-release format and guidelines.
Code
etc.)
Public Documentation Updates
additions or removals.
Tested Environment
🤖 Generated with Claude Code