Skip to content

Commit faa0f86

Browse files
craigberry1Craig Berry
andauthored
feat(lossless-round-trip): Implement lossless read writes (#400)
* Extract formatting logic from initial value read from data view * Leave empty string in base numeric string read, update tests with new function name. * Save original rawValue of data element as private property returned from readTag, manually apply formatting on returned Value property * Refactor to calculate raw and value inside value representation * Implement equality between original and formatted value on write, add deep equals implementation and tests * Add POC lossless-round-trip test with sample file from data repo * Add specific DS tests and first round of general VR tests * Cover all VRs with retain test * Fix exponential notation unit test expect * Update ParsedUnknownValue read logic and add different VR test coverage * Add remaining VRs for UN parsing * Formatting and cleanup * Verify changed value is respected on write * Add flag opt in/out of raw storage for things like pixel data or sequences * Add sequence tests * Fix comments and formatting before review --------- Co-authored-by: Craig Berry <[email protected]>
1 parent 117e3be commit faa0f86

File tree

6 files changed

+1160
-73
lines changed

6 files changed

+1160
-73
lines changed

src/DicomMessage.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { DicomDict } from "./DicomDict.js";
1010
import { DicomMetaDictionary } from "./DicomMetaDictionary.js";
1111
import { Tag } from "./Tag.js";
1212
import { log } from "./log.js";
13+
import { deepEqual } from "./utilities/deepEqual";
1314
import { ValueRepresentation } from "./ValueRepresentation.js";
1415

1516
const singleVRs = ["SQ", "OF", "OW", "OB", "UN", "LT"];
@@ -156,6 +157,7 @@ class DicomMessage {
156157
vr: readInfo.vr.type
157158
});
158159
dict[cleanTagString].Value = readInfo.values;
160+
dict[cleanTagString]._rawValue = readInfo.rawValues;
159161

160162
if (untilTag && untilTag === cleanTagString) {
161163
break;
@@ -193,7 +195,8 @@ class DicomMessage {
193195
ignoreErrors: false,
194196
untilTag: null,
195197
includeUntilTagValue: false,
196-
noCopy: false
198+
noCopy: false,
199+
forceStoreRaw: false
197200
}
198201
) {
199202
var stream = new ReadBufferStream(buffer, null, {
@@ -251,8 +254,9 @@ class DicomMessage {
251254
sortedTags.forEach(function (tagString) {
252255
var tag = Tag.fromString(tagString),
253256
tagObject = jsonObjects[tagString],
254-
vrType = tagObject.vr,
255-
values = tagObject.Value;
257+
vrType = tagObject.vr;
258+
259+
var values = DicomMessage._getTagWriteValues(vrType, tagObject);
256260

257261
written += tag.write(
258262
useStream,
@@ -266,6 +270,23 @@ class DicomMessage {
266270
return written;
267271
}
268272

273+
static _getTagWriteValues(vrType, tagObject) {
274+
if (!tagObject._rawValue) {
275+
return tagObject.Value;
276+
}
277+
278+
// apply VR specific formatting to the original _rawValue and compare to the Value
279+
const vr = ValueRepresentation.createByTypeString(vrType);
280+
const originalValue = tagObject._rawValue.map((val) => vr.applyFormatting(val))
281+
282+
// if Value has not changed, write _rawValue unformatted back into the file
283+
if (deepEqual(tagObject.Value, originalValue)) {
284+
return tagObject._rawValue;
285+
} else {
286+
return tagObject.Value;
287+
}
288+
}
289+
269290
static _readTag(
270291
stream,
271292
syntax,
@@ -340,25 +361,33 @@ class DicomMessage {
340361
}
341362

342363
var values = [];
364+
var rawValues = [];
343365
if (vr.isBinary() && length > vr.maxLength && !vr.noMultiple) {
344366
var times = length / vr.maxLength,
345367
i = 0;
346368
while (i++ < times) {
347-
values.push(vr.read(stream, vr.maxLength, syntax));
369+
const { rawValue, value } = vr.read(stream, vr.maxLength, syntax, options);
370+
rawValues.push(rawValue);
371+
values.push(value);
348372
}
349373
} else {
350-
var val = vr.read(stream, length, syntax);
374+
const { rawValue, value } = vr.read(stream, length, syntax, options);
351375
if (!vr.isBinary() && singleVRs.indexOf(vr.type) == -1) {
352-
values = val;
353-
if (typeof val === "string") {
354-
values = val.split(String.fromCharCode(VM_DELIMITER));
376+
rawValues = rawValue;
377+
values = value
378+
if (typeof value === "string") {
379+
rawValues = rawValue.split(String.fromCharCode(VM_DELIMITER));
380+
values = value.split(String.fromCharCode(VM_DELIMITER));
355381
}
356382
} else if (vr.type == "SQ") {
357-
values = val;
383+
rawValues = rawValue;
384+
values = value;
358385
} else if (vr.type == "OW" || vr.type == "OB") {
359-
values = val;
386+
rawValues = rawValue;
387+
values = value;
360388
} else {
361-
Array.isArray(val) ? (values = val) : values.push(val);
389+
Array.isArray(value) ? (values = value) : values.push(value);
390+
Array.isArray(rawValue) ? (rawValues = rawValue) : rawValues.push(rawValue);
362391
}
363392
}
364393
stream.setEndian(oldEndian);
@@ -368,6 +397,7 @@ class DicomMessage {
368397
vr: vr
369398
});
370399
retObj.values = values;
400+
retObj.rawValues = rawValues;
371401
return retObj;
372402
}
373403

0 commit comments

Comments
 (0)