comparison UnitTestsSources/FromDcmtkTests.cpp @ 2884:497a637366b4 db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 12 Oct 2018 15:18:10 +0200
parents 9b4251721f22
children 8b331be57606
comparison
equal deleted inserted replaced
1762:2b91363cc1d1 2884:497a637366b4
1 /** 1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store 2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium 4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium
5 * 6 *
6 * This program is free software: you can redistribute it and/or 7 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as 8 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the 9 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version. 10 * License, or (at your option) any later version.
31 32
32 33
33 #include "PrecompiledHeadersUnitTests.h" 34 #include "PrecompiledHeadersUnitTests.h"
34 #include "gtest/gtest.h" 35 #include "gtest/gtest.h"
35 36
36 #include "../OrthancServer/FromDcmtkBridge.h" 37 #include "../Core/DicomParsing/FromDcmtkBridge.h"
37 #include "../OrthancServer/OrthancInitialization.h" 38 #include "../Core/DicomParsing/ToDcmtkBridge.h"
38 #include "../OrthancServer/DicomModification.h" 39 #include "../Core/DicomParsing/DicomModification.h"
39 #include "../OrthancServer/ServerToolbox.h" 40 #include "../OrthancServer/ServerToolbox.h"
40 #include "../Core/OrthancException.h" 41 #include "../Core/OrthancException.h"
41 #include "../Core/Images/ImageBuffer.h" 42 #include "../Core/Images/ImageBuffer.h"
42 #include "../Core/Images/PngReader.h" 43 #include "../Core/Images/PngReader.h"
43 #include "../Core/Images/PngWriter.h" 44 #include "../Core/Images/PngWriter.h"
44 #include "../Core/Uuid.h" 45 #include "../Core/Images/Image.h"
46 #include "../Core/Images/ImageProcessing.h"
47 #include "../Core/Endianness.h"
45 #include "../Resources/EncodingTests.h" 48 #include "../Resources/EncodingTests.h"
49 #include "../Core/DicomNetworking/DicomFindAnswers.h"
50 #include "../Core/DicomParsing/Internals/DicomImageDecoder.h"
51 #include "../Plugins/Engine/PluginsEnumerations.h"
46 52
47 #include <dcmtk/dcmdata/dcelem.h> 53 #include <dcmtk/dcmdata/dcelem.h>
54 #include <dcmtk/dcmdata/dcdeftag.h>
48 55
49 using namespace Orthanc; 56 using namespace Orthanc;
50 57
51 TEST(DicomFormat, Tag) 58 TEST(DicomFormat, Tag)
52 { 59 {
53 ASSERT_EQ("PatientName", FromDcmtkBridge::GetName(DicomTag(0x0010, 0x0010))); 60 ASSERT_EQ("PatientName", FromDcmtkBridge::GetTagName(DicomTag(0x0010, 0x0010), ""));
54 61
55 DicomTag t = FromDcmtkBridge::ParseTag("SeriesDescription"); 62 DicomTag t = FromDcmtkBridge::ParseTag("SeriesDescription");
56 ASSERT_EQ(0x0008, t.GetGroup()); 63 ASSERT_EQ(0x0008, t.GetGroup());
57 ASSERT_EQ(0x103E, t.GetElement()); 64 ASSERT_EQ(0x103E, t.GetElement());
58 65
67 74
68 75
69 TEST(DicomModification, Basic) 76 TEST(DicomModification, Basic)
70 { 77 {
71 DicomModification m; 78 DicomModification m;
72 m.SetupAnonymization(); 79 m.SetupAnonymization(DicomVersion_2008);
73 //m.SetLevel(DicomRootLevel_Study); 80 //m.SetLevel(DicomRootLevel_Study);
74 //m.Replace(DICOM_TAG_PATIENT_ID, "coucou"); 81 //m.ReplacePlainString(DICOM_TAG_PATIENT_ID, "coucou");
75 //m.Replace(DICOM_TAG_PATIENT_NAME, "coucou"); 82 //m.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
76 83
77 ParsedDicomFile o; 84 ParsedDicomFile o(true);
78 o.SaveToFile("UnitTestsResults/anon.dcm"); 85 o.SaveToFile("UnitTestsResults/anon.dcm");
79 86
80 for (int i = 0; i < 10; i++) 87 for (int i = 0; i < 10; i++)
81 { 88 {
82 char b[1024]; 89 char b[1024];
83 sprintf(b, "UnitTestsResults/anon%06d.dcm", i); 90 sprintf(b, "UnitTestsResults/anon%06d.dcm", i);
84 std::auto_ptr<ParsedDicomFile> f(o.Clone()); 91 std::auto_ptr<ParsedDicomFile> f(o.Clone(false));
85 if (i > 4) 92 if (i > 4)
86 o.Replace(DICOM_TAG_SERIES_INSTANCE_UID, "coucou"); 93 o.ReplacePlainString(DICOM_TAG_SERIES_INSTANCE_UID, "coucou");
87 m.Apply(*f); 94 m.Apply(*f);
88 f->SaveToFile(b); 95 f->SaveToFile(b);
89 } 96 }
90 } 97 }
91 98
94 { 101 {
95 ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName")); 102 ASSERT_EQ(DICOM_TAG_PATIENT_NAME, FromDcmtkBridge::ParseTag("PatientName"));
96 103
97 const DicomTag privateTag(0x0045, 0x0010); 104 const DicomTag privateTag(0x0045, 0x0010);
98 const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020")); 105 const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020"));
99 ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag)); 106 ASSERT_TRUE(privateTag.IsPrivate());
100 ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag2)); 107 ASSERT_TRUE(privateTag2.IsPrivate());
101 ASSERT_EQ(0x0031, privateTag2.GetGroup()); 108 ASSERT_EQ(0x0031, privateTag2.GetGroup());
102 ASSERT_EQ(0x1020, privateTag2.GetElement()); 109 ASSERT_EQ(0x1020, privateTag2.GetElement());
103 110
104 std::string s; 111 std::string s;
105 ParsedDicomFile o; 112 ParsedDicomFile o(true);
106 o.Replace(DICOM_TAG_PATIENT_NAME, "coucou"); 113 o.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou");
107 ASSERT_FALSE(o.GetTagValue(s, privateTag)); 114 ASSERT_FALSE(o.GetTagValue(s, privateTag));
108 o.Insert(privateTag, "private tag", false); 115 o.Insert(privateTag, "private tag", false);
109 ASSERT_TRUE(o.GetTagValue(s, privateTag)); 116 ASSERT_TRUE(o.GetTagValue(s, privateTag));
110 ASSERT_STREQ("private tag", s.c_str()); 117 ASSERT_STREQ("private tag", s.c_str());
111 118
112 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); 119 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
113 ASSERT_THROW(o.Replace(privateTag2, "hello", DicomReplaceMode_ThrowIfAbsent), OrthancException); 120 ASSERT_THROW(o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_ThrowIfAbsent), OrthancException);
114 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); 121 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
115 o.Replace(privateTag2, "hello", DicomReplaceMode_IgnoreIfAbsent); 122 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_IgnoreIfAbsent);
116 ASSERT_FALSE(o.GetTagValue(s, privateTag2)); 123 ASSERT_FALSE(o.GetTagValue(s, privateTag2));
117 o.Replace(privateTag2, "hello", DicomReplaceMode_InsertIfAbsent); 124 o.Replace(privateTag2, std::string("hello"), false, DicomReplaceMode_InsertIfAbsent);
118 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); 125 ASSERT_TRUE(o.GetTagValue(s, privateTag2));
119 ASSERT_STREQ("hello", s.c_str()); 126 ASSERT_STREQ("hello", s.c_str());
120 o.Replace(privateTag2, "hello world"); 127 o.ReplacePlainString(privateTag2, "hello world");
121 ASSERT_TRUE(o.GetTagValue(s, privateTag2)); 128 ASSERT_TRUE(o.GetTagValue(s, privateTag2));
122 ASSERT_STREQ("hello world", s.c_str()); 129 ASSERT_STREQ("hello world", s.c_str());
123 130
124 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); 131 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
125 ASSERT_FALSE(Toolbox::IsUuid(s)); 132 ASSERT_FALSE(Toolbox::IsUuid(s));
126 133
127 DicomModification m; 134 DicomModification m;
128 m.SetupAnonymization(); 135 m.SetupAnonymization(DicomVersion_2008);
129 m.Keep(privateTag); 136 m.Keep(privateTag);
130 137
131 m.Apply(o); 138 m.Apply(o);
132 139
133 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); 140 ASSERT_TRUE(o.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
134 ASSERT_TRUE(Toolbox::IsUuid(s)); 141 ASSERT_TRUE(Toolbox::IsUuid(s));
135 ASSERT_TRUE(o.GetTagValue(s, privateTag)); 142 ASSERT_TRUE(o.GetTagValue(s, privateTag));
136 ASSERT_STREQ("private tag", s.c_str()); 143 ASSERT_STREQ("private tag", s.c_str());
137 144
138 m.SetupAnonymization(); 145 m.SetupAnonymization(DicomVersion_2008);
139 m.Apply(o); 146 m.Apply(o);
140 ASSERT_FALSE(o.GetTagValue(s, privateTag)); 147 ASSERT_FALSE(o.GetTagValue(s, privateTag));
141 } 148 }
142 149
143 150
147 { 154 {
148 // Red dot in http://en.wikipedia.org/wiki/Data_URI_scheme (RGBA image) 155 // Red dot in http://en.wikipedia.org/wiki/Data_URI_scheme (RGBA image)
149 std::string s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; 156 std::string s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
150 157
151 std::string m, cc; 158 std::string m, cc;
152 Toolbox::DecodeDataUriScheme(m, cc, s); 159 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(m, cc, s));
153 160
154 ASSERT_EQ("image/png", m); 161 ASSERT_EQ("image/png", m);
155 162
156 PngReader reader; 163 PngReader reader;
157 reader.ReadFromMemory(cc); 164 reader.ReadFromMemory(cc);
158 165
159 ASSERT_EQ(5u, reader.GetHeight()); 166 ASSERT_EQ(5u, reader.GetHeight());
160 ASSERT_EQ(5u, reader.GetWidth()); 167 ASSERT_EQ(5u, reader.GetWidth());
161 ASSERT_EQ(PixelFormat_RGBA32, reader.GetFormat()); 168 ASSERT_EQ(PixelFormat_RGBA32, reader.GetFormat());
162 169
163 ParsedDicomFile o; 170 ParsedDicomFile o(true);
164 o.EmbedContent(s); 171 o.EmbedContent(s);
165 o.SaveToFile("UnitTestsResults/png1.dcm"); 172 o.SaveToFile("UnitTestsResults/png1.dcm");
166 173
167 // Red dot, without alpha channel 174 // Red dot, without alpha channel
168 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDTcIn2+8BgAAACJJREFUCNdj/P//PwMjIwME/P/P+J8BBTAxEOL/R9Lx/z8AynoKAXOeiV8AAAAASUVORK5CYII="; 175 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDTcIn2+8BgAAACJJREFUCNdj/P//PwMjIwME/P/P+J8BBTAxEOL/R9Lx/z8AynoKAXOeiV8AAAAASUVORK5CYII=";
170 o.SaveToFile("UnitTestsResults/png2.dcm"); 177 o.SaveToFile("UnitTestsResults/png2.dcm");
171 178
172 // Check box in Graylevel8 179 // Check box in Graylevel8
173 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="; 180 s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII=";
174 o.EmbedContent(s); 181 o.EmbedContent(s);
175 //o.Replace(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing); 182 //o.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, UID_DigitalXRayImageStorageForProcessing);
176 o.SaveToFile("UnitTestsResults/png3.dcm"); 183 o.SaveToFile("UnitTestsResults/png3.dcm");
177 184
178 185
179 { 186 {
180 // Gradient in Graylevel16 187 // Gradient in Graylevel16
182 ImageBuffer img; 189 ImageBuffer img;
183 img.SetWidth(256); 190 img.SetWidth(256);
184 img.SetHeight(256); 191 img.SetHeight(256);
185 img.SetFormat(PixelFormat_Grayscale16); 192 img.SetFormat(PixelFormat_Grayscale16);
186 193
194 ImageAccessor accessor;
195 img.GetWriteableAccessor(accessor);
196
187 uint16_t v = 0; 197 uint16_t v = 0;
188 for (unsigned int y = 0; y < img.GetHeight(); y++) 198 for (unsigned int y = 0; y < img.GetHeight(); y++)
189 { 199 {
190 uint16_t *p = reinterpret_cast<uint16_t*>(img.GetAccessor().GetRow(y)); 200 uint16_t *p = reinterpret_cast<uint16_t*>(accessor.GetRow(y));
191 for (unsigned int x = 0; x < img.GetWidth(); x++, p++, v++) 201 for (unsigned int x = 0; x < img.GetWidth(); x++, p++, v++)
192 { 202 {
193 *p = v; 203 *p = v;
194 } 204 }
195 } 205 }
196 206
197 o.EmbedImage(img.GetAccessor()); 207 o.EmbedImage(accessor);
198 o.SaveToFile("UnitTestsResults/png4.dcm"); 208 o.SaveToFile("UnitTestsResults/png4.dcm");
199 } 209 }
200 } 210 }
201 211
202 212
213 } 223 }
214 224
215 225
216 TEST(FromDcmtkBridge, Enumerations) 226 TEST(FromDcmtkBridge, Enumerations)
217 { 227 {
228 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.12.1.1.2
218 Encoding e; 229 Encoding e;
219 230
220 ASSERT_FALSE(GetDicomEncoding(e, "")); 231 ASSERT_FALSE(GetDicomEncoding(e, ""));
221 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 6")); ASSERT_EQ(Encoding_Utf8, e); 232 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 6")); ASSERT_EQ(Encoding_Ascii, e);
222 233
223 // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-2 234 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-2
224 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 100")); ASSERT_EQ(Encoding_Latin1, e); 235 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 100")); ASSERT_EQ(Encoding_Latin1, e);
225 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 101")); ASSERT_EQ(Encoding_Latin2, e); 236 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 101")); ASSERT_EQ(Encoding_Latin2, e);
226 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 109")); ASSERT_EQ(Encoding_Latin3, e); 237 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 109")); ASSERT_EQ(Encoding_Latin3, e);
227 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 110")); ASSERT_EQ(Encoding_Latin4, e); 238 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 110")); ASSERT_EQ(Encoding_Latin4, e);
228 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 144")); ASSERT_EQ(Encoding_Cyrillic, e); 239 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 144")); ASSERT_EQ(Encoding_Cyrillic, e);
229 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 127")); ASSERT_EQ(Encoding_Arabic, e); 240 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 127")); ASSERT_EQ(Encoding_Arabic, e);
230 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 126")); ASSERT_EQ(Encoding_Greek, e); 241 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 126")); ASSERT_EQ(Encoding_Greek, e);
231 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 138")); ASSERT_EQ(Encoding_Hebrew, e); 242 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
232 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 148")); ASSERT_EQ(Encoding_Latin5, e); 243 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 148")); ASSERT_EQ(Encoding_Latin5, e);
233 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 13")); ASSERT_EQ(Encoding_Japanese, e); 244 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 13")); ASSERT_EQ(Encoding_Japanese, e);
234 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 166")); ASSERT_EQ(Encoding_Thai, e); 245 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 166")); ASSERT_EQ(Encoding_Thai, e);
235 246
236 // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-3 247 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-3
237 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 6")); ASSERT_EQ(Encoding_Utf8, e); 248 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 6")); ASSERT_EQ(Encoding_Ascii, e);
238 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 100")); ASSERT_EQ(Encoding_Latin1, e); 249 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 100")); ASSERT_EQ(Encoding_Latin1, e);
239 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 101")); ASSERT_EQ(Encoding_Latin2, e); 250 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 101")); ASSERT_EQ(Encoding_Latin2, e);
240 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 109")); ASSERT_EQ(Encoding_Latin3, e); 251 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 109")); ASSERT_EQ(Encoding_Latin3, e);
241 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 110")); ASSERT_EQ(Encoding_Latin4, e); 252 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 110")); ASSERT_EQ(Encoding_Latin4, e);
242 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 144")); ASSERT_EQ(Encoding_Cyrillic, e); 253 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 144")); ASSERT_EQ(Encoding_Cyrillic, e);
243 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 127")); ASSERT_EQ(Encoding_Arabic, e); 254 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 127")); ASSERT_EQ(Encoding_Arabic, e);
244 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 126")); ASSERT_EQ(Encoding_Greek, e); 255 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 126")); ASSERT_EQ(Encoding_Greek, e);
245 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 138")); ASSERT_EQ(Encoding_Hebrew, e); 256 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 138")); ASSERT_EQ(Encoding_Hebrew, e);
246 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 148")); ASSERT_EQ(Encoding_Latin5, e); 257 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 148")); ASSERT_EQ(Encoding_Latin5, e);
247 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 13")); ASSERT_EQ(Encoding_Japanese, e); 258 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 13")); ASSERT_EQ(Encoding_Japanese, e);
248 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 166")); ASSERT_EQ(Encoding_Thai, e); 259 ASSERT_TRUE(GetDicomEncoding(e, "ISO 2022 IR 166")); ASSERT_EQ(Encoding_Thai, e);
249 260
250 // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-4 261 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-4
251 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 87")); //ASSERT_EQ(Encoding_JapaneseKanji, e); 262 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 87")); //ASSERT_EQ(Encoding_JapaneseKanji, e);
252 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 159")); //ASSERT_EQ(Encoding_JapaneseKanjiSupplementary, e); 263 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 159")); //ASSERT_EQ(Encoding_JapaneseKanjiSupplementary, e);
253 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 149")); //ASSERT_EQ(Encoding_Korean, e); 264 ASSERT_FALSE(GetDicomEncoding(e, "ISO 2022 IR 149")); //ASSERT_EQ(Encoding_Korean, e);
254 265
255 // http://www.dabsoft.ch/dicom/3/C.12.1.1.2/ - Table C.12-5 266 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#table_C.12-5
256 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 192")); ASSERT_EQ(Encoding_Utf8, e); 267 ASSERT_TRUE(GetDicomEncoding(e, "ISO_IR 192")); ASSERT_EQ(Encoding_Utf8, e);
257 ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e); 268 ASSERT_TRUE(GetDicomEncoding(e, "GB18030")); ASSERT_EQ(Encoding_Chinese, e);
269 ASSERT_TRUE(GetDicomEncoding(e, "GBK")); ASSERT_EQ(Encoding_Chinese, e);
258 } 270 }
259 271
260 272
261 TEST(FromDcmtkBridge, Encodings3) 273 TEST(FromDcmtkBridge, Encodings3)
262 { 274 {
264 { 276 {
265 //std::cout << EnumerationToString(testEncodings[i]) << std::endl; 277 //std::cout << EnumerationToString(testEncodings[i]) << std::endl;
266 std::string dicom; 278 std::string dicom;
267 279
268 { 280 {
269 ParsedDicomFile f; 281 ParsedDicomFile f(true);
270 f.SetEncoding(testEncodings[i]); 282 f.SetEncoding(testEncodings[i]);
271 283
272 std::string s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i]); 284 std::string s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i]);
273 f.Insert(DICOM_TAG_PATIENT_NAME, s, false); 285 f.Insert(DICOM_TAG_PATIENT_NAME, s, false);
274 f.SaveToMemoryBuffer(dicom); 286 f.SaveToMemoryBuffer(dicom);
291 } 303 }
292 304
293 305
294 TEST(FromDcmtkBridge, ValueRepresentation) 306 TEST(FromDcmtkBridge, ValueRepresentation)
295 { 307 {
296 ASSERT_EQ(ValueRepresentation_PatientName, 308 ASSERT_EQ(ValueRepresentation_PersonName,
297 FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_NAME)); 309 FromDcmtkBridge::LookupValueRepresentation(DICOM_TAG_PATIENT_NAME));
298 ASSERT_EQ(ValueRepresentation_Date, 310 ASSERT_EQ(ValueRepresentation_Date,
299 FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */)); 311 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
300 ASSERT_EQ(ValueRepresentation_Time, 312 ASSERT_EQ(ValueRepresentation_Time,
301 FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */)); 313 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0030) /* StudyTime */));
302 ASSERT_EQ(ValueRepresentation_DateTime, 314 ASSERT_EQ(ValueRepresentation_DateTime,
303 FromDcmtkBridge::GetValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */)); 315 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x002a) /* AcquisitionDateTime */));
304 ASSERT_EQ(ValueRepresentation_Other, 316 ASSERT_EQ(ValueRepresentation_NotSupported,
305 FromDcmtkBridge::GetValueRepresentation(DICOM_TAG_PATIENT_ID)); 317 FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0001, 0x0001) /* some private tag */));
318 }
319
320
321 TEST(FromDcmtkBridge, ValueRepresentationConversions)
322 {
323 ASSERT_EQ(1, ValueRepresentation_ApplicationEntity);
324 ASSERT_EQ(1, OrthancPluginValueRepresentation_AE);
325
326 for (int i = ValueRepresentation_ApplicationEntity;
327 i <= ValueRepresentation_NotSupported; i++)
328 {
329 ValueRepresentation vr = static_cast<ValueRepresentation>(i);
330
331 if (vr == ValueRepresentation_NotSupported)
332 {
333 ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
334 ASSERT_THROW(Plugins::Convert(vr), OrthancException);
335 }
336 else if (vr == ValueRepresentation_OtherDouble ||
337 vr == ValueRepresentation_OtherLong ||
338 vr == ValueRepresentation_UniversalResource ||
339 vr == ValueRepresentation_UnlimitedCharacters)
340 {
341 // These VR are not supported as of DCMTK 3.6.0
342 ASSERT_THROW(ToDcmtkBridge::Convert(vr), OrthancException);
343 ASSERT_EQ(OrthancPluginValueRepresentation_UN, Plugins::Convert(vr));
344 }
345 else
346 {
347 ASSERT_EQ(vr, FromDcmtkBridge::Convert(ToDcmtkBridge::Convert(vr)));
348
349 OrthancPluginValueRepresentation plugins = Plugins::Convert(vr);
350 ASSERT_EQ(vr, Plugins::Convert(plugins));
351 }
352 }
353
354 for (int i = OrthancPluginValueRepresentation_AE;
355 i <= OrthancPluginValueRepresentation_UT; i++)
356 {
357 OrthancPluginValueRepresentation plugins = static_cast<OrthancPluginValueRepresentation>(i);
358 ValueRepresentation orthanc = Plugins::Convert(plugins);
359 ASSERT_EQ(plugins, Plugins::Convert(orthanc));
360 }
306 } 361 }
307 362
308 363
309 364
310 static const DicomTag REFERENCED_STUDY_SEQUENCE(0x0008, 0x1110); 365 static const DicomTag REFERENCED_STUDY_SEQUENCE(0x0008, 0x1110);
327 a.append(b); 382 a.append(b);
328 } 383 }
329 } 384 }
330 385
331 386
332 TEST(FromDcmtkBridge, FromJson) 387 namespace Orthanc
333 { 388 {
334 std::auto_ptr<DcmElement> element; 389 // Namespace for the "FRIEND_TEST()" directive in "FromDcmtkBridge" to apply:
335 390 // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#private-class-members
336 { 391 TEST(FromDcmtkBridge, FromJson)
337 Json::Value a; 392 {
338 a = "Hello"; 393 std::auto_ptr<DcmElement> element;
339 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8)); 394
340 395 {
341 Json::Value b; 396 Json::Value a;
342 FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii); 397 a = "Hello";
343 ASSERT_EQ("Hello", b["0010,0010"].asString()); 398 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8));
344 } 399
345
346 {
347 Json::Value a;
348 a = "Hello";
349 // Cannot assign a string to a sequence
350 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, false, Encoding_Utf8)), OrthancException);
351 }
352
353 {
354 Json::Value a = Json::arrayValue;
355 a.append("Hello");
356 // Cannot assign an array to a string
357 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8)), OrthancException);
358 }
359
360 {
361 Json::Value a;
362 a = "data:application/octet-stream;base64,SGVsbG8="; // echo -n "Hello" | base64
363 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8));
364
365 Json::Value b;
366 FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii);
367 ASSERT_EQ("Hello", b["0010,0010"].asString());
368 }
369
370 {
371 Json::Value a = Json::arrayValue;
372 CreateSampleJson(a);
373 element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, true, Encoding_Utf8));
374
375 {
376 Json::Value b; 400 Json::Value b;
377 FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii); 401 std::set<DicomTag> ignoreTagLength;
378 ASSERT_EQ(Json::arrayValue, b["0008,1110"].type()); 402 ignoreTagLength.insert(DICOM_TAG_PATIENT_ID);
379 ASSERT_EQ(2, b["0008,1110"].size()); 403
404 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
405 DicomToJsonFlags_Default, 0, Encoding_Ascii, ignoreTagLength);
406 ASSERT_TRUE(b.isMember("0010,0010"));
407 ASSERT_EQ("Hello", b["0010,0010"].asString());
408
409 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
410 DicomToJsonFlags_Default, 3, Encoding_Ascii, ignoreTagLength);
411 ASSERT_TRUE(b["0010,0010"].isNull()); // "Hello" has more than 3 characters
412
413 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Full,
414 DicomToJsonFlags_Default, 3, Encoding_Ascii, ignoreTagLength);
415 ASSERT_TRUE(b["0010,0010"].isObject());
416 ASSERT_EQ("PatientName", b["0010,0010"]["Name"].asString());
417 ASSERT_EQ("TooLong", b["0010,0010"]["Type"].asString());
418 ASSERT_TRUE(b["0010,0010"]["Value"].isNull());
419
420 ignoreTagLength.insert(DICOM_TAG_PATIENT_NAME);
421 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
422 DicomToJsonFlags_Default, 3, Encoding_Ascii, ignoreTagLength);
423 ASSERT_EQ("Hello", b["0010,0010"].asString());
424 }
425
426 {
427 Json::Value a;
428 a = "Hello";
429 // Cannot assign a string to a sequence
430 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, false, Encoding_Utf8)), OrthancException);
431 }
432
433 {
434 Json::Value a = Json::arrayValue;
435 a.append("Hello");
436 // Cannot assign an array to a string
437 ASSERT_THROW(element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8)), OrthancException);
438 }
439
440 {
441 Json::Value a;
442 a = "data:application/octet-stream;base64,SGVsbG8="; // echo -n "Hello" | base64
443 element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8));
444
445 Json::Value b;
446 std::set<DicomTag> ignoreTagLength;
447 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
448 DicomToJsonFlags_Default, 0, Encoding_Ascii, ignoreTagLength);
449 ASSERT_EQ("Hello", b["0010,0010"].asString());
450 }
451
452 {
453 Json::Value a = Json::arrayValue;
454 CreateSampleJson(a);
455 element.reset(FromDcmtkBridge::FromJson(REFERENCED_STUDY_SEQUENCE, a, true, Encoding_Utf8));
456
457 {
458 Json::Value b;
459 std::set<DicomTag> ignoreTagLength;
460 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Short,
461 DicomToJsonFlags_Default, 0, Encoding_Ascii, ignoreTagLength);
462 ASSERT_EQ(Json::arrayValue, b["0008,1110"].type());
463 ASSERT_EQ(2u, b["0008,1110"].size());
380 464
381 Json::Value::ArrayIndex i = (b["0008,1110"][0]["0010,0010"].asString() == "Hello") ? 0 : 1; 465 Json::Value::ArrayIndex i = (b["0008,1110"][0]["0010,0010"].asString() == "Hello") ? 0 : 1;
382 466
383 ASSERT_EQ(3, b["0008,1110"][i].size()); 467 ASSERT_EQ(3u, b["0008,1110"][i].size());
384 ASSERT_EQ(2, b["0008,1110"][1 - i].size()); 468 ASSERT_EQ(2u, b["0008,1110"][1 - i].size());
385 ASSERT_EQ(b["0008,1110"][i]["0010,0010"].asString(), "Hello"); 469 ASSERT_EQ(b["0008,1110"][i]["0010,0010"].asString(), "Hello");
386 ASSERT_EQ(b["0008,1110"][i]["0010,0020"].asString(), "World"); 470 ASSERT_EQ(b["0008,1110"][i]["0010,0020"].asString(), "World");
387 ASSERT_EQ(b["0008,1110"][i]["0008,1030"].asString(), "Toto"); 471 ASSERT_EQ(b["0008,1110"][i]["0008,1030"].asString(), "Toto");
388 ASSERT_EQ(b["0008,1110"][1 - i]["0010,0010"].asString(), "Hello2"); 472 ASSERT_EQ(b["0008,1110"][1 - i]["0010,0010"].asString(), "Hello2");
389 ASSERT_EQ(b["0008,1110"][1 - i]["0010,0020"].asString(), "World2"); 473 ASSERT_EQ(b["0008,1110"][1 - i]["0010,0020"].asString(), "World2");
390 } 474 }
391 475
392 { 476 {
393 Json::Value b; 477 Json::Value b;
394 FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0, Encoding_Ascii); 478 std::set<DicomTag> ignoreTagLength;
395 479 FromDcmtkBridge::ElementToJson(b, *element, DicomToJsonFormat_Full,
396 Json::Value c; 480 DicomToJsonFlags_Default, 0, Encoding_Ascii, ignoreTagLength);
397 Toolbox::SimplifyTags(c, b); 481
398 482 Json::Value c;
399 a[1]["PatientName"] = "Hello2"; // To remove the Data URI Scheme encoding 483 ServerToolbox::SimplifyTags(c, b, DicomToJsonFormat_Human);
400 ASSERT_EQ(0, c["ReferencedStudySequence"].compare(a)); 484
401 } 485 a[1]["PatientName"] = "Hello2"; // To remove the Data URI Scheme encoding
402 } 486 ASSERT_EQ(0, c["ReferencedStudySequence"].compare(a));
403 } 487 }
404 488 }
489 }
490 }
405 491
406 492
407 TEST(ParsedDicomFile, InsertReplaceStrings) 493 TEST(ParsedDicomFile, InsertReplaceStrings)
408 { 494 {
409 ParsedDicomFile f; 495 ParsedDicomFile f(true);
410 496
411 f.Insert(DICOM_TAG_PATIENT_NAME, "World", false); 497 f.Insert(DICOM_TAG_PATIENT_NAME, "World", false);
412 ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false), OrthancException); // Already existing tag 498 ASSERT_THROW(f.Insert(DICOM_TAG_PATIENT_ID, "Hello", false), OrthancException); // Already existing tag
413 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*) 499 f.ReplacePlainString(DICOM_TAG_SOP_INSTANCE_UID, "Toto"); // (*)
414 f.Replace(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**) 500 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "Tata"); // (**)
415 501
416 std::string s; 502 std::string s;
417 503 ASSERT_FALSE(f.LookupTransferSyntax(s));
418 ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_ThrowIfAbsent), OrthancException); 504
419 f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_IgnoreIfAbsent); 505 ASSERT_THROW(f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"),
506 false, DicomReplaceMode_ThrowIfAbsent), OrthancException);
507 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_IgnoreIfAbsent);
420 ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); 508 ASSERT_FALSE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
421 f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession", DicomReplaceMode_InsertIfAbsent); 509 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession"), false, DicomReplaceMode_InsertIfAbsent);
422 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); 510 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
423 ASSERT_EQ(s, "Accession"); 511 ASSERT_EQ(s, "Accession");
424 f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession2", DicomReplaceMode_IgnoreIfAbsent); 512 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession2"), false, DicomReplaceMode_IgnoreIfAbsent);
425 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); 513 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
426 ASSERT_EQ(s, "Accession2"); 514 ASSERT_EQ(s, "Accession2");
427 f.Replace(DICOM_TAG_ACCESSION_NUMBER, "Accession3", DicomReplaceMode_ThrowIfAbsent); 515 f.Replace(DICOM_TAG_ACCESSION_NUMBER, std::string("Accession3"), false, DicomReplaceMode_ThrowIfAbsent);
428 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER)); 516 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_ACCESSION_NUMBER));
429 ASSERT_EQ(s, "Accession3"); 517 ASSERT_EQ(s, "Accession3");
430 518
431 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_PATIENT_NAME)); 519 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_PATIENT_NAME));
432 ASSERT_EQ(s, "World"); 520 ASSERT_EQ(s, "World");
443 531
444 532
445 533
446 TEST(ParsedDicomFile, InsertReplaceJson) 534 TEST(ParsedDicomFile, InsertReplaceJson)
447 { 535 {
448 ParsedDicomFile f; 536 ParsedDicomFile f(true);
449 537
450 Json::Value a; 538 Json::Value a;
451 CreateSampleJson(a); 539 CreateSampleJson(a);
452 540
453 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE)); 541 ASSERT_FALSE(f.HasTag(REFERENCED_STUDY_SEQUENCE));
468 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_InsertIfAbsent); 556 f.Replace(REFERENCED_PATIENT_SEQUENCE, a, false, DicomReplaceMode_InsertIfAbsent);
469 ASSERT_TRUE(f.HasTag(REFERENCED_PATIENT_SEQUENCE)); 557 ASSERT_TRUE(f.HasTag(REFERENCED_PATIENT_SEQUENCE));
470 558
471 { 559 {
472 Json::Value b; 560 Json::Value b;
473 f.ToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0); 561 f.DatasetToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0);
474 562
475 Json::Value c; 563 Json::Value c;
476 Toolbox::SimplifyTags(c, b); 564 ServerToolbox::SimplifyTags(c, b, DicomToJsonFormat_Human);
477 565
478 ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a)); 566 ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a));
479 ASSERT_NE(0, c["ReferencedStudySequence"].compare(a)); // Because Data URI Scheme decoding was enabled 567 ASSERT_NE(0, c["ReferencedStudySequence"].compare(a)); // Because Data URI Scheme decoding was enabled
480 } 568 }
481 569
482 a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64 570 a = "data:application/octet-stream;base64,VGF0YQ=="; // echo -n "Tata" | base64
483 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false); // (*) 571 f.Replace(DICOM_TAG_SOP_INSTANCE_UID, a, false, DicomReplaceMode_InsertIfAbsent); // (*)
484 f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true); // (**) 572 f.Replace(DICOM_TAG_SOP_CLASS_UID, a, true, DicomReplaceMode_InsertIfAbsent); // (**)
485 573
486 std::string s; 574 std::string s;
487 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID)); 575 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_SOP_INSTANCE_UID));
488 ASSERT_EQ(s, a.asString()); 576 ASSERT_EQ(s, a.asString());
489 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*) 577 ASSERT_TRUE(f.GetTagValue(s, DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID)); // Implicitly modified by (*)
495 } 583 }
496 584
497 585
498 TEST(ParsedDicomFile, JsonEncoding) 586 TEST(ParsedDicomFile, JsonEncoding)
499 { 587 {
500 ParsedDicomFile f; 588 ParsedDicomFile f(true);
501 589
502 for (unsigned int i = 0; i < testEncodingsCount; i++) 590 for (unsigned int i = 0; i < testEncodingsCount; i++)
503 { 591 {
504 if (testEncodings[i] != Encoding_Windows1251) 592 if (testEncodings[i] != Encoding_Windows1251)
505 { 593 {
510 { 598 {
511 ASSERT_EQ(testEncodings[i], f.GetEncoding()); 599 ASSERT_EQ(testEncodings[i], f.GetEncoding());
512 } 600 }
513 601
514 Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i]); 602 Json::Value s = Toolbox::ConvertToUtf8(testEncodingsEncoded[i], testEncodings[i]);
515 f.Replace(DICOM_TAG_PATIENT_NAME, s, false); 603 f.Replace(DICOM_TAG_PATIENT_NAME, s, false, DicomReplaceMode_InsertIfAbsent);
516 604
517 Json::Value v; 605 Json::Value v;
518 f.ToJson(v, DicomToJsonFormat_Simple, DicomToJsonFlags_Default, 0); 606 f.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
519 ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i])); 607 ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i]));
520 } 608 }
521 } 609 }
522 } 610 }
523 611
524 612
525 TEST(ParsedDicomFile, ToJsonFlags1) 613 TEST(ParsedDicomFile, ToJsonFlags1)
526 { 614 {
527 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), EVR_PN, "MyPrivateTag", 1, 1); 615 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_PersonName, "MyPrivateTag", 1, 1, "");
528 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), EVR_PN, "Declared public tag", 1, 1); 616 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1, "");
529 617
530 ParsedDicomFile f; 618 ParsedDicomFile f(true);
531 f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false); // Even group => public tag 619 f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false); // Even group => public tag
532 f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false); // Even group => public, unknown tag 620 f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false); // Even group => public, unknown tag
533 f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false); // Odd group => private tag 621 f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false); // Odd group => private tag
534 622
535 Json::Value v; 623 Json::Value v;
536 f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); 624 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
537 ASSERT_EQ(Json::objectValue, v.type()); 625 ASSERT_EQ(Json::objectValue, v.type());
538 ASSERT_EQ(6, v.getMemberNames().size()); 626 ASSERT_EQ(6u, v.getMemberNames().size());
539 ASSERT_FALSE(v.isMember("7052,1000")); 627 ASSERT_FALSE(v.isMember("7052,1000"));
540 ASSERT_FALSE(v.isMember("7053,1000")); 628 ASSERT_FALSE(v.isMember("7053,1000"));
541 ASSERT_TRUE(v.isMember("7050,1000")); 629 ASSERT_TRUE(v.isMember("7050,1000"));
542 ASSERT_EQ(Json::stringValue, v["7050,1000"].type()); 630 ASSERT_EQ(Json::stringValue, v["7050,1000"].type());
543 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); 631 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
544 632
545 f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0); 633 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
546 ASSERT_EQ(Json::objectValue, v.type()); 634 ASSERT_EQ(Json::objectValue, v.type());
547 ASSERT_EQ(7, v.getMemberNames().size()); 635 ASSERT_EQ(7u, v.getMemberNames().size());
548 ASSERT_FALSE(v.isMember("7052,1000")); 636 ASSERT_FALSE(v.isMember("7052,1000"));
549 ASSERT_TRUE(v.isMember("7050,1000")); 637 ASSERT_TRUE(v.isMember("7050,1000"));
550 ASSERT_TRUE(v.isMember("7053,1000")); 638 ASSERT_TRUE(v.isMember("7053,1000"));
551 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); 639 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
552 ASSERT_EQ(Json::nullValue, v["7053,1000"].type()); // TODO SHOULD BE STRING 640 ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
553 641
554 f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludeUnknownTags, 0); 642 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0);
555 ASSERT_EQ(Json::objectValue, v.type()); 643 ASSERT_EQ(Json::objectValue, v.type());
556 ASSERT_EQ(7, v.getMemberNames().size()); 644 ASSERT_EQ(7u, v.getMemberNames().size());
645 ASSERT_FALSE(v.isMember("7052,1000"));
646 ASSERT_TRUE(v.isMember("7050,1000"));
647 ASSERT_TRUE(v.isMember("7053,1000"));
648 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
649 std::string mime, content;
650 ASSERT_EQ(Json::stringValue, v["7053,1000"].type());
651 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString()));
652 ASSERT_EQ("application/octet-stream", mime);
653 ASSERT_EQ("Some private tag", content);
654
655 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
656 ASSERT_EQ(Json::objectValue, v.type());
657 ASSERT_EQ(7u, v.getMemberNames().size());
557 ASSERT_TRUE(v.isMember("7050,1000")); 658 ASSERT_TRUE(v.isMember("7050,1000"));
558 ASSERT_TRUE(v.isMember("7052,1000")); 659 ASSERT_TRUE(v.isMember("7052,1000"));
559 ASSERT_FALSE(v.isMember("7053,1000")); 660 ASSERT_FALSE(v.isMember("7053,1000"));
560 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); 661 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
561 ASSERT_EQ(Json::nullValue, v["7052,1000"].type()); // TODO SHOULD BE STRING 662 ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
562 663
563 f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags), 0); 664 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags), 0);
564 ASSERT_EQ(Json::objectValue, v.type()); 665 ASSERT_EQ(Json::objectValue, v.type());
565 ASSERT_EQ(8, v.getMemberNames().size()); 666 ASSERT_EQ(7u, v.getMemberNames().size());
667 ASSERT_TRUE(v.isMember("7050,1000"));
668 ASSERT_TRUE(v.isMember("7052,1000"));
669 ASSERT_FALSE(v.isMember("7053,1000"));
670 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
671 ASSERT_EQ(Json::stringValue, v["7052,1000"].type());
672 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString()));
673 ASSERT_EQ("application/octet-stream", mime);
674 ASSERT_EQ("Some unknown tag", content);
675
676 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
677 ASSERT_EQ(Json::objectValue, v.type());
678 ASSERT_EQ(8u, v.getMemberNames().size());
566 ASSERT_TRUE(v.isMember("7050,1000")); 679 ASSERT_TRUE(v.isMember("7050,1000"));
567 ASSERT_TRUE(v.isMember("7052,1000")); 680 ASSERT_TRUE(v.isMember("7052,1000"));
568 ASSERT_TRUE(v.isMember("7053,1000")); 681 ASSERT_TRUE(v.isMember("7053,1000"));
569 ASSERT_EQ("Some public tag", v["7050,1000"].asString()); 682 ASSERT_EQ("Some public tag", v["7050,1000"].asString());
570 ASSERT_EQ(Json::nullValue, v["7052,1000"].type()); // TODO SHOULD BE STRING 683 ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
571 ASSERT_EQ(Json::nullValue, v["7053,1000"].type()); // TODO SHOULD BE STRING 684 ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
572 } 685 }
573 686
574 687
575 TEST(ParsedDicomFile, ToJsonFlags2) 688 TEST(ParsedDicomFile, ToJsonFlags2)
576 { 689 {
577 ParsedDicomFile f; 690 ParsedDicomFile f(true);
578 f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false); 691 f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false);
579 692
580 Json::Value v; 693 Json::Value v;
581 f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); 694 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
582 ASSERT_EQ(Json::objectValue, v.type()); 695 ASSERT_EQ(Json::objectValue, v.type());
583 ASSERT_EQ(5, v.getMemberNames().size()); 696 ASSERT_EQ(5u, v.getMemberNames().size());
584 ASSERT_FALSE(v.isMember("7fe0,0010")); 697 ASSERT_FALSE(v.isMember("7fe0,0010"));
585 698
586 f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToNull), 0); 699 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToNull), 0);
587 ASSERT_EQ(Json::objectValue, v.type()); 700 ASSERT_EQ(Json::objectValue, v.type());
588 ASSERT_EQ(6, v.getMemberNames().size()); 701 ASSERT_EQ(6u, v.getMemberNames().size());
589 ASSERT_TRUE(v.isMember("7fe0,0010")); 702 ASSERT_TRUE(v.isMember("7fe0,0010"));
590 ASSERT_EQ(Json::nullValue, v["7fe0,0010"].type()); 703 ASSERT_EQ(Json::nullValue, v["7fe0,0010"].type());
591 704
592 f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToAscii), 0); 705 f.DatasetToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToAscii), 0);
593 ASSERT_EQ(Json::objectValue, v.type()); 706 ASSERT_EQ(Json::objectValue, v.type());
594 ASSERT_EQ(6, v.getMemberNames().size()); 707 ASSERT_EQ(6u, v.getMemberNames().size());
595 ASSERT_TRUE(v.isMember("7fe0,0010")); 708 ASSERT_TRUE(v.isMember("7fe0,0010"));
596 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type()); 709 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
597 ASSERT_EQ("Pixels", v["7fe0,0010"].asString()); 710 ASSERT_EQ("Pixels", v["7fe0,0010"].asString());
598 711
599 f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0); 712 f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0);
600 ASSERT_EQ(Json::objectValue, v.type()); 713 ASSERT_EQ(Json::objectValue, v.type());
601 ASSERT_EQ(6, v.getMemberNames().size()); 714 ASSERT_EQ(6u, v.getMemberNames().size());
602 ASSERT_TRUE(v.isMember("7fe0,0010")); 715 ASSERT_TRUE(v.isMember("7fe0,0010"));
603 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type()); 716 ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
604 std::string mime, content; 717 std::string mime, content;
605 Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString()); 718 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString()));
606 ASSERT_EQ("application/octet-stream", mime); 719 ASSERT_EQ("application/octet-stream", mime);
607 ASSERT_EQ("Pixels", content); 720 ASSERT_EQ("Pixels", content);
608 } 721 }
722
723
724 TEST(DicomFindAnswers, Basic)
725 {
726 DicomFindAnswers a(false);
727
728 {
729 DicomMap m;
730 m.SetValue(DICOM_TAG_PATIENT_ID, "hello", false);
731 a.Add(m);
732 }
733
734 {
735 ParsedDicomFile d(true);
736 d.ReplacePlainString(DICOM_TAG_PATIENT_ID, "my");
737 a.Add(d);
738 }
739
740 {
741 DicomMap m;
742 m.SetValue(DICOM_TAG_PATIENT_ID, "world", false);
743 a.Add(m);
744 }
745
746 Json::Value j;
747 a.ToJson(j, true);
748 ASSERT_EQ(3u, j.size());
749
750 //std::cout << j;
751 }
752
753
754 TEST(ParsedDicomFile, FromJson)
755 {
756 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag2", 1, 1, "ORTHANC");
757 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag3", 1, 1, "");
758 FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag2", 1, 1, "");
759
760 Json::Value v;
761 const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1"; // CR Image Storage:
762
763 // Test the private creator
764 ASSERT_EQ(DcmTag_ERROR_TagName, FromDcmtkBridge::GetTagName(DicomTag(0x7057, 0x1000), "NOPE"));
765 ASSERT_EQ("MyPrivateTag2", FromDcmtkBridge::GetTagName(DicomTag(0x7057, 0x1000), "ORTHANC"));
766
767 {
768 v["SOPClassUID"] = sopClassUid;
769 v["SpecificCharacterSet"] = "ISO_IR 148"; // This is latin-5
770 v["PatientName"] = "Sébastien";
771 v["7050-1000"] = "Some public tag"; // Even group => public tag
772 v["7052-1000"] = "Some unknown tag"; // Even group => public, unknown tag
773 v["7057-1000"] = "Some private tag"; // Odd group => private tag
774 v["7059-1000"] = "Some private tag2"; // Odd group => private tag, with an odd length to test padding
775
776 std::string s;
777 Toolbox::EncodeDataUriScheme(s, "application/octet-stream", "Sebastien");
778 v["StudyDescription"] = s;
779
780 v["PixelData"] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // A red dot of 5x5 pixels
781 v["0040,0100"] = Json::arrayValue; // ScheduledProcedureStepSequence
782
783 Json::Value vv;
784 vv["Modality"] = "MR";
785 v["0040,0100"].append(vv);
786
787 vv["Modality"] = "CT";
788 v["0040,0100"].append(vv);
789 }
790
791 const DicomToJsonFlags toJsonFlags = static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeBinary |
792 DicomToJsonFlags_IncludePixelData |
793 DicomToJsonFlags_IncludePrivateTags |
794 DicomToJsonFlags_IncludeUnknownTags |
795 DicomToJsonFlags_ConvertBinaryToAscii);
796
797
798 {
799 std::auto_ptr<ParsedDicomFile> dicom
800 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers)));
801
802 Json::Value vv;
803 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, toJsonFlags, 0);
804
805 ASSERT_EQ(vv["SOPClassUID"].asString(), sopClassUid);
806 ASSERT_EQ(vv["MediaStorageSOPClassUID"].asString(), sopClassUid);
807 ASSERT_TRUE(vv.isMember("SOPInstanceUID"));
808 ASSERT_TRUE(vv.isMember("SeriesInstanceUID"));
809 ASSERT_TRUE(vv.isMember("StudyInstanceUID"));
810 ASSERT_TRUE(vv.isMember("PatientID"));
811 }
812
813
814 {
815 std::auto_ptr<ParsedDicomFile> dicom
816 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers)));
817
818 Json::Value vv;
819 dicom->DatasetToJson(vv, DicomToJsonFormat_Human, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0);
820
821 std::string mime, content;
822 ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, vv["PixelData"].asString()));
823 ASSERT_EQ("application/octet-stream", mime);
824 ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size());
825 }
826
827
828 {
829 std::auto_ptr<ParsedDicomFile> dicom
830 (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_DecodeDataUriScheme)));
831
832 Json::Value vv;
833 dicom->DatasetToJson(vv, DicomToJsonFormat_Short, toJsonFlags, 0);
834
835 ASSERT_FALSE(vv.isMember("SOPInstanceUID"));
836 ASSERT_FALSE(vv.isMember("SeriesInstanceUID"));
837 ASSERT_FALSE(vv.isMember("StudyInstanceUID"));
838 ASSERT_FALSE(vv.isMember("PatientID"));
839 ASSERT_EQ(2u, vv["0040,0100"].size());
840 ASSERT_EQ("MR", vv["0040,0100"][0]["0008,0060"].asString());
841 ASSERT_EQ("CT", vv["0040,0100"][1]["0008,0060"].asString());
842 ASSERT_EQ("Some public tag", vv["7050,1000"].asString());
843 ASSERT_EQ("Some unknown tag", vv["7052,1000"].asString());
844 ASSERT_EQ("Some private tag", vv["7057,1000"].asString());
845 ASSERT_EQ("Some private tag2", vv["7059,1000"].asString());
846 ASSERT_EQ("Sébastien", vv["0010,0010"].asString());
847 ASSERT_EQ("Sebastien", vv["0008,1030"].asString());
848 ASSERT_EQ("ISO_IR 148", vv["0008,0005"].asString());
849 ASSERT_EQ("5", vv[DICOM_TAG_ROWS.Format()].asString());
850 ASSERT_EQ("5", vv[DICOM_TAG_COLUMNS.Format()].asString());
851 ASSERT_TRUE(vv[DICOM_TAG_PIXEL_DATA.Format()].asString().empty());
852 }
853 }
854
855
856
857 TEST(TestImages, PatternGrayscale8)
858 {
859 static const char* PATH = "UnitTestsResults/PatternGrayscale8.dcm";
860
861 Orthanc::Image image(Orthanc::PixelFormat_Grayscale8, 256, 256, false);
862
863 for (int y = 0; y < 256; y++)
864 {
865 uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
866 for (int x = 0; x < 256; x++, p++)
867 {
868 *p = y;
869 }
870 }
871
872 Orthanc::ImageAccessor r;
873
874 image.GetRegion(r, 32, 32, 64, 192);
875 Orthanc::ImageProcessing::Set(r, 0);
876
877 image.GetRegion(r, 160, 32, 64, 192);
878 Orthanc::ImageProcessing::Set(r, 255);
879
880 {
881 ParsedDicomFile f(true);
882 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
883 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
884 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
885 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
886 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
887 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale8");
888 f.EmbedImage(image);
889
890 f.SaveToFile(PATH);
891 }
892
893 {
894 std::string s;
895 Orthanc::SystemToolbox::ReadFile(s, PATH);
896 Orthanc::ParsedDicomFile f(s);
897
898 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
899 ASSERT_EQ(256u, decoded->GetWidth());
900 ASSERT_EQ(256u, decoded->GetHeight());
901 ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat());
902
903 for (int y = 0; y < 256; y++)
904 {
905 const void* a = image.GetConstRow(y);
906 const void* b = decoded->GetConstRow(y);
907 ASSERT_EQ(0, memcmp(a, b, 256));
908 }
909 }
910 }
911
912
913 TEST(TestImages, PatternRGB)
914 {
915 static const char* PATH = "UnitTestsResults/PatternRGB24.dcm";
916
917 Orthanc::Image image(Orthanc::PixelFormat_RGB24, 384, 256, false);
918
919 for (int y = 0; y < 256; y++)
920 {
921 uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
922 for (int x = 0; x < 128; x++, p += 3)
923 {
924 p[0] = y;
925 p[1] = 0;
926 p[2] = 0;
927 }
928 for (int x = 128; x < 128 * 2; x++, p += 3)
929 {
930 p[0] = 0;
931 p[1] = 255 - y;
932 p[2] = 0;
933 }
934 for (int x = 128 * 2; x < 128 * 3; x++, p += 3)
935 {
936 p[0] = 0;
937 p[1] = 0;
938 p[2] = y;
939 }
940 }
941
942 {
943 ParsedDicomFile f(true);
944 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
945 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
946 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
947 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
948 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
949 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "RGB24");
950 f.EmbedImage(image);
951
952 f.SaveToFile(PATH);
953 }
954
955 {
956 std::string s;
957 Orthanc::SystemToolbox::ReadFile(s, PATH);
958 Orthanc::ParsedDicomFile f(s);
959
960 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
961 ASSERT_EQ(384u, decoded->GetWidth());
962 ASSERT_EQ(256u, decoded->GetHeight());
963 ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat());
964
965 for (int y = 0; y < 256; y++)
966 {
967 const void* a = image.GetConstRow(y);
968 const void* b = decoded->GetConstRow(y);
969 ASSERT_EQ(0, memcmp(a, b, 3 * 384));
970 }
971 }
972 }
973
974
975 TEST(TestImages, PatternUint16)
976 {
977 static const char* PATH = "UnitTestsResults/PatternGrayscale16.dcm";
978
979 Orthanc::Image image(Orthanc::PixelFormat_Grayscale16, 256, 256, false);
980
981 uint16_t v = 0;
982 for (int y = 0; y < 256; y++)
983 {
984 uint16_t *p = reinterpret_cast<uint16_t*>(image.GetRow(y));
985 for (int x = 0; x < 256; x++, v++, p++)
986 {
987 *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
988 }
989 }
990
991 Orthanc::ImageAccessor r;
992
993 image.GetRegion(r, 32, 32, 64, 192);
994 Orthanc::ImageProcessing::Set(r, 0);
995
996 image.GetRegion(r, 160, 32, 64, 192);
997 Orthanc::ImageProcessing::Set(r, 65535);
998
999 {
1000 ParsedDicomFile f(true);
1001 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
1002 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
1003 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
1004 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
1005 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
1006 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale16");
1007 f.EmbedImage(image);
1008
1009 f.SaveToFile(PATH);
1010 }
1011
1012 {
1013 std::string s;
1014 Orthanc::SystemToolbox::ReadFile(s, PATH);
1015 Orthanc::ParsedDicomFile f(s);
1016
1017 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
1018 ASSERT_EQ(256u, decoded->GetWidth());
1019 ASSERT_EQ(256u, decoded->GetHeight());
1020 ASSERT_EQ(Orthanc::PixelFormat_Grayscale16, decoded->GetFormat());
1021
1022 for (int y = 0; y < 256; y++)
1023 {
1024 const void* a = image.GetConstRow(y);
1025 const void* b = decoded->GetConstRow(y);
1026 ASSERT_EQ(0, memcmp(a, b, 512));
1027 }
1028 }
1029 }
1030
1031
1032 TEST(TestImages, PatternInt16)
1033 {
1034 static const char* PATH = "UnitTestsResults/PatternSignedGrayscale16.dcm";
1035
1036 Orthanc::Image image(Orthanc::PixelFormat_SignedGrayscale16, 256, 256, false);
1037
1038 int16_t v = -32768;
1039 for (int y = 0; y < 256; y++)
1040 {
1041 int16_t *p = reinterpret_cast<int16_t*>(image.GetRow(y));
1042 for (int x = 0; x < 256; x++, v++, p++)
1043 {
1044 *p = htole16(v); // Orthanc uses Little-Endian transfer syntax to encode images
1045 }
1046 }
1047
1048 Orthanc::ImageAccessor r;
1049 image.GetRegion(r, 32, 32, 64, 192);
1050 Orthanc::ImageProcessing::Set(r, -32768);
1051
1052 image.GetRegion(r, 160, 32, 64, 192);
1053 Orthanc::ImageProcessing::Set(r, 32767);
1054
1055 {
1056 ParsedDicomFile f(true);
1057 f.ReplacePlainString(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
1058 f.ReplacePlainString(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
1059 f.ReplacePlainString(DICOM_TAG_PATIENT_ID, "ORTHANC");
1060 f.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "Orthanc");
1061 f.ReplacePlainString(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
1062 f.ReplacePlainString(DICOM_TAG_SERIES_DESCRIPTION, "SignedGrayscale16");
1063 f.EmbedImage(image);
1064
1065 f.SaveToFile(PATH);
1066 }
1067
1068 {
1069 std::string s;
1070 Orthanc::SystemToolbox::ReadFile(s, PATH);
1071 Orthanc::ParsedDicomFile f(s);
1072
1073 std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
1074 ASSERT_EQ(256u, decoded->GetWidth());
1075 ASSERT_EQ(256u, decoded->GetHeight());
1076 ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat());
1077
1078 for (int y = 0; y < 256; y++)
1079 {
1080 const void* a = image.GetConstRow(y);
1081 const void* b = decoded->GetConstRow(y);
1082 ASSERT_EQ(0, memcmp(a, b, 512));
1083 }
1084 }
1085 }
1086
1087
1088
1089 static void CheckEncoding(const ParsedDicomFile& dicom,
1090 Encoding expected)
1091 {
1092 const char* value = NULL;
1093 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_SpecificCharacterSet, value).good());
1094
1095 Encoding encoding;
1096 ASSERT_TRUE(GetDicomEncoding(encoding, value));
1097 ASSERT_EQ(expected, encoding);
1098 }
1099
1100
1101 TEST(ParsedDicomFile, DicomMapEncodings1)
1102 {
1103 SetDefaultDicomEncoding(Encoding_Ascii);
1104 ASSERT_EQ(Encoding_Ascii, GetDefaultDicomEncoding());
1105
1106 {
1107 DicomMap m;
1108 ParsedDicomFile dicom(m);
1109 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1110 CheckEncoding(dicom, Encoding_Ascii);
1111 }
1112
1113 {
1114 DicomMap m;
1115 ParsedDicomFile dicom(m, Encoding_Latin4);
1116 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1117 CheckEncoding(dicom, Encoding_Latin4);
1118 }
1119
1120 {
1121 DicomMap m;
1122 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 148", false);
1123 ParsedDicomFile dicom(m);
1124 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1125 CheckEncoding(dicom, Encoding_Latin5);
1126 }
1127
1128 {
1129 DicomMap m;
1130 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 148", false);
1131 ParsedDicomFile dicom(m, Encoding_Latin1);
1132 ASSERT_EQ(1u, dicom.GetDcmtkObject().getDataset()->card());
1133 CheckEncoding(dicom, Encoding_Latin5);
1134 }
1135 }
1136
1137
1138 TEST(ParsedDicomFile, DicomMapEncodings2)
1139 {
1140 const char* utf8 = NULL;
1141 for (unsigned int i = 0; i < testEncodingsCount; i++)
1142 {
1143 if (testEncodings[i] == Encoding_Utf8)
1144 {
1145 utf8 = testEncodingsEncoded[i];
1146 break;
1147 }
1148 }
1149
1150 ASSERT_TRUE(utf8 != NULL);
1151
1152 for (unsigned int i = 0; i < testEncodingsCount; i++)
1153 {
1154 // 1251 codepage is not supported by the core DICOM standard, ignore it
1155 if (testEncodings[i] != Encoding_Windows1251)
1156 {
1157 {
1158 // Sanity check to test the proper behavior of "EncodingTests.py"
1159 std::string encoded = Toolbox::ConvertFromUtf8(testEncodingsExpected[i], testEncodings[i]);
1160 ASSERT_STREQ(testEncodingsEncoded[i], encoded.c_str());
1161 std::string decoded = Toolbox::ConvertToUtf8(encoded, testEncodings[i]);
1162 ASSERT_STREQ(testEncodingsExpected[i], decoded.c_str());
1163
1164 if (testEncodings[i] != Encoding_Chinese)
1165 {
1166 // A specific source string is used in "EncodingTests.py" to
1167 // test against Chinese, it is normal that it does not correspond to UTF8
1168
1169 std::string encoded = Toolbox::ConvertToUtf8(Toolbox::ConvertFromUtf8(utf8, testEncodings[i]), testEncodings[i]);
1170 ASSERT_STREQ(testEncodingsExpected[i], encoded.c_str());
1171 }
1172 }
1173
1174
1175 Json::Value v;
1176
1177 {
1178 DicomMap m;
1179 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1180
1181 ParsedDicomFile dicom(m, testEncodings[i]);
1182
1183 const char* encoded = NULL;
1184 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_PatientName, encoded).good());
1185 ASSERT_STREQ(testEncodingsEncoded[i], encoded);
1186
1187 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1188
1189 Encoding encoding;
1190 ASSERT_TRUE(GetDicomEncoding(encoding, v["SpecificCharacterSet"].asCString()));
1191 ASSERT_EQ(encoding, testEncodings[i]);
1192 ASSERT_STREQ(testEncodingsExpected[i], v["PatientName"].asCString());
1193 }
1194
1195
1196 {
1197 DicomMap m;
1198 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, GetDicomSpecificCharacterSet(testEncodings[i]), false);
1199 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1200
1201 ParsedDicomFile dicom(m, testEncodings[i]);
1202
1203 Json::Value v2;
1204 dicom.DatasetToJson(v2, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1205
1206 ASSERT_EQ(v2["PatientName"].asString(), v["PatientName"].asString());
1207 ASSERT_EQ(v2["SpecificCharacterSet"].asString(), v["SpecificCharacterSet"].asString());
1208 }
1209 }
1210 }
1211 }
1212
1213
1214 TEST(ParsedDicomFile, ChangeEncoding)
1215 {
1216 for (unsigned int i = 0; i < testEncodingsCount; i++)
1217 {
1218 // 1251 codepage is not supported by the core DICOM standard, ignore it
1219 if (testEncodings[i] != Encoding_Windows1251)
1220 {
1221 DicomMap m;
1222 m.SetValue(DICOM_TAG_PATIENT_NAME, testEncodingsExpected[i], false);
1223
1224 std::string tag;
1225
1226 ParsedDicomFile dicom(m, Encoding_Utf8);
1227 ASSERT_EQ(Encoding_Utf8, dicom.GetEncoding());
1228 ASSERT_TRUE(dicom.GetTagValue(tag, DICOM_TAG_PATIENT_NAME));
1229 ASSERT_EQ(tag, testEncodingsExpected[i]);
1230
1231 {
1232 Json::Value v;
1233 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1234 ASSERT_STREQ(v["SpecificCharacterSet"].asCString(), "ISO_IR 192");
1235 ASSERT_STREQ(v["PatientName"].asCString(), testEncodingsExpected[i]);
1236 }
1237
1238 dicom.ChangeEncoding(testEncodings[i]);
1239
1240 ASSERT_EQ(testEncodings[i], dicom.GetEncoding());
1241
1242 const char* c = NULL;
1243 ASSERT_TRUE(dicom.GetDcmtkObject().getDataset()->findAndGetString(DCM_PatientName, c).good());
1244 EXPECT_STREQ(c, testEncodingsEncoded[i]);
1245
1246 ASSERT_TRUE(dicom.GetTagValue(tag, DICOM_TAG_PATIENT_NAME)); // Decodes to UTF-8
1247 EXPECT_EQ(tag, testEncodingsExpected[i]);
1248
1249 {
1250 Json::Value v;
1251 dicom.DatasetToJson(v, DicomToJsonFormat_Human, DicomToJsonFlags_Default, 0);
1252 ASSERT_STREQ(v["SpecificCharacterSet"].asCString(), GetDicomSpecificCharacterSet(testEncodings[i]));
1253 ASSERT_STREQ(v["PatientName"].asCString(), testEncodingsExpected[i]);
1254 }
1255 }
1256 }
1257 }
1258
1259
1260 TEST(Toolbox, CaseWithAccents)
1261 {
1262 ASSERT_EQ(toUpperResult, Toolbox::ToUpperCaseWithAccents(toUpperSource));
1263 }
1264
1265
1266
1267 TEST(ParsedDicomFile, InvalidCharacterSets)
1268 {
1269 {
1270 // No encoding provided, fallback to default encoding
1271 DicomMap m;
1272 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1273
1274 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */);
1275 ASSERT_EQ(Encoding_Latin3, d.GetEncoding());
1276 }
1277
1278 {
1279 // Valid encoding, "ISO_IR 13" is Japanese
1280 DicomMap m;
1281 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 13", false);
1282 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1283
1284 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */);
1285 ASSERT_EQ(Encoding_Japanese, d.GetEncoding());
1286 }
1287
1288 {
1289 // Invalid value for an encoding ("nope" is not in the DICOM standard)
1290 DicomMap m;
1291 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "nope", false);
1292 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1293
1294 ASSERT_THROW(ParsedDicomFile d(m, Encoding_Latin3), OrthancException);
1295 }
1296
1297 {
1298 // Invalid encoding, as provided as a binary string
1299 DicomMap m;
1300 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "ISO_IR 13", true);
1301 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1302
1303 ASSERT_THROW(ParsedDicomFile d(m, Encoding_Latin3), OrthancException);
1304 }
1305
1306 {
1307 // Encoding provided as an empty string, fallback to default encoding
1308 // In Orthanc <= 1.3.1, this test was throwing an exception
1309 DicomMap m;
1310 m.SetValue(DICOM_TAG_SPECIFIC_CHARACTER_SET, "", false);
1311 m.SetValue(DICOM_TAG_PATIENT_NAME, "HELLO", false);
1312
1313 ParsedDicomFile d(m, Encoding_Latin3 /* default encoding */);
1314 ASSERT_EQ(Encoding_Latin3, d.GetEncoding());
1315 }
1316 }