/src/dcmtk/dcmimgle/libsrc/dcmimage.cc
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (C) 1996-2025, OFFIS e.V. |
4 | | * All rights reserved. See COPYRIGHT file for details. |
5 | | * |
6 | | * This software and supporting documentation were developed by |
7 | | * |
8 | | * OFFIS e.V. |
9 | | * R&D Division Health |
10 | | * Escherweg 2 |
11 | | * D-26121 Oldenburg, Germany |
12 | | * |
13 | | * |
14 | | * Module: dcmimgle |
15 | | * |
16 | | * Author: Joerg Riesmeier |
17 | | * |
18 | | * Purpose: DicomImage-Interface (Source) |
19 | | * |
20 | | */ |
21 | | |
22 | | |
23 | | #include "dcmtk/config/osconfig.h" |
24 | | #include "dcmtk/dcmimgle/dcmimage.h" |
25 | | |
26 | | #include "dcmtk/dcmdata/dctypes.h" |
27 | | #include "dcmtk/dcmdata/dcdeftag.h" |
28 | | #include "dcmtk/dcmdata/dcobject.h" |
29 | | #include "dcmtk/dcmdata/dcuid.h" |
30 | | #include "dcmtk/dcmdata/dcdict.h" |
31 | | #include "dcmtk/ofstd/ofstd.h" |
32 | | |
33 | | #include "dcmtk/dcmimgle/diovlimg.h" |
34 | | #include "dcmtk/dcmimgle/dimo1img.h" |
35 | | #include "dcmtk/dcmimgle/dimo2img.h" |
36 | | #include "dcmtk/dcmimgle/didocu.h" |
37 | | #include "dcmtk/dcmimgle/diregbas.h" |
38 | | #include "dcmtk/dcmimgle/diplugin.h" |
39 | | |
40 | | #ifndef FILENAME_MAX |
41 | | #define FILENAME_MAX 255 |
42 | | #endif |
43 | | |
44 | | |
45 | | /*------------------* |
46 | | * initialization * |
47 | | *------------------*/ |
48 | | |
49 | | DiRegisterBase *DiRegisterBase::Pointer = NULL; |
50 | | |
51 | | |
52 | | /*----------------* |
53 | | * constructors * |
54 | | *----------------*/ |
55 | | |
56 | | // --- create 'DicomImage' from 'filename', for valid 'flags' see 'diutils.h' |
57 | | |
58 | | DicomImage::DicomImage(const OFFilename &filename, |
59 | | const unsigned long flags, |
60 | | const unsigned long fstart, |
61 | | const unsigned long fcount) |
62 | 0 | : ImageStatus(EIS_Normal), |
63 | 0 | PhotometricInterpretation(EPI_Unknown), |
64 | 0 | Document(NULL), |
65 | 0 | Image(NULL) |
66 | 0 | { |
67 | 0 | if (checkDataDictionary()) // valid 'dicom.dic' found ? |
68 | 0 | { |
69 | 0 | Document = new DiDocument(filename, flags | CIF_MayDetachPixelData, fstart, fcount); |
70 | 0 | Init(); |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | |
75 | | // --- create 'DicomImage' from valid 'DicomObject' with transfer syntax 'xfer' |
76 | | |
77 | | DicomImage::DicomImage(DcmObject *object, |
78 | | const E_TransferSyntax xfer, |
79 | | const unsigned long flags, |
80 | | const unsigned long fstart, |
81 | | const unsigned long fcount) |
82 | 1.04k | : ImageStatus(EIS_Normal), |
83 | 1.04k | PhotometricInterpretation(EPI_Unknown), |
84 | 1.04k | Document(NULL), |
85 | 1.04k | Image(NULL) |
86 | 1.04k | { |
87 | 1.04k | if (checkDataDictionary()) // valid 'dicom.dic' found ? |
88 | 0 | { |
89 | 0 | Document = new DiDocument(object, xfer, flags, fstart, fcount); |
90 | 0 | Init(); |
91 | 0 | } |
92 | 1.04k | } |
93 | | |
94 | | |
95 | | // --- create 'DicomImage' from valid 'DicomObject' with given rescale 'slope' and 'intercept' |
96 | | |
97 | | DicomImage::DicomImage(DcmObject *object, |
98 | | const E_TransferSyntax xfer, |
99 | | const double slope, |
100 | | const double intercept, |
101 | | const unsigned long flags, |
102 | | const unsigned long fstart, |
103 | | const unsigned long fcount) |
104 | 0 | : ImageStatus(EIS_Normal), |
105 | 0 | PhotometricInterpretation(EPI_Unknown), |
106 | 0 | Document(NULL), |
107 | 0 | Image(NULL) |
108 | 0 | { |
109 | 0 | if (checkDataDictionary()) // valid 'dicom.dic' found ? |
110 | 0 | { |
111 | 0 | Document = new DiDocument(object, xfer, flags, fstart, fcount); |
112 | 0 | if ((Document != NULL) && (Document->good())) |
113 | 0 | { |
114 | 0 | PhotometricInterpretation = EPI_Monochrome2; // default for presentation states |
115 | 0 | Image = new DiMono2Image(Document, ImageStatus, slope, intercept); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | |
121 | | // --- create 'DicomImage' from valid 'DicomObject' with given modality LUT (specified by 'data' and 'descriptor') |
122 | | |
123 | | DicomImage::DicomImage(DcmObject *object, |
124 | | E_TransferSyntax xfer, |
125 | | const DcmUnsignedShort &data, |
126 | | const DcmUnsignedShort &descriptor, |
127 | | const DcmLongString *explanation, |
128 | | const unsigned long flags, |
129 | | const unsigned long fstart, |
130 | | const unsigned long fcount) |
131 | 0 | : ImageStatus(EIS_Normal), |
132 | 0 | PhotometricInterpretation(EPI_Unknown), |
133 | 0 | Document(NULL), |
134 | 0 | Image(NULL) |
135 | 0 | { |
136 | 0 | if (checkDataDictionary()) // valid 'dicom.dic' found ? |
137 | 0 | { |
138 | 0 | Document = new DiDocument(object, xfer, flags, fstart, fcount); |
139 | 0 | if ((Document != NULL) && (Document->good())) |
140 | 0 | { |
141 | 0 | PhotometricInterpretation = EPI_Monochrome2; // default for presentation states |
142 | 0 | Image = new DiMono2Image(Document, ImageStatus, data, descriptor, explanation); |
143 | 0 | } |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | |
148 | | // --- protected: create 'DicomImage' from source with different image data and photometric interpretation |
149 | | |
150 | | DicomImage::DicomImage(const DicomImage *dicom, |
151 | | DiImage *image, |
152 | | const EP_Interpretation interpret) |
153 | 0 | : ImageStatus(dicom->ImageStatus), |
154 | 0 | PhotometricInterpretation(dicom->PhotometricInterpretation), |
155 | 0 | Document(dicom->Document), // necessary, ever used ?? |
156 | 0 | Image(image) |
157 | 0 | { |
158 | 0 | if (interpret != EPI_Unknown) |
159 | 0 | PhotometricInterpretation = interpret; |
160 | 0 | if (Document != NULL) |
161 | 0 | Document->addReference(); // 'Document' is only referenced not copied ! |
162 | 0 | } |
163 | | |
164 | | |
165 | | /*--------------* |
166 | | * destructor * |
167 | | *--------------*/ |
168 | | |
169 | | DicomImage::~DicomImage() |
170 | 1.04k | { |
171 | 1.04k | delete Image; |
172 | 1.04k | if (Document != NULL) |
173 | 0 | Document->removeReference(); // only delete if object is no longer referenced |
174 | 1.04k | } |
175 | | |
176 | | |
177 | | /*********************************************************************/ |
178 | | |
179 | | // --- initialize 'DicomImage' object (same procedure for every 'real' constructor) |
180 | | |
181 | | void DicomImage::Init() |
182 | 0 | { |
183 | 0 | if ((Document != NULL) && (Document->good())) |
184 | 0 | { |
185 | 0 | const char *str; |
186 | 0 | if (hasSOPclassUID(UID_RETIRED_StandaloneOverlayStorage)) |
187 | 0 | { |
188 | 0 | PhotometricInterpretation = EPI_Monochrome2; // standalone overlays are handled like monochrome |
189 | 0 | Image = new DiOverlayImage(Document, ImageStatus); // images without pixel data |
190 | 0 | } |
191 | 0 | else if (Document->getFlags() & CIF_UsePresentationState) |
192 | 0 | { |
193 | 0 | PhotometricInterpretation = EPI_Monochrome2; // default for presentation states |
194 | 0 | Image = new DiMono2Image(Document, ImageStatus); |
195 | 0 | } |
196 | 0 | else if (strlen(str = Document->getPhotometricInterpretation()) > 0) |
197 | 0 | { |
198 | 0 | const SP_Interpretation *pin = PhotometricInterpretationNames; |
199 | 0 | char *cstr = new char[strlen(str) + 1]; |
200 | 0 | if (cstr != NULL) |
201 | 0 | { |
202 | 0 | char *q = cstr; |
203 | 0 | char c; |
204 | 0 | for (const char *p = str; *p != 0; p++) // remove invalid chars |
205 | 0 | { |
206 | 0 | c = *p; |
207 | 0 | if (isalpha(c)) |
208 | 0 | *(q++) = OFstatic_cast(char, toupper(c)); |
209 | 0 | else if (isdigit(c)) |
210 | 0 | *(q++) = c; |
211 | 0 | } |
212 | 0 | *q = '\0'; // end of C string |
213 | 0 | DCMIMGLE_DEBUG("filtered version of 'PhotometricInterpretation' = " << OFSTRING_GUARD(cstr)); |
214 | 0 | while ((pin->Name != NULL) && (strcmp(pin->Name, cstr) != 0)) |
215 | 0 | ++pin; |
216 | 0 | delete[] cstr; |
217 | 0 | } else { |
218 | 0 | DCMIMGLE_WARN("can't create filtered version of 'PhotometricInterpretation' (" << str << ")"); |
219 | 0 | cstr = OFconst_cast(char *, str); // just reference the original string |
220 | 0 | while ((pin->DefinedTerm != NULL) && (strcmp(pin->DefinedTerm, cstr) != 0)) |
221 | 0 | ++pin; |
222 | 0 | } |
223 | 0 | PhotometricInterpretation = pin->Type; // unknown if last entry |
224 | 0 | switch (PhotometricInterpretation) |
225 | 0 | { |
226 | 0 | case EPI_Monochrome1: // create 'Image' depending on color model |
227 | 0 | Image = new DiMono1Image(Document, ImageStatus); |
228 | 0 | break; |
229 | 0 | case EPI_Monochrome2: |
230 | 0 | Image = new DiMono2Image(Document, ImageStatus); |
231 | 0 | break; |
232 | 0 | default: // unknown or unsupported color model |
233 | 0 | if (DiRegisterBase::Pointer != NULL) |
234 | 0 | Image = DiRegisterBase::Pointer->createImage(Document, ImageStatus, PhotometricInterpretation); |
235 | 0 | else |
236 | 0 | DCMIMGLE_DEBUG("Support for color images not registered, need to include \"dcmtk/dcmimage/diregist.h\""); |
237 | 0 | if (Image == NULL) |
238 | 0 | { |
239 | 0 | if (PhotometricInterpretation == EPI_Unknown) |
240 | 0 | { |
241 | 0 | ImageStatus = EIS_InvalidValue; |
242 | 0 | DCMIMGLE_ERROR("invalid value for 'PhotometricInterpretation' (" << str << ")"); |
243 | 0 | } else { |
244 | 0 | ImageStatus = EIS_NotSupportedValue; |
245 | 0 | DCMIMGLE_ERROR("unsupported value for 'PhotometricInterpretation' (" << str << ")"); |
246 | 0 | } |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } |
250 | 0 | else if (Document->getFlags() & CIF_AcrNemaCompatibility) // ACR-NEMA has no 'photometric interpretation' |
251 | 0 | { |
252 | 0 | PhotometricInterpretation = EPI_Monochrome2; |
253 | 0 | Image = new DiMono2Image(Document, ImageStatus); |
254 | 0 | } else { |
255 | 0 | ImageStatus = EIS_MissingAttribute; |
256 | 0 | PhotometricInterpretation = EPI_Missing; |
257 | 0 | if (Document->getPixelData() != NULL) |
258 | 0 | DCMIMGLE_ERROR("mandatory attribute 'PhotometricInterpretation' is missing or can't be determined"); |
259 | 0 | } |
260 | 0 | } |
261 | 0 | else |
262 | 0 | ImageStatus = EIS_InvalidDocument; |
263 | 0 | } |
264 | | |
265 | | |
266 | | // --- check whether the loadable 'DataDictionary' is present/loaded |
267 | | |
268 | | int DicomImage::checkDataDictionary() |
269 | 1.04k | { |
270 | 1.04k | if (!dcmDataDict.isDictionaryLoaded()) |
271 | 1.04k | { |
272 | 1.04k | ImageStatus = EIS_NoDataDictionary; |
273 | 1.04k | DCMIMGLE_ERROR("can't load data dictionary"); |
274 | 1.04k | } |
275 | 1.04k | return ImageStatus == EIS_Normal; |
276 | 1.04k | } |
277 | | |
278 | | |
279 | | /*********************************************************************/ |
280 | | |
281 | | |
282 | | const char *DicomImage::getString(const EI_Status status) |
283 | 0 | { |
284 | 0 | switch (status) |
285 | 0 | { |
286 | 0 | case EIS_Normal: |
287 | 0 | return "Status OK"; |
288 | 0 | case EIS_NoDataDictionary: |
289 | 0 | return "No data dictionary"; |
290 | 0 | case EIS_InvalidDocument: |
291 | 0 | return "Invalid DICOM document"; |
292 | 0 | case EIS_MissingAttribute: |
293 | 0 | return "Missing attribute"; |
294 | 0 | case EIS_InvalidValue: |
295 | 0 | return "Invalid element value"; |
296 | 0 | case EIS_NotSupportedValue: |
297 | 0 | return "Unsupported element value"; |
298 | 0 | case EIS_MemoryFailure: |
299 | 0 | return "Out of memory"; |
300 | 0 | case EIS_InvalidImage: |
301 | 0 | return "Invalid DICOM image"; |
302 | 0 | case EIS_OtherError: |
303 | 0 | default: |
304 | 0 | return "Unspecified"; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | |
309 | | const char *DicomImage::getString(const EP_Interpretation interpret) |
310 | 0 | { |
311 | 0 | const SP_Interpretation *pin = PhotometricInterpretationNames; |
312 | 0 | while ((pin->DefinedTerm != NULL) && (pin->Type != interpret)) |
313 | 0 | ++pin; |
314 | 0 | return pin->DefinedTerm; |
315 | 0 | } |
316 | | |
317 | | |
318 | | // --- return unique 'SOPclassUID' string |
319 | | |
320 | | const char *DicomImage::getSOPclassUID() const |
321 | 0 | { |
322 | 0 | if (Document != NULL) |
323 | 0 | { |
324 | 0 | const char *str; |
325 | 0 | if (Document->getValue(DCM_SOPClassUID, str)) |
326 | 0 | return str; |
327 | 0 | } |
328 | 0 | return NULL; |
329 | 0 | } |
330 | | |
331 | | |
332 | | // --- return 'true' (1) if 'Document' has the same 'SOPclassUID' as given in parameter 'uid' |
333 | | |
334 | | int DicomImage::hasSOPclassUID(const char *uid) const |
335 | 0 | { |
336 | 0 | const char *str = getSOPclassUID(); |
337 | 0 | return (str != NULL) && (strcmp(str, uid) == 0); |
338 | 0 | } |
339 | | |
340 | | |
341 | | // --- create new 'DicomImage' with 'fcount' frames starting with frame 'fstart' |
342 | | |
343 | | DicomImage *DicomImage::createDicomImage(unsigned long fstart, |
344 | | unsigned long fcount) const |
345 | 0 | { |
346 | 0 | if ((Image != NULL) && (fstart < getFrameCount())) |
347 | 0 | { |
348 | 0 | if ((fcount == 0) || (fstart + fcount > getFrameCount())) |
349 | 0 | fcount = getFrameCount() - fstart; |
350 | 0 | DiImage *image = Image->createImage(fstart, fcount); |
351 | 0 | if (image != NULL) |
352 | 0 | { |
353 | 0 | DicomImage *dicom = new DicomImage(this, image); |
354 | 0 | return dicom; |
355 | 0 | } |
356 | 0 | } |
357 | 0 | return NULL; |
358 | 0 | } |
359 | | |
360 | | |
361 | | // --- create scaled to given size ('width' and 'height') image, memory isn't handled internally ! |
362 | | // --- if one dimension is '0' the other is automatically adjusted (with respect to pixel aspect ratio) |
363 | | |
364 | | DicomImage *DicomImage::createScaledImage(const unsigned long width, |
365 | | const unsigned long height, |
366 | | const int interpolate, |
367 | | int aspect) const |
368 | 0 | { |
369 | 0 | return createScaledImage(0, 0, getWidth(), getHeight(), width, height, interpolate, aspect); |
370 | 0 | } |
371 | | |
372 | | |
373 | | // --- create scaled with given factors ('xfactor' and 'yfactor') image, memory isn't handled internally ! |
374 | | |
375 | | DicomImage *DicomImage::createScaledImage(const double xfactor, |
376 | | const double yfactor, |
377 | | const int interpolate, |
378 | | const int aspect) const |
379 | 0 | { |
380 | 0 | return createScaledImage(0, 0, getWidth(), getHeight(), OFstatic_cast(unsigned long, xfactor * getWidth()), |
381 | 0 | OFstatic_cast(unsigned long, yfactor * getHeight()), interpolate, aspect); |
382 | 0 | } |
383 | | |
384 | | |
385 | | // --- clip & scale |
386 | | |
387 | | DicomImage *DicomImage::createScaledImage(const signed long left_pos, |
388 | | const signed long top_pos, |
389 | | unsigned long clip_width, |
390 | | unsigned long clip_height, |
391 | | unsigned long scale_width, |
392 | | unsigned long scale_height, |
393 | | const int interpolate, |
394 | | int aspect, |
395 | | const Uint16 pvalue) const |
396 | 0 | { |
397 | 0 | const unsigned long gw = getWidth(); |
398 | 0 | const unsigned long gh = getHeight(); |
399 | 0 | if ((Image != NULL) && (gw > 0) && (gh > 0)) |
400 | 0 | { |
401 | 0 | if ((clip_width == 0) && (left_pos < OFstatic_cast(signed long, gw))) // set 'width' if parameter is missing |
402 | 0 | clip_width = gw - left_pos; |
403 | 0 | if ((clip_height == 0) && (top_pos < OFstatic_cast(signed long, gh))) // same for 'height' |
404 | 0 | clip_height = gh - top_pos; |
405 | 0 | if ((scale_width == 0) && (scale_height == 0)) |
406 | 0 | { |
407 | 0 | scale_width = clip_width; // auto-set width/height |
408 | 0 | scale_height = clip_height; |
409 | 0 | } |
410 | 0 | else if ((clip_width > 0) && (clip_height > 0)) |
411 | 0 | { |
412 | 0 | if (aspect) // maintain pixel aspect ratio |
413 | 0 | { |
414 | 0 | if (scale_width == 0) |
415 | 0 | scale_width = OFstatic_cast(unsigned long, getWidthHeightRatio() * OFstatic_cast(double, scale_height * clip_width) / clip_height); |
416 | 0 | else if (scale_height == 0) |
417 | 0 | scale_height = OFstatic_cast(unsigned long, getHeightWidthRatio() * OFstatic_cast(double, scale_width * clip_height) / clip_width); |
418 | 0 | else |
419 | 0 | aspect = 0; // ignore pixel aspect ratio |
420 | 0 | } |
421 | 0 | else // ignore pixel aspect ratio |
422 | 0 | { |
423 | 0 | if (scale_width == 0) |
424 | 0 | scale_width = OFstatic_cast(unsigned long, OFstatic_cast(double, scale_height * clip_width) / clip_height); |
425 | 0 | else if (scale_height == 0) |
426 | 0 | scale_height = OFstatic_cast(unsigned long, OFstatic_cast(double, scale_width * clip_height) / clip_width); |
427 | 0 | } |
428 | 0 | } |
429 | 0 | const unsigned long maxvalue = DicomImageClass::maxval(bitsof(Uint16)); |
430 | 0 | if (scale_width > maxvalue) |
431 | 0 | scale_width = maxvalue; // limit 'width' to maximum value (65535) |
432 | 0 | if (scale_height > maxvalue) |
433 | 0 | scale_height = maxvalue; // same for 'height' |
434 | | |
435 | | /* need to limit clipping region ... !? */ |
436 | |
|
437 | 0 | if (((left_pos < 0) || (OFstatic_cast(unsigned long, left_pos + clip_width) > gw) || |
438 | 0 | (top_pos < 0) || (OFstatic_cast(unsigned long, top_pos + clip_height) > gh)) && |
439 | 0 | ((clip_width != scale_width) || (clip_height != scale_height))) |
440 | 0 | { |
441 | 0 | DCMIMGLE_ERROR("combined clipping & scaling outside image boundaries not yet supported"); |
442 | 0 | } |
443 | 0 | else if ((scale_width > 0) && (scale_height > 0)) |
444 | 0 | { |
445 | 0 | DiImage *image = Image->createScale(left_pos, top_pos, clip_width, clip_height, scale_width, scale_height, |
446 | 0 | interpolate, aspect, pvalue); |
447 | 0 | if (image != NULL) |
448 | 0 | { |
449 | 0 | DicomImage *dicom = new DicomImage(this, image); |
450 | 0 | return dicom; |
451 | 0 | } |
452 | 0 | } |
453 | 0 | } |
454 | 0 | return NULL; |
455 | 0 | } |
456 | | |
457 | | |
458 | | // --- clip & scale |
459 | | |
460 | | DicomImage *DicomImage::createScaledImage(const signed long left_pos, |
461 | | const signed long top_pos, |
462 | | unsigned long width, |
463 | | unsigned long height, |
464 | | const double xfactor, |
465 | | const double yfactor, |
466 | | const int interpolate, |
467 | | const int aspect, |
468 | | const Uint16 pvalue) const |
469 | 0 | { |
470 | 0 | if ((xfactor >= 0) && (yfactor >= 0)) |
471 | 0 | { |
472 | 0 | const unsigned long gw = getWidth(); |
473 | 0 | const unsigned long gh = getHeight(); |
474 | 0 | if ((width == 0) && (left_pos < OFstatic_cast(signed long, gw))) // set 'width' if parameter is missing (0) |
475 | 0 | width = gw - left_pos; |
476 | 0 | if ((height == 0) && (top_pos < OFstatic_cast(signed long, gh))) // same for 'height' |
477 | 0 | height = gh - top_pos; |
478 | 0 | return createScaledImage(left_pos, top_pos, width, height, OFstatic_cast(unsigned long, xfactor * width), |
479 | 0 | OFstatic_cast(unsigned long, yfactor * height), interpolate, aspect, pvalue); |
480 | 0 | } |
481 | 0 | return NULL; |
482 | 0 | } |
483 | | |
484 | | |
485 | | // --- create clipped to given box ('left_pos', 'top_pos' and 'width', 'height') image, |
486 | | // ---- memory isn't handled internally! 'width' and 'height' are optional |
487 | | |
488 | | DicomImage *DicomImage::createClippedImage(const signed long left_pos, |
489 | | const signed long top_pos, |
490 | | unsigned long width, |
491 | | unsigned long height, |
492 | | const Uint16 pvalue) const |
493 | 0 | { |
494 | 0 | return createScaledImage(left_pos, top_pos, width, height, OFstatic_cast(unsigned long, 0), |
495 | 0 | OFstatic_cast(unsigned long, 0), 0, 0, pvalue); |
496 | 0 | } |
497 | | |
498 | | |
499 | | // --- flip image (horizontal: x > 1 and/or vertical y > 1) |
500 | | |
501 | | int DicomImage::flipImage(int horz, |
502 | | int vert) const |
503 | 0 | { |
504 | 0 | if ((Image != NULL) && (horz || vert)) |
505 | 0 | { |
506 | 0 | if (getWidth() <= 1) |
507 | 0 | horz = 0; |
508 | 0 | if (getHeight() <= 1) |
509 | 0 | vert = 0; |
510 | 0 | if (horz || vert) |
511 | 0 | return Image->flip(horz, vert); |
512 | 0 | else |
513 | 0 | return 2; |
514 | 0 | } |
515 | 0 | return 0; |
516 | 0 | } |
517 | | |
518 | | |
519 | | // --- create flipped image (horizontal: x > 1 and/or vertical y > 1), memory isn't handled internally ! |
520 | | |
521 | | DicomImage *DicomImage::createFlippedImage(int horz, |
522 | | int vert) const |
523 | 0 | { |
524 | 0 | if ((Image != NULL) && (horz || vert)) |
525 | 0 | { |
526 | 0 | if (getWidth() <= 1) // can't flip horizontally |
527 | 0 | horz = 0; |
528 | 0 | if (getHeight() <= 1) // can't flip vertically |
529 | 0 | vert = 0; |
530 | 0 | DiImage *image; |
531 | 0 | if (horz || vert) // flip at least one axis |
532 | 0 | image = Image->createFlip(horz, vert); |
533 | 0 | else // copy image |
534 | 0 | image = Image->createImage(0, getFrameCount()); |
535 | 0 | if (image != NULL) |
536 | 0 | { |
537 | 0 | DicomImage *dicom = new DicomImage(this, image); |
538 | 0 | return dicom; |
539 | 0 | } |
540 | 0 | } |
541 | 0 | return NULL; |
542 | 0 | } |
543 | | |
544 | | |
545 | | // -- normalize given 'degree' value to 0, 90, 180, 270 |
546 | | |
547 | | int DicomImage::normalizeDegreeValue(signed int °ree) const |
548 | 0 | { |
549 | 0 | switch (degree) |
550 | 0 | { |
551 | 0 | case 0: |
552 | 0 | case 360: |
553 | 0 | case -360: |
554 | 0 | degree = 0; |
555 | 0 | return 1; |
556 | 0 | case 90: |
557 | 0 | case -270: |
558 | 0 | degree = 90; |
559 | 0 | return 1; |
560 | 0 | case 180: |
561 | 0 | case -180: |
562 | 0 | degree = 180; |
563 | 0 | return 1; |
564 | 0 | case 270: |
565 | 0 | case -90: |
566 | 0 | degree = 270; |
567 | 0 | return 1; |
568 | 0 | default: |
569 | 0 | return 0; |
570 | 0 | } |
571 | 0 | } |
572 | | |
573 | | |
574 | | // --- rotate image by given 'degree' |
575 | | |
576 | | int DicomImage::rotateImage(signed int degree) const |
577 | 0 | { |
578 | 0 | if ((Image != NULL) && normalizeDegreeValue(degree)) |
579 | 0 | { |
580 | 0 | if ((degree == 0) || (getWidth() * getHeight() <= 1)) // nothing to do |
581 | 0 | return 2; |
582 | 0 | else |
583 | 0 | return Image->rotate(OFstatic_cast(int, degree)); |
584 | 0 | } |
585 | 0 | return 0; |
586 | 0 | } |
587 | | |
588 | | |
589 | | // --- create by given 'degree' rotated image, memory isn't handled internally ! |
590 | | |
591 | | DicomImage *DicomImage::createRotatedImage(signed int degree) const |
592 | 0 | { |
593 | 0 | if ((Image != NULL) && normalizeDegreeValue(degree)) |
594 | 0 | { |
595 | 0 | DiImage *image = Image->createRotate(OFstatic_cast(int, degree)); |
596 | 0 | if (image != NULL) |
597 | 0 | { |
598 | 0 | DicomImage *dicom = new DicomImage(this, image); |
599 | 0 | return dicom; |
600 | 0 | } |
601 | 0 | } |
602 | 0 | return NULL; |
603 | 0 | } |
604 | | |
605 | | |
606 | | // --- create color-image to mono-image with given 'red', 'green' and 'blue' coefficients converted image, memory ... ! |
607 | | |
608 | | DicomImage *DicomImage::createMonochromeImage(const double red, |
609 | | const double green, |
610 | | const double blue) const |
611 | 0 | { |
612 | 0 | if (Image != NULL) |
613 | 0 | { |
614 | 0 | DiImage *image = Image->createMono(red, green, blue); // create monochrome image data |
615 | 0 | if (image != NULL) |
616 | 0 | { |
617 | 0 | DicomImage *dicom = new DicomImage(this, image, EPI_Monochrome2); |
618 | 0 | return dicom; |
619 | 0 | } |
620 | 0 | } |
621 | 0 | return NULL; |
622 | 0 | } |
623 | | |
624 | | |
625 | | // --- create monochrome output image of specified frame (incl. windowing) |
626 | | |
627 | | DicomImage *DicomImage::createMonoOutputImage(const unsigned long frame, |
628 | | const int bits) |
629 | 0 | { |
630 | 0 | if ((Image != NULL) && (Image->getMonoImagePtr() != NULL)) |
631 | 0 | { |
632 | 0 | DiImage *image = Image->getMonoImagePtr()->createOutputImage(frame, bits); |
633 | 0 | if (image != NULL) |
634 | 0 | { |
635 | 0 | DicomImage *dicom = new DicomImage(this, image, EPI_Monochrome2); |
636 | 0 | return dicom; |
637 | 0 | } |
638 | 0 | } |
639 | 0 | return NULL; |
640 | 0 | } |
641 | | |
642 | | |
643 | | /*********************************************************************/ |
644 | | |
645 | | |
646 | | // --- write 'frame' of image data to 'filename' with 'bits' depth |
647 | | |
648 | | int DicomImage::writePPM(const char *filename, |
649 | | const int bits, |
650 | | const unsigned long frame) |
651 | 0 | { |
652 | 0 | if ((filename != NULL) && (Image != NULL)) |
653 | 0 | { |
654 | 0 | char fname[FILENAME_MAX + 1]; |
655 | 0 | if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0) // replace '%d' etc. with frame number |
656 | 0 | filename = fname; |
657 | 0 | FILE *stream = fopen(filename, "w"); // open text file for writing |
658 | 0 | int ok = writePPM(stream, bits, frame); |
659 | 0 | if (fclose(stream)) ok = 0; |
660 | 0 | return ok; |
661 | 0 | } |
662 | 0 | return 0; |
663 | 0 | } |
664 | | |
665 | | |
666 | | // --- same for C++ 'ostream' |
667 | | |
668 | | int DicomImage::writePPM(STD_NAMESPACE ostream& stream, |
669 | | const int bits, |
670 | | const unsigned long frame) |
671 | 0 | { |
672 | 0 | if ((stream.good()) && (Image != NULL)) |
673 | 0 | return Image->writePPM(stream, frame, Image->getBits(bits)); |
674 | 0 | return 0; |
675 | 0 | } |
676 | | |
677 | | |
678 | | // --- same for C 'FILE' |
679 | | |
680 | | int DicomImage::writePPM(FILE *stream, |
681 | | const int bits, |
682 | | const unsigned long frame) |
683 | 0 | { |
684 | 0 | if ((stream != NULL) && (Image != NULL)) |
685 | 0 | return Image->writePPM(stream, frame, Image->getBits(bits)); |
686 | 0 | return 0; |
687 | 0 | } |
688 | | |
689 | | |
690 | | // --- same for RAW PPM (binary form of PPM with a maximum of 8 bits depth) |
691 | | |
692 | | int DicomImage::writeRawPPM(const char *filename, |
693 | | const int bits, |
694 | | const unsigned long frame) |
695 | 0 | { |
696 | 0 | if ((filename != NULL) && (Image != NULL) && (Image->getBits(bits) <= MAX_RAWPPM_BITS)) |
697 | 0 | { |
698 | 0 | char fname[FILENAME_MAX + 1]; |
699 | 0 | if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0) // replace '%d' etc. with frame number |
700 | 0 | filename = fname; |
701 | 0 | FILE *stream = fopen(filename, "wb"); // open binary file for writing |
702 | 0 | if (stream != NULL) |
703 | 0 | { |
704 | 0 | int ok = Image->writeRawPPM(stream, frame, Image->getBits(bits)); |
705 | 0 | if (fclose(stream)) ok = 0; |
706 | 0 | return ok; |
707 | 0 | } |
708 | 0 | } |
709 | 0 | return 0; |
710 | 0 | } |
711 | | |
712 | | // --- same for C 'FILE' |
713 | | |
714 | | int DicomImage::writeRawPPM(FILE *stream, |
715 | | const int bits, |
716 | | const unsigned long frame) |
717 | 0 | { |
718 | 0 | if ((stream != NULL) && (Image != NULL)) |
719 | 0 | return Image->writeRawPPM(stream, frame, Image->getBits(bits)); |
720 | 0 | return 0; |
721 | 0 | } |
722 | | |
723 | | |
724 | | // --- write 'frame' of image data to 'filename' with 'bits' depth in BMP format |
725 | | |
726 | | int DicomImage::writeBMP(const char *filename, |
727 | | const int bits, |
728 | | const unsigned long frame) |
729 | 0 | { |
730 | 0 | if ((filename != NULL) && (Image != NULL) && |
731 | 0 | ((bits == 0) || ((bits == 8) && isMonochrome()) || (bits == 24) || (bits == 32))) |
732 | 0 | { |
733 | 0 | char fname[FILENAME_MAX + 1]; |
734 | 0 | if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0) // replace '%d' etc. with frame number |
735 | 0 | filename = fname; |
736 | 0 | FILE *stream = fopen(filename, "wb"); // open binary file for writing |
737 | 0 | if (stream != NULL) |
738 | 0 | { |
739 | 0 | int ok = Image->writeBMP(stream, frame, bits); |
740 | 0 | if (fclose(stream)) ok = 0; |
741 | 0 | return ok; |
742 | 0 | } |
743 | 0 | } |
744 | 0 | return 0; |
745 | 0 | } |
746 | | |
747 | | |
748 | | // --- same for open C 'FILE' in BMP format |
749 | | |
750 | | int DicomImage::writeBMP(FILE *stream, |
751 | | const int bits, |
752 | | const unsigned long frame) |
753 | 0 | { |
754 | 0 | if ((stream != NULL) && (Image != NULL) && |
755 | 0 | ((bits == 0) || ((bits == 8) && isMonochrome()) || (bits == 24) || (bits == 32))) |
756 | 0 | { |
757 | 0 | return Image->writeBMP(stream, frame, bits); |
758 | 0 | } |
759 | 0 | return 0; |
760 | 0 | } |
761 | | |
762 | | |
763 | | // --- write 'frame' of image data to 'filename' pluggable image format |
764 | | |
765 | | int DicomImage::writePluginFormat(const DiPluginFormat *plugin, |
766 | | const char *filename, |
767 | | const unsigned long frame) |
768 | 0 | { |
769 | 0 | if ((plugin != NULL) && (filename != NULL) && (Image != NULL)) |
770 | 0 | { |
771 | 0 | char fname[FILENAME_MAX + 1]; |
772 | 0 | if (OFStandard::snprintf(fname, sizeof(fname), filename, frame) >= 0) // replace '%d' etc. with frame number |
773 | 0 | filename = fname; |
774 | 0 | FILE *stream = fopen(filename, "wb"); // open binary file for writing |
775 | 0 | if (stream != NULL) |
776 | 0 | { |
777 | 0 | int ok = plugin->write(Image, stream, frame); |
778 | 0 | if (fclose(stream)) ok = 0; |
779 | 0 | return ok; |
780 | 0 | } |
781 | 0 | } |
782 | 0 | return 0; |
783 | 0 | } |
784 | | |
785 | | |
786 | | // --- same for open C 'FILE' in pluggable image format |
787 | | |
788 | | int DicomImage::writePluginFormat(const DiPluginFormat *plugin, |
789 | | FILE *stream, |
790 | | const unsigned long frame) |
791 | 0 | { |
792 | 0 | if ((plugin != NULL) && (stream != NULL) && (Image != NULL)) |
793 | 0 | return plugin->write(Image, stream, frame); |
794 | 0 | return 0; |
795 | 0 | } |