Hi Andy

> -----Original Message-----
> From: Andy 
> Sent: 24 November 2011 09:35
> To: Kris Thielemans
> Cc: octave-dev@lists.sourceforge.net
> Subject: Re: [OctDev] experimental dicom support: changing code for
> multiple elements and byteorder etc
> 
> On 23 November 2011 23:05, Kris 
> wrote:
> > Hi
> >
> > I'm modifying octave-forge/extra/dicom/src/dicominfo.cpp to handle data
> > which are arrays, i.e. VM>1. I realize that not many people will know
> DICOM,
> > let alone GDCM, but I'd appreciate any feedback in any case. (There's
some
> > generic octave questions at the end)
> >
> > Also, it would be good if someone could test the current dicominfo on
> > systems with different byteorders. I suspect that it'll only work when
the
> > byteorder of the dicom file is the same as the one of the machine you're
> > running it on.
> >
> > I had a look at gdcm example code and it seems to me that we cannot
really
> > use memcpy to get from the dicom raw data to octave data. This
> presumably
> > wouldn't work when byteswapping is needed (or other "transfer syntax").
> In
> > any case, I wouldn't know if memcpy would work with arrays (I'm not sure
> if
> > it's guaranteed by DICOM that the subsequent members are stored
> > consecutively, although it's very likely of course). In an attempt to
handle
> > this (and avoid horrible code repetition) I came up with the following
> > modification for element2value() in dicominfo.cpp:
> >
> > ------------------ current code -------------------
> > if (vr & gdcm::VR::US) {// unsigned short
> >                uint16_t usval ;
> >                memcpy(&usval, elem->GetByteValue()->GetPointer(), 2);
> >                *ov=usval;
> >                if(chatty) octave_stdout << '[' << usval << "]\n";
> >        } else ...
> > // almost repeated for lots of VRs and types
> > return DICOM_OK;
> > -------------------- new code -----------------
> > if (vr & gdcm::VR::US) {// unsigned short
> >          return element2intvalueHelper<gdcm::VR::US>(ov, elem, chatty);
> >        } else ...
> > // repeated for lots of VRs
> >
> > ---------------------- new function definition (in dicominfo.cpp)
> > ---------------------
> > template <gdcm::VR::VRType vrtype>
> > int element2intvalueHelper(octave_value *ov, const gdcm::DataElement *
> elem,
> >                           const int chatty) {
> >  if( !elem->IsEmpty() ) {
> >    // find type that corresponds to this VR
> >    typedef typename gdcm::VRToType<vrtype >::Type actual_type;
> > gdcm::Element<vrtype,gdcm::VM::VM1_n> el;
> >    el.Set( elem->GetValue() );
> >    intNDArray<octave_int<actual_type> > val(dim_vector
> (el.GetLength(),1));
> >    for (unsigned i=0; i<el.GetLength(); ++i)
> >      val(i)  = el.GetValue(i);
> >    *ov=val;
> >    if(chatty) octave_stdout << '[' << val << "]\n";
> >    return DICOM_OK;
> >  }
> >  else
> >  {
> >    return DICOM_NOTHING_ASSIGNED;
> >  }
> > }
> >
-------------------------------------------------------------------------
> >
> > This seems to work.  It might be somewhat inefficient though, as we're
> > creating a temporary array, copying to an octave_value, and then getting
> rid
> > of it. I'm not sure how to avoid this, or even if it's an issue.
> > Also, it's annoying that I need to have 2 separate code sets for ints
and
> > floats (as I need intNDArray<octave_int<short> > etc because of missing
> > constructors in octave_value).
> >
> > Any suggestions for improvements?
> >
> > Thanks
> >
> > Kris Thielemans
> > Algorithms and Software Consulting Ltd
> > Honorary Lecturer at Imperial College London
> 
> It's really nice to see someone else working on the dicom package.
> 
> Have you timed it on your system? How much slower is it?
> 

I tried to do timings, but as I'm octave in Linux in VirtualBox on Windows
7, timings are all over the place. I think it's known that timings in
virtual machines are unreliable. In any case, my timings are all over the
place (using tic; for i=1:200 dicominfo();end;toc). I'm afraid that's what I
all can do for the moment.

> It did occur to me that memcpy may cause problems. Do you have a
> system where it does? I have used it on a few and it always works. I
> guess having someone with different types of mac to have a try might
> be useful.
>

