Passed
Push — dev ( 86a962...d312fb )
by Salim
03:41
created

DicomFile.getRadiopharmaceuticalTag   A

Complexity

Conditions 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
1
/**
2
 Copyright (C) 2018-2020 KANOUN Salim
3
 This program is free software; you can redistribute it and/or modify
4
 it under the terms of the Affero GNU General Public v.3 License as published by
5
 the Free Software Foundation;
6
 This program is distributed in the hope that it will be useful,
7
 but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
 Affero GNU General Public Public for more details.
10
 You should have received a copy of the Affero GNU General Public Public along
11
 with this program; if not, write to the Free Software Foundation, Inc.,
12
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
13
 */
14
15
class DicomFile {
16
	constructor(originalFile, dataSet) {
17
		this.originalFile = originalFile;
18
		this.dataSet = dataSet;
19
		this.header = this.retrieveHeaderData(dataSet.byteArray);
20
		this.removeByteArrayReferences();
21
	}
22
23
	removeByteArrayReferences(dataSet = this.dataSet) {
24
		// Recursively delete byteArray references
25
		for (let propName in dataSet) {
26
			let prop = dataSet[propName];
27
28
			// Check inner elements
29
			if (propName == 'elements') {
30
				for (let elmt in prop) {
31
					if (prop[elmt].items !== undefined) {
32
						for (let it of prop[elmt].items) {
33
							this.removeByteArrayReferences(it.dataSet);
34
						}
35
					}
36
				}
37
			}
38
39
			if (propName == 'byteArray' || propName == 'byteArrayParser') {
40
				// Delete reference to the object
41
				dataSet[propName] = null;
42
			}
43
44
		}
45
	}
46
47
	retrieveHeaderData(byteArray) {
48
		let pxData = this.dataSet.elements.x7fe00010;
49
		//If no pixel data return the full byte array
50
		if(pxData === undefined){
51
			return byteArray.slice()
52
		}
53
		//if pixel data here return only header
54
		return byteArray.slice(0, pxData.dataOffset-1);
55
	}
56
57
	anonymise(tagsToErase) {
58
		if (tagsToErase === undefined) {
59
			tagsToErase = [
60
				'00101005',	// Patient's Birth Name
61
				'00100010', // Patient's Name
62
				'00100020', // Patient's ID
63
				'00100030',	// Patient's Birth Date
64
				'00101040', // Patient's Address
65
				'00080050',	// Accession Number
66
				'00080080',	// Institution Name
67
				'00080081',	// Institution Adress
68
				'00080090',	// Referring Physician's Name
69
				'00080092',	// Referring Physician's Adress
70
				'00080094', // Refering Physician's Telephone Number
71
				'00080096', // Referring Pysician ID Sequence
72
				'00081040', // Institutional Departement Name
73
				'00081048', // Physician Of Record
74
				'00081049', // Physician Of Record ID Sequence
75
				'00081050', // Performing Physician's Name
76
				'00081052', // Performing Physicians ID Sequence
77
				'00081060', // Name Of Physician Reading Study
78
				'00081062', // Physician Reading Study ID Sequence
79
				'00081070', // Operators Name
80
				'00200010', // Study ID
81
				'0040A123'  // Person Name
82
			];
83
		}
84
85
		let notFoundTags = [];
86
87
		for (let id of tagsToErase) {
88
			try {
89
				this.erase(id);
90
			} catch (e) {
91
				// Only catch "Can't find tag id" error
92
				if (e != `Can't find ${id} while erasing.`) {
93
					throw e;
94
				}
95
				notFoundTags.push(id);
96
			}
97
		}
98
99
		/*console.warn(`Couldn't find ${notFoundTags.toString()}`
100
			+ ` while anonymising ${this.originalFile.name}`
101
			+ ` => These tags will be skipped.`);*/
102
	}
103
104
	/**
105
	 * Write unsignificant content at a specified tag in the dataset
106
	 */
107
	erase(id, newContent = '*') {
108
		id = id.toLowerCase();
109
110
		const element = this.dataSet.elements[`x${id}`];
111
112
		if (element === undefined) {
113
			throw `Can't find ${id.toUpperCase()} while erasing.`;
114
		}
115
116
		// Retrieve the index position of the element in the data set array
117
		const dataOffset = element.dataOffset;
118
119
		// Retrieve the length of the element
120
		const length = element.length;
121
122
		// Fill the field with unsignificant values
123
		for (let i = 0; i < length; i++) {
124
			// Get charcode of the current char in 'newContent'
125
			const char = newContent.charCodeAt(i % newContent.length);
126
127
			// Write this char in the array
128
			this.header[dataOffset + i] = char;
129
		}
130
	}
131
132
	getRadiopharmaceuticalTag(tagAddress){
133
		try{
134
			let elmt = this.dataSet.elements['x00540016']
135
			let radioPharmElements = elmt.items[0].dataSet.elements
136
			return this.string(radioPharmElements['x'+tagAddress])
137
		}catch ( error ) { 
138
			console.log(error)
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
139
			return undefined 
140
		}
141
	}
142
143
	getDicomTag(tagAddress){
144
		let elmt = this.dataSet.elements['x'+tagAddress]
145
		if ( elmt.length > 0) {
146
			// Return the value of the dicom attribute
147
			return this.string(elmt);
148
		}
149
		else return undefined
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
150
	}
151
152
	getAccessionNumber() {
153
		return this.getDicomTag("00080050");
154
	}
155
	getAcquisitionDate() {
156
		return this.getDicomTag("00080020");
157
	}
158
	getInstanceNumber() {
159
		return this.getDicomTag("00200013");
160
	}
161
	getModality() {
162
		return this.getDicomTag("00080060");
163
	}
164
	getPatientBirthDate() {
165
		return this.getDicomTag("00100030");
166
	}
167
	getPatientID() {
168
		return this.getDicomTag("00100020");
169
	}
170
	getPatientName() {
171
		return this.getDicomTag("00100010");
172
	}
173
	getPatientSex() {
174
		return this.getDicomTag("00100040");
175
	}
176
	getSeriesInstanceUID() {
177
		return this.getDicomTag("0020000e");
178
	}
179
	getSeriesDate() {
180
		return this.getDicomTag("00080021");
181
	}
182
	getSeriesDescription() {
183
		return this.getDicomTag("0008103e");
184
	}
185
	getSOPInstanceUID() {
186
		return this.getDicomTag("00080018");
187
	}
188
	getSOPClassUID() {
189
		return this.getDicomTag("00080016");
190
	}
191
	//SK A TESTER : On ne pourrait utiliser que le 0002,0002
192
	//Ce tag est un duplicat de 00080016 cf https://stackoverflow.com/questions/32689446/is-it-true-that-dicom-media-storage-sop-instance-uid-sop-instance-uid-why
193
	getMediaStorageSOP(){
194
		return this.getDicomTag("00020002")
195
	}
196
	getSeriesNumber() {
197
		return this.getDicomTag('00200011')
198
	}
199
	getStudyInstanceUID() {
200
		return this.getDicomTag('0020000d')
201
	}
202
	getStudyDate() {
203
		return this.getDicomTag("00080020");
204
	}
205
	getStudyID() {
206
		return this.getDicomTag("00200010");
207
	}
208
	getStudyDescription() {
209
		return this.getDicomTag("00081030");
210
	}
211
212
	/**
213
	 * Returns element contain as a string
214
	 * @param {*} element element from the data set
215
	 */
216
	string(element) {
217
		let position = element.dataOffset;
218
		let length = element.length;
219
220
		if (length < 0) {
221
			throw 'Negative length';
222
		}
223
		if (position + length > this.header.length) {
224
			throw 'Out of range index';
225
		}
226
227
		var result = '';
228
		var byte;
229
230
		for (var i = 0; i < length; i++) {
231
			byte = this.header[position + i];
232
			if (byte === 0) {
233
				position += length;
0 ignored issues
show
Unused Code introduced by
The assignment to variable position seems to be never used. Consider removing it.
Loading history...
234
				return result.trim();
235
			}
236
			result += String.fromCharCode(byte);
237
		}
238
		return result.trim();
239
	}
240
241
	isSecondaryCaptureImg() {
242
		const secondaryCaptureImgValues = [
243
			'1.2.840.10008.5.1.4.1.1.7',
244
			'1.2.840.10008.5.1.4.1.1.7.1',
245
			'1.2.840.10008.5.1.4.1.1.7.2',
246
			'1.2.840.10008.5.1.4.1.1.7.3',
247
			'1.2.840.10008.5.1.4.1.1.7.4',
248
			'1.2.840.10008.5.1.4.1.1.88.11',
249
			'1.2.840.10008.5.1.4.1.1.88.22',
250
			'1.2.840.10008.5.1.4.1.1.88.33',
251
			'1.2.840.10008.5.1.4.1.1.88.40',
252
			'1.2.840.10008.5.1.4.1.1.88.50',
253
			'1.2.840.10008.5.1.4.1.1.88.59',
254
			'1.2.840.10008.5.1.4.1.1.88.65',
255
			'1.2.840.10008.5.1.4.1.1.88.67'
256
		];
257
		return secondaryCaptureImgValues.includes(this.getSOPClassUID());
258
	}
259
260
	isDicomDir(){
261
		const dicomDirSopValues = [
262
			'1.2.840.10008.1.3.10'
263
		]
264
		return dicomDirSopValues.includes(this.getMediaStorageSOP());
265
	}
266
267
	clearData() {
268
		this.header = null;
269
		this.dataSet = null;
270
	}
271
}