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-2021 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 Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #include "../PrecompiledHeaders.h" 24 #include "IDicomTranscoder.h" 25 26 #include "../OrthancException.h" 27 #include "FromDcmtkBridge.h" 28 #include "ParsedDicomFile.h" 29 30 #include <dcmtk/dcmdata/dcfilefo.h> 31 #include <dcmtk/dcmdata/dcdeftag.h> 32 33 namespace Orthanc 34 { GetTranscodingType(DicomTransferSyntax target,DicomTransferSyntax source)35 IDicomTranscoder::TranscodingType IDicomTranscoder::GetTranscodingType(DicomTransferSyntax target, 36 DicomTransferSyntax source) 37 { 38 if (target == source) 39 { 40 return TranscodingType_Lossless; 41 } 42 else if (target == DicomTransferSyntax_LittleEndianImplicit || 43 target == DicomTransferSyntax_LittleEndianExplicit || 44 target == DicomTransferSyntax_BigEndianExplicit || 45 target == DicomTransferSyntax_DeflatedLittleEndianExplicit || 46 target == DicomTransferSyntax_JPEGProcess14 || 47 target == DicomTransferSyntax_JPEGProcess14SV1 || 48 target == DicomTransferSyntax_JPEGLSLossless || 49 target == DicomTransferSyntax_JPEG2000LosslessOnly || 50 target == DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly) 51 { 52 return TranscodingType_Lossless; 53 } 54 else if (target == DicomTransferSyntax_JPEGProcess1 || 55 target == DicomTransferSyntax_JPEGProcess2_4 || 56 target == DicomTransferSyntax_JPEGLSLossy || 57 target == DicomTransferSyntax_JPEG2000 || 58 target == DicomTransferSyntax_JPEG2000Multicomponent) 59 { 60 return TranscodingType_Lossy; 61 } 62 else 63 { 64 return TranscodingType_Unknown; 65 } 66 } 67 68 GetSopInstanceUid(DcmFileFormat & dicom)69 std::string IDicomTranscoder::GetSopInstanceUid(DcmFileFormat& dicom) 70 { 71 if (dicom.getDataset() == NULL) 72 { 73 throw OrthancException(ErrorCode_InternalError); 74 } 75 76 DcmDataset& dataset = *dicom.getDataset(); 77 78 const char* v = NULL; 79 80 if (dataset.findAndGetString(DCM_SOPInstanceUID, v).good() && 81 v != NULL) 82 { 83 return std::string(v); 84 } 85 else 86 { 87 throw OrthancException(ErrorCode_BadFileFormat, "File without SOP instance UID"); 88 } 89 } 90 91 CheckTranscoding(IDicomTranscoder::DicomImage & transcoded,DicomTransferSyntax sourceSyntax,const std::string & sourceSopInstanceUid,const std::set<DicomTransferSyntax> & allowedSyntaxes,bool allowNewSopInstanceUid)92 void IDicomTranscoder::CheckTranscoding(IDicomTranscoder::DicomImage& transcoded, 93 DicomTransferSyntax sourceSyntax, 94 const std::string& sourceSopInstanceUid, 95 const std::set<DicomTransferSyntax>& allowedSyntaxes, 96 bool allowNewSopInstanceUid) 97 { 98 DcmFileFormat& parsed = transcoded.GetParsed(); 99 100 if (parsed.getDataset() == NULL) 101 { 102 throw OrthancException(ErrorCode_InternalError); 103 } 104 105 std::string targetSopInstanceUid = GetSopInstanceUid(parsed); 106 107 if (parsed.getDataset()->tagExists(DCM_PixelData)) 108 { 109 if (!allowNewSopInstanceUid && (targetSopInstanceUid != sourceSopInstanceUid)) 110 { 111 throw OrthancException(ErrorCode_InternalError); 112 } 113 } 114 else 115 { 116 if (targetSopInstanceUid != sourceSopInstanceUid) 117 { 118 throw OrthancException(ErrorCode_InternalError, 119 "No pixel data: Transcoding must not change the SOP instance UID"); 120 } 121 } 122 123 DicomTransferSyntax targetSyntax; 124 if (!FromDcmtkBridge::LookupOrthancTransferSyntax(targetSyntax, parsed)) 125 { 126 return; // Unknown transfer syntax, cannot do further test 127 } 128 129 if (allowedSyntaxes.find(sourceSyntax) != allowedSyntaxes.end()) 130 { 131 // No transcoding should have happened 132 if (targetSopInstanceUid != sourceSopInstanceUid) 133 { 134 throw OrthancException(ErrorCode_InternalError); 135 } 136 } 137 138 if (allowedSyntaxes.find(targetSyntax) == allowedSyntaxes.end()) 139 { 140 throw OrthancException(ErrorCode_InternalError, "An incorrect output transfer syntax was chosen"); 141 } 142 143 if (parsed.getDataset()->tagExists(DCM_PixelData)) 144 { 145 switch (GetTranscodingType(targetSyntax, sourceSyntax)) 146 { 147 case TranscodingType_Lossy: 148 if (targetSopInstanceUid == sourceSopInstanceUid) 149 { 150 throw OrthancException(ErrorCode_InternalError); 151 } 152 break; 153 154 case TranscodingType_Lossless: 155 if (targetSopInstanceUid != sourceSopInstanceUid) 156 { 157 throw OrthancException(ErrorCode_InternalError); 158 } 159 break; 160 161 default: 162 break; 163 } 164 } 165 } 166 167 Parse()168 void IDicomTranscoder::DicomImage::Parse() 169 { 170 if (parsed_.get() != NULL) 171 { 172 // Already parsed 173 throw OrthancException(ErrorCode_BadSequenceOfCalls); 174 } 175 else if (buffer_.get() != NULL) 176 { 177 if (isExternalBuffer_) 178 { 179 throw OrthancException(ErrorCode_InternalError); 180 } 181 else 182 { 183 parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer( 184 buffer_->empty() ? NULL : buffer_->c_str(), buffer_->size())); 185 186 if (parsed_.get() == NULL) 187 { 188 throw OrthancException(ErrorCode_BadFileFormat); 189 } 190 } 191 } 192 else if (isExternalBuffer_) 193 { 194 parsed_.reset(FromDcmtkBridge::LoadFromMemoryBuffer(externalBuffer_, externalSize_)); 195 196 if (parsed_.get() == NULL) 197 { 198 throw OrthancException(ErrorCode_BadFileFormat); 199 } 200 } 201 else 202 { 203 // No buffer is available 204 throw OrthancException(ErrorCode_BadSequenceOfCalls); 205 } 206 } 207 208 Serialize()209 void IDicomTranscoder::DicomImage::Serialize() 210 { 211 if (parsed_.get() == NULL || 212 buffer_.get() != NULL || 213 isExternalBuffer_) 214 { 215 throw OrthancException(ErrorCode_BadSequenceOfCalls); 216 } 217 else if (parsed_->getDataset() == NULL) 218 { 219 throw OrthancException(ErrorCode_InternalError); 220 } 221 else 222 { 223 buffer_.reset(new std::string); 224 FromDcmtkBridge::SaveToMemoryBuffer(*buffer_, *parsed_->getDataset()); 225 } 226 } 227 228 DicomImage()229 IDicomTranscoder::DicomImage::DicomImage() : 230 isExternalBuffer_(false), 231 externalBuffer_(NULL), 232 externalSize_(0) 233 { 234 } 235 236 Clear()237 void IDicomTranscoder::DicomImage::Clear() 238 { 239 parsed_.reset(NULL); 240 buffer_.reset(NULL); 241 isExternalBuffer_ = false; 242 } 243 244 AcquireParsed(ParsedDicomFile & parsed)245 void IDicomTranscoder::DicomImage::AcquireParsed(ParsedDicomFile& parsed) 246 { 247 AcquireParsed(parsed.ReleaseDcmtkObject()); 248 } 249 250 AcquireParsed(DcmFileFormat * parsed)251 void IDicomTranscoder::DicomImage::AcquireParsed(DcmFileFormat* parsed) 252 { 253 if (parsed == NULL) 254 { 255 throw OrthancException(ErrorCode_NullPointer); 256 } 257 else if (parsed->getDataset() == NULL) 258 { 259 throw OrthancException(ErrorCode_InternalError); 260 } 261 else if (parsed_.get() != NULL) 262 { 263 throw OrthancException(ErrorCode_BadSequenceOfCalls); 264 } 265 else 266 { 267 parsed_.reset(parsed); 268 } 269 } 270 271 AcquireParsed(DicomImage & other)272 void IDicomTranscoder::DicomImage::AcquireParsed(DicomImage& other) 273 { 274 AcquireParsed(other.ReleaseParsed()); 275 } 276 277 AcquireBuffer(std::string & buffer)278 void IDicomTranscoder::DicomImage::AcquireBuffer(std::string& buffer /* will be swapped */) 279 { 280 if (buffer_.get() != NULL || 281 isExternalBuffer_) 282 { 283 throw OrthancException(ErrorCode_BadSequenceOfCalls); 284 } 285 else 286 { 287 buffer_.reset(new std::string); 288 buffer_->swap(buffer); 289 } 290 } 291 292 AcquireBuffer(DicomImage & other)293 void IDicomTranscoder::DicomImage::AcquireBuffer(DicomImage& other) 294 { 295 if (buffer_.get() != NULL || 296 isExternalBuffer_) 297 { 298 throw OrthancException(ErrorCode_BadSequenceOfCalls); 299 } 300 else if (other.isExternalBuffer_) 301 { 302 assert(other.buffer_.get() == NULL); 303 isExternalBuffer_ = true; 304 externalBuffer_ = other.externalBuffer_; 305 externalSize_ = other.externalSize_; 306 } 307 else if (other.buffer_.get() != NULL) 308 { 309 buffer_.reset(other.buffer_.release()); 310 } 311 else 312 { 313 buffer_.reset(NULL); 314 } 315 } 316 317 SetExternalBuffer(const void * buffer,size_t size)318 void IDicomTranscoder::DicomImage::SetExternalBuffer(const void* buffer, 319 size_t size) 320 { 321 if (buffer_.get() != NULL || 322 isExternalBuffer_) 323 { 324 throw OrthancException(ErrorCode_BadSequenceOfCalls); 325 } 326 else 327 { 328 isExternalBuffer_ = true; 329 externalBuffer_ = buffer; 330 externalSize_ = size; 331 } 332 } 333 334 SetExternalBuffer(const std::string & buffer)335 void IDicomTranscoder::DicomImage::SetExternalBuffer(const std::string& buffer) 336 { 337 SetExternalBuffer(buffer.empty() ? NULL : buffer.c_str(), buffer.size()); 338 } 339 340 GetParsed()341 DcmFileFormat& IDicomTranscoder::DicomImage::GetParsed() 342 { 343 if (parsed_.get() != NULL) 344 { 345 return *parsed_; 346 } 347 else if (buffer_.get() != NULL || 348 isExternalBuffer_) 349 { 350 Parse(); 351 return *parsed_; 352 } 353 else 354 { 355 throw OrthancException( 356 ErrorCode_BadSequenceOfCalls, 357 "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called"); 358 } 359 } 360 361 ReleaseParsed()362 DcmFileFormat* IDicomTranscoder::DicomImage::ReleaseParsed() 363 { 364 if (parsed_.get() != NULL) 365 { 366 buffer_.reset(NULL); 367 return parsed_.release(); 368 } 369 else if (buffer_.get() != NULL || 370 isExternalBuffer_) 371 { 372 Parse(); 373 buffer_.reset(NULL); 374 return parsed_.release(); 375 } 376 else 377 { 378 throw OrthancException( 379 ErrorCode_BadSequenceOfCalls, 380 "AcquireParsed(), AcquireBuffer() or SetExternalBuffer() should have been called"); 381 } 382 } 383 384 ReleaseAsParsedDicomFile()385 ParsedDicomFile* IDicomTranscoder::DicomImage::ReleaseAsParsedDicomFile() 386 { 387 return ParsedDicomFile::AcquireDcmtkObject(ReleaseParsed()); 388 } 389 390 GetBufferData()391 const void* IDicomTranscoder::DicomImage::GetBufferData() 392 { 393 if (isExternalBuffer_) 394 { 395 assert(buffer_.get() == NULL); 396 return externalBuffer_; 397 } 398 else 399 { 400 if (buffer_.get() == NULL) 401 { 402 Serialize(); 403 } 404 405 assert(buffer_.get() != NULL); 406 return buffer_->empty() ? NULL : buffer_->c_str(); 407 } 408 } 409 410 GetBufferSize()411 size_t IDicomTranscoder::DicomImage::GetBufferSize() 412 { 413 if (isExternalBuffer_) 414 { 415 assert(buffer_.get() == NULL); 416 return externalSize_; 417 } 418 else 419 { 420 if (buffer_.get() == NULL) 421 { 422 Serialize(); 423 } 424 425 assert(buffer_.get() != NULL); 426 return buffer_->size(); 427 } 428 } 429 } 430