Mercurial > hg > orthanc
comparison UnitTestsSources/FromDcmtkTests.cpp @ 3786:3801435e34a1 SylvainRouquette/fix-issue169-95b752c
integration Orthanc-1.6.0->SylvainRouquette
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 19 Mar 2020 11:48:30 +0100 |
parents | c6658187e4b1 |
children | 66e18aad0654 ab9a0d1e0cc1 |
comparison
equal
deleted
inserted
replaced
3785:763533d6dd67 | 3786:3801435e34a1 |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
4 * Department, University Hospital of Liege, Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium | 5 * Copyright (C) 2017-2020 Osimis S.A., Belgium |
6 * | 6 * |
7 * This program is free software: you can redistribute it and/or | 7 * This program is free software: you can redistribute it and/or |
8 * modify it under the terms of the GNU General Public License as | 8 * modify it under the terms of the GNU General Public License as |
9 * published by the Free Software Foundation, either version 3 of the | 9 * published by the Free Software Foundation, either version 3 of the |
10 * License, or (at your option) any later version. | 10 * License, or (at your option) any later version. |
32 | 32 |
33 | 33 |
34 #include "PrecompiledHeadersUnitTests.h" | 34 #include "PrecompiledHeadersUnitTests.h" |
35 #include "gtest/gtest.h" | 35 #include "gtest/gtest.h" |
36 | 36 |
37 #include "../Core/Compatibility.h" | |
37 #include "../Core/DicomNetworking/DicomFindAnswers.h" | 38 #include "../Core/DicomNetworking/DicomFindAnswers.h" |
38 #include "../Core/DicomParsing/DicomModification.h" | 39 #include "../Core/DicomParsing/DicomModification.h" |
39 #include "../Core/DicomParsing/DicomWebJsonVisitor.h" | 40 #include "../Core/DicomParsing/DicomWebJsonVisitor.h" |
40 #include "../Core/DicomParsing/FromDcmtkBridge.h" | 41 #include "../Core/DicomParsing/FromDcmtkBridge.h" |
41 #include "../Core/DicomParsing/Internals/DicomImageDecoder.h" | 42 #include "../Core/DicomParsing/Internals/DicomImageDecoder.h" |
93 | 94 |
94 for (int i = 0; i < 10; i++) | 95 for (int i = 0; i < 10; i++) |
95 { | 96 { |
96 char b[1024]; | 97 char b[1024]; |
97 sprintf(b, "UnitTestsResults/anon%06d.dcm", i); | 98 sprintf(b, "UnitTestsResults/anon%06d.dcm", i); |
98 std::auto_ptr<ParsedDicomFile> f(o.Clone(false)); | 99 std::unique_ptr<ParsedDicomFile> f(o.Clone(false)); |
99 if (i > 4) | 100 if (i > 4) |
100 o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou"); | 101 o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou"); |
101 m.Apply(*f); | 102 m.Apply(*f); |
102 f->SaveToFile(b); | 103 f->SaveToFile(b); |
103 } | 104 } |
106 | 107 |
107 TEST(DicomModification, Anonymization) | 108 TEST(DicomModification, Anonymization) |
108 { | 109 { |
109 ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName")); | 110 ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName")); |
110 | 111 |
111 const DicomTag privateTag(0x0045, 0x0010); | 112 const DicomTag privateTag(0x0045, 0x1010); |
112 const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020")); | 113 const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020")); |
113 ASSERT_TRUE(privateTag.IsPrivate()); | 114 ASSERT_TRUE(privateTag.IsPrivate()); |
114 ASSERT_TRUE(privateTag2.IsPrivate()); | 115 ASSERT_TRUE(privateTag2.IsPrivate()); |
115 ASSERT_EQ(0x0031, privateTag2.GetGroup()); | 116 ASSERT_EQ(0x0031, privateTag2.GetGroup()); |
116 ASSERT_EQ(0x1020, privateTag2.GetElement()); | 117 ASSERT_EQ(0x1020, privateTag2.GetElement()); |
117 | 118 |
118 std::string s; | 119 std::string s; |
119 ParsedDicomFile o(true); | 120 ParsedDicomFile o(true); |
120 o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou"); | 121 o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou"); |
121 ASSERT_FALSE(o.GetTagValue(s, privateTag)); | 122 ASSERT_FALSE(o.GetTagValue(s, privateTag)); |
122 o.Insert(privateTag, "private tag", false); | 123 o.Insert(privateTag, "private tag", false, "OrthancCreator"); |
123 ASSERT_TRUE(o.GetTagValue(s, privateTag)); | 124 ASSERT_TRUE(o.GetTagValue(s, privateTag)); |
124 ASSERT_STREQ("private tag", s.c_str()); | 125 ASSERT_STREQ("private tag", s.c_str()); |
125 | 126 |
126 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); | 127 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); |
127 ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent), OrthancException); | 128 ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent, "OrthancCreator"), OrthancException); |
128 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); | 129 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); |
129 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent); | 130 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent, "OrthancCreator"); |
130 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); | 131 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); |
131 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent); | 132 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator"); |
132 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); | 133 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); |
133 ASSERT_STREQ("hello", s.c_str()); | 134 ASSERT_STREQ("hello", s.c_str()); |
134 o.ReplacePlainString(privateTag2, "hello world"); | 135 o.Replace(privateTag2, std::string("hello world"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator"); |
135 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); | 136 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); |
136 ASSERT_STREQ("hello world", s.c_str()); | 137 ASSERT_STREQ("hello world", s.c_str()); |
137 | 138 |
138 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); | 139 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); |
139 ASSERT_FALSE(Toolbox::IsUuid(s)); | 140 ASSERT_FALSE(Toolbox::IsUuid(s)); |
288 { | 289 { |
289 ParsedDicomFile f(true); | 290 ParsedDicomFile f(true); |
290 f.SetEncoding(testEncodings[i]); | 291 f.SetEncoding(testEncodings[i]); |
291 | 292 |
292 std::string s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false); | 293 std::string s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false); |
293 f.Insert(DICOM_TAG_PATIENT_NAME, s, false); | 294 f.Insert(DICOM_TAG_PATIENT_NAME, s, false, ""); |
294 f.SaveToMemoryBuffer(dicom); | 295 f.SaveToMemoryBuffer(dicom); |
295 } | 296 } |
296 | 297 |
297 if (testEncodings[i] != Encoding_Windows1251) | 298 if (testEncodings[i] != Encoding_Windows1251) |
298 { | 299 { |
400 { | 401 { |
401 // Namespace for the "FRIEND_TEST()" directive in "FromDcmtkBridge" to apply: | 402 // Namespace for the "FRIEND_TEST()" directive in "FromDcmtkBridge" to apply: |
402 // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#private-class-members | 403 // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#private-class-members |
403 TEST(FromDcmtkBridge, FromJson) | 404 TEST(FromDcmtkBridge, FromJson) |
404 { | 405 { |
405 std::auto_ptr<DcmElement> element; | 406 std::unique_ptr<DcmElement> element; |
406 | 407 |
407 { | 408 { |
408 Json::Value a; | 409 Json::Value a; |
409 a = "Hello"; | 410 a = "Hello"; |
410 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8)); | 411 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8, "")); |
411 | 412 |
412 Json::Value b; | 413 Json::Value b; |
413 std::set<DicomTag> ignoreTagLength; | 414 std::set<DicomTag> ignoreTagLength; |
414 ignoreTagLength.insert(DICOM_TAG_PATIENT_ID); | 415 ignoreTagLength.insert(DICOM_TAG_PATIENT_ID); |
415 | 416 |
437 | 438 |
438 { | 439 { |
439 Json::Value a; | 440 Json::Value a; |
440 a = "Hello"; | 441 a = "Hello"; |
441 // Cannot assign a string to a sequence | 442 // Cannot assign a string to a sequence |
442 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, false, Encoding_Utf8)), OrthancException); | 443 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, false, Encoding_Utf8, "")), OrthancException); |
443 } | 444 } |
444 | 445 |
445 { | 446 { |
446 Json::Value a = Json::arrayValue; | 447 Json::Value a = Json::arrayValue; |
447 a.append("Hello"); | 448 a.append("Hello"); |
448 // Cannot assign an array to a string | 449 // Cannot assign an array to a string |
449 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8)), OrthancException); | 450 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8, "")), OrthancException); |
450 } | 451 } |
451 | 452 |
452 { | 453 { |
453 Json::Value a; | 454 Json::Value a; |
454 a = "data:application/octet-stream;base64,SGVsbG8="; // echo -n "Hello" | base64 | 455 a = "data:application/octet-stream;base64,SGVsbG8="; // echo -n "Hello" | base64 |
455 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8)); | 456 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8, "")); |
456 | 457 |
457 Json::Value b; | 458 Json::Value b; |
458 std::set<DicomTag> ignoreTagLength; | 459 std::set<DicomTag> ignoreTagLength; |
459 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short, | 460 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short, |
460 DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength); | 461 DicomToJsonFlags_Default, 0, Encoding_Ascii, false, ignoreTagLength); |
462 } | 463 } |
463 | 464 |
464 { | 465 { |
465 Json::Value a = Json::arrayValue; | 466 Json::Value a = Json::arrayValue; |
466 CreateSampleJson(a); | 467 CreateSampleJson(a); |
467 element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, true, Encoding_Utf8)); | 468 element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, true, Encoding_Utf8, "")); |
468 | 469 |
469 { | 470 { |
470 Json::Value b; | 471 Json::Value b; |
471 std::set<DicomTag> ignoreTagLength; | 472 std::set<DicomTag> ignoreTagLength; |
472 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short, | 473 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short, |
504 | 505 |
505 TEST(ParsedDicomFile, InsertReplaceStrings) | 506 TEST(ParsedDicomFile, InsertReplaceStrings) |
506 { | 507 { |
507 ParsedDicomFile f(true); | 508 ParsedDicomFile f(true); |
508 | 509 |
509 f.Insert(DICOM_TAG_PATIENT_NAME, "World", false); | 510 f.Insert(DICOM_TAG_PATIENT_NAME, "World", false, ""); |
510 ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false), OrthancException); // Already existing tag | 511 ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false, ""), OrthancException); // Already existing tag |
511 f.ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*) | 512 f.ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*) |
512 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**) | 513 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**) |
513 | 514 |
514 std::string s; | 515 std::string s; |
515 ASSERT_FALSE(f.LookupTransferSyntax(s)); | 516 ASSERT_FALSE(f.LookupTransferSyntax(s)); |
516 | 517 |
517 ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), | 518 ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), |
518 false, DicomReplaceMode_ThrowIfAbsent), OrthancException); | 519 false, DicomReplaceMode_ThrowIfAbsent, ""), OrthancException); |
519 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_IgnoreIfAbsent); | 520 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_IgnoreIfAbsent, ""); |
520 ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); | 521 ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); |
521 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_InsertIfAbsent); | 522 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_InsertIfAbsent, ""); |
522 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); | 523 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); |
523 ASSERT_EQ(s, "Accession"); | 524 ASSERT_EQ(s, "Accession"); |
524 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession2"), false, DicomReplaceMode_IgnoreIfAbsent); | 525 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession2"), false, DicomReplaceMode_IgnoreIfAbsent, ""); |
525 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); | 526 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); |
526 ASSERT_EQ(s, "Accession2"); | 527 ASSERT_EQ(s, "Accession2"); |
527 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession3"), false, DicomReplaceMode_ThrowIfAbsent); | 528 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession3"), false, DicomReplaceMode_ThrowIfAbsent, ""); |
528 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); | 529 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); |
529 ASSERT_EQ(s, "Accession3"); | 530 ASSERT_EQ(s, "Accession3"); |
530 | 531 |
531 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); | 532 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); |
532 ASSERT_EQ(s, "World"); | 533 ASSERT_EQ(s, "World"); |
550 Json::Value a; | 551 Json::Value a; |
551 CreateSampleJson(a); | 552 CreateSampleJson(a); |
552 | 553 |
553 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); | 554 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); |
554 f.Remove(REFERENCED_STUDY_SEQUENCE); // No effect | 555 f.Remove(REFERENCED_STUDY_SEQUENCE); // No effect |
555 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true); | 556 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, ""); |
556 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); | 557 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); |
557 ASSERT_THROW(f.Insert(REFERENCED_STUDY_SEQUENCE, a, true), OrthancException); | 558 ASSERT_THROW(f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, ""), OrthancException); |
558 f.Remove(REFERENCED_STUDY_SEQUENCE); | 559 f.Remove(REFERENCED_STUDY_SEQUENCE); |
559 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); | 560 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); |
560 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true); | 561 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, ""); |
561 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); | 562 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); |
562 | 563 |
563 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); | 564 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); |
564 ASSERT_THROW(f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_ThrowIfAbsent), OrthancException); | 565 ASSERT_THROW(f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_ThrowIfAbsent, ""), OrthancException); |
565 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); | 566 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); |
566 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_IgnoreIfAbsent); | 567 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_IgnoreIfAbsent, ""); |
567 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); | 568 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); |
568 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_InsertIfAbsent); | 569 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_InsertIfAbsent, ""); |
569 ASSERT_TRUE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); | 570 ASSERT_TRUE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); |
570 | 571 |
571 { | 572 { |
572 Json::Value b; | 573 Json::Value b; |
573 f.DatasetToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0); | 574 f.DatasetToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0); |
578 ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a)); | 579 ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a)); |
579 ASSERT_NE(0, c["ReferencedStudySequence"].compare(a)); // Because Data URI Scheme decoding was enabled | 580 ASSERT_NE(0, c["ReferencedStudySequence"].compare(a)); // Because Data URI Scheme decoding was enabled |
580 } | 581 } |
581 | 582 |
582 a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64 | 583 a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64 |
583 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false, DicomReplaceMode_InsertIfAbsent); // (*) | 584 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false, DicomReplaceMode_InsertIfAbsent, ""); // (*) |
584 f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true, DicomReplaceMode_InsertIfAbsent); // (**) | 585 f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true, DicomReplaceMode_InsertIfAbsent, ""); // (**) |
585 | 586 |
586 std::string s; | 587 std::string s; |
587 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID)); | 588 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID)); |
588 ASSERT_EQ(s, a.asString()); | 589 ASSERT_EQ(s, a.asString()); |
589 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*) | 590 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*) |
612 ASSERT_EQ(testEncodings[i], f.DetectEncoding(hasCodeExtensions)); | 613 ASSERT_EQ(testEncodings[i], f.DetectEncoding(hasCodeExtensions)); |
613 ASSERT_FALSE(hasCodeExtensions); | 614 ASSERT_FALSE(hasCodeExtensions); |
614 } | 615 } |
615 | 616 |
616 Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false); | 617 Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false); |
617 f.Replace(DICOM_TAG_PATIENT_NAME, s, false, DicomReplaceMode_InsertIfAbsent); | 618 f.Replace(DICOM_TAG_PATIENT_NAME, s, false, DicomReplaceMode_InsertIfAbsent, ""); |
618 | 619 |
619 Json::Value v; | 620 Json::Value v; |
620 f.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0); | 621 f.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0); |
621 ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i])); | 622 ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i])); |
622 } | 623 } |
624 } | 625 } |
625 | 626 |
626 | 627 |
627 TEST(ParsedDicomFile, ToJsonFlags1) | 628 TEST(ParsedDicomFile, ToJsonFlags1) |
628 { | 629 { |
629 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_PersonName, "MyPrivateTag", 1, 1, ""); | 630 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1, "OrthancCreator"); |
630 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1, ""); | 631 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1, ""); |
631 | 632 |
632 ParsedDicomFile f(true); | 633 ParsedDicomFile f(true); |
633 f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false); // Even group => public tag | 634 f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false, ""); // Even group => public tag |
634 f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false); // Even group => public, unknown tag | 635 f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false, ""); // Even group => public, unknown tag |
635 f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false); // Odd group => private tag | 636 f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false, "OrthancCreator"); // Odd group => private tag |
636 | 637 |
637 Json::Value v; | 638 Json::Value v; |
638 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); | 639 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); |
639 ASSERT_EQ(Json::objectValue, v.type()); | 640 ASSERT_EQ(Json::objectValue, v.type()); |
640 ASSERT_EQ(6u, v.getMemberNames().size()); | 641 ASSERT_EQ(6u, v.getMemberNames().size()); |
642 ASSERT_FALSE(v.isMember("7053,1000")); | 643 ASSERT_FALSE(v.isMember("7053,1000")); |
643 ASSERT_TRUE(v.isMember("7050,1000")); | 644 ASSERT_TRUE(v.isMember("7050,1000")); |
644 ASSERT_EQ(Json::stringValue, v["7050,1000"].type()); | 645 ASSERT_EQ(Json::stringValue, v["7050,1000"].type()); |
645 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); | 646 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); |
646 | 647 |
647 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0); | 648 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0); |
648 ASSERT_EQ(Json::objectValue, v.type()); | 649 ASSERT_EQ(Json::objectValue, v.type()); |
649 ASSERT_EQ(7u, v.getMemberNames().size()); | 650 ASSERT_EQ(7u, v.getMemberNames().size()); |
650 ASSERT_FALSE(v.isMember("7052,1000")); | 651 ASSERT_FALSE(v.isMember("7052,1000")); |
651 ASSERT_TRUE(v.isMember("7050,1000")); | 652 ASSERT_TRUE(v.isMember("7050,1000")); |
652 ASSERT_TRUE(v.isMember("7053,1000")); | 653 ASSERT_TRUE(v.isMember("7053,1000")); |
653 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); | 654 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); |
654 ASSERT_EQ(Json::nullValue, v["7053,1000"].type()); | 655 ASSERT_EQ(Json::nullValue, v["7053,1000"].type()); |
655 | 656 |
656 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0); | 657 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags), 0); |
658 ASSERT_EQ(Json::objectValue, v.type()); | |
659 ASSERT_EQ(6u, v.getMemberNames().size()); | |
660 ASSERT_FALSE(v.isMember("7052,1000")); | |
661 ASSERT_TRUE(v.isMember("7050,1000")); | |
662 ASSERT_FALSE(v.isMember("7053,1000")); | |
663 | |
664 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary), 0); | |
657 ASSERT_EQ(Json::objectValue, v.type()); | 665 ASSERT_EQ(Json::objectValue, v.type()); |
658 ASSERT_EQ(7u, v.getMemberNames().size()); | 666 ASSERT_EQ(7u, v.getMemberNames().size()); |
659 ASSERT_FALSE(v.isMember("7052,1000")); | 667 ASSERT_FALSE(v.isMember("7052,1000")); |
660 ASSERT_TRUE(v.isMember("7050,1000")); | 668 ASSERT_TRUE(v.isMember("7050,1000")); |
661 ASSERT_TRUE(v.isMember("7053,1000")); | 669 ASSERT_TRUE(v.isMember("7053,1000")); |
664 ASSERT_EQ(Json::stringValue, v["7053,1000"].type()); | 672 ASSERT_EQ(Json::stringValue, v["7053,1000"].type()); |
665 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString())); | 673 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString())); |
666 ASSERT_EQ("application/octet-stream", mime); | 674 ASSERT_EQ("application/octet-stream", mime); |
667 ASSERT_EQ("Some private tag", content); | 675 ASSERT_EQ("Some private tag", content); |
668 | 676 |
669 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_ConvertBinaryToNull), 0); | 677 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0); |
670 ASSERT_EQ(Json::objectValue, v.type()); | 678 ASSERT_EQ(Json::objectValue, v.type()); |
671 ASSERT_EQ(7u, v.getMemberNames().size()); | 679 ASSERT_EQ(7u, v.getMemberNames().size()); |
672 ASSERT_TRUE(v.isMember("7050,1000")); | 680 ASSERT_TRUE(v.isMember("7050,1000")); |
673 ASSERT_TRUE(v.isMember("7052,1000")); | 681 ASSERT_TRUE(v.isMember("7052,1000")); |
674 ASSERT_FALSE(v.isMember("7053,1000")); | 682 ASSERT_FALSE(v.isMember("7053,1000")); |
675 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); | 683 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); |
676 ASSERT_EQ(Json::nullValue, v["7052,1000"].type()); | 684 ASSERT_EQ(Json::nullValue, v["7052,1000"].type()); |
677 | 685 |
678 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags), 0); | 686 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludeBinary), 0); |
679 ASSERT_EQ(Json::objectValue, v.type()); | 687 ASSERT_EQ(Json::objectValue, v.type()); |
680 ASSERT_EQ(7u, v.getMemberNames().size()); | 688 ASSERT_EQ(7u, v.getMemberNames().size()); |
681 ASSERT_TRUE(v.isMember("7050,1000")); | 689 ASSERT_TRUE(v.isMember("7050,1000")); |
682 ASSERT_TRUE(v.isMember("7052,1000")); | 690 ASSERT_TRUE(v.isMember("7052,1000")); |
683 ASSERT_FALSE(v.isMember("7053,1000")); | 691 ASSERT_FALSE(v.isMember("7053,1000")); |
685 ASSERT_EQ(Json::stringValue, v["7052,1000"].type()); | 693 ASSERT_EQ(Json::stringValue, v["7052,1000"].type()); |
686 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString())); | 694 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString())); |
687 ASSERT_EQ("application/octet-stream", mime); | 695 ASSERT_EQ("application/octet-stream", mime); |
688 ASSERT_EQ("Some unknown tag", content); | 696 ASSERT_EQ("Some unknown tag", content); |
689 | 697 |
690 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0); | 698 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0); |
691 ASSERT_EQ(Json::objectValue, v.type()); | 699 ASSERT_EQ(Json::objectValue, v.type()); |
692 ASSERT_EQ(8u, v.getMemberNames().size()); | 700 ASSERT_EQ(8u, v.getMemberNames().size()); |
693 ASSERT_TRUE(v.isMember("7050,1000")); | 701 ASSERT_TRUE(v.isMember("7050,1000")); |
694 ASSERT_TRUE(v.isMember("7052,1000")); | 702 ASSERT_TRUE(v.isMember("7052,1000")); |
695 ASSERT_TRUE(v.isMember("7053,1000")); | 703 ASSERT_TRUE(v.isMember("7053,1000")); |
700 | 708 |
701 | 709 |
702 TEST(ParsedDicomFile, ToJsonFlags2) | 710 TEST(ParsedDicomFile, ToJsonFlags2) |
703 { | 711 { |
704 ParsedDicomFile f(true); | 712 ParsedDicomFile f(true); |
705 f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false); | 713 f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false, ""); |
706 | 714 |
707 Json::Value v; | 715 Json::Value v; |
708 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); | 716 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); |
709 ASSERT_EQ(Json::objectValue, v.type()); | 717 ASSERT_EQ(Json::objectValue, v.type()); |
710 ASSERT_EQ(5u, v.getMemberNames().size()); | 718 ASSERT_EQ(5u, v.getMemberNames().size()); |
808 DicomToJsonFlags_IncludeUnknownTags | | 816 DicomToJsonFlags_IncludeUnknownTags | |
809 DicomToJsonFlags_ConvertBinaryToAscii); | 817 DicomToJsonFlags_ConvertBinaryToAscii); |
810 | 818 |
811 | 819 |
812 { | 820 { |
813 std::auto_ptr<ParsedDicomFile> dicom | 821 std::unique_ptr<ParsedDicomFile> dicom |
814 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers))); | 822 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers), "")); |
815 | 823 |
816 Json::Value vv; | 824 Json::Value vv; |
817 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, toJsonFlags, 0); | 825 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, toJsonFlags, 0); |
818 | 826 |
819 ASSERT_EQ(vv["SOPClassUID"].asString(), sopClassUid); | 827 ASSERT_EQ(vv["SOPClassUID"].asString(), sopClassUid); |
824 ASSERT_TRUE(vv.isMember("PatientID")); | 832 ASSERT_TRUE(vv.isMember("PatientID")); |
825 } | 833 } |
826 | 834 |
827 | 835 |
828 { | 836 { |
829 std::auto_ptr<ParsedDicomFile> dicom | 837 std::unique_ptr<ParsedDicomFile> dicom |
830 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers))); | 838 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers), "")); |
831 | 839 |
832 Json::Value vv; | 840 Json::Value vv; |
833 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0); | 841 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0); |
834 | 842 |
835 std::string mime, content; | 843 std::string mime, content; |
838 ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size()); | 846 ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size()); |
839 } | 847 } |
840 | 848 |
841 | 849 |
842 { | 850 { |
843 std::auto_ptr<ParsedDicomFile> dicom | 851 std::unique_ptr<ParsedDicomFile> dicom |
844 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_DecodeDataUriScheme))); | 852 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_DecodeDataUriScheme), "")); |
845 | 853 |
846 Json::Value vv; | 854 Json::Value vv; |
847 dicom->DatasetToJson(vv, DicomToJsonFormat_Short, toJsonFlags, 0); | 855 dicom->DatasetToJson(vv, DicomToJsonFormat_Short, toJsonFlags, 0); |
848 | 856 |
849 ASSERT_FALSE(vv.isMember("SOPInstanceUID")); | 857 ASSERT_FALSE(vv.isMember("SOPInstanceUID")); |
907 { | 915 { |
908 std::string s; | 916 std::string s; |
909 Orthanc::SystemToolbox::ReadFile(s, PATH); | 917 Orthanc::SystemToolbox::ReadFile(s, PATH); |
910 Orthanc::ParsedDicomFile f(s); | 918 Orthanc::ParsedDicomFile f(s); |
911 | 919 |
912 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); | 920 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); |
913 ASSERT_EQ(256u, decoded->GetWidth()); | 921 ASSERT_EQ(256u, decoded->GetWidth()); |
914 ASSERT_EQ(256u, decoded->GetHeight()); | 922 ASSERT_EQ(256u, decoded->GetHeight()); |
915 ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat()); | 923 ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat()); |
916 | 924 |
917 for (int y = 0; y < 256; y++) | 925 for (int y = 0; y < 256; y++) |
969 { | 977 { |
970 std::string s; | 978 std::string s; |
971 Orthanc::SystemToolbox::ReadFile(s, PATH); | 979 Orthanc::SystemToolbox::ReadFile(s, PATH); |
972 Orthanc::ParsedDicomFile f(s); | 980 Orthanc::ParsedDicomFile f(s); |
973 | 981 |
974 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); | 982 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); |
975 ASSERT_EQ(384u, decoded->GetWidth()); | 983 ASSERT_EQ(384u, decoded->GetWidth()); |
976 ASSERT_EQ(256u, decoded->GetHeight()); | 984 ASSERT_EQ(256u, decoded->GetHeight()); |
977 ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat()); | 985 ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat()); |
978 | 986 |
979 for (int y = 0; y < 256; y++) | 987 for (int y = 0; y < 256; y++) |
1026 { | 1034 { |
1027 std::string s; | 1035 std::string s; |
1028 Orthanc::SystemToolbox::ReadFile(s, PATH); | 1036 Orthanc::SystemToolbox::ReadFile(s, PATH); |
1029 Orthanc::ParsedDicomFile f(s); | 1037 Orthanc::ParsedDicomFile f(s); |
1030 | 1038 |
1031 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); | 1039 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); |
1032 ASSERT_EQ(256u, decoded->GetWidth()); | 1040 ASSERT_EQ(256u, decoded->GetWidth()); |
1033 ASSERT_EQ(256u, decoded->GetHeight()); | 1041 ASSERT_EQ(256u, decoded->GetHeight()); |
1034 ASSERT_EQ(Orthanc::PixelFormat_Grayscale16, decoded->GetFormat()); | 1042 ASSERT_EQ(Orthanc::PixelFormat_Grayscale16, decoded->GetFormat()); |
1035 | 1043 |
1036 for (int y = 0; y < 256; y++) | 1044 for (int y = 0; y < 256; y++) |
1082 { | 1090 { |
1083 std::string s; | 1091 std::string s; |
1084 Orthanc::SystemToolbox::ReadFile(s, PATH); | 1092 Orthanc::SystemToolbox::ReadFile(s, PATH); |
1085 Orthanc::ParsedDicomFile f(s); | 1093 Orthanc::ParsedDicomFile f(s); |
1086 | 1094 |
1087 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); | 1095 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0)); |
1088 ASSERT_EQ(256u, decoded->GetWidth()); | 1096 ASSERT_EQ(256u, decoded->GetWidth()); |
1089 ASSERT_EQ(256u, decoded->GetHeight()); | 1097 ASSERT_EQ(256u, decoded->GetHeight()); |
1090 ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat()); | 1098 ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat()); |
1091 | 1099 |
1092 for (int y = 0; y < 256; y++) | 1100 for (int y = 0; y < 256; y++) |
1902 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line2), sizeof(line2)), lines[1]); | 1910 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line2), sizeof(line2)), lines[1]); |
1903 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line3), sizeof(line3)), lines[2]); | 1911 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line3), sizeof(line3)), lines[2]); |
1904 ASSERT_TRUE(lines[3].empty()); | 1912 ASSERT_TRUE(lines[3].empty()); |
1905 } | 1913 } |
1906 | 1914 |
1915 | |
1916 | |
1917 | |
1918 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 | |
1919 | |
1920 #include "../Core/DicomParsing/Internals/DicomFrameIndex.h" | |
1921 | |
1922 #include <dcmtk/dcmdata/dcostrmb.h> | |
1923 #include <dcmtk/dcmdata/dcpixel.h> | |
1924 #include <dcmtk/dcmdata/dcpxitem.h> | |
1925 | |
1926 | |
1927 namespace Orthanc | |
1928 { | |
1929 class IDicomTranscoder : public boost::noncopyable | |
1930 { | |
1931 public: | |
1932 virtual ~IDicomTranscoder() | |
1933 { | |
1934 } | |
1935 | |
1936 virtual DicomTransferSyntax GetTransferSyntax() = 0; | |
1937 | |
1938 virtual std::string GetSopClassUid() = 0; | |
1939 | |
1940 virtual std::string GetSopInstanceUid() = 0; | |
1941 | |
1942 virtual unsigned int GetFramesCount() = 0; | |
1943 | |
1944 virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0; | |
1945 | |
1946 virtual void GetCompressedFrame(std::string& target, | |
1947 unsigned int frame) = 0; | |
1948 | |
1949 virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes, | |
1950 bool allowNewSopInstanceUid) = 0; | |
1951 }; | |
1952 | |
1953 | |
1954 class DcmtkTranscoder : public IDicomTranscoder | |
1955 { | |
1956 private: | |
1957 std::unique_ptr<DcmFileFormat> dicom_; | |
1958 std::unique_ptr<DicomFrameIndex> index_; | |
1959 DicomTransferSyntax transferSyntax_; | |
1960 std::string sopClassUid_; | |
1961 std::string sopInstanceUid_; | |
1962 | |
1963 void Setup(DcmFileFormat* dicom) | |
1964 { | |
1965 dicom_.reset(dicom); | |
1966 | |
1967 if (dicom == NULL || | |
1968 dicom_->getDataset() == NULL) | |
1969 { | |
1970 throw OrthancException(ErrorCode_NullPointer); | |
1971 } | |
1972 | |
1973 DcmDataset& dataset = *dicom_->getDataset(); | |
1974 index_.reset(new DicomFrameIndex(dataset)); | |
1975 | |
1976 E_TransferSyntax xfer = dataset.getOriginalXfer(); | |
1977 if (xfer == EXS_Unknown) | |
1978 { | |
1979 dataset.updateOriginalXfer(); | |
1980 xfer = dataset.getOriginalXfer(); | |
1981 if (xfer == EXS_Unknown) | |
1982 { | |
1983 throw OrthancException(ErrorCode_BadFileFormat, | |
1984 "Cannot determine the transfer syntax of the DICOM instance"); | |
1985 } | |
1986 } | |
1987 | |
1988 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer)) | |
1989 { | |
1990 throw OrthancException( | |
1991 ErrorCode_BadFileFormat, | |
1992 "Unsupported transfer syntax: " + boost::lexical_cast<std::string>(xfer)); | |
1993 } | |
1994 | |
1995 const char* a = NULL; | |
1996 const char* b = NULL; | |
1997 | |
1998 if (!dataset.findAndGetString(DCM_SOPClassUID, a).good() || | |
1999 !dataset.findAndGetString(DCM_SOPInstanceUID, b).good() || | |
2000 a == NULL || | |
2001 b == NULL) | |
2002 { | |
2003 throw OrthancException(ErrorCode_BadFileFormat, | |
2004 "Missing SOP class/instance UID in DICOM instance"); | |
2005 } | |
2006 | |
2007 sopClassUid_.assign(a); | |
2008 sopInstanceUid_.assign(b); | |
2009 } | |
2010 | |
2011 public: | |
2012 DcmtkTranscoder(DcmFileFormat* dicom) // Takes ownership | |
2013 { | |
2014 Setup(dicom); | |
2015 } | |
2016 | |
2017 DcmtkTranscoder(const void* dicom, | |
2018 size_t size) | |
2019 { | |
2020 Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size)); | |
2021 } | |
2022 | |
2023 virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE | |
2024 { | |
2025 return transferSyntax_; | |
2026 } | |
2027 | |
2028 virtual std::string GetSopClassUid() ORTHANC_OVERRIDE | |
2029 { | |
2030 return sopClassUid_; | |
2031 } | |
2032 | |
2033 virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE | |
2034 { | |
2035 return sopInstanceUid_; | |
2036 } | |
2037 | |
2038 virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE | |
2039 { | |
2040 return index_->GetFramesCount(); | |
2041 } | |
2042 | |
2043 virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE | |
2044 { | |
2045 assert(dicom_->getDataset() != NULL); | |
2046 return DicomImageDecoder::Decode(*dicom_->getDataset(), frame); | |
2047 } | |
2048 | |
2049 virtual void GetCompressedFrame(std::string& target, | |
2050 unsigned int frame) ORTHANC_OVERRIDE | |
2051 { | |
2052 #if 1 | |
2053 index_->GetRawFrame(target, frame); | |
2054 printf("%d: %d\n", frame, target.size()); | |
2055 #endif | |
2056 | |
2057 #if 1 | |
2058 assert(dicom_->getDataset() != NULL); | |
2059 DcmDataset& dataset = *dicom_->getDataset(); | |
2060 | |
2061 DcmPixelSequence* pixelSequence = FromDcmtkBridge::GetPixelSequence(dataset); | |
2062 | |
2063 if (pixelSequence != NULL && | |
2064 frame == 0 && | |
2065 pixelSequence->card() != GetFramesCount() + 1) | |
2066 { | |
2067 printf("COMPRESSED\n"); | |
2068 | |
2069 // Check out "djcodecd.cc" | |
2070 | |
2071 printf("%d fragments\n", pixelSequence->card()); | |
2072 | |
2073 // Skip the first fragment, that is the offset table | |
2074 for (unsigned long i = 1; ;i++) | |
2075 { | |
2076 DcmPixelItem *fragment = NULL; | |
2077 if (pixelSequence->getItem(fragment, i).good()) | |
2078 { | |
2079 printf("fragment %d %d\n", i, fragment->getLength()); | |
2080 } | |
2081 else | |
2082 { | |
2083 break; | |
2084 } | |
2085 } | |
2086 } | |
2087 #endif | |
2088 } | |
2089 | |
2090 virtual IDicomTranscoder* Transcode(std::set<DicomTransferSyntax> syntaxes, | |
2091 bool allowNewSopInstanceUid) ORTHANC_OVERRIDE | |
2092 { | |
2093 throw OrthancException(ErrorCode_NotImplemented); | |
2094 } | |
2095 }; | |
2096 } | |
2097 | |
2098 | |
2099 | |
2100 | |
2101 static bool Transcode(std::string& buffer, | |
2102 DcmDataset& dataSet, | |
2103 E_TransferSyntax xfer) | |
2104 { | |
2105 // Determine the transfer syntax which shall be used to write the | |
2106 // information to the file. We always switch to the Little Endian | |
2107 // syntax, with explicit length. | |
2108 | |
2109 // http://support.dcmtk.org/docs/dcxfer_8h-source.html | |
2110 | |
2111 | |
2112 /** | |
2113 * Note that up to Orthanc 0.7.1 (inclusive), the | |
2114 * "EXS_LittleEndianExplicit" was always used to save the DICOM | |
2115 * dataset into memory. We now keep the original transfer syntax | |
2116 * (if available). | |
2117 **/ | |
2118 //E_TransferSyntax xfer = dataSet.getOriginalXfer(); | |
2119 if (xfer == EXS_Unknown) | |
2120 { | |
2121 // No information about the original transfer syntax: This is | |
2122 // most probably a DICOM dataset that was read from memory. | |
2123 xfer = EXS_LittleEndianExplicit; | |
2124 } | |
2125 | |
2126 E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength; | |
2127 | |
2128 // Create the meta-header information | |
2129 DcmFileFormat ff(&dataSet); | |
2130 ff.validateMetaInfo(xfer); | |
2131 ff.removeInvalidGroups(); | |
2132 | |
2133 // Create a memory buffer with the proper size | |
2134 { | |
2135 const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType); // (*) | |
2136 buffer.resize(estimatedSize); | |
2137 } | |
2138 | |
2139 DcmOutputBufferStream ob(&buffer[0], buffer.size()); | |
2140 | |
2141 // Fill the memory buffer with the meta-header and the dataset | |
2142 ff.transferInit(); | |
2143 OFCondition c = ff.write(ob, xfer, encodingType, NULL, | |
2144 /*opt_groupLength*/ EGL_recalcGL, | |
2145 /*opt_paddingType*/ EPD_withoutPadding); | |
2146 ff.transferEnd(); | |
2147 | |
2148 if (c.good()) | |
2149 { | |
2150 // The DICOM file is successfully written, truncate the target | |
2151 // buffer if its size was overestimated by (*) | |
2152 ob.flush(); | |
2153 | |
2154 size_t effectiveSize = static_cast<size_t>(ob.tell()); | |
2155 if (effectiveSize < buffer.size()) | |
2156 { | |
2157 buffer.resize(effectiveSize); | |
2158 } | |
2159 | |
2160 return true; | |
2161 } | |
2162 else | |
2163 { | |
2164 // Error | |
2165 buffer.clear(); | |
2166 return false; | |
2167 } | |
2168 } | |
2169 | |
2170 #include "dcmtk/dcmjpeg/djrploss.h" /* for DJ_RPLossy */ | |
2171 #include "dcmtk/dcmjpeg/djrplol.h" /* for DJ_RPLossless */ | |
2172 | |
2173 #include <boost/filesystem.hpp> | |
2174 | |
2175 | |
2176 static void TestFile(const std::string& path) | |
2177 { | |
2178 printf("** %s\n", path.c_str()); | |
2179 | |
2180 std::string s; | |
2181 SystemToolbox::ReadFile(s, path); | |
2182 | |
2183 Orthanc::DcmtkTranscoder transcoder(s.c_str(), s.size()); | |
2184 | |
2185 printf("[%s] [%s] [%s] %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()), | |
2186 transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(), | |
2187 transcoder.GetFramesCount()); | |
2188 | |
2189 for (size_t i = 0; i < transcoder.GetFramesCount(); i++) | |
2190 { | |
2191 std::string f; | |
2192 transcoder.GetCompressedFrame(f, i); | |
2193 | |
2194 if (i == 0) | |
2195 { | |
2196 static unsigned int i = 0; | |
2197 char buf[1024]; | |
2198 sprintf(buf, "/tmp/frame-%06d.dcm", i++); | |
2199 printf(">> %s\n", buf); | |
2200 Orthanc::SystemToolbox::WriteFile(f, buf); | |
2201 } | |
2202 } | |
2203 | |
2204 printf("\n"); | |
2205 } | |
2206 | |
2207 TEST(Toto, Transcode) | |
2208 { | |
2209 if (0) | |
2210 { | |
2211 OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); | |
2212 | |
2213 std::string s; | |
2214 //SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.4.50.dcm"); | |
2215 //SystemToolbox::ReadFile(s, "/home/jodogne/DICOM/Alain.dcm"); | |
2216 SystemToolbox::ReadFile(s, "/home/jodogne/Subversion/orthanc-tests/Database/Brainix/Epi/IM-0001-0002.dcm"); | |
2217 | |
2218 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(s.c_str(), s.size())); | |
2219 | |
2220 // less /home/jodogne/Downloads/dcmtk-3.6.4/dcmdata/include/dcmtk/dcmdata/dcxfer.h | |
2221 printf(">> %d\n", dicom->getDataset()->getOriginalXfer()); // => 4 == EXS_JPEGProcess1 | |
2222 | |
2223 const DcmRepresentationParameter *p; | |
2224 | |
2225 #if 0 | |
2226 E_TransferSyntax target = EXS_LittleEndianExplicit; | |
2227 p = NULL; | |
2228 #elif 1 | |
2229 E_TransferSyntax target = EXS_JPEGProcess14SV1; | |
2230 DJ_RPLossless rp_lossless(6, 0); | |
2231 p = &rp_lossless; | |
2232 #else | |
2233 E_TransferSyntax target = EXS_JPEGProcess1; | |
2234 DJ_RPLossy rp_lossy(90); // quality | |
2235 p = &rp_lossy; | |
2236 #endif | |
2237 | |
2238 ASSERT_TRUE(dicom->getDataset()->chooseRepresentation(target, p).good()); | |
2239 ASSERT_TRUE(dicom->getDataset()->canWriteXfer(target)); | |
2240 | |
2241 std::string t; | |
2242 ASSERT_TRUE(Transcode(t, *dicom->getDataset(), target)); | |
2243 | |
2244 SystemToolbox::WriteFile(s, "source.dcm"); | |
2245 SystemToolbox::WriteFile(t, "target.dcm"); | |
2246 } | |
2247 | |
2248 if (1) | |
2249 { | |
2250 const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes"; | |
2251 | |
2252 for (boost::filesystem::directory_iterator it(PATH); | |
2253 it != boost::filesystem::directory_iterator(); ++it) | |
2254 { | |
2255 if (boost::filesystem::is_regular_file(it->status())) | |
2256 { | |
2257 TestFile(it->path().string()); | |
2258 } | |
2259 } | |
2260 | |
2261 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm"); | |
2262 TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm"); | |
2263 } | |
2264 } | |
2265 | |
2266 #endif |