r3375: Create a sandbox for DICOM support..

URL: https://svn.lrde.epita.fr/svn/oln/trunk/milena/sandbox ChangeLog: 2009-02-13 Fabien Freling <fabien.freling@lrde.epita.fr> Create a sandbox for DICOM support.. * fabien/dicom/load.hh: Load implementation. * fabien/dicom/save.hh: Save implementation. * fabien/igr/igr.cc: Minor fixes. --- dicom/load.hh | 547 +++++++++++++++++++++++++++ dicom/save.hh | 1171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ igr/igr.cc | 2 3 files changed, 1719 insertions(+), 1 deletion(-) Index: trunk/milena/sandbox/fabien/igr/igr.cc =================================================================== --- trunk/milena/sandbox/fabien/igr/igr.cc (revision 3374) +++ trunk/milena/sandbox/fabien/igr/igr.cc (revision 3375) @@ -41,7 +41,7 @@ #include <mln/io/cloud/all.hh> #include <mln/io/dump/all.hh> -#include <mln/core/alias/neighb2d.hh> +#include <mln/core/alias/neigyyhb2d.hh> #include <mln/core/alias/neighb3d.hh> #include <mln/labeling/flat_zones.hh> #include <mln/labeling/background.hh> Index: trunk/milena/sandbox/fabien/dicom/save.hh =================================================================== --- trunk/milena/sandbox/fabien/dicom/save.hh (revision 0) +++ trunk/milena/sandbox/fabien/dicom/save.hh (revision 3375) @@ -0,0 +1,1171 @@ +/* + * + * Copyright (C) 1994-2005, OFFIS + * + * This software and supporting documentation were developed by + * + * Kuratorium OFFIS e.V. + * Healthcare Information and Communication Systems + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY + * REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR + * FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR + * ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND + * PERFORMANCE OF THE SOFTWARE IS WITH THE USER. + * + * Module: dcmdata + * + * Author: Andreas Barth + * + * Purpose: create a Dicom FileFormat or DataSet from an ASCII-dump + * + * Last Update: $Author: onken $ + * Update Date: $Date: 2005/12/16 09:07:03 $ + * CVS/RCS Revision: $Revision: 1.51 $ + * Status: $State: Exp $ + * + * CVS/RCS Log at end of file + * + */ + +/* + * Input File Description: + * The input file be an output of dcmdump. One element (Tag, VR, value) must + * be written into one line separated by arbitrary spaces or tab characters. + * A # begins a comment that ends at the line end. Empty lines are allowed. + * This parts of a line have the following syntax: + * Tag: (gggg,eeee) + * with gggg and eeee are 4 character hexadecimal values representing + * group- and element-tag. Spaces and Tabs can be anywhere in a Tag + * specification + * VR: Value Representation must be written as 2 characters as in Part 6 + * of the DICOM 3.0 standard. No Spaces or Tabs are allowed between the + * two characters. If the VR can determined from the Tag, this part of + * a line is optional. + * Value: There are several rules for writing values: + * 1. US, SS, SL, UL, FD, FL are written as + * decimal strings that can be read by scanf. + * 2. AT is written as (gggg,eeee) with additional spaces stripped off + * automatically and gggg and eeee being decimal strings that + * can be read by scanf. + * 3. OB, OW values are written as byte or word hexadecimal values + * separated by '\' character. Alternatively, OB or OW values can + * be read from a separate file by writing the filename prefixed + * by a '=' character (e.g. =largepixeldata.dat). The contents of + * the file will be read as is. OW data is expected to be little + * endian ordered and will be swapped if necessary. No checks will + * be made to ensure that the amount of data is reasonable in terms + * of other attributes such as Rows or Columns. + * 4. UI is written as =Name in data dictionary or as + * unique identifer string (see 6.) , e.g. [1.2.840.....] + * 5. Strings without () <> [] spaces, tabs and # can be + * written directly + * 6. Other strings with must be surrounded by [ ]. No + * bracket structure is passed. The value ends at the last ] in + * the line. Anything after the ] is interpreted as comment. + * 7. ( < are interpreted special and may not be used when writing + * an input file by hand as beginning characters of a string. + * Multiple Value are separated by \ + * The sequence of lines must not be ordered but they can. + * References in DICOM Directories are not supported. + * Semantic errors are not detected. + * + * Examples: + * (0008,0020) DA [19921012] # 8, 1 StudyDate + * (0008,0016) UI =MRImageStorage # 26, 1 SOPClassUID + * (0002,0012) UI [1.2.276.0.7230010.100.1.1] + * (0020,0032) DS [0.0\0.0] # 8, 2 ImagePositionPatient + * (0028,0009) AT (3004,000c) # 4, 1 FrameIncrementPointer + * (0028,0010) US 256 # 4, 1 Rows + * (0002,0001) OB 01\00 + * + */ + +#include "dcmtk/config/osconfig.h" + +#define INCLUDE_CSTDLIB +#define INCLUDE_CSTDIO +#define INCLUDE_CCTYPE +#include "dcmtk/ofstd/ofstdinc.h" + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_STAT_H +#include <stat.h> +#endif +#ifdef HAVE_GUSI_H +#include <GUSI.h> +#endif + +#include "dcmtk/ofstd/ofstream.h" +#include "dcmtk/dcmdata/dctk.h" +#include "dcmtk/dcmdata/dcdebug.h" +#include "dcmtk/dcmdata/cmdlnarg.h" +#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/ofstd/ofstd.h" +#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ + +#ifdef WITH_ZLIB +#include <zlib.h> /* for zlibVersion() */ +#endif + +#define OFFIS_CONSOLE_APPLICATION "dump2dcm" + +static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" + OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; + +#define SHORTCOL 3 +#define LONGCOL 21 + +// Maximum Line Size + +const unsigned int DCM_DumpMaxLineSize = 4096; + + +// Field definition separators + +#define DCM_DumpCommentChar '#' +#define DCM_DumpTagDelim ')' +#define DCM_DumpOpenString '[' +#define DCM_DumpCloseString ']' +#define DCM_DumpOpenDescription '(' +#define DCM_DumpOpenFile '<' +#define DCM_DumpCloseFile '>' + +static void +stripWhitespace(char* s) +{ + char* p; + + if (s == NULL) return; + + p = s; + while (*s != '\0') { + if (isspace(*s) == OFFalse) { + *p++ = *s; + } + s++; + } + *p = '\0'; +} + +static char* +stripTrailingWhitespace(char* s) +{ + int i, n; + if (s == NULL) return s; + + n = strlen(s); + for (i = n - 1; i >= 0 && isspace(s[i]); i--) + s[i] = '\0'; + return s; +} + + +static char * +stripPrecedingWhitespace(char * s) +{ + char * p; + if (s == NULL) return s; + + for(p = s; *p && isspace(*p); p++) + ; + + return p; +} + +static OFBool +onlyWhitespace(const char* s) +{ + int len = strlen(s); + int charsFound = OFFalse; + + for (int i=0; (!charsFound) && (i<len); i++) { + charsFound = !isspace(s[i]); + } + return (!charsFound)?(OFTrue):(OFFalse); +} + +static char* +getLine(char* line, int maxLineLen, FILE* f, const unsigned long lineNumber) +{ + char* s; + + s = fgets(line, maxLineLen, f); + + // if line is too long, throw rest of it away + if (s && strlen(s) == size_t(maxLineLen-1) && s[maxLineLen-2] != '\n') + { + int c = fgetc(f); + while(c != '\n' && c != EOF) + c = fgetc(f); + CERR << "line " << lineNumber << " too long." << endl; + } + + /* strip any trailing white space */ + stripTrailingWhitespace(s); + + return s; +} + +static OFBool +isaCommentLine(const char* s) +{ + OFBool isComment = OFFalse; /* assumption */ + int len = strlen(s); + int i = 0; + for (i=0; i<len && isspace(s[i]); i++) /*loop*/; + isComment = (s[i] == DCM_DumpCommentChar); + return isComment; +} + +static OFBool +parseTag(char* & s, DcmTagKey& key) +{ + OFBool ok = OFTrue; + char * p; + unsigned int g, e; + + // find tag begin + p = strchr(s, DCM_DumpTagDelim); + if (p) + { + // string all white spaces and read tag + int len = p-s+1; + p = new char[len+1]; + OFStandard::strlcpy(p, s, len+1); + stripWhitespace(p); + s += len; + + if (sscanf(p, "(%x,%x)", &g, &e) == 2) { + key.set(g, e); + } else { + ok = OFFalse; + } + delete[] p; + } + else ok = OFFalse; + + return ok; +} + + +static OFBool +parseVR(char * & s, DcmEVR & vr) +{ + OFBool ok = OFTrue; + + s = stripPrecedingWhitespace(s); + + // Are there two upper characters? + if (isupper(*s) && isupper(*(s+1))) + { + char c_vr[3]; + OFStandard::strlcpy(c_vr, s, 3); + // Convert to VR + DcmVR dcmVR(c_vr); + vr = dcmVR.getEVR(); + s+=2; + } + else if ((*s == 'p')&&(*(s+1) == 'i')) + { + vr = EVR_pixelItem; + s+=2; + } + else if (((*s == 'o')&&(*(s+1) == 'x')) || ((*s == 'x')&&(*(s+1) == 's')) || + (*s == 'n')&&(*(s+1) == 'a') || ((*s == 'u')&&(*(s+1) == 'p'))) + { + // swallow internal VRs + vr = EVR_UNKNOWN; + s+=2; + } + else ok = OFFalse; + + return ok; +} + + +static int +searchLastClose(char *s, char closeChar) +{ + // search last close bracket in a line + // no bracket structure will be considered + + char * found = NULL; + char * p = s; + + while(p && *p) + { + p = strchr(p, closeChar); + if (p) + { + found = p; + p++; + } + } + + if (found) + return (found - s) + 1; + else + return 0; +} + + +static int +searchCommentOrEol(char *s) +{ + char * comment = strchr(s, DCM_DumpCommentChar); + if (comment) + return comment - s; + else if (s) + return strlen(s); + else + return 0; +} + + +static char* +convertNewlineCharacters(char* s) +{ + // convert the string "\n" into the \r\n combination required by DICOM + if (s == NULL || s[0] == '\0') return s; + int len = strlen(s); + int i = 0; + for (i=0; i<(len-1); i++) { + if (s[i] == '\\' && s[i+1] == 'n') { + s[i] = '\r'; + s[i+1] = '\n'; + i++; + } + } + + return s; +} + +static OFBool +parseValue(char * & s, char * & value, const DcmEVR & vr) +{ + OFBool ok = OFTrue; + int len; + value = NULL; + + s = stripPrecedingWhitespace(s); + + switch (*s) + { + case DCM_DumpOpenString: + len = searchLastClose(s, DCM_DumpCloseString); + if (len == 0) + ok = OFFalse; + else if (len > 2) + { + value = new char[len-1]; + OFStandard::strlcpy(value, s+1, len-1); + value = convertNewlineCharacters(value); + } + else + value = NULL; + break; + + case DCM_DumpOpenDescription: + /* need to distinguish vr=AT from description field */ + /* NB: if the vr is unknown this workaround will not succeed */ + if (vr == EVR_AT) + { + len = searchLastClose(s, DCM_DumpTagDelim); + if (len >= 11) // (gggg,eeee) allow non-significant spaces + { + char *pv = s; + DcmTagKey tag; + if (parseTag(pv, tag)) // check for valid tag format + { + value = new char[len+1]; + OFStandard::strlcpy(value, s, len+1); + stripWhitespace(value); + } else + ok = OFFalse; // skip description + } + else + ok = OFFalse; // skip description + } + break; + + case DCM_DumpOpenFile: + ok = OFFalse; // currently not supported + break; + + case DCM_DumpCommentChar: + break; + + default: + len = searchCommentOrEol(s); + if (len) + { + value = new char[len+1]; + OFStandard::strlcpy(value, s, len+1); + stripTrailingWhitespace(value); + } + break; + } + return ok; +} + + +static unsigned long +fileSize(const char *fname) +{ + struct stat s; + unsigned long nbytes = 0; + + if (stat(fname, &s) == 0) { + nbytes = s.st_size; + } + return nbytes; +} + +static OFCondition +putFileContentsIntoElement(DcmElement* elem, const char* filename) +{ + FILE* f = NULL; + OFCondition ec = EC_Normal; + + if ((f = fopen(filename, "rb")) == NULL) { + CERR << "ERROR: cannot open binary data file: " << filename << endl; + return EC_InvalidTag; + } + + unsigned long len = fileSize(filename); + unsigned long buflen = len; + if (buflen & 1) buflen++; /* if odd then make even (DICOM required even length values) */ + + Uint8* buf = new Uint8[buflen]; + if (buf == NULL) { + CERR << "ERROR: out of memory reading binary data file: " << filename << endl; + ec = EC_MemoryExhausted; + } else if (fread(buf, 1, OFstatic_cast(size_t, len), f) != len) { + CERR << "ERROR: error reading binary data file: " << filename << ": "; + perror(NULL); + ec = EC_CorruptedData; + } else { + /* assign buffer to attribute */ + DcmEVR evr = elem->getVR(); + if (evr == EVR_OB) { + /* put 8 bit OB data into the attribute */ + ec = elem->putUint8Array(buf, buflen); + } else if (evr == EVR_OW) { + /* put 16 bit OW data into the attribute */ + swapIfNecessary(gLocalByteOrder, EBO_LittleEndian, buf, buflen, sizeof(Uint16)); + ec = elem->putUint16Array(OFreinterpret_cast(Uint16 *, buf), buflen / 2); + } else if (evr == EVR_pixelItem) { + /* pixel item not yet supported */ + } + } + + fclose(f); + delete[] buf; + return ec; +} + +static OFCondition +insertIntoSet(DcmStack & stack, DcmTagKey tagkey, DcmEVR vr, char * value) +{ + // insert new Element into dataset or metaheader + + OFCondition l_error = EC_Normal; + OFCondition newElementError = EC_Normal; + + if (stack.empty()) + l_error = EC_CorruptedData; + + if (l_error == EC_Normal) + { + DcmElement * newElement = NULL; + DcmObject * topOfStack = stack.top(); + + // convert tagkey to tag including VR + DcmTag tag(tagkey); + DcmVR dcmvr(vr); + + const DcmEVR tagvr = tag.getEVR(); + if (tagvr != vr && vr != EVR_UNKNOWN && tagvr != EVR_UNKNOWN && + (tagkey != DCM_LUTData || (vr != EVR_US && vr != EVR_SS && vr != EVR_OW)) && + (tagvr != EVR_xs || (vr != EVR_US && vr != EVR_SS)) && + (tagvr != EVR_ox || (vr != EVR_OB && vr != EVR_OW)) && + (tagvr != EVR_na || vr != EVR_pixelItem)) + { + CERR << "Warning: Tag " << tag << " with wrong VR " + << dcmvr.getVRName() << " found, correct is " + << tag.getVR().getVRName() << endl; + } + + if (vr != EVR_UNKNOWN) + tag.setVR(dcmvr); + + // create new element + newElementError = newDicomElement(newElement, tag); + + if (newElementError == EC_Normal) + { + // tag describes an Element + if (!newElement) + // Tag was ambiguous + l_error = EC_InvalidVR; + else + { + // fill value + if (value) + { + if (value[0] == '=' && (tag.getEVR() == EVR_OB || tag.getEVR() == EVR_OW)) + { + /* + * Special case handling for OB or OW data. + * Allow a value beginning with a '=' character to represent + * a file containing data to be used as the attribute value. + * A '=' character is not a normal value since OB and OW values + * must be written as multivalued hexidecimal (e.g. "00\ff\0d\8f"); + */ + l_error = putFileContentsIntoElement(newElement, value+1); + } + else if (tag.getEVR() == EVR_pixelItem) + { + /* pixel items not yet supported */ + l_error = EC_InvalidTag; + } else { + l_error = newElement->putString(value); + } + } + + // insert element into hierarchy + if (l_error == EC_Normal) + { + switch(topOfStack->ident()) + { + case EVR_item: + case EVR_dirRecord: + case EVR_dataset: + case EVR_metainfo: + { + DcmItem *item = OFstatic_cast(DcmItem *, topOfStack); + item -> insert(newElement); + if (newElement->ident() == EVR_SQ || newElement->ident() == EVR_pixelSQ) + stack.push(newElement); + } + break; + default: + l_error = EC_InvalidTag; + break; + } + } + } + } + else if (newElementError == EC_SequEnd) + { + // pop stack if stack object was a sequence + if (topOfStack->ident() == EVR_SQ || topOfStack->ident() == EVR_pixelSQ) + stack.pop(); + else + l_error = EC_InvalidTag; + } + else if (newElementError == EC_ItemEnd) + { + // pop stack if stack object was an item + switch (topOfStack->ident()) + { + case EVR_item: + case EVR_dirRecord: + case EVR_dataset: + case EVR_metainfo: + stack.pop(); + break; + + default: + l_error = EC_InvalidTag; + break; + } + } + else if (newElementError == EC_InvalidTag) + { + if (tag.getXTag() == DCM_Item) + { + DcmItem * item = NULL; + if (topOfStack->getTag().getXTag() == DCM_DirectoryRecordSequence) + { + // an Item must be pushed to the stack + item = new DcmDirectoryRecord(tag, 0); + OFstatic_cast(DcmSequenceOfItems *, topOfStack) -> insert(item); + stack.push(item); + } + else if (topOfStack->ident() == EVR_SQ) + { + // an item must be pushed to the stack + item = new DcmItem(tag); + OFstatic_cast(DcmSequenceOfItems *, topOfStack) -> insert(item); + stack.push(item); + } + else + l_error = EC_InvalidTag; + } + else + l_error = EC_InvalidTag; + } + else + { + l_error = EC_InvalidTag; + } + } + + return l_error; +} + +static OFBool +readDumpFile(DcmMetaInfo * metaheader, DcmDataset * dataset, + FILE * infile, const char * ifname, const OFBool stopOnErrors, + const unsigned long maxLineLength) +{ + char * lineBuf = new char[maxLineLength]; + int lineNumber = 0; + OFBool errorOnThisLine = OFFalse; + char * parse = NULL; + char * value = NULL; + DcmEVR vr = EVR_UNKNOWN; + int errorsEncountered = 0; + DcmTagKey tagkey; + DcmStack metaheaderStack; + DcmStack datasetStack; + + if (metaheader) + metaheaderStack.push(metaheader); + + datasetStack.push(dataset); + + while(getLine(lineBuf, int(maxLineLength), infile, lineNumber+1)) + { + lineNumber++; + + // ignore empty lines and comment lines + if (onlyWhitespace(lineBuf)) + continue; + if (isaCommentLine(lineBuf)) + continue; + + errorOnThisLine = OFFalse; + + parse = &lineBuf[0]; + + // parse tag an the line + if (!parseTag(parse, tagkey)) + { + CERR << OFFIS_CONSOLE_APPLICATION ": "<< ifname << ": " + << "no Tag found (line " << lineNumber << ")"<< endl; + errorOnThisLine = OFTrue; + } + + // parse optional VR + if (!errorOnThisLine && !parseVR(parse, vr)) + vr = EVR_UNKNOWN; + + // parse optional value + if (!errorOnThisLine && !parseValue(parse, value, vr)) + { + CERR << OFFIS_CONSOLE_APPLICATION ": "<< ifname << ": " + << "incorrect value specification (line " << lineNumber << ")"<< endl; + errorOnThisLine = OFTrue; + } + + + // insert new element that consists of tag, VR, and value + if (!errorOnThisLine) + { + OFCondition l_error = EC_Normal; + if (tagkey.getGroup() == 2) + { + if (metaheader) + l_error = insertIntoSet(metaheaderStack, tagkey, vr, value); + } + else + l_error = insertIntoSet(datasetStack, tagkey, vr, value); + + if (value) + { + delete[] value; + value = NULL; + } + if (l_error != EC_Normal) + { + errorOnThisLine = OFTrue; + CERR << OFFIS_CONSOLE_APPLICATION ": " << ifname << ": Error in creating Element: " + << l_error.text() << " (line " << lineNumber << ")"<< endl; + } + + } + + if (errorOnThisLine) + errorsEncountered++; + } + + + // test blocking structure + if (metaheader && metaheaderStack.card() != 1) + { + CERR << OFFIS_CONSOLE_APPLICATION ": " << ifname << ": Block Error in metaheader" << endl; + errorsEncountered++; + } + + if (datasetStack.card() != 1) + { + CERR << OFFIS_CONSOLE_APPLICATION ": " << ifname << ": Block Error in dataset" << endl; + errorsEncountered++; + } + + delete[] lineBuf; + + if (errorsEncountered) + { + CERR << errorsEncountered << " Errors found in " << ifname << endl; + return !stopOnErrors; + } + else + return OFTrue; +} + +// ******************************************** + +int main(int argc, char *argv[]) +{ +#ifdef HAVE_GUSI_H + GUSISetup(GUSIwithSIOUXSockets); + GUSISetup(GUSIwithInternetSockets); +#endif + + SetDebugLevel(( 0 )); + + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION , "Convert ASCII dump to DICOM file", rcsid); + OFCommandLine cmd; + + cmd.setOptionColumns(LONGCOL, SHORTCOL); + cmd.setParamColumn(LONGCOL + SHORTCOL + 4); + + cmd.addParam("dumpfile-in", "dump input filename"); + cmd.addParam("dcmfile-out", "DICOM output filename"); + + cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); + cmd.addOption("--help", "-h", "print this help text and exit"); + cmd.addOption("--version", "print version information and exit", OFTrue /* exclusive */); + cmd.addOption("--verbose", "-v", "verbose mode, print processing details"); + cmd.addOption("--debug", "-d", "debug mode, print debug information"); + + cmd.addGroup("input options:", LONGCOL, SHORTCOL + 2); + cmd.addOption("--line", "+l", 1, "[m]ax-length: integer", + "maximum line length m (default 4096)"); + + 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("error handling:"); + cmd.addOption("--stop-on-error", "-E", "do not write if dump is damaged (default)"); + cmd.addOption("--ignore-errors", "+E", "attempt to write even if dump is damaged"); + 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"); + + int opt_debugMode = 0; + const char* ifname = NULL; + const char* ofname = NULL; + E_TransferSyntax oxfer = EXS_LittleEndianExplicit; + E_EncodingType oenctype = EET_ExplicitLength; + E_GrpLenEncoding oglenc = EGL_recalcGL; + E_PaddingEncoding opadenc = EPD_withoutPadding; + OFCmdUnsignedInt opt_filepad = 0; + OFCmdUnsignedInt opt_itempad = 0; + OFCmdUnsignedInt opt_linelength = DCM_DumpMaxLineSize; + OFBool verbosemode = OFFalse; + OFBool stopOnErrors = OFTrue; + OFBool createFileFormat = OFTrue; + + /* evaluate command line */ + prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); + if (app.parseCommandLine(cmd, argc, argv, OFCommandLine::ExpandWildcards)) + { + /* check exclusive options first */ + + if (cmd.getParamCount() == 0) + { + if (cmd.findOption("--version")) + { + app.printHeader(OFTrue /*print host identifier*/); // uses ofConsole.lockCerr() + CERR << endl << "External libraries used:"; +#ifdef WITH_ZLIB + CERR << endl << "- ZLIB, Version " << zlibVersion() << endl; +#else + CERR << " none" << endl; +#endif + return 0; + } + } + + /* command line parameters */ + + cmd.getParam(1, ifname); + cmd.getParam(2, ofname); + + if (cmd.findOption("--verbose")) verbosemode = OFTrue; + if (cmd.findOption("--debug")) opt_debugMode = 5; + + if (cmd.findOption("--line")) + { + app.checkValue(cmd.getValueAndCheckMin(opt_linelength, 80)); + } + + cmd.beginOptionBlock(); + if (cmd.findOption("--write-file")) createFileFormat = OFTrue; + if (cmd.findOption("--write-dataset")) createFileFormat = OFFalse; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--write-xfer-little")) oxfer = EXS_LittleEndianExplicit; + if (cmd.findOption("--write-xfer-big")) oxfer = EXS_BigEndianExplicit; + if (cmd.findOption("--write-xfer-implicit")) oxfer = EXS_LittleEndianImplicit; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--stop-on-error")) stopOnErrors = OFTrue; + if (cmd.findOption("--ignore-errors")) stopOnErrors = OFFalse; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-new-vr")) + { + dcmEnableUnknownVRGeneration.set(OFTrue); + dcmEnableUnlimitedTextVRGeneration.set(OFTrue); + } + if (cmd.findOption("--disable-new-vr")) + { + dcmEnableUnknownVRGeneration.set(OFFalse); + dcmEnableUnlimitedTextVRGeneration.set(OFFalse); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--group-length-recalc")) oglenc = EGL_recalcGL; + if (cmd.findOption("--group-length-create")) oglenc = EGL_withGL; + if (cmd.findOption("--group-length-remove")) oglenc = EGL_withoutGL; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--length-explicit")) oenctype = EET_ExplicitLength; + if (cmd.findOption("--length-undefined")) oenctype = EET_UndefinedLength; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--padding-retain")) + { + if (!createFileFormat) app.printError("--padding-retain not allowed with --write-dataset"); + opadenc = EPD_noChange; + } + if (cmd.findOption("--padding-off")) opadenc = EPD_withoutPadding; + if (cmd.findOption("--padding-create")) + { + if (!createFileFormat) app.printError("--padding-create not allowed with --write-dataset"); + app.checkValue(cmd.getValueAndCheckMin(opt_filepad, 0)); + app.checkValue(cmd.getValueAndCheckMin(opt_itempad, 0)); + opadenc = EPD_withPadding; + } + cmd.endOptionBlock(); + + } + + DcmFileFormat fileformat; + DcmMetaInfo * metaheader = fileformat.getMetaInfo(); + DcmDataset * dataset = fileformat.getDataset(); + + SetDebugLevel((opt_debugMode)); + + /* make sure data dictionary is loaded */ + if (!dcmDataDict.isDictionaryLoaded()) { + CERR << "Warning: no data dictionary loaded, " + << "check environment variable: " + << DCM_DICT_ENVIRONMENT_VARIABLE << endl; + } + + if (verbosemode) + COUT << "reading dump file: " << ifname << endl; + + // open input dump file + if ((ifname == NULL) || (strlen(ifname) == 0)) + { + CERR << "invalid input filename: <empty string>" << endl; + return 1; + } + FILE * dumpfile = fopen(ifname, "r"); + if (!dumpfile) + { + CERR << "input file does not exist: " << ifname << endl; + return 1; + } + + // read dump file into metaheader and dataset + if (readDumpFile(metaheader, dataset, dumpfile, ifname, stopOnErrors, + OFstatic_cast(unsigned long, opt_linelength))) + { + // write into file format or dataset + if (verbosemode) + COUT << "writing DICOM file" << endl; + + OFCondition l_error = fileformat.saveFile(ofname, oxfer, oenctype, oglenc, opadenc, + OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad), !createFileFormat); + + if (l_error == EC_Normal) + COUT << "dump successfully converted." << endl; + else + { + CERR << "Error: " << l_error.text() + << ": writing file: " << ofname << endl; + return 1; + } + } + + return 0; +} + +//******************************* + +/* +** CVS/RCS Log: +** $Log: dump2dcm.cc,v $ +** Revision 1.51 2005/12/16 09:07:03 onken +** - Added variable initialization to avoid compiler warning +** +** Revision 1.50 2005/12/08 15:40:50 meichel +** Changed include path schema for all DCMTK header files +** +** Revision 1.49 2004/07/13 09:43:10 meichel +** Fixed memory leak occuring when raw data is read from file. +** +** Revision 1.48 2004/03/05 09:59:00 joergr +** Avoid wrong warning for LUTData (0028,3006) having a VR of US or SS. +** Added initial "hooks" for (compressed) pixel items. +** Added "ignore errors" option (similar to dcmdump). +** +** Revision 1.47 2004/01/16 10:53:16 joergr +** Adapted type casts to new-style typecast operators defined in ofcast.h. +** Removed acknowledgements with e-mail addresses from CVS log. +** +** Revision 1.46 2003/11/05 16:15:27 meichel +** Removed useless "--write-xfer-same" command line option +** +** Revision 1.45 2002/12/05 13:59:29 joergr +** Fixed typo. +** +** Revision 1.44 2002/11/27 12:07:18 meichel +** Adapted module dcmdata to use of new header file ofstdinc.h +** +** Revision 1.43 2002/11/26 08:43:02 meichel +** Replaced all includes for "zlib.h" with <zlib.h> +** to avoid inclusion of zlib.h in the makefile dependencies. +** +** Revision 1.42 2002/09/23 17:52:04 joergr +** Prepared code for future support of 'config.guess' host identifiers. +** +** Revision 1.41 2002/09/23 13:50:42 joergr +** Added new command line option "--version" which prints the name and version +** number of external libraries used. +** +** Revision 1.40 2002/08/21 10:14:16 meichel +** Adapted code to new loadFile and saveFile methods, thus removing direct +** use of the DICOM stream classes. +** +** Revision 1.39 2002/04/16 13:38:55 joergr +** Added configurable support for C++ ANSI standard includes (e.g. streams). +** +** Revision 1.38 2001/12/11 14:00:39 joergr +** Fixed bug in 'dump2dcm' parser causing AT attribute values to be ignored. +** +** Revision 1.37 2001/11/09 15:50:53 joergr +** Renamed some of the getValue/getParam methods to avoid ambiguities reported +** by certain compilers. +** +** Revision 1.36 2001/09/25 17:21:01 meichel +** Adapted dcmdata to class OFCondition +** +** Revision 1.35 2001/06/01 15:48:30 meichel +** Updated copyright header +** +** Revision 1.34 2000/04/14 15:42:54 meichel +** Global VR generation flags are now derived from OFGlobal and, thus, +** safe for use in multi-thread applications. +** +** Revision 1.33 2000/03/08 16:26:06 meichel +** Updated copyright header. +** +** Revision 1.32 2000/03/06 18:09:38 joergr +** Avoid empty statement in the body of if-statements (MSVC6 reports warnings). +** +** Revision 1.31 2000/03/03 14:05:16 meichel +** Implemented library support for redirecting error messages into memory +** instead of printing them to stdout/stderr for GUI applications. +** +** Revision 1.30 2000/02/29 11:48:51 meichel +** Removed support for VS value representation. This was proposed in CP 101 +** but never became part of the standard. +** +** Revision 1.29 2000/02/23 15:11:36 meichel +** Corrected macro for Borland C++ Builder 4 workaround. +** +** Revision 1.28 2000/02/10 16:02:51 joergr +** Enhanced handling of PixelData/Item element. Externally stored raw data is +** now always imported as little endian and swapped if necessary. This change +** reflects the new 'export' feature of dcmdump. +** +** Revision 1.27 2000/02/01 10:11:58 meichel +** Avoiding to include <stdlib.h> as extern "C" on Borland C++ Builder 4, +** workaround for bug in compiler header files. +** +** Revision 1.26 1999/05/03 14:13:40 joergr +** Minor code purifications to keep Sun CC 2.0.1 quiet. +** +** Revision 1.25 1999/04/27 17:50:53 joergr +** Adapted console applications to new OFCommandLine and OFConsoleApplication +** functionality. +** +** Revision 1.24 1999/04/27 12:23:27 meichel +** Prevented dcmdata applications from opening a file with empty filename, +** leads to application crash on Win32. +** +** Revision 1.23 1999/03/31 09:24:23 meichel +** Updated copyright header in module dcmdata +** +** Revision 1.22 1999/03/29 10:14:15 meichel +** Adapted command line options of dcmdata applications to new scheme. +** +** Revision 1.21 1999/03/22 16:16:01 meichel +** dump2dcm now allows to include the contents of binary files +** as OB/OW values while converting a dump to a DICOM file. +** +** Revision 1.20 1999/01/07 14:13:12 meichel +** Corrected bug in dump2dcm that prevented the correct processing of +** dumps created with dcmdump if they contained the "internal" VR markers +** "xs" (US or SS) or "ox" (OB or OW). +** +** Revision 1.19 1998/01/27 10:51:27 meichel +** Removed some unused variables, meaningless const modifiers +** and unreached statements. +** +** Revision 1.18 1998/01/14 14:41:15 hewett +** Modified existing -u command line option to also disable generation +** of UT and VS (previously just disabled generation of UN). +** +** Revision 1.17 1997/08/05 07:34:54 andreas +** Corrected Error handling of SQ in dump2dcm +** +** Revision 1.16 1997/07/21 07:59:02 andreas +** - Deleted support for DcmPixelItems and DcmPixelSequences in dump2dcm +** ToDo: New support should be added in the future compatible to +** the new DcmPixel class. +** - Replace all boolean types (BOOLEAN, CTNBOOLEAN, DICOM_BOOL, BOOL) +** with one unique boolean type OFBool. +** +** Revision 1.15 1997/07/03 15:09:40 andreas +** - removed debugging functions Bdebug() and Edebug() since +** they write a static array and are not very useful at all. +** Cdebug and Vdebug are merged since they have the same semantics. +** The debugging functions in dcmdata changed their interfaces +** (see dcmdata/include/dcdebug.h) +** +** Revision 1.14 1997/05/30 06:44:57 andreas +** - fixed scanf format problem leading to warnings on 64 bit machines. +** +** Revision 1.13 1997/05/29 15:52:52 meichel +** Added constant for dcmtk release date in dcuid.h. +** All dcmtk applications now contain a version string +** which is displayed with the command line options ("usage" message) +** and which can be queried in the binary with the "ident" command. +** +** Revision 1.12 1997/05/22 13:26:25 hewett +** Modified the test for presence of a data dictionary to use the +** method DcmDataDictionary::isDictionaryLoaded(). +** +** Revision 1.11 1997/05/20 07:57:12 andreas +** - Removed obsolete applications file2ds and ds2file. The functionality of these +** applications is now peformed by dcmconv. Unified calling parameters +** are implemented in dump2dcm, dcmdump and dcmconv. +** +** Revision 1.10 1997/05/16 08:31:06 andreas +** - Revised handling of GroupLength elements and support of +** DataSetTrailingPadding elements. The enumeratio E_GrpLenEncoding +** got additional enumeration values (for a description see dctypes.h). +** addGroupLength and removeGroupLength methods are replaced by +** computeGroupLengthAndPadding. To support Padding, the parameters of +** element and sequence write functions changed. +** +** Revision 1.9 1997/04/18 08:06:56 andreas +** - Minor corrections: correct some warnings of the SUN-C++ Compiler +** concerning the assignments of wrong types and inline compiler +** errors +** - The put/get-methods for all VRs did not conform to the C++-Standard +** draft. Some Compilers (e.g. SUN-C++ Compiler, Metroworks +** CodeWarrier, etc.) create many warnings concerning the hiding of +** overloaded get methods in all derived classes of DcmElement. +** So the interface of all value representation classes in the +** library are changed rapidly, e.g. +** OFCondition get(Uint16 & value, const unsigned long pos); +** becomes +** OFCondition getUint16(Uint16 & value, const unsigned long pos); +** All (retired) "returntype get(...)" methods are deleted. +** For more information see dcmdata/include/dcelem.h +** +** Revision 1.8 1997/03/27 15:47:25 hewett +** Added command line switche to allow generation of UN to be +** disabled (it is enabled by default). +** +** Revision 1.7 1996/09/24 16:13:51 hewett +** Added preliminary support for the Macintosh environment (GUSI library). +** +** Revision 1.6 1996/05/02 17:00:23 hewett +** Corrected program name in usage description. +** +** Revision 1.5 1996/05/02 15:55:11 hewett +** Stopped whitespace being stripped from inside value strings when +** no [] delimiter present. Now only leading and trailing whitespace +** is stripped. +** +** Revision 1.4 1996/04/27 12:13:01 hewett +** Corrected bug in last bug-fix. A tag value [some text] was being +** parsed as an empty string. Now both [] and [some text] appear to +** work as intended. +** +** Revision 1.3 1996/03/22 12:38:44 andreas +** Correct some mistakes: handling [] as empty string (no value field) +** handling =Name correct if Name is not correct +** +** Revision 1.2 1996/03/12 15:11:39 hewett +** Added call to prepareCmdLineArgs to enable command line arguments +** in environments which do not provide them. +** +** Revision 1.1 1996/01/29 13:36:38 andreas +** dump2dcm added convert ASCII descriptions into DICOM files +** +** +*/ Index: trunk/milena/sandbox/fabien/dicom/load.hh =================================================================== --- trunk/milena/sandbox/fabien/dicom/load.hh (revision 0) +++ trunk/milena/sandbox/fabien/dicom/load.hh (revision 3375) @@ -0,0 +1,547 @@ +/* + * + * Copyright (C) 1994-2005, OFFIS + * + * This software and supporting documentation were developed by + * + * Kuratorium OFFIS e.V. + * Healthcare Information and Communication Systems + * Escherweg 2 + * D-26121 Oldenburg, Germany + * + * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY + * REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR + * FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR + * ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND + * PERFORMANCE OF THE SOFTWARE IS WITH THE USER. + * + * Module: dcmdata + * + * Author: Gerd Ehlers + * + * Purpose: List the contents of a dicom file + * + * Last Update: $Author: meichel $ + * Update Date: $Date: 2005/12/08 15:40:46 $ + * CVS/RCS Revision: $Revision: 1.55 $ + * Status: $State: Exp $ + * + * CVS/RCS Log at end of file + * + */ + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/ofstd/ofstream.h" +#include "dcmtk/dcmdata/dctk.h" +#include "dcmtk/dcmdata/dcdebug.h" +#include "dcmtk/dcmdata/cmdlnarg.h" +#include "dcmtk/ofstd/ofconapp.h" +#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */ +#include "dcmtk/dcmdata/dcistrmz.h" /* for dcmZlibExpectRFC1950Encoding */ + +#define INCLUDE_CSTDLIB +#define INCLUDE_CSTRING +#include "dcmtk/ofstd/ofstdinc.h" + +#ifdef WITH_ZLIB +#include <zlib.h> /* for zlibVersion() */ +#endif + +#define OFFIS_CONSOLE_APPLICATION "dcmdump" + +static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" + OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; + +#ifdef HAVE_GUSI_H + /* needed for Macintosh */ +#include <GUSI.h> +#include <SIOUX.h> +#endif + +static int dumpFile(ostream & out, + const char *ifname, + const E_FileReadMode readMode, + const E_TransferSyntax xfer, + const size_t printFlags, + const OFBool loadIntoMemory, + const OFBool stopOnErrors, + const OFBool writePixelData, + const char *pixelDirectory); + +// ******************************************** + +static OFBool printAllInstances = OFTrue; +static OFBool prependSequenceHierarchy = OFFalse; +static int printTagCount = 0; +static const int MAX_PRINT_TAG_NAMES = 1024; +static const char* printTagNames[MAX_PRINT_TAG_NAMES]; +static const DcmTagKey* printTagKeys[MAX_PRINT_TAG_NAMES]; +static OFCmdUnsignedInt maxReadLength = 4096; // default is 4 KB + +static OFBool addPrintTagName(const char* tagName) +{ + if (printTagCount >= MAX_PRINT_TAG_NAMES) { + CERR << "error: too many print Tag options (max: " << MAX_PRINT_TAG_NAMES << ")" << endl; + return OFFalse; + } + + unsigned int group = 0xffff; + unsigned int elem = 0xffff; + if (sscanf( tagName, "%x,%x", &group, &elem ) != 2 ) + { + /* it is a name */ + const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock(); + const DcmDictEntry *dicent = globalDataDict.findEntry(tagName); + if( dicent == NULL ) { + CERR << "error: unrecognised tag name: '" << tagName << "'" << endl; + dcmDataDict.unlock(); + return OFFalse; + } else { + /* note for later */ + printTagKeys[printTagCount] = new DcmTagKey(dicent->getKey()); + } + dcmDataDict.unlock(); + } else { + /* tag name has format xxxx,xxxx */ + /* do not lookup in dictionary, tag could be private */ + printTagKeys[printTagCount] = NULL; + } + + printTagNames[printTagCount] = strcpy(OFstatic_cast(char*, malloc(strlen(tagName)+1)), tagName); + printTagCount++; + return OFTrue; +} + +#define SHORTCOL 3 +#define LONGCOL 20 + +int main(int argc, char *argv[]) +{ + int opt_debugMode = 0; + OFBool loadIntoMemory = OFTrue; + size_t printFlags = DCMTypes::PF_shortenLongTagValues /*| DCMTypes::PF_showTreeStructure*/; + OFBool printFilename = OFFalse; + OFBool writePixelData = OFFalse; + E_FileReadMode readMode = ERM_autoDetect; + E_TransferSyntax xfer = EXS_Unknown; + OFBool stopOnErrors = OFTrue; + const char *current = NULL; + const char *pixelDirectory = NULL; + + +#ifdef HAVE_GUSI_H + /* needed for Macintosh */ + /* set options for the Metrowerks CodeWarrior SIOUX console */ + SIOUXSettings.autocloseonquit = OFFalse; + SIOUXSettings.asktosaveonclose = OFFalse; + SIOUXSettings.showstatusline = OFTrue; + SIOUXSettings.setupmenus = OFTrue; + /* set options for the GUSI sockets library */ + GUSISetup(GUSIwithSIOUXSockets); + GUSISetup(GUSIwithInternetSockets); +#endif + + SetDebugLevel(( 0 )); + + OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION, "Dump DICOM file and data set", rcsid); + OFCommandLine cmd; + cmd.setOptionColumns(LONGCOL, SHORTCOL); + cmd.setParamColumn(LONGCOL + SHORTCOL + 4); + + cmd.addParam("dcmfile-in", "DICOM input filename to be dumped", OFCmdParam::PM_MultiMandatory); + + cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2); + cmd.addOption("--help", "-h", "print this help text and exit"); + cmd.addOption("--version", "print version information and exit", OFTrue /* exclusive */); +#ifdef USE_EXPERIMENTAL_QUIET_MODE + cmd.addOption("--quiet", "-q", "quiet mode, print no warnings and errors"); +#endif + cmd.addOption("--debug", "-d", "debug mode, print debug information"); + + 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.addSubGroup("input transfer syntax:"); + cmd.addOption("--read-xfer-auto", "-t=", "use TS recognition (default)"); + cmd.addOption("--read-xfer-detect", "-td", "ignore TS specified in the file meta header"); + cmd.addOption("--read-xfer-little", "-te", "read with explicit VR little endian TS"); + cmd.addOption("--read-xfer-big", "-tb", "read with explicit VR big endian TS"); + cmd.addOption("--read-xfer-implicit", "-ti", "read with implicit VR little endian TS"); + cmd.addSubGroup("parsing of odd-length attributes:"); + cmd.addOption("--accept-odd-length", "+ao", "accept odd length attributes (default)"); + cmd.addOption("--assume-even-length", "+ae", "assume real length is one byte larger"); + cmd.addSubGroup("handling of undefined length UN elements:"); + cmd.addOption("--enable-cp246", "+ui", "read undefined len UN as implicit VR (default)"); + cmd.addOption("--disable-cp246", "-ui", "read undefined len UN as explicit VR"); + cmd.addSubGroup("handling of defined length UN elements:"); + cmd.addOption("--retain-un", "-uc", "retain elements as UN (default)"); + cmd.addOption("--convert-un", "+uc", "convert to real VR if known"); + cmd.addSubGroup("automatic data correction:"); + cmd.addOption("--enable-correction", "+dc", "enable automatic data correction (default)"); + cmd.addOption("--disable-correction", "-dc", "disable automatic data correction"); +#ifdef WITH_ZLIB + cmd.addSubGroup("bitstream format of deflated input:"); + cmd.addOption("--bitstream-deflated", "+bd", "expect deflated bitstream (default)"); + cmd.addOption("--bitstream-zlib", "+bz", "expect deflated zlib bitstream"); +#endif + + cmd.addGroup("output options:"); + cmd.addSubGroup("printing:"); + cmd.addOption("--load-all", "+M", "load very long tag values (default)"); + cmd.addOption("--load-short", "-M", "do not load very long values (e.g. pixel data)"); + cmd.addOption("--max-read-length", "+R", 1, "[k]bytes: integer [4..4194302] (default: 4)", + "set threshold for long values to k kbytes"); + cmd.addOption("--print-all", "+L", "print long tag values completely"); + cmd.addOption("--print-short", "-L", "print long tag values shortened (default)"); + cmd.addOption("--print-filename", "+F", "print header with filename for each input file"); + + cmd.addSubGroup("error handling:"); + cmd.addOption("--stop-on-error", "-E", "do not print if file is damaged (default)"); + cmd.addOption("--ignore-errors", "+E", "attempt to print even if file is damaged"); + + cmd.addSubGroup("searching:"); + cmd.addOption("--search", "+P", 1, "[t]ag: \"xxxx,xxxx\" or a data dictionary name", + "print the value of tag t\nthis option can be specified multiple times\n(default: the complete file is printed)"); + + cmd.addOption("--search-all", "+s", "print all instances of searched tags (default)"); + cmd.addOption("--search-first", "-s", "only print first instance of searched tags"); + + cmd.addOption("--prepend", "+p", "prepend sequence hierarchy to printed tag,\ndenoted by: (xxxx,xxxx).(xxxx,xxxx).*\n(only with --search-all or --search-first)"); + cmd.addOption("--no-prepend", "-p", "do not prepend hierarchy to tag (default)"); + + cmd.addSubGroup("writing:"); + cmd.addOption("--write-pixel", "+W", 1, "[d]irectory : string", + "write pixel data to a .raw file stored in d\n(little endian, filename created automatically)"); + + /* evaluate command line */ + prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION); + if (app.parseCommandLine(cmd, argc, argv, OFCommandLine::ExpandWildcards)) + { + /* check exclusive options first */ + + if (cmd.getParamCount() == 0) + { + if (cmd.findOption("--version")) + { + app.printHeader(OFTrue /*print host identifier*/); // uses ofConsole.lockCerr() + CERR << endl << "External libraries used:"; +#ifdef WITH_ZLIB + CERR << endl << "- ZLIB, Version " << zlibVersion() << endl; +#else + CERR << " none" << endl; +#endif + return 0; + } + } + + /* options */ + +#ifdef USE_EXPERIMENTAL_QUIET_MODE + if (cmd.findOption("--quiet")) + { + // tbd: disable ofConsole output! + app.setQuietMode(); + } +#endif + if (cmd.findOption("--debug")) opt_debugMode = 5; + + cmd.beginOptionBlock(); + if (cmd.findOption("--read-file")) readMode = ERM_autoDetect; + if (cmd.findOption("--read-file-only")) readMode = ERM_fileOnly; + if (cmd.findOption("--read-dataset")) readMode = ERM_dataset; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--read-xfer-auto")) + xfer = EXS_Unknown; + if (cmd.findOption("--read-xfer-detect")) + dcmAutoDetectDatasetXfer.set(OFTrue); + if (cmd.findOption("--read-xfer-little")) + { + app.checkDependence("--read-xfer-little", "--read-dataset", readMode == ERM_dataset); + xfer = EXS_LittleEndianExplicit; + } + if (cmd.findOption("--read-xfer-big")) + { + app.checkDependence("--read-xfer-big", "--read-dataset", readMode == ERM_dataset); + xfer = EXS_BigEndianExplicit; + } + if (cmd.findOption("--read-xfer-implicit")) + { + app.checkDependence("--read-xfer-implicit", "--read-dataset", readMode == ERM_dataset); + xfer = EXS_LittleEndianImplicit; + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--accept-odd-length")) + { + dcmAcceptOddAttributeLength.set(OFTrue); + } + if (cmd.findOption("--assume-even-length")) + { + dcmAcceptOddAttributeLength.set(OFFalse); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-cp246")) + { + dcmEnableCP246Support.set(OFTrue); + } + if (cmd.findOption("--disable-cp246")) + { + dcmEnableCP246Support.set(OFFalse); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--retain-un")) + { + dcmEnableUnknownVRConversion.set(OFFalse); + } + if (cmd.findOption("--convert-un")) + { + dcmEnableUnknownVRConversion.set(OFTrue); + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--enable-correction")) + { + dcmEnableAutomaticInputDataCorrection.set(OFTrue); + } + if (cmd.findOption("--disable-correction")) + { + dcmEnableAutomaticInputDataCorrection.set(OFFalse); + } + cmd.endOptionBlock(); + +#ifdef WITH_ZLIB + cmd.beginOptionBlock(); + if (cmd.findOption("--bitstream-deflated")) + { + dcmZlibExpectRFC1950Encoding.set(OFFalse); + } + if (cmd.findOption("--bitstream-zlib")) + { + dcmZlibExpectRFC1950Encoding.set(OFTrue); + } + cmd.endOptionBlock(); +#endif + + if (cmd.findOption("--max-read-length")) + { + app.checkValue(cmd.getValueAndCheckMinMax(maxReadLength, 4, 4194302)); + maxReadLength *= 1024; // convert kbytes to bytes + } + cmd.beginOptionBlock(); + if (cmd.findOption("--load-all")) loadIntoMemory = OFTrue; + if (cmd.findOption("--load-short")) loadIntoMemory = OFFalse; + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--print-all")) printFlags &= ~DCMTypes::PF_shortenLongTagValues; + if (cmd.findOption("--print-short")) printFlags |= DCMTypes::PF_shortenLongTagValues; + cmd.endOptionBlock(); + + if (cmd.findOption("--print-filename")) + printFilename = OFTrue; + + cmd.beginOptionBlock(); + if (cmd.findOption("--stop-on-error")) stopOnErrors = OFTrue; + if (cmd.findOption("--ignore-errors")) stopOnErrors = OFFalse; + cmd.endOptionBlock(); + + if (cmd.findOption("--search", 0, OFCommandLine::FOM_First)) + { + do + { + app.checkValue(cmd.getValue(current)); + if (!addPrintTagName(current)) return 1; + } while (cmd.findOption("--search", 0, OFCommandLine::FOM_Next)); + } + + cmd.beginOptionBlock(); + if (cmd.findOption("--search-all")) + { + app.checkDependence("--search-all", "--search", printTagCount>0); + printAllInstances = OFTrue; + } + if (cmd.findOption("--search-first")) + { + app.checkDependence("--search-first", "--search", printTagCount>0); + printAllInstances = OFFalse; + } + cmd.endOptionBlock(); + + cmd.beginOptionBlock(); + if (cmd.findOption("--prepend")) + { + app.checkDependence("--prepend", "--search", printTagCount>0); + prependSequenceHierarchy = OFTrue; + } + if (cmd.findOption("--no-prepend")) + { + app.checkDependence("--no-prepend", "--search", printTagCount>0); + prependSequenceHierarchy = OFFalse; + } + cmd.endOptionBlock(); + + if (cmd.findOption("--write-pixel")) + { + app.checkValue(cmd.getValue(pixelDirectory)); + writePixelData = OFTrue; + } + } + + SetDebugLevel((opt_debugMode)); + + /* make sure data dictionary is loaded */ + if (!dcmDataDict.isDictionaryLoaded()) + { + CERR << "Warning: no data dictionary loaded, " + << "check environment variable: " + << DCM_DICT_ENVIRONMENT_VARIABLE; + } + + int errorCount = 0; + int count = cmd.getParamCount(); + for (int i=1; i<=count; i++) + { + cmd.getParam(i, current); + if (printFilename) + { + /* a newline separates two consecutive "dumps" */ + if (i > 1) + COUT << endl; + /* print header with filename */ + COUT << "# " << OFFIS_CONSOLE_APPLICATION << " (" << i << "/" << count << "): " << current << endl; + } + errorCount += dumpFile(COUT, current, readMode, xfer, printFlags, loadIntoMemory, stopOnErrors, + writePixelData, pixelDirectory); + } + + return errorCount; +} + +static void printResult(ostream& out, DcmStack& stack, size_t printFlags) +{ + unsigned long n = stack.card(); + if (n == 0) { + return; + } + + if (prependSequenceHierarchy) { + /* print the path leading up to the top stack elem */ + for (unsigned long i=n-1; i>=1; i--) { + DcmObject *dobj = stack.elem(i); + /* do not print if a DCM_Item as this is not + * very helpful to distinguish instances. + */ + if (dobj != NULL && dobj->getTag().getXTag() != DCM_Item) { + char buf[512]; + sprintf(buf, "(%x,%x).", + OFstatic_cast(unsigned, dobj->getGTag()), + OFstatic_cast(unsigned, dobj->getETag())); + out << buf; + } + } + } + + /* print the tag and its value */ + DcmObject *dobj = stack.top(); + dobj->print(out, printFlags); +} + +static int dumpFile(ostream & out, + const char *ifname, + const E_FileReadMode readMode, + const E_TransferSyntax xfer, + const size_t printFlags, + const OFBool loadIntoMemory, + const OFBool stopOnErrors, + const OFBool writePixelData, + const char *pixelDirectory) +{ + int result = 0; + + if ((ifname == NULL) || (strlen(ifname) == 0)) + { + CERR << OFFIS_CONSOLE_APPLICATION << ": invalid filename: <empty string>" << endl; + return 1; + } + + DcmFileFormat dfile; + DcmObject *dset = &dfile; + + if (readMode == ERM_dataset) dset = dfile.getDataset(); + + // Load file + + OFCondition cond = dfile.loadFile(ifname, xfer, EGL_noChange, maxReadLength, readMode); + + // Read error + + if (! cond.good()) + { + CERR << OFFIS_CONSOLE_APPLICATION << ": error: " << dfile.error().text() + << ": reading file: "<< ifname << endl; + + result = 1; + if (stopOnErrors) return result; + } + + if (loadIntoMemory) dfile.loadAllDataIntoMemory(); + + if (printTagCount == 0) + { + if (writePixelData) + { + OFString str = ifname; + OFString rname = pixelDirectory; + if ((rname.length() > 0) && (rname[rname.length() - 1] != PATH_SEPARATOR)) + rname += PATH_SEPARATOR; + size_t pos = str.find_last_of(PATH_SEPARATOR); + if (pos == OFString_npos) + rname += str; + else + rname += str.substr(pos + 1); + size_t counter = 0; + dset->print(out, printFlags, 0 /*level*/, rname.c_str(), &counter); + } else + dset->print(out, printFlags); + } else { + /* only print specified tags */ + for (int i=0; i<printTagCount; i++) + { + unsigned int group = 0xffff; + unsigned int elem = 0xffff; + DcmTagKey searchKey; + const char* tagName = printTagNames[i]; + if (printTagKeys[i]) searchKey = *printTagKeys[i]; + else if (sscanf( tagName, "%x,%x", &group, &elem ) == 2 ) searchKey.set(group, elem); + else { + CERR << "Internal ERROR in File " << __FILE__ << ", Line " + << __LINE__ << endl + << "-- Named tag inconsistency" << endl; + abort(); + } + + DcmStack stack; + if (dset->search(searchKey, stack, ESM_fromHere, OFTrue) == EC_Normal) + { + printResult(out, stack, printFlags); + if (printAllInstances) + { + while (dset->search(searchKey, stack, ESM_afterStackTop, OFTrue) == EC_Normal) + printResult(out, stack, printFlags); + } + } + } + } + + return result; +}
participants (1)
-
Fabien Freling