in src/engine/loaders/LoaderDcmDaikon.js [90:460]
loadSingleSlice(fileIndex, fileName, strContent) {
// console.log("loadSingleSlice [" + fileIndex.toString() + "]");
const dataFile = new DataView(strContent);
let image = null;
try {
image = daikon.Series.parseImage(dataFile);
} catch (err) {
console.log('error parse dcm file buffer');
return LoadResult.BAD_DICOM;
}
if (image === undefined || image === null) {
return LoadResult.BAD_DICOM;
}
// console.log("dcm parse completed");
const yDim = image.getRows();
const xDim = image.getCols();
const bits = image.getBitsAllocated();
if (bits !== 8 && bits !== 16) {
console.log('Parse Dicom data. Strange bits per pixel = ' + bits.toString());
}
// data for hash code evaluate
const patientName = image.getPatientName();
const studyDescr = image.getImageDescription();
const studyDate = image.getStudyDate();
const seriesTime = image.getStudyTime();
const seriesDescr = image.getSeriesDescription();
const bodyPartExamined = 'NonExistInDiakonReader';
//const hasPixels = image.hasPixelData();
//const isComp = image.isCompressed();
const dicomInfo = this.m_loaderDicom.m_dicomInfo;
//const volSlice = this.m_loaderDicom.m_slicesVolume.getNewSlice();
const volSlice = new DicomSlice();
volSlice.m_sliceNumber = fileIndex;
volSlice.m_sliceLocation = -1;
volSlice.m_patientName = patientName;
volSlice.m_studyDescr = studyDescr;
volSlice.m_studyDate = studyDate;
volSlice.m_seriesTime = seriesTime;
volSlice.m_seriesDescr = seriesDescr;
volSlice.m_bodyPartExamined = bodyPartExamined;
volSlice.buildHash();
this.m_loaderDicom.m_minSlice = 0;
this.m_loaderDicom.m_maxSlice = 0;
this.m_loaderDicom.m_pixelSpacing.x = 0.0;
this.m_loaderDicom.m_pixelSpacing.y = 0.0;
this.m_loaderDicom.m_pixelSpacing.z = 0.0;
this.m_loaderDicom.m_xDim = xDim;
this.m_loaderDicom.m_yDim = yDim;
if (fileIndex < this.m_loaderDicom.m_slicesVolume.m_minSlice) {
this.m_loaderDicom.m_slicesVolume.m_minSlice = fileIndex;
}
if (fileIndex > this.m_loaderDicom.m_slicesVolume.m_maxSlice) {
this.m_loaderDicom.m_slicesVolume.m_maxSlice = fileIndex;
}
this.m_loaderDicom.m_newTagEvent.detail.fileName = fileName;
const sliceInfo = new DicomSliceInfo();
const strSlice = 'Slice ' + fileIndex.toString();
sliceInfo.m_sliceName = strSlice;
sliceInfo.m_fileName = fileName;
sliceInfo.m_tags = [];
dicomInfo.m_sliceInfo.push(sliceInfo);
const TAG_PADDING_VALUE = [0x0028, 0x0120];
const knownTags = [
// image dims
daikon.Tag.TAG_ROWS,
daikon.Tag.TAG_COLS,
daikon.Tag.TAG_ACQUISITION_MATRIX,
daikon.Tag.TAG_NUMBER_OF_FRAMES,
daikon.Tag.TAG_NUMBER_TEMPORAL_POSITIONS,
// voxel dims
daikon.Tag.TAG_PIXEL_SPACING,
daikon.Tag.TAG_SLICE_THICKNESS,
daikon.Tag.TAG_SLICE_GAP,
daikon.Tag.TAG_TR,
daikon.Tag.TAG_FRAME_TIME,
// datatype
daikon.Tag.TAG_BITS_ALLOCATED,
daikon.Tag.TAG_BITS_STORED,
daikon.Tag.TAG_PIXEL_REPRESENTATION,
daikon.Tag.TAG_HIGH_BIT,
daikon.Tag.TAG_PHOTOMETRIC_INTERPRETATION,
daikon.Tag.TAG_SAMPLES_PER_PIXEL,
daikon.Tag.TAG_PLANAR_CONFIG,
daikon.Tag.TAG_PALETTE_RED,
daikon.Tag.TAG_PALETTE_GREEN,
daikon.Tag.TAG_PALETTE_BLUE,
// data scale
daikon.Tag.TAG_DATA_SCALE_SLOPE,
daikon.Tag.TAG_DATA_SCALE_INTERCEPT,
daikon.Tag.TAG_DATA_SCALE_ELSCINT,
daikon.Tag.TAG_PIXEL_BANDWIDTH,
// range
daikon.Tag.TAG_IMAGE_MIN,
daikon.Tag.TAG_IMAGE_MAX,
daikon.Tag.TAG_WINDOW_CENTER,
daikon.Tag.TAG_WINDOW_WIDTH,
// description
daikon.Tag.TAG_PATIENT_NAME,
daikon.Tag.TAG_PATIENT_ID,
daikon.Tag.TAG_STUDY_DATE,
daikon.Tag.TAG_STUDY_TIME,
daikon.Tag.TAG_STUDY_DES,
daikon.Tag.TAG_IMAGE_TYPE,
daikon.Tag.TAG_IMAGE_COMMENTS,
daikon.Tag.TAG_SEQUENCE_NAME,
daikon.Tag.TAG_MODALITY,
daikon.Tag.TAG_FRAME_OF_REF_UID,
daikon.Tag.TAG_STUDY_UID,
// volume id
daikon.Tag.TAG_SERIES_DESCRIPTION,
daikon.Tag.TAG_SERIES_INSTANCE_UID,
daikon.Tag.TAG_SERIES_NUMBER,
daikon.Tag.TAG_ECHO_NUMBER,
daikon.Tag.TAG_TEMPORAL_POSITION,
// slice id
daikon.Tag.TAG_IMAGE_NUM,
daikon.Tag.TAG_SLICE_LOCATION,
// orientation
daikon.Tag.TAG_IMAGE_ORIENTATION,
daikon.Tag.TAG_IMAGE_POSITION,
daikon.Tag.TAG_SLICE_LOCATION_VECTOR,
// lut shape
daikon.Tag.TAG_LUT_SHAPE,
// pixel padding value
TAG_PADDING_VALUE,
];
// some default values
const TAG_PATIENT_BIRTH_DATE = [0x0010, 0x0030];
const TAG_BODY_PART_EXAMINED = [0x0018, 0x0015];
const TAG_INSTITUTION_NAME = [0x0008, 0x0080];
const TAG_OPERATORS_NAME = [0x0008, 0x1070];
const TAG_PHYSICANS_NAME = [0x0008, 0x0090];
dicomInfo.m_patientName = image.getPatientName();
dicomInfo.m_patientDateOfBirth = daikon.Image.getSingleValueSafely(
image.getTag(TAG_PATIENT_BIRTH_DATE[0], TAG_PATIENT_BIRTH_DATE[1]),
0
);
dicomInfo.m_seriesDescr = seriesDescr;
dicomInfo.m_studyDescr = studyDescr;
dicomInfo.m_studyDate = studyDate;
dicomInfo.m_seriesTime = seriesTime;
dicomInfo.m_bodyPartExamined = daikon.Image.getSingleValueSafely(image.getTag(TAG_BODY_PART_EXAMINED[0], TAG_BODY_PART_EXAMINED[1]), 0);
dicomInfo.m_institutionName = daikon.Image.getSingleValueSafely(image.getTag(TAG_INSTITUTION_NAME[0], TAG_INSTITUTION_NAME[1]), 0);
dicomInfo.m_operatorsName = daikon.Image.getSingleValueSafely(image.getTag(TAG_OPERATORS_NAME[0], TAG_OPERATORS_NAME[1]), 0);
dicomInfo.m_physicansName = daikon.Image.getSingleValueSafely(image.getTag(TAG_PHYSICANS_NAME[0], TAG_PHYSICANS_NAME[1]), 0);
// save all known tags to info array (can be displayed in app UI)
const numKnownTags = knownTags.length;
for (let k = 0; k < numKnownTags; k++) {
const ind = daikon.Utils.dec2hex(knownTags[k][0]) + daikon.Utils.dec2hex(knownTags[k][1]);
const tag = image.tags[ind];
if (tag !== undefined) {
const val = tag.value;
const group = tag.group;
const element = tag.element;
// const sliceInfo = dicomInfo.m_sliceInfo[0];
const tagInfo = new DicomTagInfo();
tagInfo.m_tag = '(' + LoaderDicom.numberToHexString(group) + ',' + LoaderDicom.numberToHexString(element) + ')';
const strTagName = this.m_loaderDicom.m_dictionary.getTextDesc(group, element);
tagInfo.m_attrName = strTagName.length > 1 ? strTagName : '';
// let strVal = LoaderDicom.getAttrValueAsString(tag);
let strVal = '';
if (val !== null) {
strVal = val.toString();
}
tagInfo.m_attrValue = strVal;
sliceInfo.m_tags.push(tagInfo);
if (NEED_DEBUG_PRINT_TAGS) {
console.log('tag readed = ' + group.toString() + ', ' + element.toString() + ', v = ' + strVal);
}
} // if non null tag
} // for k all known tags
let ind;
// slice location detect (to correct slices order on z)
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_SLICE_LOCATION[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_SLICE_LOCATION[1]);
const tagSLoc = image.tags[ind];
if (tagSLoc !== undefined) {
let sliceLoc = tagSLoc.value[0];
volSlice.m_sliceLocation = sliceLoc;
this.m_sliceLocMin = sliceLoc < this.m_sliceLocMin ? sliceLoc : this.m_sliceLocMin;
this.m_sliceLocMax = sliceLoc > this.m_sliceLocMax ? sliceLoc : this.m_sliceLocMax;
}
// slice number
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_IMAGE_NUM[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_IMAGE_NUM[1]);
const tagSlNum = image.tags[ind];
if (tagSlNum !== undefined) {
if (tagSlNum.value !== null) {
let sliceNumber = tagSlNum.value[0];
volSlice.m_sliceNumber = sliceNumber;
}
}
// samples per pixel
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_SAMPLES_PER_PIXEL[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_SAMPLES_PER_PIXEL[1]);
const tagSamPerPix = image.tags[ind];
if (tagSamPerPix !== undefined) {
let samPerPixel = tagSamPerPix.value[0];
this.m_loaderDicom.m_samplesPerPixel = samPerPixel;
}
// read pixel padding value
ind = daikon.Utils.dec2hex(0x0028) + daikon.Utils.dec2hex(0x0120);
const tagPPV = image.tags[ind];
if (tagPPV !== undefined) {
let valPad = tagPPV.value[0];
if (valPad < 0) {
valPad = -(valPad ^ 0xffff) - 1;
}
// console.log('val pad = ' + valPad);
this.m_loaderDicom.m_padValue = valPad;
}
// read window center
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_WINDOW_CENTER[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_WINDOW_CENTER[1]);
const tagWinCen = image.tags[ind];
if (tagWinCen !== undefined) {
this.m_loaderDicom.m_windowCenter = tagWinCen.value[0];
}
// read window width
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_WINDOW_WIDTH[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_WINDOW_WIDTH[1]);
const tagWinWid = image.tags[ind];
if (tagWinWid !== undefined) {
this.m_loaderDicom.m_windowWidth = tagWinWid.value[0];
}
// read rescale intercept
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_DATA_SCALE_INTERCEPT[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_DATA_SCALE_INTERCEPT[1]);
const tagResInt = image.tags[ind];
if (tagResInt !== undefined) {
this.m_loaderDicom.m_rescaleIntercept = tagResInt.value[0];
}
// read rescale slope
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_DATA_SCALE_SLOPE[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_DATA_SCALE_SLOPE[1]);
const tagResSlo = image.tags[ind];
if (tagResSlo !== undefined) {
this.m_loaderDicom.m_rescaleSlope = tagResSlo.value[0];
}
// read rescale type
const TAG_RESCALE_TYPE = [0x0028, 0x1054];
ind = daikon.Utils.dec2hex(TAG_RESCALE_TYPE[0]) + daikon.Utils.dec2hex(TAG_RESCALE_TYPE[1]);
const tagResTyp = image.tags[ind];
if (tagResTyp !== undefined) {
if (tagResTyp.value !== null && tagResTyp.value[0] === 'HU') {
this.m_loaderDicom.m_rescaleHounsfield = true;
}
}
// read pixel representation
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_PIXEL_REPRESENTATION[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_PIXEL_REPRESENTATION[1]);
const tagPixRep = image.tags[ind];
if (tagPixRep !== undefined) {
if (tagPixRep.value[0] === 1) {
this.m_loaderDicom.m_pixelRepresentaionSigned = true;
}
}
// read pixel spacing on xy (physical dimensions)
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_PIXEL_SPACING[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_PIXEL_SPACING[1]);
const tagPS = image.tags[ind];
if (tagPS !== undefined) {
let arrSpa = tagPS.value;
const VAL_2 = 2;
if (arrSpa.length === VAL_2) {
this.m_loaderDicom.m_pixelSpacing.x = parseFloat(arrSpa[0]);
this.m_loaderDicom.m_pixelSpacing.y = parseFloat(arrSpa[1]);
}
}
// read pixel spacing on z (physical dimensions)
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_SLICE_THICKNESS[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_SLICE_THICKNESS[1]);
const tagSLT = image.tags[ind];
if (tagSLT !== undefined) {
let arrThick = tagSLT.value;
if (arrThick && arrThick.length === 1) {
this.m_loaderDicom.m_pixelSpacing.z = parseFloat(arrThick[0]);
}
// console.log('val pad = ' + valPad);
}
// get image position (x,y,z), help to detect volume physical size
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_IMAGE_POSITION[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_IMAGE_POSITION[1]);
const tagImPos = image.tags[ind];
if (tagImPos !== undefined) {
const NUM_COMPONENTS_3 = 3;
if (tagImPos.value.length === NUM_COMPONENTS_3) {
// eslint-disable-next-line
const xPos = tagImPos.value[0];
// eslint-disable-next-line
const yPos = tagImPos.value[1];
// eslint-disable-next-line
const zPos = tagImPos.value[2];
this.m_loaderDicom.m_imagePosMin.x = xPos < this.m_loaderDicom.m_imagePosMin.x ? xPos : this.m_loaderDicom.m_imagePosMin.x;
this.m_loaderDicom.m_imagePosMin.y = yPos < this.m_loaderDicom.m_imagePosMin.y ? yPos : this.m_loaderDicom.m_imagePosMin.y;
this.m_loaderDicom.m_imagePosMin.z = zPos < this.m_loaderDicom.m_imagePosMin.z ? zPos : this.m_loaderDicom.m_imagePosMin.z;
this.m_loaderDicom.m_imagePosMax.x = xPos > this.m_loaderDicom.m_imagePosMax.x ? xPos : this.m_loaderDicom.m_imagePosMax.x;
this.m_loaderDicom.m_imagePosMax.y = yPos > this.m_loaderDicom.m_imagePosMax.y ? yPos : this.m_loaderDicom.m_imagePosMax.y;
this.m_loaderDicom.m_imagePosMax.z = zPos > this.m_loaderDicom.m_imagePosMax.z ? zPos : this.m_loaderDicom.m_imagePosMax.z;
if (NEED_DEBUG_PRINT_TAGS) {
console.log(`TAG. image position x,y,z = ${xPos}, ${yPos}, ${zPos}`);
} // if print
} // if 3 components in array
} // if tag exists
// read transfer syntax (detect big endian)
ind = daikon.Utils.dec2hex(daikon.Tag.TAG_TRANSFER_SYNTAX[0]) + daikon.Utils.dec2hex(daikon.Tag.TAG_TRANSFER_SYNTAX[1]);
const tagTraSyn = image.tags[ind];
if (tagTraSyn !== undefined) {
let arrStrTra = tagTraSyn.value;
if (arrStrTra[0] === '1.2.840.10008.1.2.2') {
this.m_loaderDicom.m_littleEndian = false;
}
// console.log('val pad = ' + valPad);
}
// add volume slice to slices volume (and manage series)
this.m_loaderDicom.m_slicesVolume.addSlice(volSlice);
// check correct read image
const pixels = image.getRawData();
const numBytesPixelsRead = pixels.byteLength;
const VAL_8 = 8;
const numBytesPixelsExpected = xDim * yDim * Math.floor(bits / VAL_8) * this.m_loaderDicom.m_samplesPerPixel;
if (numBytesPixelsRead !== numBytesPixelsExpected) {
console.log('Error read Dicom via Diakon parser');
return LoadResult.ERROR_COMPRESSED_IMAGE_NOT_SUPPORTED;
}
// create pixels storage in volume
if (this.m_loaderDicom.m_pixelRepresentaionSigned) {
volSlice.m_image = new Int16Array(xDim * yDim);
} else {
volSlice.m_image = new Uint16Array(xDim * yDim);
}
// copy pixels (ArrayBuffer) into volSlice.m_image
const numPixels = xDim * yDim;
if (this.m_loaderDicom.m_samplesPerPixel === 1) {
const pixSrc = new Uint16Array(pixels);
for (let i = 0; i < numPixels; i++) {
volSlice.m_image[i] = pixSrc[i];
} // for i
} // if 1 sample per pixel
else if (this.m_loaderDicom.m_samplesPerPixel === 3) {
const pixSrc = new Uint8Array(pixels);
let j = 0;
for (let i = 0; i < numPixels; i++, j += 3) {
const bVal = pixSrc[j + 0];
const gVal = pixSrc[j + 1];
const rVal = pixSrc[j + 2];
volSlice.m_image[i] = Math.floor((bVal + gVal + rVal) / 3);
} // for i
} // if samples per pixel is 3
// store x, y dims
volSlice.m_xDim = xDim;
volSlice.m_yDim = yDim;
return LoadResult.SUCCESS;
} // end load single slice