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