1 /*
2 *
3 * Copyright (C) 2002-2020, 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: dcmimage
15 *
16 * Authors: Joerg Riesmeier
17 *
18 * Purpose: Scale DICOM images
19 *
20 */
21
22
23 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
24
25 #define INCLUDE_CSTDIO
26 #define INCLUDE_CSTRING
27 #include "dcmtk/ofstd/ofstdinc.h"
28
29 #include "dcmtk/dcmdata/dctk.h" /* for various dcmdata headers */
30 #include "dcmtk/dcmdata/cmdlnarg.h" /* for prepareCmdLineArgs */
31 #include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */
32
33 #include "dcmtk/ofstd/ofconapp.h" /* for OFConsoleApplication */
34 #include "dcmtk/ofstd/ofcmdln.h" /* for OFCommandLine */
35
36 #include "dcmtk/oflog/oflog.h" /* for OFLogger */
37
38 #include "dcmtk/dcmimgle/dcmimage.h" /* for DicomImage */
39 #include "dcmtk/dcmimage/diregist.h" /* include to support color images */
40 #include "dcmtk/dcmdata/dcrledrg.h" /* for DcmRLEDecoderRegistration */
41
42 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
43 #include "dcmtk/dcmjpeg/djdecode.h" /* for dcmjpeg decoders */
44 #include "dcmtk/dcmjpeg/dipijpeg.h" /* for dcmimage JPEG plugin */
45 #endif
46
47 #ifdef WITH_ZLIB
48 #include <zlib.h> /* for zlibVersion() */
49 #endif
50
51 #define OFFIS_CONSOLE_DESCRIPTION "Scale DICOM images"
52
53 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
54 #define OFFIS_CONSOLE_APPLICATION "dcmjscal"
55 #else
56 #define OFFIS_CONSOLE_APPLICATION "dcmscale"
57 #endif
58
59 static OFLogger dcmscaleLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION);
60
61 static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v"
62 OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $";
63
64 #define SHORTCOL 4
65 #define LONGCOL 21
66
67
68 // ********************************************
69
main(int argc,char * argv[])70 int main(int argc, char *argv[])
71 {
72 OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, OFFIS_CONSOLE_DESCRIPTION, rcsid);
73 OFCommandLine cmd;
74
75 OFBool opt_uidCreation = OFTrue;
76 E_FileReadMode opt_readMode = ERM_autoDetect;
77 E_FileWriteMode opt_writeMode = EWM_createNewMeta;
78 E_TransferSyntax opt_ixfer = EXS_Unknown;
79 E_TransferSyntax opt_oxfer = EXS_Unknown;
80 E_GrpLenEncoding opt_oglenc = EGL_recalcGL;
81 E_EncodingType opt_oenctype = EET_ExplicitLength;
82 E_PaddingEncoding opt_opadenc = EPD_noChange;
83 OFCmdUnsignedInt opt_filepad = 0;
84 OFCmdUnsignedInt opt_itempad = 0;
85
86 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
87 // JPEG parameters, currently not used
88 # if 0
89 OFCmdUnsignedInt opt_quality = 90; /* default: 90% JPEG quality */
90 E_SubSampling opt_sampling = ESS_422; /* default: 4:2:2 sub-sampling */
91 # endif
92 E_DecompressionColorSpaceConversion opt_decompCSconversion = EDC_photometricInterpretation;
93 #endif
94
95 int opt_useAspectRatio = 1; /* default: use aspect ratio for scaling */
96 OFCmdUnsignedInt opt_useInterpolation = 1; /* default: use interpolation method '1' for scaling */
97 int opt_scaleType = 0; /* default: no scaling */
98 /* 1 = X-factor, 2 = Y-factor, 3=X-size, 4=Y-size */
99 OFCmdFloat opt_scale_factor = 1.0;
100 OFCmdUnsignedInt opt_scale_size = 1;
101
102 OFBool opt_useClip = OFFalse; /* default: don't clip */
103 OFCmdSignedInt opt_left = 0, opt_top = 0; /* clip region (origin) */
104 OFCmdUnsignedInt opt_width = 0, opt_height = 0; /* clip region (extension) */
105
106 const char *opt_ifname = NULL;
107 const char *opt_ofname = NULL;
108
109 prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION);
110 cmd.setOptionColumns(LONGCOL, SHORTCOL);
111
112 cmd.addParam("dcmfile-in", "DICOM input filename to be scaled");
113 cmd.addParam("dcmfile-out", "DICOM output filename to be written");
114
115 cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2);
116 cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
117 cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive);
118 OFLog::addOptions(cmd);
119
120 cmd.addGroup("input options:");
121 cmd.addSubGroup("input file format:");
122 cmd.addOption("--read-file", "+f", "read file format or data set (default)");
123 cmd.addOption("--read-file-only", "+fo", "read file format only");
124 cmd.addOption("--read-dataset", "-f", "read data set without file meta information");
125 cmd.addSubGroup("input transfer syntax:");
126 cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)");
127 cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header");
128 cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS");
129 cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS");
130 cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS");
131
132 cmd.addGroup("image processing and encoding options:");
133 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
134 cmd.addSubGroup("color space conversion options (compressed images only):");
135 cmd.addOption("--conv-photometric", "+cp", "convert if YCbCr photometric interpr. (default)");
136 cmd.addOption("--conv-lossy", "+cl", "convert YCbCr to RGB if lossy JPEG");
137 cmd.addOption("--conv-guess", "+cg", "convert to RGB if YCbCr is guessed by library");
138 cmd.addOption("--conv-guess-lossy", "+cgl", "convert to RGB if lossy JPEG and YCbCr is\nguessed by the underlying JPEG library");
139 cmd.addOption("--conv-always", "+ca", "always convert YCbCr to RGB");
140 cmd.addOption("--conv-never", "+cn", "never convert color space");
141 #endif
142 cmd.addSubGroup("scaling:");
143 cmd.addOption("--recognize-aspect", "+a", "recognize pixel aspect ratio when scaling (def)");
144 cmd.addOption("--ignore-aspect", "-a", "ignore pixel aspect ratio when scaling");
145 cmd.addOption("--interpolate", "+i", 1, "[n]umber of algorithm: integer",
146 "use interpolation when scaling (1..4, def: 1)");
147 cmd.addOption("--no-interpolation", "-i", "no interpolation when scaling");
148 cmd.addOption("--no-scaling", "-S", "no scaling, ignore pixel aspect ratio (default)");
149 cmd.addOption("--scale-x-factor", "+Sxf", 1, "[f]actor: float",
150 "scale x axis by factor, auto-compute y axis");
151 cmd.addOption("--scale-y-factor", "+Syf", 1, "[f]actor: float",
152 "scale y axis by factor, auto-compute x axis");
153 cmd.addOption("--scale-x-size", "+Sxv", 1, "[n]umber: integer",
154 "scale x axis to n pixels, auto-compute y axis");
155 cmd.addOption("--scale-y-size", "+Syv", 1, "[n]umber: integer",
156 "scale y axis to n pixels, auto-compute x axis");
157 cmd.addSubGroup("other transformations:");
158 cmd.addOption("--clip-region", "+C", 4, "[l]eft [t]op [w]idth [h]eight: integer",
159 "clip rectangular image region (l, t, w, h)");
160 cmd.addSubGroup("SOP Instance UID:");
161 cmd.addOption("--uid-always", "+ua", "always assign new SOP Instance UID (default)");
162 cmd.addOption("--uid-never", "+un", "never assign new SOP Instance UID");
163
164 cmd.addGroup("output options:");
165 cmd.addSubGroup("output file format:");
166 cmd.addOption("--write-file", "+F", "write file format (default)");
167 cmd.addOption("--write-dataset", "-F", "write data set without file meta information");
168 cmd.addSubGroup("output transfer syntax:");
169 cmd.addOption("--write-xfer-same", "+t=", "write with same TS as input (default)");
170 cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian TS");
171 cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS");
172 cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS");
173 cmd.addSubGroup("post-1993 value representations:");
174 cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)");
175 cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB");
176 cmd.addSubGroup("group length encoding:");
177 cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)");
178 cmd.addOption("--group-length-create", "+g", "always write with group length elements");
179 cmd.addOption("--group-length-remove", "-g", "always write without group length elements");
180 cmd.addSubGroup("length encoding in sequences and items:");
181 cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)");
182 cmd.addOption("--length-undefined", "-e", "write with undefined lengths");
183 cmd.addSubGroup("data set trailing padding (not with --write-dataset):");
184 cmd.addOption("--padding-retain", "-p=", "do not change padding\n(default if not --write-dataset)");
185 cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)");
186 cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer",
187 "align file on multiple of f bytes\nand items on multiple of i bytes");
188
189 if (app.parseCommandLine(cmd, argc, argv))
190 {
191 /* check exclusive options first */
192 if (cmd.hasExclusiveOption())
193 {
194 if (cmd.findOption("--version"))
195 {
196 app.printHeader(OFTrue /*print host identifier*/);
197 COUT << OFendl << "External libraries used:";
198 #if !defined(WITH_ZLIB) && !defined(BUILD_DCMSCALE_AS_DCMJSCAL)
199 COUT << " none" << OFendl;
200 #else
201 COUT << OFendl;
202 #endif
203 #ifdef WITH_ZLIB
204 COUT << "- ZLIB, Version " << zlibVersion() << OFendl;
205 #endif
206 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
207 COUT << "- " << DiJPEGPlugin::getLibraryVersionString() << OFendl;
208 #endif
209 return 0;
210 }
211 }
212
213 /* command line parameters */
214
215 cmd.getParam(1, opt_ifname);
216 cmd.getParam(2, opt_ofname);
217
218 /* general options */
219
220 OFLog::configureFromCommandLine(cmd, app);
221
222 /* input options */
223
224 cmd.beginOptionBlock();
225 if (cmd.findOption("--read-file")) opt_readMode = ERM_autoDetect;
226 if (cmd.findOption("--read-file-only")) opt_readMode = ERM_fileOnly;
227 if (cmd.findOption("--read-dataset")) opt_readMode = ERM_dataset;
228 cmd.endOptionBlock();
229
230 cmd.beginOptionBlock();
231 if (cmd.findOption("--read-xfer-auto"))
232 opt_ixfer = EXS_Unknown;
233 if (cmd.findOption("--read-xfer-detect"))
234 dcmAutoDetectDatasetXfer.set(OFTrue);
235 if (cmd.findOption("--read-xfer-little"))
236 {
237 app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset);
238 opt_ixfer = EXS_LittleEndianExplicit;
239 }
240 if (cmd.findOption("--read-xfer-big"))
241 {
242 app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset);
243 opt_ixfer = EXS_BigEndianExplicit;
244 }
245 if (cmd.findOption("--read-xfer-implicit"))
246 {
247 app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset);
248 opt_ixfer = EXS_LittleEndianImplicit;
249 }
250 cmd.endOptionBlock();
251
252 /* image processing options: color space conversion */
253
254 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
255 cmd.beginOptionBlock();
256 if (cmd.findOption("--conv-photometric"))
257 opt_decompCSconversion = EDC_photometricInterpretation;
258 if (cmd.findOption("--conv-lossy"))
259 opt_decompCSconversion = EDC_lossyOnly;
260 if (cmd.findOption("--conv-guess"))
261 opt_decompCSconversion = EDC_guess;
262 if (cmd.findOption("--conv-guess-lossy"))
263 opt_decompCSconversion = EDC_guessLossyOnly;
264 if (cmd.findOption("--conv-always"))
265 opt_decompCSconversion = EDC_always;
266 if (cmd.findOption("--conv-never"))
267 opt_decompCSconversion = EDC_never;
268 cmd.endOptionBlock();
269 #endif
270
271 /* image processing options: scaling */
272
273 cmd.beginOptionBlock();
274 if (cmd.findOption("--recognize-aspect"))
275 opt_useAspectRatio = 1;
276 if (cmd.findOption("--ignore-aspect"))
277 opt_useAspectRatio = 0;
278 cmd.endOptionBlock();
279
280 cmd.beginOptionBlock();
281 if (cmd.findOption("--interpolate"))
282 app.checkValue(cmd.getValueAndCheckMinMax(opt_useInterpolation, 1, 4));
283 if (cmd.findOption("--no-interpolation"))
284 opt_useInterpolation = 0;
285 cmd.endOptionBlock();
286
287 cmd.beginOptionBlock();
288 if (cmd.findOption("--no-scaling"))
289 opt_scaleType = 0;
290 if (cmd.findOption("--scale-x-factor"))
291 {
292 opt_scaleType = 1;
293 app.checkValue(cmd.getValueAndCheckMin(opt_scale_factor, 0.0, OFFalse));
294 }
295 if (cmd.findOption("--scale-y-factor"))
296 {
297 opt_scaleType = 2;
298 app.checkValue(cmd.getValueAndCheckMin(opt_scale_factor, 0.0, OFFalse));
299 }
300 if (cmd.findOption("--scale-x-size"))
301 {
302 opt_scaleType = 3;
303 app.checkValue(cmd.getValueAndCheckMin(opt_scale_size, 1));
304 }
305 if (cmd.findOption("--scale-y-size"))
306 {
307 opt_scaleType = 4;
308 app.checkValue(cmd.getValueAndCheckMin(opt_scale_size, 1));
309 }
310 cmd.endOptionBlock();
311
312 /* image processing options: other transformations */
313
314 if (cmd.findOption("--clip-region"))
315 {
316 app.checkValue(cmd.getValue(opt_left));
317 app.checkValue(cmd.getValue(opt_top));
318 app.checkValue(cmd.getValue(opt_width));
319 app.checkValue(cmd.getValue(opt_height));
320 opt_useClip = OFTrue;
321 }
322
323 /* image processing options: SOP Instance UID options */
324
325 cmd.beginOptionBlock();
326 if (cmd.findOption("--uid-always")) opt_uidCreation = OFTrue;
327 if (cmd.findOption("--uid-never")) opt_uidCreation = OFFalse;
328 cmd.endOptionBlock();
329
330 /* output options */
331
332 cmd.beginOptionBlock();
333 if (cmd.findOption("--write-file")) opt_writeMode = EWM_createNewMeta;
334 if (cmd.findOption("--write-dataset")) opt_writeMode = EWM_dataset;
335 cmd.endOptionBlock();
336
337 cmd.beginOptionBlock();
338 if (cmd.findOption("--write-xfer-same")) opt_oxfer = EXS_Unknown;
339 if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit;
340 if (cmd.findOption("--write-xfer-big")) opt_oxfer = EXS_BigEndianExplicit;
341 if (cmd.findOption("--write-xfer-implicit")) opt_oxfer = EXS_LittleEndianImplicit;
342 cmd.endOptionBlock();
343
344 cmd.beginOptionBlock();
345 if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs();
346 if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs();
347 cmd.endOptionBlock();
348
349 cmd.beginOptionBlock();
350 if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL;
351 if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL;
352 if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL;
353 cmd.endOptionBlock();
354
355 cmd.beginOptionBlock();
356 if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength;
357 if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength;
358 cmd.endOptionBlock();
359
360 cmd.beginOptionBlock();
361 if (cmd.findOption("--padding-retain"))
362 {
363 app.checkConflict("--padding-retain", "--write-dataset", opt_writeMode == EWM_dataset);
364 opt_opadenc = EPD_noChange;
365 }
366 if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding;
367 if (cmd.findOption("--padding-create"))
368 {
369 app.checkConflict("--padding-create", "--write-dataset", opt_writeMode == EWM_dataset);
370 app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0));
371 app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0));
372 opt_opadenc = EPD_withPadding;
373 }
374 cmd.endOptionBlock();
375 }
376
377 /* print resource identifier */
378 OFLOG_DEBUG(dcmscaleLogger, rcsid << OFendl);
379
380 /* make sure data dictionary is loaded */
381 if (!dcmDataDict.isDictionaryLoaded())
382 {
383 OFLOG_WARN(dcmscaleLogger, "no data dictionary loaded, check environment variable: "
384 << DCM_DICT_ENVIRONMENT_VARIABLE);
385 }
386
387 // register RLE decompression codec
388 DcmRLEDecoderRegistration::registerCodecs();
389 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
390 // register global decompression codecs
391 DJDecoderRegistration::registerCodecs(opt_decompCSconversion);
392 #endif
393
394 // ======================================================================
395 // read input file
396
397 if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0))
398 {
399 OFLOG_FATAL(dcmscaleLogger, "invalid input filename: <empty string>");
400 return 1;
401 }
402
403 /* no clipping/scaling */
404 if (!opt_scaleType && !opt_useClip)
405 {
406 OFLOG_FATAL(dcmscaleLogger, "nothing to do");
407 return 1;
408 }
409
410 OFLOG_INFO(dcmscaleLogger, "open input file " << opt_ifname);
411
412 DcmFileFormat fileformat;
413 DcmDataset *dataset = fileformat.getDataset();
414
415 OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode);
416 if (error.bad())
417 {
418 OFLOG_FATAL(dcmscaleLogger, error.text() << ": reading file: " << opt_ifname);
419 return 1;
420 }
421
422 OFLOG_INFO(dcmscaleLogger, "load all data into memory");
423
424 /* make sure that pixel data is loaded before output file is created */
425 dataset->loadAllDataIntoMemory();
426
427 // select uncompressed output transfer syntax.
428 // this will implicitly decompress the image if necessary.
429
430 if (opt_oxfer == EXS_Unknown)
431 {
432 OFLOG_INFO(dcmscaleLogger, "set output transfer syntax to input transfer syntax");
433 opt_oxfer = dataset->getOriginalXfer();
434 }
435
436 OFLOG_INFO(dcmscaleLogger, "check if new output transfer syntax is possible");
437
438 DcmXfer opt_oxferSyn(opt_oxfer);
439 if (dataset->chooseRepresentation(opt_oxfer, NULL).good() && dataset->canWriteXfer(opt_oxfer))
440 {
441 OFLOG_INFO(dcmscaleLogger, "output transfer syntax " << opt_oxferSyn.getXferName()
442 << " can be written");
443 } else {
444 OFLOG_FATAL(dcmscaleLogger, "no conversion to transfer syntax " << opt_oxferSyn.getXferName()
445 << " possible");
446 return 1;
447 }
448
449 // ======================================================================
450 // image processing starts here
451
452 OFLOG_INFO(dcmscaleLogger, "preparing pixel data");
453
454 const unsigned long flags = (opt_scaleType > 0) ? CIF_MayDetachPixelData : 0;
455 // create DicomImage object
456 DicomImage *di = new DicomImage(dataset, opt_oxfer, flags);
457 if (di == NULL)
458 {
459 OFLOG_FATAL(dcmscaleLogger, "memory exhausted");
460 return 1;
461 }
462 if (di->getStatus() != EIS_Normal)
463 {
464 OFLOG_FATAL(dcmscaleLogger, DicomImage::getString(di->getStatus()));
465 return 1;
466 }
467
468 DicomImage *newimage = NULL;
469 OFString derivationDescription;
470
471 if (opt_useClip)
472 OFLOG_INFO(dcmscaleLogger, "clipping image to (" << opt_left << "," << opt_top
473 << "," << opt_width << "," << opt_height << ")");
474 // perform clipping (without scaling)
475 if (opt_scaleType <= 0)
476 {
477 if (opt_useClip)
478 {
479 newimage = di->createClippedImage(opt_left, opt_top, opt_width, opt_height);
480 derivationDescription = "Clipped rectangular image region";
481 }
482 }
483 // perform scaling (and possibly clipping)
484 else if (opt_scaleType <= 4)
485 {
486 switch (opt_scaleType)
487 {
488 case 1:
489 OFLOG_INFO(dcmscaleLogger, "scaling image, X factor=" << opt_scale_factor
490 << ", Interpolation=" << OFstatic_cast(int, opt_useInterpolation)
491 << ", Aspect Ratio=" << (opt_useAspectRatio ? "yes" : "no"));
492 if (opt_useClip)
493 newimage = di->createScaledImage(opt_left, opt_top, opt_width, opt_height, opt_scale_factor, 0.0,
494 OFstatic_cast(int, opt_useInterpolation), opt_useAspectRatio);
495 else
496 newimage = di->createScaledImage(opt_scale_factor, 0.0, OFstatic_cast(int, opt_useInterpolation),
497 opt_useAspectRatio);
498 break;
499 case 2:
500 OFLOG_INFO(dcmscaleLogger, "scaling image, Y factor=" << opt_scale_factor
501 << ", Interpolation=" << OFstatic_cast(int, opt_useInterpolation)
502 << ", Aspect Ratio=" << (opt_useAspectRatio ? "yes" : "no"));
503 if (opt_useClip)
504 newimage = di->createScaledImage(opt_left, opt_top, opt_width, opt_height, 0.0, opt_scale_factor,
505 OFstatic_cast(int, opt_useInterpolation), opt_useAspectRatio);
506 else
507 newimage = di->createScaledImage(0.0, opt_scale_factor, OFstatic_cast(int, opt_useInterpolation),
508 opt_useAspectRatio);
509 break;
510 case 3:
511 OFLOG_INFO(dcmscaleLogger, "scaling image, X size=" << opt_scale_size
512 << ", Interpolation=" << OFstatic_cast(int, opt_useInterpolation)
513 << ", Aspect Ratio=" << (opt_useAspectRatio ? "yes" : "no"));
514 if (opt_useClip)
515 newimage = di->createScaledImage(opt_left, opt_top, opt_width, opt_height, opt_scale_size, 0,
516 OFstatic_cast(int, opt_useInterpolation), opt_useAspectRatio);
517 else
518 newimage = di->createScaledImage(opt_scale_size, 0, OFstatic_cast(int, opt_useInterpolation),
519 opt_useAspectRatio);
520 break;
521 case 4:
522 OFLOG_INFO(dcmscaleLogger, "scaling image, Y size=" << opt_scale_size
523 << ", Interpolation=" << OFstatic_cast(int, opt_useInterpolation)
524 << ", Aspect Ratio=" << (opt_useAspectRatio ? "yes" : "no"));
525 if (opt_useClip)
526 newimage = di->createScaledImage(opt_left, opt_top, opt_width, opt_height, 0, opt_scale_size,
527 OFstatic_cast(int, opt_useInterpolation), opt_useAspectRatio);
528 else
529 newimage = di->createScaledImage(0, opt_scale_size, OFstatic_cast(int, opt_useInterpolation),
530 opt_useAspectRatio);
531 break;
532 default:
533 break;
534 }
535 if (opt_useClip)
536 derivationDescription = "Scaled rectangular image region";
537 else
538 derivationDescription = "Scaled image";
539 }
540 if (opt_scaleType > 4)
541 OFLOG_ERROR(dcmscaleLogger, "internal error: unknown scaling type");
542 else if (newimage == NULL)
543 {
544 OFLOG_FATAL(dcmscaleLogger, "cannot create new image");
545 return 1;
546 }
547 else if (newimage->getStatus() != EIS_Normal)
548 {
549 OFLOG_FATAL(dcmscaleLogger, DicomImage::getString(newimage->getStatus()));
550 return 1;
551 }
552 /* write scaled image to dataset (update attributes of Image Pixel Module) */
553 else if (!newimage->writeImageToDataset(*dataset))
554 {
555 OFLOG_FATAL(dcmscaleLogger, "cannot write new image to dataset");
556 return 1;
557 }
558 delete newimage;
559
560 /* cleanup original image */
561 delete di;
562
563 // ======================================================================
564 // update some header attributes
565
566 // update Derivation Description
567 if (!derivationDescription.empty())
568 {
569 const char *oldDerivation = NULL;
570 if (dataset->findAndGetString(DCM_DerivationDescription, oldDerivation).good() && oldDerivation)
571 {
572 // append old Derivation Description, if any
573 derivationDescription += " [";
574 derivationDescription += oldDerivation;
575 derivationDescription += "]";
576 if (derivationDescription.length() > 1024)
577 {
578 // ST is limited to 1024 characters, cut off tail
579 derivationDescription.erase(1020);
580 derivationDescription += "...]";
581 }
582 }
583 dataset->putAndInsertString(DCM_DerivationDescription, derivationDescription.c_str());
584 }
585
586 // update Image Type
587 OFString imageType = "DERIVED";
588 const char *oldImageType = NULL;
589 if (dataset->findAndGetString(DCM_ImageType, oldImageType).good())
590 {
591 if (oldImageType != NULL)
592 {
593 // append old image type information beginning with second entry
594 const char *pos = strchr(oldImageType, '\\');
595 if (pos != NULL)
596 imageType += pos;
597 }
598 }
599 dataset->putAndInsertString(DCM_ImageType, imageType.c_str());
600
601 // update SOP Instance UID
602 if (opt_uidCreation)
603 {
604 // add reference to source image
605 DcmItem *ditem = NULL;
606 const char *sopClassUID = NULL;
607 const char *sopInstanceUID = NULL;
608 if (dataset->findAndGetString(DCM_SOPClassUID, sopClassUID).good() &&
609 dataset->findAndGetString(DCM_SOPInstanceUID, sopInstanceUID).good())
610 {
611 dataset->findAndDeleteElement(DCM_SourceImageSequence);
612 if (dataset->findOrCreateSequenceItem(DCM_SourceImageSequence, ditem).good())
613 {
614 ditem->putAndInsertString(DCM_SOPClassUID, sopClassUID);
615 ditem->putAndInsertString(DCM_SOPInstanceUID, sopInstanceUID);
616 }
617 }
618 // create new SOP instance UID
619 char new_uid[100];
620 dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(new_uid));
621 }
622
623 // ======================================================================
624 // write back output file
625
626 OFLOG_INFO(dcmscaleLogger, "create output file " << opt_ofname);
627
628 error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc,
629 OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), opt_writeMode);
630
631 if (error.bad())
632 {
633 OFLOG_FATAL(dcmscaleLogger, error.text() << ": writing file: " << opt_ofname);
634 return 1;
635 }
636
637 OFLOG_INFO(dcmscaleLogger, "conversion successful");
638
639 // deregister RLE decompression codec
640 DcmRLEDecoderRegistration::cleanup();
641 #ifdef BUILD_DCMSCALE_AS_DCMJSCAL
642 // deregister global decompression codecs
643 DJDecoderRegistration::cleanup();
644 #endif
645
646 return 0;
647 }
648