dicom-synth 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/describe/describe.js +77 -6
- package/dist/esm/index.js +77 -6
- package/package.json +1 -1
|
@@ -607,9 +607,13 @@ var UID_SWEEP_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
607
607
|
"SeriesInstanceUID",
|
|
608
608
|
"SOPClassUID"
|
|
609
609
|
]);
|
|
610
|
+
var vrCache = /* @__PURE__ */ new Map();
|
|
610
611
|
function vrOf(keyword) {
|
|
612
|
+
if (vrCache.has(keyword)) return vrCache.get(keyword);
|
|
611
613
|
const nm = dcmjsAny.data.DicomMetaDictionary.nameMap;
|
|
612
|
-
|
|
614
|
+
const vr = (nm?.[keyword] ?? nm?.[`RETIRED_${keyword}`])?.vr;
|
|
615
|
+
vrCache.set(keyword, vr);
|
|
616
|
+
return vr;
|
|
613
617
|
}
|
|
614
618
|
function hashUidVia(map, salt, original) {
|
|
615
619
|
let synthetic = map.get(original);
|
|
@@ -619,22 +623,78 @@ function hashUidVia(map, salt, original) {
|
|
|
619
623
|
}
|
|
620
624
|
return synthetic;
|
|
621
625
|
}
|
|
626
|
+
function isUidLeaf(value) {
|
|
627
|
+
return typeof value === "string" || Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
628
|
+
}
|
|
629
|
+
function hasHashableUid(value) {
|
|
630
|
+
const uids = typeof value === "string" ? [value] : value;
|
|
631
|
+
return uids.some((v) => !v.startsWith(DICOM_ORG_ROOT));
|
|
632
|
+
}
|
|
633
|
+
function pruneUidSequence(value) {
|
|
634
|
+
const items = Array.isArray(value) ? value : [value];
|
|
635
|
+
const pruned = [];
|
|
636
|
+
for (const item of items) {
|
|
637
|
+
if (typeof item !== "object" || item === null || Array.isArray(item))
|
|
638
|
+
continue;
|
|
639
|
+
const skeleton = {};
|
|
640
|
+
let hashable = false;
|
|
641
|
+
for (const [keyword, v] of Object.entries(item)) {
|
|
642
|
+
if (keyword === "_vrMap") continue;
|
|
643
|
+
const vr = vrOf(keyword);
|
|
644
|
+
if (vr === "UI") {
|
|
645
|
+
if (isUidLeaf(v)) {
|
|
646
|
+
skeleton[keyword] = v;
|
|
647
|
+
if (hasHashableUid(v)) hashable = true;
|
|
648
|
+
}
|
|
649
|
+
} else if (vr === "SQ") {
|
|
650
|
+
const nested = pruneUidSequence(v);
|
|
651
|
+
if (nested) {
|
|
652
|
+
skeleton[keyword] = nested;
|
|
653
|
+
hashable = true;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (hashable) pruned.push(skeleton);
|
|
658
|
+
}
|
|
659
|
+
return pruned.length > 0 ? pruned : void 0;
|
|
660
|
+
}
|
|
622
661
|
function collectUidOriginals(record) {
|
|
623
662
|
const uids = {};
|
|
624
663
|
let found = false;
|
|
625
664
|
for (const [keyword, value] of Object.entries(record)) {
|
|
626
665
|
if (UID_SWEEP_EXCLUDE.has(keyword)) continue;
|
|
627
|
-
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
|
|
666
|
+
const vr = vrOf(keyword);
|
|
667
|
+
if (vr === "UI") {
|
|
668
|
+
if (isUidLeaf(value) && hasHashableUid(value)) {
|
|
669
|
+
uids[keyword] = value;
|
|
670
|
+
found = true;
|
|
671
|
+
}
|
|
672
|
+
} else if (vr === "SQ") {
|
|
673
|
+
const pruned = pruneUidSequence(value);
|
|
674
|
+
if (pruned) {
|
|
675
|
+
uids[keyword] = pruned;
|
|
676
|
+
found = true;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
631
679
|
}
|
|
632
680
|
return found ? uids : void 0;
|
|
633
681
|
}
|
|
682
|
+
function hashUidValue(value, salt, map) {
|
|
683
|
+
if (typeof value === "string") {
|
|
684
|
+
return value.startsWith(DICOM_ORG_ROOT) ? value : hashUidVia(map, salt, value);
|
|
685
|
+
}
|
|
686
|
+
if (Array.isArray(value)) return value.map((v) => hashUidValue(v, salt, map));
|
|
687
|
+
return Object.fromEntries(
|
|
688
|
+
Object.entries(value).map(([k, v]) => [
|
|
689
|
+
k,
|
|
690
|
+
hashUidValue(v, salt, map)
|
|
691
|
+
])
|
|
692
|
+
);
|
|
693
|
+
}
|
|
634
694
|
function hashUidTags(uids, salt, map) {
|
|
635
695
|
const tags = {};
|
|
636
696
|
for (const [keyword, original] of Object.entries(uids)) {
|
|
637
|
-
tags[keyword] =
|
|
697
|
+
tags[keyword] = hashUidValue(original, salt, map);
|
|
638
698
|
}
|
|
639
699
|
return tags;
|
|
640
700
|
}
|
|
@@ -820,11 +880,17 @@ function describeDirectory(dir, options = {}) {
|
|
|
820
880
|
}
|
|
821
881
|
const salt = preserveUids ? options.uidSalt ?? deriveSalt([...studies.keys()]) : void 0;
|
|
822
882
|
if (salt !== void 0) {
|
|
883
|
+
const sweptKeepTags = /* @__PURE__ */ new Set();
|
|
823
884
|
for (const study of studies.values()) {
|
|
824
885
|
for (const series of study.values()) {
|
|
825
886
|
if (!Array.isArray(series)) continue;
|
|
826
887
|
for (const rec of series) {
|
|
827
888
|
if (rec.uidOriginals) {
|
|
889
|
+
if (rec.tags) {
|
|
890
|
+
for (const keyword of Object.keys(rec.uidOriginals)) {
|
|
891
|
+
if (keyword in rec.tags) sweptKeepTags.add(keyword);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
828
894
|
rec.tags = mergeTags(
|
|
829
895
|
rec.tags,
|
|
830
896
|
hashUidTags(rec.uidOriginals, salt, uidMap)
|
|
@@ -833,6 +899,11 @@ function describeDirectory(dir, options = {}) {
|
|
|
833
899
|
}
|
|
834
900
|
}
|
|
835
901
|
}
|
|
902
|
+
for (const keyword of sweptKeepTags) {
|
|
903
|
+
console.warn(
|
|
904
|
+
`describe: keep-tag "${keyword}" contains UID references \u2014 kept as its hashed UID skeleton, not verbatim (disable with preserveUids: false)`
|
|
905
|
+
);
|
|
906
|
+
}
|
|
836
907
|
}
|
|
837
908
|
const withGroupUid = (spec2, seriesUid) => salt === void 0 ? spec2 : {
|
|
838
909
|
...spec2,
|
package/dist/esm/index.js
CHANGED
|
@@ -1562,9 +1562,13 @@ var UID_SWEEP_EXCLUDE = /* @__PURE__ */ new Set([
|
|
|
1562
1562
|
"SeriesInstanceUID",
|
|
1563
1563
|
"SOPClassUID"
|
|
1564
1564
|
]);
|
|
1565
|
+
var vrCache = /* @__PURE__ */ new Map();
|
|
1565
1566
|
function vrOf(keyword) {
|
|
1567
|
+
if (vrCache.has(keyword)) return vrCache.get(keyword);
|
|
1566
1568
|
const nm = dcmjsAny2.data.DicomMetaDictionary.nameMap;
|
|
1567
|
-
|
|
1569
|
+
const vr = (nm?.[keyword] ?? nm?.[`RETIRED_${keyword}`])?.vr;
|
|
1570
|
+
vrCache.set(keyword, vr);
|
|
1571
|
+
return vr;
|
|
1568
1572
|
}
|
|
1569
1573
|
function hashUidVia(map, salt, original) {
|
|
1570
1574
|
let synthetic = map.get(original);
|
|
@@ -1574,22 +1578,78 @@ function hashUidVia(map, salt, original) {
|
|
|
1574
1578
|
}
|
|
1575
1579
|
return synthetic;
|
|
1576
1580
|
}
|
|
1581
|
+
function isUidLeaf(value) {
|
|
1582
|
+
return typeof value === "string" || Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
1583
|
+
}
|
|
1584
|
+
function hasHashableUid(value) {
|
|
1585
|
+
const uids = typeof value === "string" ? [value] : value;
|
|
1586
|
+
return uids.some((v) => !v.startsWith(DICOM_ORG_ROOT));
|
|
1587
|
+
}
|
|
1588
|
+
function pruneUidSequence(value) {
|
|
1589
|
+
const items = Array.isArray(value) ? value : [value];
|
|
1590
|
+
const pruned = [];
|
|
1591
|
+
for (const item of items) {
|
|
1592
|
+
if (typeof item !== "object" || item === null || Array.isArray(item))
|
|
1593
|
+
continue;
|
|
1594
|
+
const skeleton = {};
|
|
1595
|
+
let hashable = false;
|
|
1596
|
+
for (const [keyword, v] of Object.entries(item)) {
|
|
1597
|
+
if (keyword === "_vrMap") continue;
|
|
1598
|
+
const vr = vrOf(keyword);
|
|
1599
|
+
if (vr === "UI") {
|
|
1600
|
+
if (isUidLeaf(v)) {
|
|
1601
|
+
skeleton[keyword] = v;
|
|
1602
|
+
if (hasHashableUid(v)) hashable = true;
|
|
1603
|
+
}
|
|
1604
|
+
} else if (vr === "SQ") {
|
|
1605
|
+
const nested = pruneUidSequence(v);
|
|
1606
|
+
if (nested) {
|
|
1607
|
+
skeleton[keyword] = nested;
|
|
1608
|
+
hashable = true;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
if (hashable) pruned.push(skeleton);
|
|
1613
|
+
}
|
|
1614
|
+
return pruned.length > 0 ? pruned : void 0;
|
|
1615
|
+
}
|
|
1577
1616
|
function collectUidOriginals(record) {
|
|
1578
1617
|
const uids = {};
|
|
1579
1618
|
let found = false;
|
|
1580
1619
|
for (const [keyword, value] of Object.entries(record)) {
|
|
1581
1620
|
if (UID_SWEEP_EXCLUDE.has(keyword)) continue;
|
|
1582
|
-
|
|
1583
|
-
if (
|
|
1584
|
-
|
|
1585
|
-
|
|
1621
|
+
const vr = vrOf(keyword);
|
|
1622
|
+
if (vr === "UI") {
|
|
1623
|
+
if (isUidLeaf(value) && hasHashableUid(value)) {
|
|
1624
|
+
uids[keyword] = value;
|
|
1625
|
+
found = true;
|
|
1626
|
+
}
|
|
1627
|
+
} else if (vr === "SQ") {
|
|
1628
|
+
const pruned = pruneUidSequence(value);
|
|
1629
|
+
if (pruned) {
|
|
1630
|
+
uids[keyword] = pruned;
|
|
1631
|
+
found = true;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1586
1634
|
}
|
|
1587
1635
|
return found ? uids : void 0;
|
|
1588
1636
|
}
|
|
1637
|
+
function hashUidValue(value, salt, map) {
|
|
1638
|
+
if (typeof value === "string") {
|
|
1639
|
+
return value.startsWith(DICOM_ORG_ROOT) ? value : hashUidVia(map, salt, value);
|
|
1640
|
+
}
|
|
1641
|
+
if (Array.isArray(value)) return value.map((v) => hashUidValue(v, salt, map));
|
|
1642
|
+
return Object.fromEntries(
|
|
1643
|
+
Object.entries(value).map(([k, v]) => [
|
|
1644
|
+
k,
|
|
1645
|
+
hashUidValue(v, salt, map)
|
|
1646
|
+
])
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1589
1649
|
function hashUidTags(uids, salt, map) {
|
|
1590
1650
|
const tags = {};
|
|
1591
1651
|
for (const [keyword, original] of Object.entries(uids)) {
|
|
1592
|
-
tags[keyword] =
|
|
1652
|
+
tags[keyword] = hashUidValue(original, salt, map);
|
|
1593
1653
|
}
|
|
1594
1654
|
return tags;
|
|
1595
1655
|
}
|
|
@@ -1775,11 +1835,17 @@ function describeDirectory(dir, options = {}) {
|
|
|
1775
1835
|
}
|
|
1776
1836
|
const salt = preserveUids ? options.uidSalt ?? deriveSalt([...studies.keys()]) : void 0;
|
|
1777
1837
|
if (salt !== void 0) {
|
|
1838
|
+
const sweptKeepTags = /* @__PURE__ */ new Set();
|
|
1778
1839
|
for (const study of studies.values()) {
|
|
1779
1840
|
for (const series of study.values()) {
|
|
1780
1841
|
if (!Array.isArray(series)) continue;
|
|
1781
1842
|
for (const rec of series) {
|
|
1782
1843
|
if (rec.uidOriginals) {
|
|
1844
|
+
if (rec.tags) {
|
|
1845
|
+
for (const keyword of Object.keys(rec.uidOriginals)) {
|
|
1846
|
+
if (keyword in rec.tags) sweptKeepTags.add(keyword);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1783
1849
|
rec.tags = mergeTags(
|
|
1784
1850
|
rec.tags,
|
|
1785
1851
|
hashUidTags(rec.uidOriginals, salt, uidMap)
|
|
@@ -1788,6 +1854,11 @@ function describeDirectory(dir, options = {}) {
|
|
|
1788
1854
|
}
|
|
1789
1855
|
}
|
|
1790
1856
|
}
|
|
1857
|
+
for (const keyword of sweptKeepTags) {
|
|
1858
|
+
console.warn(
|
|
1859
|
+
`describe: keep-tag "${keyword}" contains UID references \u2014 kept as its hashed UID skeleton, not verbatim (disable with preserveUids: false)`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1791
1862
|
}
|
|
1792
1863
|
const withGroupUid = (spec2, seriesUid) => salt === void 0 ? spec2 : {
|
|
1793
1864
|
...spec2,
|