I think that we don't see problems because most processors these days have
the same endianness, even recent macs. We need someone with a Sun SPARC
probably.

> 
> Driver issues with my linux system are still consuming my spare time,
> so I have not actually tried your code yet. I would not actually
> commit this yet unless;
> - The timed performance degradation is tiny, or,
> - We have confirmation of our suspicion that it does cause a problem
> on some systems.
> 

My personal opinion is a bit different. I'm rather safe than sorry. I think
we should first aim to get this working in all cases before trying to
optimise.

In any case, the current code is already faulty for those elements for which
VM>1, i.e. which have multiple numbers. It is of course possible to rewrite
those with memcpy as well, but we'd first need to allocate sufficient memory
etc. That'll get somewhat painful.

I've attached my current version of dicominfo. The only modification is that
in the element2value loop, the int/float etc cases call a helper function.
In that helper function, you can switch between implementations by using
pre-processor directives (look for KT). I did check that they give identical
results (except that the memcpy version only returns the 1st element of any
array).

There's also a pre-processor directive in there that would allow (maybe)
avoid allocating an array. See comments in the file. If this code is
necessary probably can only be decided on timings (or by someone who knows
in detail how octave_value etc work).

In summary, if nobody can do the timings now, I suggest to check-in this
version, and do the timings later.

Kris

/*
 * The GNU Octave dicom package is Copyright Andy Buckle 2010
 * Contact: blondandy using the sf.net system, 
 * <https://sourceforge.net/sendmessage.php?touser=1760416>
 * 
 * Changes Copyright Kris Thielemans 2011:
 * - support usage dicominfo(filename, 'dictionary', dictname)
 * - support FL, FD and SL VRs, which means that many more fields are now read 
correctly from the dicom file.
 * - check if the VR in the file is the same as the one in the dictionary. If 
not, issue a warning but use the VR from the file.
 * - assign values to private dicom fields, just like with others
 * - changed convention for private fields to use lower-case for the 
hexadecimal numbers to be compatible with Matlab
 * - if an entry is not in the dictionary, determine its VR from the file (if 
possible) and assign anyway.
 * - updated doc-string
 *
 *
 * The GNU Octave dicom package is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or (at your option) any later version.
 * 
 * The GNU Octave dicom package is distributed in the hope that it 
 * will be useful, but WITHOUT ANY WARRANTY; without even the 
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 * PURPOSE.  See the GNU General Public License for more details.
 * 
 * Please see the file, "COPYING" for further details of GNU General 
 * Public License version 3.
 * 
 */
 
#include <stdlib.h> //for calloc, free
#include <stdio.h>  //for printf

#include <sys/stat.h>
#include <unistd.h>
#include <time.h>

#include <iostream>
#include <string>
#include <map>

#include "octave/oct.h"
#include "octave/ov-struct.h"

#include "gdcm-2.0/gdcmSystem.h"
#include "gdcm-2.0/gdcmReader.h"
#include "gdcm-2.0/gdcmWriter.h"
#include "gdcm-2.0/gdcmAttribute.h"
#include "gdcm-2.0/gdcmDataSet.h"
#include "gdcm-2.0/gdcmGlobal.h"
#include "gdcm-2.0/gdcmDicts.h"
#include "gdcm-2.0/gdcmDict.h"
#include "gdcm-2.0/gdcmCSAHeader.h"
#include "gdcm-2.0/gdcmPrivateTag.h"
#include "gdcm-2.0/gdcmVR.h"
#include "gdcm-2.0/gdcmSequenceOfItems.h"
 
#include "dicomdict.h" 


  
#define DICOM_ERR -1
#define DICOM_OK 0
#define DICOM_NOTHING_ASSIGNED 1

#define TIME_STR_LEN 31

#define OCT_FN_NAME dicominfo
#define QUOTED_(x) #x
#define QUOTED(x) QUOTED_(x)

#ifdef NOT_OCT
#       define octave_stdout    std::cout
#       define error                    printf
#endif

