1 /*
2 *
3 * Copyright (C) 2002-2017, 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: dcmdata
15 *
16 * Author: Marco Eichelberg
17 *
18 * Purpose: Decompress RLE-compressed DICOM file
19 *
20 */
21
22 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
23
24 #define INCLUDE_CSTDLIB
25 #define INCLUDE_CSTDIO
26 #define INCLUDE_CSTRING
27 #include "dcmtk/ofstd/ofstdinc.h"
28
29 #include "dcmtk/dcmdata/dctk.h"
30 #include "dcmtk/dcmdata/cmdlnarg.h"
31 #include "dcmtk/ofstd/ofconapp.h"
32 #include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */
33 #include "dcmtk/dcmdata/dcrledrg.h" /* for DcmRLEDecoderRegistration */
34
35 #ifdef WITH_ZLIB
36 #include <zlib.h> /* for zlibVersion() */
37 #endif
38
39 #define OFFIS_CONSOLE_APPLICATION "dcmdrle"
40
41 static OFLogger dcmdrleLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION);
42
43 static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v"
44 OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $";
45
46 // ********************************************
47
48
49 #define SHORTCOL 3
50 #define LONGCOL 21
51
main(int argc,char * argv[])52 int main(int argc, char *argv[])
53 {
54
55 const char *opt_ifname = NULL;
56 const char *opt_ofname = NULL;
57
58 E_TransferSyntax opt_oxfer = EXS_LittleEndianExplicit;
59 E_GrpLenEncoding opt_oglenc = EGL_recalcGL;
60 E_EncodingType opt_oenctype = EET_ExplicitLength;
61 E_PaddingEncoding opt_opadenc = EPD_noChange;
62 OFCmdUnsignedInt opt_filepad = 0;
63 OFCmdUnsignedInt opt_itempad = 0;
64 E_FileReadMode opt_readMode = ERM_autoDetect;
65 E_FileWriteMode opt_writeMode = EWM_fileformat;
66 E_TransferSyntax opt_ixfer = EXS_Unknown;
67
68 // RLE parameters
69 OFBool opt_uidcreation = OFFalse;
70 OFBool opt_reversebyteorder = OFFalse;
71
72 OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Decode RLE-compressed DICOM file", rcsid);
73 OFCommandLine cmd;
74 cmd.setOptionColumns(LONGCOL, SHORTCOL);
75 cmd.setParamColumn(LONGCOL + SHORTCOL + 4);
76
77 cmd.addParam("dcmfile-in", "DICOM input filename to be converted");
78 cmd.addParam("dcmfile-out", "DICOM output filename");
79
80 cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2);
81 cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
82 cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive);
83 OFLog::addOptions(cmd);
84
85 cmd.addGroup("input options:");
86 cmd.addSubGroup("input file format:");
87 cmd.addOption("--read-file", "+f", "read file format or data set (default)");
88 cmd.addOption("--read-file-only", "+fo", "read file format only");
89 cmd.addOption("--read-dataset", "-f", "read data set without file meta information");
90
91 cmd.addGroup("processing options:");
92 cmd.addSubGroup("SOP Instance UID:");
93 cmd.addOption("--uid-default", "+ud", "keep same SOP Instance UID (default)");
94 cmd.addOption("--uid-always", "+ua", "always assign new UID");
95 cmd.addSubGroup("RLE byte segment order:");
96 cmd.addOption("--byte-order-default", "+bd", "most significant byte first (default)");
97 cmd.addOption("--byte-order-reverse", "+br", "least significant byte first");
98
99 cmd.addGroup("output options:");
100 cmd.addSubGroup("output file format:");
101 cmd.addOption("--write-file", "+F", "write file format (default)");
102 cmd.addOption("--write-dataset", "-F", "write data set without file meta information");
103 cmd.addSubGroup("output transfer syntax:");
104 cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian (default)");
105 cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS");
106 cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS");
107 cmd.addSubGroup("post-1993 value representations:");
108 cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)");
109 cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB");
110 cmd.addSubGroup("group length encoding:");
111 cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)");
112 cmd.addOption("--group-length-create", "+g", "always write with group length elements");
113 cmd.addOption("--group-length-remove", "-g", "always write without group length elements");
114 cmd.addSubGroup("length encoding in sequences and items:");
115 cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)");
116 cmd.addOption("--length-undefined", "-e", "write with undefined lengths");
117 cmd.addSubGroup("data set trailing padding (not with --write-dataset):");
118 cmd.addOption("--padding-retain", "-p=", "do not change padding\n(default if not --write-dataset)");
119 cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)");
120 cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer",
121 "align file on multiple of f bytes\nand items on multiple of i bytes");
122
123 /* evaluate command line */
124 prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION);
125 if (app.parseCommandLine(cmd, argc, argv))
126 {
127 /* check exclusive options first */
128 if (cmd.hasExclusiveOption())
129 {
130 if (cmd.findOption("--version"))
131 {
132 app.printHeader(OFTrue /*print host identifier*/);
133 COUT << OFendl << "External libraries used:";
134 #ifdef WITH_ZLIB
135 COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl;
136 #else
137 COUT << " none" << OFendl;
138 #endif
139 return 0;
140 }
141 }
142
143 /* command line parameters */
144
145 cmd.getParam(1, opt_ifname);
146 cmd.getParam(2, opt_ofname);
147
148 OFLog::configureFromCommandLine(cmd, app);
149
150 cmd.beginOptionBlock();
151 if (cmd.findOption("--uid-default")) opt_uidcreation = OFFalse;
152 if (cmd.findOption("--uid-always")) opt_uidcreation = OFTrue;
153 cmd.endOptionBlock();
154
155 cmd.beginOptionBlock();
156 if (cmd.findOption("--byte-order-default")) opt_reversebyteorder = OFFalse;
157 if (cmd.findOption("--byte-order-reverse")) opt_reversebyteorder = OFTrue;
158 cmd.endOptionBlock();
159
160 cmd.beginOptionBlock();
161 if (cmd.findOption("--read-file"))
162 {
163 opt_readMode = ERM_autoDetect;
164 opt_ixfer = EXS_Unknown;
165 }
166 if (cmd.findOption("--read-file-only"))
167 {
168 opt_readMode = ERM_fileOnly;
169 opt_ixfer = EXS_Unknown;
170 }
171 if (cmd.findOption("--read-dataset"))
172 {
173 opt_readMode = ERM_dataset;
174 opt_ixfer = EXS_RLELossless;
175 }
176 cmd.endOptionBlock();
177
178 cmd.beginOptionBlock();
179 if (cmd.findOption("--write-file")) opt_writeMode = EWM_fileformat;
180 if (cmd.findOption("--write-dataset")) opt_writeMode = EWM_dataset;
181 cmd.endOptionBlock();
182
183 cmd.beginOptionBlock();
184 if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit;
185 if (cmd.findOption("--write-xfer-big")) opt_oxfer = EXS_BigEndianExplicit;
186 if (cmd.findOption("--write-xfer-implicit")) opt_oxfer = EXS_LittleEndianImplicit;
187 cmd.endOptionBlock();
188
189 cmd.beginOptionBlock();
190 if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs();
191 if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs();
192 cmd.endOptionBlock();
193
194 cmd.beginOptionBlock();
195 if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL;
196 if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL;
197 if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL;
198 cmd.endOptionBlock();
199
200 cmd.beginOptionBlock();
201 if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength;
202 if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength;
203 cmd.endOptionBlock();
204
205 cmd.beginOptionBlock();
206 if (cmd.findOption("--padding-retain"))
207 {
208 app.checkConflict("--padding-retain", "--write-dataset", opt_writeMode == EWM_dataset);
209 opt_opadenc = EPD_noChange;
210 }
211 if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding;
212 if (cmd.findOption("--padding-create"))
213 {
214 app.checkConflict("--padding-create", "--write-dataset", opt_writeMode == EWM_dataset);
215 app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0));
216 app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0));
217 opt_opadenc = EPD_withPadding;
218 }
219 cmd.endOptionBlock();
220 }
221
222 /* print resource identifier */
223 OFLOG_DEBUG(dcmdrleLogger, rcsid << OFendl);
224
225 // register global decompression codecs
226 DcmRLEDecoderRegistration::registerCodecs(opt_uidcreation, opt_reversebyteorder);
227
228 /* make sure data dictionary is loaded */
229 if (!dcmDataDict.isDictionaryLoaded())
230 {
231 OFLOG_WARN(dcmdrleLogger, "no data dictionary loaded, check environment variable: "
232 << DCM_DICT_ENVIRONMENT_VARIABLE);
233 }
234
235 // open inputfile
236 if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0))
237 {
238 OFLOG_FATAL(dcmdrleLogger, "invalid filename: <empty string>");
239 return 1;
240 }
241
242 DcmFileFormat fileformat;
243 DcmDataset * dataset = fileformat.getDataset();
244
245 OFLOG_INFO(dcmdrleLogger, "open input file " << opt_ifname);
246
247 OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode);
248
249 if (error.bad())
250 {
251 OFLOG_FATAL(dcmdrleLogger, error.text() << ": reading file: " << opt_ifname);
252 return 1;
253 }
254
255 OFLOG_INFO(dcmdrleLogger, "decompressing file");
256
257 DcmXfer opt_oxferSyn(opt_oxfer);
258 DcmXfer original_xfer(dataset->getOriginalXfer());
259
260 error = dataset->chooseRepresentation(opt_oxfer, NULL);
261 if (error.bad())
262 {
263 OFLOG_FATAL(dcmdrleLogger, error.text() << ": decompressing file: " << opt_ifname);
264 if (error == EC_CannotChangeRepresentation)
265 OFLOG_FATAL(dcmdrleLogger, "input transfer syntax " << original_xfer.getXferName() << " not supported");
266 return 1;
267 }
268
269 if (! dataset->canWriteXfer(opt_oxfer))
270 {
271 OFLOG_FATAL(dcmdrleLogger, "no conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible");
272 return 1;
273 }
274
275 OFLOG_INFO(dcmdrleLogger, "create output file " << opt_ofname);
276
277 // update file meta information with new SOP Instance UID
278 if (opt_uidcreation && (opt_writeMode == EWM_fileformat))
279 opt_writeMode = EWM_updateMeta;
280
281 fileformat.loadAllDataIntoMemory();
282 error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc,
283 OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), opt_writeMode);
284
285 if (error.bad())
286 {
287 OFLOG_FATAL(dcmdrleLogger, error.text() << ": writing file: " << opt_ofname);
288 return 1;
289 }
290
291 OFLOG_INFO(dcmdrleLogger, "conversion successful");
292
293 // deregister RLE codec
294 DcmRLEDecoderRegistration::cleanup();
295
296 return 0;
297 }
298