Skip to content

Commit

Permalink
fix(exports): interpret PN tags as union between object and string (#364
Browse files Browse the repository at this point in the history
)

* chore: format codebase

yarn run format

* fix(exports): interpret PN tags as union between object and string

the JSON standard represents PeopleNames as an object, rather than a delineated string.  this caused SRs derived
from metadata to have [object Object] for the patient name, notable in Google's Healthcare dicom store, which
seems to pick a random patient name when searching for series. much of the change is attempting to communicate
this union somewhat opaquely, and ensuring objects with additional properties aren't destroyed when being
naturalized. tested with OHIF master branch.

* fix(exports): add SR to test PN tags

Since the sample dcm has image data in it, and we don't support serializing
InlineBinary, I've added a simple SR derived from a TCIA frame for testing.

Adds test for equality between the SR dcm and the same SR recreated from the
json equivalent.

* refactor: 💡 move PersonName code into functions

for easier testing and cleanliness

* feat: 🎸 add value multiplicity to PersonName tags

And also adds the concept of tag and value accessors to
ValueRepresentation. Whenever a tag is created, it is assigned to a
Proxy which allows values to be represented differently according to
whether it's being output to JSON or being interacted with. This allows
a user to assign a string to a PN tag, and have that tag be output as a
JSON object conformant to spec.

* test: 💍 separate PersonName multiplicity test

* refactor: 💡 make proxy handler static, update comments
  • Loading branch information
dmlambo committed Oct 24, 2023
1 parent 136936b commit f0dc199
Show file tree
Hide file tree
Showing 32 changed files with 3,600 additions and 587 deletions.
5 changes: 4 additions & 1 deletion src/DicomDict.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WriteBufferStream } from "./BufferStream";
import { DicomMessage } from "./DicomMessage";
import { ValueRepresentation } from "./ValueRepresentation";

const EXPLICIT_LITTLE_ENDIAN = "1.2.840.10008.1.2.1";

Expand All @@ -11,9 +12,11 @@ class DicomDict {

upsertTag(tag, vr, values) {
if (this.dict[tag]) {
// Should already have tag accessors.
this.dict[tag].Value = values;
} else {
this.dict[tag] = { vr: vr, Value: values };
this.dict[tag] = ValueRepresentation.addTagAccessors({ vr: vr });
this.dict[tag].Value = values;
}
}

Expand Down
23 changes: 15 additions & 8 deletions src/DicomMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import {
DEFLATED_EXPLICIT_LITTLE_ENDIAN,
EXPLICIT_BIG_ENDIAN,
EXPLICIT_LITTLE_ENDIAN,
IMPLICIT_LITTLE_ENDIAN
} from "./constants/dicom";
IMPLICIT_LITTLE_ENDIAN,
VM_DELIMITER
} from "./constants/dicom.js";
import { DicomDict } from "./DicomDict.js";
import { DicomMetaDictionary } from "./DicomMetaDictionary.js";
import { Tag } from "./Tag.js";
Expand Down Expand Up @@ -147,10 +148,11 @@ class DicomMessage {
}
readInfo.values = ["ISO_IR 192"]; // change SpecificCharacterSet to UTF-8
}
dict[cleanTagString] = {
vr: readInfo.vr.type,
Value: readInfo.values
};

dict[cleanTagString] = ValueRepresentation.addTagAccessors({
vr: readInfo.vr.type
});
dict[cleanTagString].Value = readInfo.values;

if (untilTag && untilTag === cleanTagString) {
break;
Expand Down Expand Up @@ -334,7 +336,7 @@ class DicomMessage {
if (!vr.isBinary() && singleVRs.indexOf(vr.type) == -1) {
values = val;
if (typeof val === "string") {
values = val.split(String.fromCharCode(0x5c));
values = val.split(String.fromCharCode(VM_DELIMITER));
}
} else if (vr.type == "SQ") {
values = val;
Expand All @@ -346,7 +348,12 @@ class DicomMessage {
}
stream.setEndian(oldEndian);

return { tag: tag, vr: vr, values: values };
const retObj = ValueRepresentation.addTagAccessors({
tag: tag,
vr: vr
});
retObj.values = values;
return retObj;
}

static lookupTag(tag) {
Expand Down
24 changes: 15 additions & 9 deletions src/DicomMetaDictionary.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dictionary from "./dictionary";
import log from "./log.js";
import addAccessors from "./utilities/addAccessors";
import { ValueRepresentation } from "./ValueRepresentation";
import dicomJson from "./utilities/dicomJson";

class DicomMetaDictionary {
// intakes a custom dictionary that will be used to parse/denaturalize the dataset
Expand Down Expand Up @@ -109,9 +110,9 @@ class DicomMetaDictionary {
* - object member names are dictionary, not group/element tag
*/
static naturalizeDataset(dataset) {
const naturalDataset = {
const naturalDataset = ValueRepresentation.addTagAccessors({
_vrMap: {}
};
});

Object.keys(dataset).forEach(tag => {
const data = dataset[tag];
Expand Down Expand Up @@ -176,6 +177,7 @@ class DicomMetaDictionary {
}
}
});

return naturalDataset;
}

Expand All @@ -193,9 +195,11 @@ class DicomMetaDictionary {
);
}
}

value = value.map(entry =>
entry.constructor.name == "Number" ? String(entry) : entry
);

return value;
}

Expand All @@ -214,10 +218,10 @@ class DicomMetaDictionary {
return;
}
// process this one entry
var dataItem = {
vr: entry.vr,
Value: dataset[naturalName]
};
var dataItem = ValueRepresentation.addTagAccessors({
vr: entry.vr
});
dataItem.Value = dataset[naturalName];

if (dataValue !== null) {
if (entry.vr == "ox") {
Expand All @@ -231,6 +235,10 @@ class DicomMetaDictionary {
}
}

let vr = ValueRepresentation.createByTypeString(
dataItem.vr
);

dataItem.Value = DicomMetaDictionary.denaturalizeValue(
dataItem.Value
);
Expand All @@ -252,9 +260,7 @@ class DicomMetaDictionary {
}
dataItem.Value = unnaturalValues;
}
let vr = ValueRepresentation.createByTypeString(
dataItem.vr
);

if (!vr.isBinary() && vr.maxLength) {
dataItem.Value = dataItem.Value.map(value => {
if (value.length > vr.maxLength) {
Expand Down
Loading

0 comments on commit f0dc199

Please sign in to comment.