char* byteval2string(char * d, int d_len_p, const gdcm::ByteValue *bv);
char* name2Keyword(char *d, int *d_len_p, const char* s);
Matrix str2DoubleVec(const char*);
Octave_map dump(const char filename[], int chatty);
void dumpDataSet(Octave_map *om, const gdcm::DataSet *ds, int chatty, int 
sequenceDepth);
void getFileModTime(char *timeStr, const char *filename);
void dumpElement(Octave_map *om, const gdcm::DataElement * elem, int chatty, 
int sequenceDepth);
void dumpSequence(octave_value *ov, gdcm::SequenceOfItems *seq, int chatty, int 
sequenceDepth);
int element2value(std::string & varname, octave_value *ov, const 
gdcm::DataElement * elem, int chatty, int sequenceDepth) ;

int dicom_truncate_numchar=40;

#ifdef NOT_OCT
int main( int argc, const char* argv[] ) {
        dump(argv[1], 1 /* chatty on */ ); // 1 cmd line arg: dicom filename
        return 0;
}
#else
DEFUN_DLD (OCT_FN_NAME, args, nargout,
                "-*- texinfo -*- \n\
 @deftypefn {Loadable Function} {@var{info}} = "QUOTED(OCT_FN_NAME)" 
(@var{filename}) \n\
 @deftypefnx {Loadable Function} {@var{info}} = "QUOTED(OCT_FN_NAME)" 
(@var{filename}, @code{dictionary}, @var{dictionary-name}) \n\
 @deftypefnx  {Loadable Function} {} "QUOTED(OCT_FN_NAME)" (@var{filename}, 
@var{options}) \n\
 @deftypefnx {Command} {} "QUOTED(OCT_FN_NAME)" @var{filename} \n\
 @deftypefnx {Command} {} "QUOTED(OCT_FN_NAME)" @var{filename} @var{options} \n\
 Get all data from a DICOM file, excluding any actual image. \n\
 @var{info} is a nested struct containing the data. \n\
 \n\
 If no return argument is given, then there will be output similar to \n\
 a DICOM dump. \n\
 \n\
 If the @code{dictionary} argument is used, the given @var{dictionary-name} is 
used for this operation, \n\
 otherwise, the dictionary set by @code{dicomdict} is used.\n\
 \n\
 @var{options}:\n\
 @code{truncate=n}\n\
 where n is the number of characters to limit the dump output display to 
@code{n}\
 for each value. \n\
\n\
 @seealso{dicomread, dicomdict} \n\
 @end deftypefn \n\
                ") {
        octave_value_list retval;  // create object to store return values
        if ( 0 == args.length()) {
                error(QUOTED(OCT_FN_NAME)": one arg required: dicom filename");
                return retval; 
        }
        int chatty = !nargout; // dump output to stdout if not assigning to var
        charMatrix ch = args(0).char_matrix_value ();
        if (ch.rows()!=1) {
                error(QUOTED(OCT_FN_NAME)": arg should be a filename, 1 row of 
chars");
                return retval; 
        }
        std::string filename = ch.row_as_string (0);

        std::string current_dict = get_current_dict();
        // TODO the dictionary can be set by the code below
        // we really need to catch any errors in this function
        // and reset the dictionary to current_dict.
        // Currently that's only done when no error occured.

        int i; // parse any additional args
        for (i=1; i<args.length(); i++) {
                charMatrix chex = args(i).char_matrix_value();
                if (chex.rows()!=1) {
                        error(QUOTED(OCT_FN_NAME)": arg should be a string, 1 
row of chars");
                        return retval; 
                }
                std::string argex = chex.row_as_string (0);
                if (!argex.compare("dictionary") || 
!argex.compare("Dictionary")) {
                        if (i+1==args.length()) {
                              error(QUOTED(OCT_FN_NAME)": Dictionary needs 
another argument");
                              return retval;
                        }
                        if (!args(i+1).is_string()) {
                              error(QUOTED(OCT_FN_NAME)": Dictionary needs a 
string argument");
                              return retval;
                        }
                        std::string dictionary = args(i+1).string_value();
                        load_dict(dictionary.c_str());
                        // ignore dictionary argument for further arg processing
                        ++i;
                }
                else if (!argex.compare(0,9,"truncate=")) {
                        dicom_truncate_numchar=atoi(argex.substr(9).c_str());
                } else {
                        warning(QUOTED(OCT_FN_NAME)": arg not understood: %s", 
argex.c_str());
                }
        }
        
        Octave_map om=dump(filename.c_str(),chatty);
        retval(0)=om;

        load_dict(current_dict.c_str());
        return retval;
}
#endif


Octave_map dump(const char filename[], int chatty) {
        // output struct
        Octave_map om;
        // Instantiate the reader:
        gdcm::Reader reader;
        reader.SetFileName( filename );
        if( !reader.Read() ) {
                error("Could not read: %s",filename);
                return om; //TODO: set error state somehow so the main 
DEFUN_DLD function knows
        }
        gdcm::File &file = reader.GetFile();
        gdcm::DataSet &ds = file.GetDataSet();
        gdcm::FileMetaInformation &hds=file.GetHeader();
        
        om.assign("Filename",filename);
        char dateStr[TIME_STR_LEN+1];
        getFileModTime(dateStr, filename);
        om.assign("FileModDate", dateStr);
        if(chatty) octave_stdout << "# file info\nFilename:" 
                << filename << "\nFileModDate:" << dateStr << '\n';
        
        if(chatty) octave_stdout << "# header\n" ;
        dumpDataSet(&om, &hds, chatty, 0);
        if(chatty) octave_stdout << "# metadata\n" ;
        dumpDataSet(&om, &ds, chatty, 0);
        
        return om;
}

void dumpDataSet(Octave_map *om, const gdcm::DataSet *ds, int chatty, int 
sequenceDepth) {
        
        const gdcm::DataSet::DataElementSet DES=ds->GetDES(); // 
gdcm::DataSet::DataElementSet is a std::set
        gdcm::DataSet::Iterator it;
        
        for ( it=DES.begin() ; it != DES.end(); it++ ) {                
                dumpElement(om, &(*it), chatty, sequenceDepth);
        }
}

void dumpElement(Octave_map *om, const gdcm::DataElement * elem, 
                                int chatty, int sequenceDepth) {
        std::string varname;
        octave_value ov;
        if(DICOM_OK==element2value(varname, &ov, elem, chatty, sequenceDepth)) {
                om->assign(varname.c_str(), ov);
        } else {
                if (0==varname.length()) return ;
                om->assign(varname.c_str(), "not assigned");
        }
}

/* helper function for element2value below.
   This function converts a 'simple' type (such as an (array of) floats or 
ints, etc)
   to an octave_value, returning DICOM_NOTHING_ASSIGNED if the dicom element is 
empty
   or DICOM_OK otherwise.

   It is templated in 
   - the VR Type, such that we can use GDCM's functions to 
     convert data appropriately.
   - valueType, i.e. float, or int etc
   - octaveArrayType, i.e. Array<float>, or intNDArray<octave_int<int> >, etc

  There are other helper functions below that find the template arguments.

  Note, the octaveArrayType is currently necessary as we cannot use Array<int> 
etc, 
  as octave_value does not have a constructor for Array<int>.
*/
template <gdcm::VR::VRType vrtype, typename valueType, typename octaveArrayType>
static inline
int element2simplevalueHelper2(octave_value *ov, const gdcm::DataElement *elem,
                            const int chatty) {
    if( elem->IsEmpty() )
      return DICOM_NOTHING_ASSIGNED;

#if 0 // KT if to choose between implementations
    // original fast implementation using memcpy. However, ignores byteorder 
and VMs (i.e. arrays)
    valueType val ; 
    memcpy(&val, elem->GetByteValue()->GetPointer(), sizeof(valueType));
    *ov=val;
    if(chatty) octave_stdout << '[' << val << "]\n";
#else
    // save (but slow?) implementation that uses GDCM functions
    typedef gdcm::Element<vrtype,gdcm::VM::VM1_n> el;
    el.Set( elem->GetValue() );
    // possible optimisation here. If there's only 1 element, maybe we can
    // save time by not making array. However, because all octave values are
    // arrays, maybe this doesn't matter. the "else" statement below works
    // always in any case.
    // If you want to try this optimisation, put #if 1 below
#   if 0
    if (el.GetLength()==1)
      {
        const valueType& val = el.GetValue();
        *ov=val;
        if(chatty) octave_stdout << '[' << val << "]\n";
      }
    else
#   endif
      {
        octaveArrayType val(dim_vector (el.GetLength(),1));
        for (unsigned i=0; i<el.GetLength(); ++i)
          val(i)  = el.GetValue(i); 
        *ov=val;
        if(chatty) octave_stdout << '[' << val << "]\n";
      }
#endif
    return DICOM_OK;
}

/* Helper functions for elemement2value for integer and real types.
   They simply call element2simplevalueHelper2 with the appropriate template 
arguments.
*/
template <gdcm::VR::VRType vrtype>
static inline 
int element2intvalueHelper(octave_value *ov, const gdcm::DataElement * elem,
                           const int chatty) {
  typedef typename gdcm::VRToType<vrtype >::Type valueType;
  return 
element2simplevalueHelper2<vrtype,valueType,intNDArray<octave_int<valueType> > 
>(ov, elem, chatty);
}


template <gdcm::VR::VRType vrtype>
static inline 
int element2realvalueHelper(octave_value *ov, const gdcm::DataElement * elem,
                           const int chatty) {
  typedef typename gdcm::VRToType<vrtype >::Type valueType;
  return element2simplevalueHelper2<vrtype,valueType,Array<valueType> >(ov, 
elem, chatty);
}

int element2value(std::string & varname, octave_value *ov, const 
gdcm::DataElement * elem, 
                                int chatty, int sequenceDepth) {
        // get dicom dictionary
        //static const gdcm::Global& g = gdcm::Global::GetInstance();
        //static const gdcm::Dicts &dicts = g.GetDicts();
        
        const gdcm::Tag tag = elem->GetTag();
                
        // skip "Group Length" tags. note: these are deprecated in DICOM 2008
        if(tag.GetElement() == (uint16_t)0 || elem->GetByteValue() == NULL) 
return DICOM_NOTHING_ASSIGNED;
        //const gdcm::DictEntry dictEntry = dicts.GetDictEntry(tag,(const 
char*)0);

        const gdcm::VR vr = elem->GetVR(); // value representation. ie DICOM 

        gdcm::DictEntry dictEntry ;
        if (!is_present(tag)) {
                char fallbackVarname[64];
                
snprintf(fallbackVarname,63,"Private_%04x_%04x",tag.GetGroup(),tag.GetElement());
                varname=std::string(fallbackVarname);
#if 0
                *ov=std::string("");
                warning(QUOTED(OCT_FN_NAME)": %s", fallbackVarname);
                return DICOM_NOTHING_ASSIGNED; //TODO maybe could carry on, if 
we know the VR
#endif
        }
        else {
                lookup_entry(dictEntry, tag);
                varname=dictEntry.GetName();
                const gdcm::VR dictvr= dictEntry.GetVR(); // value 
representation. ie DICOM     
                if (!vr.Compatible(vr)) {
                  warning(QUOTED(OCT_FN_NAME)": %s has different VR from 
dictionary. Using VR from file.", varname.c_str());
                }
        }

        
        //int tagVarNameBufLen=127;
        //char *keyword=(char *)malloc((tagVarNameBufLen+1)*sizeof(char));
        //keyword=name2Keyword(keyword,&tagVarNameBufLen,tagName);
        //*varname=std::string(keyword);
        
        if(chatty) {
                octave_stdout << std::setw(2*sequenceDepth) << "" << 
std::setw(0) ;
                octave_stdout << tag << ":" << vr << ":" << varname << ":" ;
                // TODO: error if var name repeated. 
        }
#define strValBufLen 511
        char strValBuf[strValBufLen+1];
        char* strVal=strValBuf;
        
        if ( vr & gdcm::VR::VRASCII) {
                
strVal=byteval2string(strValBuf,strValBufLen,elem->GetByteValue()); 
                if(chatty) {
                        if (dicom_truncate_numchar>0) {
                                octave_stdout << '[' << 
std::string(strVal).substr(0,dicom_truncate_numchar) 
                                        << ( 
((int)strlen(strVal)>dicom_truncate_numchar) ? "..." : "") << ']' << std::endl;
                        } else {
                                octave_stdout << '[' << strVal << ']' << 
std::endl;
                        }
                }
                if (vr & VRSTRING) { //all straight to string types
                        *ov=std::string(strVal); // TODO: error if om already 
has member with this name
                } else if (vr & gdcm::VR::IS) { // Integer String. spec tallies 
with signed 32 bit int
                        *ov=(int32_t)atoi(strVal); 
                } else if (vr & gdcm::VR::DS) { // Double String. vector 
separated by '/'
                        Matrix vec=str2DoubleVec(strVal);
                        *ov=vec;
                } else {
                        if(chatty) octave_stdout << "   ### string type not 
handled ###" << std::endl;
                        return DICOM_NOTHING_ASSIGNED;
                }
                if (strVal != strValBuf) free(strVal); // long string. malloc'd 
instead of using buf, now needs free'ng
        } else if (vr & gdcm::VR::SQ) {
                if(chatty) octave_stdout << " reading sequence. "; // \n 
provided in dumpSequence fn
                gdcm::SmartPointer<gdcm::SequenceOfItems> sqi = 
elem->GetValueAsSQ();
                dumpSequence(ov, sqi, chatty, sequenceDepth+1);
        } else if (vr & gdcm::VR::AT) { // attribute tag
                intNDArray<octave_uint16> uint16pair(dim_vector(1,2));
                octave_uint16 *fv=uint16pair.fortran_vec();
                uint16_t *p=(uint16_t *)elem->GetByteValue()->GetPointer();
                memcpy(fv,p,4); // TODO. not sure if memcpy is ok
                *ov=uint16pair;
                if (chatty) {
                        char buf[16];
                        snprintf(buf,15,"[(%04X,%04X)]\n",p[0],p[1]);
                        octave_stdout << buf  ;
                }
        } else if (vr & gdcm::VR::FL) {// float
          return element2realvalueHelper<gdcm::VR::FL>(ov, elem, chatty);
        } else if (vr & gdcm::VR::FD) {// double
          return element2realvalueHelper<gdcm::VR::FD>(ov, elem, chatty);
        } else if (vr & gdcm::VR::UL) {// unsigned long
          return element2intvalueHelper<gdcm::VR::UL>(ov, elem, chatty);
        } else if (vr & gdcm::VR::SL) {// signed long
          return element2intvalueHelper<gdcm::VR::SL>(ov, elem, chatty);
        } else if (vr & gdcm::VR::US) {// unsigned short
          return element2intvalueHelper<gdcm::VR::US>(ov, elem, chatty);
        } else if (vr & gdcm::VR::SS) {// signed short
          return element2intvalueHelper<gdcm::VR::SS>(ov, elem, chatty);
        } else if (vr & gdcm::VR::OB) {// other byte
                if (tag==gdcm::Tag(0x7FE0,0x0010)) { // PixelData
                        if(chatty) octave_stdout  << "skipping, leave for 
dicomread\n";
                        return DICOM_NOTHING_ASSIGNED;
                }
                const uint32_t len=elem->GetByteValue()->GetLength();
                intNDArray<octave_uint8> bytearr(dim_vector(1,len));
                octave_uint8 *fv=bytearr.fortran_vec(); 
                const char *p=elem->GetByteValue()->GetPointer();
                memcpy(fv,p , len);
                *ov=bytearr;
                if (chatty) {
                        uint32_t i;
                        char buf[8];
                        octave_stdout  << '[' ;
                        for (i=0; i<len; i++) {
                                snprintf(buf,7,"%02X ",(const uint8_t)p[i]);
                                octave_stdout  << buf << " " ;
                        }
                        octave_stdout  << "]\n";
                }
        } else {
                if(chatty) octave_stdout << "   ### VR not handled ###" << 
std::endl;
                return DICOM_NOTHING_ASSIGNED;
        }
        //free(keyword);
        return DICOM_OK;
}

void dumpSequence(octave_value *ov, gdcm::SequenceOfItems *seq, int chatty, int 
sequenceDepth) {
        const octave_idx_type nDataSet=seq->GetNumberOfItems(); // objects in 
sequence
        if (chatty) octave_stdout << nDataSet << " object" << 
((nDataSet==1)?"":"s") << std::endl;
        char item_name_buf[16];
        Octave_map om;
        for (octave_idx_type j=1; j<=nDataSet; j++ ) {
                const gdcm::DataSet::DataElementSet 
des=seq->GetItem(j).GetNestedDataSet().GetDES() ;
                Octave_map subom;
                for (gdcm::DataSet::Iterator it=des.begin(); it != des.end(); 
it++) {
                        std::string key("");
                        octave_value subov;
                        if( DICOM_OK==element2value(key, &subov, &(*it), 
chatty, sequenceDepth)) {
                                subom.assign(key.c_str(), subov);
                        } else {
                                if (0==key.length()) continue ;
                                subom.assign(key.c_str(), "not assigned");
                        }
                }
                snprintf(item_name_buf,15,"Item_%i",j);
                om.assign(item_name_buf, subom);
        }
        *ov=om;
}

void getFileModTime(char *timeStr, const char *filename) {
        struct tm* clock;                               // create a time 
structure
        struct stat attrib;                     // create a file attribute 
structure
        stat(filename, &attrib);                // get the attributes of 
afile.txt
        clock = gmtime(&(attrib.st_mtime));     // Get the last modified time 
and put it into the time structure
        char monthStr[4];
        switch(clock->tm_mon) {
                case  0: strcpy(monthStr,"Jan"); break;
                case  1: strcpy(monthStr,"Feb"); break;
                case  2: strcpy(monthStr,"Mar"); break;
                case  3: strcpy(monthStr,"Apr"); break;
                case  4: strcpy(monthStr,"May"); break;
                case  5: strcpy(monthStr,"Jun"); break;
                case  6: strcpy(monthStr,"Jul"); break;
                case  7: strcpy(monthStr,"Aug"); break;
                case  8: strcpy(monthStr,"Sep"); break;
                case  9: strcpy(monthStr,"Oct"); break;
                case 10: strcpy(monthStr,"Nov"); break;
                case 11: strcpy(monthStr,"Dec"); break;
        }
        snprintf(timeStr, TIME_STR_LEN, "%02i-%s-%i %02i:%02i:%02i",
                clock->tm_mday,monthStr,1900+clock->tm_year, 
                clock->tm_hour, clock->tm_min, clock->tm_sec);
        //clock->tm_year returns the year (since 1900)
        // clock->tm_mon returns the month (January = 0)
        // clock->tm_mday returns the day of the month
        // 18-Dec-2000 11:06:43
}

Matrix str2DoubleVec(const char* s){
        // count separators, hence elements
        int n=1;
        char *sl=(char *)s; 
        for (; *sl != '\0'; sl++) n = (*sl == '\\') ? n+1 : n;
        // create output args
        Matrix dv(n, 1);
        double *fv=dv.fortran_vec();
        // parse into output
        int i=0;
        for (sl=(char *)s; i<n ; i++, sl++) {
                fv[i]=strtod(sl,&sl);
        }
        return dv;
}


// this fn will malloc new space if the length of the supplied destination
// string is not sufficient. so, if the returned pointer is not to the same
// place as the supplied, the returned pointer should be freed.
// returned pointer, as the supplied one may be invalid.
// d_len: length of d.
char* byteval2string(char * d, int d_len, const gdcm::ByteValue *bv) {
        if(bv==NULL) { // make a null string, "" 
                *d='\0';
                return d;
        }
        int len = bv->GetLength();
        if ( len > d_len ) {
                d=(char *)malloc((len+1)*sizeof(char));
        }
        memcpy(d, bv->GetPointer(), len);
        d[len]='\0';
        return d;
}

// remove non-alphabet characters from a string.
// remove s following quote
// the destination string, d, must be malloc'd space.
// this fn will realloc if it is not big enough. so use
// returned pointer, as the supplied one may be invalid.
// d_len_p: pointer to length of d. is updated if required.
char* name2Keyword(char *d, int *d_len_p, const char* s) {
        char *f=(char*)s; //from (loop through source)
        int len=strlen(s);
        if ( len > *d_len_p ) {
                d=(char *)realloc(d,(len+1)*sizeof(char));
        }
        char *tl=(char*)d; // pointer to loop through the destination
        for (; *f != '\0' ; f++ ) {
                if ( (*f >= 'A' && *f <= 'Z') || (*f >= 'a' && *f <= 'z') ) {
                        *tl++ = *f;
                } else if (*f=='\'' && *(f+1)=='s') {
                        f++; // if quote followed by s, skip both chars
                } else if (*f==' ' && *(f+1) >= 'a' && *(f+1) <= 'z') {
                        *tl++ = *++f - ('a'-'A') ; // space folowed by lower 
case char, cap char
                }
        }
        *tl = '\0';
        return d;
}
------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure 
contains a definitive record of customers, application performance, 
security threats, fraudulent activity, and more. Splunk takes this 
data and makes sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-novd2d
_______________________________________________
Octave-dev mailing list
Octave-dev@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/octave-dev

Reply via email to