Skip to content

Commit

Permalink
added support for parsing deflated explicit little endian transfer sy…
Browse files Browse the repository at this point in the history
…ntax (if pako is loaded at the time of parse)
  • Loading branch information
chafey committed Nov 2, 2015
1 parent 2430876 commit 347fe9a
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 97 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ dicomParser
===========

dicomParser is a lightweight library for parsing DICOM P10 byte streams in modern web browsers (IE10+), Node.js
and Meteor. dicomParser is fast, easy to use and has no external dependencies.
and Meteor. dicomParser is fast, easy to use and has no required external dependencies
([pako](https://github.com/nodeca/pako) is required if you support for the Deflated Explicit VR Little Endian
transfer syntax)

Live Examples
---------------

The best way to see the power of this library is to actually see it in use. A number of live examples are
included that are not only useful but also show how to use dicomParser.
[Click here for a list of all live examples](https://rawgithub.com/chafey/dicomParser/master/examples/index.html)
Make sure you try out the [DICOM Dump with Data Dictionary](https://rawgit.com/chafey/dicomParser/master/examples/dumpWithDataDictionary/index.html)
which is a very useful tool and excellent example of most features.

Community
---------
Expand Down Expand Up @@ -37,6 +41,9 @@ Or install via atmosphere for [Meteor](https://www.meteor.com/) applications

> meteor add chafey:dicom-parser
* Note - make sure you install [pako](https://github.com/nodeca/pako) if you need to support
the Deflated Explicit VR Little Endian transfer syntax

Usage
-----

Expand Down Expand Up @@ -83,6 +90,7 @@ Key Features
* Parses DICOM Part 10 byte arrays in all encodings
* Explicit and implicit
* Little endian and big endian
* Deflated Explicit VR Little Endian transfer syntax
* Supports all VR's including sequences
* Supports elements with undefined length
* Supports sequence items with undefined length
Expand Down Expand Up @@ -152,6 +160,7 @@ Future:
* dataSet creation logic could filter out unwanted tags to improve performance of parse
* dataSet creation logic could defer creation of sequence dataSets to improve performance of parse
* Function to parse non P10 byte streams given the byte stream and the transfer syntax
* Support for encrypted dicom

Contributors
============================================
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dicomParser",
"version": "1.1.7",
"version": "1.2.0",
"description": "Javascript parser for DICOM Part 10 data",
"main" : "dist/dicomParser.js",
"ignore": [
Expand Down
136 changes: 91 additions & 45 deletions dist/dicomParser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! dicom-parser - v1.1.7 - 2015-09-18 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */
/*! dicom-parser - v1.2.0 - 2015-11-02 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */
(function (root, factory) {

// node.js
Expand Down Expand Up @@ -46,49 +46,12 @@ var dicomParser = (function(dicomParser) {
throw "dicomParser.parseDicom: missing required parameter 'byteArray'";
}

var littleEndianByteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray);

function readPrefix()
{
littleEndianByteStream.seek(128);
var prefix = littleEndianByteStream.readFixedString(4);
if(prefix !== "DICM")
{
throw "dicomParser.parseDicom: DICM prefix not found at location 132";
}
}

function readPart10Header()
{
// 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();

var warnings = [];
var elements = {};
while(littleEndianByteStream.position < littleEndianByteStream.byteArray.length) {
var position = littleEndianByteStream.position;
var element = dicomParser.readDicomElementExplicit(littleEndianByteStream, warnings);
if(element.tag > 'x0002ffff') {
littleEndianByteStream.position = position;
break;
}
// Cache the littleEndianByteArrayParser for meta header elements, since the rest of the data set may be big endian
// and this parser will be needed later if the meta header values are to be read.
element.parser = dicomParser.littleEndianByteArrayParser;
elements[element.tag] = element;
}
var metaHeaderDataSet = new dicomParser.DataSet(littleEndianByteStream.byteArrayParser, littleEndianByteStream.byteArray, elements);
metaHeaderDataSet.warnings = littleEndianByteStream.warnings;
return metaHeaderDataSet;
}

function readTransferSyntax(metaHeaderDataSet) {
if(metaHeaderDataSet.elements.x00020010 === undefined) {
throw 'dicomParser.parseDicom: missing required meta header attribute 0002,0010';
}
var transferSyntaxElement = metaHeaderDataSet.elements.x00020010;
return dicomParser.readFixedString(littleEndianByteStream.byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length);
return dicomParser.readFixedString(byteArray, transferSyntaxElement.dataOffset, transferSyntaxElement.length);
}

function isExplicit(transferSyntax) {
Expand All @@ -100,16 +63,30 @@ var dicomParser = (function(dicomParser) {
return true;
}

function getDataSetByteStream(transferSyntax) {
function getDataSetByteStream(transferSyntax, position) {
if(transferSyntax === '1.2.840.10008.1.2.1.99')
{
if(typeof(pako) === "undefined") {
throw 'dicomParser.parseDicom: deflated transfer syntax encountered but pako not loaded';
}
var deflated = byteArray.slice(position);
var inflated = pako.inflateRaw(deflated);

// create a single byte array with the full header bytes and the inflated bytes
var fullByteArray = new Uint8Array(inflated.length + position);
fullByteArray.set(byteArray.slice(0, position), 0);
fullByteArray.set(inflated, position);
return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, fullByteArray, 0);
}
if(transferSyntax === '1.2.840.10008.1.2.2') // explicit big endian
{
return new dicomParser.ByteStream(dicomParser.bigEndianByteArrayParser, byteArray, littleEndianByteStream.position);
return new dicomParser.ByteStream(dicomParser.bigEndianByteArrayParser, byteArray, position);
}
else
{
// all other transfer syntaxes are little endian; only the pixel encoding differs
// make a new stream so the metaheader warnings don't come along for the ride
return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray, littleEndianByteStream.position);
return new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray, position);
}
}

Expand All @@ -132,7 +109,7 @@ var dicomParser = (function(dicomParser) {
{
var transferSyntax = readTransferSyntax(metaHeaderDataSet);
var explicit = isExplicit(transferSyntax);
var dataSetByteStream = getDataSetByteStream(transferSyntax);
var dataSetByteStream = getDataSetByteStream(transferSyntax, metaHeaderDataSet.position);

var elements = {};
var dataSet = new dicomParser.DataSet(dataSetByteStream.byteArrayParser, dataSetByteStream.byteArray, elements);
Expand All @@ -159,7 +136,7 @@ var dicomParser = (function(dicomParser) {

// main function here
function parseTheByteStream() {
var metaHeaderDataSet = readPart10Header();
var metaHeaderDataSet = dicomParser.readPart10Header(byteArray, options);

var dataSet = readDataSet(metaHeaderDataSet);

Expand Down Expand Up @@ -1898,6 +1875,75 @@ var dicomParser = (function (dicomParser)

return dicomParser;
}(dicomParser));
/**
* Parses a DICOM P10 byte array and returns a DataSet object with the parsed elements. If the options
* argument is supplied and it contains the untilTag property, parsing will stop once that
* tag is encoutered. This can be used to parse partial byte streams.
*
* @param byteArray the byte array
* @param options object to control parsing behavior (optional)
* @returns {DataSet}
* @throws error if an error occurs while parsing. The exception object will contain a property dataSet with the
* elements successfully parsed before the error.
*/
var dicomParser = (function(dicomParser) {
if(dicomParser === undefined)
{
dicomParser = {};
}

dicomParser.readPart10Header = function(byteArray, options) {

if(byteArray === undefined)
{
throw "dicomParser.readPart10Header: missing required parameter 'byteArray'";
}

var littleEndianByteStream = new dicomParser.ByteStream(dicomParser.littleEndianByteArrayParser, byteArray);

function readPrefix()
{
littleEndianByteStream.seek(128);
var prefix = littleEndianByteStream.readFixedString(4);
if(prefix !== "DICM")
{
throw "dicomParser.readPart10Header: DICM prefix not found at location 132";
}
}

// main function here
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();

var warnings = [];
var elements = {};
while(littleEndianByteStream.position < littleEndianByteStream.byteArray.length) {
var position = littleEndianByteStream.position;
var element = dicomParser.readDicomElementExplicit(littleEndianByteStream, warnings);
if(element.tag > 'x0002ffff') {
littleEndianByteStream.position = position;
break;
}
// Cache the littleEndianByteArrayParser for meta header elements, since the rest of the data set may be big endian
// and this parser will be needed later if the meta header values are to be read.
element.parser = dicomParser.littleEndianByteArrayParser;
elements[element.tag] = element;
}
var metaHeaderDataSet = new dicomParser.DataSet(littleEndianByteStream.byteArrayParser, littleEndianByteStream.byteArray, elements);
metaHeaderDataSet.warnings = littleEndianByteStream.warnings;
metaHeaderDataSet.position = littleEndianByteStream.position;
return metaHeaderDataSet;
}

// This is where we actually start parsing
return readTheHeader();
};

return dicomParser;
})(dicomParser);

/**
* Internal helper functions for parsing DICOM elements
*/
Expand Down Expand Up @@ -2207,7 +2253,7 @@ var dicomParser = (function (dicomParser)
dicomParser = {};
}

dicomParser.version = "1.1.6";
dicomParser.version = "1.2.0";

return dicomParser;
}(dicomParser));
Expand Down
4 changes: 2 additions & 2 deletions dist/dicomParser.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/package.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package.describe({
name: 'chafey:dicom-parser',
summary: 'Javascript parser for DICOM Part 10 data',
version: '1.1.7',
version: '1.2.0',
git: 'https://github.com/chafey/dicomParser.git/',
documentation: null
});
Expand Down
7 changes: 6 additions & 1 deletion examples/dumpWithDataDictionary/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ <h1>DICOM Dump with Data Dictionary v<span id="version"></span></h1>
<div class="text-center" style="margin-top:225px;"><h3>Drag and drop a DICOM P10 file here to dump its elements</h3></div>

</div>

</div>
</div>

Expand All @@ -100,6 +101,9 @@ <h1>DICOM Dump with Data Dictionary v<span id="version"></span></h1>
<!-- jquery - included to make things easier to demo, not needed by dicomParser -->
<script src="../jquery.min.js"></script>

<!-- pako - needed to support deflated transfer syntax -->
<script src="../pako.min.js"></script>

<!-- include the data dictionary -->
<script src="dataDictionary.js"></script>

Expand Down Expand Up @@ -269,7 +273,8 @@ <h1>DICOM Dump with Data Dictionary v<span id="version"></span></h1>
}
else if (element.fragments) {
text += "encapsulated pixel data with " + element.basicOffsetTable.length + " offsets and " + element.fragments.length + " fragments";
output.push('<li>' + text + '</li>');
title += "; dataOffset=" + element.dataOffset;
output.push("<li title='" + title + "'=>" + text + '</li>');
/*
output.push('<ul>');
Expand Down
3 changes: 3 additions & 0 deletions examples/pako.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion meteor/dicomParser/package.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package.describe({
name: 'chafey:dicom-parser',
summary: 'Javascript parser for DICOM Part 10 data',
version: '1.1.7',
version: '1.2.0',
git: 'https://github.com/chafey/dicomParser.git/',
documentation: null
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dicom-parser",
"version": "1.1.7",
"version": "1.2.0",
"description": "Javascript parser for DICOM Part 10 data",
"keywords": [
"DICOM",
Expand Down
Loading

0 comments on commit 347fe9a

Please sign in to comment.