Mercurial > hg > orthanc
annotate OrthancServer/ParsedDicomFile.cpp @ 831:84513f2ee1f3
pch for unit tests and server
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 02 Jun 2014 12:15:45 +0200 |
parents | a811bdf8b8eb |
children | 839be3022203 |
rev | line source |
---|---|
790 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | |
4 * Belgium | |
5 * | |
6 * 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 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 | |
34 /*========================================================================= | |
35 | |
36 This file is based on portions of the following project: | |
37 | |
38 Program: GDCM (Grassroots DICOM). A DICOM library | |
39 Module: http://gdcm.sourceforge.net/Copyright.html | |
40 | |
41 Copyright (c) 2006-2011 Mathieu Malaterre | |
42 Copyright (c) 1993-2005 CREATIS | |
43 (CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image) | |
44 All rights reserved. | |
45 | |
46 Redistribution and use in source and binary forms, with or without | |
47 modification, are permitted provided that the following conditions are met: | |
48 | |
49 * Redistributions of source code must retain the above copyright notice, | |
50 this list of conditions and the following disclaimer. | |
51 | |
52 * Redistributions in binary form must reproduce the above copyright notice, | |
53 this list of conditions and the following disclaimer in the documentation | |
54 and/or other materials provided with the distribution. | |
55 | |
56 * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any | |
57 contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to | |
58 endorse or promote products derived from this software without specific | |
59 prior written permission. | |
60 | |
61 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' | |
62 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
63 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
64 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR | |
65 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
66 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
67 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
68 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
69 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
70 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
71 | |
72 =========================================================================*/ | |
73 | |
74 | |
831
84513f2ee1f3
pch for unit tests and server
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
824
diff
changeset
|
75 #include "PrecompiledHeadersServer.h" |
824
a811bdf8b8eb
precompiled headers
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
817
diff
changeset
|
76 |
790 | 77 #ifndef NOMINMAX |
78 #define NOMINMAX | |
79 #endif | |
80 | |
81 #include "ParsedDicomFile.h" | |
82 | |
83 #include "FromDcmtkBridge.h" | |
84 #include "ToDcmtkBridge.h" | |
85 #include "../Core/Toolbox.h" | |
86 #include "../Core/OrthancException.h" | |
799 | 87 #include "../Core/ImageFormats/PngWriter.h" |
790 | 88 #include "../Core/Uuid.h" |
89 #include "../Core/DicomFormat/DicomString.h" | |
90 #include "../Core/DicomFormat/DicomNullValue.h" | |
91 #include "../Core/DicomFormat/DicomIntegerPixelAccessor.h" | |
800
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
92 #include "../Core/ImageFormats/PngReader.h" |
790 | 93 |
94 #include <list> | |
95 #include <limits> | |
96 | |
97 #include <boost/lexical_cast.hpp> | |
98 | |
99 #include <dcmtk/dcmdata/dcchrstr.h> | |
100 #include <dcmtk/dcmdata/dcdicent.h> | |
101 #include <dcmtk/dcmdata/dcdict.h> | |
102 #include <dcmtk/dcmdata/dcfilefo.h> | |
103 #include <dcmtk/dcmdata/dcistrmb.h> | |
104 #include <dcmtk/dcmdata/dcuid.h> | |
105 #include <dcmtk/dcmdata/dcmetinf.h> | |
106 | |
107 #include <dcmtk/dcmdata/dcvrae.h> | |
108 #include <dcmtk/dcmdata/dcvras.h> | |
109 #include <dcmtk/dcmdata/dcvrcs.h> | |
110 #include <dcmtk/dcmdata/dcvrda.h> | |
111 #include <dcmtk/dcmdata/dcvrds.h> | |
112 #include <dcmtk/dcmdata/dcvrdt.h> | |
113 #include <dcmtk/dcmdata/dcvrfd.h> | |
114 #include <dcmtk/dcmdata/dcvrfl.h> | |
115 #include <dcmtk/dcmdata/dcvris.h> | |
116 #include <dcmtk/dcmdata/dcvrlo.h> | |
117 #include <dcmtk/dcmdata/dcvrlt.h> | |
118 #include <dcmtk/dcmdata/dcvrpn.h> | |
119 #include <dcmtk/dcmdata/dcvrsh.h> | |
120 #include <dcmtk/dcmdata/dcvrsl.h> | |
121 #include <dcmtk/dcmdata/dcvrss.h> | |
122 #include <dcmtk/dcmdata/dcvrst.h> | |
123 #include <dcmtk/dcmdata/dcvrtm.h> | |
124 #include <dcmtk/dcmdata/dcvrui.h> | |
125 #include <dcmtk/dcmdata/dcvrul.h> | |
126 #include <dcmtk/dcmdata/dcvrus.h> | |
127 #include <dcmtk/dcmdata/dcvrut.h> | |
128 #include <dcmtk/dcmdata/dcpixel.h> | |
129 #include <dcmtk/dcmdata/dcpixseq.h> | |
130 #include <dcmtk/dcmdata/dcpxitem.h> | |
131 | |
132 | |
133 #include <boost/math/special_functions/round.hpp> | |
134 #include <glog/logging.h> | |
135 #include <dcmtk/dcmdata/dcostrmb.h> | |
136 | |
137 | |
138 static const char* CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"; | |
139 | |
140 | |
141 | |
142 namespace Orthanc | |
143 { | |
794 | 144 struct ParsedDicomFile::PImpl |
145 { | |
146 std::auto_ptr<DcmFileFormat> file_; | |
147 }; | |
148 | |
149 | |
150 // This method can only be called from the constructors! | |
790 | 151 void ParsedDicomFile::Setup(const char* buffer, size_t size) |
152 { | |
153 DcmInputBufferStream is; | |
154 if (size > 0) | |
155 { | |
156 is.setBuffer(buffer, size); | |
157 } | |
158 is.setEos(); | |
159 | |
794 | 160 pimpl_->file_.reset(new DcmFileFormat); |
161 pimpl_->file_->transferInit(); | |
162 if (!pimpl_->file_->read(is).good()) | |
790 | 163 { |
794 | 164 delete pimpl_; // Avoid a memory leak due to exception |
165 // throwing, as we are in the constructor | |
166 | |
790 | 167 throw OrthancException(ErrorCode_BadFileFormat); |
168 } | |
794 | 169 pimpl_->file_->loadAllDataIntoMemory(); |
170 pimpl_->file_->transferEnd(); | |
790 | 171 } |
172 | |
173 | |
174 static void SendPathValueForDictionary(RestApiOutput& output, | |
175 DcmItem& dicom) | |
176 { | |
177 Json::Value v = Json::arrayValue; | |
178 | |
179 for (unsigned long i = 0; i < dicom.card(); i++) | |
180 { | |
181 DcmElement* element = dicom.getElement(i); | |
182 if (element) | |
183 { | |
184 char buf[16]; | |
185 sprintf(buf, "%04x-%04x", element->getTag().getGTag(), element->getTag().getETag()); | |
186 v.append(buf); | |
187 } | |
188 } | |
189 | |
190 output.AnswerJson(v); | |
191 } | |
192 | |
193 static inline uint16_t GetCharValue(char c) | |
194 { | |
195 if (c >= '0' && c <= '9') | |
196 return c - '0'; | |
197 else if (c >= 'a' && c <= 'f') | |
198 return c - 'a' + 10; | |
199 else if (c >= 'A' && c <= 'F') | |
200 return c - 'A' + 10; | |
201 else | |
202 return 0; | |
203 } | |
204 | |
205 static inline uint16_t GetTagValue(const char* c) | |
206 { | |
207 return ((GetCharValue(c[0]) << 12) + | |
208 (GetCharValue(c[1]) << 8) + | |
209 (GetCharValue(c[2]) << 4) + | |
210 GetCharValue(c[3])); | |
211 } | |
212 | |
213 static void ParseTagAndGroup(DcmTagKey& key, | |
214 const std::string& tag) | |
215 { | |
216 DicomTag t = FromDcmtkBridge::ParseTag(tag); | |
217 key = DcmTagKey(t.GetGroup(), t.GetElement()); | |
218 } | |
219 | |
220 | |
221 static void SendSequence(RestApiOutput& output, | |
222 DcmSequenceOfItems& sequence) | |
223 { | |
224 // This element is a sequence | |
225 Json::Value v = Json::arrayValue; | |
226 | |
227 for (unsigned long i = 0; i < sequence.card(); i++) | |
228 { | |
229 v.append(boost::lexical_cast<std::string>(i)); | |
230 } | |
231 | |
232 output.AnswerJson(v); | |
233 } | |
234 | |
235 | |
236 static unsigned int GetPixelDataBlockCount(DcmPixelData& pixelData, | |
237 E_TransferSyntax transferSyntax) | |
238 { | |
239 DcmPixelSequence* pixelSequence = NULL; | |
240 if (pixelData.getEncapsulatedRepresentation | |
241 (transferSyntax, NULL, pixelSequence).good() && pixelSequence) | |
242 { | |
243 return pixelSequence->card(); | |
244 } | |
245 else | |
246 { | |
247 return 1; | |
248 } | |
249 } | |
250 | |
251 | |
252 static void AnswerDicomField(RestApiOutput& output, | |
253 DcmElement& element, | |
254 E_TransferSyntax transferSyntax) | |
255 { | |
256 // This element is nor a sequence, neither a pixel-data | |
257 std::string buffer; | |
258 buffer.resize(65536); | |
259 Uint32 length = element.getLength(transferSyntax); | |
260 Uint32 offset = 0; | |
261 | |
262 output.GetLowLevelOutput().SendOkHeader(CONTENT_TYPE_OCTET_STREAM, true, length, NULL); | |
263 | |
264 while (offset < length) | |
265 { | |
266 Uint32 nbytes; | |
267 if (length - offset < buffer.size()) | |
268 { | |
269 nbytes = length - offset; | |
270 } | |
271 else | |
272 { | |
273 nbytes = buffer.size(); | |
274 } | |
275 | |
276 OFCondition cond = element.getPartialValue(&buffer[0], offset, nbytes); | |
277 | |
278 if (cond.good()) | |
279 { | |
280 output.GetLowLevelOutput().Send(&buffer[0], nbytes); | |
281 offset += nbytes; | |
282 } | |
283 else | |
284 { | |
285 LOG(ERROR) << "Error while sending a DICOM field: " << cond.text(); | |
286 return; | |
287 } | |
288 } | |
289 | |
290 output.MarkLowLevelOutputDone(); | |
291 } | |
292 | |
293 | |
294 static bool AnswerPixelData(RestApiOutput& output, | |
295 DcmItem& dicom, | |
296 E_TransferSyntax transferSyntax, | |
297 const std::string* blockUri) | |
298 { | |
299 DcmTag k(DICOM_TAG_PIXEL_DATA.GetGroup(), | |
300 DICOM_TAG_PIXEL_DATA.GetElement()); | |
301 | |
302 DcmElement *element = NULL; | |
303 if (!dicom.findAndGetElement(k, element).good() || | |
304 element == NULL) | |
305 { | |
306 return false; | |
307 } | |
308 | |
309 try | |
310 { | |
311 DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element); | |
312 if (blockUri == NULL) | |
313 { | |
314 // The user asks how many blocks are presents in this pixel data | |
315 unsigned int blocks = GetPixelDataBlockCount(pixelData, transferSyntax); | |
316 | |
317 Json::Value result(Json::arrayValue); | |
318 for (unsigned int i = 0; i < blocks; i++) | |
319 { | |
320 result.append(boost::lexical_cast<std::string>(i)); | |
321 } | |
322 | |
323 output.AnswerJson(result); | |
324 return true; | |
325 } | |
326 | |
327 | |
328 unsigned int block = boost::lexical_cast<unsigned int>(*blockUri); | |
329 | |
330 if (block < GetPixelDataBlockCount(pixelData, transferSyntax)) | |
331 { | |
332 DcmPixelSequence* pixelSequence = NULL; | |
333 if (pixelData.getEncapsulatedRepresentation | |
334 (transferSyntax, NULL, pixelSequence).good() && pixelSequence) | |
335 { | |
336 // This is the case for JPEG transfer syntaxes | |
337 if (block < pixelSequence->card()) | |
338 { | |
339 DcmPixelItem* pixelItem = NULL; | |
340 if (pixelSequence->getItem(pixelItem, block).good() && pixelItem) | |
341 { | |
342 if (pixelItem->getLength() == 0) | |
343 { | |
344 output.AnswerBuffer(NULL, 0, CONTENT_TYPE_OCTET_STREAM); | |
345 return true; | |
346 } | |
347 | |
348 Uint8* buffer = NULL; | |
349 if (pixelItem->getUint8Array(buffer).good() && buffer) | |
350 { | |
351 output.AnswerBuffer(buffer, pixelItem->getLength(), CONTENT_TYPE_OCTET_STREAM); | |
352 return true; | |
353 } | |
354 } | |
355 } | |
356 } | |
357 else | |
358 { | |
359 // This is the case for raw, uncompressed image buffers | |
360 assert(*blockUri == "0"); | |
361 AnswerDicomField(output, *element, transferSyntax); | |
362 } | |
363 } | |
364 } | |
365 catch (boost::bad_lexical_cast&) | |
366 { | |
367 // The URI entered by the user is not a number | |
368 } | |
369 catch (std::bad_cast&) | |
370 { | |
371 // This should never happen | |
372 } | |
373 | |
374 return false; | |
375 } | |
376 | |
377 | |
378 | |
379 static void SendPathValueForLeaf(RestApiOutput& output, | |
380 const std::string& tag, | |
381 DcmItem& dicom, | |
382 E_TransferSyntax transferSyntax) | |
383 { | |
384 DcmTagKey k; | |
385 ParseTagAndGroup(k, tag); | |
386 | |
387 DcmSequenceOfItems* sequence = NULL; | |
388 if (dicom.findAndGetSequence(k, sequence).good() && | |
389 sequence != NULL && | |
390 sequence->getVR() == EVR_SQ) | |
391 { | |
392 SendSequence(output, *sequence); | |
393 return; | |
394 } | |
395 | |
396 DcmElement* element = NULL; | |
397 if (dicom.findAndGetElement(k, element).good() && | |
398 element != NULL && | |
399 //element->getVR() != EVR_UNKNOWN && // This would forbid private tags | |
400 element->getVR() != EVR_SQ) | |
401 { | |
402 AnswerDicomField(output, *element, transferSyntax); | |
403 } | |
404 } | |
405 | |
406 void ParsedDicomFile::SendPathValue(RestApiOutput& output, | |
407 const UriComponents& uri) | |
408 { | |
794 | 409 DcmItem* dicom = pimpl_->file_->getDataset(); |
410 E_TransferSyntax transferSyntax = pimpl_->file_->getDataset()->getOriginalXfer(); | |
790 | 411 |
412 // Special case: Accessing the pixel data | |
413 if (uri.size() == 1 || | |
414 uri.size() == 2) | |
415 { | |
416 DcmTagKey tag; | |
417 ParseTagAndGroup(tag, uri[0]); | |
418 | |
419 if (tag.getGroup() == DICOM_TAG_PIXEL_DATA.GetGroup() && | |
420 tag.getElement() == DICOM_TAG_PIXEL_DATA.GetElement()) | |
421 { | |
422 AnswerPixelData(output, *dicom, transferSyntax, uri.size() == 1 ? NULL : &uri[1]); | |
423 return; | |
424 } | |
425 } | |
426 | |
427 // Go down in the tag hierarchy according to the URI | |
428 for (size_t pos = 0; pos < uri.size() / 2; pos++) | |
429 { | |
430 size_t index; | |
431 try | |
432 { | |
433 index = boost::lexical_cast<size_t>(uri[2 * pos + 1]); | |
434 } | |
435 catch (boost::bad_lexical_cast&) | |
436 { | |
437 return; | |
438 } | |
439 | |
440 DcmTagKey k; | |
441 DcmItem *child = NULL; | |
442 ParseTagAndGroup(k, uri[2 * pos]); | |
443 if (!dicom->findAndGetSequenceItem(k, child, index).good() || | |
444 child == NULL) | |
445 { | |
446 return; | |
447 } | |
448 | |
449 dicom = child; | |
450 } | |
451 | |
452 // We have reached the end of the URI | |
453 if (uri.size() % 2 == 0) | |
454 { | |
455 SendPathValueForDictionary(output, *dicom); | |
456 } | |
457 else | |
458 { | |
459 SendPathValueForLeaf(output, uri.back(), *dicom, transferSyntax); | |
460 } | |
461 } | |
462 | |
463 | |
464 | |
465 | |
466 | |
467 static DcmElement* CreateElementForTag(const DicomTag& tag) | |
468 { | |
469 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
470 | |
471 switch (key.getEVR()) | |
472 { | |
473 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
474 | |
475 /** | |
476 * TODO. | |
477 **/ | |
478 | |
479 case EVR_OB: // other byte | |
480 case EVR_OF: // other float | |
481 case EVR_OW: // other word | |
482 case EVR_AT: // attribute tag | |
483 throw OrthancException(ErrorCode_NotImplemented); | |
484 | |
485 case EVR_UN: // unknown value representation | |
486 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
487 | |
488 | |
489 /** | |
490 * String types. | |
491 * http://support.dcmtk.org/docs/classDcmByteString.html | |
492 **/ | |
493 | |
494 case EVR_AS: // age string | |
495 return new DcmAgeString(key); | |
496 | |
497 case EVR_AE: // application entity title | |
498 return new DcmApplicationEntity(key); | |
499 | |
500 case EVR_CS: // code string | |
501 return new DcmCodeString(key); | |
502 | |
503 case EVR_DA: // date string | |
504 return new DcmDate(key); | |
505 | |
506 case EVR_DT: // date time string | |
507 return new DcmDateTime(key); | |
508 | |
509 case EVR_DS: // decimal string | |
510 return new DcmDecimalString(key); | |
511 | |
512 case EVR_IS: // integer string | |
513 return new DcmIntegerString(key); | |
514 | |
515 case EVR_TM: // time string | |
516 return new DcmTime(key); | |
517 | |
518 case EVR_UI: // unique identifier | |
519 return new DcmUniqueIdentifier(key); | |
520 | |
521 case EVR_ST: // short text | |
522 return new DcmShortText(key); | |
523 | |
524 case EVR_LO: // long string | |
525 return new DcmLongString(key); | |
526 | |
527 case EVR_LT: // long text | |
528 return new DcmLongText(key); | |
529 | |
530 case EVR_UT: // unlimited text | |
531 return new DcmUnlimitedText(key); | |
532 | |
533 case EVR_SH: // short string | |
534 return new DcmShortString(key); | |
535 | |
536 case EVR_PN: // person name | |
537 return new DcmPersonName(key); | |
538 | |
539 | |
540 /** | |
541 * Numerical types | |
542 **/ | |
543 | |
544 case EVR_SL: // signed long | |
545 return new DcmSignedLong(key); | |
546 | |
547 case EVR_SS: // signed short | |
548 return new DcmSignedShort(key); | |
549 | |
550 case EVR_UL: // unsigned long | |
551 return new DcmUnsignedLong(key); | |
552 | |
553 case EVR_US: // unsigned short | |
554 return new DcmUnsignedShort(key); | |
555 | |
556 case EVR_FL: // float single-precision | |
557 return new DcmFloatingPointSingle(key); | |
558 | |
559 case EVR_FD: // float double-precision | |
560 return new DcmFloatingPointDouble(key); | |
561 | |
562 | |
563 /** | |
564 * Sequence types, should never occur at this point. | |
565 **/ | |
566 | |
567 case EVR_SQ: // sequence of items | |
568 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
569 | |
570 | |
571 /** | |
572 * Internal to DCMTK. | |
573 **/ | |
574 | |
575 case EVR_ox: // OB or OW depending on context | |
576 case EVR_xs: // SS or US depending on context | |
577 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
578 case EVR_na: // na="not applicable", for data which has no VR | |
579 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
580 case EVR_item: // used internally for items | |
581 case EVR_metainfo: // used internally for meta info datasets | |
582 case EVR_dataset: // used internally for datasets | |
583 case EVR_fileFormat: // used internally for DICOM files | |
584 case EVR_dicomDir: // used internally for DICOMDIR objects | |
585 case EVR_dirRecord: // used internally for DICOMDIR records | |
586 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
587 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
588 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
589 case EVR_PixelData: // used internally for uncompressed pixeld data | |
590 case EVR_OverlayData: // used internally for overlay data | |
591 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
592 default: | |
593 break; | |
594 } | |
595 | |
596 throw OrthancException(ErrorCode_InternalError); | |
597 } | |
598 | |
599 | |
600 | |
601 static void FillElementWithString(DcmElement& element, | |
602 const DicomTag& tag, | |
603 const std::string& value) | |
604 { | |
605 DcmTag key(tag.GetGroup(), tag.GetElement()); | |
606 bool ok = false; | |
607 | |
608 try | |
609 { | |
610 switch (key.getEVR()) | |
611 { | |
612 // http://support.dcmtk.org/docs/dcvr_8h-source.html | |
613 | |
614 /** | |
615 * TODO. | |
616 **/ | |
617 | |
618 case EVR_OB: // other byte | |
619 case EVR_OF: // other float | |
620 case EVR_OW: // other word | |
621 case EVR_AT: // attribute tag | |
622 throw OrthancException(ErrorCode_NotImplemented); | |
623 | |
624 case EVR_UN: // unknown value representation | |
625 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
626 | |
627 | |
628 /** | |
629 * String types. | |
630 **/ | |
631 | |
632 case EVR_DS: // decimal string | |
633 case EVR_IS: // integer string | |
634 case EVR_AS: // age string | |
635 case EVR_DA: // date string | |
636 case EVR_DT: // date time string | |
637 case EVR_TM: // time string | |
638 case EVR_AE: // application entity title | |
639 case EVR_CS: // code string | |
640 case EVR_SH: // short string | |
641 case EVR_LO: // long string | |
642 case EVR_ST: // short text | |
643 case EVR_LT: // long text | |
644 case EVR_UT: // unlimited text | |
645 case EVR_PN: // person name | |
646 case EVR_UI: // unique identifier | |
647 { | |
648 ok = element.putString(value.c_str()).good(); | |
649 break; | |
650 } | |
651 | |
652 | |
653 /** | |
654 * Numerical types | |
655 **/ | |
656 | |
657 case EVR_SL: // signed long | |
658 { | |
659 ok = element.putSint32(boost::lexical_cast<Sint32>(value)).good(); | |
660 break; | |
661 } | |
662 | |
663 case EVR_SS: // signed short | |
664 { | |
665 ok = element.putSint16(boost::lexical_cast<Sint16>(value)).good(); | |
666 break; | |
667 } | |
668 | |
669 case EVR_UL: // unsigned long | |
670 { | |
671 ok = element.putUint32(boost::lexical_cast<Uint32>(value)).good(); | |
672 break; | |
673 } | |
674 | |
675 case EVR_US: // unsigned short | |
676 { | |
677 ok = element.putUint16(boost::lexical_cast<Uint16>(value)).good(); | |
678 break; | |
679 } | |
680 | |
681 case EVR_FL: // float single-precision | |
682 { | |
683 ok = element.putFloat32(boost::lexical_cast<float>(value)).good(); | |
684 break; | |
685 } | |
686 | |
687 case EVR_FD: // float double-precision | |
688 { | |
689 ok = element.putFloat64(boost::lexical_cast<double>(value)).good(); | |
690 break; | |
691 } | |
692 | |
693 | |
694 /** | |
695 * Sequence types, should never occur at this point. | |
696 **/ | |
697 | |
698 case EVR_SQ: // sequence of items | |
699 { | |
700 ok = false; | |
701 break; | |
702 } | |
703 | |
704 | |
705 /** | |
706 * Internal to DCMTK. | |
707 **/ | |
708 | |
709 case EVR_ox: // OB or OW depending on context | |
710 case EVR_xs: // SS or US depending on context | |
711 case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) | |
712 case EVR_na: // na="not applicable", for data which has no VR | |
713 case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor | |
714 case EVR_item: // used internally for items | |
715 case EVR_metainfo: // used internally for meta info datasets | |
716 case EVR_dataset: // used internally for datasets | |
717 case EVR_fileFormat: // used internally for DICOM files | |
718 case EVR_dicomDir: // used internally for DICOMDIR objects | |
719 case EVR_dirRecord: // used internally for DICOMDIR records | |
720 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | |
721 case EVR_pixelItem: // used internally for pixel items in a compressed image | |
722 case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) | |
723 case EVR_PixelData: // used internally for uncompressed pixeld data | |
724 case EVR_OverlayData: // used internally for overlay data | |
725 case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR | |
726 default: | |
727 break; | |
728 } | |
729 } | |
730 catch (boost::bad_lexical_cast&) | |
731 { | |
732 ok = false; | |
733 } | |
734 | |
735 if (!ok) | |
736 { | |
737 throw OrthancException(ErrorCode_InternalError); | |
738 } | |
739 } | |
740 | |
741 | |
742 void ParsedDicomFile::Remove(const DicomTag& tag) | |
743 { | |
744 DcmTagKey key(tag.GetGroup(), tag.GetElement()); | |
794 | 745 DcmElement* element = pimpl_->file_->getDataset()->remove(key); |
790 | 746 if (element != NULL) |
747 { | |
748 delete element; | |
749 } | |
750 } | |
751 | |
752 | |
753 | |
754 void ParsedDicomFile::RemovePrivateTags() | |
755 { | |
756 typedef std::list<DcmElement*> Tags; | |
757 | |
758 Tags privateTags; | |
759 | |
794 | 760 DcmDataset& dataset = *pimpl_->file_->getDataset(); |
790 | 761 for (unsigned long i = 0; i < dataset.card(); i++) |
762 { | |
763 DcmElement* element = dataset.getElement(i); | |
764 DcmTag tag(element->getTag()); | |
765 if (!strcmp("PrivateCreator", tag.getTagName()) || // TODO - This may change with future versions of DCMTK | |
766 tag.getPrivateCreator() != NULL) | |
767 { | |
768 privateTags.push_back(element); | |
769 } | |
770 } | |
771 | |
772 for (Tags::iterator it = privateTags.begin(); | |
773 it != privateTags.end(); ++it) | |
774 { | |
775 DcmElement* tmp = dataset.remove(*it); | |
776 if (tmp != NULL) | |
777 { | |
778 delete tmp; | |
779 } | |
780 } | |
781 } | |
782 | |
783 | |
784 | |
785 void ParsedDicomFile::Insert(const DicomTag& tag, | |
786 const std::string& value) | |
787 { | |
788 std::auto_ptr<DcmElement> element(CreateElementForTag(tag)); | |
789 FillElementWithString(*element, tag, value); | |
790 | |
794 | 791 if (!pimpl_->file_->getDataset()->insert(element.release(), false, false).good()) |
790 | 792 { |
793 // This field already exists | |
794 throw OrthancException(ErrorCode_InternalError); | |
795 } | |
796 } | |
797 | |
798 | |
799 void ParsedDicomFile::Replace(const DicomTag& tag, | |
800 const std::string& value, | |
801 DicomReplaceMode mode) | |
802 { | |
803 DcmTagKey key(tag.GetGroup(), tag.GetElement()); | |
804 DcmElement* element = NULL; | |
805 | |
794 | 806 if (!pimpl_->file_->getDataset()->findAndGetElement(key, element).good() || |
790 | 807 element == NULL) |
808 { | |
809 // This field does not exist, act wrt. the specified "mode" | |
810 switch (mode) | |
811 { | |
812 case DicomReplaceMode_InsertIfAbsent: | |
813 Insert(tag, value); | |
814 break; | |
815 | |
816 case DicomReplaceMode_ThrowIfAbsent: | |
817 throw OrthancException(ErrorCode_InexistentItem); | |
818 | |
819 case DicomReplaceMode_IgnoreIfAbsent: | |
820 return; | |
821 } | |
822 } | |
823 else | |
824 { | |
825 FillElementWithString(*element, tag, value); | |
826 } | |
827 | |
828 | |
829 /** | |
830 * dcmodify will automatically correct 'Media Storage SOP Class | |
831 * UID' and 'Media Storage SOP Instance UID' in the metaheader, if | |
832 * you make changes to the related tags in the dataset ('SOP Class | |
833 * UID' and 'SOP Instance UID') via insert or modify mode | |
834 * options. You can disable this behaviour by using the -nmu | |
835 * option. | |
836 **/ | |
837 | |
838 if (tag == DICOM_TAG_SOP_CLASS_UID) | |
839 { | |
840 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_CLASS_UID, value, DicomReplaceMode_InsertIfAbsent); | |
841 } | |
842 | |
843 if (tag == DICOM_TAG_SOP_INSTANCE_UID) | |
844 { | |
845 Replace(DICOM_TAG_MEDIA_STORAGE_SOP_INSTANCE_UID, value, DicomReplaceMode_InsertIfAbsent); | |
846 } | |
847 } | |
848 | |
849 | |
850 void ParsedDicomFile::Answer(RestApiOutput& output) | |
851 { | |
852 std::string serialized; | |
794 | 853 if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, pimpl_->file_->getDataset())) |
790 | 854 { |
855 output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM); | |
856 } | |
857 } | |
858 | |
859 | |
860 | |
861 bool ParsedDicomFile::GetTagValue(std::string& value, | |
862 const DicomTag& tag) | |
863 { | |
864 DcmTagKey k(tag.GetGroup(), tag.GetElement()); | |
794 | 865 DcmDataset& dataset = *pimpl_->file_->getDataset(); |
790 | 866 DcmElement* element = NULL; |
867 if (!dataset.findAndGetElement(k, element).good() || | |
868 element == NULL) | |
869 { | |
870 return false; | |
871 } | |
872 | |
873 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element)); | |
874 | |
875 if (v.get() == NULL) | |
876 { | |
877 value = ""; | |
878 } | |
879 else | |
880 { | |
881 value = v->AsString(); | |
882 } | |
883 | |
884 return true; | |
885 } | |
886 | |
887 | |
888 | |
889 DicomInstanceHasher ParsedDicomFile::GetHasher() | |
890 { | |
891 std::string patientId, studyUid, seriesUid, instanceUid; | |
892 | |
893 if (!GetTagValue(patientId, DICOM_TAG_PATIENT_ID) || | |
894 !GetTagValue(studyUid, DICOM_TAG_STUDY_INSTANCE_UID) || | |
895 !GetTagValue(seriesUid, DICOM_TAG_SERIES_INSTANCE_UID) || | |
896 !GetTagValue(instanceUid, DICOM_TAG_SOP_INSTANCE_UID)) | |
897 { | |
898 throw OrthancException(ErrorCode_BadFileFormat); | |
899 } | |
900 | |
901 return DicomInstanceHasher(patientId, studyUid, seriesUid, instanceUid); | |
902 } | |
903 | |
904 | |
905 static void StoreElement(Json::Value& target, | |
906 DcmElement& element, | |
907 unsigned int maxStringLength); | |
908 | |
909 static void StoreItem(Json::Value& target, | |
910 DcmItem& item, | |
911 unsigned int maxStringLength) | |
912 { | |
913 target = Json::Value(Json::objectValue); | |
914 | |
915 for (unsigned long i = 0; i < item.card(); i++) | |
916 { | |
917 DcmElement* element = item.getElement(i); | |
918 StoreElement(target, *element, maxStringLength); | |
919 } | |
920 } | |
921 | |
922 | |
923 static void StoreElement(Json::Value& target, | |
924 DcmElement& element, | |
925 unsigned int maxStringLength) | |
926 { | |
927 assert(target.type() == Json::objectValue); | |
928 | |
929 DicomTag tag(FromDcmtkBridge::GetTag(element)); | |
930 const std::string formattedTag = tag.Format(); | |
931 | |
932 #if 0 | |
933 const std::string tagName = FromDcmtkBridge::GetName(tag); | |
934 #else | |
935 // This version of the code gives access to the name of the private tags | |
936 DcmTag tagbis(element.getTag()); | |
937 const std::string tagName(tagbis.getTagName()); | |
938 #endif | |
939 | |
940 if (element.isLeaf()) | |
941 { | |
942 Json::Value value(Json::objectValue); | |
943 value["Name"] = tagName; | |
944 | |
945 if (tagbis.getPrivateCreator() != NULL) | |
946 { | |
947 value["PrivateCreator"] = tagbis.getPrivateCreator(); | |
948 } | |
949 | |
950 std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element)); | |
951 if (v->IsNull()) | |
952 { | |
953 value["Type"] = "Null"; | |
954 value["Value"] = Json::nullValue; | |
955 } | |
956 else | |
957 { | |
958 std::string s = v->AsString(); | |
959 if (maxStringLength == 0 || | |
960 s.size() <= maxStringLength) | |
961 { | |
962 value["Type"] = "String"; | |
963 value["Value"] = s; | |
964 } | |
965 else | |
966 { | |
967 value["Type"] = "TooLong"; | |
968 value["Value"] = Json::nullValue; | |
969 } | |
970 } | |
971 | |
972 target[formattedTag] = value; | |
973 } | |
974 else | |
975 { | |
976 Json::Value children(Json::arrayValue); | |
977 | |
978 // "All subclasses of DcmElement except for DcmSequenceOfItems | |
979 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset | |
980 // etc. are not." The following cast is thus OK. | |
981 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(element); | |
982 | |
983 for (unsigned long i = 0; i < sequence.card(); i++) | |
984 { | |
985 DcmItem* child = sequence.getItem(i); | |
986 Json::Value& v = children.append(Json::objectValue); | |
987 StoreItem(v, *child, maxStringLength); | |
988 } | |
989 | |
990 target[formattedTag]["Name"] = tagName; | |
991 target[formattedTag]["Type"] = "Sequence"; | |
992 target[formattedTag]["Value"] = children; | |
993 } | |
994 } | |
995 | |
996 | |
997 template <typename T> | |
998 static void ExtractPngImageTruncate(std::string& result, | |
999 DicomIntegerPixelAccessor& accessor, | |
1000 PixelFormat format) | |
1001 { | |
1002 assert(accessor.GetChannelCount() == 1); | |
1003 | |
1004 PngWriter w; | |
1005 | |
1006 std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0); | |
1007 T* pixel = &image[0]; | |
1008 for (unsigned int y = 0; y < accessor.GetHeight(); y++) | |
1009 { | |
1010 for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) | |
1011 { | |
1012 int32_t v = accessor.GetValue(x, y); | |
1013 if (v < static_cast<int32_t>(std::numeric_limits<T>::min())) | |
1014 *pixel = std::numeric_limits<T>::min(); | |
1015 else if (v > static_cast<int32_t>(std::numeric_limits<T>::max())) | |
1016 *pixel = std::numeric_limits<T>::max(); | |
1017 else | |
1018 *pixel = static_cast<T>(v); | |
1019 } | |
1020 } | |
1021 | |
1022 w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), | |
1023 accessor.GetWidth() * sizeof(T), format, &image[0]); | |
1024 } | |
1025 | |
1026 | |
1027 void ParsedDicomFile::SaveToMemoryBuffer(std::string& buffer) | |
1028 { | |
794 | 1029 FromDcmtkBridge::SaveToMemoryBuffer(buffer, pimpl_->file_->getDataset()); |
790 | 1030 } |
1031 | |
1032 | |
1033 void ParsedDicomFile::SaveToFile(const std::string& path) | |
1034 { | |
1035 // TODO Avoid using a temporary memory buffer, write directly on disk | |
1036 std::string content; | |
1037 SaveToMemoryBuffer(content); | |
1038 Toolbox::WriteFile(content, path); | |
1039 } | |
1040 | |
1041 | |
794 | 1042 ParsedDicomFile::ParsedDicomFile() : pimpl_(new PImpl) |
790 | 1043 { |
794 | 1044 pimpl_->file_.reset(new DcmFileFormat); |
790 | 1045 Replace(DICOM_TAG_PATIENT_ID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Patient)); |
1046 Replace(DICOM_TAG_STUDY_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Study)); | |
1047 Replace(DICOM_TAG_SERIES_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Series)); | |
1048 Replace(DICOM_TAG_SOP_INSTANCE_UID, FromDcmtkBridge::GenerateUniqueIdentifier(ResourceType_Instance)); | |
1049 } | |
1050 | |
791 | 1051 |
794 | 1052 ParsedDicomFile::ParsedDicomFile(const char* content, size_t size) : pimpl_(new PImpl) |
791 | 1053 { |
1054 Setup(content, size); | |
1055 } | |
1056 | |
794 | 1057 ParsedDicomFile::ParsedDicomFile(const std::string& content) : pimpl_(new PImpl) |
791 | 1058 { |
1059 if (content.size() == 0) | |
1060 { | |
1061 Setup(NULL, 0); | |
1062 } | |
1063 else | |
1064 { | |
1065 Setup(&content[0], content.size()); | |
1066 } | |
1067 } | |
792 | 1068 |
1069 | |
794 | 1070 ParsedDicomFile::ParsedDicomFile(ParsedDicomFile& other) : |
1071 pimpl_(new PImpl) | |
792 | 1072 { |
794 | 1073 pimpl_->file_.reset(dynamic_cast<DcmFileFormat*>(other.pimpl_->file_->clone())); |
792 | 1074 } |
1075 | |
1076 | |
1077 ParsedDicomFile::~ParsedDicomFile() | |
1078 { | |
794 | 1079 delete pimpl_; |
792 | 1080 } |
1081 | |
793 | 1082 |
1083 void* ParsedDicomFile::GetDcmtkObject() | |
1084 { | |
794 | 1085 return pimpl_->file_.get(); |
793 | 1086 } |
1087 | |
1088 | |
1089 ParsedDicomFile* ParsedDicomFile::Clone() | |
1090 { | |
794 | 1091 return new ParsedDicomFile(*this); |
793 | 1092 } |
800
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1093 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1094 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1095 void ParsedDicomFile::EmbedImage(const std::string& dataUriScheme) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1096 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1097 std::string mime, content; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1098 Toolbox::DecodeDataUriScheme(mime, content, dataUriScheme); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1099 |
809
8ce2f69436ca
do not return strings with base64
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
800
diff
changeset
|
1100 std::string decoded; |
8ce2f69436ca
do not return strings with base64
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
800
diff
changeset
|
1101 Toolbox::DecodeBase64(decoded, content); |
800
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1102 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1103 if (mime == "image/png") |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1104 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1105 PngReader reader; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1106 reader.ReadFromMemory(decoded); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1107 EmbedImage(reader); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1108 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1109 else |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1110 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1111 throw OrthancException(ErrorCode_NotImplemented); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1112 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1113 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1114 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1115 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1116 void ParsedDicomFile::EmbedImage(const ImageAccessor& accessor) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1117 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1118 if (accessor.GetFormat() != PixelFormat_Grayscale8 && |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1119 accessor.GetFormat() != PixelFormat_Grayscale16 && |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1120 accessor.GetFormat() != PixelFormat_RGB24 && |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1121 accessor.GetFormat() != PixelFormat_RGBA32) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1122 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1123 throw OrthancException(ErrorCode_NotImplemented); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1124 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1125 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1126 if (accessor.GetFormat() == PixelFormat_RGBA32) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1127 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1128 LOG(WARNING) << "Getting rid of the alpha channel when embedding a RGBA image inside DICOM"; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1129 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1130 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1131 // http://dicomiseasy.blogspot.be/2012/08/chapter-12-pixel-data.html |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1132 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1133 Remove(DICOM_TAG_PIXEL_DATA); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1134 Replace(DICOM_TAG_COLUMNS, boost::lexical_cast<std::string>(accessor.GetWidth())); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1135 Replace(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight())); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1136 Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "1"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1137 Replace(DICOM_TAG_NUMBER_OF_FRAMES, "1"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1138 Replace(DICOM_TAG_PIXEL_REPRESENTATION, "0"); // Unsigned pixels |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1139 Replace(DICOM_TAG_PLANAR_CONFIGURATION, "0"); // Color channels are interleaved |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1140 Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1141 Replace(DICOM_TAG_BITS_ALLOCATED, "8"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1142 Replace(DICOM_TAG_BITS_STORED, "8"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1143 Replace(DICOM_TAG_HIGH_BIT, "7"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1144 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1145 unsigned int bytesPerPixel = 1; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1146 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1147 switch (accessor.GetFormat()) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1148 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1149 case PixelFormat_RGB24: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1150 case PixelFormat_RGBA32: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1151 Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1152 Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "3"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1153 bytesPerPixel = 3; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1154 break; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1155 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1156 case PixelFormat_Grayscale8: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1157 break; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1158 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1159 case PixelFormat_Grayscale16: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1160 Replace(DICOM_TAG_BITS_ALLOCATED, "16"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1161 Replace(DICOM_TAG_BITS_STORED, "16"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1162 Replace(DICOM_TAG_HIGH_BIT, "15"); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1163 bytesPerPixel = 2; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1164 break; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1165 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1166 default: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1167 throw OrthancException(ErrorCode_NotImplemented); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1168 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1169 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1170 DcmTag key(DICOM_TAG_PIXEL_DATA.GetGroup(), |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1171 DICOM_TAG_PIXEL_DATA.GetElement()); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1172 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1173 std::auto_ptr<DcmPixelData> pixels(new DcmPixelData(key)); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1174 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1175 unsigned int pitch = accessor.GetWidth() * bytesPerPixel; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1176 Uint8* target = NULL; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1177 pixels->createUint8Array(accessor.GetHeight() * pitch, target); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1178 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1179 for (unsigned int y = 0; y < accessor.GetHeight(); y++) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1180 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1181 switch (accessor.GetFormat()) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1182 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1183 case PixelFormat_RGB24: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1184 case PixelFormat_Grayscale8: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1185 case PixelFormat_Grayscale16: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1186 case PixelFormat_SignedGrayscale16: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1187 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1188 memcpy(target, reinterpret_cast<const Uint8*>(accessor.GetConstRow(y)), pitch); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1189 target += pitch; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1190 break; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1191 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1192 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1193 case PixelFormat_RGBA32: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1194 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1195 // The alpha channel is not supported by the DICOM standard |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1196 const Uint8* source = reinterpret_cast<const Uint8*>(accessor.GetConstRow(y)); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1197 for (unsigned int x = 0; x < accessor.GetWidth(); x++, target += 3, source += 4) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1198 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1199 target[0] = source[0]; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1200 target[1] = source[1]; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1201 target[2] = source[2]; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1202 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1203 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1204 break; |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1205 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1206 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1207 default: |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1208 throw OrthancException(ErrorCode_NotImplemented); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1209 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1210 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1211 |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1212 if (!pimpl_->file_->getDataset()->insert(pixels.release(), false, false).good()) |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1213 { |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1214 throw OrthancException(ErrorCode_InternalError); |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1215 } |
ecedd89055db
generation of DICOM images from PNG files
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
799
diff
changeset
|
1216 } |
790 | 1217 } |