/* * * Copyright (C) 2001-2021, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmjpeg * * Author: Marco Eichelberg * * Purpose: Decompress DICOM file * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmdata/dctk.h" #include "dcmtk/dcmdata/cmdlnarg.h" #include "dcmtk/ofstd/ofconapp.h" #include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ #include "dcmtk/dcmjpeg/djdecode.h" /* for dcmjpeg decoders */ #include "dcmtk/dcmjpeg/dipijpeg.h" /* for dcmimage JPEG plugin */ #ifdef WITH_ZLIB #include /* for zlibVersion() */ #endif #define OFFIS_CONSOLE_APPLICATION "dcmdjpeg" static OFLogger dcmdjpegLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; // ******************************************** #define SHORTCOL 4 #define LONGCOL 21 int main(int argc, char *argv[]) { const char *opt_ifname = NULL; const char *opt_ofname = NULL; E_FileReadMode opt_readMode = ERM_autoDetect; E_FileWriteMode opt_writeMode = EWM_createNewMeta; E_TransferSyntax opt_oxfer = EXS_LittleEndianExplicit; E_GrpLenEncoding opt_oglenc = EGL_recalcGL; E_EncodingType opt_oenctype = EET_ExplicitLength; E_PaddingEncoding opt_opadenc = EPD_noChange; OFCmdUnsignedInt opt_filepad = 0; OFCmdUnsignedInt opt_itempad = 0; E_TransferSyntax opt_ixfer = EXS_Unknown; // JPEG parameters E_DecompressionColorSpaceConversion opt_decompCSconversion = EDC_photometricInterpretation; E_UIDCreation opt_uidcreation = EUC_default; E_PlanarConfiguration opt_planarconfig = EPC_default; OFBool opt_predictor6WorkaroundEnable = OFFalse; OFBool opt_cornellWorkaroundEnable = OFFalse; OFBool opt_forceSingleFragmentPerFrame = OFFalse; OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Decode JPEG-compressed DICOM file", rcsid); OFCommandLine cmd; cmd.setOptionColumns(LONGCOL, SHORTCOL); cmd.setParamColumn(LONGCOL + SHORTCOL + 4); cmd.addParam("dcmfile-in", "DICOM input filename to be converted"); cmd.addParam("dcmfile-out", "DICOM output filename"); cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive); cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive); OFLog::addOptions(cmd); cmd.addGroup("input options:"); cmd.addSubGroup("input file format:"); cmd.addOption("--read-file", "+f", "read file format or data set (default)"); cmd.addOption("--read-file-only", "+fo", "read file format only"); cmd.addOption("--read-dataset", "-f", "read data set without file meta information"); cmd.addGroup("processing options:"); cmd.addSubGroup("color space conversion:"); cmd.addOption("--conv-photometric", "+cp", "convert if YCbCr photometric interpr. (default)"); cmd.addOption("--conv-lossy", "+cl", "convert YCbCr to RGB if lossy JPEG"); cmd.addOption("--conv-guess", "+cg", "convert to RGB if YCbCr is guessed by library"); cmd.addOption("--conv-guess-lossy", "+cgl", "convert to RGB if lossy JPEG and YCbCr is\nguessed by the underlying JPEG library"); cmd.addOption("--conv-always", "+ca", "always convert YCbCr to RGB"); cmd.addOption("--conv-never", "+cn", "never convert YCbCr to RGB"); cmd.addSubGroup("planar configuration:"); cmd.addOption("--planar-auto", "+pa", "automatically determine planar configuration\nfrom SOP class and color space (default)"); cmd.addOption("--color-by-pixel", "+px", "always store color-by-pixel"); cmd.addOption("--color-by-plane", "+pl", "always store color-by-plane"); cmd.addSubGroup("SOP Instance UID:"); cmd.addOption("--uid-default", "+ud", "keep same SOP Instance UID (default)"); cmd.addOption("--uid-always", "+ua", "always assign new UID"); cmd.addSubGroup("workaround options for incorrect JPEG encodings:"); cmd.addOption("--workaround-pred6", "+w6", "enable workaround for JPEG lossless images\nwith overflow in predictor 6"); cmd.addOption("--workaround-incpl", "+wi", "enable workaround for incomplete JPEG data"); cmd.addOption("--workaround-cornell", "+wc", "enable workaround for 16-bit JPEG lossless\nCornell images with Huffman table overflow"); cmd.addGroup("output options:"); cmd.addSubGroup("output file format:"); cmd.addOption("--write-file", "+F", "write file format (default)"); cmd.addOption("--write-dataset", "-F", "write data set without file meta information"); cmd.addSubGroup("output transfer syntax:"); cmd.addOption("--write-xfer-little", "+te", "write with explicit VR little endian (default)"); cmd.addOption("--write-xfer-big", "+tb", "write with explicit VR big endian TS"); cmd.addOption("--write-xfer-implicit", "+ti", "write with implicit VR little endian TS"); cmd.addSubGroup("post-1993 value representations:"); cmd.addOption("--enable-new-vr", "+u", "enable support for new VRs (UN/UT) (default)"); cmd.addOption("--disable-new-vr", "-u", "disable support for new VRs, convert to OB"); cmd.addSubGroup("group length encoding:"); cmd.addOption("--group-length-recalc", "+g=", "recalculate group lengths if present (default)"); cmd.addOption("--group-length-create", "+g", "always write with group length elements"); cmd.addOption("--group-length-remove", "-g", "always write without group length elements"); cmd.addSubGroup("length encoding in sequences and items:"); cmd.addOption("--length-explicit", "+e", "write with explicit lengths (default)"); cmd.addOption("--length-undefined", "-e", "write with undefined lengths"); cmd.addSubGroup("data set trailing padding (not with --write-dataset):"); cmd.addOption("--padding-retain", "-p=", "do not change padding\n(default if not --write-dataset)"); cmd.addOption("--padding-off", "-p", "no padding (implicit if --write-dataset)"); cmd.addOption("--padding-create", "+p", 2, "[f]ile-pad [i]tem-pad: integer", "align file on multiple of f bytes\nand items on multiple of i bytes"); /* evaluate command line */ prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); if (app.parseCommandLine(cmd, argc, argv)) { /* check exclusive options first */ if (cmd.hasExclusiveOption()) { if (cmd.findOption("--version")) { app.printHeader(OFTrue /*print host identifier*/); COUT << OFendl << "External libraries used:" << OFendl; #ifdef WITH_ZLIB COUT << "- ZLIB, Version " << zlibVersion() << OFendl; #endif COUT << "- " << DiJPEGPlugin::getLibraryVersionString() << OFendl; return 0; } } /* command line parameters */ cmd.getParam(1, opt_ifname); cmd.getParam(2, opt_ofname); /* options */ OFLog::configureFromCommandLine(cmd, app); cmd.beginOptionBlock(); if (cmd.findOption("--planar-auto")) opt_planarconfig = EPC_default; if (cmd.findOption("--color-by-pixel")) opt_planarconfig = EPC_colorByPixel; if (cmd.findOption("--color-by-plane")) opt_planarconfig = EPC_colorByPlane; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--conv-photometric")) opt_decompCSconversion = EDC_photometricInterpretation; if (cmd.findOption("--conv-lossy")) opt_decompCSconversion = EDC_lossyOnly; if (cmd.findOption("--conv-guess")) opt_decompCSconversion = EDC_guess; if (cmd.findOption("--conv-guess-lossy")) opt_decompCSconversion = EDC_guessLossyOnly; if (cmd.findOption("--conv-always")) opt_decompCSconversion = EDC_always; if (cmd.findOption("--conv-never")) opt_decompCSconversion = EDC_never; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--uid-default")) opt_uidcreation = EUC_default; if (cmd.findOption("--uid-always")) opt_uidcreation = EUC_always; cmd.endOptionBlock(); if (cmd.findOption("--workaround-pred6")) opt_predictor6WorkaroundEnable = OFTrue; if (cmd.findOption("--workaround-incpl")) opt_forceSingleFragmentPerFrame = OFTrue; if (cmd.findOption("--workaround-cornell")) opt_cornellWorkaroundEnable = OFTrue; cmd.beginOptionBlock(); if (cmd.findOption("--read-file")) { opt_readMode = ERM_autoDetect; opt_ixfer = EXS_Unknown; } if (cmd.findOption("--read-file-only")) { opt_readMode = ERM_fileOnly; opt_ixfer = EXS_Unknown; } if (cmd.findOption("--read-dataset")) { opt_readMode = ERM_dataset; // we don't know the real transfer syntax of the dataset, but this does // not matter. As long as the content of encapsulated pixel sequences is // some kind of JPEG bitstream supported by the underlying library, the // decompression will work. So we simply choose one of the lossless // transfer syntaxes, because these support all bit depths up to 16. opt_ixfer = EXS_JPEGProcess14; } cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--write-file")) opt_writeMode = EWM_createNewMeta; if (cmd.findOption("--write-dataset")) opt_writeMode = EWM_dataset; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--write-xfer-little")) opt_oxfer = EXS_LittleEndianExplicit; if (cmd.findOption("--write-xfer-big")) opt_oxfer = EXS_BigEndianExplicit; if (cmd.findOption("--write-xfer-implicit")) opt_oxfer = EXS_LittleEndianImplicit; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--enable-new-vr")) dcmEnableGenerationOfNewVRs(); if (cmd.findOption("--disable-new-vr")) dcmDisableGenerationOfNewVRs(); cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--group-length-recalc")) opt_oglenc = EGL_recalcGL; if (cmd.findOption("--group-length-create")) opt_oglenc = EGL_withGL; if (cmd.findOption("--group-length-remove")) opt_oglenc = EGL_withoutGL; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--length-explicit")) opt_oenctype = EET_ExplicitLength; if (cmd.findOption("--length-undefined")) opt_oenctype = EET_UndefinedLength; cmd.endOptionBlock(); cmd.beginOptionBlock(); if (cmd.findOption("--padding-retain")) { app.checkConflict("--padding-retain", "--write-dataset", opt_writeMode == EWM_dataset); opt_opadenc = EPD_noChange; } if (cmd.findOption("--padding-off")) opt_opadenc = EPD_withoutPadding; if (cmd.findOption("--padding-create")) { app.checkConflict("--padding-create", "--write-dataset", opt_writeMode == EWM_dataset); app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0)); app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0)); opt_opadenc = EPD_withPadding; } cmd.endOptionBlock(); } /* print resource identifier */ OFLOG_DEBUG(dcmdjpegLogger, rcsid << OFendl); // register global decompression codecs DJDecoderRegistration::registerCodecs( opt_decompCSconversion, opt_uidcreation, opt_planarconfig, opt_predictor6WorkaroundEnable, opt_cornellWorkaroundEnable, opt_forceSingleFragmentPerFrame); /* make sure data dictionary is loaded */ if (!dcmDataDict.isDictionaryLoaded()) { OFLOG_WARN(dcmdjpegLogger, "no data dictionary loaded, check environment variable: " << DCM_DICT_ENVIRONMENT_VARIABLE); } // open inputfile if ((opt_ifname == NULL) || (strlen(opt_ifname) == 0)) { OFLOG_FATAL(dcmdjpegLogger, "invalid filename: "); return 1; } OFCondition error = EC_Normal; DcmFileFormat fileformat; OFLOG_INFO(dcmdjpegLogger, "reading input file " << opt_ifname); error = fileformat.loadFile(opt_ifname, opt_ixfer, EGL_noChange, DCM_MaxReadLength, opt_readMode); if (error.bad()) { OFLOG_FATAL(dcmdjpegLogger, error.text() << ": reading file: " << opt_ifname); return 1; } DcmDataset *dataset = fileformat.getDataset(); OFLOG_INFO(dcmdjpegLogger, "decompressing file"); DcmXfer opt_oxferSyn(opt_oxfer); DcmXfer original_xfer(dataset->getOriginalXfer()); error = dataset->chooseRepresentation(opt_oxfer, NULL); if (error.bad()) { OFLOG_FATAL(dcmdjpegLogger, error.text() << ": decompressing file: " << opt_ifname); if (error == EJ_UnsupportedColorConversion) OFLOG_FATAL(dcmdjpegLogger, "Try --conv-never to disable color space conversion"); else if (error == EC_CannotChangeRepresentation) OFLOG_FATAL(dcmdjpegLogger, "Input transfer syntax " << original_xfer.getXferName() << " not supported"); return 1; } if (! dataset->canWriteXfer(opt_oxfer)) { OFLOG_FATAL(dcmdjpegLogger, "no conversion to transfer syntax " << opt_oxferSyn.getXferName() << " possible"); return 1; } OFLOG_INFO(dcmdjpegLogger, "creating output file " << opt_ofname); fileformat.loadAllDataIntoMemory(); error = fileformat.saveFile(opt_ofname, opt_oxfer, opt_oenctype, opt_oglenc, opt_opadenc, OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), opt_writeMode); if (error != EC_Normal) { OFLOG_FATAL(dcmdjpegLogger, error.text() << ": writing file: " << opt_ofname); return 1; } OFLOG_INFO(dcmdjpegLogger, "conversion successful"); // deregister global decompression codecs DJDecoderRegistration::cleanup(); return 0; }