comparison Core/DicomParsing/DcmtkTranscoder.cpp @ 3946:1f33ed7f82e6 transcoding

automatic test of transcoding
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 19 May 2020 13:44:56 +0200
parents 0b3256c3ee14
children 5fe8c6d3212e
comparison
equal deleted inserted replaced
3945:0b3256c3ee14 3946:1f33ed7f82e6
60 { 60 {
61 return dataset.findAndGetUint16(DCM_BitsStored, bitsStored).good(); 61 return dataset.findAndGetUint16(DCM_BitsStored, bitsStored).good();
62 } 62 }
63 63
64 64
65 static std::string GetSopInstanceUid(DcmDataset& dataset)
66 {
67 const char* v = NULL;
68
69 if (dataset.findAndGetString(DCM_SOPInstanceUID, v).good() &&
70 v != NULL)
71 {
72 return std::string(v);
73 }
74 else
75 {
76 throw OrthancException(ErrorCode_BadFileFormat, "File without SOP instance UID");
77 }
78 }
79
80
81 static void CheckSopInstanceUid(DcmFileFormat& dicom,
82 const std::string& sopInstanceUid,
83 bool mustEqual)
84 {
85 if (dicom.getDataset() == NULL)
86 {
87 throw OrthancException(ErrorCode_InternalError);
88 }
89
90 bool ok;
91
92 if (dicom.getDataset()->tagExists(DCM_PixelData))
93 {
94 if (mustEqual)
95 {
96 ok = (GetSopInstanceUid(*dicom.getDataset()) == sopInstanceUid);
97 }
98 else
99 {
100 ok = (GetSopInstanceUid(*dicom.getDataset()) != sopInstanceUid);
101 }
102 }
103 else
104 {
105 // No pixel data: Transcoding must not change the SOP instance UID
106 ok = (GetSopInstanceUid(*dicom.getDataset()) == sopInstanceUid);
107 }
108
109 if (!ok)
110 {
111 throw OrthancException(ErrorCode_InternalError,
112 mustEqual ? "The SOP instance UID has changed unexpectedly during transcoding" :
113 "The SOP instance UID has not changed as expected during transcoding");
114 }
115 }
116
117
118 void DcmtkTranscoder::SetLossyQuality(unsigned int quality) 65 void DcmtkTranscoder::SetLossyQuality(unsigned int quality)
119 { 66 {
120 if (quality <= 0 || 67 if (quality <= 0 ||
121 quality > 100) 68 quality > 100)
122 { 69 {
132 } 79 }
133 } 80 }
134 81
135 82
136 bool DcmtkTranscoder::InplaceTranscode(bool& hasSopInstanceUidChanged /* out */, 83 bool DcmtkTranscoder::InplaceTranscode(bool& hasSopInstanceUidChanged /* out */,
84 DicomTransferSyntax& selectedSyntax /* out */,
137 DcmFileFormat& dicom, 85 DcmFileFormat& dicom,
138 const std::set<DicomTransferSyntax>& allowedSyntaxes, 86 const std::set<DicomTransferSyntax>& allowedSyntaxes,
139 bool allowNewSopInstanceUid) 87 bool allowNewSopInstanceUid)
140 { 88 {
141 if (dicom.getDataset() == NULL) 89 if (dicom.getDataset() == NULL)
153 } 101 }
154 102
155 uint16_t bitsStored; 103 uint16_t bitsStored;
156 bool hasBitsStored = GetBitsStored(bitsStored, *dicom.getDataset()); 104 bool hasBitsStored = GetBitsStored(bitsStored, *dicom.getDataset());
157 105
158 std::string sourceSopInstanceUid = GetSopInstanceUid(*dicom.getDataset()); 106 std::string sourceSopInstanceUid = IDicomTranscoder::GetSopInstanceUid(dicom);
159 107
160 if (allowedSyntaxes.find(syntax) != allowedSyntaxes.end()) 108 if (allowedSyntaxes.find(syntax) != allowedSyntaxes.end())
161 { 109 {
162 // No transcoding is needed 110 // No transcoding is needed
163 return true; 111 return true;
164 } 112 }
165 113
166 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianImplicit) != allowedSyntaxes.end() && 114 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianImplicit) != allowedSyntaxes.end() &&
167 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianImplicit, NULL)) 115 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianImplicit, NULL))
168 { 116 {
169 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 117 selectedSyntax = DicomTransferSyntax_LittleEndianImplicit;
170 return true; 118 return true;
171 } 119 }
172 120
173 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianExplicit) != allowedSyntaxes.end() && 121 if (allowedSyntaxes.find(DicomTransferSyntax_LittleEndianExplicit) != allowedSyntaxes.end() &&
174 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianExplicit, NULL)) 122 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_LittleEndianExplicit, NULL))
175 { 123 {
176 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 124 selectedSyntax = DicomTransferSyntax_LittleEndianExplicit;
177 return true; 125 return true;
178 } 126 }
179 127
180 if (allowedSyntaxes.find(DicomTransferSyntax_BigEndianExplicit) != allowedSyntaxes.end() && 128 if (allowedSyntaxes.find(DicomTransferSyntax_BigEndianExplicit) != allowedSyntaxes.end() &&
181 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_BigEndianExplicit, NULL)) 129 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_BigEndianExplicit, NULL))
182 { 130 {
183 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 131 selectedSyntax = DicomTransferSyntax_BigEndianExplicit;
184 return true; 132 return true;
185 } 133 }
186 134
187 if (allowedSyntaxes.find(DicomTransferSyntax_DeflatedLittleEndianExplicit) != allowedSyntaxes.end() && 135 if (allowedSyntaxes.find(DicomTransferSyntax_DeflatedLittleEndianExplicit) != allowedSyntaxes.end() &&
188 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL)) 136 FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL))
189 { 137 {
190 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 138 selectedSyntax = DicomTransferSyntax_DeflatedLittleEndianExplicit;
191 return true; 139 return true;
192 } 140 }
193 141
194 #if ORTHANC_ENABLE_DCMTK_JPEG == 1 142 #if ORTHANC_ENABLE_DCMTK_JPEG == 1
195 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess1) != allowedSyntaxes.end() && 143 if (allowedSyntaxes.find(DicomTransferSyntax_JPEGProcess1) != allowedSyntaxes.end() &&
199 // Check out "dcmjpeg/apps/dcmcjpeg.cc" 147 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
200 DJ_RPLossy parameters(lossyQuality_); 148 DJ_RPLossy parameters(lossyQuality_);
201 149
202 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, &parameters)) 150 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, &parameters))
203 { 151 {
204 CheckSopInstanceUid(dicom, sourceSopInstanceUid, false); 152 selectedSyntax = DicomTransferSyntax_JPEGProcess1;
205 hasSopInstanceUidChanged = true; 153 hasSopInstanceUidChanged = true;
206 return true; 154 return true;
207 } 155 }
208 } 156 }
209 #endif 157 #endif
215 { 163 {
216 // Check out "dcmjpeg/apps/dcmcjpeg.cc" 164 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
217 DJ_RPLossy parameters(lossyQuality_); 165 DJ_RPLossy parameters(lossyQuality_);
218 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, &parameters)) 166 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, &parameters))
219 { 167 {
220 CheckSopInstanceUid(dicom, sourceSopInstanceUid, false); 168 selectedSyntax = DicomTransferSyntax_JPEGProcess2_4;
221 hasSopInstanceUidChanged = true; 169 hasSopInstanceUidChanged = true;
222 return true; 170 return true;
223 } 171 }
224 } 172 }
225 #endif 173 #endif
230 // Check out "dcmjpeg/apps/dcmcjpeg.cc" 178 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
231 DJ_RPLossless parameters(6 /* opt_selection_value */, 179 DJ_RPLossless parameters(6 /* opt_selection_value */,
232 0 /* opt_point_transform */); 180 0 /* opt_point_transform */);
233 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14, &parameters)) 181 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14, &parameters))
234 { 182 {
235 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 183 selectedSyntax = DicomTransferSyntax_JPEGProcess14;
236 return true; 184 return true;
237 } 185 }
238 } 186 }
239 #endif 187 #endif
240 188
244 // Check out "dcmjpeg/apps/dcmcjpeg.cc" 192 // Check out "dcmjpeg/apps/dcmcjpeg.cc"
245 DJ_RPLossless parameters(6 /* opt_selection_value */, 193 DJ_RPLossless parameters(6 /* opt_selection_value */,
246 0 /* opt_point_transform */); 194 0 /* opt_point_transform */);
247 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14SV1, &parameters)) 195 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess14SV1, &parameters))
248 { 196 {
249 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 197 selectedSyntax = DicomTransferSyntax_JPEGProcess14SV1;
250 return true; 198 return true;
251 } 199 }
252 } 200 }
253 #endif 201 #endif
254 202
263 * WARNING: This call results in a segmentation fault if using 211 * WARNING: This call results in a segmentation fault if using
264 * the DCMTK package 3.6.2 from Ubuntu 18.04. 212 * the DCMTK package 3.6.2 from Ubuntu 18.04.
265 **/ 213 **/
266 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossless, &parameters)) 214 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossless, &parameters))
267 { 215 {
268 CheckSopInstanceUid(dicom, sourceSopInstanceUid, true); 216 selectedSyntax = DicomTransferSyntax_JPEGLSLossless;
269 return true; 217 return true;
270 } 218 }
271 } 219 }
272 #endif 220 #endif
273 221
283 * WARNING: This call results in a segmentation fault if using 231 * WARNING: This call results in a segmentation fault if using
284 * the DCMTK package 3.6.2 from Ubuntu 18.04. 232 * the DCMTK package 3.6.2 from Ubuntu 18.04.
285 **/ 233 **/
286 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossy, &parameters)) 234 if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGLSLossy, &parameters))
287 { 235 {
288 CheckSopInstanceUid(dicom, sourceSopInstanceUid, false); 236 selectedSyntax = DicomTransferSyntax_JPEGLSLossy;
289 hasSopInstanceUidChanged = true; 237 hasSopInstanceUidChanged = true;
290 return true; 238 return true;
291 } 239 }
292 } 240 }
293 #endif 241 #endif
341 { 289 {
342 LOG(ERROR) << "Unsupport transfer syntax for transcoding"; 290 LOG(ERROR) << "Unsupport transfer syntax for transcoding";
343 return false; 291 return false;
344 } 292 }
345 293
294 #if !defined(NDEBUG)
295 const std::string sourceSopInstanceUid = GetSopInstanceUid(source.GetParsed());
296 #endif
297
298 DicomTransferSyntax targetSyntax;
346 if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end()) 299 if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end())
347 { 300 {
348 // No transcoding is needed 301 // No transcoding is needed
349 target.AcquireParsed(source); 302 target.AcquireParsed(source);
350 target.AcquireBuffer(source); 303 target.AcquireBuffer(source);
351 return true; 304 return true;
352 } 305 }
353 else if (InplaceTranscode(hasSopInstanceUidChanged, source.GetParsed(), 306 else if (InplaceTranscode(hasSopInstanceUidChanged, targetSyntax, source.GetParsed(),
354 allowedSyntaxes, allowNewSopInstanceUid)) 307 allowedSyntaxes, allowNewSopInstanceUid))
355 { 308 {
356 // Sanity check 309 // Sanity check
357 DicomTransferSyntax targetSyntax; 310 DicomTransferSyntax targetSyntax2;
358 if (FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, source.GetParsed()) && 311 if (FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax2, source.GetParsed()) &&
359 allowedSyntaxes.find(targetSyntax) != allowedSyntaxes.end()) 312 targetSyntax == targetSyntax2 &&
313 allowedSyntaxes.find(targetSyntax2) != allowedSyntaxes.end())
360 { 314 {
361 target.AcquireParsed(source); 315 target.AcquireParsed(source);
362 source.Clear(); 316 source.Clear();
317
318 #if !defined(NDEBUG)
319 // Only run the sanity check in debug mode
320 CheckTranscoding(target, hasSopInstanceUidChanged, sourceSyntax, sourceSopInstanceUid,
321 allowedSyntaxes, allowNewSopInstanceUid);
322 #endif
323
363 return true; 324 return true;
364 } 325 }
365 else 326 else
366 { 327 {
367 throw OrthancException(ErrorCode_InternalError); 328 throw OrthancException(ErrorCode_InternalError);