Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/parse raw dicom #194

Merged
merged 7 commits into from
Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: 2
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:latest
- image: circleci/node:16.13
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved

jobs:
test:
Expand Down
2,420 changes: 1,272 additions & 1,148 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dicom-parser",
"version": "1.8.5",
"version": "1.8.6",
"description": "Javascript parser for DICOM Part 10 data",
"main": "dist/dicomParser.min.js",
"module": "dist/dicomParser.min.js",
Expand Down Expand Up @@ -50,9 +50,9 @@
"webpack:watch": "webpack --progress --watch --config ./config/webpack"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/eslint-parser": "^7.15.7",
"@babel/preset-env": "^7.15.6",
"@babel/core": "^7.17.0",
"@babel/eslint-parser": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"chai": "^4.3.4",
Expand Down
2 changes: 1 addition & 1 deletion src/byteArrayParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Reads a string of 8-bit characters from an array of bytes and advances
* the position by length bytes. A null terminator will end the string
* but will not effect advancement of the position. Trailing and leading
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
* but will not affect advancement of the position. Trailing and leading
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
* spaces are preserved (not trimmed)
* @param byteArray the byteArray to read from
* @param position the position in the byte array to read from
Expand Down
4 changes: 4 additions & 0 deletions src/byteStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export default class ByteStream {
return new ByteStream(this.byteArrayParser, byteArrayView);
}

getSize() {
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
return this.byteArray.length;
}

/**
*
* Parses an unsigned int 16 from a byte array and advances
Expand Down
2 changes: 2 additions & 0 deletions src/findEndOfEncapsulatedPixelData.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export default function findEndOfEncapsulatedElement (byteStream, element, warni
const basicOffsetTableItemlength = byteStream.readUint32();
const numFragments = basicOffsetTableItemlength / 4;

// Bad idea to not include the basic offset table, as it means writing the data out is inconsistent with reading it
// but leave this for now. To fix later.
for (let i = 0; i < numFragments; i++) {
const offset = byteStream.readUint32();

Expand Down
4 changes: 2 additions & 2 deletions src/parseDicom.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default function parseDicom (byteArray, options) {

const transferSyntaxElement = metaHeaderDataSet.elements.x00020010;

return byteArrayParser.readFixedString(byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length);
return transferSyntaxElement.Value || byteArrayParser.readFixedString(byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length);
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
}

function isExplicit (transferSyntax) {
Expand Down Expand Up @@ -148,7 +148,7 @@ export default function parseDicom (byteArray, options) {
function parseTheByteStream () {
const metaHeaderDataSet = readPart10Header(byteArray, options);
const dataSet = readDataSet(metaHeaderDataSet);

return mergeDataSets(metaHeaderDataSet, dataSet);
}

Expand Down
30 changes: 25 additions & 5 deletions src/readPart10Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,51 @@ import readDicomElementExplicit from './readDicomElementExplicit.js';
* elements successfully parsed before the error.
*/

export default function readPart10Header (byteArray, options) {
export default function readPart10Header(byteArray, options) {
if (byteArray === undefined) {
throw 'dicomParser.readPart10Header: missing required parameter \'byteArray\'';
}

const littleEndianByteStream = new ByteStream(littleEndianByteArrayParser, byteArray);

function readPrefix () {
function readPrefix() {
const { TransferSyntaxUID } = options || {};
wayfarer3130 marked this conversation as resolved.
Show resolved Hide resolved
if( littleEndianByteStream.getSize() <=132 && TransferSyntaxUID ) {
return false;
}
littleEndianByteStream.seek(128);
const prefix = littleEndianByteStream.readFixedString(4);

if (prefix !== 'DICM') {
throw 'dicomParser.readPart10Header: DICM prefix not found at location 132 - this is not a valid DICOM P10 file.';
const { TransferSyntaxUID } = options || {};
if (!TransferSyntaxUID) {
throw 'dicomParser.readPart10Header: DICM prefix not found at location 132 - this is not a valid DICOM P10 file.';
}
littleEndianByteStream.seek(0);
return false;
}
return true;
}

// main function here
function readTheHeader () {
function readTheHeader() {
// Per the DICOM standard, the header is always encoded in Explicit VR Little Endian (see PS3.10, section 7.1)
// so use littleEndianByteStream throughout this method regardless of the transfer syntax
readPrefix();
const isPart10 = readPrefix();

const warnings = [];
const elements = {};

if (!isPart10) {
littleEndianByteStream.position = 0;
const metaHeaderDataSet = {
elements: {x00020010: { tag: 'x00020010', vr: 'UI', Value: options.TransferSyntaxUID }},
warnings,
};
// console.log('Returning metaHeaderDataSet', metaHeaderDataSet);
return metaHeaderDataSet;
}

while (littleEndianByteStream.position < littleEndianByteStream.byteArray.length) {
const position = littleEndianByteStream.position;
const element = readDicomElementExplicit(littleEndianByteStream, warnings);
Expand Down
2 changes: 1 addition & 1 deletion src/version.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default '1.8.5';
export default '1.8.6';
36 changes: 34 additions & 2 deletions test/parseDicom_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,26 @@ describe('parseDicom', () => {

return convertToByteArray(rawData);
}


/**
*
* @returns a DICOM file consisting of just the raw data
*/
function makeRawData() {
const rawData = [
// Transfer Syntax UID
// x00020010 UI 20 1.2.840.10008.1.2.1 (Explicit VR Little Endian)
// Slice Location
// x00201041 DS 4 '-43'
0x20,0x00,0x41,0x10, 0x44,0x53, 0x04,0x00, 0x2D,0x34,0x33,0x00,
// Rows
// x00280010 US 2 512
0x28,0x00,0x10,0x00, 0x55,0x53, 0x02,0x00, 0x00,0x02
];

return convertToByteArray(rawData);
}

function assertMetaHeaderElements(dataSet, transferSyntax, groupLength) {
expect(dataSet.uint32('x00020000')).to.equal(groupLength); // '(0002,0000) was not read correctly');
expect(dataSet.uint16('x00020001')).to.equal(256); //'(0002,0001) was not read correctly');
Expand Down Expand Up @@ -174,7 +193,20 @@ describe('parseDicom', () => {
expect(dataSet.uint16('x00280010')).to.equal(512);
expect(dataSet.string('x00201041')).to.equal('-43');
});


it('should parse the dataset correctly (raw LEE)', () => {
// Arrange
const byteArray = makeRawData();

// Act
const dataSet = parseDicom(byteArray, {TransferSyntaxUID: '1.2.840.10008.1.2.1'});

// Assert
// assertMetaHeaderElements(dataSet, '1.2.840.10008.1.2.1', 159);
expect(dataSet.uint16('x00280010')).to.equal(512);
expect(dataSet.string('x00201041')).to.equal('-43');
});

it('should parse the dataset correctly (implicitLittleEndian)', () => {
// Arrange
const byteArray = makeImplicitLittleEndianTestData();
Expand Down