diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..bc8474bce --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text +*.js text + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary diff --git a/io/parser.js b/io/parser.js index 3dbce8e22..e0dcfde99 100644 --- a/io/parser.js +++ b/io/parser.js @@ -491,7 +491,8 @@ X.parser.createIJKVolume = function(_data, _dims, _max){ for (_i = 0; _i < _dims[0]; _i++) { _pix_value = _current_k[_data_pointer]; - _imageN[_k][_j][_i] = 255 * (_pix_value / _max); + _imageN[_k][_j][_i] = + 255 * (_pix_value / _max); _image[_k][_j][_i] = _pix_value; _data_pointer++; @@ -913,7 +914,11 @@ X.parser.reslice2 = function(_sliceOrigin, _sliceXYSpacing, _sliceNormal, _color } else { - pixelValue_r = pixelValue_g = pixelValue_b = 255 * (pixval / object._max); + // Rescale from [0, max] to [0, 255] if not negative scalars, else + // rescale from [min, max] to [0, 255] + var rescale_min = object._min < 0 ? object._min : 0; + pixelValue_r = pixelValue_g = pixelValue_b = + 255 * ((pixval - rescale_min) / (object._max - rescale_min)); pixelValue_a = 255; } @@ -1131,7 +1136,7 @@ X.parser.prototype.reslice = function(object) { // real volume object._IJKVolume = _IJKVolumes[0]; // normalized volume - object._IJKVolumeN = _IJKVolumes[1]; + object._IJKVolumeN = _IJKVolumes[1]; // appears to not be used, will not work with signed scalars X.TIMER(this._classname + '.reslice'); // ------------------------------------------ diff --git a/io/parserDCM.js b/io/parserDCM.js index 151d08aa9..cd3c5f32c 100644 --- a/io/parserDCM.js +++ b/io/parserDCM.js @@ -305,13 +305,25 @@ X.parserDCM.prototype.parse = function(container, object, data, flag) { // create data container switch (first_image[0].bits_allocated) { case 8: - first_image_data = new Uint8Array(first_image_size); + if (first_image[0]['pixel_representation'] == 1) { //signed + first_image_data = new Int8Array(first_image_size); + } else { + first_image_data = new Uint8Array(first_image_size); + } break; case 16: - first_image_data = new Uint16Array(first_image_size); + if (first_image[0]['pixel_representation'] == 1) { //signed + first_image_data = new Int16Array(first_image_size); + } else { + first_image_data = new Uint16Array(first_image_size); + } break; case 32: - first_image_data = new Uint32Array(first_image_size); + if (first_image[0]['pixel_representation'] == 1) { //signed + first_image_data = new Int32Array(first_image_size); + } else { + first_image_data = new Uint32Array(first_image_size); + } default: window.console.log("Unknown number of bits allocated - using default: 32 bits"); break; @@ -723,13 +735,22 @@ X.parserDCM.prototype.parseStream = function(data, object) { slice['pixel_spacing'] = [ parseFloat(_pixel_spacing[0]), parseFloat(_pixel_spacing[1]), Infinity ]; break; + + case 0x0103: // "Pixel Representation" + // should always be a US + // 0: unsigned + // 1: signed + var _ushort = _bytes[_bytePointer]; + slice['pixel_representation'] = _ushort; + _bytePointer+=_VL/2; + break; + case 0x1052: // rescale intercept case 0x1053: // rescale slope case 0x1050: // WindowCenter case 0x1051: // WindowWidth case 0x0004: // "Photometric Interpretation" case 0x0102: // "High Bit" - case 0x0103: // "Pixel Representation" case 0x1054: // "Rescale Type" case 0x2110: // "Lossy Image Compression" @@ -918,13 +939,25 @@ X.parserDCM.prototype.parseStream = function(data, object) { switch (slice.bits_allocated) { case 8: - slice.data = new Uint8Array(slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + slice.data = new Int8Array(slice['columns'] * slice['rows']); + } else { + slice.data = new Uint8Array(slice['columns'] * slice['rows']); + } break; case 16: - slice.data = new Uint16Array(slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + slice.data = new Int16Array(slice['columns'] * slice['rows']); + } else { + slice.data = new Uint16Array(slice['columns'] * slice['rows']); + } break; case 32: - slice.data = new Uint32Array(slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + slice.data = new Int32Array(slice['columns'] * slice['rows']); + } else { + slice.data = new Uint32Array(slice['columns'] * slice['rows']); + } break; } @@ -936,14 +969,26 @@ X.parserDCM.prototype.parseStream = function(data, object) { switch (slice.bits_allocated) { case 8: - _data = this.scan('uchar', slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + _data = this.scan('schar', slice['columns'] * slice['rows']); + } else { + _data = this.scan('uchar', slice['columns'] * slice['rows']); + } break; case 16: - _data = this.scan('ushort', slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + _data = this.scan('sshort', slice['columns'] * slice['rows']); + } else { + _data = this.scan('ushort', slice['columns'] * slice['rows']); + } break; case 32: - _data = this.scan('uint', slice['columns'] * slice['rows']); + if (slice['pixel_representation'] == 1) { // Signed + _data = this.scan('sint', slice['columns'] * slice['rows']); + } else { + _data = this.scan('uint', slice['columns'] * slice['rows']); + } break; }