comparison OrthancFramework/UnitTestsSources/FromDcmtkTests.cpp @ 4055:9214e3a7b0a2 framework

moving FromDcmtkTests.cpp from OrthancServer to OrthancFramework
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 11 Jun 2020 12:52:09 +0200
parents OrthancServer/UnitTestsSources/FromDcmtkTests.cpp@05b8fd21089c
children 0953b3dc3261
comparison
equal deleted inserted replaced
4054:9c37896a4457 4055:9214e3a7b0a2
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 General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #if ORTHANC_UNIT_TESTS_LINK_FRAMEWORK == 1
35 # include <OrthancFramework.h>
36 #endif
37
38 #include "gtest/gtest.h"
39
40 #include "../Sources/Compatibility.h"
41 #include "../Sources/DicomNetworking/DicomFindAnswers.h"
42 #include "../Sources/DicomParsing/DicomModification.h"
43 #include "../Sources/DicomParsing/DicomWebJsonVisitor.h"
44 #include "../Sources/DicomParsing/FromDcmtkBridge.h"
45 #include "../Sources/DicomParsing/Internals/DicomImageDecoder.h"
46 #include "../Sources/DicomParsing/ToDcmtkBridge.h"
47 #include "../Sources/Endianness.h"
48 #include "../Sources/Images/Image.h"
49 #include "../Sources/Images/ImageBuffer.h"
50 #include "../Sources/Images/ImageProcessing.h"
51 #include "../Sources/Images/PngReader.h"
52 #include "../Sources/Images/PngWriter.h"
53 #include "../Sources/OrthancException.h"
54 #include "../Sources/SystemToolbox.h"
55 #include "../Resources/CodeGeneration/EncodingTests.h"
56
57 #include <dcmtk/dcmdata/dcelem.h>
58 #include <dcmtk/dcmdata/dcdeftag.h>
59 #include <boost/algorithm/string/predicate.hpp>
60
61 #if ORTHANC_ENABLE_PUGIXML == 1
62 # include <pugixml.hpp>
63 #endif
64
65 using namespace Orthanc;
66
67 TEST(DicomFormat, Tag)
68 {
69 ASSERT_EQ("PatientName", FromDcmtkBridge::GetTagName(DicomTag(0x0010, 0x0010), ""));
70
71 DicomTag t = FromDcmtkBridge::ParseTag("SeriesDescription");
72 ASSERT_EQ(0x0008, t.GetGroup());
73 ASSERT_EQ(0x103E, t.GetElement());
74
75 t = FromDcmtkBridge::ParseTag("0020-e040");
76 ASSERT_EQ(0x0020, t.GetGroup());
77 ASSERT_EQ(0xe040, t.GetElement());
78
79 // Test ==() and !=() operators
80 ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020));
81 ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020));
82 }
83
84
85 TEST(DicomModification, Basic)
86 {
87 DicomModification m;
88 m.SetupAnonymization(DicomVersion_2008);
89 //m.SetLevel(DicomRootLevel_Study);
90 //m.ReplacePlainString(DICOM_TAG_PATIENT_ID, "coucou");
91 //m.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
92
93 ParsedDicomFile o(true);
94 o.SaveToFile("UnitTestsResults/anon.dcm");
95
96 for (int i = 0; i < 10; i++)
97 {
98 char b[1024];
99 sprintf(b, "UnitTestsResults/anon%06d.dcm", i);
100 std::unique_ptr<ParsedDicomFile> f(o.Clone(false));
101 if (i > 4)
102 o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou");
103 m.Apply(*f);
104 f->SaveToFile(b);
105 }
106 }
107
108
109 TEST(DicomModification, Anonymization)
110 {
111 ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName"));
112
113 const DicomTag privateTag(0x0045, 0x1010);
114 const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020"));
115 ASSERT_TRUE(privateTag.IsPrivate());
116 ASSERT_TRUE(privateTag2.IsPrivate());
117 ASSERT_EQ(0x0031, privateTag2.GetGroup());
118 ASSERT_EQ(0x1020, privateTag2.GetElement());
119
120 std::string s;
121 ParsedDicomFile o(true);
122 o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
123 ASSERT_FALSE(o.GetTagValue(s, privateTag));
124 o.Insert(privateTag, "private tag", false, "OrthancCreator");
125 ASSERT_TRUE(o.GetTagValue(s, privateTag));
126 ASSERT_STREQ("private tag", s.c_str());
127
128 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
129 ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent, "OrthancCreator"), OrthancException);
130 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
131 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent, "OrthancCreator");
132 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
133 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
134 ASSERT_TRUE(o.GetTagValue(s, privateTag2));
135 ASSERT_STREQ("hello", s.c_str());
136 o.Replace(privateTag2, std::string("hello world"), false, DicomReplaceMode_InsertIfAbsent, "OrthancCreator");
137 ASSERT_TRUE(o.GetTagValue(s, privateTag2));
138 ASSERT_STREQ("hello world", s.c_str());
139
140 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
141 ASSERT_FALSE(Toolbox::IsUuid(s));
142
143 DicomModification m;
144 m.SetupAnonymization(DicomVersion_2008);
145 m.Keep(privateTag);
146
147 m.Apply(o);
148
149 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
150 ASSERT_TRUE(Toolbox::IsUuid(s));
151 ASSERT_TRUE(o.GetTagValue(s, privateTag));
152 ASSERT_STREQ("private tag", s.c_str());
153
154 m.SetupAnonymization(DicomVersion_2008);
155 m.Apply(o);
156 ASSERT_FALSE(o.GetTagValue(s, privateTag));
157 }
158
159
160 #include <dcmtk/dcmdata/dcuid.h>
161
162 TEST(DicomModification, Png)
163 {
164 // Red dot in http://en.wikipedia.org/wiki/Data_URI_scheme (RGBA image)
165 std::string s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
166
167 std::string m, cc;
168 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(m, cc, s));
169
170 ASSERT_EQ("image/png", m);
171
172 PngReader reader;
173 reader.ReadFromMemory(cc);
174
175 ASSERT_EQ(5u, reader.GetHeight());
176 ASSERT_EQ(5u, reader.GetWidth());
177 ASSERT_EQ(PixelFormat_RGBA32, reader.GetFormat());
178
179 ParsedDicomFile o(true);
180 o.EmbedContent(s);
181 o.SaveToFile("UnitTestsResults/png1.dcm");
182
183 // Red dot, without alpha channel
184 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDTcIn2+8BgAAACJJREFUCNdj/P//PwMjIwME/P/P+J8BBTAxEOL/R9Lx/z8AynoKAXOeiV8AAAAASUVORK5CYII=";
185 o.EmbedContent(s);
186 o.SaveToFile("UnitTestsResults/png2.dcm");
187
188 // Check box in Graylevel8
189 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII=";
190 o.EmbedContent(s);
191 //o.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
192 o.SaveToFile("UnitTestsResults/png3.dcm");
193
194
195 {
196 // Gradient in Graylevel16
197
198 ImageBuffer img;
199 img.SetWidth(256);
200 img.SetHeight(256);
201 img.SetFormat(PixelFormat_Grayscale16);
202
203 ImageAccessor accessor;
204 img.GetWriteableAccessor(accessor);
205
206 uint16_t v = 0;
207 for (unsigned int y = 0; y < img.GetHeight(); y++)
208 {
209 uint16_t *p = reinterpret_cast<uint16_t*>(accessor.GetRow(y));
210 for (unsigned int x = 0; x < img.GetWidth(); x++, p++, v++)
211 {
212 *p = v;
213 }
214 }
215
216 o.EmbedImage(accessor);
217 o.SaveToFile("UnitTestsResults/png4.dcm");
218 }
219 }
220
221
222 TEST(FromDcmtkBridge, Encodings1)
223 {
224 for (unsigned int i = 0; i < testEncodingsCount; i++)
225 {
226 std::string source(testEncodingsEncoded[i]);
227 std::string expected(testEncodingsExpected[i]);
228 std::string s = Toolbox::ConvertToUtf8(source, testEncodings[i], false);
229 //std::cout << EnumerationToString(testEncodings[i]) << std::endl;
230 EXPECT_EQ(expected, s);
231 }
232 }
233
234
235 TEST(FromDcmtkBridge, Enumerations)
236 {
237 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
238 Encoding e;
239
240 ASSERT_FALSE(GetDicomEncoding(e, ""));
241 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 6")); ASSERT_EQ(Encoding_Ascii, e);
242
243 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-2
244 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 100")); ASSERT_EQ(Encoding_Latin1, e);
245 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 101")); ASSERT_EQ(Encoding_Latin2, e);
246 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 109")); ASSERT_EQ(Encoding_Latin3, e);
247 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 110")); ASSERT_EQ(Encoding_Latin4, e);
248 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 144")); ASSERT_EQ(Encoding_Cyrillic, e);
249 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 127")); ASSERT_EQ(Encoding_Arabic, e);
250 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 126")); ASSERT_EQ(Encoding_Greek, e);
251 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
252 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 148")); ASSERT_EQ(Encoding_Latin5, e);
253 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 13")); ASSERT_EQ(Encoding_Japanese, e);
254 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 166")); ASSERT_EQ(Encoding_Thai, e);
255
256 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-3
257 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 6")); ASSERT_EQ(Encoding_Ascii, e);
258 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 100")); ASSERT_EQ(Encoding_Latin1, e);
259 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 101")); ASSERT_EQ(Encoding_Latin2, e);
260 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 109")); ASSERT_EQ(Encoding_Latin3, e);
261 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 110")); ASSERT_EQ(Encoding_Latin4, e);
262 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 144")); ASSERT_EQ(Encoding_Cyrillic, e);
263 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 127")); ASSERT_EQ(Encoding_Arabic, e);
264 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 126")); ASSERT_EQ(Encoding_Greek, e);
265 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
266 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 148")); ASSERT_EQ(Encoding_Latin5, e);
267 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 13")); ASSERT_EQ(Encoding_Japanese, e);
268 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 166")); ASSERT_EQ(Encoding_Thai, e);
269
270 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-4
271 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 87")); ASSERT_EQ(Encoding_JapaneseKanji, e);
272 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 159")); //ASSERT_EQ(Encoding_JapaneseKanjiSupplementary, e);
273 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 149")); ASSERT_EQ(Encoding_Korean, e);
274 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 58")); ASSERT_EQ(Encoding_SimplifiedChinese, e);
275
276 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-5
277 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 192")); ASSERT_EQ(Encoding_Utf8, e);
278 ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e);
279 ASSERT_TRUE(GetDicomEncoding(e, "GBK")); ASSERT_EQ(Encoding_Chinese, e);
280 }
281
282
283 TEST(FromDcmtkBridge, Encodings3)
284 {
285 for (unsigned int i = 0; i < testEncodingsCount; i++)
286 {
287 //std::cout << EnumerationToString(testEncodings[i]) << std::endl;
288 std::string dicom;
289
290 {
291 ParsedDicomFile f(true);
292 f.SetEncoding(testEncodings[i]);
293
294 std::string s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false);
295 f.Insert(DICOM_TAG_PATIENT_NAME, s, false, "");
296 f.SaveToMemoryBuffer(dicom);
297 }
298
299 if (testEncodings[i] != Encoding_Windows1251)
300 {
301 ParsedDicomFile g(dicom);
302
303 if (testEncodings[i] != Encoding_Ascii)
304 {
305 bool hasCodeExtensions;
306 ASSERT_EQ(testEncodings[i], g.DetectEncoding(hasCodeExtensions));
307 ASSERT_FALSE(hasCodeExtensions);
308 }
309
310 std::string tag;
311 ASSERT_TRUE(g.GetTagValue(tag, DICOM_TAG_PATIENT_NAME));
312 ASSERT_EQ(std::string(testEncodingsExpected[i]), tag);
313 }
314 }
315 }
316
317
318 TEST(FromDcmtkBridge, ValueRepresentation)
319 {
320 ASSERT_EQ(ValueRepresentation_PersonName,
321 FromDcmtkBridge::LookupValueRepresentation(DICOM_TAG_PATIENT_NAME));
322 ASSERT_EQ(ValueRepresentation_Date,
323 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
324 ASSERT_EQ(ValueRepresentation_Time,
325 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */));
326 ASSERT_EQ(ValueRepresentation_DateTime,
327 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */));
328 ASSERT_EQ(ValueRepresentation_NotSupported,
329 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0001, 0x0001) /* some private tag */));
330 }
331
332
333
334 static const DicomTag REFERENCED_STUDY_SEQUENCE(0x0008, 0x1110);
335 static const DicomTag REFERENCED_PATIENT_SEQUENCE(0x0008, 0x1120);
336
337 static void CreateSampleJson(Json::Value& a)
338 {
339 {
340 Json::Value b = Json::objectValue;
341 b["PatientName"] = "Hello";
342 b["PatientID"] = "World";
343 b["StudyDescription"] = "Toto";
344 a.append(b);
345 }
346
347 {
348 Json::Value b = Json::objectValue;
349 b["PatientName"] = "data:application/octet-stream;base64,SGVsbG8y"; // echo -n "Hello2" | base64
350 b["PatientID"] = "World2";
351 a.append(b);
352 }
353 }
354
355
356
357 TEST(ParsedDicomFile, InsertReplaceStrings)
358 {
359 ParsedDicomFile f(true);
360
361 f.Insert(DICOM_TAG_PATIENT_NAME, "World", false, "");
362 ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false, ""), OrthancException); // Already existing tag
363 f.ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*)
364 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**)
365
366 std::string s;
367 ASSERT_TRUE(f.LookupTransferSyntax(s));
368 // The default transfer syntax depends on the OS endianness
369 ASSERT_TRUE(s == GetTransferSyntaxUid(DicomTransferSyntax_LittleEndianExplicit) ||
370 s == GetTransferSyntaxUid(DicomTransferSyntax_BigEndianExplicit));
371
372 ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"),
373 false, DicomReplaceMode_ThrowIfAbsent, ""), OrthancException);
374 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_IgnoreIfAbsent, "");
375 ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
376 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_InsertIfAbsent, "");
377 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
378 ASSERT_EQ(s, "Accession");
379 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession2"), false, DicomReplaceMode_IgnoreIfAbsent, "");
380 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
381 ASSERT_EQ(s, "Accession2");
382 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession3"), false, DicomReplaceMode_ThrowIfAbsent, "");
383 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
384 ASSERT_EQ(s, "Accession3");
385
386 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
387 ASSERT_EQ(s, "World");
388 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID));
389 ASSERT_EQ(s, "Toto");
390 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*)
391 ASSERT_EQ(s, "Toto");
392 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_CLASS_UID));
393 ASSERT_EQ(s, "Tata");
394 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID)); // Implicitly modified by (**)
395 ASSERT_EQ(s, "Tata");
396 }
397
398
399
400
401 TEST(ParsedDicomFile, InsertReplaceJson)
402 {
403 ParsedDicomFile f(true);
404
405 Json::Value a;
406 CreateSampleJson(a);
407
408 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE));
409 f.Remove(REFERENCED_STUDY_SEQUENCE); // No effect
410 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, "");
411 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE));
412 ASSERT_THROW(f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, ""), OrthancException);
413 f.Remove(REFERENCED_STUDY_SEQUENCE);
414 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE));
415 f.Insert(REFERENCED_STUDY_SEQUENCE, a, true, "");
416 ASSERT_TRUE(f.HasTag(REFERENCED_STUDY_SEQUENCE));
417
418 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE));
419 ASSERT_THROW(f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_ThrowIfAbsent, ""), OrthancException);
420 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE));
421 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_IgnoreIfAbsent, "");
422 ASSERT_FALSE(f.HasTag(REFERENCED_PATIENT_SEQUENCE));
423 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_InsertIfAbsent, "");
424 ASSERT_TRUE(f.HasTag(REFERENCED_PATIENT_SEQUENCE));
425
426 {
427 Json::Value b;
428 f.DatasetToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0);
429
430 Json::Value c;
431 Toolbox::SimplifyDicomAsJson(c, b, DicomToJsonFormat_Human);
432
433 ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a));
434 ASSERT_NE(0, c["ReferencedStudySequence"].compare(a)); // Because Data URI Scheme decoding was enabled
435 }
436
437 a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64
438 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false, DicomReplaceMode_InsertIfAbsent, ""); // (*)
439 f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true, DicomReplaceMode_InsertIfAbsent, ""); // (**)
440
441 std::string s;
442 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID));
443 ASSERT_EQ(s, a.asString());
444 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*)
445 ASSERT_EQ(s, a.asString());
446 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_CLASS_UID));
447 ASSERT_EQ(s, "Tata");
448 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID)); // Implicitly modified by (**)
449 ASSERT_EQ(s, "Tata");
450 }
451
452
453 TEST(ParsedDicomFile, JsonEncoding)
454 {
455 ParsedDicomFile f(true);
456
457 for (unsigned int i = 0; i < testEncodingsCount; i++)
458 {
459 if (testEncodings[i] != Encoding_Windows1251)
460 {
461 //std::cout << EnumerationToString(testEncodings[i]) << std::endl;
462 f.SetEncoding(testEncodings[i]);
463
464 if (testEncodings[i] != Encoding_Ascii)
465 {
466 bool hasCodeExtensions;
467 ASSERT_EQ(testEncodings[i], f.DetectEncoding(hasCodeExtensions));
468 ASSERT_FALSE(hasCodeExtensions);
469 }
470
471 Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i], false);
472 f.Replace(DICOM_TAG_PATIENT_NAME, s, false, DicomReplaceMode_InsertIfAbsent, "");
473
474 Json::Value v;
475 f.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
476 ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i]));
477 }
478 }
479 }
480
481
482 TEST(ParsedDicomFile, ToJsonFlags1)
483 {
484 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1, "OrthancCreator");
485 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1, "");
486
487 ParsedDicomFile f(true);
488 f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false, ""); // Even group => public tag
489 f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false, ""); // Even group => public, unknown tag
490 f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false, "OrthancCreator"); // Odd group => private tag
491
492 Json::Value v;
493 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
494 ASSERT_EQ(Json::objectValue, v.type());
495 ASSERT_EQ(6u, v.getMemberNames().size());
496 ASSERT_FALSE(v.isMember("7052,1000"));
497 ASSERT_FALSE(v.isMember("7053,1000"));
498 ASSERT_TRUE(v.isMember("7050,1000"));
499 ASSERT_EQ(Json::stringValue, v["7050,1000"].type());
500 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
501
502 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0);
503 ASSERT_EQ(Json::objectValue, v.type());
504 ASSERT_EQ(7u, v.getMemberNames().size());
505 ASSERT_FALSE(v.isMember("7052,1000"));
506 ASSERT_TRUE(v.isMember("7050,1000"));
507 ASSERT_TRUE(v.isMember("7053,1000"));
508 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
509 ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
510
511 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags), 0);
512 ASSERT_EQ(Json::objectValue, v.type());
513 ASSERT_EQ(6u, v.getMemberNames().size());
514 ASSERT_FALSE(v.isMember("7052,1000"));
515 ASSERT_TRUE(v.isMember("7050,1000"));
516 ASSERT_FALSE(v.isMember("7053,1000"));
517
518 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary), 0);
519 ASSERT_EQ(Json::objectValue, v.type());
520 ASSERT_EQ(7u, v.getMemberNames().size());
521 ASSERT_FALSE(v.isMember("7052,1000"));
522 ASSERT_TRUE(v.isMember("7050,1000"));
523 ASSERT_TRUE(v.isMember("7053,1000"));
524 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
525 std::string mime, content;
526 ASSERT_EQ(Json::stringValue, v["7053,1000"].type());
527 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString()));
528 ASSERT_EQ("application/octet-stream", mime);
529 ASSERT_EQ("Some private tag", content);
530
531 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0);
532 ASSERT_EQ(Json::objectValue, v.type());
533 ASSERT_EQ(7u, v.getMemberNames().size());
534 ASSERT_TRUE(v.isMember("7050,1000"));
535 ASSERT_TRUE(v.isMember("7052,1000"));
536 ASSERT_FALSE(v.isMember("7053,1000"));
537 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
538 ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
539
540 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludeBinary), 0);
541 ASSERT_EQ(Json::objectValue, v.type());
542 ASSERT_EQ(7u, v.getMemberNames().size());
543 ASSERT_TRUE(v.isMember("7050,1000"));
544 ASSERT_TRUE(v.isMember("7052,1000"));
545 ASSERT_FALSE(v.isMember("7053,1000"));
546 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
547 ASSERT_EQ(Json::stringValue, v["7052,1000"].type());
548 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString()));
549 ASSERT_EQ("application/octet-stream", mime);
550 ASSERT_EQ("Some unknown tag", content);
551
552 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_IncludeBinary | DicomToJsonFlags_ConvertBinaryToNull), 0);
553 ASSERT_EQ(Json::objectValue, v.type());
554 ASSERT_EQ(8u, v.getMemberNames().size());
555 ASSERT_TRUE(v.isMember("7050,1000"));
556 ASSERT_TRUE(v.isMember("7052,1000"));
557 ASSERT_TRUE(v.isMember("7053,1000"));
558 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
559 ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
560 ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
561 }
562
563
564 TEST(ParsedDicomFile, ToJsonFlags2)
565 {
566 ParsedDicomFile f(true);
567
568 {
569 // "ParsedDicomFile" uses Little Endian => 'B' (least significant
570 // byte) will be stored first in the memory buffer and in the
571 // file, then 'A'. Hence the expected "BA" value below.
572 Uint16 v[] = { 'A' * 256 + 'B', 0 };
573 ASSERT_TRUE(f.GetDcmtkObject().getDataset()->putAndInsertUint16Array(DCM_PixelData, v, 2).good());
574 }
575
576 Json::Value v;
577 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
578 ASSERT_EQ(Json::objectValue, v.type());
579 ASSERT_EQ(5u, v.getMemberNames().size());
580 ASSERT_FALSE(v.isMember("7fe0,0010"));
581
582 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToNull), 0);
583 ASSERT_EQ(Json::objectValue, v.type());
584 ASSERT_EQ(6u, v.getMemberNames().size());
585 ASSERT_TRUE(v.isMember("7fe0,0010"));
586 ASSERT_EQ(Json::nullValue, v["7fe0,0010"].type());
587
588 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToAscii), 0);
589 ASSERT_EQ(Json::objectValue, v.type());
590 ASSERT_EQ(6u, v.getMemberNames().size());
591 ASSERT_TRUE(v.isMember("7fe0,0010"));
592 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
593 ASSERT_EQ("BA", v["7fe0,0010"].asString().substr(0, 2));
594
595 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0);
596 ASSERT_EQ(Json::objectValue, v.type());
597 ASSERT_EQ(6u, v.getMemberNames().size());
598 ASSERT_TRUE(v.isMember("7fe0,0010"));
599 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
600 std::string mime, content;
601 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString()));
602 ASSERT_EQ("application/octet-stream", mime);
603 ASSERT_EQ("BA", content.substr(0, 2));
604 }
605
606
607 TEST(DicomFindAnswers, Basic)
608 {
609 DicomFindAnswers a(false);
610
611 {
612 DicomMap m;
613 m.SetValue(DICOM_TAG_PATIENT_ID, "hello", false);
614 a.Add(m);
615 }
616
617 {
618 ParsedDicomFile d(true);
619 d.ReplacePlainString(DICOM_TAG_PATIENT_ID, "my");
620 a.Add(d);
621 }
622
623 {
624 DicomMap m;
625 m.SetValue(DICOM_TAG_PATIENT_ID, "world", false);
626 a.Add(m);
627 }
628
629 Json::Value j;
630 a.ToJson(j, true);
631 ASSERT_EQ(3u, j.size());
632
633 //std::cout << j;
634 }
635
636
637 TEST(ParsedDicomFile, FromJson)
638 {
639 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag2", 1, 1, "ORTHANC");
640 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag3", 1, 1, "");
641 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag2", 1, 1, "");
642
643 Json::Value v;
644 const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1"; // CR Image Storage:
645
646 // Test the private creator
647 ASSERT_EQ(DcmTag_ERROR_TagName, FromDcmtkBridge::GetTagName(DicomTag(0x7057, 0x1000), "NOPE"));
648 ASSERT_EQ("MyPrivateTag2", FromDcmtkBridge::GetTagName(DicomTag(0x7057, 0x1000), "ORTHANC"));
649
650 {
651 v["SOPClassUID"] = sopClassUid;
652 v["SpecificCharacterSet"] = "ISO_IR 148"; // This is latin-5
653 v["PatientName"] = "Sébastien";
654 v["7050-1000"] = "Some public tag"; // Even group => public tag
655 v["7052-1000"] = "Some unknown tag"; // Even group => public, unknown tag
656 v["7057-1000"] = "Some private tag"; // Odd group => private tag
657 v["7059-1000"] = "Some private tag2"; // Odd group => private tag, with an odd length to test padding
658
659 std::string s;
660 Toolbox::EncodeDataUriScheme(s, "application/octet-stream", "Sebastien");
661 v["StudyDescription"] = s;
662
663 v["PixelData"] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // A red dot of 5x5 pixels
664 v["0040,0100"] = Json::arrayValue; // ScheduledProcedureStepSequence
665
666 Json::Value vv;
667 vv["Modality"] = "MR";
668 v["0040,0100"].append(vv);
669
670 vv["Modality"] = "CT";
671 v["0040,0100"].append(vv);
672 }
673
674 const DicomToJsonFlags toJsonFlags = static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeBinary |
675 DicomToJsonFlags_IncludePixelData |
676 DicomToJsonFlags_IncludePrivateTags |
677 DicomToJsonFlags_IncludeUnknownTags |
678 DicomToJsonFlags_ConvertBinaryToAscii);
679
680
681 {
682 std::unique_ptr<ParsedDicomFile> dicom
683 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers), ""));
684
685 Json::Value vv;
686 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, toJsonFlags, 0);
687
688 ASSERT_EQ(vv["SOPClassUID"].asString(), sopClassUid);
689 ASSERT_EQ(vv["MediaStorageSOPClassUID"].asString(), sopClassUid);
690 ASSERT_TRUE(vv.isMember("SOPInstanceUID"));
691 ASSERT_TRUE(vv.isMember("SeriesInstanceUID"));
692 ASSERT_TRUE(vv.isMember("StudyInstanceUID"));
693 ASSERT_TRUE(vv.isMember("PatientID"));
694 }
695
696
697 {
698 std::unique_ptr<ParsedDicomFile> dicom
699 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers), ""));
700
701 Json::Value vv;
702 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0);
703
704 std::string mime, content;
705 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, vv["PixelData"].asString()));
706 ASSERT_EQ("application/octet-stream", mime);
707 ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size());
708 }
709
710
711 {
712 std::unique_ptr<ParsedDicomFile> dicom
713 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_DecodeDataUriScheme), ""));
714
715 Json::Value vv;
716 dicom->DatasetToJson(vv, DicomToJsonFormat_Short, toJsonFlags, 0);
717
718 ASSERT_FALSE(vv.isMember("SOPInstanceUID"));
719 ASSERT_FALSE(vv.isMember("SeriesInstanceUID"));
720 ASSERT_FALSE(vv.isMember("StudyInstanceUID"));
721 ASSERT_FALSE(vv.isMember("PatientID"));
722 ASSERT_EQ(2u, vv["0040,0100"].size());
723 ASSERT_EQ("MR", vv["0040,0100"][0]["0008,0060"].asString());
724 ASSERT_EQ("CT", vv["0040,0100"][1]["0008,0060"].asString());
725 ASSERT_EQ("Some public tag", vv["7050,1000"].asString());
726 ASSERT_EQ("Some unknown tag", vv["7052,1000"].asString());
727 ASSERT_EQ("Some private tag", vv["7057,1000"].asString());
728 ASSERT_EQ("Some private tag2", vv["7059,1000"].asString());
729 ASSERT_EQ("Sébastien", vv["0010,0010"].asString());
730 ASSERT_EQ("Sebastien", vv["0008,1030"].asString());
731 ASSERT_EQ("ISO_IR 148", vv["0008,0005"].asString());
732 ASSERT_EQ("5", vv[DICOM_TAG_ROWS.Format()].asString());
733 ASSERT_EQ("5", vv[DICOM_TAG_COLUMNS.Format()].asString());
734 ASSERT_TRUE(vv[DICOM_TAG_PIXEL_DATA.Format()].asString().empty());
735 }
736 }
737
738
739
740 TEST(TestImages, PatternGrayscale8)
741 {
742 static const char* PATH = "UnitTestsResults/PatternGrayscale8.dcm";
743
744 Orthanc::Image image(Orthanc::PixelFormat_Grayscale8, 256, 256, false);
745
746 for (int y = 0; y < 256; y++)
747 {
748 uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
749 for (int x = 0; x < 256; x++, p++)
750 {
751 *p = y;
752 }
753 }
754
755 Orthanc::ImageAccessor r;
756
757 image.GetRegion(r, 32, 32, 64, 192);
758 Orthanc::ImageProcessing::Set(r, 0);
759
760 image.GetRegion(r, 160, 32, 64, 192);
761 Orthanc::ImageProcessing::Set(r, 255);
762
763 {
764 ParsedDicomFile f(true);
765 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
766 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
767 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
768 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
769 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
770 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale8");
771 f.EmbedImage(image);
772
773 f.SaveToFile(PATH);
774 }
775
776 {
777 std::string s;
778 Orthanc::SystemToolbox::ReadFile(s, PATH);
779 Orthanc::ParsedDicomFile f(s);
780
781 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
782 ASSERT_EQ(256u, decoded->GetWidth());
783 ASSERT_EQ(256u, decoded->GetHeight());
784 ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat());
785
786 for (int y = 0; y < 256; y++)
787 {
788 const void* a = image.GetConstRow(y);
789 const void* b = decoded->GetConstRow(y);
790 ASSERT_EQ(0, memcmp(a, b, 256));
791 }
792 }
793 }
794
795
796 TEST(TestImages, PatternRGB)
797 {
798 static const char* PATH = "UnitTestsResults/PatternRGB24.dcm";
799
800 Orthanc::Image image(Orthanc::PixelFormat_RGB24, 384, 256, false);
801
802 for (int y = 0; y < 256; y++)
803 {
804 uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
805 for (int x = 0; x < 128; x++, p += 3)
806 {
807 p[0] = y;
808 p[1] = 0;
809 p[2] = 0;
810 }
811 for (int x = 128; x < 128 * 2; x++, p += 3)
812 {
813 p[0] = 0;
814 p[1] = 255 - y;
815 p[2] = 0;
816 }
817 for (int x = 128 * 2; x < 128 * 3; x++, p += 3)
818 {
819 p[0] = 0;
820 p[1] = 0;
821 p[2] = y;
822 }
823 }
824
825 {
826 ParsedDicomFile f(true);
827 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
828 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
829 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
830 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
831 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
832 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "RGB24");
833 f.EmbedImage(image);
834
835 f.SaveToFile(PATH);
836 }
837
838 {
839 std::string s;
840 Orthanc::SystemToolbox::ReadFile(s, PATH);
841 Orthanc::ParsedDicomFile f(s);
842
843 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
844 ASSERT_EQ(384u, decoded->GetWidth());
845 ASSERT_EQ(256u, decoded->GetHeight());
846 ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat());
847
848 for (int y = 0; y < 256; y++)
849 {
850 const void* a = image.GetConstRow(y);
851 const void* b = decoded->GetConstRow(y);
852 ASSERT_EQ(0, memcmp(a, b, 3 * 384));
853 }
854 }
855 }
856
857
858 TEST(TestImages, PatternUint16)
859 {
860 static const char* PATH = "UnitTestsResults/PatternGrayscale16.dcm";
861
862 Orthanc::Image image(Orthanc::PixelFormat_Grayscale16, 256, 256, false);
863
864 uint16_t v = 0;
865 for (int y = 0; y < 256; y++)
866 {
867 uint16_t *p = reinterpret_cast<uint16_t*>(image.GetRow(y));
868 for (int x = 0; x < 256; x++, v++, p++)
869 {
870 *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
871 }
872 }
873
874 Orthanc::ImageAccessor r;
875
876 image.GetRegion(r, 32, 32, 64, 192);
877 Orthanc::ImageProcessing::Set(r, 0);
878
879 image.GetRegion(r, 160, 32, 64, 192);
880 Orthanc::ImageProcessing::Set(r, 65535);
881
882 {
883 ParsedDicomFile f(true);
884 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
885 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
886 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
887 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
888 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
889 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale16");
890 f.EmbedImage(image);
891
892 f.SaveToFile(PATH);
893 }
894
895 {
896 std::string s;
897 Orthanc::SystemToolbox::ReadFile(s, PATH);
898 Orthanc::ParsedDicomFile f(s);
899
900 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
901 ASSERT_EQ(256u, decoded->GetWidth());
902 ASSERT_EQ(256u, decoded->GetHeight());
903 ASSERT_EQ(Orthanc::PixelFormat_Grayscale16, decoded->GetFormat());
904
905 for (int y = 0; y < 256; y++)
906 {
907 const void* a = image.GetConstRow(y);
908 const void* b = decoded->GetConstRow(y);
909 ASSERT_EQ(0, memcmp(a, b, 512));
910 }
911 }
912 }
913
914
915 TEST(TestImages, PatternInt16)
916 {
917 static const char* PATH = "UnitTestsResults/PatternSignedGrayscale16.dcm";
918
919 Orthanc::Image image(Orthanc::PixelFormat_SignedGrayscale16, 256, 256, false);
920
921 int16_t v = -32768;
922 for (int y = 0; y < 256; y++)
923 {
924 int16_t *p = reinterpret_cast<int16_t*>(image.GetRow(y));
925 for (int x = 0; x < 256; x++, v++, p++)
926 {
927 *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
928 }
929 }
930
931 Orthanc::ImageAccessor r;
932 image.GetRegion(r, 32, 32, 64, 192);
933 Orthanc::ImageProcessing::Set(r, -32768);
934
935 image.GetRegion(r, 160, 32, 64, 192);
936 Orthanc::ImageProcessing::Set(r, 32767);
937
938 {
939 ParsedDicomFile f(true);
940 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
941 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
942 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
943 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
944 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
945 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "SignedGrayscale16");
946 f.EmbedImage(image);
947
948 f.SaveToFile(PATH);
949 }
950
951 {
952 std::string s;
953 Orthanc::SystemToolbox::ReadFile(s, PATH);
954 Orthanc::ParsedDicomFile f(s);
955
956 std::unique_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
957 ASSERT_EQ(256u, decoded->GetWidth());
958 ASSERT_EQ(256u, decoded->GetHeight());
959 ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat());
960
961 for (int y = 0; y < 256; y++)
962 {
963 const void* a = image.GetConstRow(y);
964 const void* b = decoded->GetConstRow(y);
965 ASSERT_EQ(0, memcmp(a, b, 512));
966 }
967 }
968 }
969
970
971
972 static void CheckEncoding(const ParsedDicomFile& dicom,
973 Encoding expected)
974 {
975 const char* value = NULL;
976 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_SpecificCharacterSet, value).good());
977
978 Encoding encoding;
979 ASSERT_TRUE(GetDicomEncoding(encoding, value));
980 ASSERT_EQ(expected, encoding);
981 }
982
983
984 TEST(ParsedDicomFile, DicomMapEncodings1)
985 {
986 SetDefaultDicomEncoding(Encoding_Ascii);
987 ASSERT_EQ(Encoding_Ascii, GetDefaultDicomEncoding());
988
989 {
990 DicomMap m;
991 ParsedDicomFile dicom(m, GetDefaultDicomEncoding(), false);
992 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
993 CheckEncoding(dicom, Encoding_Ascii);
994 }
995
996 {
997 DicomMap m;
998 ParsedDicomFile dicom(m, Encoding_Latin4, false);
999 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1000 CheckEncoding(dicom, Encoding_Latin4);
1001 }
1002
1003 {
1004 DicomMap m;
1005 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 148", false);
1006 ParsedDicomFile dicom(m, GetDefaultDicomEncoding(), false);
1007 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1008 CheckEncoding(dicom, Encoding_Latin5);
1009 }
1010
1011 {
1012 DicomMap m;
1013 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 148", false);
1014 ParsedDicomFile dicom(m, Encoding_Latin1, false);
1015 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1016 CheckEncoding(dicom, Encoding_Latin5);
1017 }
1018 }
1019
1020
1021 TEST(ParsedDicomFile, DicomMapEncodings2)
1022 {
1023 const char* utf8 = NULL;
1024 for (unsigned int i = 0; i < testEncodingsCount; i++)
1025 {
1026 if (testEncodings[i] == Encoding_Utf8)
1027 {
1028 utf8 = testEncodingsEncoded[i];
1029 break;
1030 }
1031 }
1032
1033 ASSERT_TRUE(utf8 != NULL);
1034
1035 for (unsigned int i = 0; i < testEncodingsCount; i++)
1036 {
1037 // 1251 codepage is not supported by the core DICOM standard, ignore it
1038 if (testEncodings[i] != Encoding_Windows1251)
1039 {
1040 {
1041 // Sanity check to test the proper behavior of "EncodingTests.py"
1042 std::string encoded = Toolbox::ConvertFromUtf8(testEncodingsExpected[i], testEncodings[i]);
1043 ASSERT_STREQ(testEncodingsEncoded[i], encoded.c_str());
1044 std::string decoded = Toolbox::ConvertToUtf8(encoded, testEncodings[i], false);
1045 ASSERT_STREQ(testEncodingsExpected[i], decoded.c_str());
1046
1047 if (testEncodings[i] != Encoding_Chinese)
1048 {
1049 // A specific source string is used in "EncodingTests.py" to
1050 // test against Chinese, it is normal that it does not correspond to UTF8
1051
1052 std::string encoded = Toolbox::ConvertToUtf8(Toolbox::ConvertFromUtf8(utf8, testEncodings[i]), testEncodings[i], false);
1053 ASSERT_STREQ(testEncodingsExpected[i], encoded.c_str());
1054 }
1055 }
1056
1057
1058 Json::Value v;
1059
1060 {
1061 DicomMap m;
1062 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1063
1064 ParsedDicomFile dicom(m, testEncodings[i], false);
1065
1066 const char* encoded = NULL;
1067 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_PatientName, encoded).good());
1068 ASSERT_STREQ(testEncodingsEncoded[i], encoded);
1069
1070 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1071
1072 Encoding encoding;
1073 ASSERT_TRUE(GetDicomEncoding(encoding, v["SpecificCharacterSet"].asCString()));
1074 ASSERT_EQ(encoding, testEncodings[i]);
1075 ASSERT_STREQ(testEncodingsExpected[i], v["PatientName"].asCString());
1076 }
1077
1078
1079 {
1080 DicomMap m;
1081 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, GetDicomSpecificCharacterSet(testEncodings[i]), false);
1082 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1083
1084 ParsedDicomFile dicom(m, testEncodings[i], false);
1085
1086 Json::Value v2;
1087 dicom.DatasetToJson(v2, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1088
1089 ASSERT_EQ(v2["PatientName"].asString(), v["PatientName"].asString());
1090 ASSERT_EQ(v2["SpecificCharacterSet"].asString(), v["SpecificCharacterSet"].asString());
1091 }
1092 }
1093 }
1094 }
1095
1096
1097 TEST(ParsedDicomFile, ChangeEncoding)
1098 {
1099 for (unsigned int i = 0; i < testEncodingsCount; i++)
1100 {
1101 // 1251 codepage is not supported by the core DICOM standard, ignore it
1102 if (testEncodings[i] != Encoding_Windows1251)
1103 {
1104 DicomMap m;
1105 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1106
1107 std::string tag;
1108
1109 ParsedDicomFile dicom(m, Encoding_Utf8, false);
1110 bool hasCodeExtensions;
1111 ASSERT_EQ(Encoding_Utf8, dicom.DetectEncoding(hasCodeExtensions));
1112 ASSERT_FALSE(hasCodeExtensions);
1113 ASSERT_TRUE(dicom.GetTagValue(tag, DICOM_TAG_PATIENT_NAME));
1114 ASSERT_EQ(tag, testEncodingsExpected[i]);
1115
1116 {
1117 Json::Value v;
1118 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1119 ASSERT_STREQ(v["SpecificCharacterSet"].asCString(), "ISO_IR 192");
1120 ASSERT_STREQ(v["PatientName"].asCString(), testEncodingsExpected[i]);
1121 }
1122
1123 dicom.ChangeEncoding(testEncodings[i]);
1124
1125 ASSERT_EQ(testEncodings[i], dicom.DetectEncoding(hasCodeExtensions));
1126 ASSERT_FALSE(hasCodeExtensions);
1127
1128 const char* c = NULL;
1129 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_PatientName, c).good());
1130 EXPECT_STREQ(c, testEncodingsEncoded[i]);
1131
1132 ASSERT_TRUE(dicom.GetTagValue(tag, DICOM_TAG_PATIENT_NAME)); // Decodes to UTF-8
1133 EXPECT_EQ(tag, testEncodingsExpected[i]);
1134
1135 {
1136 Json::Value v;
1137 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1138 ASSERT_STREQ(v["SpecificCharacterSet"].asCString(), GetDicomSpecificCharacterSet(testEncodings[i]));
1139 ASSERT_STREQ(v["PatientName"].asCString(), testEncodingsExpected[i]);
1140 }
1141 }
1142 }
1143 }
1144
1145
1146 TEST(Toolbox, CaseWithAccents)
1147 {
1148 ASSERT_EQ(toUpperResult, Toolbox::ToUpperCaseWithAccents(toUpperSource));
1149 }
1150
1151
1152
1153 TEST(ParsedDicomFile, InvalidCharacterSets)
1154 {
1155 {
1156 // No encoding provided, fallback to default encoding
1157 DicomMap m;
1158 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1159
1160 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */, false);
1161
1162 bool hasCodeExtensions;
1163 ASSERT_EQ(Encoding_Latin3, d.DetectEncoding(hasCodeExtensions));
1164 ASSERT_FALSE(hasCodeExtensions);
1165 }
1166
1167 {
1168 // Valid encoding, "ISO_IR 13" is Japanese
1169 DicomMap m;
1170 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 13", false);
1171 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1172
1173 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */, false);
1174
1175 bool hasCodeExtensions;
1176 ASSERT_EQ(Encoding_Japanese, d.DetectEncoding(hasCodeExtensions));
1177 ASSERT_FALSE(hasCodeExtensions);
1178 }
1179
1180 {
1181 // Invalid value for an encoding ("nope" is not in the DICOM standard)
1182 DicomMap m;
1183 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "nope", false);
1184 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1185
1186 ASSERT_THROW(ParsedDicomFile d(m, Encoding_Latin3, false),
1187 OrthancException);
1188 }
1189
1190 {
1191 // Invalid encoding, as provided as a binary string
1192 DicomMap m;
1193 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 13", true);
1194 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1195
1196 ASSERT_THROW(ParsedDicomFile d(m, Encoding_Latin3, false),
1197 OrthancException);
1198 }
1199
1200 {
1201 // Encoding provided as an empty string, fallback to default encoding
1202 // In Orthanc <= 1.3.1, this test was throwing an exception
1203 DicomMap m;
1204 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "", false);
1205 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1206
1207 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */, false);
1208
1209 bool hasCodeExtensions;
1210 ASSERT_EQ(Encoding_Latin3, d.DetectEncoding(hasCodeExtensions));
1211 ASSERT_FALSE(hasCodeExtensions);
1212 }
1213 }
1214
1215
1216
1217 TEST(Toolbox, RemoveIso2022EscapeSequences)
1218 {
1219 // +----------------------------------+
1220 // | one-byte control messages |
1221 // +----------------------------------+
1222
1223 static const uint8_t iso2022_cstr_oneByteControl[] = {
1224 0x0f, 0x41,
1225 0x0e, 0x42,
1226 0x8e, 0x1b, 0x4e, 0x43,
1227 0x8f, 0x1b, 0x4f, 0x44,
1228 0x8e, 0x1b, 0x4a, 0x45,
1229 0x8f, 0x1b, 0x4a, 0x46,
1230 0x50, 0x51, 0x52, 0x00
1231 };
1232
1233 static const uint8_t iso2022_cstr_oneByteControl_ref[] = {
1234 0x41,
1235 0x42,
1236 0x43,
1237 0x44,
1238 0x8e, 0x1b, 0x4a, 0x45,
1239 0x8f, 0x1b, 0x4a, 0x46,
1240 0x50, 0x51, 0x52, 0x00
1241 };
1242
1243 // +----------------------------------+
1244 // | two-byte control messages |
1245 // +----------------------------------+
1246
1247 static const uint8_t iso2022_cstr_twoByteControl[] = {
1248 0x1b, 0x6e, 0x41,
1249 0x1b, 0x6f, 0x42,
1250 0x1b, 0x4e, 0x43,
1251 0x1b, 0x4f, 0x44,
1252 0x1b, 0x7e, 0x45,
1253 0x1b, 0x7d, 0x46,
1254 0x1b, 0x7c, 0x47, 0x00
1255 };
1256
1257 static const uint8_t iso2022_cstr_twoByteControl_ref[] = {
1258 0x41,
1259 0x42,
1260 0x43,
1261 0x44,
1262 0x45,
1263 0x46,
1264 0x47, 0x00
1265 };
1266
1267 // +----------------------------------+
1268 // | various-length escape sequences |
1269 // +----------------------------------+
1270
1271 static const uint8_t iso2022_cstr_escapeSequence[] = {
1272 0x1b, 0x40, 0x41, // 1b and 40 should not be removed (invalid esc seq)
1273 0x1b, 0x50, 0x42, // ditto
1274 0x1b, 0x7f, 0x43, // ditto
1275 0x1b, 0x21, 0x4a, 0x44, // this will match
1276 0x1b, 0x20, 0x21, 0x2f, 0x40, 0x45, // this will match
1277 0x1b, 0x20, 0x21, 0x2f, 0x2f, 0x40, 0x46, // this will match too
1278 0x1b, 0x20, 0x21, 0x2f, 0x1f, 0x47, 0x48, 0x00 // this will NOT match!
1279 };
1280
1281 static const uint8_t iso2022_cstr_escapeSequence_ref[] = {
1282 0x1b, 0x40, 0x41, // 1b and 40 should not be removed (invalid esc seq)
1283 0x1b, 0x50, 0x42, // ditto
1284 0x1b, 0x7f, 0x43, // ditto
1285 0x44, // this will match
1286 0x45, // this will match
1287 0x46, // this will match too
1288 0x1b, 0x20, 0x21, 0x2f, 0x1f, 0x47, 0x48, 0x00 // this will NOT match!
1289 };
1290
1291
1292 // +----------------------------------+
1293 // | a real-world japanese sample |
1294 // +----------------------------------+
1295
1296 static const uint8_t iso2022_cstr_real_ir13[] = {
1297 0xd4, 0xcf, 0xc0, 0xde, 0x5e, 0xc0, 0xdb, 0xb3,
1298 0x3d, 0x1b, 0x24, 0x42, 0x3b, 0x33, 0x45, 0x44,
1299 0x1b, 0x28, 0x4a, 0x5e, 0x1b, 0x24, 0x42, 0x42,
1300 0x40, 0x4f, 0x3a, 0x1b, 0x28, 0x4a, 0x3d, 0x1b,
1301 0x24, 0x42, 0x24, 0x64, 0x24, 0x5e, 0x24, 0x40,
1302 0x1b, 0x28, 0x4a, 0x5e, 0x1b, 0x24, 0x42, 0x24,
1303 0x3f, 0x24, 0x6d, 0x24, 0x26, 0x1b, 0x28, 0x4a, 0x00
1304 };
1305
1306 static const uint8_t iso2022_cstr_real_ir13_ref[] = {
1307 0xd4, 0xcf, 0xc0, 0xde, 0x5e, 0xc0, 0xdb, 0xb3,
1308 0x3d,
1309 0x3b, 0x33, 0x45, 0x44,
1310 0x5e,
1311 0x42,
1312 0x40, 0x4f, 0x3a,
1313 0x3d,
1314 0x24, 0x64, 0x24, 0x5e, 0x24, 0x40,
1315 0x5e,
1316 0x24,
1317 0x3f, 0x24, 0x6d, 0x24, 0x26, 0x00
1318 };
1319
1320
1321
1322 // +----------------------------------+
1323 // | the actual test |
1324 // +----------------------------------+
1325
1326 std::string iso2022_str_oneByteControl(
1327 reinterpret_cast<const char*>(iso2022_cstr_oneByteControl));
1328 std::string iso2022_str_oneByteControl_ref(
1329 reinterpret_cast<const char*>(iso2022_cstr_oneByteControl_ref));
1330 std::string iso2022_str_twoByteControl(
1331 reinterpret_cast<const char*>(iso2022_cstr_twoByteControl));
1332 std::string iso2022_str_twoByteControl_ref(
1333 reinterpret_cast<const char*>(iso2022_cstr_twoByteControl_ref));
1334 std::string iso2022_str_escapeSequence(
1335 reinterpret_cast<const char*>(iso2022_cstr_escapeSequence));
1336 std::string iso2022_str_escapeSequence_ref(
1337 reinterpret_cast<const char*>(iso2022_cstr_escapeSequence_ref));
1338 std::string iso2022_str_real_ir13(
1339 reinterpret_cast<const char*>(iso2022_cstr_real_ir13));
1340 std::string iso2022_str_real_ir13_ref(
1341 reinterpret_cast<const char*>(iso2022_cstr_real_ir13_ref));
1342
1343 std::string dest;
1344
1345 Toolbox::RemoveIso2022EscapeSequences(dest, iso2022_str_oneByteControl);
1346 ASSERT_EQ(dest, iso2022_str_oneByteControl_ref);
1347
1348 Toolbox::RemoveIso2022EscapeSequences(dest, iso2022_str_twoByteControl);
1349 ASSERT_EQ(dest, iso2022_str_twoByteControl_ref);
1350
1351 Toolbox::RemoveIso2022EscapeSequences(dest, iso2022_str_escapeSequence);
1352 ASSERT_EQ(dest, iso2022_str_escapeSequence_ref);
1353
1354 Toolbox::RemoveIso2022EscapeSequences(dest, iso2022_str_real_ir13);
1355 ASSERT_EQ(dest, iso2022_str_real_ir13_ref);
1356 }
1357
1358
1359
1360 static std::string DecodeFromSpecification(const std::string& s)
1361 {
1362 std::vector<std::string> tokens;
1363 Toolbox::TokenizeString(tokens, s, ' ');
1364
1365 std::string result;
1366 result.resize(tokens.size());
1367
1368 for (size_t i = 0; i < tokens.size(); i++)
1369 {
1370 std::vector<std::string> components;
1371 Toolbox::TokenizeString(components, tokens[i], '/');
1372
1373 if (components.size() != 2)
1374 {
1375 throw;
1376 }
1377
1378 int a = boost::lexical_cast<int>(components[0]);
1379 int b = boost::lexical_cast<int>(components[1]);
1380 if (a < 0 || a > 15 ||
1381 b < 0 || b > 15 ||
1382 (a == 0 && b == 0))
1383 {
1384 throw;
1385 }
1386
1387 result[i] = static_cast<uint8_t>(a * 16 + b);
1388 }
1389
1390 return result;
1391 }
1392
1393
1394
1395 // Compatibility wrapper
1396 static pugi::xpath_node SelectNode(const pugi::xml_document& doc,
1397 const char* xpath)
1398 {
1399 #if PUGIXML_VERSION <= 140
1400 return doc.select_single_node(xpath); // Deprecated in pugixml 1.5
1401 #else
1402 return doc.select_node(xpath);
1403 #endif
1404 }
1405
1406
1407 TEST(Toolbox, EncodingsKorean)
1408 {
1409 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_I.2.html
1410
1411 std::string korean = DecodeFromSpecification(
1412 "04/08 06/15 06/14 06/07 05/14 04/07 06/09 06/12 06/04 06/15 06/14 06/07 03/13 "
1413 "01/11 02/04 02/09 04/03 15/11 15/03 05/14 01/11 02/04 02/09 04/03 13/01 12/14 "
1414 "13/04 13/07 03/13 01/11 02/04 02/09 04/03 12/08 10/11 05/14 01/11 02/04 02/09 "
1415 "04/03 11/01 14/06 11/05 11/15");
1416
1417 // This array can be re-generated using command-line:
1418 // echo -n "Hong^Gildong=..." | hexdump -v -e '14/1 "0x%02x, "' -e '"\n"'
1419 static const uint8_t utf8raw[] = {
1420 0x48, 0x6f, 0x6e, 0x67, 0x5e, 0x47, 0x69, 0x6c, 0x64, 0x6f, 0x6e, 0x67, 0x3d, 0xe6,
1421 0xb4, 0xaa, 0x5e, 0xe5, 0x90, 0x89, 0xe6, 0xb4, 0x9e, 0x3d, 0xed, 0x99, 0x8d, 0x5e,
1422 0xea, 0xb8, 0xb8, 0xeb, 0x8f, 0x99
1423 };
1424
1425 std::string utf8(reinterpret_cast<const char*>(utf8raw), sizeof(utf8raw));
1426
1427 ParsedDicomFile dicom(false);
1428 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "\\ISO 2022 IR 149");
1429 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1430 (DCM_PatientName, korean.c_str(), OFBool(true)).good());
1431
1432 bool hasCodeExtensions;
1433 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1434 ASSERT_EQ(Encoding_Korean, encoding);
1435 ASSERT_TRUE(hasCodeExtensions);
1436
1437 std::string value;
1438 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_NAME));
1439 ASSERT_EQ(utf8, value);
1440
1441 DicomWebJsonVisitor visitor;
1442 dicom.Apply(visitor);
1443 ASSERT_EQ(utf8.substr(0, 12), visitor.GetResult()["00100010"]["Value"][0]["Alphabetic"].asString());
1444 ASSERT_EQ(utf8.substr(13, 10), visitor.GetResult()["00100010"]["Value"][0]["Ideographic"].asString());
1445 ASSERT_EQ(utf8.substr(24), visitor.GetResult()["00100010"]["Value"][0]["Phonetic"].asString());
1446
1447 #if ORTHANC_ENABLE_PUGIXML == 1
1448 // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.3.html#table_F.3.1-1
1449 std::string xml;
1450 visitor.FormatXml(xml);
1451
1452 pugi::xml_document doc;
1453 doc.load_buffer(xml.c_str(), xml.size());
1454
1455 pugi::xpath_node node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00080005\"]/Value");
1456 ASSERT_STREQ("ISO 2022 IR 149", node.node().text().as_string());
1457
1458 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00080005\"]");
1459 ASSERT_STREQ("CS", node.node().attribute("vr").value());
1460
1461 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]");
1462 ASSERT_STREQ("PN", node.node().attribute("vr").value());
1463
1464 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Alphabetic/FamilyName");
1465 ASSERT_STREQ("Hong", node.node().text().as_string());
1466
1467 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Alphabetic/GivenName");
1468 ASSERT_STREQ("Gildong", node.node().text().as_string());
1469
1470 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Ideographic/FamilyName");
1471 ASSERT_EQ(utf8.substr(13, 3), node.node().text().as_string());
1472
1473 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Ideographic/GivenName");
1474 ASSERT_EQ(utf8.substr(17, 6), node.node().text().as_string());
1475
1476 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/FamilyName");
1477 ASSERT_EQ(utf8.substr(24, 3), node.node().text().as_string());
1478
1479 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/GivenName");
1480 ASSERT_EQ(utf8.substr(28), node.node().text().as_string());
1481 #endif
1482
1483 {
1484 DicomMap m;
1485 m.FromDicomWeb(visitor.GetResult());
1486 ASSERT_EQ(2u, m.GetSize());
1487
1488 std::string s;
1489 ASSERT_TRUE(m.LookupStringValue(s, DICOM_TAG_SPECIFIC_CHARACTER_SET, false));
1490 ASSERT_EQ("ISO 2022 IR 149", s);
1491
1492 ASSERT_TRUE(m.LookupStringValue(s, DICOM_TAG_PATIENT_NAME, false));
1493 std::vector<std::string> v;
1494 Toolbox::TokenizeString(v, s, '=');
1495 ASSERT_EQ(3u, v.size());
1496 ASSERT_EQ("Hong^Gildong", v[0]);
1497 ASSERT_EQ(utf8, s);
1498 }
1499 }
1500
1501
1502 TEST(Toolbox, EncodingsJapaneseKanji)
1503 {
1504 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_H.3.html
1505
1506 std::string japanese = DecodeFromSpecification(
1507 "05/09 06/01 06/13 06/01 06/04 06/01 05/14 05/04 06/01 07/02 06/15 07/05 03/13 "
1508 "01/11 02/04 04/02 03/11 03/03 04/05 04/04 01/11 02/08 04/02 05/14 01/11 02/04 "
1509 "04/02 04/02 04/00 04/15 03/10 01/11 02/08 04/02 03/13 01/11 02/04 04/02 02/04 "
1510 "06/04 02/04 05/14 02/04 04/00 01/11 02/08 04/02 05/14 01/11 02/04 04/02 02/04 "
1511 "03/15 02/04 06/13 02/04 02/06 01/11 02/08 04/02");
1512
1513 // This array can be re-generated using command-line:
1514 // echo -n "Yamada^Tarou=..." | hexdump -v -e '14/1 "0x%02x, "' -e '"\n"'
1515 static const uint8_t utf8raw[] = {
1516 0x59, 0x61, 0x6d, 0x61, 0x64, 0x61, 0x5e, 0x54, 0x61, 0x72, 0x6f, 0x75, 0x3d, 0xe5,
1517 0xb1, 0xb1, 0xe7, 0x94, 0xb0, 0x5e, 0xe5, 0xa4, 0xaa, 0xe9, 0x83, 0x8e, 0x3d, 0xe3,
1518 0x82, 0x84, 0xe3, 0x81, 0xbe, 0xe3, 0x81, 0xa0, 0x5e, 0xe3, 0x81, 0x9f, 0xe3, 0x82,
1519 0x8d, 0xe3, 0x81, 0x86
1520 };
1521
1522 std::string utf8(reinterpret_cast<const char*>(utf8raw), sizeof(utf8raw));
1523
1524 ParsedDicomFile dicom(false);
1525 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "\\ISO 2022 IR 87");
1526 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1527 (DCM_PatientName, japanese.c_str(), OFBool(true)).good());
1528
1529 bool hasCodeExtensions;
1530 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1531 ASSERT_EQ(Encoding_JapaneseKanji, encoding);
1532 ASSERT_TRUE(hasCodeExtensions);
1533
1534 std::string value;
1535 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_NAME));
1536 ASSERT_EQ(utf8, value);
1537
1538 DicomWebJsonVisitor visitor;
1539 dicom.Apply(visitor);
1540 ASSERT_EQ(utf8.substr(0, 12), visitor.GetResult()["00100010"]["Value"][0]["Alphabetic"].asString());
1541 ASSERT_EQ(utf8.substr(13, 13), visitor.GetResult()["00100010"]["Value"][0]["Ideographic"].asString());
1542 ASSERT_EQ(utf8.substr(27), visitor.GetResult()["00100010"]["Value"][0]["Phonetic"].asString());
1543
1544 #if ORTHANC_ENABLE_PUGIXML == 1
1545 // http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.3.html#table_F.3.1-1
1546 std::string xml;
1547 visitor.FormatXml(xml);
1548
1549 pugi::xml_document doc;
1550 doc.load_buffer(xml.c_str(), xml.size());
1551
1552 pugi::xpath_node node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00080005\"]/Value");
1553 ASSERT_STREQ("ISO 2022 IR 87", node.node().text().as_string());
1554
1555 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00080005\"]");
1556 ASSERT_STREQ("CS", node.node().attribute("vr").value());
1557
1558 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]");
1559 ASSERT_STREQ("PN", node.node().attribute("vr").value());
1560
1561 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Alphabetic/FamilyName");
1562 ASSERT_STREQ("Yamada", node.node().text().as_string());
1563
1564 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Alphabetic/GivenName");
1565 ASSERT_STREQ("Tarou", node.node().text().as_string());
1566
1567 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Ideographic/FamilyName");
1568 ASSERT_EQ(utf8.substr(13, 6), node.node().text().as_string());
1569
1570 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Ideographic/GivenName");
1571 ASSERT_EQ(utf8.substr(20, 6), node.node().text().as_string());
1572
1573 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/FamilyName");
1574 ASSERT_EQ(utf8.substr(27, 9), node.node().text().as_string());
1575
1576 node = SelectNode(doc, "//NativeDicomModel/DicomAttribute[@tag=\"00100010\"]/PersonName/Phonetic/GivenName");
1577 ASSERT_EQ(utf8.substr(37), node.node().text().as_string());
1578 #endif
1579
1580 {
1581 DicomMap m;
1582 m.FromDicomWeb(visitor.GetResult());
1583 ASSERT_EQ(2u, m.GetSize());
1584
1585 std::string s;
1586 ASSERT_TRUE(m.LookupStringValue(s, DICOM_TAG_SPECIFIC_CHARACTER_SET, false));
1587 ASSERT_EQ("ISO 2022 IR 87", s);
1588
1589 ASSERT_TRUE(m.LookupStringValue(s, DICOM_TAG_PATIENT_NAME, false));
1590 std::vector<std::string> v;
1591 Toolbox::TokenizeString(v, s, '=');
1592 ASSERT_EQ(3u, v.size());
1593 ASSERT_EQ("Yamada^Tarou", v[0]);
1594 ASSERT_EQ(utf8, s);
1595 }
1596 }
1597
1598
1599
1600 TEST(Toolbox, EncodingsChinese3)
1601 {
1602 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_J.3.html
1603
1604 static const uint8_t chinese[] = {
1605 0x57, 0x61, 0x6e, 0x67, 0x5e, 0x58, 0x69, 0x61, 0x6f, 0x44, 0x6f,
1606 0x6e, 0x67, 0x3d, 0xcd, 0xf5, 0x5e, 0xd0, 0xa1, 0xb6, 0xab, 0x3d, 0x00
1607 };
1608
1609 ParsedDicomFile dicom(false);
1610 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "GB18030");
1611 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1612 (DCM_PatientName, reinterpret_cast<const char*>(chinese), OFBool(true)).good());
1613
1614 bool hasCodeExtensions;
1615 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1616 ASSERT_EQ(Encoding_Chinese, encoding);
1617 ASSERT_FALSE(hasCodeExtensions);
1618
1619 std::string value;
1620 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_NAME));
1621
1622 std::vector<std::string> tokens;
1623 Orthanc::Toolbox::TokenizeString(tokens, value, '=');
1624 ASSERT_EQ(3u, tokens.size());
1625 ASSERT_EQ("Wang^XiaoDong", tokens[0]);
1626 ASSERT_TRUE(tokens[2].empty());
1627
1628 std::vector<std::string> middle;
1629 Orthanc::Toolbox::TokenizeString(middle, tokens[1], '^');
1630 ASSERT_EQ(2u, middle.size());
1631 ASSERT_EQ(3u, middle[0].size());
1632 ASSERT_EQ(6u, middle[1].size());
1633
1634 // CDF5 in GB18030
1635 ASSERT_EQ(static_cast<char>(0xe7), middle[0][0]);
1636 ASSERT_EQ(static_cast<char>(0x8e), middle[0][1]);
1637 ASSERT_EQ(static_cast<char>(0x8b), middle[0][2]);
1638
1639 // D0A1 in GB18030
1640 ASSERT_EQ(static_cast<char>(0xe5), middle[1][0]);
1641 ASSERT_EQ(static_cast<char>(0xb0), middle[1][1]);
1642 ASSERT_EQ(static_cast<char>(0x8f), middle[1][2]);
1643
1644 // B6AB in GB18030
1645 ASSERT_EQ(static_cast<char>(0xe4), middle[1][3]);
1646 ASSERT_EQ(static_cast<char>(0xb8), middle[1][4]);
1647 ASSERT_EQ(static_cast<char>(0x9c), middle[1][5]);
1648 }
1649
1650
1651 TEST(Toolbox, EncodingsChinese4)
1652 {
1653 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_J.4.html
1654
1655 static const uint8_t chinese[] = {
1656 0x54, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6c, 0x69, 0x6e,
1657 0x65, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0xd6, 0xd0, 0xce,
1658 0xc4, 0x2e, 0x0d, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e,
1659 0x64, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64,
1660 0x65, 0x73, 0xd6, 0xd0, 0xce, 0xc4, 0x2c, 0x20, 0x74, 0x6f, 0x6f, 0x2e, 0x0d,
1661 0x0a, 0x54, 0x68, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0x20, 0x6c, 0x69,
1662 0x6e, 0x65, 0x2e, 0x0d, 0x0a, 0x00
1663 };
1664
1665 static const uint8_t patternRaw[] = {
1666 0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87
1667 };
1668
1669 const std::string pattern(reinterpret_cast<const char*>(patternRaw), sizeof(patternRaw));
1670
1671 ParsedDicomFile dicom(false);
1672 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "GB18030");
1673 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1674 (DCM_PatientComments, reinterpret_cast<const char*>(chinese), OFBool(true)).good());
1675
1676 bool hasCodeExtensions;
1677 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1678 ASSERT_EQ(Encoding_Chinese, encoding);
1679 ASSERT_FALSE(hasCodeExtensions);
1680
1681 std::string value;
1682 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_COMMENTS));
1683
1684 std::vector<std::string> lines;
1685 Orthanc::Toolbox::TokenizeString(lines, value, '\n');
1686 ASSERT_EQ(4u, lines.size());
1687 ASSERT_TRUE(boost::starts_with(lines[0], "The first line includes"));
1688 ASSERT_TRUE(boost::ends_with(lines[0], ".\r"));
1689 ASSERT_TRUE(lines[0].find(pattern) != std::string::npos);
1690 ASSERT_TRUE(boost::starts_with(lines[1], "The second line includes"));
1691 ASSERT_TRUE(boost::ends_with(lines[1], ", too.\r"));
1692 ASSERT_TRUE(lines[1].find(pattern) != std::string::npos);
1693 ASSERT_EQ("The third line.\r", lines[2]);
1694 ASSERT_FALSE(lines[1].find(pattern) == std::string::npos);
1695 ASSERT_TRUE(lines[3].empty());
1696 }
1697
1698
1699 TEST(Toolbox, EncodingsSimplifiedChinese2)
1700 {
1701 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_K.2.html
1702
1703 static const uint8_t chinese[] = {
1704 0x5a, 0x68, 0x61, 0x6e, 0x67, 0x5e, 0x58, 0x69, 0x61, 0x6f, 0x44, 0x6f,
1705 0x6e, 0x67, 0x3d, 0x1b, 0x24, 0x29, 0x41, 0xd5, 0xc5, 0x5e, 0x1b, 0x24,
1706 0x29, 0x41, 0xd0, 0xa1, 0xb6, 0xab, 0x3d, 0x20, 0x00
1707 };
1708
1709 // echo -n "Zhang^XiaoDong=..." | hexdump -v -e '14/1 "0x%02x, "' -e '"\n"'
1710 static const uint8_t utf8[] = {
1711 0x5a, 0x68, 0x61, 0x6e, 0x67, 0x5e, 0x58, 0x69, 0x61, 0x6f, 0x44, 0x6f, 0x6e, 0x67,
1712 0x3d, 0xe5, 0xbc, 0xa0, 0x5e, 0xe5, 0xb0, 0x8f, 0xe4, 0xb8, 0x9c, 0x3d
1713 };
1714
1715 ParsedDicomFile dicom(false);
1716 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "\\ISO 2022 IR 58");
1717 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1718 (DCM_PatientName, reinterpret_cast<const char*>(chinese), OFBool(true)).good());
1719
1720 bool hasCodeExtensions;
1721 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1722 ASSERT_EQ(Encoding_SimplifiedChinese, encoding);
1723 ASSERT_TRUE(hasCodeExtensions);
1724
1725 std::string value;
1726 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_NAME));
1727 ASSERT_EQ(value, std::string(reinterpret_cast<const char*>(utf8), sizeof(utf8)));
1728 }
1729
1730
1731 TEST(Toolbox, EncodingsSimplifiedChinese3)
1732 {
1733 // http://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part05/sect_K.2.html
1734
1735 static const uint8_t chinese[] = {
1736 0x31, 0x2e, 0x1b, 0x24, 0x29, 0x41, 0xb5, 0xda, 0xd2, 0xbb, 0xd0, 0xd0, 0xce, 0xc4, 0xd7, 0xd6, 0xa1, 0xa3, 0x0d, 0x0a,
1737 0x32, 0x2e, 0x1b, 0x24, 0x29, 0x41, 0xb5, 0xda, 0xb6, 0xfe, 0xd0, 0xd0, 0xce, 0xc4, 0xd7, 0xd6, 0xa1, 0xa3, 0x0d, 0x0a,
1738 0x33, 0x2e, 0x1b, 0x24, 0x29, 0x41, 0xb5, 0xda, 0xc8, 0xfd, 0xd0, 0xd0, 0xce, 0xc4, 0xd7, 0xd6, 0xa1, 0xa3, 0x0d, 0x0a, 0x00
1739 };
1740
1741 static const uint8_t line1[] = {
1742 0x31, 0x2e, 0xe7, 0xac, 0xac, 0xe4, 0xb8, 0x80, 0xe8, 0xa1, 0x8c, 0xe6, 0x96, 0x87,
1743 0xe5, 0xad, 0x97, 0xe3, 0x80, 0x82, '\r'
1744 };
1745
1746 static const uint8_t line2[] = {
1747 0x32, 0x2e, 0xe7, 0xac, 0xac, 0xe4, 0xba, 0x8c, 0xe8, 0xa1, 0x8c, 0xe6, 0x96, 0x87,
1748 0xe5, 0xad, 0x97, 0xe3, 0x80, 0x82, '\r'
1749 };
1750
1751 static const uint8_t line3[] = {
1752 0x33, 0x2e, 0xe7, 0xac, 0xac, 0xe4, 0xb8, 0x89, 0xe8, 0xa1, 0x8c, 0xe6, 0x96, 0x87,
1753 0xe5, 0xad, 0x97, 0xe3, 0x80, 0x82, '\r'
1754 };
1755
1756 ParsedDicomFile dicom(false);
1757 dicom.ReplacePlainString(DICOM_TAG_SPECIFIC_CHARACTER_SET, "\\ISO 2022 IR 58");
1758 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->putAndInsertString
1759 (DCM_PatientName, reinterpret_cast<const char*>(chinese), OFBool(true)).good());
1760
1761 bool hasCodeExtensions;
1762 Encoding encoding = dicom.DetectEncoding(hasCodeExtensions);
1763 ASSERT_EQ(Encoding_SimplifiedChinese, encoding);
1764 ASSERT_TRUE(hasCodeExtensions);
1765
1766 std::string value;
1767 ASSERT_TRUE(dicom.GetTagValue(value, DICOM_TAG_PATIENT_NAME));
1768
1769 std::vector<std::string> lines;
1770 Toolbox::TokenizeString(lines, value, '\n');
1771 ASSERT_EQ(4u, lines.size());
1772 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line1), sizeof(line1)), lines[0]);
1773 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line2), sizeof(line2)), lines[1]);
1774 ASSERT_EQ(std::string(reinterpret_cast<const char*>(line3), sizeof(line3)), lines[2]);
1775 ASSERT_TRUE(lines[3].empty());
1776 }
1777
1778
1779
1780
1781 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
1782
1783 #include "../Sources/DicomNetworking/DicomStoreUserConnection.h"
1784 #include "../Sources/DicomParsing/DcmtkTranscoder.h"
1785
1786 TEST(Toto, DISABLED_Transcode3)
1787 {
1788 DicomAssociationParameters p;
1789 p.SetRemotePort(2000);
1790
1791 DicomStoreUserConnection scu(p);
1792 scu.SetCommonClassesProposed(false);
1793 scu.SetRetiredBigEndianProposed(true);
1794
1795 DcmtkTranscoder transcoder;
1796
1797 for (int j = 0; j < 2; j++)
1798 for (int i = 0; i <= DicomTransferSyntax_XML; i++)
1799 {
1800 DicomTransferSyntax a = (DicomTransferSyntax) i;
1801
1802 std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" +
1803 std::string(GetTransferSyntaxUid(a)) + ".dcm");
1804 if (Orthanc::SystemToolbox::IsRegularFile(path))
1805 {
1806 printf("\n======= %s\n", GetTransferSyntaxUid(a));
1807
1808 std::string source;
1809 Orthanc::SystemToolbox::ReadFile(source, path);
1810
1811 std::string c, i;
1812 try
1813 {
1814 scu.Transcode(c, i, transcoder, source.c_str(), source.size(), false, "", 0);
1815 }
1816 catch (OrthancException& e)
1817 {
1818 if (e.GetErrorCode() == ErrorCode_NotImplemented)
1819 {
1820 LOG(ERROR) << "cannot transcode " << GetTransferSyntaxUid(a);
1821 }
1822 else
1823 {
1824 throw e;
1825 }
1826 }
1827 }
1828 }
1829 }
1830
1831
1832 TEST(Toto, DISABLED_Transcode4)
1833 {
1834 std::string source;
1835 Orthanc::SystemToolbox::ReadFile(source, "/home/jodogne/Subversion/orthanc-tests/Database/KarstenHilbertRF.dcm");
1836
1837 std::unique_ptr<DcmFileFormat> toto(FromDcmtkBridge::LoadFromMemoryBuffer(source.c_str(), source.size()));
1838 const std::string sourceUid = IDicomTranscoder::GetSopInstanceUid(*toto);
1839
1840 DicomTransferSyntax sourceSyntax;
1841 ASSERT_TRUE(FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, *toto));
1842
1843 DcmtkTranscoder transcoder;
1844
1845 for (int i = 0; i <= DicomTransferSyntax_XML; i++)
1846 {
1847 DicomTransferSyntax a = (DicomTransferSyntax) i;
1848
1849 std::set<DicomTransferSyntax> s;
1850 s.insert(a);
1851
1852 std::string t;
1853
1854 IDicomTranscoder::DicomImage source, target;
1855 source.AcquireParsed(dynamic_cast<DcmFileFormat*>(toto->clone()));
1856
1857 if (!transcoder.Transcode(target, source, s, true))
1858 {
1859 printf("**************** CANNOT: [%s] => [%s]\n",
1860 GetTransferSyntaxUid(sourceSyntax), GetTransferSyntaxUid(a));
1861 }
1862 else
1863 {
1864 DicomTransferSyntax targetSyntax;
1865 ASSERT_TRUE(FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, target.GetParsed()));
1866
1867 ASSERT_EQ(targetSyntax, a);
1868 bool lossy = (a == DicomTransferSyntax_JPEGProcess1 ||
1869 a == DicomTransferSyntax_JPEGProcess2_4 ||
1870 a == DicomTransferSyntax_JPEGLSLossy);
1871
1872 printf("SIZE: %lu\n", t.size());
1873 if (sourceUid == IDicomTranscoder::GetSopInstanceUid(target.GetParsed()))
1874 {
1875 ASSERT_FALSE(lossy);
1876 }
1877 else
1878 {
1879 ASSERT_TRUE(lossy);
1880 }
1881 }
1882 }
1883 }
1884
1885 #endif