Mercurial > hg > orthanc
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 |