comparison OrthancFramework/Sources/DicomFormat/DicomStreamReader.cpp @ 4220:92a21efa5c96

reorganization of DicomStreamReader
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 30 Sep 2020 15:33:47 +0200
parents
children e4c0218b6b23
comparison
equal deleted inserted replaced
4219:b8ed2852a35d 4220:92a21efa5c96
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "../PrecompiledHeaders.h"
24 #include "DicomStreamReader.h"
25
26 #include "../OrthancException.h"
27
28 namespace Orthanc
29 {
30 static uint16_t ReadUnsignedInteger16(const char* dicom,
31 bool littleEndian)
32 {
33 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom);
34
35 if (littleEndian)
36 {
37 return (static_cast<uint16_t>(p[0]) |
38 (static_cast<uint16_t>(p[1]) << 8));
39 }
40 else
41 {
42 return (static_cast<uint16_t>(p[1]) |
43 (static_cast<uint16_t>(p[0]) << 8));
44 }
45 }
46
47
48 static uint32_t ReadUnsignedInteger32(const char* dicom,
49 bool littleEndian)
50 {
51 const uint8_t* p = reinterpret_cast<const uint8_t*>(dicom);
52
53 if (littleEndian)
54 {
55 return (static_cast<uint32_t>(p[0]) |
56 (static_cast<uint32_t>(p[1]) << 8) |
57 (static_cast<uint32_t>(p[2]) << 16) |
58 (static_cast<uint32_t>(p[3]) << 24));
59 }
60 else
61 {
62 return (static_cast<uint32_t>(p[3]) |
63 (static_cast<uint32_t>(p[2]) << 8) |
64 (static_cast<uint32_t>(p[1]) << 16) |
65 (static_cast<uint32_t>(p[0]) << 24));
66 }
67 }
68
69
70 static DicomTag ReadTag(const char* dicom,
71 bool littleEndian)
72 {
73 return DicomTag(ReadUnsignedInteger16(dicom, littleEndian),
74 ReadUnsignedInteger16(dicom + 2, littleEndian));
75 }
76
77
78 static bool IsShortExplicitTag(ValueRepresentation vr)
79 {
80 /**
81 * Are we in the case of Table 7.1-2? "Data Element with
82 * Explicit VR of AE, AS, AT, CS, DA, DS, DT, FL, FD, IS, LO,
83 * LT, PN, SH, SL, SS, ST, TM, UI, UL and US"
84 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
85 **/
86 return (vr == ValueRepresentation_ApplicationEntity /* AE */ ||
87 vr == ValueRepresentation_AgeString /* AS */ ||
88 vr == ValueRepresentation_AttributeTag /* AT */ ||
89 vr == ValueRepresentation_CodeString /* CS */ ||
90 vr == ValueRepresentation_Date /* DA */ ||
91 vr == ValueRepresentation_DecimalString /* DS */ ||
92 vr == ValueRepresentation_DateTime /* DT */ ||
93 vr == ValueRepresentation_FloatingPointSingle /* FL */ ||
94 vr == ValueRepresentation_FloatingPointDouble /* FD */ ||
95 vr == ValueRepresentation_IntegerString /* IS */ ||
96 vr == ValueRepresentation_LongString /* LO */ ||
97 vr == ValueRepresentation_LongText /* LT */ ||
98 vr == ValueRepresentation_PersonName /* PN */ ||
99 vr == ValueRepresentation_ShortString /* SH */ ||
100 vr == ValueRepresentation_SignedLong /* SL */ ||
101 vr == ValueRepresentation_SignedShort /* SS */ ||
102 vr == ValueRepresentation_ShortText /* ST */ ||
103 vr == ValueRepresentation_Time /* TM */ ||
104 vr == ValueRepresentation_UniqueIdentifier /* UI */ ||
105 vr == ValueRepresentation_UnsignedLong /* UL */ ||
106 vr == ValueRepresentation_UnsignedShort /* US */);
107 }
108
109
110 static void PrintBlock(const std::string& block)
111 {
112 for (size_t i = 0; i < block.size(); i++)
113 {
114 printf("%02x ", static_cast<uint8_t>(block[i]));
115 if (i % 16 == 15)
116 printf("\n");
117 }
118 printf("\n");
119 }
120
121
122
123 bool DicomStreamReader::IsLittleEndian() const
124 {
125 return (transferSyntax_ != DicomTransferSyntax_BigEndianExplicit);
126 }
127
128
129 void DicomStreamReader::HandlePreamble(IVisitor& visitor,
130 const std::string& block)
131 {
132 //printf("PREAMBLE:\n");
133 //PrintBlock(block);
134
135 assert(block.size() == 144u);
136 assert(reader_.GetProcessedBytes() == 144u);
137
138 /**
139 * The "DICOM file meta information" is always encoded using
140 * "Explicit VR Little Endian Transfer Syntax"
141 * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html
142 **/
143 if (block[128] != 'D' ||
144 block[129] != 'I' ||
145 block[130] != 'C' ||
146 block[131] != 'M' ||
147 ReadTag(block.c_str() + 132, true) != DicomTag(0x0002, 0x0000) ||
148 block[136] != 'U' ||
149 block[137] != 'L' ||
150 ReadUnsignedInteger16(block.c_str() + 138, true) != 4)
151 {
152 throw OrthancException(ErrorCode_BadFileFormat);
153 }
154
155 uint32_t length = ReadUnsignedInteger32(block.c_str() + 140, true);
156
157 reader_.Schedule(length);
158 state_ = State_MetaHeader;
159 }
160
161
162 void DicomStreamReader::HandleMetaHeader(IVisitor& visitor,
163 const std::string& block)
164 {
165 //printf("META-HEADER:\n");
166 //PrintBlock(block);
167
168 size_t pos = 0;
169 const char* p = block.c_str();
170
171 bool hasTransferSyntax = false;
172
173 while (pos + 8 <= block.size())
174 {
175 DicomTag tag = ReadTag(p + pos, true);
176
177 ValueRepresentation vr = StringToValueRepresentation(std::string(p + pos + 4, 2), true);
178
179 if (IsShortExplicitTag(vr))
180 {
181 uint16_t length = ReadUnsignedInteger16(p + pos + 6, true);
182
183 std::string value;
184 value.assign(p + pos + 8, length);
185
186 if (tag.GetGroup() == 0x0002)
187 {
188 visitor.VisitMetaHeaderTag(tag, vr, value);
189 }
190
191 if (tag == DICOM_TAG_TRANSFER_SYNTAX_UID)
192 {
193 // Remove possible padding byte
194 if (!value.empty() &&
195 value[value.size() - 1] == '\0')
196 {
197 value.resize(value.size() - 1);
198 }
199
200 if (LookupTransferSyntax(transferSyntax_, value))
201 {
202 hasTransferSyntax = true;
203 }
204 else
205 {
206 throw OrthancException(ErrorCode_NotImplemented, "Unsupported transfer syntax: " + value);
207 }
208 }
209
210 pos += length + 8;
211 }
212 else if (pos + 12 <= block.size())
213 {
214 uint16_t reserved = ReadUnsignedInteger16(p + pos + 6, true);
215 if (reserved != 0)
216 {
217 break;
218 }
219
220 uint32_t length = ReadUnsignedInteger32(p + pos + 8, true);
221
222 std::string value;
223 value.assign(p + pos + 12, length);
224
225 if (tag.GetGroup() == 0x0002)
226 {
227 visitor.VisitMetaHeaderTag(tag, vr, value);
228 }
229
230 pos += length + 12;
231 }
232 }
233
234 if (pos != block.size())
235 {
236 throw OrthancException(ErrorCode_BadFileFormat);
237 }
238
239 if (!hasTransferSyntax)
240 {
241 throw OrthancException(ErrorCode_BadFileFormat, "DICOM file meta-header without transfer syntax UID");
242 }
243
244 visitor.VisitTransferSyntax(transferSyntax_);
245
246 reader_.Schedule(8);
247 state_ = State_DatasetTag;
248 }
249
250
251 void DicomStreamReader::HandleDatasetTag(const std::string& block,
252 const DicomTag& untilTag)
253 {
254 static const DicomTag DICOM_TAG_SEQUENCE_ITEM(0xfffe, 0xe000);
255 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_ITEM(0xfffe, 0xe00d);
256 static const DicomTag DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE(0xfffe, 0xe0dd);
257
258 assert(block.size() == 8u);
259
260 const bool littleEndian = IsLittleEndian();
261 DicomTag tag = ReadTag(block.c_str(), littleEndian);
262
263 if (sequenceDepth_ == 0 &&
264 tag >= untilTag)
265 {
266 state_ = State_Done;
267 return;
268 }
269
270 if (tag == DICOM_TAG_SEQUENCE_ITEM ||
271 tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM ||
272 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
273 {
274 //printf("SEQUENCE TAG:\n");
275 //PrintBlock(block);
276
277 // The special sequence items are encoded like "Implicit VR"
278 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, littleEndian);
279
280 if (tag == DICOM_TAG_SEQUENCE_ITEM)
281 {
282 for (unsigned int i = 0; i <= sequenceDepth_; i++)
283 printf(" ");
284 if (length == 0xffffffffu)
285 {
286 // Undefined length: Need to loop over the tags of the nested dataset
287 printf("...next dataset in sequence...\n");
288 reader_.Schedule(8);
289 state_ = State_DatasetTag;
290 }
291 else
292 {
293 // Explicit length: Can skip the full sequence at once
294 printf("...next dataset in sequence... %u bytes\n", length);
295 reader_.Schedule(length);
296 state_ = State_DatasetValue;
297 }
298 }
299 else if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_ITEM ||
300 tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
301 {
302 if (length != 0 ||
303 sequenceDepth_ == 0)
304 {
305 throw OrthancException(ErrorCode_BadFileFormat);
306 }
307
308 if (tag == DICOM_TAG_SEQUENCE_DELIMITATION_SEQUENCE)
309 {
310 for (unsigned int i = 0; i < sequenceDepth_; i++)
311 printf(" ");
312 printf("...leaving sequence...\n");
313
314 sequenceDepth_ --;
315 }
316 else
317 {
318 if (sequenceDepth_ == 0)
319 {
320 throw OrthancException(ErrorCode_BadFileFormat);
321 }
322 }
323
324 reader_.Schedule(8);
325 state_ = State_DatasetTag;
326 }
327 else
328 {
329 throw OrthancException(ErrorCode_InternalError);
330 }
331 }
332 else
333 {
334 //printf("DATASET TAG:\n");
335 //PrintBlock(block);
336
337 previousTag_ = tag;
338
339 ValueRepresentation vr = ValueRepresentation_Unknown;
340
341 if (transferSyntax_ == DicomTransferSyntax_LittleEndianImplicit)
342 {
343 if (sequenceDepth_ == 0)
344 {
345 danglingTag_ = tag;
346 danglingVR_ = vr;
347 }
348
349 uint32_t length = ReadUnsignedInteger32(block.c_str() + 4, true /* little endian */);
350 HandleDatasetExplicitLength(length);
351 }
352 else
353 {
354 // This in an explicit transfer syntax
355
356 vr = StringToValueRepresentation(
357 std::string(block.c_str() + 4, 2), false /* ignore unknown VR */);
358
359 if (vr != ValueRepresentation_Sequence &&
360 sequenceDepth_ > 0)
361 {
362 for (unsigned int i = 0; i <= sequenceDepth_; i++)
363 printf(" ");
364 printf("%s\n", tag.Format().c_str());
365 }
366
367 if (vr == ValueRepresentation_Sequence)
368 {
369 for (unsigned int i = 0; i <= sequenceDepth_; i++)
370 printf(" ");
371 printf("...entering sequence... %s\n", tag.Format().c_str());
372 sequenceDepth_ ++;
373 reader_.Schedule(4);
374 state_ = State_SequenceExplicitLength;
375 }
376 else if (IsShortExplicitTag(vr))
377 {
378 uint16_t length = ReadUnsignedInteger16(block.c_str() + 6, littleEndian);
379
380 reader_.Schedule(length);
381 state_ = State_DatasetValue;
382 }
383 else
384 {
385 uint16_t reserved = ReadUnsignedInteger16(block.c_str() + 6, littleEndian);
386 if (reserved != 0)
387 {
388 throw OrthancException(ErrorCode_BadFileFormat);
389 }
390
391 reader_.Schedule(4);
392 state_ = State_DatasetExplicitLength;
393 }
394
395 if (sequenceDepth_ == 0)
396 {
397 danglingTag_ = tag;
398 danglingVR_ = vr;
399 }
400 }
401 }
402 }
403
404
405 void DicomStreamReader::HandleDatasetExplicitLength(uint32_t length)
406 {
407 if (length == 0xffffffffu)
408 {
409 /**
410 * This is the case of pixel data with compressed transfer
411 * syntaxes. Schedule the reading of the first tag of the
412 * nested dataset.
413 * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_7.5.html
414 **/
415
416 for (unsigned int i = 0; i <= sequenceDepth_; i++)
417 printf(" ");
418 printf("...entering sequence... %s\n", previousTag_.Format().c_str());
419
420 state_ = State_DatasetTag;
421 reader_.Schedule(8);
422 sequenceDepth_ ++;
423 }
424 else
425 {
426 reader_.Schedule(length);
427 state_ = State_DatasetValue;
428 }
429 }
430
431
432 void DicomStreamReader::HandleDatasetExplicitLength(const std::string& block)
433 {
434 //printf("DATASET TAG LENGTH:\n");
435 //PrintBlock(block);
436
437 assert(block.size() == 4);
438
439 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian());
440 HandleDatasetExplicitLength(length);
441 }
442
443
444 void DicomStreamReader::HandleSequenceExplicitLength(const std::string& block)
445 {
446 //printf("DATASET TAG LENGTH:\n");
447 //PrintBlock(block);
448
449 assert(block.size() == 4);
450
451 uint32_t length = ReadUnsignedInteger32(block.c_str(), IsLittleEndian());
452 if (length == 0xffffffffu)
453 {
454 state_ = State_DatasetTag;
455 reader_.Schedule(8);
456 }
457 else
458 {
459 for (unsigned int i = 0; i <= sequenceDepth_; i++)
460 printf(" ");
461 printf("...skipping sequence thanks to explicit length... %d\n", length);
462
463 reader_.Schedule(length);
464 state_ = State_SequenceExplicitValue;
465 }
466 }
467
468
469 void DicomStreamReader::HandleSequenceExplicitValue()
470 {
471 if (sequenceDepth_ == 0)
472 {
473 throw OrthancException(ErrorCode_InternalError);
474 }
475
476 sequenceDepth_ --;
477
478 state_ = State_DatasetTag;
479 reader_.Schedule(8);
480 }
481
482
483 void DicomStreamReader::HandleDatasetValue(IVisitor& visitor,
484 const std::string& block)
485 {
486 if (sequenceDepth_ == 0)
487 {
488 bool c;
489
490 if (!block.empty() &&
491 (block[block.size() - 1] == ' ' ||
492 block[block.size() - 1] == '\0') &&
493 (danglingVR_ == ValueRepresentation_ApplicationEntity ||
494 danglingVR_ == ValueRepresentation_AgeString ||
495 danglingVR_ == ValueRepresentation_CodeString ||
496 danglingVR_ == ValueRepresentation_DecimalString ||
497 danglingVR_ == ValueRepresentation_IntegerString ||
498 danglingVR_ == ValueRepresentation_LongString ||
499 danglingVR_ == ValueRepresentation_LongText ||
500 danglingVR_ == ValueRepresentation_PersonName ||
501 danglingVR_ == ValueRepresentation_ShortString ||
502 danglingVR_ == ValueRepresentation_ShortText ||
503 danglingVR_ == ValueRepresentation_UniqueIdentifier ||
504 danglingVR_ == ValueRepresentation_UnlimitedText))
505 {
506 std::string s(block.begin(), block.end() - 1);
507 c = visitor.VisitDatasetTag(danglingTag_, danglingVR_, s, IsLittleEndian());
508 }
509 else
510 {
511 c = visitor.VisitDatasetTag(danglingTag_, danglingVR_, block, IsLittleEndian());
512 }
513
514 if (!c)
515 {
516 state_ = State_Done;
517 return;
518 }
519 }
520
521 reader_.Schedule(8);
522 state_ = State_DatasetTag;
523 }
524
525
526 DicomStreamReader::DicomStreamReader(std::istream& stream) :
527 reader_(stream),
528 state_(State_Preamble),
529 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit), // Dummy
530 previousTag_(0x0000, 0x0000), // Dummy
531 danglingTag_(0x0000, 0x0000), // Dummy
532 danglingVR_(ValueRepresentation_Unknown), // Dummy
533 sequenceDepth_(0)
534 {
535 reader_.Schedule(128 /* empty header */ +
536 4 /* "DICM" magic value */ +
537 4 /* (0x0002, 0x0000) tag */ +
538 2 /* value representation of (0x0002, 0x0000) == "UL" */ +
539 2 /* length of "UL" value == 4 */ +
540 4 /* actual length of the meta-header */);
541 }
542
543
544 void DicomStreamReader::Consume(IVisitor& visitor,
545 const DicomTag& untilTag)
546 {
547 while (state_ != State_Done)
548 {
549 std::string block;
550 if (reader_.Read(block))
551 {
552 switch (state_)
553 {
554 case State_Preamble:
555 HandlePreamble(visitor, block);
556 break;
557
558 case State_MetaHeader:
559 HandleMetaHeader(visitor, block);
560 break;
561
562 case State_DatasetTag:
563 HandleDatasetTag(block, untilTag);
564 break;
565
566 case State_DatasetExplicitLength:
567 HandleDatasetExplicitLength(block);
568 break;
569
570 case State_SequenceExplicitLength:
571 HandleSequenceExplicitLength(block);
572 break;
573
574 case State_SequenceExplicitValue:
575 HandleSequenceExplicitValue();
576 break;
577
578 case State_DatasetValue:
579 HandleDatasetValue(visitor, block);
580 break;
581
582 default:
583 throw OrthancException(ErrorCode_InternalError);
584 }
585 }
586 else
587 {
588 return; // No more data in the stream
589 }
590 }
591 }
592
593
594 void DicomStreamReader::Consume(IVisitor& visitor)
595 {
596 DicomTag untilTag(0xffff, 0xffff);
597 Consume(visitor, untilTag);
598 }
599 }