1 /*
2 *
3 * Copyright (C) 2002-2018, 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: Compress DICOM file with RLE Transfer Syntax
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/dcrleerg.h" /* for DcmRLEEncoderRegistration */
34
35 #ifdef WITH_ZLIB
36 #include <zlib.h> /* for zlibVersion() */
37 #endif
38
39 #define OFFIS_CONSOLE_APPLICATION "dcmcrle"
40
41 static OFLogger dcmcrleLogger = 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_FileReadMode opt_readMode = ERM_autoDetect;
59 E_TransferSyntax opt_ixfer = EXS_Unknown;
60 E_GrpLenEncoding opt_oglenc = EGL_recalcGL;
61 E_EncodingType opt_oenctype = EET_ExplicitLength;
62 E_PaddingEncoding opt_opadenc = EPD_noChange;
63 OFCmdUnsignedInt opt_filepad = 0;
64 OFCmdUnsignedInt opt_itempad = 0;
65
66 // RLE options
67 E_TransferSyntax opt_oxfer = EXS_RLELossless;
68 OFCmdUnsignedInt opt_fragmentSize = 0; // 0=unlimited
69 OFBool opt_createOffsetTable = OFTrue;
70 OFBool opt_uidcreation = OFFalse;
71 OFBool opt_secondarycapture = OFFalse;
72
73 OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Encode DICOM file to RLE transfer syntax", rcsid);
74 OFCommandLine cmd;
75 cmd.setOptionColumns(LONGCOL, SHORTCOL);
76 cmd.setParamColumn(LONGCOL + SHORTCOL + 4);
77
78 cmd.addParam("dcmfile-in", "DICOM input filename to be converted");
79 cmd.addParam("dcmfile-out", "DICOM output filename");
80
81 cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2);
82 cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
83 cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive);
84 OFLog::addOptions(cmd);
85
86 cmd.addGroup("input options:");
87 cmd.addSubGroup("input file format:");
88 cmd.addOption("--read-file", "+f", "read file format or data set (default)");
89 cmd.addOption("--read-file-only", "+fo", "read file format only");
90 cmd.addOption("--read-dataset", "-f", "read data set without file meta information");
91 cmd.addSubGroup("input transfer syntax:", LONGCOL, SHORTCOL);
92 cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)");
93 cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header");
94 cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS");
95 cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS");
96 cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS");
97
98 cmd.addGroup("encapsulated pixel data encoding options:");
99 cmd.addSubGroup("pixel data fragmentation:");
100 cmd.addOption("--fragment-per-frame", "+ff", "encode each frame as one fragment (default)");
101 cmd.addOption("--fragment-size", "+fs", 1, "[s]ize: integer",
102 "limit fragment size to s kbytes (non-standard)");
103 cmd.addSubGroup("basic offset table encoding:");
104 cmd.addOption("--offset-table-create", "+ot", "create offset table (default)");
105 cmd.addOption("--offset-table-empty", "-ot", "leave offset table empty");
106
107 cmd.addSubGroup("SOP Class UID:");
108 cmd.addOption("--class-default", "+cd", "keep SOP Class UID (default)");
109 cmd.addOption("--class-sc", "+cs", "convert to Secondary Capture Image\n(implies --uid-always)");
110
111 cmd.addSubGroup("SOP Instance UID:");
112 cmd.addOption("--uid-never", "+un", "never assign new UID (default)");
113 cmd.addOption("--uid-always", "+ua", "always assign new UID");
114
115 cmd.addGroup("output options:");
116 cmd.addSubGroup("post-1993 value representations:");
117 cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)");
118 cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB");
119 cmd.addSubGroup("group length encoding:");
120 cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)");
121 cmd.addOption("--group-length-create", "+g", "always write with group length elements");
122 cmd.addOption("--group-length-remove", "-g", "always write without group length elements");
123 cmd.addSubGroup("length encoding in sequences and items:");
124 cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)");
125 cmd.addOption("--length-undefined", "-e", "write with undefined lengths");
126 cmd.addSubGroup("data set trailing padding:");
127 cmd.addOption("--padding-retain", "-p=", "do not change padding (default)");
128 cmd.addOption("--padding-off", "-p", "no padding");
129 cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer",
130 "align file on multiple of f bytes\nand items on multiple of i bytes");
131
132 /* evaluate command line */
133 prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION);
134 if (app.parseCommandLine(cmd, argc, argv))
135 {
136 /* check exclusive options first */
137 if (cmd.hasExclusiveOption())
138 {
139 if (cmd.findOption("--version"))
140 {
141 app.printHeader(OFTrue /*print host identifier*/);
142 COUT << OFendl << "External libraries used:";
143 #ifdef WITH_ZLIB
144 COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl;
145 #else
146 COUT << " none" << OFendl;
147 #endif
148 return 0;
149 }
150 }
151
152 /* command line parameters */
153
154 cmd.getParam(1, opt_ifname);
155 cmd.getParam(2, opt_ofname);
156
157 OFLog::configureFromCommandLine(cmd, app);
158
159 cmd.beginOptionBlock();
160 if (cmd.findOption("--read-file")) opt_readMode = ERM_autoDetect;
161 if (cmd.findOption("--read-file-only")) opt_readMode = ERM_fileOnly;
162 if (cmd.findOption("--read-dataset")) opt_readMode = ERM_dataset;
163 cmd.endOptionBlock();
164
165 cmd.beginOptionBlock();
166 if (cmd.findOption("--read-xfer-auto"))
167 opt_ixfer = EXS_Unknown;
168 if (cmd.findOption("--read-xfer-detect"))
169 dcmAutoDetectDatasetXfer.set(OFTrue);
170 if (cmd.findOption("--read-xfer-little"))
171 {
172 app.checkDependence("--read-xfer-little", "--read-dataset", opt_readMode == ERM_dataset);
173 opt_ixfer = EXS_LittleEndianExplicit;
174 }
175 if (cmd.findOption("--read-xfer-big"))
176 {
177 app.checkDependence("--read-xfer-big", "--read-dataset", opt_readMode == ERM_dataset);
178 opt_ixfer = EXS_BigEndianExplicit;
179 }
180 if (cmd.findOption("--read-xfer-implicit"))
181 {
182 app.checkDependence("--read-xfer-implicit", "--read-dataset", opt_readMode == ERM_dataset);
183 opt_ixfer = EXS_LittleEndianImplicit;
184 }
185 cmd.endOptionBlock();
186
187 // RLE options
188
189 cmd.beginOptionBlock();
190 if (cmd.findOption("--fragment-per-frame")) opt_fragmentSize = 0;
191 if (cmd.findOption("--fragment-size"))
192 {
193 app.checkValue(cmd.getValueAndCheckMin(opt_fragmentSize, OFstatic_cast(OFCmdUnsignedInt, 1)));
194 }
195 cmd.endOptionBlock();
196
197 cmd.beginOptionBlock();
198 if (cmd.findOption("--offset-table-create")) opt_createOffsetTable = OFTrue;
199 if (cmd.findOption("--offset-table-empty")) opt_createOffsetTable = OFFalse;
200 cmd.endOptionBlock();
201
202 cmd.beginOptionBlock();
203 if (cmd.findOption("--class-default")) opt_secondarycapture = OFFalse;
204 if (cmd.findOption("--class-sc")) opt_secondarycapture = OFTrue;
205 cmd.endOptionBlock();
206
207 cmd.beginOptionBlock();
208 if (cmd.findOption("--uid-always")) opt_uidcreation = OFTrue;
209 if (cmd.findOption("--uid-never")) opt_uidcreation = OFFalse;
210 cmd.endOptionBlock();
211
212 cmd.beginOptionBlock();
213 if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs();
214 if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs();
215 cmd.endOptionBlock();
216
217 cmd.beginOptionBlock();
218 if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL;
219 if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL;
220 if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL;
221 cmd.endOptionBlock();
222
223 cmd.beginOptionBlock();
224 if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength;
225 if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength;
226 cmd.endOptionBlock();
227
228 cmd.beginOptionBlock();
229 if (cmd.findOption("--padding-retain")) opt_opadenc = EPD_noChange;
230 if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding;
231 if (cmd.findOption("--padding-create"))
232 {
233 app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0));
234 app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0));
235 opt_opadenc = EPD_withPadding;
236 }
237 cmd.endOptionBlock();
238 }
239
240 /* print resource identifier */
241 OFLOG_DEBUG(dcmcrleLogger, rcsid << OFendl);
242
243 // register RLE compression codec
244 DcmRLEEncoderRegistration::registerCodecs(opt_uidcreation,
245 OFstatic_cast(Uint32, opt_fragmentSize), opt_createOffsetTable, opt_secondarycapture);
246
247 /* make sure data dictionary is loaded */
248 if (!dcmDataDict.isDictionaryLoaded())
249 {
250 OFLOG_WARN(dcmcrleLogger, "no data dictionary loaded, check environment variable: "
251 << DCM_DICT_ENVIRONMENT_VARIABLE);
252 }
253
254 // open inputfile
255 if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0))
256 {
257 OFLOG_FATAL(dcmcrleLogger, "invalid filename: <empty string>");
258 return 1;
259 }
260
261 DcmFileFormat fileformat;
262 DcmDataset * dataset = fileformat.getDataset();
263
264 OFLOG_INFO(dcmcrleLogger, "open input file " << opt_ifname);
265
266 OFCondition error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode);
267
268 if (error.bad())
269 {
270 OFLOG_FATAL(dcmcrleLogger, error.text() << ": reading file: " << opt_ifname);
271 return 1;
272 }
273
274 DcmXfer original_xfer(dataset->getOriginalXfer());
275 if (original_xfer.isEncapsulated())
276 {
277 OFLOG_INFO(dcmcrleLogger, "DICOM file is already compressed, converting to uncompressed transfer syntax first");
278 if (EC_Normal != dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL))
279 {
280 OFLOG_FATAL(dcmcrleLogger, "No conversion from compressed original to uncompressed transfer syntax possible!");
281 return 1;
282 }
283 }
284
285 OFString sopClass;
286 if (fileformat.getMetaInfo()->findAndGetOFString(DCM_MediaStorageSOPClassUID, sopClass).good())
287 {
288 /* check for DICOMDIR files */
289 if (sopClass == UID_MediaStorageDirectoryStorage)
290 {
291 OFLOG_FATAL(dcmcrleLogger, "DICOMDIR files (Media Storage Directory Storage SOP Class) cannot be compressed!");
292 return 1;
293 }
294 }
295
296 OFLOG_INFO(dcmcrleLogger, "Convert DICOM file to compressed transfer syntax");
297
298 DcmXfer opt_oxferSyn(opt_oxfer);
299
300 if (dataset->chooseRepresentation(opt_oxfer, NULL).good() && dataset->canWriteXfer(opt_oxfer))
301 {
302 OFLOG_INFO(dcmcrleLogger, "Output transfer syntax " << opt_oxferSyn.getXferName() << " can be written");
303 } else {
304 OFLOG_FATAL(dcmcrleLogger, "No conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible!");
305 return 1;
306 }
307
308 OFLOG_INFO(dcmcrleLogger, "create output file " << opt_ofname);
309
310 fileformat.loadAllDataIntoMemory();
311 error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc,
312 OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), EWM_updateMeta);
313
314 if (error.bad())
315 {
316 OFLOG_FATAL(dcmcrleLogger, error.text() << ": writing file: " << opt_ofname);
317 return 1;
318 }
319
320 OFLOG_INFO(dcmcrleLogger, "conversion successful");
321
322 // deregister RLE codec
323 DcmRLEEncoderRegistration::cleanup();
324
325 return 0;
326 }
327